Skip to content

Commit

Permalink
feat: add upload and delete support for Avatar API (#829)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwwoda committed May 26, 2022
1 parent a298f01 commit 4dcb84a
Show file tree
Hide file tree
Showing 18 changed files with 377 additions and 4 deletions.
1 change: 1 addition & 0 deletions Box.V2.Core/Box.V2.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
Expand Down
3 changes: 3 additions & 0 deletions Box.V2.Test.Integration/Box.V2.Test.Integration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
<None Update="config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestData\smallpic.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestData\smalltest.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
2 changes: 1 addition & 1 deletion Box.V2.Test.Integration/BoxFilesManagerIntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ await Retry(async () =>
var response = await AdminClient.FilesManager.GetInformationAsync(uploadedFile.Id, new List<string>() { "disposition_at" });
Assert.IsTrue(newDispositionDate.IsEqualUpToSeconds(response.DispositionAt.Value));
}, 5, 5000);
});
}

[TestMethod]
Expand Down
46 changes: 46 additions & 0 deletions Box.V2.Test.Integration/BoxUsersManagerIntegrationTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Box.V2.Models;
Expand Down Expand Up @@ -69,5 +70,50 @@ public async Task DeleteEnterpriseUserAsync_ForExistingUser_ShouldDeleteThisUser
var appUsers = await AdminClient.UsersManager.GetEnterpriseUsersAsync();
Assert.IsFalse(appUsers.Entries.Any(item => item.Id == newUser.Id));
}

[TestMethod]
public async Task UploadUserAvatar_UsingFileStream_ShouldReturnUploadedAvatarUris()
{
var user = await CreateEnterpriseUser();

using (var fileStream = new FileStream(GetSmallPicturePath(), FileMode.OpenOrCreate))
{
var response = await AdminClient.UsersManager.AddOrUpdateUserAvatarAsync(user.Id, fileStream);

Assert.IsNotNull(response.PicUrls.Preview);
Assert.IsNotNull(response.PicUrls.Small);
Assert.IsNotNull(response.PicUrls.Large);
}
}

[TestMethod]
public async Task UploadUserAvatar_UsingFileStreamWithExplicitFilename_ShouldReturnUploadedAvatarUris()
{
var user = await CreateEnterpriseUser();

using (var fileStream = new FileStream(GetSmallPicturePath(), FileMode.OpenOrCreate))
{
var response = await AdminClient.UsersManager.AddOrUpdateUserAvatarAsync(user.Id, fileStream, "newAvatar.png");

Assert.IsNotNull(response.PicUrls.Preview);
Assert.IsNotNull(response.PicUrls.Small);
Assert.IsNotNull(response.PicUrls.Large);
}
}

[TestMethod]
public async Task DeleteAvatar_ForExistingUserAvatar_ShouldReturnSuccessStatusCode()
{
var user = await CreateEnterpriseUser();

using (var fileStream = new FileStream(GetSmallPicturePath(), FileMode.OpenOrCreate))
{
var response = await AdminClient.UsersManager.AddOrUpdateUserAvatarAsync(user.Id, fileStream);
}

var deleteAvatarResponse = await AdminClient.UsersManager.DeleteUserAvatarAsync(user.Id);

Assert.IsTrue(deleteAvatarResponse);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.IO;
using System.Threading.Tasks;
using Box.V2.Models;

namespace Box.V2.Test.Integration.Configuration.Commands.DisposableCommands
{
public class CreateUserAvatarCommand : CommandBase, IDisposableCommand
{
private readonly string _userId;
private readonly string _filePath;

public BoxUploadAvatarResponse Response;

public CreateUserAvatarCommand(string userId, string filePath,
CommandScope scope = CommandScope.Test, CommandAccessLevel accessLevel = CommandAccessLevel.Admin) : base(scope, accessLevel)
{
_userId = userId;
_filePath = filePath;
}

public async Task<string> Execute(IBoxClient client)
{
using (var fileStream = new FileStream(_filePath, FileMode.OpenOrCreate))
{
Response = await client.UsersManager.AddOrUpdateUserAvatarAsync(_userId, fileStream);
}

return "0";
}

public async Task Dispose(IBoxClient client)
{
await client.UsersManager.DeleteUserAvatarAsync(_userId);
}
}
}
18 changes: 15 additions & 3 deletions Box.V2.Test.Integration/Configuration/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ protected static Task<BoxUser> CreateNewUser(IBoxClient client)
{
var userRequest = new BoxUserRequest
{
Name = "IT App User - " + Guid.NewGuid().ToString(),
Name = "IT App User - " + Guid.NewGuid().ToString(),
IsPlatformAccessOnly = true
};
var user = client.UsersManager.CreateEnterpriseUserAsync(userRequest);
Expand Down Expand Up @@ -166,7 +166,7 @@ public static async Task<string> ExecuteCommand(IDisposableCommand command)
IBoxClient client = GetClient(command);

var resourceId = await command.Execute(client);
if(command.Scope == CommandScope.Test)
if (command.Scope == CommandScope.Test)
{
TestCommands.Push(command);
}
Expand Down Expand Up @@ -200,6 +200,11 @@ public static string GetSmallFileV2Path()
return string.Format(AppDomain.CurrentDomain.BaseDirectory + "/TestData/smalltestV2.pdf");
}

public static string GetSmallPicturePath()
{
return string.Format(AppDomain.CurrentDomain.BaseDirectory + "/TestData/smallpic.png");
}

public static string ReadFromJson(string path)
{
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
Expand Down Expand Up @@ -345,7 +350,14 @@ public static async Task<BoxFile> CreateNewFileVersion(string fileId)
return createNewFileVersionCommand.File;
}

public static async Task Retry(Func<Task> action, int retries = 3, int sleep = 1000)
public static async Task<BoxUploadAvatarResponse> CreateUserAvatar(string userId)
{
var createAvatarCommand = new CreateUserAvatarCommand(userId, GetSmallPicturePath());
await ExecuteCommand(createAvatarCommand);
return createAvatarCommand.Response;
}

public static async Task Retry(Func<Task> action, int retries = 5, int sleep = 5000)
{
var retryCount = 0;
while (retryCount < retries)
Expand Down
Binary file added Box.V2.Test.Integration/TestData/smallpic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions Box.V2.Test/Box.V2.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@
<None Update="Fixtures\BoxSignRequest\GetSignRequest200.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fixtures\BoxUsers\AddOrUpdateUserAvatar200.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestData\smalltest.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
91 changes: 91 additions & 0 deletions Box.V2.Test/BoxUsersManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -720,5 +720,96 @@ public async Task GetUserAvatar_ValidResponse_ValidStream()

}
}

