From 95d0b6895d0382574196c0c4c6d066d3830a20bf Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Fri, 13 Oct 2023 00:17:46 +0530 Subject: [PATCH 01/71] Integrate release drafter (#389) * Add release drafter workflow * Add release drafter configuration --- .github/release-drafter.yml | 16 +++++++++++ .github/workflows/release-drafter.yml | 41 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..f42ffd37 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,16 @@ +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +template: | + ## Changes + + $CHANGES \ No newline at end of file diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000..4eed5a56 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,41 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - main + # pull_request event is required only for autolabeler + pull_request: + # Only following types are handled by the action, but one can default to all as well + types: [opened, reopened, synchronize] + # pull_request_target event is required for autolabeler to support PRs from forks + pull_request_target: + types: [opened, reopened, synchronize] + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + # write permission is required to create a github release + contents: write + # write permission is required for autolabeler + # otherwise, read permission is required at least + pull-requests: write + runs-on: ubuntu-latest + steps: + # (Optional) GitHub Enterprise requires GHE_HOST variable set + #- name: Set GHE_HOST + # run: | + # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV + + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5.21.1 + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + # with: + # config-name: my-config.yml + # disable-autolabeler: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 5ec0c9586f084e4aa3d145059f07310c96dd7a90 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:54:51 +0530 Subject: [PATCH 02/71] Add interface for Wiki database --- Src/Notion.Client/Models/Database/Database.cs | 2 +- Src/Notion.Client/Models/Database/IWikiDatabase.cs | 13 +++++++++++++ Src/Notion.Client/Models/Page/Page.cs | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 Src/Notion.Client/Models/Database/IWikiDatabase.cs diff --git a/Src/Notion.Client/Models/Database/Database.cs b/Src/Notion.Client/Models/Database/Database.cs index f83d3316..c5a46ed6 100644 --- a/Src/Notion.Client/Models/Database/Database.cs +++ b/Src/Notion.Client/Models/Database/Database.cs @@ -4,7 +4,7 @@ namespace Notion.Client { - public class Database : IObject, IObjectModificationData + public class Database : IObject, IObjectModificationData, IWikiDatabase { [JsonProperty("title")] public List Title { get; set; } diff --git a/Src/Notion.Client/Models/Database/IWikiDatabase.cs b/Src/Notion.Client/Models/Database/IWikiDatabase.cs new file mode 100644 index 00000000..ac7a798b --- /dev/null +++ b/Src/Notion.Client/Models/Database/IWikiDatabase.cs @@ -0,0 +1,13 @@ +using JsonSubTypes; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [JsonConverter(typeof(JsonSubtypes), "object")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(Page), ObjectType.Page)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(Database), ObjectType.Database)] + public interface IWikiDatabase : IObject + { + + } +} diff --git a/Src/Notion.Client/Models/Page/Page.cs b/Src/Notion.Client/Models/Page/Page.cs index cd81c411..c6bcfae1 100644 --- a/Src/Notion.Client/Models/Page/Page.cs +++ b/Src/Notion.Client/Models/Page/Page.cs @@ -4,7 +4,7 @@ namespace Notion.Client { - public class Page : IObject, IObjectModificationData + public class Page : IObject, IObjectModificationData, IWikiDatabase { /// /// The parent of this page. Can be a database, page, or workspace. From 28132edb7827cccd936b61911b0a51b38136dff7 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:57:30 +0530 Subject: [PATCH 03/71] Update Query database endpoint to return both Page and Database --- .../Api/Databases/DatabasesClient.cs | 24 ++-------------- .../Api/Databases/IDatabasesClient.cs | 2 +- .../Api/Databases/Query/DatabasesClient.cs | 28 +++++++++++++++++++ .../Request}/DatabasesQueryParameters.cs | 0 .../Request}/IDatabaseQueryBodyParameters.cs | 0 .../Request}/IDatabaseQueryQueryParameters.cs | 0 .../Query/Response/DatabaseQueryResponse.cs | 7 +++++ Test/Notion.UnitTests/DatabasesClientTests.cs | 6 ++-- 8 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 Src/Notion.Client/Api/Databases/Query/DatabasesClient.cs rename Src/Notion.Client/Api/Databases/{RequestParams => Query/Request}/DatabasesQueryParameters.cs (100%) rename Src/Notion.Client/Api/Databases/{RequestParams => Query/Request}/IDatabaseQueryBodyParameters.cs (100%) rename Src/Notion.Client/Api/Databases/{RequestParams => Query/Request}/IDatabaseQueryQueryParameters.cs (100%) create mode 100644 Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs diff --git a/Src/Notion.Client/Api/Databases/DatabasesClient.cs b/Src/Notion.Client/Api/Databases/DatabasesClient.cs index ca6a1bef..673ee122 100644 --- a/Src/Notion.Client/Api/Databases/DatabasesClient.cs +++ b/Src/Notion.Client/Api/Databases/DatabasesClient.cs @@ -1,12 +1,10 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using static Notion.Client.ApiEndpoints; namespace Notion.Client { - public class DatabasesClient : IDatabasesClient + public sealed partial class DatabasesClient : IDatabasesClient { private readonly IRestClient _client; @@ -20,24 +18,6 @@ public async Task RetrieveAsync(string databaseId, CancellationToken c return await _client.GetAsync(DatabasesApiUrls.Retrieve(databaseId), cancellationToken: cancellationToken); } - public async Task> QueryAsync( - string databaseId, - DatabasesQueryParameters databasesQueryParameters, CancellationToken cancellationToken = default) - { - var body = (IDatabaseQueryBodyParameters)databasesQueryParameters; - var queryParameters = (IDatabaseQueryQueryParameters)databasesQueryParameters; - - var queryParams = queryParameters.FilterProperties? - .Select(x => new KeyValuePair("filter_properties", x)); - - return await _client.PostAsync>( - DatabasesApiUrls.Query(databaseId), - body, - queryParams, - cancellationToken: cancellationToken - ); - } - public async Task CreateAsync(DatabasesCreateParameters databasesCreateParameters, CancellationToken cancellationToken = default) { var body = (IDatabasesCreateBodyParameters)databasesCreateParameters; diff --git a/Src/Notion.Client/Api/Databases/IDatabasesClient.cs b/Src/Notion.Client/Api/Databases/IDatabasesClient.cs index c7e1c21a..98df1273 100644 --- a/Src/Notion.Client/Api/Databases/IDatabasesClient.cs +++ b/Src/Notion.Client/Api/Databases/IDatabasesClient.cs @@ -24,7 +24,7 @@ public interface IDatabasesClient /// /// /// - Task> QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters, CancellationToken cancellationToken = default); + Task QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters, CancellationToken cancellationToken = default); /// /// Creates a database as a subpage in the specified parent page, with the specified properties schema. diff --git a/Src/Notion.Client/Api/Databases/Query/DatabasesClient.cs b/Src/Notion.Client/Api/Databases/Query/DatabasesClient.cs new file mode 100644 index 00000000..2d0c62be --- /dev/null +++ b/Src/Notion.Client/Api/Databases/Query/DatabasesClient.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class DatabasesClient + { + public async Task QueryAsync( + string databaseId, + DatabasesQueryParameters databasesQueryParameters, CancellationToken cancellationToken = default) + { + var body = (IDatabaseQueryBodyParameters)databasesQueryParameters; + var queryParameters = (IDatabaseQueryQueryParameters)databasesQueryParameters; + + var queryParams = queryParameters.FilterProperties? + .Select(x => new KeyValuePair("filter_properties", x)); + + return await _client.PostAsync( + ApiEndpoints.DatabasesApiUrls.Query(databaseId), + body, + queryParams, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesQueryParameters.cs b/Src/Notion.Client/Api/Databases/Query/Request/DatabasesQueryParameters.cs similarity index 100% rename from Src/Notion.Client/Api/Databases/RequestParams/DatabasesQueryParameters.cs rename to Src/Notion.Client/Api/Databases/Query/Request/DatabasesQueryParameters.cs diff --git a/Src/Notion.Client/Api/Databases/RequestParams/IDatabaseQueryBodyParameters.cs b/Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryBodyParameters.cs similarity index 100% rename from Src/Notion.Client/Api/Databases/RequestParams/IDatabaseQueryBodyParameters.cs rename to Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryBodyParameters.cs diff --git a/Src/Notion.Client/Api/Databases/RequestParams/IDatabaseQueryQueryParameters.cs b/Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryQueryParameters.cs similarity index 100% rename from Src/Notion.Client/Api/Databases/RequestParams/IDatabaseQueryQueryParameters.cs rename to Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryQueryParameters.cs diff --git a/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs b/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs new file mode 100644 index 00000000..73968c38 --- /dev/null +++ b/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs @@ -0,0 +1,7 @@ +namespace Notion.Client +{ + // ReSharper disable once ClassNeverInstantiated.Global + public class DatabaseQueryResponse : PaginatedList + { + } +} diff --git a/Test/Notion.UnitTests/DatabasesClientTests.cs b/Test/Notion.UnitTests/DatabasesClientTests.cs index fc06ebc7..3bd047a7 100644 --- a/Test/Notion.UnitTests/DatabasesClientTests.cs +++ b/Test/Notion.UnitTests/DatabasesClientTests.cs @@ -64,8 +64,9 @@ public async Task QueryAsync() pagesPaginatedList.Results.Should().ContainSingle(); - foreach (var page in pagesPaginatedList.Results) + foreach (var iWikiDatabase in pagesPaginatedList.Results) { + var page = (Page)iWikiDatabase; page.Parent.Should().BeAssignableTo(); page.Object.Should().Be(ObjectType.Page); } @@ -476,8 +477,9 @@ var jsonData pagesPaginatedList.Results.Should().ContainSingle(); - foreach (var page in pagesPaginatedList.Results) + foreach (var iWikiDatabase in pagesPaginatedList.Results) { + var page = (Page)iWikiDatabase; page.Parent.Should().BeAssignableTo(); page.Object.Should().Be(ObjectType.Page); From 770c4e59ccba465f8f9ed1e129323ab6f9aeca77 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:57:52 +0530 Subject: [PATCH 04/71] Add integration test for Query database api --- .../DatabasesClientTests.cs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Test/Notion.IntegrationTests/DatabasesClientTests.cs diff --git a/Test/Notion.IntegrationTests/DatabasesClientTests.cs b/Test/Notion.IntegrationTests/DatabasesClientTests.cs new file mode 100644 index 00000000..069f5be8 --- /dev/null +++ b/Test/Notion.IntegrationTests/DatabasesClientTests.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Notion.Client; +using Xunit; + +namespace Notion.IntegrationTests; + +public class DatabasesClientTests : IntegrationTestBase, IDisposable +{ + private readonly Page _page; + + public DatabasesClientTests() + { + _page = Client.Pages.CreateAsync( + PagesCreateParametersBuilder.Create( + new ParentPageInput { PageId = ParentPageId } + ).Build() + ).Result; + } + + public void Dispose() + { + Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }).Wait(); + } + + [Fact] + public async Task QueryDatabase() + { + // Arrange + var createdDatabase = await CreateDatabaseWithAPageAsync(); + + + // Act + var response = await Client.Databases.QueryAsync(createdDatabase.Id, new DatabasesQueryParameters()); + + // Assert + Assert.NotNull(response.Results); + Assert.Single(response.Results); + var page = response.Results.Cast().First(); + var title = page.Properties["Name"] as TitlePropertyValue; + Assert.Equal("Test Title", (title!.Title.Cast().First()).Text.Content); + } + + private async Task CreateDatabaseWithAPageAsync() + { + var createDbRequest = new DatabasesCreateParameters + { + Title = new List + { + new RichTextTextInput + { + Text = new Text + { + Content = "Test List", + Link = null + } + } + }, + Properties = new Dictionary + { + { "Name", new TitlePropertySchema { Title = new Dictionary() } }, + }, + Parent = new ParentPageInput { PageId = _page.Id } + }; + + var createdDatabase = await Client.Databases.CreateAsync(createDbRequest); + + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = createdDatabase.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextText { Text = new Text { Content = "Test Title" } } + } + }) + .Build(); + + await Client.Pages.CreateAsync(pagesCreateParameters); + + return createdDatabase; + } +} From 8236df37f5bfd444f4efc27d0755a8e438efd5b0 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:02:19 +0530 Subject: [PATCH 05/71] Resolve code factor violations --- Src/Notion.Client/Models/Database/IWikiDatabase.cs | 1 - Test/Notion.IntegrationTests/DatabasesClientTests.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/Src/Notion.Client/Models/Database/IWikiDatabase.cs b/Src/Notion.Client/Models/Database/IWikiDatabase.cs index ac7a798b..59969175 100644 --- a/Src/Notion.Client/Models/Database/IWikiDatabase.cs +++ b/Src/Notion.Client/Models/Database/IWikiDatabase.cs @@ -8,6 +8,5 @@ namespace Notion.Client [JsonSubtypes.KnownSubTypeAttribute(typeof(Database), ObjectType.Database)] public interface IWikiDatabase : IObject { - } } diff --git a/Test/Notion.IntegrationTests/DatabasesClientTests.cs b/Test/Notion.IntegrationTests/DatabasesClientTests.cs index 069f5be8..e2255339 100644 --- a/Test/Notion.IntegrationTests/DatabasesClientTests.cs +++ b/Test/Notion.IntegrationTests/DatabasesClientTests.cs @@ -31,7 +31,6 @@ public async Task QueryDatabase() // Arrange var createdDatabase = await CreateDatabaseWithAPageAsync(); - // Act var response = await Client.Databases.QueryAsync(createdDatabase.Id, new DatabasesQueryParameters()); From c932a4264b89c0e402f0a1755d93069eab07fafb Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:14:41 +0530 Subject: [PATCH 06/71] Add support for create token endpoint --- Src/Notion.Client/Api/ApiEndpoints.cs | 5 +++ .../Authentication/AuthenticationClient.cs | 12 +++++++ .../CreateToken/AuthenticationClient.cs | 21 +++++++++++ .../CreateToken/Request/CreateTokenRequest.cs | 13 +++++++ .../CreateToken/Request/ExternalAccount.cs | 22 ++++++++++++ .../Request/ICreateTokenBodyParameters.cs | 35 +++++++++++++++++++ .../Response/CreateTokenResponse.cs | 31 ++++++++++++++++ .../Authentication/IAuthenticationClient.cs | 22 ++++++++++++ Src/Notion.Client/NotionClient.cs | 8 ++++- Src/Notion.Client/NotionClientFactory.cs | 5 +-- 10 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 Src/Notion.Client/Api/Authentication/AuthenticationClient.cs create mode 100644 Src/Notion.Client/Api/Authentication/CreateToken/AuthenticationClient.cs create mode 100644 Src/Notion.Client/Api/Authentication/CreateToken/Request/CreateTokenRequest.cs create mode 100644 Src/Notion.Client/Api/Authentication/CreateToken/Request/ExternalAccount.cs create mode 100644 Src/Notion.Client/Api/Authentication/CreateToken/Request/ICreateTokenBodyParameters.cs create mode 100644 Src/Notion.Client/Api/Authentication/CreateToken/Response/CreateTokenResponse.cs create mode 100644 Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs diff --git a/Src/Notion.Client/Api/ApiEndpoints.cs b/Src/Notion.Client/Api/ApiEndpoints.cs index a6434683..4ed80bb1 100644 --- a/Src/Notion.Client/Api/ApiEndpoints.cs +++ b/Src/Notion.Client/Api/ApiEndpoints.cs @@ -131,5 +131,10 @@ public static string Create() return "/v1/comments"; } } + + public static class AuthenticationUrls + { + public static string CreateToken() => "/v1/oauth/token"; + } } } diff --git a/Src/Notion.Client/Api/Authentication/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/AuthenticationClient.cs new file mode 100644 index 00000000..ab141200 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/AuthenticationClient.cs @@ -0,0 +1,12 @@ +namespace Notion.Client +{ + public sealed partial class AuthenticationClient : IAuthenticationClient + { + private readonly IRestClient _client; + + public AuthenticationClient(IRestClient restClient) + { + _client = restClient; + } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/CreateToken/AuthenticationClient.cs new file mode 100644 index 00000000..23632632 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/AuthenticationClient.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class AuthenticationClient + { + public async Task CreateTokenAsync( + CreateTokenRequest createTokenRequest, + CancellationToken cancellationToken = default) + { + var body = (ICreateTokenBodyParameters)createTokenRequest; + + return await _client.PostAsync( + ApiEndpoints.AuthenticationUrls.CreateToken(), + body, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/Request/CreateTokenRequest.cs b/Src/Notion.Client/Api/Authentication/CreateToken/Request/CreateTokenRequest.cs new file mode 100644 index 00000000..3fe8ffde --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/Request/CreateTokenRequest.cs @@ -0,0 +1,13 @@ +namespace Notion.Client +{ + public class CreateTokenRequest : ICreateTokenBodyParameters + { + public string GrantType => "authorization_code"; + + public string Code { get; set; } + + public string RedirectUri { get; set; } + + public ExternalAccount ExternalAccount { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/Request/ExternalAccount.cs b/Src/Notion.Client/Api/Authentication/CreateToken/Request/ExternalAccount.cs new file mode 100644 index 00000000..432d709f --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/Request/ExternalAccount.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + /// + /// External account info + /// + public class ExternalAccount + { + /// + /// External account key + /// + [JsonProperty("key")] + public string Key { get; set; } + + /// + /// External account name + /// + [JsonProperty("name")] + public string Name { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/Request/ICreateTokenBodyParameters.cs b/Src/Notion.Client/Api/Authentication/CreateToken/Request/ICreateTokenBodyParameters.cs new file mode 100644 index 00000000..e896e71a --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/Request/ICreateTokenBodyParameters.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface ICreateTokenBodyParameters + { + /// + /// A constant string: "authorization_code". + /// + [JsonProperty("grant_type")] + string GrantType { get; } + + /// + /// A unique random code that Notion generates to authenticate with your service, + /// generated when a user initiates the OAuth flow. + /// + [JsonProperty("code")] + string Code { get; set; } + + /// + /// The "redirect_uri" that was provided in the OAuth Domain & URI section + /// of the integration's Authorization settings. Do not include this field if a + /// "redirect_uri" query param was not included in the Authorization URL + /// provided to users. In most cases, this field is required. + /// + [JsonProperty("redirect_uri")] + string RedirectUri { get; set; } + + /// + /// External account details + /// + [JsonProperty("external_account")] + ExternalAccount ExternalAccount { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/Response/CreateTokenResponse.cs b/Src/Notion.Client/Api/Authentication/CreateToken/Response/CreateTokenResponse.cs new file mode 100644 index 00000000..fbbe7a7a --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/Response/CreateTokenResponse.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CreateTokenResponse + { + [JsonProperty("access_token")] + public string AccessToken { get; set; } + + [JsonProperty("token_type")] + public string TokenType { get; set; } = "bearer"; + + [JsonProperty("bot_id")] + public string BotId { get; set; } + + [JsonProperty("duplicated_template_id")] + public string DuplicatedTemplateId { get; set; } + + [JsonProperty("owner")] + public IBotOwner Owner { get; set; } + + [JsonProperty("workspace_icon")] + public string WorkspaceIcon { get; set; } + + [JsonProperty("workspace_id")] + public string WorkspaceId { get; set; } + + [JsonProperty("workspace_name")] + public string WorkspaceName { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs new file mode 100644 index 00000000..0b4ebeb8 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs @@ -0,0 +1,22 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + /// + /// Authentication client + /// + public interface IAuthenticationClient + { + /// + /// Creates an access token that a third-party service can use to authenticate with Notion. + /// + /// + /// + /// + Task CreateTokenAsync( + CreateTokenRequest createTokenRequest, + CancellationToken cancellationToken = default + ); + } +} diff --git a/Src/Notion.Client/NotionClient.cs b/Src/Notion.Client/NotionClient.cs index ca0c1334..2fc6f479 100644 --- a/Src/Notion.Client/NotionClient.cs +++ b/Src/Notion.Client/NotionClient.cs @@ -5,6 +5,8 @@ namespace Notion.Client [SuppressMessage("ReSharper", "UnusedMemberInSuper.Global")] public interface INotionClient { + IAuthenticationClient AuthenticationClient { get; } + IUsersClient Users { get; } IDatabasesClient Databases { get; } @@ -29,7 +31,8 @@ public NotionClient( IPagesClient pages, ISearchClient search, ICommentsClient comments, - IBlocksClient blocks) + IBlocksClient blocks, + IAuthenticationClient authenticationClient) { RestClient = restClient; Users = users; @@ -38,8 +41,11 @@ public NotionClient( Search = search; Comments = comments; Blocks = blocks; + AuthenticationClient = authenticationClient; } + public IAuthenticationClient AuthenticationClient { get; } + public IUsersClient Users { get; } public IDatabasesClient Databases { get; } diff --git a/Src/Notion.Client/NotionClientFactory.cs b/Src/Notion.Client/NotionClientFactory.cs index e82ba2de..e66f0781 100644 --- a/Src/Notion.Client/NotionClientFactory.cs +++ b/Src/Notion.Client/NotionClientFactory.cs @@ -12,8 +12,9 @@ public static NotionClient Create(ClientOptions options) , new DatabasesClient(restClient) , new PagesClient(restClient) , new SearchClient(restClient) - , blocks: new BlocksClient(restClient) - , comments: new CommentsClient(restClient) + , new CommentsClient(restClient) + , new BlocksClient(restClient) + , new AuthenticationClient(restClient) ); } } From 68ddcd1b22e6296825e52fbcb602319ae65b98c6 Mon Sep 17 00:00:00 2001 From: Kashif Soofi Date: Fri, 13 Oct 2023 12:30:37 +0100 Subject: [PATCH 07/71] #390: Add missing property in paginated response --- Src/Notion.Client/Models/PaginatedList.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Src/Notion.Client/Models/PaginatedList.cs b/Src/Notion.Client/Models/PaginatedList.cs index e126125a..d0b1107f 100644 --- a/Src/Notion.Client/Models/PaginatedList.cs +++ b/Src/Notion.Client/Models/PaginatedList.cs @@ -25,5 +25,8 @@ public class PaginatedList [JsonProperty("next_cursor")] public string NextCursor { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } } } From 9ada4418e846c53c0978ef585687fa26a5abf9e7 Mon Sep 17 00:00:00 2001 From: Kashif Soofi Date: Fri, 13 Oct 2023 20:20:08 +0100 Subject: [PATCH 08/71] #390: Remove Type from RetrieveCommentsResponse --- Src/Notion.Client/Api/Comments/Retrieve/Response/Comments.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Src/Notion.Client/Api/Comments/Retrieve/Response/Comments.cs b/Src/Notion.Client/Api/Comments/Retrieve/Response/Comments.cs index c89182a5..8b3b43b3 100644 --- a/Src/Notion.Client/Api/Comments/Retrieve/Response/Comments.cs +++ b/Src/Notion.Client/Api/Comments/Retrieve/Response/Comments.cs @@ -5,9 +5,6 @@ namespace Notion.Client { public class RetrieveCommentsResponse : PaginatedList { - [JsonProperty("type")] - public string Type { get; set; } - [JsonProperty("comment")] public Dictionary Comment { get; set; } } From e66a83fa422a4298344788bba9b805ccc276aa93 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:03:37 +0530 Subject: [PATCH 09/71] Add block property in paginated block children response --- Src/Notion.Client/Api/Blocks/BlocksClient.cs | 24 +------------ Src/Notion.Client/Api/Blocks/IBlocksClient.cs | 19 +++++++--- .../BlocksRetrieveChildrenParameters.cs | 9 ----- .../IBlocksRetrieveChildrenQueryParameters.cs | 6 ---- .../Blocks/RetrieveChildren/BlocksClient.cs | 36 +++++++++++++++++++ .../Request/BlockRetrieveChildrenRequest.cs | 13 +++++++ .../IBlockRetrieveChildrenPathParameters.cs | 7 ++++ .../IBlockRetrieveChildrenQueryParameters.cs | 6 ++++ .../Response/RetrieveChildrenResponse.cs | 11 ++++++ .../IBlocksClientTests.cs | 14 ++++++-- Test/Notion.UnitTests/BlocksClientTests.cs | 5 ++- 11 files changed, 104 insertions(+), 46 deletions(-) delete mode 100644 Src/Notion.Client/Api/Blocks/RequestParams/BlocksRetrieveChildrenParameters.cs delete mode 100644 Src/Notion.Client/Api/Blocks/RequestParams/IBlocksRetrieveChildrenQueryParameters.cs create mode 100644 Src/Notion.Client/Api/Blocks/RetrieveChildren/BlocksClient.cs create mode 100644 Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/BlockRetrieveChildrenRequest.cs create mode 100644 Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenPathParameters.cs create mode 100644 Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenQueryParameters.cs create mode 100644 Src/Notion.Client/Api/Blocks/RetrieveChildren/Response/RetrieveChildrenResponse.cs diff --git a/Src/Notion.Client/Api/Blocks/BlocksClient.cs b/Src/Notion.Client/Api/Blocks/BlocksClient.cs index 068ce969..2c82457f 100644 --- a/Src/Notion.Client/Api/Blocks/BlocksClient.cs +++ b/Src/Notion.Client/Api/Blocks/BlocksClient.cs @@ -6,7 +6,7 @@ namespace Notion.Client { - public class BlocksClient : IBlocksClient + public sealed partial class BlocksClient : IBlocksClient { private readonly IRestClient _client; @@ -15,28 +15,6 @@ public BlocksClient(IRestClient client) _client = client; } - public async Task> RetrieveChildrenAsync( - string blockId, - BlocksRetrieveChildrenParameters parameters = null, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(blockId)) - { - throw new ArgumentNullException(nameof(blockId)); - } - - var url = BlocksApiUrls.RetrieveChildren(blockId); - - var queryParameters = (IBlocksRetrieveChildrenQueryParameters)parameters; - - var queryParams = new Dictionary - { - { "start_cursor", queryParameters?.StartCursor }, - { "page_size", queryParameters?.PageSize?.ToString() } - }; - - return await _client.GetAsync>(url, queryParams, cancellationToken: cancellationToken); - } - public async Task> AppendChildrenAsync( string blockId, BlocksAppendChildrenParameters parameters = null, CancellationToken cancellationToken = default) diff --git a/Src/Notion.Client/Api/Blocks/IBlocksClient.cs b/Src/Notion.Client/Api/Blocks/IBlocksClient.cs index 252a218d..59dac072 100644 --- a/Src/Notion.Client/Api/Blocks/IBlocksClient.cs +++ b/Src/Notion.Client/Api/Blocks/IBlocksClient.cs @@ -18,11 +18,22 @@ public interface IBlocksClient /// /// /// Block - Task UpdateAsync(string blockId, IUpdateBlock updateBlock, CancellationToken cancellationToken = default); + Task UpdateAsync(string blockId, IUpdateBlock updateBlock, + CancellationToken cancellationToken = default); - Task> RetrieveChildrenAsync( - string blockId, - BlocksRetrieveChildrenParameters parameters = null, CancellationToken cancellationToken = default); + /// + /// Returns a paginated array of child block objects contained in the block using the ID specified. + ///
+ /// In order to receive a complete representation of a block, you may need to recursively retrieve the + /// block children of child blocks. + ///
+ /// + /// + /// + Task RetrieveChildrenAsync( + BlockRetrieveChildrenRequest request, + CancellationToken cancellationToken = default + ); /// /// Creates and appends new children blocks to the parent block_id specified. diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksRetrieveChildrenParameters.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksRetrieveChildrenParameters.cs deleted file mode 100644 index 30f9c17d..00000000 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksRetrieveChildrenParameters.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Notion.Client -{ - public class BlocksRetrieveChildrenParameters : IBlocksRetrieveChildrenQueryParameters - { - public string StartCursor { get; set; } - - public int? PageSize { get; set; } - } -} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksRetrieveChildrenQueryParameters.cs b/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksRetrieveChildrenQueryParameters.cs deleted file mode 100644 index 5c85e06a..00000000 --- a/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksRetrieveChildrenQueryParameters.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Notion.Client -{ - public interface IBlocksRetrieveChildrenQueryParameters : IPaginationParameters - { - } -} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/BlocksClient.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/BlocksClient.cs new file mode 100644 index 00000000..c91f5c6d --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/BlocksClient.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class BlocksClient + { + public async Task RetrieveChildrenAsync( + BlockRetrieveChildrenRequest request, + CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(request.BlockId)) + { + throw new ArgumentNullException(nameof(request.BlockId)); + } + + var url = ApiEndpoints.BlocksApiUrls.RetrieveChildren(request.BlockId); + + var queryParameters = (IBlockRetrieveChildrenQueryParameters)request; + + var queryParams = new Dictionary + { + { "start_cursor", queryParameters?.StartCursor }, + { "page_size", queryParameters?.PageSize?.ToString() } + }; + + return await _client.GetAsync( + url, + queryParams, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/BlockRetrieveChildrenRequest.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/BlockRetrieveChildrenRequest.cs new file mode 100644 index 00000000..c0e87f3e --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/BlockRetrieveChildrenRequest.cs @@ -0,0 +1,13 @@ +namespace Notion.Client +{ + public class BlockRetrieveChildrenRequest : + IBlockRetrieveChildrenQueryParameters, + IBlockRetrieveChildrenPathParameters + { + public string StartCursor { get; set; } + + public int? PageSize { get; set; } + + public string BlockId { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenPathParameters.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenPathParameters.cs new file mode 100644 index 00000000..c23c2e90 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenPathParameters.cs @@ -0,0 +1,7 @@ +namespace Notion.Client +{ + public interface IBlockRetrieveChildrenPathParameters + { + public string BlockId { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenQueryParameters.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenQueryParameters.cs new file mode 100644 index 00000000..68c7de32 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenQueryParameters.cs @@ -0,0 +1,6 @@ +namespace Notion.Client +{ + public interface IBlockRetrieveChildrenQueryParameters : IPaginationParameters + { + } +} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/Response/RetrieveChildrenResponse.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Response/RetrieveChildrenResponse.cs new file mode 100644 index 00000000..0b34619f --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Response/RetrieveChildrenResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class RetrieveChildrenResponse : PaginatedList + { + [JsonProperty("block")] + public Dictionary Block { get; set; } + } +} diff --git a/Test/Notion.IntegrationTests/IBlocksClientTests.cs b/Test/Notion.IntegrationTests/IBlocksClientTests.cs index 3f616cfa..9435060c 100644 --- a/Test/Notion.IntegrationTests/IBlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/IBlocksClientTests.cs @@ -68,7 +68,10 @@ public async Task UpdateBlockAsync_UpdatesGivenBlock() var blockId = blocks.Results.First().Id; await Client.Blocks.UpdateAsync(blockId, new BreadcrumbUpdateBlock()); - blocks = await Client.Blocks.RetrieveChildrenAsync(page.Id); + blocks = await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest + { + BlockId = page.Id + }); blocks.Results.Should().HaveCount(1); // cleanup @@ -121,7 +124,10 @@ public async Task UpdateAsync_UpdatesGivenBlock( var blockId = blocks.Results.First().Id; await Client.Blocks.UpdateAsync(blockId, updateBlock); - blocks = await Client.Blocks.RetrieveChildrenAsync(page.Id); + blocks = await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest + { + BlockId = page.Id + }); blocks.Results.Should().HaveCount(1); var updatedBlock = blocks.Results.First(); @@ -443,7 +449,9 @@ private static IEnumerable BlockData() var tableBlock = block.Should().NotBeNull().And.BeOfType().Subject; tableBlock.HasChildren.Should().BeTrue(); - var children = client.Blocks.RetrieveChildrenAsync(tableBlock.Id).GetAwaiter().GetResult(); + var children = client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest{ + BlockId = tableBlock.Id + }).GetAwaiter().GetResult(); children.Results.Should().ContainSingle() .Subject.Should().BeOfType() diff --git a/Test/Notion.UnitTests/BlocksClientTests.cs b/Test/Notion.UnitTests/BlocksClientTests.cs index 0d6b2c01..a1432050 100644 --- a/Test/Notion.UnitTests/BlocksClientTests.cs +++ b/Test/Notion.UnitTests/BlocksClientTests.cs @@ -34,7 +34,10 @@ public async Task RetrieveBlockChildren() ); // Act - var childrenResult = await _client.RetrieveChildrenAsync(blockId, new BlocksRetrieveChildrenParameters()); + var childrenResult = await _client.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest + { + BlockId = blockId + }); // Assert var children = childrenResult.Results; From 0d3f322fcf802f7b21ebc53e207693db1ac1288a Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:37:33 +0530 Subject: [PATCH 10/71] Add block property in Append children api response --- .../Api/Blocks/AppendChildren/BlocksClient.cs | 25 ++++++++++++++++ .../Request/BlockAppendChildrenRequest.cs} | 4 ++- .../IBlockAppendChildrenBodyParameters.cs} | 2 +- .../IBlockAppendChildrenPathParameters.cs | 7 +++++ .../Response/AppendChildrenResponse.cs | 11 +++++++ Src/Notion.Client/Api/Blocks/BlocksClient.cs | 16 ---------- Src/Notion.Client/Api/Blocks/IBlocksClient.cs | 11 +++---- .../IBlocksClientTests.cs | 29 ++++++++++--------- Test/Notion.UnitTests/BlocksClientTests.cs | 5 ++-- 9 files changed, 72 insertions(+), 38 deletions(-) create mode 100644 Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs rename Src/Notion.Client/Api/Blocks/{RequestParams/BlocksAppendChildrenParameters.cs => AppendChildren/Request/BlockAppendChildrenRequest.cs} (52%) rename Src/Notion.Client/Api/Blocks/{RequestParams/IBlocksAppendChildrenBodyParameters.cs => AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs} (88%) create mode 100644 Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenPathParameters.cs create mode 100644 Src/Notion.Client/Api/Blocks/AppendChildren/Response/AppendChildrenResponse.cs diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs new file mode 100644 index 00000000..66d9e1ea --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class BlocksClient + { + public async Task AppendChildrenAsync( + BlockAppendChildrenRequest request, + CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(request.BlockId)) + { + throw new ArgumentNullException(nameof(request.BlockId)); + } + + var url = ApiEndpoints.BlocksApiUrls.AppendChildren(request.BlockId); + + var body = (IBlockAppendChildrenBodyParameters)request; + + return await _client.PatchAsync(url, body, cancellationToken: cancellationToken); + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksAppendChildrenParameters.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs similarity index 52% rename from Src/Notion.Client/Api/Blocks/RequestParams/BlocksAppendChildrenParameters.cs rename to Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs index 0d5574dd..580d164a 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksAppendChildrenParameters.cs +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs @@ -2,10 +2,12 @@ namespace Notion.Client { - public class BlocksAppendChildrenParameters : IBlocksAppendChildrenBodyParameters + public class BlockAppendChildrenRequest : IBlockAppendChildrenBodyParameters, IBlockAppendChildrenPathParameters { public IEnumerable Children { get; set; } public string After { get; set; } + + public string BlockId { get; set; } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksAppendChildrenBodyParameters.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs similarity index 88% rename from Src/Notion.Client/Api/Blocks/RequestParams/IBlocksAppendChildrenBodyParameters.cs rename to Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs index ef1321aa..3d8650aa 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksAppendChildrenBodyParameters.cs +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs @@ -4,7 +4,7 @@ namespace Notion.Client { // TODO: need an input version of Block - public interface IBlocksAppendChildrenBodyParameters + public interface IBlockAppendChildrenBodyParameters { [JsonProperty("children")] IEnumerable Children { get; set; } diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenPathParameters.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenPathParameters.cs new file mode 100644 index 00000000..350382f6 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenPathParameters.cs @@ -0,0 +1,7 @@ +namespace Notion.Client +{ + public interface IBlockAppendChildrenPathParameters + { + public string BlockId { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Response/AppendChildrenResponse.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Response/AppendChildrenResponse.cs new file mode 100644 index 00000000..88e04ad5 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Response/AppendChildrenResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class AppendChildrenResponse : PaginatedList + { + [JsonProperty("block")] + public Dictionary Block { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/BlocksClient.cs b/Src/Notion.Client/Api/Blocks/BlocksClient.cs index 2c82457f..92713a78 100644 --- a/Src/Notion.Client/Api/Blocks/BlocksClient.cs +++ b/Src/Notion.Client/Api/Blocks/BlocksClient.cs @@ -15,22 +15,6 @@ public BlocksClient(IRestClient client) _client = client; } - public async Task> AppendChildrenAsync( - string blockId, - BlocksAppendChildrenParameters parameters = null, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(blockId)) - { - throw new ArgumentNullException(nameof(blockId)); - } - - var url = BlocksApiUrls.AppendChildren(blockId); - - var body = (IBlocksAppendChildrenBodyParameters)parameters; - - return await _client.PatchAsync>(url, body, cancellationToken: cancellationToken); - } - public async Task RetrieveAsync(string blockId, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(blockId)) diff --git a/Src/Notion.Client/Api/Blocks/IBlocksClient.cs b/Src/Notion.Client/Api/Blocks/IBlocksClient.cs index 59dac072..fec1b9f5 100644 --- a/Src/Notion.Client/Api/Blocks/IBlocksClient.cs +++ b/Src/Notion.Client/Api/Blocks/IBlocksClient.cs @@ -38,12 +38,13 @@ Task RetrieveChildrenAsync( /// /// Creates and appends new children blocks to the parent block_id specified. /// - /// Identifier for a block - /// + /// + /// /// A paginated list of newly created first level children block objects. - Task> AppendChildrenAsync( - string blockId, - BlocksAppendChildrenParameters parameters = null, CancellationToken cancellationToken = default); + Task AppendChildrenAsync( + BlockAppendChildrenRequest request, + CancellationToken cancellationToken = default + ); /// /// Sets a Block object, including page blocks, to archived: true using the ID specified. diff --git a/Test/Notion.IntegrationTests/IBlocksClientTests.cs b/Test/Notion.IntegrationTests/IBlocksClientTests.cs index 9435060c..24e28da7 100644 --- a/Test/Notion.IntegrationTests/IBlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/IBlocksClientTests.cs @@ -20,9 +20,9 @@ public async Task AppendChildrenAsync_AppendsBlocksGivenBlocks() ); var blocks = await Client.Blocks.AppendChildrenAsync( - page.Id, - new BlocksAppendChildrenParameters + new BlockAppendChildrenRequest { + BlockId = page.Id, Children = new List { new BreadcrumbBlock { Breadcrumb = new BreadcrumbBlock.Data() }, @@ -58,9 +58,9 @@ public async Task UpdateBlockAsync_UpdatesGivenBlock() ); var blocks = await Client.Blocks.AppendChildrenAsync( - page.Id, - new BlocksAppendChildrenParameters + new BlockAppendChildrenRequest { + BlockId = page.Id, Children = new List { new BreadcrumbBlock { Breadcrumb = new BreadcrumbBlock.Data() } } } ); @@ -68,11 +68,11 @@ public async Task UpdateBlockAsync_UpdatesGivenBlock() var blockId = blocks.Results.First().Id; await Client.Blocks.UpdateAsync(blockId, new BreadcrumbUpdateBlock()); - blocks = await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest + var updatedBlocks = await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest { BlockId = page.Id }); - blocks.Results.Should().HaveCount(1); + updatedBlocks.Results.Should().HaveCount(1); // cleanup await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); @@ -88,9 +88,9 @@ public async Task DeleteAsync_DeleteBlockWithGivenId() ); var blocks = await Client.Blocks.AppendChildrenAsync( - page.Id, - new BlocksAppendChildrenParameters + new BlockAppendChildrenRequest { + BlockId = page.Id, Children = new List { new DividerBlock { Divider = new DividerBlock.Data() }, @@ -117,20 +117,23 @@ public async Task UpdateAsync_UpdatesGivenBlock( ); var blocks = await Client.Blocks.AppendChildrenAsync( - page.Id, - new BlocksAppendChildrenParameters { Children = new List { block } } + new BlockAppendChildrenRequest + { + BlockId = page.Id, + Children = new List { block } + } ); var blockId = blocks.Results.First().Id; await Client.Blocks.UpdateAsync(blockId, updateBlock); - blocks = await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest + var updatedBlocks = await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest { BlockId = page.Id }); - blocks.Results.Should().HaveCount(1); + updatedBlocks.Results.Should().HaveCount(1); - var updatedBlock = blocks.Results.First(); + var updatedBlock = updatedBlocks.Results.First(); assert.Invoke(updatedBlock, Client); diff --git a/Test/Notion.UnitTests/BlocksClientTests.cs b/Test/Notion.UnitTests/BlocksClientTests.cs index a1432050..600e0bf4 100644 --- a/Test/Notion.UnitTests/BlocksClientTests.cs +++ b/Test/Notion.UnitTests/BlocksClientTests.cs @@ -60,8 +60,9 @@ public async Task AppendBlockChildren() .WithBody(jsonData) ); - var parameters = new BlocksAppendChildrenParameters + var request = new BlockAppendChildrenRequest { + BlockId = blockId, Children = new List { new HeadingTwoBlock @@ -100,7 +101,7 @@ public async Task AppendBlockChildren() }; // Act - var blocksResult = await _client.AppendChildrenAsync(blockId, parameters); + var blocksResult = await _client.AppendChildrenAsync(request); // Assert var blocks = blocksResult.Results; From 37a7125afac4c5b16a64d8126a3980b2e142e4fe Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:40:07 +0530 Subject: [PATCH 11/71] Add database property in DatabaseQuery response --- .../Api/Databases/Query/Response/DatabaseQueryResponse.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs b/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs index 73968c38..5b7c9ec6 100644 --- a/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs +++ b/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs @@ -1,7 +1,12 @@ -namespace Notion.Client +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client { // ReSharper disable once ClassNeverInstantiated.Global public class DatabaseQueryResponse : PaginatedList { + [JsonProperty("database")] + public Dictionary Database { get; set; } } } From 063723dd96d5120ec7b53a5a99644407494b35a9 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sat, 14 Oct 2023 23:51:17 +0530 Subject: [PATCH 12/71] Add PageOrDatabase property in search response --- Src/Notion.Client/Api/Search/ISearchClient.cs | 8 ++++++-- .../{Parameters => Request}/ISearchBodyParameters.cs | 0 .../Search/{Parameters => Request}/SearchDirection.cs | 0 .../Search/{Parameters => Request}/SearchFilter.cs | 0 .../{Parameters => Request}/SearchObjectType.cs | 0 .../SearchParameters.cs => Request/SearchRequest.cs} | 2 +- .../Api/Search/{Parameters => Request}/SearchSort.cs | 0 .../Api/Search/Response/SearchResponse.cs | 11 +++++++++++ Src/Notion.Client/Api/Search/SearchClient.cs | 10 ++++++---- Test/Notion.UnitTests/SearchClientTest.cs | 2 +- 10 files changed, 25 insertions(+), 8 deletions(-) rename Src/Notion.Client/Api/Search/{Parameters => Request}/ISearchBodyParameters.cs (100%) rename Src/Notion.Client/Api/Search/{Parameters => Request}/SearchDirection.cs (100%) rename Src/Notion.Client/Api/Search/{Parameters => Request}/SearchFilter.cs (100%) rename Src/Notion.Client/Api/Search/{Parameters => Request}/SearchObjectType.cs (100%) rename Src/Notion.Client/Api/Search/{Parameters/SearchParameters.cs => Request/SearchRequest.cs} (82%) rename Src/Notion.Client/Api/Search/{Parameters => Request}/SearchSort.cs (100%) create mode 100644 Src/Notion.Client/Api/Search/Response/SearchResponse.cs diff --git a/Src/Notion.Client/Api/Search/ISearchClient.cs b/Src/Notion.Client/Api/Search/ISearchClient.cs index a1e05990..accee1e1 100644 --- a/Src/Notion.Client/Api/Search/ISearchClient.cs +++ b/Src/Notion.Client/Api/Search/ISearchClient.cs @@ -11,10 +11,14 @@ public interface ISearchClient /// Searches all original pages, databases, and child pages/databases that are shared with the integration. /// It will not return linked databases, since these duplicate their source databases. /// - /// Search filters and body parameters + /// Search filters and body parameters + /// /// /// /// - Task> SearchAsync(SearchParameters parameters, CancellationToken cancellationToken = default); + Task SearchAsync( + SearchRequest request, + CancellationToken cancellationToken = default + ); } } diff --git a/Src/Notion.Client/Api/Search/Parameters/ISearchBodyParameters.cs b/Src/Notion.Client/Api/Search/Request/ISearchBodyParameters.cs similarity index 100% rename from Src/Notion.Client/Api/Search/Parameters/ISearchBodyParameters.cs rename to Src/Notion.Client/Api/Search/Request/ISearchBodyParameters.cs diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchDirection.cs b/Src/Notion.Client/Api/Search/Request/SearchDirection.cs similarity index 100% rename from Src/Notion.Client/Api/Search/Parameters/SearchDirection.cs rename to Src/Notion.Client/Api/Search/Request/SearchDirection.cs diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchFilter.cs b/Src/Notion.Client/Api/Search/Request/SearchFilter.cs similarity index 100% rename from Src/Notion.Client/Api/Search/Parameters/SearchFilter.cs rename to Src/Notion.Client/Api/Search/Request/SearchFilter.cs diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchObjectType.cs b/Src/Notion.Client/Api/Search/Request/SearchObjectType.cs similarity index 100% rename from Src/Notion.Client/Api/Search/Parameters/SearchObjectType.cs rename to Src/Notion.Client/Api/Search/Request/SearchObjectType.cs diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchParameters.cs b/Src/Notion.Client/Api/Search/Request/SearchRequest.cs similarity index 82% rename from Src/Notion.Client/Api/Search/Parameters/SearchParameters.cs rename to Src/Notion.Client/Api/Search/Request/SearchRequest.cs index e1c8ced8..a9ac887f 100644 --- a/Src/Notion.Client/Api/Search/Parameters/SearchParameters.cs +++ b/Src/Notion.Client/Api/Search/Request/SearchRequest.cs @@ -1,6 +1,6 @@ namespace Notion.Client { - public class SearchParameters : ISearchBodyParameters + public class SearchRequest : ISearchBodyParameters { public string Query { get; set; } diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchSort.cs b/Src/Notion.Client/Api/Search/Request/SearchSort.cs similarity index 100% rename from Src/Notion.Client/Api/Search/Parameters/SearchSort.cs rename to Src/Notion.Client/Api/Search/Request/SearchSort.cs diff --git a/Src/Notion.Client/Api/Search/Response/SearchResponse.cs b/Src/Notion.Client/Api/Search/Response/SearchResponse.cs new file mode 100644 index 00000000..2be0e2f1 --- /dev/null +++ b/Src/Notion.Client/Api/Search/Response/SearchResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class SearchResponse : PaginatedList + { + [JsonProperty("page_or_database")] + public Dictionary PageOrDatabase { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Search/SearchClient.cs b/Src/Notion.Client/Api/Search/SearchClient.cs index cebe6aa3..492e0940 100644 --- a/Src/Notion.Client/Api/Search/SearchClient.cs +++ b/Src/Notion.Client/Api/Search/SearchClient.cs @@ -4,7 +4,7 @@ namespace Notion.Client { - public class SearchClient : ISearchClient + public sealed class SearchClient : ISearchClient { private readonly IRestClient _client; @@ -13,13 +13,15 @@ public SearchClient(IRestClient client) _client = client; } - public async Task> SearchAsync(SearchParameters parameters, CancellationToken cancellationToken = default) + public async Task SearchAsync( + SearchRequest request, + CancellationToken cancellationToken = default) { var url = SearchApiUrls.Search(); - var body = (ISearchBodyParameters)parameters; + var body = (ISearchBodyParameters)request; - return await _client.PostAsync>(url, body, cancellationToken: cancellationToken); + return await _client.PostAsync(url, body, cancellationToken: cancellationToken); } } } diff --git a/Test/Notion.UnitTests/SearchClientTest.cs b/Test/Notion.UnitTests/SearchClientTest.cs index 63fb4a94..35765cef 100644 --- a/Test/Notion.UnitTests/SearchClientTest.cs +++ b/Test/Notion.UnitTests/SearchClientTest.cs @@ -30,7 +30,7 @@ public async Task Search() .WithBody(jsonData) ); - var searchParameters = new SearchParameters + var searchParameters = new SearchRequest { Query = "External tasks", Sort = new SearchSort From 535b161fc2d39cae5758a9de0955aa0532722565 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 00:03:34 +0530 Subject: [PATCH 13/71] Add User property to ListUsers response --- Src/Notion.Client/Api/Users/IUsersClient.cs | 18 ++++++++++++++---- ...tUsersParameters.cs => ListUsersRequest.cs} | 2 +- .../Users/List/Response/ListUsersResponse.cs | 11 +++++++++++ .../Api/Users/List/UsersClient.cs | 16 ++++++++++++---- Src/Notion.Client/Api/Users/UsersClient.cs | 5 ----- 5 files changed, 38 insertions(+), 14 deletions(-) rename Src/Notion.Client/Api/Users/List/Request/{ListUsersParameters.cs => ListUsersRequest.cs} (78%) create mode 100644 Src/Notion.Client/Api/Users/List/Response/ListUsersResponse.cs diff --git a/Src/Notion.Client/Api/Users/IUsersClient.cs b/Src/Notion.Client/Api/Users/IUsersClient.cs index 0f9f18ac..bc0c3d8a 100644 --- a/Src/Notion.Client/Api/Users/IUsersClient.cs +++ b/Src/Notion.Client/Api/Users/IUsersClient.cs @@ -19,13 +19,23 @@ public interface IUsersClient /// Returns a paginated list of Users for the workspace. /// The response may contain fewer than page_size of results. /// + /// /// - /// + /// /// - Task> ListAsync(CancellationToken cancellationToken = default); + Task ListAsync(CancellationToken cancellationToken = default); - Task> ListAsync( - ListUsersParameters listUsersParameters, + /// + /// Returns a paginated list of Users for the workspace. + /// The response may contain fewer than page_size of results. + /// + /// + /// + /// + /// + /// + Task ListAsync( + ListUsersRequest listUsersRequest, CancellationToken cancellationToken = default ); diff --git a/Src/Notion.Client/Api/Users/List/Request/ListUsersParameters.cs b/Src/Notion.Client/Api/Users/List/Request/ListUsersRequest.cs similarity index 78% rename from Src/Notion.Client/Api/Users/List/Request/ListUsersParameters.cs rename to Src/Notion.Client/Api/Users/List/Request/ListUsersRequest.cs index 046a0c5b..f3684d09 100644 --- a/Src/Notion.Client/Api/Users/List/Request/ListUsersParameters.cs +++ b/Src/Notion.Client/Api/Users/List/Request/ListUsersRequest.cs @@ -4,7 +4,7 @@ public interface IListUsersQueryParameters : IPaginationParameters { } - public class ListUsersParameters : IListUsersQueryParameters + public class ListUsersRequest : IListUsersQueryParameters { public string StartCursor { get; set; } diff --git a/Src/Notion.Client/Api/Users/List/Response/ListUsersResponse.cs b/Src/Notion.Client/Api/Users/List/Response/ListUsersResponse.cs new file mode 100644 index 00000000..2d30237f --- /dev/null +++ b/Src/Notion.Client/Api/Users/List/Response/ListUsersResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ListUsersResponse : PaginatedList + { + [JsonProperty("user")] + public Dictionary User { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Users/List/UsersClient.cs b/Src/Notion.Client/Api/Users/List/UsersClient.cs index 57c40ea3..ce13633a 100644 --- a/Src/Notion.Client/Api/Users/List/UsersClient.cs +++ b/Src/Notion.Client/Api/Users/List/UsersClient.cs @@ -7,12 +7,20 @@ namespace Notion.Client { public partial class UsersClient { - public async Task> ListAsync( - ListUsersParameters listUsersParameters, + public async Task ListAsync(CancellationToken cancellationToken = default) + { + return await _client.GetAsync( + ApiEndpoints.UsersApiUrls.List(), + cancellationToken: cancellationToken + ); + } + + public async Task ListAsync( + ListUsersRequest listUsersRequest, CancellationToken cancellationToken = default ) { - var queryParameters = (IListUsersQueryParameters)listUsersParameters; + var queryParameters = (IListUsersQueryParameters)listUsersRequest; var queryParams = new Dictionary { @@ -20,7 +28,7 @@ public async Task> ListAsync( { "page_size", queryParameters?.PageSize?.ToString() } }; - return await _client.GetAsync>( + return await _client.GetAsync( ApiEndpoints.UsersApiUrls.List(), queryParams, cancellationToken: cancellationToken diff --git a/Src/Notion.Client/Api/Users/UsersClient.cs b/Src/Notion.Client/Api/Users/UsersClient.cs index f40299ee..24681b53 100644 --- a/Src/Notion.Client/Api/Users/UsersClient.cs +++ b/Src/Notion.Client/Api/Users/UsersClient.cs @@ -18,11 +18,6 @@ public async Task RetrieveAsync(string userId, CancellationToken cancellat return await _client.GetAsync(UsersApiUrls.Retrieve(userId), cancellationToken: cancellationToken); } - public async Task> ListAsync(CancellationToken cancellationToken = default) - { - return await _client.GetAsync>(UsersApiUrls.List(), cancellationToken: cancellationToken); - } - /// /// Retrieves the bot User associated with the API token provided in the authorization header. /// From bb2f97e679379f2e6c3d588b3e4441e82e391b40 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:12:06 +0530 Subject: [PATCH 14/71] Add wrapper class to avoid sending block id as part of request body --- .../Api/Blocks/AppendChildren/BlocksClient.cs | 2 +- .../Request/IBlockAppendChildrenBodyParameters.cs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs index 66d9e1ea..50ce3c61 100644 --- a/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs @@ -17,7 +17,7 @@ public async Task AppendChildrenAsync( var url = ApiEndpoints.BlocksApiUrls.AppendChildren(request.BlockId); - var body = (IBlockAppendChildrenBodyParameters)request; + var body = new BlockAppendChildrenBodyParameters(request); return await _client.PatchAsync(url, body, cancellationToken: cancellationToken); } diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs index 3d8650aa..b476c089 100644 --- a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs @@ -15,4 +15,17 @@ public interface IBlockAppendChildrenBodyParameters [JsonProperty("after")] public string After { get; set; } } + + internal class BlockAppendChildrenBodyParameters : IBlockAppendChildrenBodyParameters + { + public IEnumerable Children { get; set; } + + public string After { get; set; } + + public BlockAppendChildrenBodyParameters(BlockAppendChildrenRequest request) + { + Children = request.Children; + After = request.After; + } + } } From 2c94a50422f77d949567b6ca41bca0dcff1eb06c Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 02:44:05 +0530 Subject: [PATCH 15/71] Adjust CommentsClient Tests --- Test/Notion.IntegrationTests/CommentsClientTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Notion.IntegrationTests/CommentsClientTests.cs b/Test/Notion.IntegrationTests/CommentsClientTests.cs index f2a84d21..0eb56bea 100644 --- a/Test/Notion.IntegrationTests/CommentsClientTests.cs +++ b/Test/Notion.IntegrationTests/CommentsClientTests.cs @@ -78,7 +78,7 @@ public async Task ShouldCreateADiscussionComment() ); // Arrange - Assert.Null(response.Parent); + Assert.NotNull(response.Parent); Assert.NotNull(response.Id); Assert.Equal(comment.DiscussionId, response.DiscussionId); From 8a0d469febfec2b521808ee4b9171b150ef775ed Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 02:44:34 +0530 Subject: [PATCH 16/71] Adjust PageClient tests --- ...IPageClientTests.cs => PageClientTests.cs} | 157 +++++++++++------- 1 file changed, 100 insertions(+), 57 deletions(-) rename Test/Notion.IntegrationTests/{IPageClientTests.cs => PageClientTests.cs} (71%) diff --git a/Test/Notion.IntegrationTests/IPageClientTests.cs b/Test/Notion.IntegrationTests/PageClientTests.cs similarity index 71% rename from Test/Notion.IntegrationTests/IPageClientTests.cs rename to Test/Notion.IntegrationTests/PageClientTests.cs index 84ac493e..1e697c59 100644 --- a/Test/Notion.IntegrationTests/IPageClientTests.cs +++ b/Test/Notion.IntegrationTests/PageClientTests.cs @@ -8,13 +8,69 @@ namespace Notion.IntegrationTests; -public class IPageClientTests : IntegrationTestBase +public class PageClientTests : IntegrationTestBase, IDisposable { + private readonly Page _page; + private readonly Database _database; + + public PageClientTests() + { + // Create a page + _page = Client.Pages.CreateAsync( + PagesCreateParametersBuilder.Create( + new ParentPageInput { PageId = ParentPageId } + ).Build() + ).Result; + + // Create a database + var createDbRequest = new DatabasesCreateParameters + { + Title = new List + { + new RichTextTextInput + { + Text = new Text + { + Content = "Test List", + Link = null + } + } + }, + Properties = new Dictionary + { + { "Name", new TitlePropertySchema { Title = new Dictionary() } }, + { + "TestSelect", + new SelectPropertySchema + { + Select = new OptionWrapper + { + Options = new List + { + new() { Name = "Red" }, + new() { Name = "Blue" } + } + } + } + }, + { "Number", new NumberPropertySchema { Number = new Number { Format = "number" } } } + }, + Parent = new ParentPageInput { PageId = _page.Id } + }; + + _database = Client.Databases.CreateAsync(createDbRequest).GetAwaiter().GetResult(); + } + + public void Dispose() + { + Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }).GetAwaiter().GetResult(); + } + [Fact] public async Task CreateAsync_CreatesANewPage() { var pagesCreateParameters = PagesCreateParametersBuilder - .Create(new DatabaseParentInput { DatabaseId = ParentDatabaseId }) + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) .AddProperty("Name", new TitlePropertyValue { @@ -30,10 +86,10 @@ public async Task CreateAsync_CreatesANewPage() page.Should().NotBeNull(); page.Parent.Should().BeOfType().Which - .DatabaseId.Should().Be(ParentDatabaseId); + .DatabaseId.Should().Be(_database.Id); page.Properties.Should().ContainKey("Name"); - var pageProperty = page.Properties["Name"].Should().BeOfType().Subject; + var pageProperty = page.Properties["Name"].Should().BeOfType().Subject; var titleProperty = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( @@ -44,15 +100,14 @@ var titleProperty }); titleProperty.Results.First().As().Title.PlainText.Should().Be("Test Page Title"); - - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); } [Fact] public async Task Bug_unable_to_create_page_with_select_property() { + // Arrange var pagesCreateParameters = PagesCreateParametersBuilder - .Create(new DatabaseParentInput { DatabaseId = ParentDatabaseId }) + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) .AddProperty("Name", new TitlePropertyValue { @@ -62,37 +117,33 @@ public async Task Bug_unable_to_create_page_with_select_property() } }) .AddProperty("TestSelect", - new SelectPropertyValue { Select = new SelectOption { Id = "dfbfbe65-6f67-4876-9f75-699124334d06" } }) + new SelectPropertyValue { Select = new SelectOption { Name = "Blue" } }) .Build(); + // Act var page = await Client.Pages.CreateAsync(pagesCreateParameters); + // Asserts page.Should().NotBeNull(); page.Parent.Should().BeOfType().Which - .DatabaseId.Should().Be(ParentDatabaseId); + .DatabaseId.Should().Be(_database.Id); page.Properties.Should().ContainKey("Name"); - var pageProperty = page.Properties["Name"].Should().BeOfType().Subject; - - var titleProperty - = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( - new RetrievePropertyItemParameters - { - PageId = page.Id, - PropertyId = pageProperty.Id - }); - - titleProperty.Results.First().As().Title.PlainText.Should().Be("Test Page Title"); + var titlePropertyValue = page.Properties["Name"].Should().BeOfType().Subject; + titlePropertyValue.Title.First().PlainText.Should().Be("Test Page Title"); - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); + page.Properties.Should().ContainKey("TestSelect"); + var selectPropertyValue = page.Properties["TestSelect"].Should().BeOfType().Subject; + selectPropertyValue.Select.Name.Should().Be("Blue"); } [Fact] public async Task Test_RetrievePagePropertyItemAsync() { + // Arrange var pagesCreateParameters = PagesCreateParametersBuilder - .Create(new DatabaseParentInput { DatabaseId = ParentDatabaseId }) + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) .AddProperty("Name", new TitlePropertyValue { @@ -105,12 +156,14 @@ public async Task Test_RetrievePagePropertyItemAsync() var page = await Client.Pages.CreateAsync(pagesCreateParameters); + // Act var property = await Client.Pages.RetrievePagePropertyItemAsync(new RetrievePropertyItemParameters { PageId = page.Id, PropertyId = "title" }); + // Assert property.Should().NotBeNull(); property.Should().BeOfType(); @@ -125,16 +178,14 @@ public async Task Test_RetrievePagePropertyItemAsync() titleProperty.Title.PlainText.Should().Be("Test Page Title"); }); - - // cleanup - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); } [Fact] public async Task Test_UpdatePageProperty_with_date_as_null() { - // setup - add property to db and create a page with the property having a date + // Arrange + // Add property Date property to database const string DatePropertyName = "Test Date Property"; var updateDatabaseParameters = new DatabasesUpdateParameters @@ -149,8 +200,9 @@ public async Task Test_UpdatePageProperty_with_date_as_null() } }; + // Create a page with the property having a date var pagesCreateParameters = PagesCreateParametersBuilder - .Create(new DatabaseParentInput { DatabaseId = ParentDatabaseId }) + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) .AddProperty("Name", new TitlePropertyValue { @@ -170,10 +222,11 @@ public async Task Test_UpdatePageProperty_with_date_as_null() }) .Build(); - await Client.Databases.UpdateAsync(ParentDatabaseId, updateDatabaseParameters); + await Client.Databases.UpdateAsync(_database.Id, updateDatabaseParameters); var page = await Client.Pages.CreateAsync(pagesCreateParameters); + // Act var setDate = (DatePropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( new RetrievePropertyItemParameters { @@ -182,27 +235,28 @@ public async Task Test_UpdatePageProperty_with_date_as_null() } ); + // Assert setDate?.Date?.Start.Should().Be(Convert.ToDateTime("2020-12-08T12:00:00Z")); - // verify - IDictionary testProps = new Dictionary(); - - testProps.Add(DatePropertyName, new DatePropertyValue { Date = null }); + var pageUpdateParameters = new PagesUpdateParameters + { + Properties = new Dictionary + { + { DatePropertyName, new DatePropertyValue { Date = null } } + } + }; - var updatedPage = - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Properties = testProps }); + var updatedPage = await Client.Pages.UpdateAsync(page.Id, pageUpdateParameters); var verifyDate = (DatePropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( new RetrievePropertyItemParameters { PageId = page.Id, PropertyId = updatedPage.Properties[DatePropertyName].Id - }); + } + ); verifyDate?.Date.Should().BeNull(); - - //cleanup - await Client.Blocks.DeleteAsync(page.Id); } [Fact] @@ -210,14 +264,16 @@ public async Task Bug_Unable_To_Parse_NumberPropertyItem() { // Arrange var pagesCreateParameters = PagesCreateParametersBuilder - .Create(new DatabaseParentInput { DatabaseId = ParentDatabaseId }).AddProperty("Name", + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) + .AddProperty("Name", new TitlePropertyValue { Title = new List { new RichTextText { Text = new Text { Content = "Test Page Title" } } } - }).AddProperty("Number", new NumberPropertyValue { Number = 200.00 }).Build(); + }) + .AddProperty("Number", new NumberPropertyValue { Number = 200.00 }).Build(); // Act var page = await Client.Pages.CreateAsync(pagesCreateParameters); @@ -225,7 +281,7 @@ public async Task Bug_Unable_To_Parse_NumberPropertyItem() // Assert Assert.NotNull(page); var pageParent = Assert.IsType(page.Parent); - Assert.Equal(ParentDatabaseId, pageParent.DatabaseId); + Assert.Equal(_database.Id, pageParent.DatabaseId); var titleProperty = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( new RetrievePropertyItemParameters @@ -244,8 +300,6 @@ public async Task Bug_Unable_To_Parse_NumberPropertyItem() }); Assert.Equal(200.00, numberProperty.Number); - - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); } [Fact] @@ -255,19 +309,11 @@ public async Task Bug_exception_when_attempting_to_set_select_property_to_nothin var databaseCreateRequest = new DatabasesCreateParameters { Title = - new List - { - new RichTextTextInput() { Text = new Text { Content = "Test Database" } } - }, - Parent = new ParentPageInput() { PageId = ParentPageId }, + new List { new RichTextTextInput { Text = new Text { Content = "Test Database" } } }, + Parent = new ParentPageInput() { PageId = _page.Id }, Properties = new Dictionary { - { - "title", new TitlePropertySchema - { - Title = new Dictionary() - } - }, + { "title", new TitlePropertySchema { Title = new Dictionary() } }, { "Colors1", new SelectPropertySchema @@ -337,8 +383,5 @@ public async Task Bug_exception_when_attempting_to_set_select_property_to_nothin updatedPage.Properties["Colors1"].As().Select.Name.Should().Be("Blue"); updatedPage.Properties["Colors2"].As().Select.Should().BeNull(); - - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); - await Client.Databases.UpdateAsync(database.Id, new DatabasesUpdateParameters { Archived = true }); } } From 0813c026ae7b126ee24632e34e5a789efefa090b Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 02:52:58 +0530 Subject: [PATCH 17/71] Make use of IAsyncDisposable in PageWithPageParent test --- .../PageWithPageParentTests.cs | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Test/Notion.IntegrationTests/PageWithPageParentTests.cs b/Test/Notion.IntegrationTests/PageWithPageParentTests.cs index cca3356b..3d31e94d 100644 --- a/Test/Notion.IntegrationTests/PageWithPageParentTests.cs +++ b/Test/Notion.IntegrationTests/PageWithPageParentTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -7,12 +8,12 @@ namespace Notion.IntegrationTests; -public class PageWithPageParentTests : IntegrationTestBase +public class PageWithPageParentTests : IntegrationTestBase, IAsyncDisposable { - [Fact] - public async Task Update_Title_Of_Page() + private readonly Page _page; + + public PageWithPageParentTests() { - // Arrange var pagesCreateParameters = PagesCreateParametersBuilder .Create(new ParentPageInput() { PageId = ParentPageId }) .AddProperty("title", @@ -24,8 +25,13 @@ public async Task Update_Title_Of_Page() } }).Build(); - var page = await Client.Pages.CreateAsync(pagesCreateParameters); + _page = Client.Pages.CreateAsync(pagesCreateParameters).GetAwaiter().GetResult(); + } + [Fact] + public async Task Update_Title_Of_Page() + { + // Arrange var updatePage = new PagesUpdateParameters() { Properties = new Dictionary @@ -44,7 +50,7 @@ public async Task Update_Title_Of_Page() }; // Act - var updatedPage = await Client.Pages.UpdateAsync(page.Id, updatePage); + var updatedPage = await Client.Pages.UpdateAsync(_page.Id, updatePage); // Assert var titleProperty = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( @@ -55,9 +61,11 @@ public async Task Update_Title_Of_Page() } ); - Assert.Equal("Page Title Updated", titleProperty.Results.First().As().Title.PlainText); + titleProperty.Results.First().As().Title.PlainText.Should().Be("Page Title Updated"); + } - // Clean Up - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); + public async ValueTask DisposeAsync() + { + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); } } From 66d75a4fa8bff78b5e4b67a4e4a6a699ef18c7ae Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 03:01:19 +0530 Subject: [PATCH 18/71] User IAsyncDisposable in PageClient tests --- Test/Notion.IntegrationTests/PageClientTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Test/Notion.IntegrationTests/PageClientTests.cs b/Test/Notion.IntegrationTests/PageClientTests.cs index 1e697c59..d2843dc4 100644 --- a/Test/Notion.IntegrationTests/PageClientTests.cs +++ b/Test/Notion.IntegrationTests/PageClientTests.cs @@ -8,7 +8,7 @@ namespace Notion.IntegrationTests; -public class PageClientTests : IntegrationTestBase, IDisposable +public class PageClientTests : IntegrationTestBase, IAsyncDisposable { private readonly Page _page; private readonly Database _database; @@ -20,7 +20,7 @@ public PageClientTests() PagesCreateParametersBuilder.Create( new ParentPageInput { PageId = ParentPageId } ).Build() - ).Result; + ).GetAwaiter().GetResult(); // Create a database var createDbRequest = new DatabasesCreateParameters @@ -61,9 +61,9 @@ public PageClientTests() _database = Client.Databases.CreateAsync(createDbRequest).GetAwaiter().GetResult(); } - public void Dispose() + public async ValueTask DisposeAsync() { - Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }).GetAwaiter().GetResult(); + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); } [Fact] From 5231ed1c40c21f43aa89ce0b43b7262e3d7d9c0f Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 03:03:07 +0530 Subject: [PATCH 19/71] Use IAsyncDisposable in Comments client tests --- Test/Notion.IntegrationTests/CommentsClientTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Test/Notion.IntegrationTests/CommentsClientTests.cs b/Test/Notion.IntegrationTests/CommentsClientTests.cs index 0eb56bea..5a6de044 100644 --- a/Test/Notion.IntegrationTests/CommentsClientTests.cs +++ b/Test/Notion.IntegrationTests/CommentsClientTests.cs @@ -7,7 +7,7 @@ namespace Notion.IntegrationTests; -public class CommentsClientTests : IntegrationTestBase, IDisposable +public class CommentsClientTests : IntegrationTestBase, IAsyncDisposable { private readonly Page _page; @@ -17,12 +17,12 @@ public CommentsClientTests() PagesCreateParametersBuilder.Create( new ParentPageInput { PageId = ParentPageId } ).Build() - ).Result; + ).GetAwaiter().GetResult(); } - public void Dispose() + public async ValueTask DisposeAsync() { - Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }).Wait(); + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); } [Fact] From f5db1489c22e846d606775270dbcd3ffb7feb8ac Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 03:09:00 +0530 Subject: [PATCH 20/71] Use IAsyncDisposable in Databases Client tests * use fluent assertions --- .../DatabasesClientTests.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Test/Notion.IntegrationTests/DatabasesClientTests.cs b/Test/Notion.IntegrationTests/DatabasesClientTests.cs index e2255339..cbdfb652 100644 --- a/Test/Notion.IntegrationTests/DatabasesClientTests.cs +++ b/Test/Notion.IntegrationTests/DatabasesClientTests.cs @@ -2,12 +2,13 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using FluentAssertions; using Notion.Client; using Xunit; namespace Notion.IntegrationTests; -public class DatabasesClientTests : IntegrationTestBase, IDisposable +public class DatabasesClientTests : IntegrationTestBase, IAsyncDisposable { private readonly Page _page; @@ -17,12 +18,12 @@ public DatabasesClientTests() PagesCreateParametersBuilder.Create( new ParentPageInput { PageId = ParentPageId } ).Build() - ).Result; + ).GetAwaiter().GetResult(); } - public void Dispose() + public async ValueTask DisposeAsync() { - Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }).Wait(); + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); } [Fact] @@ -35,11 +36,12 @@ public async Task QueryDatabase() var response = await Client.Databases.QueryAsync(createdDatabase.Id, new DatabasesQueryParameters()); // Assert - Assert.NotNull(response.Results); - Assert.Single(response.Results); - var page = response.Results.Cast().First(); - var title = page.Properties["Name"] as TitlePropertyValue; - Assert.Equal("Test Title", (title!.Title.Cast().First()).Text.Content); + response.Results.Should().NotBeNull(); + var page = response.Results.Should().ContainSingle().Subject.As(); + + page.Properties["Name"].As() + .Title.Cast().First() + .Text.Content.Should().Be("Test Title"); } private async Task CreateDatabaseWithAPageAsync() From 50a7b66a51edf6aefd48d47fdb4a844974c1116c Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:21:19 +0530 Subject: [PATCH 21/71] Adjust Breaking BlocksClients tests --- ...cksClientTests.cs => BlocksClientTests.cs} | 58 +++---------------- 1 file changed, 7 insertions(+), 51 deletions(-) rename Test/Notion.IntegrationTests/{IBlocksClientTests.cs => BlocksClientTests.cs} (89%) diff --git a/Test/Notion.IntegrationTests/IBlocksClientTests.cs b/Test/Notion.IntegrationTests/BlocksClientTests.cs similarity index 89% rename from Test/Notion.IntegrationTests/IBlocksClientTests.cs rename to Test/Notion.IntegrationTests/BlocksClientTests.cs index 24e28da7..9b09ec3f 100644 --- a/Test/Notion.IntegrationTests/IBlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/BlocksClientTests.cs @@ -188,12 +188,13 @@ private static IEnumerable BlockData() }, new object[] { - new DividerBlock { Divider = new DividerBlock.Data() }, new DividerUpdateBlock(), new Action( - block => - { - Assert.NotNull(block); - _ = Assert.IsType(block); - }) + new DividerBlock { Divider = new DividerBlock.Data() }, + new DividerUpdateBlock(), + new Action((block, client) => + { + Assert.NotNull(block); + _ = Assert.IsType(block); + }) }, new object[] { @@ -354,51 +355,6 @@ private static IEnumerable BlockData() }) }, new object[] - { - new TemplateBlock - { - Template = new TemplateBlock.Data - { - RichText = new List - { - new RichTextText { Text = new Text { Content = "Test Template" } } - }, - Children = new List - { - new EmbedBlock - { - Embed = new EmbedBlock.Info - { - Url - = "https://zephoria.com/wp-content/uploads/2014/08/online-community.jpg" - } - } - } - } - }, - new TemplateUpdateBlock - { - Template = new TemplateUpdateBlock.Info - { - RichText = new List - { - new RichTextTextInput { Text = new Text { Content = "Test Template 2" } } - } - } - }, - new Action((block, client) => - { - Assert.NotNull(block); - var templateBlock = Assert.IsType(block); - - Assert.Single(templateBlock.Template.RichText); - Assert.Null(templateBlock.Template.Children); - - Assert.Equal("Test Template 2", - templateBlock.Template.RichText.OfType().First().Text.Content); - }) - }, - new object[] { new LinkToPageBlock { From 3b4324befb554d858383e102fde4b4e0f0f905d7 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:28:20 +0530 Subject: [PATCH 22/71] Use IAsyncLifetime to initialize and cleanup common resources --- .../BlocksClientTests.cs | 81 +++++++------------ 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/Test/Notion.IntegrationTests/BlocksClientTests.cs b/Test/Notion.IntegrationTests/BlocksClientTests.cs index 9b09ec3f..71d853b6 100644 --- a/Test/Notion.IntegrationTests/BlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/BlocksClientTests.cs @@ -8,21 +8,31 @@ namespace Notion.IntegrationTests; -public class IBlocksClientTests : IntegrationTestBase +public class IBlocksClientTests : IntegrationTestBase, IAsyncLifetime { - [Fact] - public async Task AppendChildrenAsync_AppendsBlocksGivenBlocks() + private Page _page = null!; + + public async Task InitializeAsync() { - var page = await Client.Pages.CreateAsync( + _page = await Client.Pages.CreateAsync( PagesCreateParametersBuilder.Create( new ParentPageInput { PageId = ParentPageId } ).Build() ); + } + public async Task DisposeAsync() + { + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); + } + + [Fact] + public async Task AppendChildrenAsync_AppendsBlocksGivenBlocks() + { var blocks = await Client.Blocks.AppendChildrenAsync( new BlockAppendChildrenRequest { - BlockId = page.Id, + BlockId = _page.Id, Children = new List { new BreadcrumbBlock { Breadcrumb = new BreadcrumbBlock.Data() }, @@ -43,24 +53,15 @@ public async Task AppendChildrenAsync_AppendsBlocksGivenBlocks() ); blocks.Results.Should().HaveCount(4); - - // cleanup - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); } [Fact] public async Task UpdateBlockAsync_UpdatesGivenBlock() { - var page = await Client.Pages.CreateAsync( - PagesCreateParametersBuilder.Create( - new ParentPageInput { PageId = ParentPageId } - ).Build() - ); - var blocks = await Client.Blocks.AppendChildrenAsync( new BlockAppendChildrenRequest { - BlockId = page.Id, + BlockId = _page.Id, Children = new List { new BreadcrumbBlock { Breadcrumb = new BreadcrumbBlock.Data() } } } ); @@ -68,29 +69,19 @@ public async Task UpdateBlockAsync_UpdatesGivenBlock() var blockId = blocks.Results.First().Id; await Client.Blocks.UpdateAsync(blockId, new BreadcrumbUpdateBlock()); - var updatedBlocks = await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest - { - BlockId = page.Id - }); - updatedBlocks.Results.Should().HaveCount(1); + var updatedBlocks = + await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest { BlockId = _page.Id }); - // cleanup - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); + updatedBlocks.Results.Should().HaveCount(1); } [Fact] public async Task DeleteAsync_DeleteBlockWithGivenId() { - var page = await Client.Pages.CreateAsync( - PagesCreateParametersBuilder.Create( - new ParentPageInput { PageId = ParentPageId } - ).Build() - ); - var blocks = await Client.Blocks.AppendChildrenAsync( new BlockAppendChildrenRequest { - BlockId = page.Id, + BlockId = _page.Id, Children = new List { new DividerBlock { Divider = new DividerBlock.Data() }, @@ -100,9 +91,6 @@ public async Task DeleteAsync_DeleteBlockWithGivenId() ); blocks.Results.Should().HaveCount(2); - - // cleanup - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); } [Theory] @@ -110,16 +98,10 @@ public async Task DeleteAsync_DeleteBlockWithGivenId() public async Task UpdateAsync_UpdatesGivenBlock( IBlock block, IUpdateBlock updateBlock, Action assert) { - var page = await Client.Pages.CreateAsync( - PagesCreateParametersBuilder.Create( - new ParentPageInput { PageId = ParentPageId } - ).Build() - ); - var blocks = await Client.Blocks.AppendChildrenAsync( new BlockAppendChildrenRequest { - BlockId = page.Id, + BlockId = _page.Id, Children = new List { block } } ); @@ -127,18 +109,14 @@ public async Task UpdateAsync_UpdatesGivenBlock( var blockId = blocks.Results.First().Id; await Client.Blocks.UpdateAsync(blockId, updateBlock); - var updatedBlocks = await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest - { - BlockId = page.Id - }); + var updatedBlocks = + await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest { BlockId = _page.Id }); + updatedBlocks.Results.Should().HaveCount(1); var updatedBlock = updatedBlocks.Results.First(); assert.Invoke(updatedBlock, Client); - - // cleanup - await Client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters { Archived = true }); } private static IEnumerable BlockData() @@ -188,8 +166,7 @@ private static IEnumerable BlockData() }, new object[] { - new DividerBlock { Divider = new DividerBlock.Data() }, - new DividerUpdateBlock(), + new DividerBlock { Divider = new DividerBlock.Data() }, new DividerUpdateBlock(), new Action((block, client) => { Assert.NotNull(block); @@ -374,7 +351,7 @@ private static IEnumerable BlockData() var linkToPageBlock = Assert.IsType(block); var pageParent = Assert.IsType(linkToPageBlock.LinkToPage); - + // TODO: Currently the api doesn't allow to update the link_to_page block type // This will change to updated ID once api start to support Assert.Equal(Guid.Parse("533578e3edf14c0a91a9da6b09bac3ee"), Guid.Parse(pageParent.PageId)); @@ -408,9 +385,9 @@ private static IEnumerable BlockData() var tableBlock = block.Should().NotBeNull().And.BeOfType().Subject; tableBlock.HasChildren.Should().BeTrue(); - var children = client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest{ - BlockId = tableBlock.Id - }).GetAwaiter().GetResult(); + var children = client.Blocks + .RetrieveChildrenAsync(new BlockRetrieveChildrenRequest { BlockId = tableBlock.Id }) + .GetAwaiter().GetResult(); children.Results.Should().ContainSingle() .Subject.Should().BeOfType() From c79531a165be24a7400c2ca74ec28506f6361a79 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:30:48 +0530 Subject: [PATCH 23/71] Discard unused lambda args --- .../Notion.IntegrationTests/BlocksClientTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Test/Notion.IntegrationTests/BlocksClientTests.cs b/Test/Notion.IntegrationTests/BlocksClientTests.cs index 71d853b6..4db78d85 100644 --- a/Test/Notion.IntegrationTests/BlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/BlocksClientTests.cs @@ -147,7 +147,7 @@ private static IEnumerable BlockData() } } }, - new Action((block, client) => + new Action((block, _) => { var updatedBlock = (BookmarkBlock)block; Assert.Equal("https://github.com/notion-dotnet/notion-sdk-net", updatedBlock.Bookmark.Url); @@ -158,7 +158,7 @@ private static IEnumerable BlockData() { new EquationBlock { Equation = new EquationBlock.Info { Expression = "e=mc^3" } }, new EquationUpdateBlock { Equation = new EquationUpdateBlock.Info { Expression = "e=mc^2" } }, - new Action((block, client) => + new Action((block, _) => { var updatedBlock = (EquationBlock)block; Assert.Equal("e=mc^2", updatedBlock.Equation.Expression); @@ -195,7 +195,7 @@ private static IEnumerable BlockData() } } }, - new Action((block, client) => + new Action((block, _) => { block.Should().NotBeNull(); @@ -235,7 +235,7 @@ private static IEnumerable BlockData() } } }, - new Action((block, client) => + new Action((block, _) => { Assert.NotNull(block); var calloutBlock = Assert.IsType(block); @@ -265,7 +265,7 @@ private static IEnumerable BlockData() } } }, - new Action((block, client) => + new Action((block, _) => { Assert.NotNull(block); var quoteBlock = Assert.IsType(block); @@ -296,7 +296,7 @@ private static IEnumerable BlockData() } } }, - new Action((block, client) => + new Action((block, _) => { Assert.NotNull(block); var imageBlock = Assert.IsType(block); @@ -322,7 +322,7 @@ private static IEnumerable BlockData() Url = "https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg" } }, - new Action((block, client) => + new Action((block, _) => { Assert.NotNull(block); var embedBlock = Assert.IsType(block); @@ -345,7 +345,7 @@ private static IEnumerable BlockData() { LinkToPage = new ParentPageInput { PageId = "3c357473a28149a488c010d2b245a589" } }, - new Action((block, client) => + new Action((block, _) => { Assert.NotNull(block); var linkToPageBlock = Assert.IsType(block); From 88135e48942a9c7b0fabbc1366666785933acaaa Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:33:02 +0530 Subject: [PATCH 24/71] Use IAsyncLifetime in CommentsClient Tests --- .../CommentsClientTests.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Test/Notion.IntegrationTests/CommentsClientTests.cs b/Test/Notion.IntegrationTests/CommentsClientTests.cs index 5a6de044..1655fc71 100644 --- a/Test/Notion.IntegrationTests/CommentsClientTests.cs +++ b/Test/Notion.IntegrationTests/CommentsClientTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Notion.Client; @@ -7,20 +6,20 @@ namespace Notion.IntegrationTests; -public class CommentsClientTests : IntegrationTestBase, IAsyncDisposable +public class CommentsClientTests : IntegrationTestBase, IAsyncLifetime { - private readonly Page _page; + private Page _page = null!; - public CommentsClientTests() + public async Task InitializeAsync() { - _page = Client.Pages.CreateAsync( + _page = await Client.Pages.CreateAsync( PagesCreateParametersBuilder.Create( new ParentPageInput { PageId = ParentPageId } ).Build() - ).GetAwaiter().GetResult(); + ); } - public async ValueTask DisposeAsync() + public async Task DisposeAsync() { await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); } From f6e8feb4c0e8c17ff134ecdf23d61dc0ff86abe7 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:34:38 +0530 Subject: [PATCH 25/71] User IAsyncLifeTIme in DatabasesClient tests --- .../DatabasesClientTests.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Test/Notion.IntegrationTests/DatabasesClientTests.cs b/Test/Notion.IntegrationTests/DatabasesClientTests.cs index cbdfb652..c5c7082b 100644 --- a/Test/Notion.IntegrationTests/DatabasesClientTests.cs +++ b/Test/Notion.IntegrationTests/DatabasesClientTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -8,20 +7,20 @@ namespace Notion.IntegrationTests; -public class DatabasesClientTests : IntegrationTestBase, IAsyncDisposable +public class DatabasesClientTests : IntegrationTestBase, IAsyncLifetime { - private readonly Page _page; + private Page _page = null!; - public DatabasesClientTests() + public async Task InitializeAsync() { - _page = Client.Pages.CreateAsync( + _page = await Client.Pages.CreateAsync( PagesCreateParametersBuilder.Create( new ParentPageInput { PageId = ParentPageId } ).Build() - ).GetAwaiter().GetResult(); + ); } - public async ValueTask DisposeAsync() + public async Task DisposeAsync() { await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); } From 889fa12412b52f158f342af7ef7499603753b42a Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:36:41 +0530 Subject: [PATCH 26/71] Use IAsyncLifetime in PageClient tests --- Test/Notion.IntegrationTests/PageClientTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Test/Notion.IntegrationTests/PageClientTests.cs b/Test/Notion.IntegrationTests/PageClientTests.cs index d2843dc4..2e4153eb 100644 --- a/Test/Notion.IntegrationTests/PageClientTests.cs +++ b/Test/Notion.IntegrationTests/PageClientTests.cs @@ -8,19 +8,19 @@ namespace Notion.IntegrationTests; -public class PageClientTests : IntegrationTestBase, IAsyncDisposable +public class PageClientTests : IntegrationTestBase, IAsyncLifetime { - private readonly Page _page; - private readonly Database _database; + private Page _page = null!; + private Database _database = null!; - public PageClientTests() + public async Task InitializeAsync() { // Create a page - _page = Client.Pages.CreateAsync( + _page = await Client.Pages.CreateAsync( PagesCreateParametersBuilder.Create( new ParentPageInput { PageId = ParentPageId } ).Build() - ).GetAwaiter().GetResult(); + ); // Create a database var createDbRequest = new DatabasesCreateParameters @@ -58,10 +58,10 @@ public PageClientTests() Parent = new ParentPageInput { PageId = _page.Id } }; - _database = Client.Databases.CreateAsync(createDbRequest).GetAwaiter().GetResult(); + _database = await Client.Databases.CreateAsync(createDbRequest); } - public async ValueTask DisposeAsync() + public async Task DisposeAsync() { await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); } From f1c0a228313131b4e963d18d8d6722248996bf77 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:38:26 +0530 Subject: [PATCH 27/71] User IAsyncLifetime in PageWithPageParent tests --- .../PageWithPageParentTests.cs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Test/Notion.IntegrationTests/PageWithPageParentTests.cs b/Test/Notion.IntegrationTests/PageWithPageParentTests.cs index 3d31e94d..5f398dc7 100644 --- a/Test/Notion.IntegrationTests/PageWithPageParentTests.cs +++ b/Test/Notion.IntegrationTests/PageWithPageParentTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -8,11 +7,11 @@ namespace Notion.IntegrationTests; -public class PageWithPageParentTests : IntegrationTestBase, IAsyncDisposable +public class PageWithPageParentTests : IntegrationTestBase, IAsyncLifetime { - private readonly Page _page; + private Page _page = null!; - public PageWithPageParentTests() + public async Task InitializeAsync() { var pagesCreateParameters = PagesCreateParametersBuilder .Create(new ParentPageInput() { PageId = ParentPageId }) @@ -25,7 +24,12 @@ public PageWithPageParentTests() } }).Build(); - _page = Client.Pages.CreateAsync(pagesCreateParameters).GetAwaiter().GetResult(); + _page = await Client.Pages.CreateAsync(pagesCreateParameters); + } + + public async Task DisposeAsync() + { + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); } [Fact] @@ -63,9 +67,4 @@ public async Task Update_Title_Of_Page() titleProperty.Results.First().As().Title.PlainText.Should().Be("Page Title Updated"); } - - public async ValueTask DisposeAsync() - { - await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); - } } From 8d574d66407f301c0f0c6dce72a5ca04c52969ff Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 01:43:23 +0530 Subject: [PATCH 28/71] Add Block object request models --- .../Blocks/Request/AudioBlockRequest.cs | 12 +++++++ .../Blocks/Request/BlockObjectRequest.cs | 28 +++++++++++++++++ .../Blocks/Request/BookmarkBlockRequest.cs | 22 +++++++++++++ .../Blocks/Request/BreadcrumbBlockRequest.cs | 16 ++++++++++ .../Request/BulletedListItemBlockRequest.cs | 27 ++++++++++++++++ .../Blocks/Request/CalloutBlockRequest.cs | 30 ++++++++++++++++++ .../Request/ChildDatabaseBlockRequest.cs | 18 +++++++++++ .../Blocks/Request/ChildPageBlockRequest.cs | 18 +++++++++++ .../Models/Blocks/Request/CodeBlockRequest.cs | 25 +++++++++++++++ .../Blocks/Request/ColumnBlockRequest.cs | 19 ++++++++++++ .../Blocks/Request/ColumnListBlockRequest.cs | 19 ++++++++++++ .../Blocks/Request/DividerBlockRequest.cs | 16 ++++++++++ .../Blocks/Request/EmbedBlockRequest.cs | 22 +++++++++++++ .../Blocks/Request/EquationBlockRequest.cs | 18 +++++++++++ .../Models/Blocks/Request/FileBlockRequest.cs | 12 +++++++ .../Blocks/Request/HeadingOneBlockRequest.cs | 29 +++++++++++++++++ .../Request/HeadingThreeBlockRequest.cs | 29 +++++++++++++++++ .../Blocks/Request/HeadingTwoBlockRequest.cs | 29 +++++++++++++++++ .../Blocks/Request/IBlockObjectRequest.cs | 19 ++++++++++++ .../Request/IColumnChildrenBlockRequest.cs | 18 +++++++++++ .../Blocks/Request/ImageBlockRequest.cs | 12 +++++++ .../Blocks/Request/LinkPreviewBlockRequest.cs | 18 +++++++++++ .../Blocks/Request/LinkToPageBlockRequest.cs | 12 +++++++ .../Request/NumberedListItemBlockRequest.cs | 27 ++++++++++++++++ .../Models/Blocks/Request/PDFBlockRequest.cs | 14 +++++++++ .../Blocks/Request/ParagraphBlockRequest.cs | 27 ++++++++++++++++ .../Blocks/Request/QuoteBlockRequest.cs | 27 ++++++++++++++++ .../Blocks/Request/SyncedBlockBlockRequest.cs | 31 +++++++++++++++++++ .../Blocks/Request/TableBlockRequest.cs | 28 +++++++++++++++++ .../Request/TableOfContentsBlockRequest.cs | 20 ++++++++++++ .../Blocks/Request/TableRowBlockRequest.cs | 19 ++++++++++++ .../Blocks/Request/TemplateBlockRequest.cs | 22 +++++++++++++ .../Models/Blocks/Request/ToDoBlockRequest.cs | 30 ++++++++++++++++++ .../Blocks/Request/ToggleBlockRequest.cs | 27 ++++++++++++++++ .../Blocks/Request/VideoBlockRequest.cs | 12 +++++++ 35 files changed, 752 insertions(+) create mode 100644 Src/Notion.Client/Models/Blocks/Request/AudioBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/BlockObjectRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/BookmarkBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/BreadcrumbBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/BulletedListItemBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/CalloutBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/ChildDatabaseBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/ChildPageBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/CodeBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/ColumnBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/ColumnListBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/DividerBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/EmbedBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/EquationBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/FileBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/HeadingOneBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/HeadingThreeBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/HeadingTwoBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/IBlockObjectRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/IColumnChildrenBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/ImageBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/LinkPreviewBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/LinkToPageBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/NumberedListItemBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/PDFBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/ParagraphBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/QuoteBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/SyncedBlockBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/TableBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/TableOfContentsBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/TableRowBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/TemplateBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/ToDoBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/ToggleBlockRequest.cs create mode 100644 Src/Notion.Client/Models/Blocks/Request/VideoBlockRequest.cs diff --git a/Src/Notion.Client/Models/Blocks/Request/AudioBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/AudioBlockRequest.cs new file mode 100644 index 00000000..8378574e --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/AudioBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class AudioBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("audio")] + public FileObject Audio { get; set; } + + public override BlockType Type => BlockType.Audio; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/BlockObjectRequest.cs b/Src/Notion.Client/Models/Blocks/Request/BlockObjectRequest.cs new file mode 100644 index 00000000..a3eb3182 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/BlockObjectRequest.cs @@ -0,0 +1,28 @@ +using System; + +namespace Notion.Client +{ + public abstract class BlockObjectRequest : IBlockObjectRequest + { + public ObjectType Object => ObjectType.Block; + + public string Id { get; set; } + + public virtual BlockType Type { get; set; } + + public DateTime CreatedTime { get; set; } + + public DateTime LastEditedTime { get; set; } + + public virtual bool HasChildren { get; set; } + + public PartialUser CreatedBy { get; set; } + + public PartialUser LastEditedBy { get; set; } + + /// + /// Information about the block's parent. + /// + public IBlockParent Parent { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/BookmarkBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/BookmarkBlockRequest.cs new file mode 100644 index 00000000..0c7c0be2 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/BookmarkBlockRequest.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class BookmarkBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("bookmark")] + public Info Bookmark { get; set; } + + public override BlockType Type => BlockType.Bookmark; + + public class Info + { + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/BreadcrumbBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/BreadcrumbBlockRequest.cs new file mode 100644 index 00000000..f6039456 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/BreadcrumbBlockRequest.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class BreadcrumbBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("breadcrumb")] + public Data Breadcrumb { get; set; } + + public override BlockType Type => BlockType.Breadcrumb; + + public class Data + { + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/BulletedListItemBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/BulletedListItemBlockRequest.cs new file mode 100644 index 00000000..0aa8a183 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/BulletedListItemBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class BulletedListItemBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("bulleted_list_item")] + public Info BulletedListItem { get; set; } + + public override BlockType Type => BlockType.BulletedListItem; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/CalloutBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/CalloutBlockRequest.cs new file mode 100644 index 00000000..b8a56823 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/CalloutBlockRequest.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class CalloutBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("callout")] + public Info Callout { get; set; } + + public override BlockType Type => BlockType.Callout; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("icon")] + public IPageIcon Icon { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ChildDatabaseBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ChildDatabaseBlockRequest.cs new file mode 100644 index 00000000..34ff9387 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ChildDatabaseBlockRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ChildDatabaseBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("child_database")] + public Info ChildDatabase { get; set; } + + public override BlockType Type => BlockType.ChildDatabase; + + public class Info + { + [JsonProperty("title")] + public string Title { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ChildPageBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ChildPageBlockRequest.cs new file mode 100644 index 00000000..43213b77 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ChildPageBlockRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ChildPageBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("child_page")] + public Info ChildPage { get; set; } + + public override BlockType Type => BlockType.ChildPage; + + public class Info + { + [JsonProperty("title")] + public string Title { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/CodeBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/CodeBlockRequest.cs new file mode 100644 index 00000000..732e3208 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/CodeBlockRequest.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CodeBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("code")] + public Info Code { get; set; } + + public override BlockType Type => BlockType.Code; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("language")] + public string Language { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ColumnBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ColumnBlockRequest.cs new file mode 100644 index 00000000..c4136294 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ColumnBlockRequest.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ColumnBlockRequest : BlockObjectRequest + { + public override BlockType Type => BlockType.Column; + + [JsonProperty("column")] + public Info Column { get; set; } + + public class Info + { + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ColumnListBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ColumnListBlockRequest.cs new file mode 100644 index 00000000..431acf8e --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ColumnListBlockRequest.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ColumnListBlockRequest : BlockObjectRequest, INonColumnBlockRequest + { + [JsonProperty("column_list")] + public Info ColumnList { get; set; } + + public override BlockType Type => BlockType.ColumnList; + + public class Info + { + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/DividerBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/DividerBlockRequest.cs new file mode 100644 index 00000000..49c81059 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/DividerBlockRequest.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class DividerBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("divider")] + public Data Divider { get; set; } + + public override BlockType Type => BlockType.Divider; + + public class Data + { + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/EmbedBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/EmbedBlockRequest.cs new file mode 100644 index 00000000..81cb2339 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/EmbedBlockRequest.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class EmbedBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("embed")] + public Info Embed { get; set; } + + public override BlockType Type => BlockType.Embed; + + public class Info + { + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/EquationBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/EquationBlockRequest.cs new file mode 100644 index 00000000..5c2ddb8a --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/EquationBlockRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class EquationBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("equation")] + public Info Equation { get; set; } + + public override BlockType Type => BlockType.Equation; + + public class Info + { + [JsonProperty("expression")] + public string Expression { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/FileBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/FileBlockRequest.cs new file mode 100644 index 00000000..0539c41c --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/FileBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class FileBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("file")] + public FileObject File { get; set; } + + public override BlockType Type => BlockType.File; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/HeadingOneBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/HeadingOneBlockRequest.cs new file mode 100644 index 00000000..dd91451a --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/HeadingOneBlockRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class HeadingOneBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("heading_1")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_1 { get; set; } + + public override BlockType Type => BlockType.Heading_1; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/HeadingThreeBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/HeadingThreeBlockRequest.cs new file mode 100644 index 00000000..9b78c80e --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/HeadingThreeBlockRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class HeadingThreeBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("heading_3")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_3 { get; set; } + + public override BlockType Type => BlockType.Heading_3; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/HeadingTwoBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/HeadingTwoBlockRequest.cs new file mode 100644 index 00000000..dfdeb9dd --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/HeadingTwoBlockRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class HeadingTwoBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("heading_2")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_2 { get; set; } + + public override BlockType Type => BlockType.Heading_2; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/IBlockObjectRequest.cs b/Src/Notion.Client/Models/Blocks/Request/IBlockObjectRequest.cs new file mode 100644 index 00000000..b3fbaf55 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/IBlockObjectRequest.cs @@ -0,0 +1,19 @@ +using JsonSubTypes; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public interface IBlockObjectRequest : IObject, IObjectModificationData + { + [JsonProperty("type")] + [JsonConverter(typeof(StringEnumConverter))] + BlockType Type { get; } + + [JsonProperty("has_children")] + bool HasChildren { get; set; } + + [JsonProperty("parent")] + IBlockParent Parent { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/IColumnChildrenBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/IColumnChildrenBlockRequest.cs new file mode 100644 index 00000000..54e08fab --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/IColumnChildrenBlockRequest.cs @@ -0,0 +1,18 @@ +namespace Notion.Client +{ + public interface ITemplateChildrenBlockRequest : IBlockObjectRequest + { + } + + public interface ISyncedBlockChildrenRequest : IBlockObjectRequest + { + } + + public interface IColumnChildrenBlockRequest : ITemplateChildrenBlockRequest, ISyncedBlockChildrenRequest + { + } + + public interface INonColumnBlockRequest : IBlockObjectRequest + { + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ImageBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ImageBlockRequest.cs new file mode 100644 index 00000000..d3ec252b --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ImageBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ImageBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("image")] + public FileObject Image { get; set; } + + public override BlockType Type => BlockType.Image; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/LinkPreviewBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/LinkPreviewBlockRequest.cs new file mode 100644 index 00000000..11a47592 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/LinkPreviewBlockRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class LinkPreviewBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("link_preview")] + public Data LinkPreview { get; set; } + + public override BlockType Type => BlockType.LinkPreview; + + public class Data + { + [JsonProperty("url")] + public string Url { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/LinkToPageBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/LinkToPageBlockRequest.cs new file mode 100644 index 00000000..ec182df5 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/LinkToPageBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class LinkToPageBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("link_to_page")] + public IPageParent LinkToPage { get; set; } + + public override BlockType Type => BlockType.LinkToPage; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/NumberedListItemBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/NumberedListItemBlockRequest.cs new file mode 100644 index 00000000..87342c1d --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/NumberedListItemBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class NumberedListItemBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("numbered_list_item")] + public Info NumberedListItem { get; set; } + + public override BlockType Type => BlockType.NumberedListItem; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/PDFBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/PDFBlockRequest.cs new file mode 100644 index 00000000..5590000d --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/PDFBlockRequest.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public class PDFBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("pdf")] + public FileObject PDF { get; set; } + + public override BlockType Type => BlockType.PDF; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ParagraphBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ParagraphBlockRequest.cs new file mode 100644 index 00000000..8e8dfa51 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ParagraphBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class ParagraphBlockRequest : Block, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("paragraph")] + public Info Paragraph { get; set; } + + public override BlockType Type => BlockType.Paragraph; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/QuoteBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/QuoteBlockRequest.cs new file mode 100644 index 00000000..3acd3130 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/QuoteBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class QuoteBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("quote")] + public Info Quote { get; set; } + + public override BlockType Type => BlockType.Quote; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/SyncedBlockBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/SyncedBlockBlockRequest.cs new file mode 100644 index 00000000..5338e5bb --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/SyncedBlockBlockRequest.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class SyncedBlockBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("synced_block")] + public Data SyncedBlock { get; set; } + + public override BlockType Type => BlockType.SyncedBlock; + + public class Data + { + [JsonProperty("synced_from")] + public SyncedFromBlockId SyncedFrom { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + + public class SyncedFromBlockId + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("block_id")] + public string BlockId { get; set; } + } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/TableBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/TableBlockRequest.cs new file mode 100644 index 00000000..1e966182 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/TableBlockRequest.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TableBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("table")] + public Info Table { get; set; } + + public override BlockType Type => BlockType.Table; + + public class Info + { + [JsonProperty("table_width")] + public int TableWidth { get; set; } + + [JsonProperty("has_column_header")] + public bool HasColumnHeader { get; set; } + + [JsonProperty("has_row_header")] + public bool HasRowHeader { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/TableOfContentsBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/TableOfContentsBlockRequest.cs new file mode 100644 index 00000000..2aef499d --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/TableOfContentsBlockRequest.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class TableOfContentsBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("table_of_contents")] + public Data TableOfContents { get; set; } + + public override BlockType Type => BlockType.TableOfContents; + + public class Data + { + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/TableRowBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/TableRowBlockRequest.cs new file mode 100644 index 00000000..ead85b33 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/TableRowBlockRequest.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TableRowBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("table_row")] + public Info TableRow { get; set; } + + public override BlockType Type => BlockType.TableRow; + + public class Info + { + [JsonProperty("cells")] + public IEnumerable> Cells { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/TemplateBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/TemplateBlockRequest.cs new file mode 100644 index 00000000..04c161d2 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/TemplateBlockRequest.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TemplateBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("template")] + public Data Template { get; set; } + + public override BlockType Type => BlockType.Template; + + public class Data + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ToDoBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ToDoBlockRequest.cs new file mode 100644 index 00000000..ed7b09c3 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ToDoBlockRequest.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class ToDoBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("to_do")] + public Info ToDo { get; set; } + + public override BlockType Type => BlockType.ToDo; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("checked")] + public bool IsChecked { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ToggleBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ToggleBlockRequest.cs new file mode 100644 index 00000000..fbbbcd37 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ToggleBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class ToggleBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("toggle")] + public Info Toggle { get; set; } + + public override BlockType Type => BlockType.Toggle; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/VideoBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/VideoBlockRequest.cs new file mode 100644 index 00000000..238b2d26 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/VideoBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class VideoBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("video")] + public FileObject Video { get; set; } + + public override BlockType Type => BlockType.Video; + } +} From f5f69be7cf24497ce59f8a166cb47d5a1543790f Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 15 Oct 2023 01:53:05 +0530 Subject: [PATCH 29/71] Use IBlockObjectRequest in Block Append Children api --- .../Request/BlockAppendChildrenRequest.cs | 2 +- .../IBlockAppendChildrenBodyParameters.cs | 5 +- .../BlocksClientTests.cs | 63 ++++++++++--------- Test/Notion.UnitTests/BlocksClientTests.cs | 10 +-- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs index 580d164a..8a5a9cde 100644 --- a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs @@ -4,7 +4,7 @@ namespace Notion.Client { public class BlockAppendChildrenRequest : IBlockAppendChildrenBodyParameters, IBlockAppendChildrenPathParameters { - public IEnumerable Children { get; set; } + public IEnumerable Children { get; set; } public string After { get; set; } diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs index b476c089..ea6cef37 100644 --- a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs @@ -3,11 +3,10 @@ namespace Notion.Client { - // TODO: need an input version of Block public interface IBlockAppendChildrenBodyParameters { [JsonProperty("children")] - IEnumerable Children { get; set; } + IEnumerable Children { get; set; } /// /// The ID of the existing block that the new block should be appended after. @@ -18,7 +17,7 @@ public interface IBlockAppendChildrenBodyParameters internal class BlockAppendChildrenBodyParameters : IBlockAppendChildrenBodyParameters { - public IEnumerable Children { get; set; } + public IEnumerable Children { get; set; } public string After { get; set; } diff --git a/Test/Notion.IntegrationTests/BlocksClientTests.cs b/Test/Notion.IntegrationTests/BlocksClientTests.cs index 4db78d85..284ac009 100644 --- a/Test/Notion.IntegrationTests/BlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/BlocksClientTests.cs @@ -33,14 +33,14 @@ public async Task AppendChildrenAsync_AppendsBlocksGivenBlocks() new BlockAppendChildrenRequest { BlockId = _page.Id, - Children = new List + Children = new List { - new BreadcrumbBlock { Breadcrumb = new BreadcrumbBlock.Data() }, - new DividerBlock { Divider = new DividerBlock.Data() }, - new TableOfContentsBlock { TableOfContents = new TableOfContentsBlock.Data() }, - new CalloutBlock + new BreadcrumbBlockRequest { Breadcrumb = new BreadcrumbBlockRequest.Data() }, + new DividerBlockRequest { Divider = new DividerBlockRequest.Data() }, + new TableOfContentsBlockRequest { TableOfContents = new TableOfContentsBlockRequest.Data() }, + new CalloutBlockRequest { - Callout = new CalloutBlock.Info + Callout = new CalloutBlockRequest.Info { RichText = new List { @@ -62,7 +62,10 @@ public async Task UpdateBlockAsync_UpdatesGivenBlock() new BlockAppendChildrenRequest { BlockId = _page.Id, - Children = new List { new BreadcrumbBlock { Breadcrumb = new BreadcrumbBlock.Data() } } + Children = new List + { + new BreadcrumbBlockRequest { Breadcrumb = new BreadcrumbBlockRequest.Data() } + } } ); @@ -82,10 +85,10 @@ public async Task DeleteAsync_DeleteBlockWithGivenId() new BlockAppendChildrenRequest { BlockId = _page.Id, - Children = new List + Children = new List { - new DividerBlock { Divider = new DividerBlock.Data() }, - new TableOfContentsBlock { TableOfContents = new TableOfContentsBlock.Data() } + new DividerBlockRequest { Divider = new DividerBlockRequest.Data() }, + new TableOfContentsBlockRequest { TableOfContents = new TableOfContentsBlockRequest.Data() } } } ); @@ -96,13 +99,13 @@ public async Task DeleteAsync_DeleteBlockWithGivenId() [Theory] [MemberData(nameof(BlockData))] public async Task UpdateAsync_UpdatesGivenBlock( - IBlock block, IUpdateBlock updateBlock, Action assert) + IBlockObjectRequest block, IUpdateBlock updateBlock, Action assert) { var blocks = await Client.Blocks.AppendChildrenAsync( new BlockAppendChildrenRequest { BlockId = _page.Id, - Children = new List { block } + Children = new List { block } } ); @@ -125,9 +128,9 @@ private static IEnumerable BlockData() { new object[] { - new BookmarkBlock + new BookmarkBlockRequest { - Bookmark = new BookmarkBlock.Info + Bookmark = new BookmarkBlockRequest.Info { Url = "https://developers.notion.com/reference/rich-text", Caption = new List @@ -156,7 +159,7 @@ private static IEnumerable BlockData() }, new object[] { - new EquationBlock { Equation = new EquationBlock.Info { Expression = "e=mc^3" } }, + new EquationBlockRequest { Equation = new EquationBlockRequest.Info { Expression = "e=mc^3" } }, new EquationUpdateBlock { Equation = new EquationUpdateBlock.Info { Expression = "e=mc^2" } }, new Action((block, _) => { @@ -166,7 +169,7 @@ private static IEnumerable BlockData() }, new object[] { - new DividerBlock { Divider = new DividerBlock.Data() }, new DividerUpdateBlock(), + new DividerBlockRequest { Divider = new DividerBlockRequest.Data() }, new DividerUpdateBlock(), new Action((block, client) => { Assert.NotNull(block); @@ -175,7 +178,7 @@ private static IEnumerable BlockData() }, new object[] { - new AudioBlock + new AudioBlockRequest { Audio = new ExternalFile { @@ -206,7 +209,7 @@ private static IEnumerable BlockData() }, new object[] { - new TableOfContentsBlock { TableOfContents = new TableOfContentsBlock.Data() }, + new TableOfContentsBlockRequest { TableOfContents = new TableOfContentsBlockRequest.Data() }, new TableOfContentsUpdateBlock(), new Action((block, client) => { Assert.NotNull(block); @@ -215,9 +218,9 @@ private static IEnumerable BlockData() }, new object[] { - new CalloutBlock + new CalloutBlockRequest { - Callout = new CalloutBlock.Info + Callout = new CalloutBlockRequest.Info { RichText = new List { @@ -245,9 +248,9 @@ private static IEnumerable BlockData() }, new object[] { - new QuoteBlock + new QuoteBlockRequest { - Quote = new QuoteBlock.Info + Quote = new QuoteBlockRequest.Info { RichText = new List { @@ -275,7 +278,7 @@ private static IEnumerable BlockData() }, new object[] { - new ImageBlock + new ImageBlockRequest { Image = new ExternalFile { @@ -308,9 +311,9 @@ private static IEnumerable BlockData() }, new object[] { - new EmbedBlock + new EmbedBlockRequest { - Embed = new EmbedBlock.Info + Embed = new EmbedBlockRequest.Info { Url = "https://zephoria.com/wp-content/uploads/2014/08/online-community.jpg" } @@ -333,7 +336,7 @@ private static IEnumerable BlockData() }, new object[] { - new LinkToPageBlock + new LinkToPageBlockRequest { LinkToPage = new PageParent { @@ -359,16 +362,16 @@ private static IEnumerable BlockData() }, new object[] { - new TableBlock + new TableBlockRequest { - Table = new TableBlock.Info + Table = new TableBlockRequest.Info { TableWidth = 1, Children = new[] { - new TableRowBlock + new TableRowBlockRequest { - TableRow = new TableRowBlock.Info + TableRow = new TableRowBlockRequest.Info { Cells = new[] { diff --git a/Test/Notion.UnitTests/BlocksClientTests.cs b/Test/Notion.UnitTests/BlocksClientTests.cs index 600e0bf4..6dd1656b 100644 --- a/Test/Notion.UnitTests/BlocksClientTests.cs +++ b/Test/Notion.UnitTests/BlocksClientTests.cs @@ -63,11 +63,11 @@ public async Task AppendBlockChildren() var request = new BlockAppendChildrenRequest { BlockId = blockId, - Children = new List + Children = new List { - new HeadingTwoBlock + new HeadingTwoBlockRequest { - Heading_2 = new HeadingTwoBlock.Info + Heading_2 = new HeadingTwoBlockRequest.Info { RichText = new List { @@ -75,9 +75,9 @@ public async Task AppendBlockChildren() } } }, - new ParagraphBlock + new ParagraphBlockRequest { - Paragraph = new ParagraphBlock.Info + Paragraph = new ParagraphBlockRequest.Info { RichText = new List { From 09ff9194a934ed2b24ffed7cbaac130cad9fae5d Mon Sep 17 00:00:00 2001 From: Gehongyan Date: Wed, 8 Nov 2023 18:00:03 +0800 Subject: [PATCH 30/71] Fix broken database relation request parameters. --- .../PropertySchema/RelationPropertySchema.cs | 17 ++--------------- .../RelationUpdatePropertySchema.cs | 17 ++--------------- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RelationPropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RelationPropertySchema.cs index fa6a3249..4c278ac2 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RelationPropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RelationPropertySchema.cs @@ -1,23 +1,10 @@ -using System; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace Notion.Client { public class RelationPropertySchema : IPropertySchema { [JsonProperty("relation")] - public RelationInfo Relation { get; set; } - - public class RelationInfo - { - [JsonProperty("database_id")] - public Guid DatabaseId { get; set; } - - [JsonProperty("synced_property_id")] - public string SyncedPropertyId { get; set; } - - [JsonProperty("synced_property_name")] - public string SyncedPropertyName { get; set; } - } + public RelationData Relation { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RelationUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RelationUpdatePropertySchema.cs index 4f6a74a3..25f65cd4 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RelationUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RelationUpdatePropertySchema.cs @@ -1,23 +1,10 @@ -using System; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace Notion.Client { public class RelationUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("relation")] - public RelationInfo Relation { get; set; } - - public class RelationInfo - { - [JsonProperty("database_id")] - public Guid DatabaseId { get; set; } - - [JsonProperty("synced_property_id")] - public string SyncedPropertyId { get; set; } - - [JsonProperty("synced_property_name")] - public string SyncedPropertyName { get; set; } - } + public RelationData Relation { get; set; } } } From 59fd00540c0e0c1cdb619e6704bf04dbd86c4cd7 Mon Sep 17 00:00:00 2001 From: "boris.kuzmanov" Date: Sat, 27 Apr 2024 14:47:47 +0200 Subject: [PATCH 31/71] Change GetewayTimeout to GatewayTimeout (typo fix) --- Src/Notion.Client/NotionAPIErrorCode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/Notion.Client/NotionAPIErrorCode.cs b/Src/Notion.Client/NotionAPIErrorCode.cs index e0264ae7..97984f98 100644 --- a/Src/Notion.Client/NotionAPIErrorCode.cs +++ b/Src/Notion.Client/NotionAPIErrorCode.cs @@ -49,6 +49,6 @@ public enum NotionAPIErrorCode DatabaseConnectionUnavailable, [EnumMember(Value = "gateway_timeout")] - GetewayTimeout + GatewayTimeout } } From b85a175fad43b4c65d1baa35d4af4030b0a5b4f6 Mon Sep 17 00:00:00 2001 From: "boris.kuzmanov" Date: Sun, 28 Apr 2024 13:37:54 +0200 Subject: [PATCH 32/71] Add name property to FileObject --- Src/Notion.Client/Models/File/FileObject.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Src/Notion.Client/Models/File/FileObject.cs b/Src/Notion.Client/Models/File/FileObject.cs index 9ed206de..2c219a1b 100644 --- a/Src/Notion.Client/Models/File/FileObject.cs +++ b/Src/Notion.Client/Models/File/FileObject.cs @@ -11,6 +11,9 @@ public abstract class FileObject : IPageIcon { [JsonProperty("caption")] public IEnumerable Caption { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } [JsonProperty("type")] public virtual string Type { get; set; } From 09937b69db43e1b8e8abe2bd2023972297261fa8 Mon Sep 17 00:00:00 2001 From: "boris.kuzmanov" Date: Sun, 28 Apr 2024 14:24:30 +0200 Subject: [PATCH 33/71] Run lint --- Src/Notion.Client/Models/File/FileObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/Notion.Client/Models/File/FileObject.cs b/Src/Notion.Client/Models/File/FileObject.cs index 2c219a1b..aa7aed1b 100644 --- a/Src/Notion.Client/Models/File/FileObject.cs +++ b/Src/Notion.Client/Models/File/FileObject.cs @@ -11,7 +11,7 @@ public abstract class FileObject : IPageIcon { [JsonProperty("caption")] public IEnumerable Caption { get; set; } - + [JsonProperty("name")] public string Name { get; set; } From 08ed291c0d27ca02ce02f8a7d5b9d2c31032dad7 Mon Sep 17 00:00:00 2001 From: kosaku-hayashi Date: Sun, 14 Jul 2024 14:57:47 +0900 Subject: [PATCH 34/71] Removed 'archived' property and added 'in_trash' property. --- Src/Notion.Client/Api/Blocks/IBlocksClient.cs | 2 +- .../UpdateBlocks/BookmarkUpdateBlock.cs | 2 +- .../UpdateBlocks/BreadcrumbUpdateBlock.cs | 2 +- .../UpdateBlocks/DividerUpdateBlock.cs | 2 +- .../UpdateBlocks/IUpdateBlock.cs | 4 ++-- .../UpdateBlocks/TableOfContentsUpdateBlock.cs | 2 +- .../UpdateBlocks/UpdateBlock.cs | 2 +- .../DatabasesUpdateParameters.cs | 2 +- .../IDatabasesUpdateBodyParameters.cs | 4 ++-- .../IPagesUpdateBodyParameters.cs | 4 ++-- .../RequestParams/PagesUpdateParameters.cs | 4 ++-- Src/Notion.Client/Models/Blocks/Block.cs | 2 ++ Src/Notion.Client/Models/Blocks/IBlock.cs | 3 +++ Src/Notion.Client/Models/Database/Database.cs | 7 ++----- Src/Notion.Client/Models/Page/Page.cs | 6 +++--- .../BlocksClientTests.cs | 2 +- .../CommentsClientTests.cs | 2 +- .../DatabasesClientTests.cs | 2 +- .../Notion.IntegrationTests/PageClientTests.cs | 2 +- .../PageWithPageParentTests.cs | 2 +- Test/Notion.UnitTests/BlocksClientTests.cs | 1 + Test/Notion.UnitTests/Notion.UnitTests.csproj | 2 +- Test/Notion.UnitTests/PagesClientTests.cs | 14 +++++++------- Test/Notion.UnitTests/SearchClientTest.cs | 2 +- .../blocks/AppendBlockChildrenResponse.json | 1 + .../blocks/RetrieveBlockChildrenResponse.json | 18 +++++++++--------- .../data/blocks/RetrieveBlockResponse.json | 1 + .../data/blocks/UpdateBlockResponse.json | 1 + .../databases/DatabaseRetrieveResponse.json | 1 + .../data/databases/DatabasesQueryResponse.json | 2 +- ...yncDateFormulaValueReturnsNullResponse.json | 2 +- .../data/pages/CreatePageResponse.json | 2 +- ...ageObjectShouldHaveUrlPropertyResponse.json | 2 +- ...ageResponse.json => TrashPageResponse.json} | 2 +- .../pages/UpdatePagePropertiesResponse.json | 2 +- .../data/search/SearchResponse.json | 3 ++- 36 files changed, 61 insertions(+), 53 deletions(-) rename Test/Notion.UnitTests/data/pages/{ArchivePageResponse.json => TrashPageResponse.json} (97%) diff --git a/Src/Notion.Client/Api/Blocks/IBlocksClient.cs b/Src/Notion.Client/Api/Blocks/IBlocksClient.cs index fec1b9f5..c5a64e59 100644 --- a/Src/Notion.Client/Api/Blocks/IBlocksClient.cs +++ b/Src/Notion.Client/Api/Blocks/IBlocksClient.cs @@ -47,7 +47,7 @@ Task AppendChildrenAsync( ); /// - /// Sets a Block object, including page blocks, to archived: true using the ID specified. + /// Moves a Block object, including page blocks, to the trash: true using the ID specified. /// /// Identifier for a Notion block Task DeleteAsync(string blockId, CancellationToken cancellationToken = default); diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BookmarkUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BookmarkUpdateBlock.cs index 612b6dda..b147c587 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BookmarkUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BookmarkUpdateBlock.cs @@ -8,7 +8,7 @@ public class BookmarkUpdateBlock : IUpdateBlock [JsonProperty("bookmark")] public Info Bookmark { get; set; } - public bool Archived { get; set; } + public bool InTrash { get; set; } public class Info { diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BreadcrumbUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BreadcrumbUpdateBlock.cs index a90f6966..260eca82 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BreadcrumbUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BreadcrumbUpdateBlock.cs @@ -12,7 +12,7 @@ public BreadcrumbUpdateBlock() [JsonProperty("breadcrumb")] public Info Breadcrumb { get; set; } - public bool Archived { get; set; } + public bool InTrash { get; set; } public class Info { diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/DividerUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/DividerUpdateBlock.cs index 79f332f0..62b83451 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/DividerUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/DividerUpdateBlock.cs @@ -12,7 +12,7 @@ public DividerUpdateBlock() [JsonProperty("divider")] public Info Divider { get; set; } - public bool Archived { get; set; } + public bool InTrash { get; set; } public class Info { diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/IUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/IUpdateBlock.cs index ba93fe29..5b1de4be 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/IUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/IUpdateBlock.cs @@ -4,7 +4,7 @@ namespace Notion.Client { public interface IUpdateBlock { - [JsonProperty("archived")] - bool Archived { get; set; } + [JsonProperty("in_trash")] + bool InTrash { get; set; } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableOfContentsUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableOfContentsUpdateBlock.cs index bef1f278..2e4adff9 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableOfContentsUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableOfContentsUpdateBlock.cs @@ -12,7 +12,7 @@ public TableOfContentsUpdateBlock() [JsonProperty("table_of_contents")] public Info TableOfContents { get; set; } - public bool Archived { get; set; } + public bool InTrash { get; set; } public class Info { diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/UpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/UpdateBlock.cs index d0321e52..14704cb2 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/UpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/UpdateBlock.cs @@ -2,6 +2,6 @@ { public abstract class UpdateBlock : IUpdateBlock { - public bool Archived { get; set; } + public bool InTrash { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/DatabasesUpdateParameters.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/DatabasesUpdateParameters.cs index 41da2d17..5147a6b0 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/DatabasesUpdateParameters.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/DatabasesUpdateParameters.cs @@ -12,7 +12,7 @@ public class DatabasesUpdateParameters : IDatabasesUpdateBodyParameters public FileObject Cover { get; set; } - public bool Archived { get; set; } + public bool InTrash { get; set; } public bool? IsInline { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/IDatabasesUpdateBodyParameters.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/IDatabasesUpdateBodyParameters.cs index b1dc419a..f37df431 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/IDatabasesUpdateBodyParameters.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/IDatabasesUpdateBodyParameters.cs @@ -17,8 +17,8 @@ public interface IDatabasesUpdateBodyParameters [JsonProperty("cover")] FileObject Cover { get; set; } - [JsonProperty("archived")] - bool Archived { get; set; } + [JsonProperty("in_trash")] + bool InTrash { get; set; } [JsonProperty("is_inline")] bool? IsInline { get; set; } diff --git a/Src/Notion.Client/Api/Pages/RequestParams/IPagesUpdateBodyParameters.cs b/Src/Notion.Client/Api/Pages/RequestParams/IPagesUpdateBodyParameters.cs index b37dd1fd..b7ea1ea6 100644 --- a/Src/Notion.Client/Api/Pages/RequestParams/IPagesUpdateBodyParameters.cs +++ b/Src/Notion.Client/Api/Pages/RequestParams/IPagesUpdateBodyParameters.cs @@ -5,8 +5,8 @@ namespace Notion.Client { public interface IPagesUpdateBodyParameters { - [JsonProperty("archived")] - bool Archived { get; set; } + [JsonProperty("in_trash")] + bool InTrash { get; set; } [JsonProperty("properties")] IDictionary Properties { get; set; } diff --git a/Src/Notion.Client/Api/Pages/RequestParams/PagesUpdateParameters.cs b/Src/Notion.Client/Api/Pages/RequestParams/PagesUpdateParameters.cs index 742ef8dd..6845f27b 100644 --- a/Src/Notion.Client/Api/Pages/RequestParams/PagesUpdateParameters.cs +++ b/Src/Notion.Client/Api/Pages/RequestParams/PagesUpdateParameters.cs @@ -11,8 +11,8 @@ public class PagesUpdateParameters : IPagesUpdateBodyParameters [JsonProperty("cover")] public FileObject Cover { get; set; } - [JsonProperty("archived")] - public bool Archived { get; set; } + [JsonProperty("in_trash")] + public bool InTrash { get; set; } [JsonProperty("properties")] public IDictionary Properties { get; set; } diff --git a/Src/Notion.Client/Models/Blocks/Block.cs b/Src/Notion.Client/Models/Blocks/Block.cs index 883399df..fa1ef612 100644 --- a/Src/Notion.Client/Models/Blocks/Block.cs +++ b/Src/Notion.Client/Models/Blocks/Block.cs @@ -16,6 +16,8 @@ public abstract class Block : IBlock public virtual bool HasChildren { get; set; } + public bool InTrash { get; set; } + public PartialUser CreatedBy { get; set; } public PartialUser LastEditedBy { get; set; } diff --git a/Src/Notion.Client/Models/Blocks/IBlock.cs b/Src/Notion.Client/Models/Blocks/IBlock.cs index 24d6a12a..b06a3646 100644 --- a/Src/Notion.Client/Models/Blocks/IBlock.cs +++ b/Src/Notion.Client/Models/Blocks/IBlock.cs @@ -47,6 +47,9 @@ public interface IBlock : IObject, IObjectModificationData [JsonProperty("has_children")] bool HasChildren { get; set; } + [JsonProperty("in_trash")] + bool InTrash { get; set; } + [JsonProperty("parent")] IBlockParent Parent { get; set; } } diff --git a/Src/Notion.Client/Models/Database/Database.cs b/Src/Notion.Client/Models/Database/Database.cs index c5a46ed6..be288ea0 100644 --- a/Src/Notion.Client/Models/Database/Database.cs +++ b/Src/Notion.Client/Models/Database/Database.cs @@ -27,11 +27,8 @@ public class Database : IObject, IObjectModificationData, IWikiDatabase [JsonProperty("url")] public string Url { get; set; } - /// - /// The archived status of the database. - /// - [JsonProperty("archived")] - public bool Archived { get; set; } + [JsonProperty("in_trash")] + public bool InTrash { get; set; } [JsonProperty("is_inline")] public bool IsInline { get; set; } diff --git a/Src/Notion.Client/Models/Page/Page.cs b/Src/Notion.Client/Models/Page/Page.cs index c6bcfae1..fe4eb0de 100644 --- a/Src/Notion.Client/Models/Page/Page.cs +++ b/Src/Notion.Client/Models/Page/Page.cs @@ -13,10 +13,10 @@ public class Page : IObject, IObjectModificationData, IWikiDatabase public IPageParent Parent { get; set; } /// - /// The archived status of the page. + /// Indicates whether the page is currently in the trash. /// - [JsonProperty("archived")] - public bool IsArchived { get; set; } + [JsonProperty("in_trash")] + public bool InTrash { get; set; } /// /// Property values of this page. diff --git a/Test/Notion.IntegrationTests/BlocksClientTests.cs b/Test/Notion.IntegrationTests/BlocksClientTests.cs index 284ac009..5c8e4618 100644 --- a/Test/Notion.IntegrationTests/BlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/BlocksClientTests.cs @@ -23,7 +23,7 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); } [Fact] diff --git a/Test/Notion.IntegrationTests/CommentsClientTests.cs b/Test/Notion.IntegrationTests/CommentsClientTests.cs index 1655fc71..fac1fce1 100644 --- a/Test/Notion.IntegrationTests/CommentsClientTests.cs +++ b/Test/Notion.IntegrationTests/CommentsClientTests.cs @@ -21,7 +21,7 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); } [Fact] diff --git a/Test/Notion.IntegrationTests/DatabasesClientTests.cs b/Test/Notion.IntegrationTests/DatabasesClientTests.cs index c5c7082b..31b611f1 100644 --- a/Test/Notion.IntegrationTests/DatabasesClientTests.cs +++ b/Test/Notion.IntegrationTests/DatabasesClientTests.cs @@ -22,7 +22,7 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); } [Fact] diff --git a/Test/Notion.IntegrationTests/PageClientTests.cs b/Test/Notion.IntegrationTests/PageClientTests.cs index 2e4153eb..21adbd5b 100644 --- a/Test/Notion.IntegrationTests/PageClientTests.cs +++ b/Test/Notion.IntegrationTests/PageClientTests.cs @@ -63,7 +63,7 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); } [Fact] diff --git a/Test/Notion.IntegrationTests/PageWithPageParentTests.cs b/Test/Notion.IntegrationTests/PageWithPageParentTests.cs index 5f398dc7..3c6d3c6c 100644 --- a/Test/Notion.IntegrationTests/PageWithPageParentTests.cs +++ b/Test/Notion.IntegrationTests/PageWithPageParentTests.cs @@ -29,7 +29,7 @@ public async Task InitializeAsync() public async Task DisposeAsync() { - await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { Archived = true }); + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); } [Fact] diff --git a/Test/Notion.UnitTests/BlocksClientTests.cs b/Test/Notion.UnitTests/BlocksClientTests.cs index 6dd1656b..e2cc8603 100644 --- a/Test/Notion.UnitTests/BlocksClientTests.cs +++ b/Test/Notion.UnitTests/BlocksClientTests.cs @@ -184,6 +184,7 @@ public async Task UpdateAsync() block.Id.Should().Be(blockId); block.HasChildren.Should().BeFalse(); + block.InTrash.Should().BeFalse(); block.Type.Should().Be(BlockType.ToDo); var todoBlock = (ToDoBlock)block; diff --git a/Test/Notion.UnitTests/Notion.UnitTests.csproj b/Test/Notion.UnitTests/Notion.UnitTests.csproj index 30ff0791..588ddc54 100644 --- a/Test/Notion.UnitTests/Notion.UnitTests.csproj +++ b/Test/Notion.UnitTests/Notion.UnitTests.csproj @@ -51,7 +51,7 @@ Always - + Always diff --git a/Test/Notion.UnitTests/PagesClientTests.cs b/Test/Notion.UnitTests/PagesClientTests.cs index adbb9443..9541f62e 100644 --- a/Test/Notion.UnitTests/PagesClientTests.cs +++ b/Test/Notion.UnitTests/PagesClientTests.cs @@ -39,7 +39,7 @@ public async Task RetrieveAsync() page.Id.Should().Be(pageId); page.Parent.Type.Should().Be(ParentType.DatabaseId); ((DatabaseParent)page.Parent).DatabaseId.Should().Be("48f8fee9-cd79-4180-bc2f-ec0398253067"); - page.IsArchived.Should().BeFalse(); + page.InTrash.Should().BeFalse(); } [Fact] @@ -72,7 +72,7 @@ public async Task CreateAsync() page.Url.Should().NotBeNullOrEmpty(); page.Properties.Should().HaveCount(1); page.Properties.First().Key.Should().Be("Name"); - page.IsArchived.Should().BeFalse(); + page.InTrash.Should().BeFalse(); page.Parent.Should().NotBeNull(); ((DatabaseParent)page.Parent).DatabaseId.Should().Be("3c357473-a281-49a4-88c0-10d2b245a589"); } @@ -172,7 +172,7 @@ public async Task UpdatePageAsync() var page = await _client.UpdateAsync(pageId, pagesUpdateParameters); page.Id.Should().Be(pageId); - page.IsArchived.Should().BeFalse(); + page.InTrash.Should().BeFalse(); page.Properties.Should().HaveCount(2); var updatedProperty = page.Properties.First(x => x.Key == "In stock"); @@ -187,14 +187,14 @@ public async Task UpdatePageAsync() } [Fact] - public async Task ArchivePageAsync() + public async Task TrashPageAsync() { var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; var propertyId = "{>U;"; var path = ApiEndpoints.PagesApiUrls.UpdateProperties(pageId); - var jsonData = await File.ReadAllTextAsync("data/pages/ArchivePageResponse.json"); + var jsonData = await File.ReadAllTextAsync("data/pages/TrashPageResponse.json"); Server.Given(CreatePatchRequestBuilder(path)) .RespondWith( @@ -211,7 +211,7 @@ public async Task ArchivePageAsync() var pagesUpdateParameters = new PagesUpdateParameters { - Archived = true, + InTrash = true, Properties = new Dictionary { { "In stock", new CheckboxPropertyValue { Checkbox = true } } @@ -221,7 +221,7 @@ public async Task ArchivePageAsync() var page = await _client.UpdateAsync(pageId, pagesUpdateParameters); page.Id.Should().Be(pageId); - page.IsArchived.Should().BeTrue(); + page.InTrash.Should().BeTrue(); page.Properties.Should().HaveCount(2); var updatedProperty = page.Properties.First(x => x.Key == "In stock"); diff --git a/Test/Notion.UnitTests/SearchClientTest.cs b/Test/Notion.UnitTests/SearchClientTest.cs index 35765cef..a5a477cb 100644 --- a/Test/Notion.UnitTests/SearchClientTest.cs +++ b/Test/Notion.UnitTests/SearchClientTest.cs @@ -59,7 +59,7 @@ public async Task Search() obj.Object.Should().Be(ObjectType.Page); var page = (Page)obj; - page.IsArchived.Should().BeFalse(); + page.InTrash.Should().BeFalse(); page.Properties.Should().HaveCount(1); page.Parent.Should().BeAssignableTo(); ((DatabaseParent)page.Parent).DatabaseId.Should().Be("e6c6f8ff-c70e-4970-91ba-98f03e0d7fc6"); diff --git a/Test/Notion.UnitTests/data/blocks/AppendBlockChildrenResponse.json b/Test/Notion.UnitTests/data/blocks/AppendBlockChildrenResponse.json index 32612405..13966312 100644 --- a/Test/Notion.UnitTests/data/blocks/AppendBlockChildrenResponse.json +++ b/Test/Notion.UnitTests/data/blocks/AppendBlockChildrenResponse.json @@ -7,6 +7,7 @@ "created_time": "2021-03-16T16:31:00.000Z", "last_edited_time": "2021-03-16T16:32:00.000Z", "has_children": false, + "in_trash" : false, "type": "heading_2", "heading_2": { "rich_text": [ diff --git a/Test/Notion.UnitTests/data/blocks/RetrieveBlockChildrenResponse.json b/Test/Notion.UnitTests/data/blocks/RetrieveBlockChildrenResponse.json index 7b0ae104..b16dfc35 100644 --- a/Test/Notion.UnitTests/data/blocks/RetrieveBlockChildrenResponse.json +++ b/Test/Notion.UnitTests/data/blocks/RetrieveBlockChildrenResponse.json @@ -7,7 +7,7 @@ "created_time": "2021-05-29T16:22:00.000Z", "last_edited_time": "2021-05-29T16:22:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "paragraph", "paragraph": { "rich_text": [] @@ -19,7 +19,7 @@ "created_time": "2021-05-30T09:25:00.000Z", "last_edited_time": "2021-05-30T09:25:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "heading_2", "heading_2": { "rich_text": [ @@ -49,7 +49,7 @@ "created_time": "2021-05-30T09:36:00.000Z", "last_edited_time": "2021-05-30T09:36:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "heading_2", "heading_2": { "rich_text": [ @@ -79,7 +79,7 @@ "created_time": "2021-08-18T21:12:00.000Z", "last_edited_time": "2021-08-18T21:12:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "paragraph", "paragraph": { "rich_text": [] @@ -91,7 +91,7 @@ "created_time": "2021-08-18T23:00:00.000Z", "last_edited_time": "2021-08-18T23:00:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "child_page", "child_page": { "title": "" @@ -103,7 +103,7 @@ "created_time": "2021-08-18T23:00:00.000Z", "last_edited_time": "2021-08-18T23:00:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "child_page", "child_page": { "title": "" @@ -115,7 +115,7 @@ "created_time": "2021-09-09T05:22:00.000Z", "last_edited_time": "2021-09-09T05:22:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "paragraph", "paragraph": { "rich_text": [] @@ -127,7 +127,7 @@ "created_time": "2022-05-03T13:28:00.000Z", "last_edited_time": "2022-05-03T13:29:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "image", "image": { "caption": [ @@ -174,7 +174,7 @@ "id": "92e803ec-193c-4bcd-b28e-88365858d562" }, "has_children": true, - "archived": false, + "in_trash": false, "type": "synced_block", "synced_block": { "synced_from": { diff --git a/Test/Notion.UnitTests/data/blocks/RetrieveBlockResponse.json b/Test/Notion.UnitTests/data/blocks/RetrieveBlockResponse.json index 9c7deabe..6df6ddb2 100644 --- a/Test/Notion.UnitTests/data/blocks/RetrieveBlockResponse.json +++ b/Test/Notion.UnitTests/data/blocks/RetrieveBlockResponse.json @@ -4,6 +4,7 @@ "created_time": "2021-03-16T16:31:00.000Z", "last_edited_time": "2021-03-16T16:32:00.000Z", "has_children": false, + "in_trash" : false, "type": "to_do", "to_do": { "rich_text": [ diff --git a/Test/Notion.UnitTests/data/blocks/UpdateBlockResponse.json b/Test/Notion.UnitTests/data/blocks/UpdateBlockResponse.json index 497aba34..c2d3a2c4 100644 --- a/Test/Notion.UnitTests/data/blocks/UpdateBlockResponse.json +++ b/Test/Notion.UnitTests/data/blocks/UpdateBlockResponse.json @@ -4,6 +4,7 @@ "created_time": "2021-03-16T16:31:00.000Z", "last_edited_time": "2021-03-16T16:32:00.000Z", "has_children": false, + "in_trash" : false, "type": "to_do", "to_do": { "rich_text": [ diff --git a/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json b/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json index 9b5cf45f..5849bb88 100644 --- a/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json +++ b/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json @@ -33,6 +33,7 @@ } }, "url": "https://www.notion.so/668d797c76fa49349b05ad288df2d136", + "in_trash" : false, "properties": { "Tags": { "id": "YG~h", diff --git a/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json b/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json index 27e70234..cf1b1f01 100644 --- a/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json +++ b/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json @@ -10,7 +10,7 @@ "type": "database_id", "database_id": "897e5a76-ae52-4b48-9fdf-e71f5945d1af" }, - "archived": false, + "in_trash": false, "url": "https://www.notion.so/2e01e904febd43a0ad028eedb903a82c", "properties": { "Recipes": { diff --git a/Test/Notion.UnitTests/data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json b/Test/Notion.UnitTests/data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json index 8c1b95c0..00159d76 100644 --- a/Test/Notion.UnitTests/data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json +++ b/Test/Notion.UnitTests/data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json @@ -12,7 +12,7 @@ "type": "database_id", "database_id": "f86f2262-0751-40f2-8f63-e3f7a3c39fcb" }, - "archived": false, + "in_trash": false, "properties": { "Column": { "id": "B[\\E", diff --git a/Test/Notion.UnitTests/data/pages/CreatePageResponse.json b/Test/Notion.UnitTests/data/pages/CreatePageResponse.json index 89f0e58e..a801949b 100644 --- a/Test/Notion.UnitTests/data/pages/CreatePageResponse.json +++ b/Test/Notion.UnitTests/data/pages/CreatePageResponse.json @@ -3,7 +3,7 @@ "id": "251d2b5f-268c-4de2-afe9-c71ff92ca95c", "created_time": "2020-03-17T19:10:04.968Z", "last_edited_time": "2020-03-17T21:49:37.913Z", - "archived": false, + "in_trash": false, "url": "https://www.notion.so/Test-251d2b5f268c4de2afe9c71ff92ca95c", "properties": { "Name": { diff --git a/Test/Notion.UnitTests/data/pages/PageObjectShouldHaveUrlPropertyResponse.json b/Test/Notion.UnitTests/data/pages/PageObjectShouldHaveUrlPropertyResponse.json index 8f9d45ab..bf5ba373 100644 --- a/Test/Notion.UnitTests/data/pages/PageObjectShouldHaveUrlPropertyResponse.json +++ b/Test/Notion.UnitTests/data/pages/PageObjectShouldHaveUrlPropertyResponse.json @@ -7,7 +7,7 @@ "type": "database_id", "database_id": "48f8fee9-cd79-4180-bc2f-ec0398253067" }, - "archived": false, + "in_trash": false, "url": "https://www.notion.so/Avocado-251d2b5f268c4de2afe9c71ff92ca95c", "properties": { "Name": { diff --git a/Test/Notion.UnitTests/data/pages/ArchivePageResponse.json b/Test/Notion.UnitTests/data/pages/TrashPageResponse.json similarity index 97% rename from Test/Notion.UnitTests/data/pages/ArchivePageResponse.json rename to Test/Notion.UnitTests/data/pages/TrashPageResponse.json index 2f9af7c6..0c61a3c9 100644 --- a/Test/Notion.UnitTests/data/pages/ArchivePageResponse.json +++ b/Test/Notion.UnitTests/data/pages/TrashPageResponse.json @@ -3,7 +3,7 @@ "id": "251d2b5f-268c-4de2-afe9-c71ff92ca95c", "created_time": "2020-03-17T19:10:04.968Z", "last_edited_time": "2020-03-17T21:49:37.913Z", - "archived": true, + "in_trash": true, "url": "https://www.notion.so/Test-251d2b5f268c4de2afe9c71ff92ca95c", "properties": { "In stock": { diff --git a/Test/Notion.UnitTests/data/pages/UpdatePagePropertiesResponse.json b/Test/Notion.UnitTests/data/pages/UpdatePagePropertiesResponse.json index 12076368..70cc2164 100644 --- a/Test/Notion.UnitTests/data/pages/UpdatePagePropertiesResponse.json +++ b/Test/Notion.UnitTests/data/pages/UpdatePagePropertiesResponse.json @@ -3,7 +3,7 @@ "id": "251d2b5f-268c-4de2-afe9-c71ff92ca95c", "created_time": "2020-03-17T19:10:04.968Z", "last_edited_time": "2020-03-17T21:49:37.913Z", - "archived": false, + "in_trash": false, "url": "https://www.notion.so/Test-251d2b5f268c4de2afe9c71ff92ca95c", "properties": { "In stock": { diff --git a/Test/Notion.UnitTests/data/search/SearchResponse.json b/Test/Notion.UnitTests/data/search/SearchResponse.json index 107adcb3..53defa57 100644 --- a/Test/Notion.UnitTests/data/search/SearchResponse.json +++ b/Test/Notion.UnitTests/data/search/SearchResponse.json @@ -4,6 +4,7 @@ "object": "list", "results": [ { + "in_trash": false, "created_time": "2021-04-22T22:23:26.080Z", "id": "e6c6f8ff-c70e-4970-91ba-98f03e0d7fc6", "last_edited_time": "2021-04-23T04:21:00.000Z", @@ -43,7 +44,7 @@ ] }, { - "archived": false, + "in_trash": false, "created_time": "2021-04-23T04:21:00.000Z", "id": "4f555b50-3a9b-49cb-924c-3746f4ca5522", "last_edited_time": "2021-04-23T04:21:00.000Z", From 609e04dc8b9ef3969d3c214af95e5ddaf18e8b13 Mon Sep 17 00:00:00 2001 From: kosaku-hayashi Date: Sun, 14 Jul 2024 17:01:31 +0900 Subject: [PATCH 35/71] Added support for button properties. --- .../Database/Properties/ButtonProperty.cs | 16 ++++++++++++++++ .../Models/Database/Properties/Property.cs | 1 + .../Models/Database/Properties/PropertyType.cs | 3 +++ .../Models/PropertyValue/ButtonPropertyValue.cs | 17 +++++++++++++++++ .../Models/PropertyValue/PropertyValue.cs | 1 + .../Models/PropertyValue/PropertyValueType.cs | 3 +++ Test/Notion.UnitTests/PagesClientTests.cs | 2 +- Test/Notion.UnitTests/PropertyTests.cs | 2 ++ .../databases/DatabaseRetrieveResponse.json | 6 ++++++ .../data/databases/DatabasesListResponse.json | 6 ++++++ .../data/databases/DatabasesQueryResponse.json | 5 +++++ .../data/pages/TrashPageResponse.json | 5 +++++ 12 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 Src/Notion.Client/Models/Database/Properties/ButtonProperty.cs create mode 100644 Src/Notion.Client/Models/PropertyValue/ButtonPropertyValue.cs diff --git a/Src/Notion.Client/Models/Database/Properties/ButtonProperty.cs b/Src/Notion.Client/Models/Database/Properties/ButtonProperty.cs new file mode 100644 index 00000000..983138e2 --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/ButtonProperty.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public class ButtonProperty : Property + { + public override PropertyType Type => PropertyType.Button; + + [JsonProperty("button")] + public Dictionary Button { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/Property.cs b/Src/Notion.Client/Models/Database/Properties/Property.cs index fa0dac50..6392bbaf 100644 --- a/Src/Notion.Client/Models/Database/Properties/Property.cs +++ b/Src/Notion.Client/Models/Database/Properties/Property.cs @@ -26,6 +26,7 @@ namespace Notion.Client [JsonSubtypes.KnownSubTypeAttribute(typeof(TitleProperty), PropertyType.Title)] [JsonSubtypes.KnownSubTypeAttribute(typeof(UrlProperty), PropertyType.Url)] [JsonSubtypes.KnownSubTypeAttribute(typeof(UniqueIdProperty), PropertyType.UniqueId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ButtonProperty), PropertyType.Button)] public class Property { [JsonProperty("id")] diff --git a/Src/Notion.Client/Models/Database/Properties/PropertyType.cs b/Src/Notion.Client/Models/Database/Properties/PropertyType.cs index 6391308c..206f9fe2 100644 --- a/Src/Notion.Client/Models/Database/Properties/PropertyType.cs +++ b/Src/Notion.Client/Models/Database/Properties/PropertyType.cs @@ -71,5 +71,8 @@ public enum PropertyType [EnumMember(Value = "unique_id")] UniqueId, + + [EnumMember(Value = "button")] + Button, } } diff --git a/Src/Notion.Client/Models/PropertyValue/ButtonPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/ButtonPropertyValue.cs new file mode 100644 index 00000000..61ae9fc4 --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/ButtonPropertyValue.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ButtonPropertyValue : PropertyValue + { + public override PropertyValueType Type => PropertyValueType.Button; + + [JsonProperty("button")] + public ButtonValue Button { get; set; } + + public class ButtonValue { } + } +} diff --git a/Src/Notion.Client/Models/PropertyValue/PropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/PropertyValue.cs index a74b11da..e1289046 100644 --- a/Src/Notion.Client/Models/PropertyValue/PropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/PropertyValue.cs @@ -30,6 +30,7 @@ namespace Notion.Client [JsonSubtypes.KnownSubTypeAttribute(typeof(TitlePropertyValue), PropertyValueType.Title)] [JsonSubtypes.KnownSubTypeAttribute(typeof(UrlPropertyValue), PropertyValueType.Url)] [JsonSubtypes.KnownSubTypeAttribute(typeof(UniqueIdPropertyValue), PropertyValueType.UniqueId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ButtonPropertyValue), PropertyValueType.Button)] [JsonSubtypes.KnownSubTypeAttribute(typeof(VerificationPropertyValue), PropertyValueType.Verification)] [SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")] diff --git a/Src/Notion.Client/Models/PropertyValue/PropertyValueType.cs b/Src/Notion.Client/Models/PropertyValue/PropertyValueType.cs index d77a564c..574138e9 100644 --- a/Src/Notion.Client/Models/PropertyValue/PropertyValueType.cs +++ b/Src/Notion.Client/Models/PropertyValue/PropertyValueType.cs @@ -77,5 +77,8 @@ public enum PropertyValueType [EnumMember(Value = "verification")] Verification, + + [EnumMember(Value = "button")] + Button, } } diff --git a/Test/Notion.UnitTests/PagesClientTests.cs b/Test/Notion.UnitTests/PagesClientTests.cs index 9541f62e..4dc75380 100644 --- a/Test/Notion.UnitTests/PagesClientTests.cs +++ b/Test/Notion.UnitTests/PagesClientTests.cs @@ -222,7 +222,7 @@ public async Task TrashPageAsync() page.Id.Should().Be(pageId); page.InTrash.Should().BeTrue(); - page.Properties.Should().HaveCount(2); + page.Properties.Should().HaveCount(3); var updatedProperty = page.Properties.First(x => x.Key == "In stock"); var checkboxPropertyValue = (CheckboxPropertyItem)await _client.RetrievePagePropertyItemAsync( diff --git a/Test/Notion.UnitTests/PropertyTests.cs b/Test/Notion.UnitTests/PropertyTests.cs index fc748413..55f6d1c1 100644 --- a/Test/Notion.UnitTests/PropertyTests.cs +++ b/Test/Notion.UnitTests/PropertyTests.cs @@ -27,6 +27,7 @@ public class PropertyTests [InlineData(typeof(TitleProperty), PropertyType.Title)] [InlineData(typeof(UrlProperty), PropertyType.Url)] [InlineData(typeof(UniqueIdProperty), PropertyType.UniqueId)] + [InlineData(typeof(ButtonProperty),PropertyType.Button)] public void TestPropertyType(Type type, PropertyType expectedPropertyType) { var typeInstance = (Property)Activator.CreateInstance(type); @@ -56,6 +57,7 @@ public void TestPropertyType(Type type, PropertyType expectedPropertyType) [InlineData(typeof(TitleProperty), "title")] [InlineData(typeof(UrlProperty), "url")] [InlineData(typeof(UniqueIdProperty), "unique_id")] + [InlineData(typeof(ButtonProperty), "button")] public void TestPropertyTypeText(Type type, string expectedPropertyType) { var typeInstance = (Property)Activator.CreateInstance(type); diff --git a/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json b/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json index 5849bb88..9e1acfb0 100644 --- a/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json +++ b/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json @@ -90,6 +90,12 @@ } } }, + "SimpleButton": { + "id":"_ri%7C", + "name":"SimpleButton", + "type":"button", + "button": {} + }, "Name": { "id": "title", "name": "Name", diff --git a/Test/Notion.UnitTests/data/databases/DatabasesListResponse.json b/Test/Notion.UnitTests/data/databases/DatabasesListResponse.json index 713c0736..ad0e5497 100644 --- a/Test/Notion.UnitTests/data/databases/DatabasesListResponse.json +++ b/Test/Notion.UnitTests/data/databases/DatabasesListResponse.json @@ -81,6 +81,12 @@ } } }, + "SimpleButton": { + "id":"_ri%7C", + "name":"SimpleButton", + "type":"button", + "button": {} + }, "Name": { "id": "title", "name": "Name", diff --git a/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json b/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json index cf1b1f01..d291a5b2 100644 --- a/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json +++ b/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json @@ -45,6 +45,11 @@ "id": "{>U;", "type": "checkbox", "checkbox": false + }, + "Add Page Button": { + "id":"_ri%7C", + "type":"button", + "button": {} } } } diff --git a/Test/Notion.UnitTests/data/pages/TrashPageResponse.json b/Test/Notion.UnitTests/data/pages/TrashPageResponse.json index 0c61a3c9..add1f546 100644 --- a/Test/Notion.UnitTests/data/pages/TrashPageResponse.json +++ b/Test/Notion.UnitTests/data/pages/TrashPageResponse.json @@ -11,6 +11,11 @@ "type": "checkbox", "checkbox": true }, + "Add Page Button": { + "id":"_ri%7C", + "type":"button", + "button": {} + }, "Name": { "id": "title", "type": "title", From 0459c906c0c4f92fec3da8da930cdc74f9c81f44 Mon Sep 17 00:00:00 2001 From: kosaku-hayashi Date: Tue, 16 Jul 2024 14:28:46 +0900 Subject: [PATCH 36/71] Support "bad_gateway" error code --- Src/Notion.Client/NotionAPIErrorCode.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Src/Notion.Client/NotionAPIErrorCode.cs b/Src/Notion.Client/NotionAPIErrorCode.cs index e0264ae7..260c25b5 100644 --- a/Src/Notion.Client/NotionAPIErrorCode.cs +++ b/Src/Notion.Client/NotionAPIErrorCode.cs @@ -42,6 +42,9 @@ public enum NotionAPIErrorCode [EnumMember(Value = "internal_server_error")] InternalServerError, + [EnumMember(Value = "bad_gateway")] + BadGateway, + [EnumMember(Value = "service_unavailable")] ServiceUnavailable, From 54f5fd32330b903afff7cc65eeecd51832e060e6 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:28:05 +0530 Subject: [PATCH 37/71] Use DateTimeOffset instead of DateTime for DatePropertyValue --- .../Models/PropertyValue/DatePropertyValue.cs | 7 ++- .../PageClientTests.cs | 61 ++++++++++++++++++- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs index 22715b2e..7467936b 100644 --- a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs @@ -1,5 +1,6 @@ using System; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { @@ -26,13 +27,15 @@ public class Date /// Start date with optional time. /// [JsonProperty("start")] - public DateTime? Start { get; set; } + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTimeOffset? Start { get; set; } /// /// End date with optional time. /// [JsonProperty("end")] - public DateTime? End { get; set; } + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTimeOffset? End { get; set; } /// /// Optional time zone information for start and end. Possible values are extracted from the IANA database and they are diff --git a/Test/Notion.IntegrationTests/PageClientTests.cs b/Test/Notion.IntegrationTests/PageClientTests.cs index 21adbd5b..31678d73 100644 --- a/Test/Notion.IntegrationTests/PageClientTests.cs +++ b/Test/Notion.IntegrationTests/PageClientTests.cs @@ -216,8 +216,8 @@ public async Task Test_UpdatePageProperty_with_date_as_null() { Date = new Date { - Start = Convert.ToDateTime("2020-12-08T12:00:00Z"), - End = Convert.ToDateTime("2025-12-08T12:00:00Z") + Start = DateTimeOffset.Parse("2024-06-26T00:00:00.000+01:00"), + End = DateTimeOffset.Parse("2025-12-08").Date } }) .Build(); @@ -236,7 +236,8 @@ public async Task Test_UpdatePageProperty_with_date_as_null() ); // Assert - setDate?.Date?.Start.Should().Be(Convert.ToDateTime("2020-12-08T12:00:00Z")); + setDate?.Date?.Start.Should().Be(DateTimeOffset.Parse("2024-06-26T00:00:00.000+01:00")); + setDate?.Date?.End.Should().Be(DateTimeOffset.Parse("2025-12-08T00:00:00.000+01:00")); var pageUpdateParameters = new PagesUpdateParameters { @@ -384,4 +385,58 @@ public async Task Bug_exception_when_attempting_to_set_select_property_to_nothin updatedPage.Properties["Colors1"].As().Select.Name.Should().Be("Blue"); updatedPage.Properties["Colors2"].As().Select.Should().BeNull(); } + + [Fact] + public async Task Verify_date_property_is_parsed_correctly_in_mention_object() + { + var pageRequest = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextMention() + { + Mention = new Mention() + { + Page = new ObjectId() + { + Id = _page.Id, + }, + Date = new DatePropertyValue() + { + Date = new Date() + { + Start = DateTime.UtcNow + } + } + } + } + } + }) + .Build(); + + var page = await Client.Pages.CreateAsync(pageRequest); + + page.Should().NotBeNull(); + + page.Parent.Should().BeOfType().Which + .DatabaseId.Should().Be(_database.Id); + + page.Properties.Should().ContainKey("Name"); + var pageProperty = page.Properties["Name"].Should().BeOfType().Subject; + + var titleProperty = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = pageProperty.Id + }); + + titleProperty.Results.First() + .As() + .Title.As() + .Mention.Date.Date.Should().NotBeNull(); + } } From 8fc28489095a964fe306228bd780640b8c973a95 Mon Sep 17 00:00:00 2001 From: Marian Zagoruiko Date: Mon, 13 Jan 2025 12:59:57 +0200 Subject: [PATCH 38/71] fix: allow setting properties to null --- .gitignore | 2 ++ Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs | 2 +- Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs | 2 +- .../Models/PropertyValue/PhoneNumberPropertyValue.cs | 2 +- Src/Notion.Client/Models/PropertyValue/StatusPropertyValue.cs | 2 +- Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index c6d337c2..5072687a 100644 --- a/.gitignore +++ b/.gitignore @@ -354,3 +354,5 @@ MigrationBackup/ # Rider .idea/ + +.DS_Store diff --git a/Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs index 5a8e2c7a..a8ac4806 100644 --- a/Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs @@ -12,7 +12,7 @@ public class EmailPropertyValue : PropertyValue /// /// Describes an email address. /// - [JsonProperty("email")] + [JsonProperty("email", NullValueHandling = NullValueHandling.Include)] public string Email { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs index 15f9c768..a709e4f5 100644 --- a/Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs @@ -12,7 +12,7 @@ public class NumberPropertyValue : PropertyValue /// /// Value of number /// - [JsonProperty("number")] + [JsonProperty("number", NullValueHandling = NullValueHandling.Include)] public double? Number { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyValue/PhoneNumberPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/PhoneNumberPropertyValue.cs index 79a24dac..f7c37a19 100644 --- a/Src/Notion.Client/Models/PropertyValue/PhoneNumberPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/PhoneNumberPropertyValue.cs @@ -12,7 +12,7 @@ public class PhoneNumberPropertyValue : PropertyValue /// /// Phone number value /// - [JsonProperty("phone_number")] + [JsonProperty("phone_number", NullValueHandling = NullValueHandling.Include)] public string PhoneNumber { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyValue/StatusPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/StatusPropertyValue.cs index 29a64ef7..921ff810 100644 --- a/Src/Notion.Client/Models/PropertyValue/StatusPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/StatusPropertyValue.cs @@ -47,7 +47,7 @@ public enum StatusColor public override PropertyValueType Type => PropertyValueType.Status; - [JsonProperty("status")] + [JsonProperty("status", NullValueHandling = NullValueHandling.Include)] public Data Status { get; set; } public class Data diff --git a/Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs index 5fc8f8bb..91b5f71c 100644 --- a/Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs @@ -12,7 +12,7 @@ public class UrlPropertyValue : PropertyValue /// /// Describes a web address /// - [JsonProperty("url")] + [JsonProperty("url", NullValueHandling = NullValueHandling.Include)] public string Url { get; set; } } } From 34ecf9d7f706a53f453be34ddb9ccfb44b4a3b7f Mon Sep 17 00:00:00 2001 From: Gehongyan Date: Sat, 22 Feb 2025 11:22:16 +0800 Subject: [PATCH 39/71] Add UpdateDatabaseRelationProperties Test --- .../DatabasesClientTests.cs | 63 ++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/Test/Notion.IntegrationTests/DatabasesClientTests.cs b/Test/Notion.IntegrationTests/DatabasesClientTests.cs index 31b611f1..d5278519 100644 --- a/Test/Notion.IntegrationTests/DatabasesClientTests.cs +++ b/Test/Notion.IntegrationTests/DatabasesClientTests.cs @@ -29,7 +29,7 @@ public async Task DisposeAsync() public async Task QueryDatabase() { // Arrange - var createdDatabase = await CreateDatabaseWithAPageAsync(); + var createdDatabase = await CreateDatabaseWithAPageAsync("Test List"); // Act var response = await Client.Databases.QueryAsync(createdDatabase.Id, new DatabasesQueryParameters()); @@ -43,7 +43,64 @@ public async Task QueryDatabase() .Text.Content.Should().Be("Test Title"); } - private async Task CreateDatabaseWithAPageAsync() + [Fact] + public async Task UpdateDatabaseRelationProperties() + { + // Arrange + var createdSourceDatabase = await CreateDatabaseWithAPageAsync("Test Relation Source"); + var createdDestinationDatabase = await CreateDatabaseWithAPageAsync("Test Relation Destination"); + + // Act + var response = await Client.Databases.UpdateAsync(createdDestinationDatabase.Id, + new DatabasesUpdateParameters + { + Properties = new Dictionary + { + { + "Single Relation", + new RelationUpdatePropertySchema + { + Relation = new SinglePropertyRelation + { + DatabaseId = createdSourceDatabase.Id, + SingleProperty = new Dictionary() + } + } + }, + { + "Dual Relation", + new RelationUpdatePropertySchema + { + Relation = new DualPropertyRelation + { + DatabaseId = createdSourceDatabase.Id, + DualProperty = new DualPropertyRelation.Data() + } + } + } + } + }); + + // Assert + response.Properties.Should().NotBeNull(); + + response.Properties.Should().ContainKey("Single Relation"); + var singleRelation = response.Properties["Single Relation"].As().Relation; + singleRelation.Should().BeEquivalentTo( + new SinglePropertyRelation + { + DatabaseId = createdSourceDatabase.Id, + SingleProperty = new Dictionary() + }); + + response.Properties.Should().ContainKey("Dual Relation"); + var dualRelation = response.Properties["Dual Relation"].As().Relation; + dualRelation.DatabaseId.Should().Be(createdSourceDatabase.Id); + dualRelation.Type.Should().Be(RelationType.Dual); + dualRelation.Should().BeOfType(); + } + + private async Task CreateDatabaseWithAPageAsync(string databaseName) { var createDbRequest = new DatabasesCreateParameters { @@ -53,7 +110,7 @@ private async Task CreateDatabaseWithAPageAsync() { Text = new Text { - Content = "Test List", + Content = databaseName, Link = null } } From cff7585c8aac2e09aacb66ccbfe306ba5f71bf03 Mon Sep 17 00:00:00 2001 From: Marian Zagoruiko Date: Wed, 15 Jan 2025 13:50:27 +0200 Subject: [PATCH 40/71] feat: include time option for dates --- .../Models/PropertyValue/DatePropertyValue.cs | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs index 7467936b..7d785212 100644 --- a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs @@ -21,6 +21,7 @@ public class DatePropertyValue : PropertyValue /// /// Date value object. /// + [JsonConverter(typeof(DateCustomConverter))] public class Date { /// @@ -43,5 +44,94 @@ public class Date /// [JsonProperty("time_zone")] public string TimeZone { get; set; } + + /// + /// Whether to include time + /// + public bool IncludeTime { get; set; } = true; + } + + public class DateJsonObject + { + [JsonProperty("start")] + public string Start { get; set; } + + [JsonProperty("end")] + public string End { get; set; } + + [JsonProperty("time_zone")] + public string TimeZone { get; set; } + } + + public class DateCustomConverter : JsonConverter + { + public override Date ReadJson(JsonReader reader, Type objectType, Date existingValue, bool hasExistingValue, + JsonSerializer serializer) + { + var jsonObject = serializer.Deserialize(reader); + + if (jsonObject == null) + { + return null; + } + + var date = new Date + { + Start = ParseDateTime(jsonObject.Start, out bool includeTime), + End = ParseDateTime(jsonObject.End, out _), + TimeZone = jsonObject.TimeZone, + IncludeTime = includeTime, + }; + + return date; + } + + public override void WriteJson(JsonWriter writer, Date value, JsonSerializer serializer) + { + if (value is null) + { + writer.WriteNull(); + + return; + } + + writer.WriteStartObject(); + + if (value.Start.HasValue) + { + string startFormat = value.IncludeTime ? "yyyy-MM-ddTHH:mm:ss" : "yyyy-MM-dd"; + writer.WritePropertyName("start"); + writer.WriteValue(value.Start.Value.ToString(startFormat)); + } + + if (value.End.HasValue) + { + string endFormat = value.IncludeTime ? "yyyy-MM-ddTHH:mm:ss" : "yyyy-MM-dd"; + writer.WritePropertyName("end"); + writer.WriteValue(value.End.Value.ToString(endFormat)); + } + + if (!string.IsNullOrEmpty(value.TimeZone)) + { + writer.WritePropertyName("time_zone"); + writer.WriteValue(value.TimeZone); + } + + writer.WriteEndObject(); + } + + private static DateTime? ParseDateTime(string dateTimeString, out bool includeTime) + { + includeTime = false; + + if (string.IsNullOrEmpty(dateTimeString)) + { + return null; + } + + includeTime = dateTimeString.Contains("T") || dateTimeString.Contains(" "); + + return DateTimeOffset.Parse(dateTimeString).UtcDateTime; + } } } From f41327e34abf0529a18006581580ec7005738f03 Mon Sep 17 00:00:00 2001 From: Marian Zagoruiko Date: Fri, 17 Jan 2025 18:10:12 +0200 Subject: [PATCH 41/71] feat: rollup date parsing --- Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs index 2eb104fb..92020c61 100644 --- a/Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs @@ -35,7 +35,7 @@ public class RollupValue /// Date rollup property values contain a date property value. /// [JsonProperty("date")] - public DatePropertyValue Date { get; set; } + public Date Date { get; set; } /// /// Array rollup property values contain an array of element objects. From 954c042c057a0c6fc5b3a2debad938fb40424f37 Mon Sep 17 00:00:00 2001 From: Marian Zagoruiko Date: Fri, 28 Mar 2025 10:06:16 +0200 Subject: [PATCH 42/71] fix: custom emoji support --- Src/Notion.Client/Models/CustomEmoji.cs | 16 ++++++++++++++++ Src/Notion.Client/Models/CustomEmojiObject.cs | 13 +++++++++++++ Src/Notion.Client/Models/Page/IPageIcon.cs | 1 + 3 files changed, 30 insertions(+) create mode 100644 Src/Notion.Client/Models/CustomEmoji.cs create mode 100644 Src/Notion.Client/Models/CustomEmojiObject.cs diff --git a/Src/Notion.Client/Models/CustomEmoji.cs b/Src/Notion.Client/Models/CustomEmoji.cs new file mode 100644 index 00000000..0f7a4c48 --- /dev/null +++ b/Src/Notion.Client/Models/CustomEmoji.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CustomEmoji + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + } +} diff --git a/Src/Notion.Client/Models/CustomEmojiObject.cs b/Src/Notion.Client/Models/CustomEmojiObject.cs new file mode 100644 index 00000000..1faa4d74 --- /dev/null +++ b/Src/Notion.Client/Models/CustomEmojiObject.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CustomEmojiObject : IPageIcon + { + [JsonProperty("custom_emoji")] + public CustomEmoji CustomEmoji { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Page/IPageIcon.cs b/Src/Notion.Client/Models/Page/IPageIcon.cs index b9ebea56..5d692185 100644 --- a/Src/Notion.Client/Models/Page/IPageIcon.cs +++ b/Src/Notion.Client/Models/Page/IPageIcon.cs @@ -5,6 +5,7 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] [JsonSubtypes.KnownSubTypeAttribute(typeof(EmojiObject), "emoji")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CustomEmojiObject), "custom_emoji")] [JsonSubtypes.KnownSubTypeAttribute(typeof(FileObject), "file")] [JsonSubtypes.KnownSubTypeAttribute(typeof(FileObject), "external")] public interface IPageIcon From 8ee7f0fd923e55406aa88a12bdd94409c8bf2c3e Mon Sep 17 00:00:00 2001 From: Marian Zagoruiko Date: Fri, 28 Mar 2025 10:28:28 +0200 Subject: [PATCH 43/71] fix: linter --- .../PropertyValue/DateCustomConverter.cs | 77 +++++++++++++++++ .../Models/PropertyValue/DateJsonObject.cs | 16 ++++ .../Models/PropertyValue/DatePropertyValue.cs | 84 ------------------- 3 files changed, 93 insertions(+), 84 deletions(-) create mode 100644 Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs create mode 100644 Src/Notion.Client/Models/PropertyValue/DateJsonObject.cs diff --git a/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs new file mode 100644 index 00000000..6823c303 --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs @@ -0,0 +1,77 @@ +using System; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class DateCustomConverter : JsonConverter + { + public override Date ReadJson(JsonReader reader, Type objectType, Date existingValue, bool hasExistingValue, + JsonSerializer serializer) + { + var jsonObject = serializer.Deserialize(reader); + + if (jsonObject == null) + { + return null; + } + + var date = new Date + { + Start = ParseDateTime(jsonObject.Start, out bool includeTime), + End = ParseDateTime(jsonObject.End, out _), + TimeZone = jsonObject.TimeZone, + IncludeTime = includeTime, + }; + + return date; + } + + public override void WriteJson(JsonWriter writer, Date value, JsonSerializer serializer) + { + if (value is null) + { + writer.WriteNull(); + + return; + } + + writer.WriteStartObject(); + + if (value.Start.HasValue) + { + string startFormat = value.IncludeTime ? "yyyy-MM-ddTHH:mm:ss" : "yyyy-MM-dd"; + writer.WritePropertyName("start"); + writer.WriteValue(value.Start.Value.ToString(startFormat)); + } + + if (value.End.HasValue) + { + string endFormat = value.IncludeTime ? "yyyy-MM-ddTHH:mm:ss" : "yyyy-MM-dd"; + writer.WritePropertyName("end"); + writer.WriteValue(value.End.Value.ToString(endFormat)); + } + + if (!string.IsNullOrEmpty(value.TimeZone)) + { + writer.WritePropertyName("time_zone"); + writer.WriteValue(value.TimeZone); + } + + writer.WriteEndObject(); + } + + private static DateTime? ParseDateTime(string dateTimeString, out bool includeTime) + { + includeTime = false; + + if (string.IsNullOrEmpty(dateTimeString)) + { + return null; + } + + includeTime = dateTimeString.Contains("T") || dateTimeString.Contains(" "); + + return DateTimeOffset.Parse(dateTimeString).UtcDateTime; + } + } +} diff --git a/Src/Notion.Client/Models/PropertyValue/DateJsonObject.cs b/Src/Notion.Client/Models/PropertyValue/DateJsonObject.cs new file mode 100644 index 00000000..b33b58c7 --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/DateJsonObject.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + internal class DateJsonObject + { + [JsonProperty("start")] + public string Start { get; set; } + + [JsonProperty("end")] + public string End { get; set; } + + [JsonProperty("time_zone")] + public string TimeZone { get; set; } + } +} diff --git a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs index 7d785212..b79707db 100644 --- a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs @@ -50,88 +50,4 @@ public class Date /// public bool IncludeTime { get; set; } = true; } - - public class DateJsonObject - { - [JsonProperty("start")] - public string Start { get; set; } - - [JsonProperty("end")] - public string End { get; set; } - - [JsonProperty("time_zone")] - public string TimeZone { get; set; } - } - - public class DateCustomConverter : JsonConverter - { - public override Date ReadJson(JsonReader reader, Type objectType, Date existingValue, bool hasExistingValue, - JsonSerializer serializer) - { - var jsonObject = serializer.Deserialize(reader); - - if (jsonObject == null) - { - return null; - } - - var date = new Date - { - Start = ParseDateTime(jsonObject.Start, out bool includeTime), - End = ParseDateTime(jsonObject.End, out _), - TimeZone = jsonObject.TimeZone, - IncludeTime = includeTime, - }; - - return date; - } - - public override void WriteJson(JsonWriter writer, Date value, JsonSerializer serializer) - { - if (value is null) - { - writer.WriteNull(); - - return; - } - - writer.WriteStartObject(); - - if (value.Start.HasValue) - { - string startFormat = value.IncludeTime ? "yyyy-MM-ddTHH:mm:ss" : "yyyy-MM-dd"; - writer.WritePropertyName("start"); - writer.WriteValue(value.Start.Value.ToString(startFormat)); - } - - if (value.End.HasValue) - { - string endFormat = value.IncludeTime ? "yyyy-MM-ddTHH:mm:ss" : "yyyy-MM-dd"; - writer.WritePropertyName("end"); - writer.WriteValue(value.End.Value.ToString(endFormat)); - } - - if (!string.IsNullOrEmpty(value.TimeZone)) - { - writer.WritePropertyName("time_zone"); - writer.WriteValue(value.TimeZone); - } - - writer.WriteEndObject(); - } - - private static DateTime? ParseDateTime(string dateTimeString, out bool includeTime) - { - includeTime = false; - - if (string.IsNullOrEmpty(dateTimeString)) - { - return null; - } - - includeTime = dateTimeString.Contains("T") || dateTimeString.Contains(" "); - - return DateTimeOffset.Parse(dateTimeString).UtcDateTime; - } - } } From 80d32df3b95035d044d62a73bf23184d1035633c Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sat, 12 Apr 2025 17:14:04 +0530 Subject: [PATCH 44/71] 431: Throw error if database id was not provided --- Src/Notion.Client/Api/Databases/DatabasesClient.cs | 8 +++++++- Test/Notion.UnitTests/DatabasesClientTests.cs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Src/Notion.Client/Api/Databases/DatabasesClient.cs b/Src/Notion.Client/Api/Databases/DatabasesClient.cs index 673ee122..a3dc9a8c 100644 --- a/Src/Notion.Client/Api/Databases/DatabasesClient.cs +++ b/Src/Notion.Client/Api/Databases/DatabasesClient.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; using static Notion.Client.ApiEndpoints; @@ -15,6 +16,11 @@ public DatabasesClient(IRestClient client) public async Task RetrieveAsync(string databaseId, CancellationToken cancellationToken = default) { + if (string.IsNullOrWhiteSpace(databaseId)) + { + throw new ArgumentNullException(nameof(databaseId)); + } + return await _client.GetAsync(DatabasesApiUrls.Retrieve(databaseId), cancellationToken: cancellationToken); } diff --git a/Test/Notion.UnitTests/DatabasesClientTests.cs b/Test/Notion.UnitTests/DatabasesClientTests.cs index 3bd047a7..1edbc2ed 100644 --- a/Test/Notion.UnitTests/DatabasesClientTests.cs +++ b/Test/Notion.UnitTests/DatabasesClientTests.cs @@ -504,4 +504,18 @@ var jsonData formulaPropertyValue.Formula.Date.End.Should().BeNull(); } } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public async Task RetrieveAsync_throws_argument_null_exception_if_database_id_is_null_or_empty(string databaseId) + { + // Arrange && Act + async Task Act() => await _client.RetrieveAsync(databaseId); + + // Assert + var exception = await Assert.ThrowsAsync(Act); + Assert.Equal("databaseId", exception.ParamName); + } } From 4836c4b863b43a767f686add471387b5b9b32c61 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sat, 12 Apr 2025 23:52:00 +0530 Subject: [PATCH 45/71] fix: enhance DateCustomSerializer and add tests * use ISO 8601 date and time format * fix breaking test --- .../PropertyValue/DateCustomConverter.cs | 9 +- Test/Notion.UnitTests/DatabasesClientTests.cs | 2 +- .../DateCustomConverterTests.cs | 219 ++++++++++++++++++ 3 files changed, 226 insertions(+), 4 deletions(-) create mode 100644 Test/Notion.UnitTests/DateCustomConverterTests.cs diff --git a/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs index 6823c303..d4af88f4 100644 --- a/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs +++ b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs @@ -5,6 +5,9 @@ namespace Notion.Client { public class DateCustomConverter : JsonConverter { + private const string DateFormat = "yyyy-MM-dd"; + private const string DateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ"; + public override Date ReadJson(JsonReader reader, Type objectType, Date existingValue, bool hasExistingValue, JsonSerializer serializer) { @@ -39,14 +42,14 @@ public override void WriteJson(JsonWriter writer, Date value, JsonSerializer ser if (value.Start.HasValue) { - string startFormat = value.IncludeTime ? "yyyy-MM-ddTHH:mm:ss" : "yyyy-MM-dd"; + string startFormat = value.IncludeTime ? DateTimeFormat : DateFormat; writer.WritePropertyName("start"); writer.WriteValue(value.Start.Value.ToString(startFormat)); } if (value.End.HasValue) { - string endFormat = value.IncludeTime ? "yyyy-MM-ddTHH:mm:ss" : "yyyy-MM-dd"; + string endFormat = value.IncludeTime ? DateTimeFormat : DateFormat; writer.WritePropertyName("end"); writer.WriteValue(value.End.Value.ToString(endFormat)); } @@ -71,7 +74,7 @@ public override void WriteJson(JsonWriter writer, Date value, JsonSerializer ser includeTime = dateTimeString.Contains("T") || dateTimeString.Contains(" "); - return DateTimeOffset.Parse(dateTimeString).UtcDateTime; + return DateTimeOffset.Parse(dateTimeString, null, System.Globalization.DateTimeStyles.AssumeUniversal).UtcDateTime; } } } diff --git a/Test/Notion.UnitTests/DatabasesClientTests.cs b/Test/Notion.UnitTests/DatabasesClientTests.cs index 1edbc2ed..30deae75 100644 --- a/Test/Notion.UnitTests/DatabasesClientTests.cs +++ b/Test/Notion.UnitTests/DatabasesClientTests.cs @@ -500,7 +500,7 @@ var jsonData }); //var formulaPropertyValue = (FormulaPropertyValue)page.Properties["FormulaProp"]; - formulaPropertyValue.Formula.Date.Start.Should().Be(DateTime.Parse("2021-06-28")); + formulaPropertyValue.Formula.Date.Start.Should().Be(DateTimeOffset.Parse("2021-06-28", null, System.Globalization.DateTimeStyles.AssumeUniversal).UtcDateTime); formulaPropertyValue.Formula.Date.End.Should().BeNull(); } } diff --git a/Test/Notion.UnitTests/DateCustomConverterTests.cs b/Test/Notion.UnitTests/DateCustomConverterTests.cs new file mode 100644 index 00000000..81c33f57 --- /dev/null +++ b/Test/Notion.UnitTests/DateCustomConverterTests.cs @@ -0,0 +1,219 @@ +using System; +using System.IO; +using Newtonsoft.Json; +using Notion.Client; +using Xunit; + +namespace NotionUnitTests.PropertyValue; + +public class DateCustomConverterTests +{ + private readonly DateCustomConverter _converter = new(); + private readonly JsonSerializer _serializer = new(); + + [Fact] + public void Serialize_null_writes_null() + { + // Arrange + Date date = null; + var stringWriter = new StringWriter(); + var jsonWriter = new JsonTextWriter(stringWriter); + + // Act + _converter.WriteJson(jsonWriter, date, _serializer); + jsonWriter.Flush(); + + // Assert + Assert.Equal("null", stringWriter.ToString()); + } + + [Fact] + public void Serialize_start_date_only_produces_correct_json() + { + // Arrange + var date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 0, 0, 0, TimeSpan.Zero), + IncludeTime = false + }; + + // Act + var json = JsonConvert.SerializeObject(date); + + // Assert + Assert.Contains("\"start\":\"2023-05-15\"", json); + Assert.DoesNotContain("\"end\":", json); + Assert.DoesNotContain("\"time_zone\":", json); + } + + [Fact] + public void Serialize_start_and_end_dates_produces_correct_json() + { + // Arrange + var date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 0, 0, 0, TimeSpan.Zero), + End = new DateTimeOffset(2023, 5, 20, 0, 0, 0, TimeSpan.Zero), + IncludeTime = false + }; + + // Act + var json = JsonConvert.SerializeObject(date); + + // Assert + Assert.Contains("\"start\":\"2023-05-15\"", json); + Assert.Contains("\"end\":\"2023-05-20\"", json); + Assert.DoesNotContain("\"time_zone\":", json); + } + + [Fact] + public void Serialize_with_time_included_formats_time_correctly() + { + // Arrange + var date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), + IncludeTime = true + }; + + // Act + var json = JsonConvert.SerializeObject(date); + + // Assert + Assert.Contains("\"start\":\"2023-05-15T14:30:45Z\"", json); + } + + [Fact] + public void Serialize_with_time_zone_includes_time_zone() + { + // Arrange + var date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), + TimeZone = "Europe/London", + IncludeTime = true + }; + + // Act + var json = JsonConvert.SerializeObject(date); + + // Assert + Assert.Contains("\"start\":\"2023-05-15T14:30:45Z\"", json); + Assert.Contains("\"time_zone\":\"Europe/London\"", json); + } + + [Fact] + public void Deserialize_null_returns_null() + { + // Arrange + const string Json = "null"; + + // Act + var result = JsonConvert.DeserializeObject(Json); + + // Assert + Assert.Null(result); + } + + [Fact] + public void Deserialize_start_date_only_returns_correct_date() + { + // Arrange + const string Json = "{\"start\":\"2023-05-15\"}"; + + // Act + var result = JsonConvert.DeserializeObject(Json); + + // Assert + Assert.NotNull(result); + Assert.Equal(new DateTimeOffset(2023, 5, 15, 0, 0, 0, TimeSpan.Zero), result.Start); + Assert.Null(result.End); + Assert.Null(result.TimeZone); + Assert.False(result.IncludeTime); + } + + [Fact] + public void Deserialize_with_time_sets_include_time_flag() + { + // Arrange + const string Json = "{\"start\":\"2023-05-15T14:30:45\"}"; + + // Act + var result = JsonConvert.DeserializeObject(Json); + + // Assert + Assert.NotNull(result); + Assert.Equal(new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), result.Start); + Assert.True(result.IncludeTime); + } + + [Fact] + public void Deserialize_with_start_end_and_time_zone_returns_complete_date() + { + // Arrange + const string Json = "{\"start\":\"2023-05-15T14:30:45\",\"end\":\"2023-05-20T16:45:00\",\"time_zone\":\"America/New_York\"}"; + + // Act + var result = JsonConvert.DeserializeObject(Json); + + // Assert + Assert.NotNull(result); + Assert.Equal(new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), result.Start); + Assert.Equal(new DateTimeOffset(2023, 5, 20, 16, 45, 0, TimeSpan.Zero), result.End); + Assert.Equal("America/New_York", result.TimeZone); + Assert.True(result.IncludeTime); + } + + [Fact] + public void Date_property_value_serialize_deserialize_maintains_data() + { + // Arrange + var datePropertyValue = new DatePropertyValue + { + Date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), + End = new DateTimeOffset(2023, 5, 20, 16, 45, 0, TimeSpan.Zero), + TimeZone = "Europe/Berlin", + IncludeTime = true + } + }; + + // Act + var json = JsonConvert.SerializeObject(datePropertyValue); + var result = JsonConvert.DeserializeObject(json); + + // Assert + Assert.NotNull(result); + Assert.Equal(PropertyValueType.Date, result.Type); + Assert.NotNull(result.Date); + Assert.Equal(datePropertyValue.Date.Start, result.Date.Start); + Assert.Equal(datePropertyValue.Date.End, result.Date.End); + Assert.Equal(datePropertyValue.Date.TimeZone, result.Date.TimeZone); + Assert.Equal(datePropertyValue.Date.IncludeTime, result.Date.IncludeTime); + } + + [Fact] + public void Round_trip_preserves_data() + { + // Arrange + var originalDate = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), + End = new DateTimeOffset(2023, 5, 20, 16, 45, 0, TimeSpan.Zero), + TimeZone = "Europe/Berlin", + IncludeTime = true + }; + + // Act + var json = JsonConvert.SerializeObject(originalDate); + var deserializedDate = JsonConvert.DeserializeObject(json); + + // Assert + Assert.NotNull(deserializedDate); + Assert.Equal(originalDate.Start, deserializedDate.Start); + Assert.Equal(originalDate.End, deserializedDate.End); + Assert.Equal(originalDate.TimeZone, deserializedDate.TimeZone); + Assert.True(deserializedDate.IncludeTime); + } +} From 42d7e798f663cf8189f09c6b6444c4771d89b6c0 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sat, 12 Apr 2025 23:52:36 +0530 Subject: [PATCH 46/71] add explicit JsonIgnore attribute to IncludeTime property in Date class --- Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs index b79707db..66f9c6b6 100644 --- a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs @@ -48,6 +48,7 @@ public class Date /// /// Whether to include time /// + [JsonIgnore] public bool IncludeTime { get; set; } = true; } } From 48ee22a8f1afecc05a8ae24f4b0d932b06b4baa9 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 00:49:23 +0530 Subject: [PATCH 47/71] ensure culture-invariant date formatting in DateCustomConverter --- .../Models/PropertyValue/DateCustomConverter.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs index d4af88f4..78f14cdb 100644 --- a/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs +++ b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using Newtonsoft.Json; namespace Notion.Client @@ -44,14 +45,14 @@ public override void WriteJson(JsonWriter writer, Date value, JsonSerializer ser { string startFormat = value.IncludeTime ? DateTimeFormat : DateFormat; writer.WritePropertyName("start"); - writer.WriteValue(value.Start.Value.ToString(startFormat)); + writer.WriteValue(value.Start.Value.ToString(startFormat, CultureInfo.InvariantCulture)); } if (value.End.HasValue) { string endFormat = value.IncludeTime ? DateTimeFormat : DateFormat; writer.WritePropertyName("end"); - writer.WriteValue(value.End.Value.ToString(endFormat)); + writer.WriteValue(value.End.Value.ToString(endFormat, CultureInfo.InvariantCulture)); } if (!string.IsNullOrEmpty(value.TimeZone)) @@ -74,7 +75,7 @@ public override void WriteJson(JsonWriter writer, Date value, JsonSerializer ser includeTime = dateTimeString.Contains("T") || dateTimeString.Contains(" "); - return DateTimeOffset.Parse(dateTimeString, null, System.Globalization.DateTimeStyles.AssumeUniversal).UtcDateTime; + return DateTimeOffset.Parse(dateTimeString, null, DateTimeStyles.AssumeUniversal).UtcDateTime; } } } From 4e9c6bebae87b314724e72942fe6243125debb9d Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 01:09:13 +0530 Subject: [PATCH 48/71] Use CultureInfo.InvariantCulture in DateTimeOffset.Parse to ensure consistent parsing of ISO 8601 dates across different environments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs index 78f14cdb..37b84bc5 100644 --- a/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs +++ b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs @@ -75,7 +75,7 @@ public override void WriteJson(JsonWriter writer, Date value, JsonSerializer ser includeTime = dateTimeString.Contains("T") || dateTimeString.Contains(" "); - return DateTimeOffset.Parse(dateTimeString, null, DateTimeStyles.AssumeUniversal).UtcDateTime; + return DateTimeOffset.Parse(dateTimeString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).UtcDateTime; } } } From 3ea8bc4541087ecd7d738a516aa05c4f73760322 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 00:36:28 +0530 Subject: [PATCH 49/71] fix: creation and parsing of Date Mentions --- .../DatabasesCreateParameters/MentionInput.cs | 2 +- .../Database/RichText/RichTextMention.cs | 2 +- .../DatabasesClientTests.cs | 47 ++++++++++++++++++- .../PageClientTests.cs | 20 +++----- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/MentionInput.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/MentionInput.cs index 83d1e699..48b34948 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/MentionInput.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/MentionInput.cs @@ -17,6 +17,6 @@ public class MentionInput public ObjectId Database { get; set; } [JsonProperty("date")] - public DatePropertyValue Date { get; set; } + public Date Date { get; set; } } } diff --git a/Src/Notion.Client/Models/Database/RichText/RichTextMention.cs b/Src/Notion.Client/Models/Database/RichText/RichTextMention.cs index 02f6beb0..5a5b6d0c 100644 --- a/Src/Notion.Client/Models/Database/RichText/RichTextMention.cs +++ b/Src/Notion.Client/Models/Database/RichText/RichTextMention.cs @@ -25,7 +25,7 @@ public class Mention public ObjectId Database { get; set; } [JsonProperty("date")] - public DatePropertyValue Date { get; set; } + public Date Date { get; set; } } public class ObjectId diff --git a/Test/Notion.IntegrationTests/DatabasesClientTests.cs b/Test/Notion.IntegrationTests/DatabasesClientTests.cs index d5278519..7abd7b4e 100644 --- a/Test/Notion.IntegrationTests/DatabasesClientTests.cs +++ b/Test/Notion.IntegrationTests/DatabasesClientTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -140,4 +141,48 @@ private async Task CreateDatabaseWithAPageAsync(string databaseName) return createdDatabase; } + + [Fact] + public async Task Verify_mention_date_property_parsed_properly() + { + // Arrange + var createDbRequest = new DatabasesCreateParameters + { + Title = new List + { + new RichTextTextInput + { + Text = new Text + { + Content = "Test DB", + Link = null + } + }, + new RichTextMentionInput + { + Mention = new MentionInput + { + Date = new Date + { + Start = DateTime.UtcNow, + End = DateTime.UtcNow.AddDays(1) + } + } + } + }, + Properties = new Dictionary + { + { "Name", new TitlePropertySchema { Title = new Dictionary() } }, + }, + Parent = new ParentPageInput { PageId = _page.Id } + }; + + // Act + var createdDatabase = await Client.Databases.CreateAsync(createDbRequest); + + // Assert + var mention = createdDatabase.Title.OfType().First().Mention; + mention.Date.Start.Should().NotBeNull(); + mention.Date.End.Should().NotBeNull(); + } } diff --git a/Test/Notion.IntegrationTests/PageClientTests.cs b/Test/Notion.IntegrationTests/PageClientTests.cs index 31678d73..4938bcfa 100644 --- a/Test/Notion.IntegrationTests/PageClientTests.cs +++ b/Test/Notion.IntegrationTests/PageClientTests.cs @@ -400,23 +400,16 @@ public async Task Verify_date_property_is_parsed_correctly_in_mention_object() { Mention = new Mention() { - Page = new ObjectId() + Date = new Date() { - Id = _page.Id, - }, - Date = new DatePropertyValue() - { - Date = new Date() - { - Start = DateTime.UtcNow - } + Start = DateTime.UtcNow } } } } }) .Build(); - + var page = await Client.Pages.CreateAsync(pageRequest); page.Should().NotBeNull(); @@ -434,9 +427,8 @@ public async Task Verify_date_property_is_parsed_correctly_in_mention_object() PropertyId = pageProperty.Id }); - titleProperty.Results.First() - .As() - .Title.As() - .Mention.Date.Date.Should().NotBeNull(); + var mention = titleProperty.Results.First().As().Title.As().Mention; + mention.Date.Start.Should().NotBeNull(); + mention.Date.End.Should().BeNull(); } } From ae94eb5209d16464d1a410cd3e486a9efa5dcc36 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 12:27:38 +0530 Subject: [PATCH 50/71] =?UTF-8?q?add=20support=20for=20reading=20and=20wri?= =?UTF-8?q?ting=20Name=20&=20Caption=20properties=20to=20file=20blocks=20?= =?UTF-8?q?=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../File/FIleInput/ExternalFileInput.cs | 7 ++- .../Models/File/FIleInput/IFileObjectInput.cs | 10 ++++- .../File/FIleInput/UploadedFileInput.cs | 5 +++ .../BlocksClientTests.cs | 45 +++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs b/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs index 9c258ceb..38ad781c 100644 --- a/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Notion.Client { @@ -7,6 +8,10 @@ public class ExternalFileInput : IFileObjectInput [JsonProperty("external")] public Data External { get; set; } + public string Name { get; set; } + + public IEnumerable Caption { get; set; } + public class Data { [JsonProperty("url")] diff --git a/Src/Notion.Client/Models/File/FIleInput/IFileObjectInput.cs b/Src/Notion.Client/Models/File/FIleInput/IFileObjectInput.cs index 31df1573..5883b7c1 100644 --- a/Src/Notion.Client/Models/File/FIleInput/IFileObjectInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/IFileObjectInput.cs @@ -1,6 +1,14 @@ -namespace Notion.Client +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client { public interface IFileObjectInput { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } } } diff --git a/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs b/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs index 52d7934d..dabca4e1 100644 --- a/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Newtonsoft.Json; namespace Notion.Client @@ -8,6 +9,10 @@ public class UploadedFileInput : IFileObjectInput [JsonProperty("file")] public Data File { get; set; } + public string Name { get; set; } + + public IEnumerable Caption { get; set; } + public class Data { [JsonProperty("url")] diff --git a/Test/Notion.IntegrationTests/BlocksClientTests.cs b/Test/Notion.IntegrationTests/BlocksClientTests.cs index 5c8e4618..d52b8f64 100644 --- a/Test/Notion.IntegrationTests/BlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/BlocksClientTests.cs @@ -399,6 +399,51 @@ private static IEnumerable BlockData() .Subject.Should().BeOfType() .Subject.Text.Content.Should().Be("Data"); }) + }, + new object[] + { + new FileBlockRequest { + File = new ExternalFile + { + Name = "Test file", + External = new ExternalFile.Info + { + Url = "https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg" + }, + Caption = new List + { + new RichTextTextInput { Text = new Text { Content = "Test file" } } + } + } + }, + new FileUpdateBlock + { + File = new ExternalFileInput + { + Name = "Test file name", + External = new ExternalFileInput.Data + { + Url = "https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg" + }, + Caption = new List + { + new RichTextTextInput { Text = new Text { Content = "Test file caption" } } + } + } + }, + new Action((block, client) => + { + var fileBlock = block.Should().NotBeNull().And.BeOfType().Subject; + fileBlock.HasChildren.Should().BeFalse(); + + var file = fileBlock.File.Should().NotBeNull().And.BeOfType().Subject; + file.Name.Should().Be("Test file name.jpg"); + file.External.Should().NotBeNull(); + file.External.Url.Should().Be("https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg"); + file.Caption.Should().NotBeNull().And.ContainSingle() + .Subject.Should().BeOfType().Subject + .Text.Content.Should().Be("Test file caption"); + }) } }; } From 4af4559095c8ad46d55bc5c3b94f82235fad23f9 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 14:15:44 +0530 Subject: [PATCH 51/71] adding a [JsonProperty("name")] attribute for the Name property to ensure consistent JSON serialization Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs b/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs index dabca4e1..31715b12 100644 --- a/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs @@ -9,6 +9,7 @@ public class UploadedFileInput : IFileObjectInput [JsonProperty("file")] public Data File { get; set; } + [JsonProperty("name")] public string Name { get; set; } public IEnumerable Caption { get; set; } From 93ad9654a5555402bb8809fd1f970aebd343f370 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 14:16:33 +0530 Subject: [PATCH 52/71] add a [JsonProperty("caption")] attribute for the Caption property to ensure it serializes consistently with the interface Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs b/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs index 31715b12..949df10c 100644 --- a/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs @@ -12,6 +12,7 @@ public class UploadedFileInput : IFileObjectInput [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("caption")] public IEnumerable Caption { get; set; } public class Data From dd4e517d51e6ee5f66575724fe83ba7d6a26dae0 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 14:16:56 +0530 Subject: [PATCH 53/71] add a [JsonProperty("name")] attribute here for consistency with the JSON contract Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs b/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs index 38ad781c..8a602d38 100644 --- a/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs @@ -8,6 +8,7 @@ public class ExternalFileInput : IFileObjectInput [JsonProperty("external")] public Data External { get; set; } + [JsonProperty("name")] public string Name { get; set; } public IEnumerable Caption { get; set; } From 28d6b212dbd50513734bd0a68d87accf8a38cb2e Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 14:17:18 +0530 Subject: [PATCH 54/71] add a [JsonProperty("caption")] attribute to ensure proper serialization Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs b/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs index 8a602d38..6e2627cf 100644 --- a/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs @@ -11,6 +11,7 @@ public class ExternalFileInput : IFileObjectInput [JsonProperty("name")] public string Name { get; set; } + [JsonProperty("caption")] public IEnumerable Caption { get; set; } public class Data From dcd3d98d06e503829a6f8e949750419d6e63fe89 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 14:22:07 +0530 Subject: [PATCH 55/71] =?UTF-8?q?add=20a=20comment=20to=20clarify=20why=20?= =?UTF-8?q?the=20file=20name=20is=20expected=20to=20automatically=20have?= =?UTF-8?q?=20a=20'.jpg'=20or=20other=20file=20extension=F0=9F=92=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/Notion.Client/Models/File/FileObject.cs | 3 +++ Test/Notion.IntegrationTests/BlocksClientTests.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Src/Notion.Client/Models/File/FileObject.cs b/Src/Notion.Client/Models/File/FileObject.cs index aa7aed1b..0ac30a6c 100644 --- a/Src/Notion.Client/Models/File/FileObject.cs +++ b/Src/Notion.Client/Models/File/FileObject.cs @@ -12,6 +12,9 @@ public abstract class FileObject : IPageIcon [JsonProperty("caption")] public IEnumerable Caption { get; set; } + /// + /// The name of the file block, as shown in the Notion UI. Note that the UI may auto-append .pdf or other extensions. + /// [JsonProperty("name")] public string Name { get; set; } diff --git a/Test/Notion.IntegrationTests/BlocksClientTests.cs b/Test/Notion.IntegrationTests/BlocksClientTests.cs index d52b8f64..5f1ca25b 100644 --- a/Test/Notion.IntegrationTests/BlocksClientTests.cs +++ b/Test/Notion.IntegrationTests/BlocksClientTests.cs @@ -437,7 +437,10 @@ private static IEnumerable BlockData() fileBlock.HasChildren.Should().BeFalse(); var file = fileBlock.File.Should().NotBeNull().And.BeOfType().Subject; + + // NOTE: The name of the file block, as shown in the Notion UI. Note that the UI may auto-append .pdf or other extensions. file.Name.Should().Be("Test file name.jpg"); + file.External.Should().NotBeNull(); file.External.Url.Should().Be("https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg"); file.Caption.Should().NotBeNull().And.ContainSingle() From 19568ad2eb99b3f5eab0a8dbf76a1e7021f48acd Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 15:17:23 +0530 Subject: [PATCH 56/71] Create dependabot.yml --- .github/dependabot.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..331a95d5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "nuget" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + commit-message: + prefix: "deps" From 419bd1f55948a4a925da42a65174bcd80ffcb578 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Apr 2025 09:59:31 +0000 Subject: [PATCH 57/71] deps: bump coverlet.collector from 1.3.0 to 6.0.4 Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 1.3.0 to 6.0.4. - [Release notes](https://github.com/coverlet-coverage/coverlet/releases) - [Commits](https://github.com/coverlet-coverage/coverlet/commits/v6.0.4) --- updated-dependencies: - dependency-name: coverlet.collector dependency-version: 6.0.4 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Test/Notion.UnitTests/Notion.UnitTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Notion.UnitTests/Notion.UnitTests.csproj b/Test/Notion.UnitTests/Notion.UnitTests.csproj index 588ddc54..60bbd3ff 100644 --- a/Test/Notion.UnitTests/Notion.UnitTests.csproj +++ b/Test/Notion.UnitTests/Notion.UnitTests.csproj @@ -16,7 +16,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 85ebe93d52b59557e520362695abc0e950b4d3eb Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 15:30:10 +0530 Subject: [PATCH 58/71] Bump version to 4.3.0 --- Src/Notion.Client/Notion.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/Notion.Client/Notion.Client.csproj b/Src/Notion.Client/Notion.Client.csproj index c7e96e08..3e353339 100644 --- a/Src/Notion.Client/Notion.Client.csproj +++ b/Src/Notion.Client/Notion.Client.csproj @@ -1,7 +1,7 @@  - 4.2.0-preview + 4.3.0-preview netstandard2.0 9.0 From d0f1eac7bbd51e251219cbb28bb2d52af2b75b34 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 16:04:02 +0530 Subject: [PATCH 59/71] update outdated actions in publish code workflow --- .github/workflows/publish-code.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-code.yml b/.github/workflows/publish-code.yml index ef76a197..2073a733 100644 --- a/.github/workflows/publish-code.yml +++ b/.github/workflows/publish-code.yml @@ -36,7 +36,7 @@ jobs: dotnet pack --no-restore --no-build -o PackOutputs -c Release -p:Version=${{ github.event.release.tag_name }} -p:PackageReleaseNotes="See https://github.com/notion-dotnet/notion-sdk-net/releases/tag/${{ github.event.release.tag_name }}" -p:PackageReadmeFile=README.md - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Notion.Net path: PackOutputs/* @@ -45,7 +45,7 @@ jobs: run: dotnet nuget push PackOutputs/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} - name: Upload Nuget packages as release artifacts - uses: actions/github-script@v2 + uses: actions/github-script@v7 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | From d6947cca04a07e600079dc999d6aa0b67f3f293f Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sun, 13 Apr 2025 16:11:57 +0530 Subject: [PATCH 60/71] update test publish code github workflow --- .github/workflows/test-publish-code.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-publish-code.yml b/.github/workflows/test-publish-code.yml index e90afd73..ce6974e6 100644 --- a/.github/workflows/test-publish-code.yml +++ b/.github/workflows/test-publish-code.yml @@ -45,7 +45,7 @@ jobs: ls -l PackOutputs/ - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Notion.Net path: PackOutputs/* From ae01e9e253a22bf74cc470353a01136e1e7ceacf Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Tue, 15 Apr 2025 06:45:37 +0530 Subject: [PATCH 61/71] workflow to greet first-time contributors --- .github/workflows/greetings.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/greetings.yml diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 00000000..55a63c2c --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,23 @@ +name: Greetings + +on: [pull_request_target, issues] + +jobs: + greet-first-time-contributors: + name: Greet First-Time Contributors + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - name: Send greeting to first-time contributors + uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: > + 👋 Hi there! Thanks for opening your first issue in this repository. + We appreciate your contribution. A maintainer will take a look soon. + pr-message: > + 🎉 Thanks for your first pull request! + The team will review it shortly. We’re excited to have you contribute! From 8a69e7d26725bd1f39058fbd4a8bbcc1421e0739 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Apr 2025 09:57:03 +0000 Subject: [PATCH 62/71] deps: bump Microsoft.SourceLink.GitHub from 1.1.1 to 8.0.0 Bumps [Microsoft.SourceLink.GitHub](https://github.com/dotnet/sourcelink) from 1.1.1 to 8.0.0. - [Release notes](https://github.com/dotnet/sourcelink/releases) - [Commits](https://github.com/dotnet/sourcelink/compare/1.1.1...8.0.0) --- updated-dependencies: - dependency-name: Microsoft.SourceLink.GitHub dependency-version: 8.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Src/Notion.Client/Notion.Client.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/Notion.Client/Notion.Client.csproj b/Src/Notion.Client/Notion.Client.csproj index 3e353339..2a6555c3 100644 --- a/Src/Notion.Client/Notion.Client.csproj +++ b/Src/Notion.Client/Notion.Client.csproj @@ -1,4 +1,4 @@ - + 4.3.0-preview @@ -16,7 +16,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive From d9c3cd2cc23fa4f2457fa249dae2ea7286c25d16 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Apr 2025 09:57:44 +0000 Subject: [PATCH 63/71] deps: bump Microsoft.NET.Test.Sdk from 16.7.1 to 17.13.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.7.1 to 17.13.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md) - [Commits](https://github.com/microsoft/vstest/compare/v16.7.1...v17.13.0) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-version: 17.13.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Test/Notion.UnitTests/Notion.UnitTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Notion.UnitTests/Notion.UnitTests.csproj b/Test/Notion.UnitTests/Notion.UnitTests.csproj index 588ddc54..ef5c1d34 100644 --- a/Test/Notion.UnitTests/Notion.UnitTests.csproj +++ b/Test/Notion.UnitTests/Notion.UnitTests.csproj @@ -8,7 +8,7 @@ - + From 966f95893fca1c855f136d536746bb26dfa0ff95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Apr 2025 10:00:14 +0000 Subject: [PATCH 64/71] deps: bump xunit.runner.visualstudio from 2.4.3 to 3.0.2 Bumps [xunit.runner.visualstudio](https://github.com/xunit/visualstudio.xunit) from 2.4.3 to 3.0.2. - [Release notes](https://github.com/xunit/visualstudio.xunit/releases) - [Commits](https://github.com/xunit/visualstudio.xunit/compare/2.4.3...3.0.2) --- updated-dependencies: - dependency-name: xunit.runner.visualstudio dependency-version: 3.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj | 2 +- Test/Notion.UnitTests/Notion.UnitTests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj b/Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj index 42a1975a..1284ee3f 100644 --- a/Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj +++ b/Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj @@ -15,7 +15,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Test/Notion.UnitTests/Notion.UnitTests.csproj b/Test/Notion.UnitTests/Notion.UnitTests.csproj index 2da01ae8..f04f3c8e 100644 --- a/Test/Notion.UnitTests/Notion.UnitTests.csproj +++ b/Test/Notion.UnitTests/Notion.UnitTests.csproj @@ -12,7 +12,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 744229db390be105aa419074f45e23d30ed6daa7 Mon Sep 17 00:00:00 2001 From: Mr-Bally <25254611+Mr-Bally@users.noreply.github.com> Date: Tue, 15 Apr 2025 10:39:47 +0000 Subject: [PATCH 65/71] Added revoke functionality --- Src/Notion.Client/Api/ApiEndpoints.cs | 1 + .../Authentication/IAuthenticationClient.cs | 11 ++++++++++ .../RevokeToken/AuthenticationClient.cs | 21 +++++++++++++++++++ .../Request/IRevokeTokenBodyParameters.cs | 13 ++++++++++++ .../RevokeToken/Request/RevokeTokenRequest.cs | 7 +++++++ 5 files changed, 53 insertions(+) create mode 100644 Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs create mode 100644 Src/Notion.Client/Api/Authentication/RevokeToken/Request/IRevokeTokenBodyParameters.cs create mode 100644 Src/Notion.Client/Api/Authentication/RevokeToken/Request/RevokeTokenRequest.cs diff --git a/Src/Notion.Client/Api/ApiEndpoints.cs b/Src/Notion.Client/Api/ApiEndpoints.cs index 4ed80bb1..86d703b3 100644 --- a/Src/Notion.Client/Api/ApiEndpoints.cs +++ b/Src/Notion.Client/Api/ApiEndpoints.cs @@ -135,6 +135,7 @@ public static string Create() public static class AuthenticationUrls { public static string CreateToken() => "/v1/oauth/token"; + public static string RevokeToken() => "/v1/oauth/revoke"; } } } diff --git a/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs index 0b4ebeb8..2aa3e7a8 100644 --- a/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs +++ b/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs @@ -18,5 +18,16 @@ Task CreateTokenAsync( CreateTokenRequest createTokenRequest, CancellationToken cancellationToken = default ); + + /// + /// Revokes an access token. + /// + /// + /// + /// + Task RevokeTokenAsync( + RevokeTokenRequest revokeTokenRequest, + CancellationToken cancellationToken = default + ); } } diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs new file mode 100644 index 00000000..8cc5c85e --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class AuthenticationClient + { + public async Task RevokeTokenAsync( + RevokeTokenRequest revokeTokenRequest, + CancellationToken cancellationToken = default) + { + var body = (IRevokeTokenBodyParameters)revokeTokenRequest; + + return (await _client.PostAsync( + ApiEndpoints.AuthenticationUrls.RevokeToken(), + body, + cancellationToken: cancellationToken + )).StatusCode; + } + } +} diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/Request/IRevokeTokenBodyParameters.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/Request/IRevokeTokenBodyParameters.cs new file mode 100644 index 00000000..0a194516 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/Request/IRevokeTokenBodyParameters.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface IRevokeTokenBodyParameters + { + /// + /// The token to be revoked. + /// + [JsonProperty("token")] + string Token { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/Request/RevokeTokenRequest.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/Request/RevokeTokenRequest.cs new file mode 100644 index 00000000..7e92ca01 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/Request/RevokeTokenRequest.cs @@ -0,0 +1,7 @@ +namespace Notion.Client +{ + public class RevokeTokenRequest : IRevokeTokenBodyParameters + { + public string Token { get; set; } + } +} From a96af1ab55464f49fd03acdbe630a02fec6766e8 Mon Sep 17 00:00:00 2001 From: Sam-Ballantyne <25254611+Sam-Ballantyne@users.noreply.github.com> Date: Wed, 16 Apr 2025 10:25:02 +0100 Subject: [PATCH 66/71] Updates from PR review --- .../Authentication/IAuthenticationClient.cs | 4 ++-- .../RevokeToken/AuthenticationClient.cs | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs index 2aa3e7a8..1973638f 100644 --- a/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs +++ b/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs @@ -18,14 +18,14 @@ Task CreateTokenAsync( CreateTokenRequest createTokenRequest, CancellationToken cancellationToken = default ); - + /// /// Revokes an access token. /// /// /// /// - Task RevokeTokenAsync( + Task RevokeTokenAsync( RevokeTokenRequest revokeTokenRequest, CancellationToken cancellationToken = default ); diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs index 8cc5c85e..d9087aa4 100644 --- a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs @@ -1,21 +1,30 @@ +using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace Notion.Client { public sealed partial class AuthenticationClient - { - public async Task RevokeTokenAsync( + { + public async Task RevokeTokenAsync( RevokeTokenRequest revokeTokenRequest, CancellationToken cancellationToken = default) { var body = (IRevokeTokenBodyParameters)revokeTokenRequest; - - return (await _client.PostAsync( + + var response = await _client.PostAsync( ApiEndpoints.AuthenticationUrls.RevokeToken(), body, cancellationToken: cancellationToken - )).StatusCode; + ); + + if (!response.IsSuccessStatusCode) + { + throw new NotionApiException(response.StatusCode, + null, + "None success status code returned from revoke endpoint" + ); + } } } } From 0a9ed73fe62e9ecbc653a957978dea760c4f4d6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 11:53:18 +0000 Subject: [PATCH 67/71] deps: bump Microsoft.VisualStudio.VsixColorCompiler Bumps Microsoft.VisualStudio.VsixColorCompiler from 16.0.0 to 17.11.35325.10. --- updated-dependencies: - dependency-name: Microsoft.VisualStudio.VsixColorCompiler dependency-version: 17.11.35325.10 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- Test/Notion.UnitTests/Notion.UnitTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Notion.UnitTests/Notion.UnitTests.csproj b/Test/Notion.UnitTests/Notion.UnitTests.csproj index f04f3c8e..6a33f5c0 100644 --- a/Test/Notion.UnitTests/Notion.UnitTests.csproj +++ b/Test/Notion.UnitTests/Notion.UnitTests.csproj @@ -9,7 +9,7 @@ - + From 0cc805772d78e0a4b7f2ad8d18992d8889bcf67b Mon Sep 17 00:00:00 2001 From: Mr-Bally <25254611+Mr-Bally@users.noreply.github.com> Date: Thu, 17 Apr 2025 16:24:51 +0100 Subject: [PATCH 68/71] Update from PR review --- .../Authentication/RevokeToken/AuthenticationClient.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs index d9087aa4..0d67057b 100644 --- a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs @@ -12,19 +12,11 @@ public async Task RevokeTokenAsync( { var body = (IRevokeTokenBodyParameters)revokeTokenRequest; - var response = await _client.PostAsync( + await _client.PostAsync( ApiEndpoints.AuthenticationUrls.RevokeToken(), body, cancellationToken: cancellationToken ); - - if (!response.IsSuccessStatusCode) - { - throw new NotionApiException(response.StatusCode, - null, - "None success status code returned from revoke endpoint" - ); - } } } } From bb62d1e3b5ec057fc951d46d8cc964e1f790700e Mon Sep 17 00:00:00 2001 From: Mr-Bally <25254611+Mr-Bally@users.noreply.github.com> Date: Thu, 17 Apr 2025 16:52:20 +0100 Subject: [PATCH 69/71] Added empty object for HttpClient --- .../Api/Authentication/RevokeToken/AuthenticationClient.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs index 0d67057b..2c4d2c28 100644 --- a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs @@ -12,11 +12,15 @@ public async Task RevokeTokenAsync( { var body = (IRevokeTokenBodyParameters)revokeTokenRequest; - await _client.PostAsync( + await _client.PostAsync( ApiEndpoints.AuthenticationUrls.RevokeToken(), body, cancellationToken: cancellationToken ); } } + + internal class RevokeTokenResponse + { + } } From c7349ed96f35d1f38633dfa9788bac05c72b195f Mon Sep 17 00:00:00 2001 From: Sam-Ballantyne <25254611+Sam-Ballantyne@users.noreply.github.com> Date: Thu, 17 Apr 2025 18:45:15 +0100 Subject: [PATCH 70/71] moved class into separate file --- .../Api/Authentication/RevokeToken/AuthenticationClient.cs | 5 ----- .../RevokeToken/Response/RevokeTokenResponse.cs | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 Src/Notion.Client/Api/Authentication/RevokeToken/Response/RevokeTokenResponse.cs diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs index 2c4d2c28..c1f3ab9d 100644 --- a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs @@ -1,4 +1,3 @@ -using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -19,8 +18,4 @@ await _client.PostAsync( ); } } - - internal class RevokeTokenResponse - { - } } diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/Response/RevokeTokenResponse.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/Response/RevokeTokenResponse.cs new file mode 100644 index 00000000..428dd287 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/Response/RevokeTokenResponse.cs @@ -0,0 +1,6 @@ +namespace Notion.Client +{ + internal class RevokeTokenResponse + { + } +} From 237bb13e5656bd010f25839f10b12515d866d828 Mon Sep 17 00:00:00 2001 From: Vedant Koditkar <18693839+KoditkarVedant@users.noreply.github.com> Date: Sat, 19 Apr 2025 06:06:11 +0530 Subject: [PATCH 71/71] remove lgtm yaml and links --- README.md | 3 --- lgtm.yml | 6 ------ 2 files changed, 9 deletions(-) delete mode 100644 lgtm.yml diff --git a/README.md b/README.md index 5ab47338..d74a4cc6 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,6 @@ [![Publish Code](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/publish-code.yml/badge.svg)](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/publish-code.yml) [![CodeQL](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/codeql-analysis.yml) -[![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/notion-dotnet/notion-sdk-net)](https://lgtm.com/projects/g/notion-dotnet/notion-sdk-net/alerts/?mode=list) -[![LGTM Grade](https://img.shields.io/lgtm/grade/csharp/github/notion-dotnet/notion-sdk-net)](https://lgtm.com/projects/g/notion-dotnet/notion-sdk-net/alerts/?mode=list) - [![GitHub last commit](https://img.shields.io/github/last-commit/notion-dotnet/notion-sdk-net)]() [![GitHub commit activity](https://img.shields.io/github/commit-activity/w/notion-dotnet/notion-sdk-net)]() [![GitHub commit activity](https://img.shields.io/github/commit-activity/m/notion-dotnet/notion-sdk-net)]() diff --git a/lgtm.yml b/lgtm.yml deleted file mode 100644 index 5d5e1e4f..00000000 --- a/lgtm.yml +++ /dev/null @@ -1,6 +0,0 @@ -extraction: - csharp: - index: - all_solutions: true - dotnet: - version: 6.0.400