diff --git a/Box.V2.Test.Integration/BoxRetentionPolicyManagerIntegrationTest.cs b/Box.V2.Test.Integration/BoxRetentionPolicyManagerIntegrationTest.cs new file mode 100644 index 000000000..f41ba86b0 --- /dev/null +++ b/Box.V2.Test.Integration/BoxRetentionPolicyManagerIntegrationTest.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading.Tasks; +using Box.V2.Exceptions; +using Box.V2.Models; +using Box.V2.Models.Request; +using Box.V2.Test.Integration.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Box.V2.Test.Integration +{ + [TestClass] + public class BoxRetentionPolicyManagerIntegrationTest : TestInFolder + { + [TestMethod] + public async Task CreateRetentionPolicyAssignmentAsync_ForRetentionPolicyAssignmentRequest_ShouldSuccess() + { + var adminFolder = await CreateFolderAsAdmin("0"); + var retentionPolicy = await CreateRetentionPolicy(); + var policyAssignmentReq = new BoxRetentionPolicyAssignmentRequest() + { + PolicyId = retentionPolicy.Id, + AssignTo = new BoxRequestEntity() + { + Id = adminFolder.Id, + Type = BoxType.folder + } + }; + var policyAssignment = await AdminClient.RetentionPoliciesManager.CreateRetentionPolicyAssignmentAsync(policyAssignmentReq); + Assert.AreEqual(retentionPolicy.Id, policyAssignment.RetentionPolicy.Id); + Assert.AreEqual(adminFolder.Id, policyAssignment.AssignedTo.Id); + + var result = await AdminClient.RetentionPoliciesManager.DeleteRetentionPolicyAssignmentAsync(policyAssignment.Id); + Assert.IsTrue(result); + } + } +} diff --git a/Box.V2.Test.Integration/Configuration/Commands/DisposableCommands/CreateRetentionPolicyCommand.cs b/Box.V2.Test.Integration/Configuration/Commands/DisposableCommands/CreateRetentionPolicyCommand.cs index 08303c8db..526d1c868 100644 --- a/Box.V2.Test.Integration/Configuration/Commands/DisposableCommands/CreateRetentionPolicyCommand.cs +++ b/Box.V2.Test.Integration/Configuration/Commands/DisposableCommands/CreateRetentionPolicyCommand.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Box.V2.Models; using Box.V2.Models.Request; @@ -26,23 +27,49 @@ public async Task Execute(IBoxClient client) PolicyType = "finite", RetentionLength = 1, DispositionAction = DispositionAction.permanently_delete.ToString(), + RetentionType = BoxRetentionType.modifiable }; + try + { + var response = await client.RetentionPoliciesManager.CreateRetentionPolicyAsync(retentionPolicyRequest); + Policy = response; + } + catch (Exception ex) + { + // TODO: 12-09-2022, @mcong + // There is an error on backend side, which will return 409 status code "conflict" + // but retention policy still created. + // Delete this try-catch after the issue is fixed. + var policies = await client.RetentionPoliciesManager.GetRetentionPoliciesAsync(_policyName); + if (policies.Entries.Count == 1) + { + // Retention policy already created. + var policy = policies.Entries[0]; + var response = await client.RetentionPoliciesManager.GetRetentionPolicyAsync(policy.Id); + Policy = response; + } + else + { + throw ex; + } - var response = await client.RetentionPoliciesManager.CreateRetentionPolicyAsync(retentionPolicyRequest); - Policy = response; + } PolicyId = Policy.Id; - var assignmentRequest = new BoxRetentionPolicyAssignmentRequest() + if (_folderId != null) { - PolicyId = PolicyId, - AssignTo = new BoxRequestEntity() + var assignmentRequest = new BoxRetentionPolicyAssignmentRequest() { - Type = BoxType.folder, - Id = _folderId - } - }; + PolicyId = PolicyId, + AssignTo = new BoxRequestEntity() + { + Type = BoxType.folder, + Id = _folderId + } + }; - await client.RetentionPoliciesManager.CreateRetentionPolicyAssignmentAsync(assignmentRequest); + await client.RetentionPoliciesManager.CreateRetentionPolicyAssignmentAsync(assignmentRequest); + } return PolicyId; } @@ -53,8 +80,17 @@ public async Task Dispose(IBoxClient client) { Status = "retired" }; - - await client.RetentionPoliciesManager.UpdateRetentionPolicyAsync(PolicyId, retentionPolicyRequest); + try + { + await client.RetentionPoliciesManager.UpdateRetentionPolicyAsync(PolicyId, retentionPolicyRequest); + } + catch + { + // TODO: 12-09-2022, @mcong + // There is an error on backend side, which will return 500 status code "Internal Server Error" + // but retention policy still updated. + // Delete this try-catch after the issue is fixed. + } } } } diff --git a/Box.V2.Test.Integration/Configuration/IntegrationTestBase.cs b/Box.V2.Test.Integration/Configuration/IntegrationTestBase.cs index 1c7847ff8..5e02fdae7 100644 --- a/Box.V2.Test.Integration/Configuration/IntegrationTestBase.cs +++ b/Box.V2.Test.Integration/Configuration/IntegrationTestBase.cs @@ -263,7 +263,7 @@ public static MemoryStream CreateBigFileInMemoryStream() return CreateFileInMemoryStream(50000000); } - public static async Task CreateRetentionPolicy(string folderId = "0", CommandScope commandScope = CommandScope.Test) + public static async Task CreateRetentionPolicy(string folderId = null, CommandScope commandScope = CommandScope.Test) { var createRetentionPolicyCommand = new CreateRetentionPolicyCommand(folderId, GetUniqueName("policy"), commandScope); await ExecuteCommand(createRetentionPolicyCommand); diff --git a/Box.V2.Test/BoxRetentionPoliciesManagerTest.cs b/Box.V2.Test/BoxRetentionPoliciesManagerTest.cs index 2ab0ed5b3..33eb40d6d 100644 --- a/Box.V2.Test/BoxRetentionPoliciesManagerTest.cs +++ b/Box.V2.Test/BoxRetentionPoliciesManagerTest.cs @@ -28,6 +28,7 @@ public async Task CreateRetentionPolicy_OptionalParams_Success() var policyType = "finite"; var policyAction = "permanently_delete"; var notifiedUserID = "12345"; + var retentionType = BoxRetentionType.non_modifiable; var responseString = "{" + "\"type\": \"retention_policy\"," + "\"id\": \"123456789\"," @@ -51,7 +52,8 @@ public async Task CreateRetentionPolicy_OptionalParams_Success() + " \"type\": \"user\"," + " \"id\": \"" + notifiedUserID + "\"" + " }" - + "]" + + "]," + + "\"retention_type\": \"non-modifiable\"" + "}"; Handler.Setup(h => h.ExecuteAsync(It.IsAny())) .Returns(Task.FromResult>(new BoxResponse() @@ -64,7 +66,7 @@ public async Task CreateRetentionPolicy_OptionalParams_Success() var requestParams = new BoxRetentionPolicyRequest { AreOwnersNotified = true, - CanOwnerExtendRetention = true + CanOwnerExtendRetention = true, }; var notifiedUser = new BoxRequestEntity { @@ -76,6 +78,7 @@ public async Task CreateRetentionPolicy_OptionalParams_Success() requestParams.PolicyType = policyType; requestParams.RetentionLength = retentionLength; requestParams.DispositionAction = policyAction; + requestParams.RetentionType = retentionType; BoxRetentionPolicy results = await _retentionPoliciesManager.CreateRetentionPolicyAsync(requestParams); /*** Assert ***/ @@ -83,6 +86,7 @@ public async Task CreateRetentionPolicy_OptionalParams_Success() Assert.AreEqual(policyName, results.PolicyName); Assert.AreEqual(policyType, results.PolicyType); Assert.AreEqual(retentionLength.ToString(), results.RetentionLength); + Assert.AreEqual(retentionType, results.RetentionType); Assert.AreEqual(true, results.CanOwnerExtendRetention); Assert.AreEqual(true, results.AreOwnersNotified); Assert.IsNotNull(results.CustomNotificationRecipients); @@ -165,6 +169,23 @@ public async Task AssignPolicyToMetadataTemplate_OptionalParams_Success() Assert.AreEqual(42.ToString(), result.FilterFields[1].Value); } + [TestMethod] + public async Task DeleteRetentionPolicyAssignment_ValidRequest_Success() + { + /*** Arrange ***/ + var responseString = ""; + Handler.Setup(h => h.ExecuteAsync(It.IsAny())) + .Returns(Task.FromResult>(new BoxResponse() + { + Status = ResponseStatus.Success, + ContentString = responseString + })); + bool result = await _retentionPoliciesManager.DeleteRetentionPolicyAssignmentAsync("12345"); + + /*** Assert ***/ + Assert.IsTrue(result); + } + [TestMethod] public async Task GetFileVersionRetentions_OptionalParams_Success() { diff --git a/Box.V2/Managers/BoxRetentionPoliciesManager.cs b/Box.V2/Managers/BoxRetentionPoliciesManager.cs index e78a42287..39d67101c 100644 --- a/Box.V2/Managers/BoxRetentionPoliciesManager.cs +++ b/Box.V2/Managers/BoxRetentionPoliciesManager.cs @@ -172,6 +172,21 @@ public async Task GetRetentionPolicyAssignmentAsyn return response.ResponseObject; } + /// + /// Used to delete a retention policy assignment. + /// + /// ID of the retention policy assignment. + /// True if the retention policy assignment was successfully deleted. + public async Task DeleteRetentionPolicyAssignmentAsync(string retentionPolicyAssignmentId) + { + BoxRequest request = new BoxRequest(_config.RetentionPolicyAssignmentsUri, retentionPolicyAssignmentId) + .Method(RequestMethod.Delete); + + IBoxResponse response = await ToResponseAsync(request).ConfigureAwait(false); + + return response.Status == ResponseStatus.Success; + } + /// /// Retrieves all file version retentions for the given enterprise. /// diff --git a/Box.V2/Managers/IBoxRetentionPoliciesManager.cs b/Box.V2/Managers/IBoxRetentionPoliciesManager.cs index eaa5c68cf..df48539f4 100644 --- a/Box.V2/Managers/IBoxRetentionPoliciesManager.cs +++ b/Box.V2/Managers/IBoxRetentionPoliciesManager.cs @@ -78,6 +78,13 @@ public interface IBoxRetentionPoliciesManager /// The specified retention policy assignment will be returned upon success. Task GetRetentionPolicyAssignmentAsync(string retentionPolicyAssignmentId, IEnumerable fields = null); + /// + /// Used to delete a retention policy assignment. + /// + /// ID of the retention policy assignment. + /// True if the retention policy assignment was successfully deleted. + Task DeleteRetentionPolicyAssignmentAsync(string retentionPolicyAssignmentId); + /// /// Retrieves all file version retentions for the given enterprise. /// diff --git a/Box.V2/Models/BoxRetentionPolicy.cs b/Box.V2/Models/BoxRetentionPolicy.cs index ef34be0cc..85351604f 100644 --- a/Box.V2/Models/BoxRetentionPolicy.cs +++ b/Box.V2/Models/BoxRetentionPolicy.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Runtime.Serialization; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Box.V2.Models { @@ -20,6 +22,7 @@ public class BoxRetentionPolicy : BoxEntity public const string FieldCanOwnerExtendRetention = "can_owner_extend_retention"; public const string FieldAreOwnersNotified = "are_owners_notified"; public const string FieldCustomNotificationRecipients = "custom_notification_recipients"; + public const string FieldRetentionType = "retention_type"; /// /// The name given to the retention policy @@ -87,5 +90,19 @@ public class BoxRetentionPolicy : BoxEntity /// [JsonProperty(PropertyName = FieldCustomNotificationRecipients)] public virtual List CustomNotificationRecipients { get; set; } + + /// + /// The type of retention policy. Value is one of modifiable or non-modifiable. + /// + [JsonProperty(PropertyName = FieldRetentionType)] + [JsonConverter(typeof(StringEnumConverter))] + public virtual BoxRetentionType RetentionType { get; set; } + } + + public enum BoxRetentionType + { + modifiable, + [EnumMember(Value = "non-modifiable")] + non_modifiable } } diff --git a/Box.V2/Models/Request/BoxRetentionPolicyRequest.cs b/Box.V2/Models/Request/BoxRetentionPolicyRequest.cs index a8d48cdde..4cf2baf77 100644 --- a/Box.V2/Models/Request/BoxRetentionPolicyRequest.cs +++ b/Box.V2/Models/Request/BoxRetentionPolicyRequest.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Box.V2.Models.Request { @@ -55,5 +56,13 @@ public class BoxRetentionPolicyRequest /// [JsonProperty(PropertyName = "custom_notification_recipients")] public List CustomNotificationRecipients { get; set; } + + /// + /// Used to determine the type of retention policy, value can be modifiable or non-modifiable + /// When updating a retention policy, you can use non-modifiable type only. You can convert a modifiable policy to non-modifiable, but not the other way around. + /// + [JsonProperty(PropertyName = "retention_type")] + [JsonConverter(typeof(StringEnumConverter))] + public BoxRetentionType RetentionType { get; set; } } }