Skip to content

Commit

Permalink
feat: add marker-based pagination version of GetFolderItems (#936)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwwoda authored Jan 25, 2024
1 parent 3015ce5 commit f877a8f
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 1 deletion.
39 changes: 39 additions & 0 deletions Box.V2.Test.Integration/BoxFolderManagerIntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,5 +242,44 @@ public async Task GetFolderItemsAsync_ForFolderWithSharedLink_ShouldReturnAllFol
Assert.AreEqual(items.TotalCount, 1);
Assert.AreEqual(items.Entries[0].Id, file.Id);
}

[TestMethod]
public async Task GetFolderItemsAsync_WithOffsetPagination_ShouldReturnCorrectNumberOfFolderItems()
{
var folder = await CreateFolder();
await CreateSmallFile(folder.Id);
await CreateSmallFile(folder.Id);

var items = await UserClient.FoldersManager.GetFolderItemsAsync(folder.Id, 1, 0);

Assert.AreEqual(items.Entries.Count, 1);
Assert.AreEqual(items.TotalCount, 2);

items = await UserClient.FoldersManager.GetFolderItemsAsync(folder.Id, 1, 1);

Assert.AreEqual(items.Entries.Count, 1);

items = await UserClient.FoldersManager.GetFolderItemsAsync(folder.Id, 1, 2);
Assert.AreEqual(items.Entries.Count, 0);
}

[TestMethod]
public async Task GetFolderItemsMarkerBasedAsync_WithMarkerPagination_ShouldReturnCorrectNumberOfFolderItems()
{
var folder = await CreateFolder();
await CreateSmallFile(folder.Id);
await CreateSmallFile(folder.Id);

var items = await UserClient.FoldersManager.GetFolderItemsMarkerBasedAsync(folder.Id, 1);

Assert.AreEqual(items.Entries.Count, 1);
Assert.IsNotNull(items.NextMarker);

var nextMarker = items.NextMarker;

items = await UserClient.FoldersManager.GetFolderItemsMarkerBasedAsync(folder.Id, 1, marker: nextMarker);
Assert.AreEqual(items.Entries.Count, 1);
Assert.IsNull(items.NextMarker);
}
}
}
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 @@ -51,6 +51,9 @@
<None Update="Fixtures\BoxFiles\UploadNewVersionUsingSession200.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fixtures\BoxFolders\GetFolderItemsMarkerBased200.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Fixtures\BoxFolders\CreateFolderSharedLink200.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
22 changes: 22 additions & 0 deletions Box.V2.Test/BoxFoldersManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,5 +1063,27 @@ public async Task DeleteFolderLock_ValidResponse()
//Response check
Assert.AreEqual(true, result);
}