[TestMethod]
public async Task AddOrUpdateUserAvatar_ValidResponse_ValidStream()
{
/*** Arrange ***/
var responseString = LoadFixtureFromJson("Fixtures/BoxUsers/AddOrUpdateUserAvatar200.json");
BoxMultiPartRequest boxRequest = null;
Handler.Setup(h => h.ExecuteAsync<BoxUploadAvatarResponse>(It.IsAny<IBoxRequest>()))
.Returns(Task.FromResult<IBoxResponse<BoxUploadAvatarResponse>>(new BoxResponse<BoxUploadAvatarResponse>()
{
Status = ResponseStatus.Success,
ContentString = responseString
}))
.Callback<IBoxRequest>(r => boxRequest = r as BoxMultiPartRequest);

var fakeStream = new Mock<Stream>();
var fileName = "newAvatar.png";

/*** Act ***/
BoxUploadAvatarResponse response = await _usersManager.AddOrUpdateUserAvatarAsync("11111", fakeStream.Object, fileName);

/*** Assert ***/
Assert.IsNotNull(boxRequest);
Assert.AreEqual(RequestMethod.Post, boxRequest.Method);
Assert.AreEqual(new Uri("https://api.box.com/2.0/users/11111/avatar"), boxRequest.AbsoluteUri.AbsoluteUri);
Assert.IsNotNull(boxRequest.Parts[0] as BoxFileFormPart);
Assert.AreEqual(fileName, (boxRequest.Parts[0] as BoxFileFormPart).FileName);
Assert.AreEqual("image/png", (boxRequest.Parts[0] as BoxFileFormPart).ContentType);
Assert.IsTrue(ReferenceEquals(fakeStream.Object, (boxRequest.Parts[0] as BoxFileFormPart).Value));

Assert.IsNotNull(response.PicUrls);
Assert.IsNotNull(response.PicUrls.Preview);
Assert.IsNotNull(response.PicUrls.Small);
Assert.IsNotNull(response.PicUrls.Large);
}

