Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add zip functionality #700

Merged
merged 20 commits into from
Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions Box.V2.Test/BoxFilesManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
using Newtonsoft.Json;
using System.Text;
using System.Globalization;
using Box.V2.Models.Request;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;

namespace Box.V2.Test
{
Expand Down Expand Up @@ -1108,5 +1111,86 @@ public async Task PromoteVersion_ValidResponse()
Assert.AreEqual(DateTime.Parse("2013-11-20T13:20:50-08:00"), result.CreatedAt);
Assert.AreEqual(DateTime.Parse("2013-11-20T13:26:48-08:00"), result.ModifiedAt);
}

[TestMethod]
[TestCategory("CI-UNIT-TEST")]
public async Task DownloadZip_ValidResponse()
{
using (FileStream exampleFile = new FileStream(string.Format("../.././TestData/smalltest.pdf"), FileMode.OpenOrCreate))
{
/*** Arrange ***/
string responseStringCreateZip = "{\"download_url\": \"https://api.box.com/zip_downloads/124hfiowk3fa8kmrwh/content\",\"status_url\": \"https://api.box.com/zip_downloads/124hfiowk3fa8kmrwh/status\",\"expires_at\": \"2018-04-25T11:00:18-07:00\", \"name_conflicts\":[[{\"id\":\"100\",\"type\":\"file\",\"original_name\":\"salary.pdf\",\"download_name\":\"aqc823.pdf\"},{\"id\":\"200\",\"type\": \"file\",\"original_name\":\"salary.pdf\",\"download_name\": \"aci23s.pdf\"}],[{\"id\":\"1000\",\"type\": \"folder\",\"original_name\":\"employees\",\"download_name\":\"3d366a_employees\"},{\"id\":\"2000\",\"type\": \"folder\",\"original_name\":\"employees\",\"download_name\": \"3aa6a7_employees\"}]]}";
string responseStringDownloadStatus = "{\"total_file_count\": 20, \"downloaded_file_count\": 10, \"skipped_file_count\": 10, \"skipped_folder_count\": 10, \"state\": \"succeeded\"}";
IBoxRequest boxRequest = null;
IBoxRequest boxRequest2 = null;
IBoxRequest boxRequest3 = null;
Handler.Setup(h => h.ExecuteAsync<BoxZip>(It.IsAny<IBoxRequest>()))
.Returns(Task.FromResult<IBoxResponse<BoxZip>>(new BoxResponse<BoxZip>()
{
Status = ResponseStatus.Success,
ContentString = responseStringCreateZip
}))
.Callback<IBoxRequest>(r => boxRequest = r);

Handler.Setup(h => h.ExecuteAsync<Stream>(It.IsAny<IBoxRequest>()))
.Returns(Task.FromResult<IBoxResponse<Stream>>(new BoxResponse<Stream>()
{
Status = ResponseStatus.Success,
ResponseObject = exampleFile

}))
.Callback<IBoxRequest>(r => boxRequest2 = r);

Handler.Setup(h => h.ExecuteAsync<BoxZipDownloadStatus>(It.IsAny<IBoxRequest>()))
.Returns(Task.FromResult<IBoxResponse<BoxZipDownloadStatus>>(new BoxResponse<BoxZipDownloadStatus>()
{
Status = ResponseStatus.Success,
ContentString = responseStringDownloadStatus
}))
.Callback<IBoxRequest>(r => boxRequest3 = r);



/*** Act ***/
BoxZipRequest request = new BoxZipRequest();
request.Name = "test";
request.Items = new List<BoxZipRequestItem>();

var file = new BoxZipRequestItem()
{
Id = "466239504569",
Type = BoxZipItemType.file
};
var folder = new BoxZipRequestItem()
{
Id = "466239504580",
Type = BoxZipItemType.folder
};
request.Items.Add(file);
request.Items.Add(folder);
Stream fs = new MemoryStream(100);

BoxZipDownloadStatus status = await _filesManager.DownloadZip(request, fs);
/*** Assert ***/

// Request check
Assert.IsNotNull(boxRequest);
Assert.AreEqual(RequestMethod.Post, boxRequest.Method);
JObject payload = JObject.Parse(boxRequest.Payload);
Assert.AreEqual("test", payload["name"]);
JArray items = JArray.Parse(payload["items"].ToString());
Assert.AreEqual("466239504569", items[0]["id"]);
Assert.AreEqual("file", items[0]["type"]);

// Reponse Check
Assert.AreEqual(status.TotalFileCount, 20);
Assert.AreEqual(status.State, BoxZipDownloadState.succeeded);
Assert.AreEqual(status.NameConflicts[0].items[0].OriginalName, "salary.pdf");
Assert.AreEqual(status.NameConflicts[0].items[1].OriginalName, "salary.pdf");
Assert.AreEqual(status.NameConflicts[1].items[0].OriginalName, "employees");
Assert.AreEqual(status.NameConflicts[1].items[1].OriginalName, "employees");
Assert.AreNotEqual(fs.Length, 0);
}
}
}
}
Binary file added Box.V2.Test/TestData/smalltest.pdf
Binary file not shown.
7 changes: 7 additions & 0 deletions Box.V2/Box.V2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<Compile Include="Auth\Token\ActorTokenBuilder.cs" />
<Compile Include="Auth\Token\TokenExchange.cs" />
<Compile Include="BoxClient.cs" />
<Compile Include="Converter\BoxZipConflictConverter.cs" />
<Compile Include="JWTAuth\BoxJWTAuth.cs" />
<Compile Include="Converter\BoxItemConverter.cs" />
<Compile Include="Exceptions\BoxAuthenticationFailedException.cs" />
Expand Down Expand Up @@ -144,13 +145,18 @@
<Compile Include="Models\BoxWebLink.cs" />
<Compile Include="Models\BoxCollaborationWhitelistEntry.cs" />
<Compile Include="Models\BoxCollaborationWhitelistTargetEntry.cs" />
<Compile Include="Models\BoxZip.cs" />
<Compile Include="Models\BoxZipConflict.cs" />
<Compile Include="Models\BoxZipConflictItem.cs" />
<Compile Include="Models\BoxZipDownloadStatus.cs" />
<Compile Include="Models\Permissions\BoxFilePermission.cs" />
<Compile Include="Models\BoxFileVersion.cs" />
<Compile Include="Models\BoxGroup.cs" />
<Compile Include="Models\BoxGroupMembership.cs" />
<Compile Include="Models\Request\BoxActionableByRequest.cs" />
<Compile Include="Models\BoxSessionParts.cs" />
<Compile Include="Models\Request\BoxFileUploadSessionRequest.cs" />
<Compile Include="Models\Request\BoxZipRequest.cs" />
<Compile Include="Models\Request\BoxLegalHoldPolicyAssignmentRequest.cs" />
<Compile Include="Models\Request\BoxLegalHoldPolicyRequest.cs" />
<Compile Include="Models\Request\BoxMetadataQueryOrderBy.cs" />
Expand Down Expand Up @@ -184,6 +190,7 @@
<Compile Include="Models\Request\BoxUserInviteRequest.cs" />
<Compile Include="Models\Request\BoxWatermarkRequest.cs" />
<Compile Include="Models\Request\BoxWebhookRequest.cs" />
<Compile Include="Models\Request\BoxZipRequestItem.cs" />
<Compile Include="Plugin\BoxResourcePlugins.cs" />
<Compile Include="Plugin\IBoxMetadataManager.cs" />
<Compile Include="Plugin\IResourcePlugin.cs" />
Expand Down
4 changes: 4 additions & 0 deletions Box.V2/Config/BoxConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ public static IBoxConfig CreateFromJsonString(string jsonString)
/// </summary>
public virtual Uri FileVersionLegalHoldsEndpointUri { get { return new Uri(BoxApiUri, Constants.FileVersionLegalHoldsString); } }
/// <summary>
/// Gets the zip downloads endpoint URI.
/// </summary>
public virtual Uri ZipDownloadsEndpointUri { get { return new Uri(BoxApiUri, Constants.ZipDownloadsString); } }
/// <summary>
/// The web proxy for HttpRequestHandler
/// </summary>
public IWebProxy WebProxy { get; set; }
Expand Down
1 change: 1 addition & 0 deletions Box.V2/Config/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public static class Constants
public const string StoragePoliciesString = @"storage_policies/";
public const string StoragePolicyAssignmentsString = @"storage_policy_assignments/";
public const string StoragePolicyAssignmentsForTargetString = @"storage_policy_assignments";
public const string ZipDownloadsString = @"zip_downloads";