[TestMethod]
public async Task GetFolderItemsMarkerBased_ValidResponse_ValidFolder()
{
Handler.Setup(h => h.ExecuteAsync<BoxCollectionMarkerBased<BoxItem>>(It.IsAny<IBoxRequest>()))
.Returns(() => Task.FromResult<IBoxResponse<BoxCollectionMarkerBased<BoxItem>>>(new BoxResponse<BoxCollectionMarkerBased<BoxItem>>()
{
Status = ResponseStatus.Success,
ContentString = LoadFixtureFromJson("Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json")
}));

BoxCollectionMarkerBased<BoxItem> items = await _foldersManager.GetFolderItemsMarkerBasedAsync("0", 1000);

Assert.AreEqual(items.Entries.Count, 1);
Assert.AreEqual(items.Entries[0].Type, "file");
Assert.AreEqual(items.Entries[0].Id, "12345");
Assert.AreEqual(items.Entries[0].SequenceId, "3");
Assert.AreEqual(items.Entries[0].ETag, "1");
Assert.AreEqual(items.Entries[0].Name, "Contract.pdf");
Assert.AreEqual(items.NextMarker, "JV9IRGZmieiBasejOG9yDCRNgd2ymoZIbjsxbJMjIs3kioVii");
Assert.AreEqual(items.Limit, 1000);
}
}
}
197 changes: 197 additions & 0 deletions Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
{
"entries": [
{
"id": "12345",
"etag": "1",
"type": "file",
"sequence_id": "3",
"name": "Contract.pdf",
"sha1": "85136C79CBF9FE36BB9D05D0639C70C265C18D37",
"file_version": {
"id": "12345",
"type": "file_version",
"sha1": "134b65991ed521fcfe4724b7d814ab8ded5185dc"
},
"description": "Contract for Q1 renewal",
"size": 629644,
"path_collection": {
"total_count": 1,
"entries": [
{
"id": "12345",
"etag": "1",
"type": "folder",
"sequence_id": "3",
"name": "Contracts"
}
]
},
"created_at": "2012-12-12T10:53:43-08:00",
"modified_at": "2012-12-12T10:53:43-08:00",
"trashed_at": "2012-12-12T10:53:43-08:00",
"purged_at": "2012-12-12T10:53:43-08:00",
"content_created_at": "2012-12-12T10:53:43-08:00",
"content_modified_at": "2012-12-12T10:53:43-08:00",
"created_by": {
"id": "11446498",
"type": "user",
"name": "Aaron Levie",
"login": "[email protected]"
},
"modified_by": {
"id": "11446498",
"type": "user",
"name": "Aaron Levie",
"login": "[email protected]"
},
"owned_by": {
"id": "11446498",
"type": "user",
"name": "Aaron Levie",
"login": "[email protected]"
},
"shared_link": {
"url": "https://www.box.com/s/vspke7y05sb214wjokpk",
"download_url": "https://www.box.com/shared/static/rh935iit6ewrmw0unyul.jpeg",
"vanity_url": "https://acme.app.box.com/v/my_url/",
"vanity_name": "my_url",
"access": "open",
"effective_access": "company",
"effective_permission": "can_download",
"unshared_at": "2018-04-13T13:53:23-07:00",
"is_password_enabled": true,
"permissions": {
"can_download": true,
"can_preview": true,
"can_edit": false
},
"download_count": 3,
"preview_count": 3
},
"parent": {
"id": "12345",
"etag": "1",
"type": "folder",
"sequence_id": "3",
"name": "Contracts"
},
"item_status": "active",
"version_number": "1",
"comment_count": 10,
"permissions": {
"can_delete": true,
"can_download": true,
"can_invite_collaborator": true,
"can_rename": true,
"can_set_share_access": true,
"can_share": true,
"can_annotate": true,
"can_comment": true,
"can_preview": true,
"can_upload": true,
"can_view_annotations_all": true,
"can_view_annotations_self": true
},
"tags": [
"approved"
],
"lock": {
"id": "11446498",
"type": "lock",
"created_by": {
"id": "11446498",
"type": "user",
"name": "Aaron Levie",
"login": "[email protected]"
},
"created_at": "2012-12-12T10:53:43-08:00",
"expired_at": "2012-12-12T10:53:43-08:00",
"is_download_prevented": true,
"app_type": "office_wopiplus"
},
"extension": "pdf",
"is_package": true,
"expiring_embed_link": {
"expires_in": 3600,
"token_type": "bearer",
"restricted_to": [
{
"scope": "item_download",
"object": {
"id": "12345",
"etag": "1",
"type": "folder",
"sequence_id": "3",
"name": "Contracts"
}
}
],
"url": "https://cloud.app.box.com/preview/expiring_embed/..."
},
"watermark_info": {
"is_watermarked": true
},
"is_accessible_via_shared_link": true,
"allowed_invitee_roles": [
"editor"
],
"is_externally_owned": true,
"has_collaborations": true,
"metadata": {
"enterprise_27335": {
"marketingCollateral": {
"$canEdit": true,
"$id": "01234500-12f1-1234-aa12-b1d234cb567e",
"$parent": "folder_59449484661",
"$scope": "enterprise_27335",
"$template": "marketingCollateral",
"$type": "properties-6bcba49f-ca6d-4d2a-a758-57fe6edf44d0",
"$typeVersion": 2,
"$version": 1
}
}
},
"expires_at": "2012-12-12T10:53:43-08:00",
"representations": {
"entries": [
{
"content": {
"url_template": "https://dl.boxcloud.com/api/2.0/internal_files/123/versions/345/representations/png_paged_2048x2048/content/{+asset_path}?watermark_content=4567"
},
"info": {
"url": "https://api.box.com/2.0/internal_files/123/versions/345/representations/png_paged_2048x2048"
},
"properties": {
"dimensions": "2048x2048",
"paged": true,
"thumb": true
},
"representation": "png",
"status": {
"state": "success"
}
}
]
},
"classification": {
"name": "Top Secret",
"definition": "Content that should not be shared outside the company.",
"color": "#FF0000"
},
"uploader_display_name": "Ellis Wiggins",
"disposition_at": "2012-12-12T10:53:43-08:00",
"shared_link_permission_options": [
"can_preview"
]
}
],
"limit": 1000,
"next_marker": "JV9IRGZmieiBasejOG9yDCRNgd2ymoZIbjsxbJMjIs3kioVii",
"order": [
{
"by": "type",
"direction": "ASC"
}
],
"total_count": 5000
}
53 changes: 52 additions & 1 deletion Box.V2/Managers/BoxFoldersManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public BoxFoldersManager(IBoxConfig config, IBoxService service, IBoxConverter c
: base(config, service, converter, auth, asUser, suppressNotifications) { }

/// <summary>
/// Retrieves the files and/or folders contained within this folder without any other metadata about the folder.
/// Retrieves the files and/or folders contained within this folder without any other metadata about the folder.
/// Uses offset-based pagination.
/// Any attribute in the full files or folders objects can be passed in with the fields parameter to get specific attributes,
/// and only those specific attributes back; otherwise, the mini format is returned for each item by default.
/// Multiple attributes can be passed in using the fields parameter. Paginated results can be
Expand Down Expand Up @@ -67,6 +68,56 @@ public async Task<BoxCollection<BoxItem>> GetFolderItemsAsync(string id, int lim
}
}

