From 6927cc893d661b9ee211b1ee0de8eb99469f070d Mon Sep 17 00:00:00 2001 From: Sujay Garlanka Date: Mon, 15 Mar 2021 15:30:09 -0400 Subject: [PATCH] Add folder lock functionality (#725) --- Box.V2.Test/BoxFoldersManagerTest.cs | 99 ++++++++++++++++++++++++ Box.V2.Test/BoxResourceManagerTest.cs | 2 + Box.V2/Box.V2.csproj | 2 + Box.V2/Config/BoxConfig.cs | 4 + Box.V2/Config/Constants.cs | 4 + Box.V2/Config/IBoxConfig.cs | 5 +- Box.V2/Converter/BoxItemConverter.cs | 2 + Box.V2/Managers/BoxFoldersManager.cs | 72 +++++++++++++++++ Box.V2/Models/BoxFolderLock.cs | 47 +++++++++++ Box.V2/Models/BoxFolderLockOperations.cs | 26 +++++++ CHANGELOG.md | 1 + docs/folders.md | 40 ++++++++++ 12 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 Box.V2/Models/BoxFolderLock.cs create mode 100644 Box.V2/Models/BoxFolderLockOperations.cs diff --git a/Box.V2.Test/BoxFoldersManagerTest.cs b/Box.V2.Test/BoxFoldersManagerTest.cs index f3c21e6ae..a1f324db2 100644 --- a/Box.V2.Test/BoxFoldersManagerTest.cs +++ b/Box.V2.Test/BoxFoldersManagerTest.cs @@ -5,6 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; @@ -963,5 +964,103 @@ public async Task RemoveWatermarkFromFolder_ValidResponse_RemovedWatermark() Assert.AreEqual(true, result); } + + [TestMethod] + [TestCategory("CI-UNIT-TEST")] + public async Task GetFolderLocks_ValidResponse() + { + /*** Arrange ***/ + string responseString = "{\"entries\":[{\"folder\":{\"id\":\"12345\",\"etag\":\"1\",\"type\":\"folder\",\"sequence_id\":\"3\",\"name\":\"Contracts\"},\"id\":\"12345678\",\"type\":\"folder_lock\",\"created_by\":{\"id\":\"11446498\",\"type\":\"user\"},\"created_at\":\"2020-09-14T23:12:53Z\",\"locked_operations\":{\"move\":true,\"delete\":true},\"lock_type\":\"freeze\"}],\"limit\":1000,\"next_marker\":null}"; + IBoxRequest boxRequest = null; + Handler.Setup(h => h.ExecuteAsync>(It.IsAny())) + .Returns(Task.FromResult>>(new BoxResponse>() + { + Status = ResponseStatus.Success, + ContentString = responseString + })) + .Callback(r => boxRequest = r); + + /*** Act ***/ + string id = "5010739069"; + BoxCollection result = await _foldersManager.GetLocksAsync(id); + + /*** Assert ***/ + //Request check + Assert.IsNotNull(boxRequest); + Assert.AreEqual(RequestMethod.Get, boxRequest.Method); + Assert.AreEqual(Constants.FolderLocksEndpointString + "?folder_id=" + id, boxRequest.AbsoluteUri.AbsoluteUri); + + //Response check + Assert.AreEqual("12345678", result.Entries[0].Id); + Assert.AreEqual("freeze", result.Entries[0].LockType); + Assert.AreEqual(true, result.Entries[0].LockedOperations.Delete); + + } + + [TestMethod] + [TestCategory("CI-UNIT-TEST")] + public async Task CreateFolderLock_ValidResponse() + { + /*** Arrange ***/ + string responseString = "{\"id\":12345678,\"type\":\"folder_lock\",\"created_at\":\"2020-09-14T23:12:53Z\",\"created_by\":{\"id\":11446498,\"type\":\"user\"},\"folder\":{\"id\":12345,\"type\":\"folder\",\"etag\":1,\"name\":\"Contracts\",\"sequence_id\":3},\"lock_type\":\"freeze\",\"locked_operations\":{\"delete\":true,\"move\":true}}"; + IBoxRequest boxRequest = null; + Handler.Setup(h => h.ExecuteAsync(It.IsAny())) + .Returns(Task.FromResult>(new BoxResponse() + { + Status = ResponseStatus.Success, + ContentString = responseString + })) + .Callback(r => boxRequest = r); + + /*** Act ***/ + string id = "5010739069"; + BoxFolderLock result = await _foldersManager.CreateLockAsync(id); + + /*** Assert ***/ + //Request check + Assert.IsNotNull(boxRequest); + Assert.AreEqual(RequestMethod.Post, boxRequest.Method); + Assert.AreEqual(Constants.FolderLocksEndpointString, boxRequest.AbsoluteUri.AbsoluteUri); + JObject payload = JObject.Parse(boxRequest.Payload); + Assert.AreEqual("folder", payload["folder"]["type"]); + Assert.AreEqual(id, payload["folder"]["id"]); + Assert.AreEqual(true, payload["locked_operations"]["move"]); + Assert.AreEqual(true, payload["locked_operations"]["delete"]); + + //Response check + Assert.AreEqual("12345678", result.Id); + Assert.AreEqual("freeze", result.LockType); + Assert.AreEqual(true, result.LockedOperations.Delete); + + } + + [TestMethod] + [TestCategory("CI-UNIT-TEST")] + public async Task DeleteFolderLock_ValidResponse() + { + /*** Arrange ***/ + string responseString = ""; + IBoxRequest boxRequest = null; + Handler.Setup(h => h.ExecuteAsync(It.IsAny())) + .Returns(Task.FromResult>(new BoxResponse() + { + Status = ResponseStatus.Success, + ContentString = responseString + })) + .Callback(r => boxRequest = r); + + /*** Act ***/ + string id = "5010739069"; + bool result = await _foldersManager.DeleteLockAsync(id); + + /*** Assert ***/ + //Request check + Assert.IsNotNull(boxRequest); + Assert.AreEqual(RequestMethod.Delete, boxRequest.Method); + Assert.AreEqual(Constants.FolderLocksEndpointString + id, boxRequest.AbsoluteUri.AbsoluteUri); + + //Response check + Assert.AreEqual(true, result); + } } } diff --git a/Box.V2.Test/BoxResourceManagerTest.cs b/Box.V2.Test/BoxResourceManagerTest.cs index b86a2ac36..e5e9960af 100644 --- a/Box.V2.Test/BoxResourceManagerTest.cs +++ b/Box.V2.Test/BoxResourceManagerTest.cs @@ -26,6 +26,7 @@ public abstract class BoxResourceManagerTest protected Uri MetadataQueryUri = new Uri(Constants.MetadataQueryEndpointString); protected Uri UserUri = new Uri(Constants.UserEndpointString); protected Uri InviteUri = new Uri(Constants.BoxApiUriString + Constants.InviteString); + protected Uri FolderLocksUri = new Uri(Constants.FolderLocksEndpointString); protected BoxResourceManagerTest() { @@ -41,6 +42,7 @@ protected BoxResourceManagerTest() Config.SetupGet(x => x.MetadataQueryUri).Returns(MetadataQueryUri); Config.SetupGet(x => x.UserEndpointUri).Returns(UserUri); Config.SetupGet(x => x.InviteEndpointUri).Returns(InviteUri); + Config.SetupGet(x => x.FolderLocksEndpointUri).Returns(FolderLocksUri); AuthRepository = new AuthRepository(Config.Object, Service, Converter, new OAuthSession("fakeAccessToken", "fakeRefreshToken", 3600, "bearer")); } diff --git a/Box.V2/Box.V2.csproj b/Box.V2/Box.V2.csproj index d6cc0327b..70fc0a2e7 100644 --- a/Box.V2/Box.V2.csproj +++ b/Box.V2/Box.V2.csproj @@ -96,6 +96,8 @@ + + diff --git a/Box.V2/Config/BoxConfig.cs b/Box.V2/Config/BoxConfig.cs index 24baec235..701a0db09 100644 --- a/Box.V2/Config/BoxConfig.cs +++ b/Box.V2/Config/BoxConfig.cs @@ -233,6 +233,10 @@ public static IBoxConfig CreateFromJsonString(string jsonString) /// public virtual Uri ZipDownloadsEndpointUri { get { return new Uri(BoxApiUri, Constants.ZipDownloadsString); } } /// + /// Gets the folder locks endpoint URI. + /// + public virtual Uri FolderLocksEndpointUri { get { return new Uri(BoxApiUri, Constants.FolderLocksString); } } + /// /// The web proxy for HttpRequestHandler /// public IWebProxy WebProxy { get; set; } diff --git a/Box.V2/Config/Constants.cs b/Box.V2/Config/Constants.cs index 320ac3ed8..eaced27b4 100644 --- a/Box.V2/Config/Constants.cs +++ b/Box.V2/Config/Constants.cs @@ -54,6 +54,8 @@ public static class Constants public const string StoragePolicyAssignmentsString = @"storage_policy_assignments/"; public const string StoragePolicyAssignmentsForTargetString = @"storage_policy_assignments"; public const string ZipDownloadsString = @"zip_downloads"; + public const string FolderLocksString = @"folder_locks/"; + /// /// The shared items constant @@ -109,6 +111,7 @@ public static class Constants public const string StoragePoliciesEndpointString = BoxApiUriString + StoragePoliciesString; public const string StoragePolicyAssignmentsEndpointString = BoxApiUriString + StoragePolicyAssignmentsString; public const string StoragePolicyAssignmentsForTargetEndpointString = BoxApiUriString + StoragePolicyAssignmentsForTargetString; + public const string FolderLocksEndpointString = BoxApiUriString + FolderLocksString; /*** Endpoint Paths ***/ public const string ItemsPathString = @"{0}/items"; @@ -184,6 +187,7 @@ public static class Constants public const string TypeStoragePolicy = "storage_policy"; public const string TypeStoragePolicyAssignment = "storage_policy_assignment"; public const string TypeApplication = "application"; + public const string TypeFolderLock = "folder_lock"; /*** File Preview ***/ public const int DefaultRetryDelay = 1000; // milliseconds diff --git a/Box.V2/Config/IBoxConfig.cs b/Box.V2/Config/IBoxConfig.cs index 62e9ae234..66eb2fd85 100644 --- a/Box.V2/Config/IBoxConfig.cs +++ b/Box.V2/Config/IBoxConfig.cs @@ -116,7 +116,10 @@ public interface IBoxConfig /// Gets the zip downloads endpoint URI. /// Uri ZipDownloadsEndpointUri { get; } - + /// + /// Gets the folder locks endpoint URI. + /// + Uri FolderLocksEndpointUri { get; } /// /// The web proxy for HttpRequestHandler /// diff --git a/Box.V2/Converter/BoxItemConverter.cs b/Box.V2/Converter/BoxItemConverter.cs index a01a581a4..c8014ac40 100644 --- a/Box.V2/Converter/BoxItemConverter.cs +++ b/Box.V2/Converter/BoxItemConverter.cs @@ -91,6 +91,8 @@ protected override BoxEntity Create(Type objectType, JObject jObject) return new BoxStoragePolicyAssignment(); case Constants.TypeApplication: return new BoxApplication(); + case Constants.TypeFolderLock: + return new BoxFolderLock(); } } //There is an inconsistency in the events API where file sources have slightly different field names diff --git a/Box.V2/Managers/BoxFoldersManager.cs b/Box.V2/Managers/BoxFoldersManager.cs index e2d356989..53bc59220 100644 --- a/Box.V2/Managers/BoxFoldersManager.cs +++ b/Box.V2/Managers/BoxFoldersManager.cs @@ -4,6 +4,7 @@ using Box.V2.Extensions; using Box.V2.Models; using Box.V2.Services; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -448,5 +449,76 @@ public async Task RemoveWatermarkAsync(string id) return response.Status == ResponseStatus.Success; } + /// + /// Creates a folder lock on a folder, preventing it from being moved and/or deleted. + /// + /// Id of the folder to create a lock on + /// An object representing the lock on the folder + public async Task CreateLockAsync(string id) + { + id.ThrowIfNullOrWhiteSpace("id"); + + JObject bodyObject = new JObject(); + JObject folderObject = new JObject(); + JObject lockOperationsObject = new JObject(); + + folderObject.Add("id", id); + folderObject.Add("type", "folder"); + + lockOperationsObject.Add("move", true); + lockOperationsObject.Add("delete", true); + + bodyObject.Add("folder", folderObject); + bodyObject.Add("locked_operations", lockOperationsObject); + + BoxRequest request = new BoxRequest(_config.FolderLocksEndpointUri) + .Method(RequestMethod.Post) + .Payload(_converter.Serialize(bodyObject)); + request.ContentType = Constants.RequestParameters.ContentTypeJson; + + IBoxResponse response = await ToResponseAsync(request).ConfigureAwait(false); + return response.ResponseObject; + } + + /// + /// Lists all folder locks for a given folder. + /// + /// Id of the folder + /// Whether or not to auto-paginate to fetch all locks. Currently only one lock can exist per folder.; defaults to false. + /// A collection of locks on the folder + public async Task> GetLocksAsync(string id, bool autoPaginate = false) + { + id.ThrowIfNullOrWhiteSpace("id"); + + BoxRequest request = new BoxRequest(_config.FolderLocksEndpointUri) + .Method(RequestMethod.Get) + .Param("folder_id", id); + + if (autoPaginate) + { + return await AutoPaginateLimitOffset(request, 1000); + } + else + { + IBoxResponse> response = await ToResponseAsync>(request).ConfigureAwait(false); + return response.ResponseObject; + } + } + + /// + /// Delete a folder lock on a folder + /// + /// Id of the folder lock + /// True will be returned upon successful deletionr + public async Task DeleteLockAsync(string id) + { + id.ThrowIfNullOrWhiteSpace("id"); + + BoxRequest request = new BoxRequest(_config.FolderLocksEndpointUri, id) + .Method(RequestMethod.Delete); + + IBoxResponse response = await ToResponseAsync(request).ConfigureAwait(false); + return response.Status == ResponseStatus.Success; + } } } diff --git a/Box.V2/Models/BoxFolderLock.cs b/Box.V2/Models/BoxFolderLock.cs new file mode 100644 index 000000000..2c32fb725 --- /dev/null +++ b/Box.V2/Models/BoxFolderLock.cs @@ -0,0 +1,47 @@ +using Newtonsoft.Json; +using System; + +namespace Box.V2.Models +{ + /// + /// Box representation of a folder lock in box + /// + public class BoxFolderLock : BoxEntity + { + public const string FieldCreatedAt = "created_at"; + public const string FieldCreatedBy = "created_by"; + public const string FieldFolder = "folder"; + public const string FieldLockType = "lock_type"; + public const string FieldLockedOperations = "locked_operations"; + + /// + /// The time the lock was created + /// + [JsonProperty(PropertyName = FieldCreatedAt)] + public DateTime? CreatedAt { get; private set; } + + /// + /// The user who created this lock + /// + [JsonProperty(PropertyName = FieldCreatedBy)] + public BoxUser CreatedBy { get; private set; } + + /// + /// The folder that the lock applies to + /// + [JsonProperty(PropertyName = FieldFolder)] + public BoxFolder Folder { get; private set; } + + /// + /// The lock type + /// + [JsonProperty(PropertyName = FieldLockType)] + public string LockType { get; private set; } + + /// + /// The operations locked by a folder lock + /// + [JsonProperty(PropertyName = FieldLockedOperations)] + public BoxFolderLockOperations LockedOperations { get; private set; } + } +} diff --git a/Box.V2/Models/BoxFolderLockOperations.cs b/Box.V2/Models/BoxFolderLockOperations.cs new file mode 100644 index 000000000..43fd520a6 --- /dev/null +++ b/Box.V2/Models/BoxFolderLockOperations.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; + +namespace Box.V2.Models +{ + /// + /// Box representation of a operations that have been locked on a folder lock in box + /// + public class BoxFolderLockOperations + { + public const string FieldDelete = "delete"; + public const string FieldMove = "move"; + + /// + /// Whether deleting the folder is restricted + /// + [JsonProperty(PropertyName = FieldDelete)] + public bool Delete { get; private set; } + + /// + /// Whether deleting the folder is restricted + /// + [JsonProperty(PropertyName = FieldMove)] + public bool Move { get; private set; } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 2653f0118..47d4f43a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add filter fields to get file version retentions ([#717](https://github.com/box/box-windows-sdk-v2/pull/717)) - Add support for search param to get shared link items ([#721](https://github.com/box/box-windows-sdk-v2/pull/721)) +- Add folder lock functionality ([#725](https://github.com/box/box-windows-sdk-v2/pull/725)) ## 3.25.0 [2020-10-19] diff --git a/docs/folders.md b/docs/folders.md index 4e06907fb..02422cc3a 100644 --- a/docs/folders.md +++ b/docs/folders.md @@ -16,6 +16,9 @@ group, and perform other common folder operations (move, copy, delete, etc.). - [Copy a Folder](#copy-a-folder) - [Delete a Folder](#delete-a-folder) - [Create a Shared Link for a Folder](#create-a-shared-link-for-a-folder) +- [Create a Folder Lock](#create-a-folder-lock) +- [Get Folder Locks](#get-folder-locks) +- [Delete a Folder Lock](#delete-a-folder-lock) @@ -129,3 +132,40 @@ var sharedLinkParams = new BoxSharedLinkRequest() BoxFolder folder = await client.FoldersManager.CreateSharedLinkAsync("11111", sharedLinkParams); string sharedLinkUrl = folder.SharedLink.Url; ``` + +Create a Folder Lock +------------- + +To lock a folder, call +[`FoldersManager.CreateLockAsync(string id)`][create-folder-lock] +with the ID of the folder. This prevents the folder from being moved and/or deleted. + + +```c# +BoxFolderLock folderLock = await _foldersManager.CreateLockAsync("11111"); +``` + +Get Folder Locks +------------------------- + +To retrieve a list of the locks on a folder, call +[`FoldersManager.GetLocksAsync(string id, bool autoPaginate`][get-folder-locks] +with the ID of the folder. Currently only one lock can exist per folder. Folder locks define access restrictions placed by folder owners to prevent specific folders from being moved or deleted. + + +```c# +BoxCollection folderLock = await _foldersManager.GetLocksAsync("11111"); +string id = folderLock.Entries[0].Id; +``` + +Delete a Folder Lock +------------------ + +To remove a folder lock, call +[`FoldersManger.DeleteLockAsync(string id)`][delete-folder-lock] +with the ID of the folder lock. + + +```c# +await _foldersManager.DeleteLockAsync("11111"); +```