Skip to content

Commit 2d5f81e

Browse files
Add support for Append Block Children api ✨
1 parent 3347465 commit 2d5f81e

File tree

5 files changed

+126
-4
lines changed

5 files changed

+126
-4
lines changed

Src/Notion.Client/Api/ApiEndpoints.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static class UsersApiUrls
1818
public static class BlocksApiUrls
1919
{
2020
public static string RetrieveChildren(string blockId) => $"/v1/blocks/{blockId}/children";
21+
public static string AppendChildren(string blockId) => $"/v1/blocks/{blockId}/children";
2122
}
2223
}
2324
}

Src/Notion.Client/Api/Blocks/BlocksClient.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace Notion.Client
88
public interface IBlocksClient
99
{
1010
Task<PaginatedList<BlockBase>> RetrieveChildrenAsync(string blockId, BlockRetrieveChildrenParameters parameters = null);
11+
Task<BlockBase> AppendChildrenAsync(string blockId, BlocksAppendChildrenParameters parameters = null);
1112
}
1213

1314
public class BlocksClient : IBlocksClient
@@ -38,6 +39,20 @@ public async Task<PaginatedList<BlockBase>> RetrieveChildrenAsync(string blockId
3839

3940
return await _client.GetAsync<PaginatedList<BlockBase>>(url, queryParams);
4041
}
42+
43+
public async Task<BlockBase> AppendChildrenAsync(string blockId, BlocksAppendChildrenParameters parameters = null)
44+
{
45+
if (string.IsNullOrWhiteSpace(blockId))
46+
{
47+
throw new ArgumentNullException(nameof(blockId));
48+
}
49+
50+
var url = BlocksApiUrls.AppendChildren(blockId);
51+
52+
var body = (IBlocksAppendChildrenBodyParameters)parameters;
53+
54+
return await _client.PatchAsync<BlockBase>(url, body);
55+
}
4156
}
4257

4358
public interface IBlockRetrieveChildrenQueryParameters : IPaginationParameters
@@ -53,4 +68,16 @@ public class BlockRetrieveChildrenParameters : IBlockRetrieveChildrenParameters
5368
public string StartCursor { get; set; }
5469
public string PageSize { get; set; }
5570
}
71+
72+
73+
// TODO: need an input version of Block
74+
public interface IBlocksAppendChildrenBodyParameters
75+
{
76+
IEnumerable<BlockBase> Children { get; set; }
77+
}
78+
79+
public class BlocksAppendChildrenParameters : IBlocksAppendChildrenBodyParameters
80+
{
81+
public IEnumerable<BlockBase> Children { get; set; }
82+
}
5683
}

Src/Notion.Client/Models/Block.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class BlockBase
2323
public string Id { get; set; }
2424

2525
[JsonConverter(typeof(StringEnumConverter))]
26-
public BlockType Type { get; set; }
26+
public virtual BlockType Type { get; set; }
2727

2828
[JsonProperty("created_time")]
2929
public string CreatedTime { get; set; }
@@ -40,6 +40,8 @@ public class BlockBase
4040

