From c4e943b9ddfebccb250f74c46a6840859f1104b5 Mon Sep 17 00:00:00 2001
From: mwoda <84077698+mwwoda@users.noreply.github.com>
Date: Wed, 24 Jan 2024 15:42:31 +0100
Subject: [PATCH 1/2] feat: add marker-based pagination version of
`GetFolderItems`
---
.../BoxFolderManagerIntegrationTest.cs | 39 ++++
Box.V2.Test/Box.V2.Test.csproj | 3 +
Box.V2.Test/BoxFoldersManagerTest.cs | 22 ++
.../GetFolderItemsMarkerBased200.json | 198 ++++++++++++++++++
Box.V2/Managers/BoxFoldersManager.cs | 53 ++++-
Box.V2/Managers/IBoxFoldersManager.cs | 23 ++
6 files changed, 337 insertions(+), 1 deletion(-)
create mode 100644 Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json
diff --git a/Box.V2.Test.Integration/BoxFolderManagerIntegrationTest.cs b/Box.V2.Test.Integration/BoxFolderManagerIntegrationTest.cs
index 3b3451b20..df1dce7c9 100644
--- a/Box.V2.Test.Integration/BoxFolderManagerIntegrationTest.cs
+++ b/Box.V2.Test.Integration/BoxFolderManagerIntegrationTest.cs
@@ -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);
+ }
}
}
diff --git a/Box.V2.Test/Box.V2.Test.csproj b/Box.V2.Test/Box.V2.Test.csproj
index a3c2d717c..d86972715 100644
--- a/Box.V2.Test/Box.V2.Test.csproj
+++ b/Box.V2.Test/Box.V2.Test.csproj
@@ -51,6 +51,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/Box.V2.Test/BoxFoldersManagerTest.cs b/Box.V2.Test/BoxFoldersManagerTest.cs
index 9515b115e..41e93217d 100644
--- a/Box.V2.Test/BoxFoldersManagerTest.cs
+++ b/Box.V2.Test/BoxFoldersManagerTest.cs
@@ -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>(It.IsAny()))
+ .Returns(() => Task.FromResult>>(new BoxResponse>()
+ {
+ Status = ResponseStatus.Success,
+ ContentString = LoadFixtureFromJson("Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json")
+ }));
+
+ BoxCollectionMarkerBased 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);
+ }
}
}
diff --git a/Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json b/Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json
new file mode 100644
index 000000000..6669dec91
--- /dev/null
+++ b/Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json
@@ -0,0 +1,198 @@
+{
+ "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": "ceo@example.com"
+ },
+ "modified_by": {
+ "id": "11446498",
+ "type": "user",
+ "name": "Aaron Levie",
+ "login": "ceo@example.com"
+ },
+ "owned_by": {
+ "id": "11446498",
+ "type": "user",
+ "name": "Aaron Levie",
+ "login": "ceo@example.com"
+ },
+ "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": "ceo@example.com"
+ },
+ "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": {
+ "access_token": "c3FIOG9vSGV4VHo4QzAyg5T1JvNnJoZ3ExaVNyQWw6WjRsanRKZG5lQk9qUE1BVQ",
+ "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
+}
diff --git a/Box.V2/Managers/BoxFoldersManager.cs b/Box.V2/Managers/BoxFoldersManager.cs
index 49d17e77c..97cd9b2a0 100644
--- a/Box.V2/Managers/BoxFoldersManager.cs
+++ b/Box.V2/Managers/BoxFoldersManager.cs
@@ -18,7 +18,8 @@ public BoxFoldersManager(IBoxConfig config, IBoxService service, IBoxConverter c
: base(config, service, converter, auth, asUser, suppressNotifications) { }
///
- /// 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
@@ -67,6 +68,56 @@ public async Task> GetFolderItemsAsync(string id, int lim
}
}
+ ///
+ /// 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.
+ ///
+ ///
+ /// The maximum number of items to return in a page. The default is 100 and the max is 1000.
+ /// Position to return results from..
+ /// Attribute(s) to include in the response
+ /// Whether or not to auto-paginate to fetch all items; defaults to false.
+ /// The field to sort items on
+ /// The direction to sort results in: ascending or descending
+ /// The shared link for this folder
+ /// The password for the shared link (if required)
+ /// 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.
+ public async Task> GetFolderItemsMarkerBasedAsync(string id, int limit, string marker = null, IEnumerable 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(request, limit).ConfigureAwait(false);
+ }
+ else
+ {
+ IBoxResponse> response = await ToResponseAsync>(request).ConfigureAwait(false);
+ return response.ResponseObject;
+ }
+ }
+
///
/// Used to create a new empty folder. The new folder will be created inside of the specified parent folder.
///
diff --git a/Box.V2/Managers/IBoxFoldersManager.cs b/Box.V2/Managers/IBoxFoldersManager.cs
index 6f6bb2277..622bdb7ab 100644
--- a/Box.V2/Managers/IBoxFoldersManager.cs
+++ b/Box.V2/Managers/IBoxFoldersManager.cs
@@ -31,6 +31,29 @@ public interface IBoxFoldersManager
Task> GetFolderItemsAsync(string id, int limit, int offset = 0, IEnumerable fields = null, bool autoPaginate = false, string sort = null, BoxSortDirection? direction = null,
string sharedLink = null, string sharedLinkPassword = null);
+ ///
+ /// 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.
+ ///
+ ///
+ /// The maximum number of items to return in a page. The default is 100 and the max is 1000.
+ /// Position to return results from..
+ /// Attribute(s) to include in the response
+ /// Whether or not to auto-paginate to fetch all items; defaults to false.
+ /// The field to sort items on
+ /// The direction to sort results in: ascending or descending
+ /// The shared link for this folder
+ /// The password for the shared link (if required)
+ /// 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.
+ Task> GetFolderItemsMarkerBasedAsync(string id, int limit, string marker = null, IEnumerable fields = null, bool autoPaginate = false, string sort = null, BoxSortDirection? direction = null,
+ string sharedLink = null, string sharedLinkPassword = null);
+
///
/// Used to create a new empty folder. The new folder will be created inside of the specified parent folder.
///
From 86accd458969051a77760dff7cfd1e4e08b6f913 Mon Sep 17 00:00:00 2001
From: mwoda <84077698+mwwoda@users.noreply.github.com>
Date: Wed, 24 Jan 2024 16:42:10 +0100
Subject: [PATCH 2/2] remove sample access token from json
---
.../Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json | 1 -
1 file changed, 1 deletion(-)
diff --git a/Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json b/Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json
index 6669dec91..5aa445531 100644
--- a/Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json
+++ b/Box.V2.Test/Fixtures/BoxFolders/GetFolderItemsMarkerBased200.json
@@ -112,7 +112,6 @@
"extension": "pdf",
"is_package": true,
"expiring_embed_link": {
- "access_token": "c3FIOG9vSGV4VHo4QzAyg5T1JvNnJoZ3ExaVNyQWw6WjRsanRKZG5lQk9qUE1BVQ",
"expires_in": 3600,
"token_type": "bearer",
"restricted_to": [