/// <summary>
/// Retrieves the files and/or folders contained within this folder without any other metadata about the folder.
/// Uses marker-based pagination.
/// Any attribute in the full files or folders objects can be passed in with the fields parameter to get specific attributes,
/// and only those specific attributes back; otherwise, the mini format is returned for each item by default.
/// Multiple attributes can be passed in using the fields parameter. Paginated results can be
/// retrieved using the limit and marker parameters.
/// </summary>
/// <param name="id"></param>
/// <param name="limit">The maximum number of items to return in a page. The default is 100 and the max is 1000.</param>
/// <param name="marker">Position to return results from..</param>
/// <param name="fields">Attribute(s) to include in the response</param>
/// <param name="autoPaginate">Whether or not to auto-paginate to fetch all items; defaults to false.</param>
/// <param name="sort">The field to sort items on</param>
/// <param name="direction">The direction to sort results in: ascending or descending</param>
/// <param name="sharedLink">The shared link for this folder</param>
/// <param name="sharedLinkPassword">The password for the shared link (if required)</param>
/// <returns>A collection of items contained in the folder is returned. An error is thrown if the folder does not exist,
/// or if any of the parameters are invalid. The total_count returned may not match the number of entries when using enterprise scope,
/// because external folders are hidden the list of entries.</returns>
public async Task<BoxCollectionMarkerBased<BoxItem>> GetFolderItemsMarkerBasedAsync(string id, int limit, string marker = null, IEnumerable<string> fields = null, bool autoPaginate = false, string sort = null, BoxSortDirection? direction = null,
string sharedLink = null, string sharedLinkPassword = null)
{
id.ThrowIfNullOrWhiteSpace("id");

BoxRequest request = new BoxRequest(_config.FoldersEndpointUri, string.Format(Constants.ItemsPathString, id))
.Param("limit", limit.ToString())
.Param("marker", marker)
.Param("usemarker", "true")
.Param("sort", sort)
.Param("direction", direction.ToString())
.Param(ParamFields, fields);

if (!string.IsNullOrEmpty(sharedLink))
{
var sharedLinkHeader = SharedLinkUtils.GetSharedLinkHeader(sharedLink, sharedLinkPassword);
request.Header(sharedLinkHeader.Item1, sharedLinkHeader.Item2);
}

if (autoPaginate)
{
return await AutoPaginateMarker<BoxItem>(request, limit).ConfigureAwait(false);
}
else
{
IBoxResponse<BoxCollectionMarkerBased<BoxItem>> response = await ToResponseAsync<BoxCollectionMarkerBased<BoxItem>>(request).ConfigureAwait(false);
return response.ResponseObject;
}
}

/// <summary>
/// Used to create a new empty folder. The new folder will be created inside of the specified parent folder.
/// </summary>
Expand Down
23 changes: 23 additions & 0 deletions Box.V2/Managers/IBoxFoldersManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ public interface IBoxFoldersManager
Task<BoxCollection<BoxItem>> GetFolderItemsAsync(string id, int limit, int offset = 0, IEnumerable<string> fields = null, bool autoPaginate = false, string sort = null, BoxSortDirection? direction = null,
string sharedLink = null, string sharedLinkPassword = null);

/// <summary>
/// Retrieves the files and/or folders contained within this folder without any other metadata about the folder.
/// Uses marker-based pagination.
/// Any attribute in the full files or folders objects can be passed in with the fields parameter to get specific attributes,
/// and only those specific attributes back; otherwise, the mini format is returned for each item by default.
/// Multiple attributes can be passed in using the fields parameter. Paginated results can be
/// retrieved using the limit and marker parameters.
/// </summary>
/// <param name="id"></param>
/// <param name="limit">The maximum number of items to return in a page. The default is 100 and the max is 1000.</param>
/// <param name="marker">Position to return results from..</param>
/// <param name="fields">Attribute(s) to include in the response</param>
/// <param name="autoPaginate">Whether or not to auto-paginate to fetch all items; defaults to false.</param>
/// <param name="sort">The field to sort items on</param>
/// <param name="direction">The direction to sort results in: ascending or descending</param>
/// <param name="sharedLink">The shared link for this folder</param>
/// <param name="sharedLinkPassword">The password for the shared link (if required)</param>
/// <returns>A collection of items contained in the folder is returned. An error is thrown if the folder does not exist,
/// or if any of the parameters are invalid. The total_count returned may not match the number of entries when using enterprise scope,
/// because external folders are hidden the list of entries.</returns>
Task<BoxCollectionMarkerBased<BoxItem>> GetFolderItemsMarkerBasedAsync(string id, int limit, string marker = null, IEnumerable<string> fields = null, bool autoPaginate = false, string sort = null, BoxSortDirection? direction = null,
string sharedLink = null, string sharedLinkPassword = null);

/// <summary>
/// Used to create a new empty folder. The new folder will be created inside of the specified parent folder.
/// </summary>
Expand Down

0 comments on commit f877a8f

Please sign in to comment.