4141
public class ParagraphBlock : BlockBase
4242
{
43+
public override BlockType Type => BlockType.Paragraph;
44+
4345
public ParagraphClass Paragraph { get; set; }
4446

4547
public class ParagraphClass
@@ -51,6 +53,8 @@ public class ParagraphClass
5153

5254
public class HeadingOneBlock : BlockBase
5355
{
56+
public override BlockType Type => BlockType.Heading_1;
57+
5458
[JsonProperty("heading_1")]
5559
public HeadingOneClass Heading_1 { get; set; }
5660

@@ -64,6 +68,8 @@ public class HeadingOneClass
6468

6569
public class HeadingTwoBlock : BlockBase
6670
{
71+
public override BlockType Type => BlockType.Heading_2;
72+
6773
[JsonProperty("heading_2")]
6874
public HeadingTwoClass Heading_2 { get; set; }
6975

@@ -77,6 +83,8 @@ public class HeadingTwoClass
7783

7884
public class HeadingThreeeBlock : BlockBase
7985
{
86+
public override BlockType Type => BlockType.Heading_3;
87+
8088
[JsonProperty("heading_3")]
8189
public HeadingThreeClass Heading_3 { get; set; }
8290

@@ -90,6 +98,8 @@ public class HeadingThreeClass
9098

9199
public class BulletedListItemBlock : BlockBase
92100
{
101+
public override BlockType Type => BlockType.BulletedListItem;
102+
93103
[JsonProperty("bulleted_list_item")]
94104
public BulletedListItemClass BulletedListItem { get; set; }
95105

@@ -103,6 +113,8 @@ public class BulletedListItemClass
103113
public class NumberedListItemBlock : BlockBase
104114
{
105115

116+
public override BlockType Type => BlockType.NumberedListItem;
117+
106118
[JsonProperty("numbered_list_item")]
107119
public NumberedListItemClass NumberedListItem { get; set; }
108120

@@ -115,6 +127,8 @@ public class NumberedListItemClass
115127

116128
public class ToDoBlock : BlockBase
117129
{
130+
public override BlockType Type => BlockType.ToDo;
131+
118132
[JsonProperty("to_do")]
119133
public ToDoClass ToDo { get; set; }
120134

@@ -131,6 +145,8 @@ public class ToDoClass
131145

132146
public class ToggleBlock : BlockBase
133147
{
148+
public override BlockType Type => BlockType.Toggle;
149+
134150
public ToggleClass Toggle { get; set; }
135151

136152
public class ToggleClass
@@ -142,6 +158,8 @@ public class ToggleClass
142158

143159
public class ChildPageBlock : BlockBase
144160
{
161+
public override BlockType Type => BlockType.ChildPage;
162+
145163

146164
[JsonProperty("child_page")]
147165
public ChildPageClass ChildPage { get; set; }
@@ -154,6 +172,7 @@ public class ChildPageClass
154172

155173
public class UnsupportedBlock : BlockBase
156174
{
175+
public override BlockType Type => BlockType.Unsupported;
157176
}
158177

159178
public enum BlockType

Src/Notion.Client/RestClient.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Threading;
77
using System.Threading.Tasks;
88
using Newtonsoft.Json;
9+
using Newtonsoft.Json.Serialization;
910
using Notion.Client.Extensions;
1011
using Notion.Client.http;
1112

@@ -27,6 +28,14 @@ Task<T> PostAsync<T>(
2728
IDictionary<string, string> headers = null,
2829
JsonSerializerSettings serializerSettings = null,
2930
CancellationToken cancellationToken = default);
31+
32+
Task<T> PatchAsync<T>(
33+
string uri,
34+
object body,
35+
IDictionary<string, string> queryParams = null,
36+
IDictionary<string, string> headers = null,
37+
JsonSerializerSettings serializerSettings = null,
38+
CancellationToken cancellationToken = default);
3039
}
3140

3241
public class RestClient : IRestClient
@@ -36,7 +45,11 @@ public class RestClient : IRestClient
3645

3746
private readonly JsonSerializerSettings defaultSerializerSettings = new JsonSerializerSettings
3847
{
39-
NullValueHandling = NullValueHandling.Ignore
48+
NullValueHandling = NullValueHandling.Ignore,
49+
ContractResolver = new DefaultContractResolver
50+
{
51+
NamingStrategy = new CamelCaseNamingStrategy()
52+
}
4053
};
4154

4255
public RestClient(ClientOptions options)
@@ -139,6 +152,34 @@ void AttachContent(HttpRequestMessage httpRequest)
139152
throw new NotionApiException(response.StatusCode, message);
140153
}
141154

155+
public async Task<T> PatchAsync<T>(string uri, object body, IDictionary<string, string> queryParams = null, IDictionary<string, string> headers = null, JsonSerializerSettings serializerSettings = null, CancellationToken cancellationToken = default)
156+
{
157+
EnsureHttpClient();
158+
159+
void AttachContent(HttpRequestMessage httpRequest)
160+
{
161+
var serializedBody = JsonConvert.SerializeObject(body, defaultSerializerSettings);
162+
httpRequest.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json");
163+
}
164+
165+
string requestUri = queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams);
166+
167+
var response = await SendAsync(requestUri, new HttpMethod("PATCH"), headers, AttachContent, cancellationToken: cancellationToken);
168+
169+
if (response.IsSuccessStatusCode)
170+
{
171+
return await response.ParseStreamAsync<T>(serializerSettings);
172+
}
173+
174+
var message = !string.IsNullOrWhiteSpace(response.ReasonPhrase)
175+
? response.ReasonPhrase
176+
: await response.Content.ReadAsStringAsync();
177+
178+
var errorMessage = await response.Content.ReadAsStringAsync();
179+
180+
throw new NotionApiException(response.StatusCode, message);
181+
}
182+
142183
private HttpClient EnsureHttpClient()
143184
{
144185
if (_httpClient == null)

Test/Notion.UnitTests/BlocksClientTests.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Generic;
12
using System.Threading.Tasks;
23
using Notion.Client;
34
using Xunit;
@@ -18,14 +19,47 @@ public BlocksClientTests()
1819
_client = new BlocksClient(new RestClient(options));
1920
}
2021

21-
[Fact(Skip ="Dev only")]
22+
[Fact(Skip = "Dev only")]
2223
public async Task RetrieveBlockChildren()
2324
{
2425
string blockId = "3c357473-a281-49a4-88c0-10d2b245a589";
25-
26+
2627
var children = await _client.RetrieveChildrenAsync(blockId, new BlockRetrieveChildrenParameters());
2728

2829
Assert.NotNull(children);
2930
}
31+
32+
[Fact(Skip = "Dev only")]
33+
public async Task AppendBlockChildren()
34+
{
35+
string blockId = "3c357473-a281-49a4-88c0-10d2b245a589";
36+
37+
var parameters = new BlocksAppendChildrenParameters()
38+
{
39+
Children = new List<BlockBase>
40+
{
41+
new HeadingTwoBlock()
42+
{
43+
Heading_2 = new HeadingTwoBlock.HeadingTwoClass
44+
{
45+
Text = new List<RichTextBase>
46+
{
47+
new RichTextText
48+
{
49+
Text = new Text
50+
{
51+
Content = "Lacinato kale"
52+
}
53+
}
54+
}
55+
}
56+
}
57+
}
58+
};
59+
60+
var block = await _client.AppendChildrenAsync(blockId, parameters);
61+
62+
Assert.NotNull(block);
63+
}
3064
}
3165
}

0 commit comments

Comments
 (0)