diff --git a/README.md b/README.md index 900a2451..4c09f984 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ var complexFiler = new CompoundFilter( - [x] Users - [x] Retrieve a User - [x] List all users + - [x] Retrieve your token's bot user - [x] Search ## Contribution Guideline diff --git a/Src/Notion.Client/Api/ApiEndpoints.cs b/Src/Notion.Client/Api/ApiEndpoints.cs index 8e68e22b..7884123c 100644 --- a/Src/Notion.Client/Api/ApiEndpoints.cs +++ b/Src/Notion.Client/Api/ApiEndpoints.cs @@ -1,4 +1,6 @@ -namespace Notion.Client +using System; + +namespace Notion.Client { public static class ApiEndpoints { @@ -15,6 +17,12 @@ public static class UsersApiUrls { public static string Retrieve(string userId) => $"/v1/users/{userId}"; public static string List() => "/v1/users"; + + /// + /// Get the for retrieve your token's bot user. + /// + /// Returns a retrieve your token's bot user. + public static string Me() => "/v1/users/me"; } public static class BlocksApiUrls diff --git a/Src/Notion.Client/Api/Users/IUsersClient.cs b/Src/Notion.Client/Api/Users/IUsersClient.cs index 6eab0cde..95427f2b 100644 --- a/Src/Notion.Client/Api/Users/IUsersClient.cs +++ b/Src/Notion.Client/Api/Users/IUsersClient.cs @@ -6,5 +6,11 @@ public interface IUsersClient { Task RetrieveAsync(string userId); Task> ListAsync(); + + /// + /// Retrieves the bot User associated with the API token provided in the authorization header. + /// + /// User object of type bot having an owner field with information about the person who authorized the integration. + Task MeAsync(); } } diff --git a/Src/Notion.Client/Api/Users/UsersClient.cs b/Src/Notion.Client/Api/Users/UsersClient.cs index 0d2e2c90..5c745f72 100644 --- a/Src/Notion.Client/Api/Users/UsersClient.cs +++ b/Src/Notion.Client/Api/Users/UsersClient.cs @@ -21,5 +21,14 @@ public async Task> ListAsync() { return await _client.GetAsync>(UsersApiUrls.List()); } + + /// + /// Retrieves the bot User associated with the API token provided in the authorization header. + /// + /// User object of type bot having an owner field with information about the person who authorized the integration. + public async Task MeAsync() + { + return await _client.GetAsync(UsersApiUrls.Me()); + } } } diff --git a/Src/Notion.Client/Models/User/Bot.cs b/Src/Notion.Client/Models/User/Bot.cs new file mode 100644 index 00000000..2eaf6420 --- /dev/null +++ b/Src/Notion.Client/Models/User/Bot.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class Bot + { + [JsonProperty("owner")] + public IBotOwner Owner { get; set; } + } +} diff --git a/Src/Notion.Client/Models/User/BotOwner/IBotOwner.cs b/Src/Notion.Client/Models/User/BotOwner/IBotOwner.cs new file mode 100644 index 00000000..61a7a887 --- /dev/null +++ b/Src/Notion.Client/Models/User/BotOwner/IBotOwner.cs @@ -0,0 +1,14 @@ +using JsonSubTypes; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [JsonConverter(typeof(JsonSubtypes), "type")] + [JsonSubtypes.KnownSubType(typeof(UserOwner), "user")] + [JsonSubtypes.KnownSubType(typeof(WorkspaceIntegrationOwner), "workspace")] + public interface IBotOwner + { + [JsonProperty("type")] + string Type { get; set; } + } +} diff --git a/Src/Notion.Client/Models/User/BotOwner/UserOwner.cs b/Src/Notion.Client/Models/User/BotOwner/UserOwner.cs new file mode 100644 index 00000000..40d69867 --- /dev/null +++ b/Src/Notion.Client/Models/User/BotOwner/UserOwner.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class UserOwner : IBotOwner + { + public string Type { get; set; } + + [JsonProperty("user")] + public User User { get; set; } + } +} diff --git a/Src/Notion.Client/Models/User/BotOwner/WorkspaceIntegrationOwner.cs b/Src/Notion.Client/Models/User/BotOwner/WorkspaceIntegrationOwner.cs new file mode 100644 index 00000000..78a84f6f --- /dev/null +++ b/Src/Notion.Client/Models/User/BotOwner/WorkspaceIntegrationOwner.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class WorkspaceIntegrationOwner : IBotOwner + { + public string Type { get; set; } + + [JsonProperty("workspace")] + public bool Workspace { get; set; } + } +} diff --git a/Src/Notion.Client/Models/User/Person.cs b/Src/Notion.Client/Models/User/Person.cs new file mode 100644 index 00000000..621f3b48 --- /dev/null +++ b/Src/Notion.Client/Models/User/Person.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class Person + { + [JsonProperty("email")] + public string Email { get; set; } + } +} diff --git a/Src/Notion.Client/Models/User.cs b/Src/Notion.Client/Models/User/User.cs similarity index 79% rename from Src/Notion.Client/Models/User.cs rename to Src/Notion.Client/Models/User/User.cs index ce39e822..7f336929 100644 --- a/Src/Notion.Client/Models/User.cs +++ b/Src/Notion.Client/Models/User/User.cs @@ -22,15 +22,4 @@ public class User : IObject [JsonProperty("bot")] public Bot Bot { get; set; } } - - public class Person - { - [JsonProperty("email")] - public string Email { get; set; } - } - - public class Bot - { - - } } diff --git a/Test/Notion.UnitTests/Notion.UnitTests.csproj b/Test/Notion.UnitTests/Notion.UnitTests.csproj index 8382e6bc..3b729bf5 100644 --- a/Test/Notion.UnitTests/Notion.UnitTests.csproj +++ b/Test/Notion.UnitTests/Notion.UnitTests.csproj @@ -81,6 +81,12 @@ Always + + Always + + + Always + Always diff --git a/Test/Notion.UnitTests/UserClientTest.cs b/Test/Notion.UnitTests/UserClientTest.cs index a6ea75ef..d9b27984 100644 --- a/Test/Notion.UnitTests/UserClientTest.cs +++ b/Test/Notion.UnitTests/UserClientTest.cs @@ -78,5 +78,72 @@ public async Task RetrieveUser() user.Person.Email.Should().Be("vedkoditkar@gmail.com"); user.Bot.Should().BeNull(); } + + [Fact] + public async Task RetrieveTokenUser_WorkspaceInternalToken() + { + // Arrange + var jsonData = await File.ReadAllTextAsync("data/users/MeResponse.json"); + + var path = ApiEndpoints.UsersApiUrls.Me(); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithBody(jsonData) + ); + + // Act + var user = await _client.MeAsync(); + + // Assert + user.Id.Should().Be("590693f3-797f-4970-98ff-7284106393e5"); + user.Name.Should().Be("Test"); + user.AvatarUrl.Should().BeNull(); + user.Type.Should().Be("bot"); + user.Person.Should().BeNull(); + user.Bot.Should().NotBeNull(); + user.Bot.Owner.Should().BeOfType(); + + var owner = (WorkspaceIntegrationOwner)user.Bot.Owner; + owner.Workspace.Should().BeTrue(); + } + + [Fact] + public async Task RetrieveTokenUser_UserLevelToken() + { + // Arrange + var jsonData = await File.ReadAllTextAsync("data/users/MeUserLevelResponse.json"); + + var path = ApiEndpoints.UsersApiUrls.Me(); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithBody(jsonData) + ); + + // Act + var user = await _client.MeAsync(); + + // Assert + user.Id.Should().Be("16d84278-ab0e-484c-9bdd-b35da3bd8905"); + user.Name.Should().Be("pied piper"); + user.AvatarUrl.Should().BeNull(); + user.Type.Should().Be("bot"); + user.Person.Should().BeNull(); + user.Bot.Should().NotBeNull(); + user.Bot.Owner.Should().BeOfType(); + + var owner = (UserOwner)user.Bot.Owner; + owner.User.Id.Should().Be("5389a034-eb5c-47b5-8a9e-f79c99ef166c"); + owner.User.Name.Should().Be("christine makenotion"); + owner.User.AvatarUrl.Should().BeNull(); + owner.User.Type.Should().Be("person"); + owner.User.Person.Email.Should().Be("christine@makenotion.com"); + owner.User.Bot.Should().BeNull(); + } } } diff --git a/Test/Notion.UnitTests/data/users/MeResponse.json b/Test/Notion.UnitTests/data/users/MeResponse.json new file mode 100644 index 00000000..411af097 --- /dev/null +++ b/Test/Notion.UnitTests/data/users/MeResponse.json @@ -0,0 +1,13 @@ +{ + "object": "user", + "id": "590693f3-797f-4970-98ff-7284106393e5", + "name": "Test", + "avatar_url": null, + "type": "bot", + "bot": { + "owner": { + "type": "workspace", + "workspace": true + } + } +} diff --git a/Test/Notion.UnitTests/data/users/MeUserLevelResponse.json b/Test/Notion.UnitTests/data/users/MeUserLevelResponse.json new file mode 100644 index 00000000..cbeec973 --- /dev/null +++ b/Test/Notion.UnitTests/data/users/MeUserLevelResponse.json @@ -0,0 +1,22 @@ +{ + "object": "user", + "id": "16d84278-ab0e-484c-9bdd-b35da3bd8905", + "name": "pied piper", + "avatar_url": null, + "type": "bot", + "bot": { + "owner": { + "type": "user", + "user": { + "object": "user", + "id": "5389a034-eb5c-47b5-8a9e-f79c99ef166c", + "name": "christine makenotion", + "avatar_url": null, + "type": "person", + "person": { + "email": "christine@makenotion.com" + } + } + } + } +} diff --git a/docs/README.md b/docs/README.md index 4088b54b..542fe472 100644 --- a/docs/README.md +++ b/docs/README.md @@ -84,6 +84,7 @@ var complexFiler = new CompoundFilter( - [x] Users - [x] Retrieve a User - [x] List all users + - [x] Retrieve your token's bot user - [x] Search ## Contribution Guideline