/// <summary>
/// The shared items constant
Expand Down
4 changes: 4 additions & 0 deletions Box.V2/Config/IBoxConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ public interface IBoxConfig
/// Gets the file viersion legal holds endpoint URI.
/// </summary>
Uri FileVersionLegalHoldsEndpointUri { get; }
/// <summary>
/// Gets the zip downloads endpoint URI.
/// </summary>
Uri ZipDownloadsEndpointUri { get; }

/// <summary>
/// The web proxy for HttpRequestHandler
Expand Down
40 changes: 40 additions & 0 deletions Box.V2/Converter/BoxZipConflictConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using Box.V2.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Box.V2.Converter
{
internal class BoxZipConflictConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
List<BoxZipConflict> conflicts = new List<BoxZipConflict>();

// Load JObject from stream
JArray conflictsArray = JArray.Load(reader);
foreach (JArray conflict in conflictsArray)
{
BoxZipConflict zipConflict = new BoxZipConflict();
JObject conflictObject = new JObject();
conflictObject.Add("items", conflict);
// Populate the object properties
serializer.Populate(conflictObject.CreateReader(), zipConflict);
conflicts.Add(zipConflict);
}

return conflicts;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
46 changes: 46 additions & 0 deletions Box.V2/Managers/BoxFilesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using Newtonsoft.Json.Linq;
using Box.V2.Models.Request;

namespace Box.V2.Managers
{
Expand Down Expand Up @@ -1326,6 +1327,41 @@ public async Task<BoxRepresentationCollection<BoxRepresentation>> GetRepresentat
return response.ResponseObject.Representations;
}

/// <summary>
/// Creates a zip and downloads it to a given Stream.
/// </summary>
/// <param name="zipRequest">Object of type BoxZipRequest that contains name and items.</param>
/// <param name="output">The stream to where the zip file will be written.</param>
/// <returns>The status of the download.</returns>
/// </summary>
public async Task<BoxZipDownloadStatus> DownloadZip(BoxZipRequest zipRequest, Stream output)
sujaygarlanka marked this conversation as resolved.
Show resolved Hide resolved
{
BoxZip createdZip = await CreateZip(zipRequest);
IBoxRequest downloadRequest = new BoxRequest(createdZip.DownloadUrl);
IBoxResponse<Stream> streamResponse = await ToResponseAsync<Stream>(downloadRequest).ConfigureAwait(false);
Stream fileStream = streamResponse.ResponseObject;

// Default the buffer size to 4K.
const int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesRead = 0;
do
{
bytesRead = fileStream.Read(buffer, 0, bufferSize);
if (bytesRead > 0)
{
output.Write(buffer, 0, bytesRead);
}
} while (bytesRead > 0);

BoxRequest downloadStatusRequest = new BoxRequest(createdZip.StatusUrl)
.Method(RequestMethod.Get);
IBoxResponse<BoxZipDownloadStatus> response = await ToResponseAsync<BoxZipDownloadStatus>(downloadStatusRequest).ConfigureAwait(false);
BoxZipDownloadStatus finalResponse = response.ResponseObject;
finalResponse.NameConflicts = createdZip.NameConflicts;
return finalResponse;
}

/// <summary>
/// Representations are digital assets stored in Box. We can request the following representations: PDF, Extracted Text, Thumbnail,
/// and Single Page depending on whether the file type is supported by passing in the corresponding x-rep-hints header. This will generate a
Expand Down Expand Up @@ -1367,6 +1403,16 @@ public async Task<Stream> GetRepresentationContentAsync(BoxRepresentationRequest

}

private async Task<BoxZip> CreateZip(BoxZipRequest zipRequest)
{
BoxRequest request = new BoxRequest(_config.ZipDownloadsEndpointUri)
.Method(RequestMethod.Post)
.Payload(_converter.Serialize(zipRequest));

IBoxResponse<BoxZip> response = await ToResponseAsync<BoxZip>(request).ConfigureAwait(false);
return response.ResponseObject;
}

private async Task<string> PollRepresentationInfo(string infoUrl)
{
var infoRequest = new BoxRequest(new Uri(infoUrl));
Expand Down
44 changes: 44 additions & 0 deletions Box.V2/Models/BoxZip.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Box.V2.Converter;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace Box.V2.Models
{
/// <summary>
/// Box representation of a created zip
/// </summary>
public class BoxZip
sujaygarlanka marked this conversation as resolved.
Show resolved Hide resolved
{
public const string FieldDownloadUrl = "download_url";
public const string FieldStatusUrl = "status_url";
public const string FieldExpiresAt = "expires_at";
public const string FieldNameConflicts = "name_conflicts";

/// <summary>
/// A URL to download the zip from
/// </summary>
[JsonProperty(PropertyName = FieldDownloadUrl)]
public Uri DownloadUrl { get; private set; }

/// <summary>
/// A URL to get the download status of a zip
/// </summary>
[JsonProperty(PropertyName = FieldStatusUrl)]
public Uri StatusUrl { get; private set; }

/// <summary>
/// The date after which the zip can no longer be downloaded
/// </summary>
[JsonProperty(PropertyName = FieldExpiresAt)]
public DateTime ExpiresAt { get; private set; }

/// <summary>
/// A list of naming conflicts among the files and folders in the zip
/// </summary>
[JsonProperty(PropertyName = FieldNameConflicts)]
[JsonConverter(typeof(BoxZipConflictConverter))]
public List<BoxZipConflict> NameConflicts { get; private set; }
}
}

19 changes: 19 additions & 0 deletions Box.V2/Models/BoxZipConflict.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace Box.V2.Models
{
/// <summary>
/// Represents a conflict that occurs between items that have the same name.
/// </summary>
public class BoxZipConflict
{
/// <summary>
/// The items that have a conflict
/// </summary>
[JsonProperty(PropertyName = "items")]
public List<BoxZipConflictItem> items { get; private set; }
}
}

39 changes: 39 additions & 0 deletions Box.V2/Models/BoxZipConflictItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Newtonsoft.Json;

namespace Box.V2.Models
{
/// <summary>
/// Box representation of a naming conflict creating a zip file for an item
/// </summary>
public class BoxZipConflictItem
{
public const string FieldId = "id";
public const string FieldType = "type";
public const string FieldOriginalName = "original_name";
public const string FieldDownloadName = "download_name";

/// <summary>
/// The Id of the item
/// </summary>
[JsonProperty(PropertyName = FieldId)]
public string Id { get; private set; }

/// <summary>
/// The type of the item
/// </summary>
[JsonProperty(PropertyName = FieldType)]
public string Type { get; private set; }

/// <summary>
/// The original name of the item
/// </summary>
[JsonProperty(PropertyName = FieldOriginalName)]
public string OriginalName { get; private set; }

/// <summary>
/// the new name of the item when it downloads that resolves the conflict
/// </summary>
[JsonProperty(PropertyName = FieldDownloadName)]
public string DownloadName { get; private set; }
}
}
Loading