[TestMethod]
public async Task AddOrUpdateUserAvatar_ValidResponse_ValidFileStream()
{
var responseString = LoadFixtureFromJson("Fixtures/BoxUsers/AddOrUpdateUserAvatar200.json");
BoxMultiPartRequest boxRequest = null;
Handler.Setup(h => h.ExecuteAsync<BoxUploadAvatarResponse>(It.IsAny<IBoxRequest>()))
.Returns(Task.FromResult<IBoxResponse<BoxUploadAvatarResponse>>(new BoxResponse<BoxUploadAvatarResponse>()
{
Status = ResponseStatus.Success,
ContentString = responseString
}))
.Callback<IBoxRequest>(r => boxRequest = r as BoxMultiPartRequest);

var stream = new FileStream("newAvatar.png", FileMode.OpenOrCreate);

BoxUploadAvatarResponse response = await _usersManager.AddOrUpdateUserAvatarAsync("11111", stream);

Assert.IsNotNull(boxRequest);
Assert.AreEqual(RequestMethod.Post, boxRequest.Method);
Assert.AreEqual(new Uri("https://api.box.com/2.0/users/11111/avatar"), boxRequest.AbsoluteUri.AbsoluteUri);
Assert.IsNotNull(boxRequest.Parts[0] as BoxFileFormPart);
Assert.AreEqual("newAvatar.png", (boxRequest.Parts[0] as BoxFileFormPart).FileName);
Assert.AreEqual("image/png", (boxRequest.Parts[0] as BoxFileFormPart).ContentType);
Assert.IsTrue(ReferenceEquals(stream, (boxRequest.Parts[0] as BoxFileFormPart).Value));

Assert.IsNotNull(response.PicUrls);
Assert.IsNotNull(response.PicUrls.Preview);
Assert.IsNotNull(response.PicUrls.Small);
Assert.IsNotNull(response.PicUrls.Large);
}

[TestMethod]
public async Task AddOrUpdateUserAvatar_ValidResponse()
{
/*** Arrange ***/
var responseString = "";
IBoxRequest boxRequest = null;
Handler.Setup(h => h.ExecuteAsync<BoxEntity>(It.IsAny<IBoxRequest>()))
.Returns(Task.FromResult<IBoxResponse<BoxEntity>>(new BoxResponse<BoxEntity>()
{
Status = ResponseStatus.Success,
ContentString = responseString
})).Callback<IBoxRequest>(r => boxRequest = r);

/*** Act ***/
var result = await _usersManager.DeleteUserAvatarAsync("11111");

/*** Assert ***/
/*** Request ***/
Assert.IsNotNull(boxRequest);
Assert.AreEqual(RequestMethod.Delete, boxRequest.Method);
Assert.AreEqual(new Uri("https://api.box.com/2.0/users/11111/avatar"), boxRequest.AbsoluteUri.AbsoluteUri);
/*** Response ***/
Assert.AreEqual(true, result);
}
}
}
18 changes: 18 additions & 0 deletions Box.V2.Test/ContentTypeMapperTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Box.V2.Utility;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Box.V2.Test
{
[TestClass]
public class ContentTypeMapperTest : BoxResourceManagerTest
{
[TestMethod]
[DataRow("avatar.png", "image/png")]
[DataRow("avatar.jpg", "image/jpeg")]
[DataRow("avatar.jpeg", "image/jpeg")]
public void ContentTypeMapper_ForGivenFilename_ShouldMapToTheCorrectMimeType(string fileName, string mimeType)
{
Assert.AreEqual(ContentTypeMapper.GetContentTypeFromFilename(fileName), mimeType);
}
}
}
7 changes: 7 additions & 0 deletions Box.V2.Test/Fixtures/BoxUsers/AddOrUpdateUserAvatar200.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"pic_urls": {
"large": "https://app.box.com/index.php?rm=pic_storage_auth&pic=euks pac3kv01!lipGQlQQOtCTCoB6zCOArUjVWLFJtLr5tn6aOZMCybhRx0NNuFQbVI36nw jtEk5YjUUz1KVdVuvU2yDhu_ftK_bvxeKP1Ffrx9vKGVvJ-UJc1z32p6n2CmFzzpc gSoX4pnPhFgydAL-u9jDspXUGElr-htDG_HPMiE9DZjqDueOxXHy8xe22wbaPAheC ao1emv8r_fmufaUgSndeMYmyZj-KqOYsLBrBNgdeiK5tZmPOQggAEUmyQPkrd8W92TQ6sSlIp0r",
"preview": "https://app.box.com/index.php?rm=pic_storage_auth&pic=euks! pac3kv01!8UcNPweOOAWj2DtHk_dCQB4wJpzyPkl7mT5nHj5ZdjY92ejYCBBZc95--403b29CW k-8hSo_uBjh5h9QG42Ihu-cOZ-816sej1kof3SOm5gjn7qjMAx89cHjUaNK-6XasRqSNboenjZ 04laZuV9vSH12BZGAYycIZvvQ5R66Go8xG5GTMARf2nBU84c4H_SL5iws-HeBS4oQJWOJh6FBl sSJDSTI74LGXqeZb3EY_As34VFC95F10uozoTOSubZmPYylPlaKXoKWk2f9wYQso1ZTN7sh-Gc 9Kp43zMLhArIWhok0Im6FlRAuWOQ03KYgL-k4L5EZp4Gw6B7uqVRwcBbsTwMIorWq1g",
"small": "https://app.box.com/index.php?rm=pic_storage_auth&pic=euks! pac3kv01!7B6R5cZLmurEV_xB-KkycPk8Oi7oENUX2O_qUtIuO4342CG IldyCto9hqiQP7uxqYU5V2w63Ft4ln4UVVLDtDZu903OqzkflY2O-Lq00 ubA29xU-RJ6b_KzJEWRYgUhX1zEl3dzWo12g8eWRE2rStf123DF7AYahNqM 1BmLmviL_nODc7SDQHedTXPAjxURUAra5BvtLe7B05AizbNXdPlCNp-LNh _S-eZ_RjDXcGO-MkRWd_3BOMHnvjf450t5BfKoJ15WhEfiMlfXH1tmouHXrsC 66cT6-pzF9E40Iir_zThqSlrFxzP_xcmXzHapr_k-0E2qr2TXp4iC396TSlEw"
}
}
3 changes: 3 additions & 0 deletions Box.V2/Box.V2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
<HintPath>..\packages\System.IdentityModel.Tokens.Jwt.6.12.2\lib\net45\System.IdentityModel.Tokens.Jwt.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
Expand Down Expand Up @@ -185,6 +186,7 @@
<Compile Include="Models\BoxTaskAssignment.cs" />
<Compile Include="Models\BoxTermsOfService.cs" />
<Compile Include="Models\BoxTermsOfServiceUserStatuses.cs" />
<Compile Include="Models\BoxUploadAvatarResponse.cs" />
<Compile Include="Models\BoxUserFileCollaborationEventSource.cs" />
<Compile Include="Models\BoxUserFolderCollaborationEventSource.cs" />
<Compile Include="Models\BoxTrackingCode.cs" />
Expand Down Expand Up @@ -262,6 +264,7 @@
<Compile Include="Utility\Helper.cs" />
<Compile Include="Utility\IRetryStrategy.cs" />
<Compile Include="Utility\LRUCache.cs" />
<Compile Include="Utility\ContentTypeMapper.cs" />
<Compile Include="Utility\Retry.cs" />
<Compile Include="Wrappers\BoxError.cs" />
<Compile Include="Wrappers\BoxErrorContextInfo.cs" />
Expand Down
50 changes: 50 additions & 0 deletions Box.V2/Managers/BoxUsersManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Box.V2.Models;
using Box.V2.Models.Request;
using Box.V2.Services;
using Box.V2.Utility;

namespace Box.V2.Managers
{
Expand Down Expand Up @@ -387,5 +388,54 @@ public async Task<Stream> GetUserAvatar(string userId)
IBoxResponse<Stream> response = await ToResponseAsync<Stream>(request).ConfigureAwait(false);
return response.ResponseObject;
}

/// <summary>
/// Adds or updates a user avatar. Supported formats are JPG, JPEG and PNG. Maximum allowed file size is 1MB and 1024x1024 pixels resolution.
/// </summary>
/// <param name="userId">The Id of the user.</param>
/// <param name="stream">FileStream with avatar image.</param>
/// <returns>Response containing avatar Urls.</returns>
public async Task<BoxUploadAvatarResponse> AddOrUpdateUserAvatarAsync(string userId, FileStream stream)
{
return await AddOrUpdateUserAvatarAsync(userId, stream, Path.GetFileName(stream.Name)).ConfigureAwait(false);
}

/// <summary>
/// Adds or updates a user avatar. Supported formats are JPG, JPEG and PNG. Maximum allowed file size is 1MB and 1024x1024 pixels resolution.
/// </summary>
/// <param name="userId">The Id of the user.</param>
/// <param name="stream">Stream with avatar image.</param>
/// <param name="fileName">Filename of the avatar image.</param>
/// <returns>Response containing avatar Urls.</returns>
public async Task<BoxUploadAvatarResponse> AddOrUpdateUserAvatarAsync(string userId, Stream stream, string fileName)
{
BoxMultiPartRequest request = new BoxMultiPartRequest(_config.UserEndpointUri, userId + "/avatar")
.FormPart(new BoxFileFormPart()
{
Name = "pic",
Value = stream,
FileName = fileName,
ContentType = ContentTypeMapper.GetContentTypeFromFilename(fileName)
});

IBoxResponse<BoxUploadAvatarResponse> response = await ToResponseAsync<BoxUploadAvatarResponse>(request).ConfigureAwait(false);

return response.ResponseObject;
}

/// <summary>
/// Deletes a user's avatar image.
/// </summary>
/// <param name="userId">Removes an existing user avatar. You cannot reverse this operation.</param>
/// <returns>True if deletion success.</returns>
public async Task<bool> DeleteUserAvatarAsync(string userId)
{
var request = new BoxRequest(_config.UserEndpointUri, userId + "/avatar")
.Method(RequestMethod.Delete);

IBoxResponse<BoxEntity> response = await ToResponseAsync<BoxEntity>(request).ConfigureAwait(false);

return response.Status == ResponseStatus.Success;
}
}
}
Loading

0 comments on commit 4dcb84a

Please sign in to comment.