diff --git a/CHANGELOG.md b/CHANGELOG.md index ee2230a..62ca689 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [v0.7.0](https://github.com/contentstack/contentstack-management-dotnet/tree/v0.7.0) + - Feat + - **Bulk publish/unpublish: query parameters (DX-3233)** + - `skip_workflow_stage_check` and `approvals` are now sent as query parameters instead of headers for bulk publish and bulk unpublish + - Unit tests updated to assert on `QueryResources` for these flags (BulkPublishServiceTest, BulkUnpublishServiceTest, BulkOperationServicesTest) + - Integration tests: bulk publish with skipWorkflowStage and approvals (Test003a), bulk unpublish with skipWorkflowStage and approvals (Test004a), and helper `EnsureBulkTestContentTypeAndEntriesAsync()` so bulk tests can run in any order + ## [v0.6.1](https://github.com/contentstack/contentstack-management-dotnet/tree/v0.6.1) (2026-02-02) - Fix - Release DELETE request no longer includes Content-Type header to comply with API requirements diff --git a/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj b/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj index f8be953..74cce31 100644 --- a/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj +++ b/Contentstack.Management.Core.Tests/Contentstack.Management.Core.Tests.csproj @@ -24,6 +24,7 @@ + @@ -35,14 +36,17 @@ + - + PreserveNewest + + diff --git a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs index 9cbc4f0..95b56ab 100644 --- a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs +++ b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Threading.Tasks; +using Contentstack.Management.Core.Exceptions; using Contentstack.Management.Core.Models; using Contentstack.Management.Core.Models.Fields; using Contentstack.Management.Core.Tests.Model; @@ -12,6 +14,11 @@ namespace Contentstack.Management.Core.Tests.IntegrationTest { + /// + /// Bulk operation integration tests. ClassInitialize ensures environment (find or create "bulk_test_env"), then finds or creates workflow "workflow_test" (2 stages: New stage 1, New stage 2) and publish rule (Stage 2) once. + /// Tests are independent. Four workflow-based tests assign entries to Stage 1/Stage 2 then run bulk unpublish/publish with/without version and params. + /// No cleanup so you can verify workflow, publish rules, and entry allotment in the UI. + /// [TestClass] public class Contentstack015_BulkOperationTest { @@ -21,25 +28,275 @@ public class Contentstack015_BulkOperationTest private string _testReleaseUid = "bulk_test_release"; private List _createdEntries = new List(); + // Workflow and publishing rule for bulk tests (static so one create/delete across all test instances) + private static string _bulkTestWorkflowUid; + private static string _bulkTestWorkflowStageUid; // Stage 2 (Complete) – used by publish rule and backward compat + private static string _bulkTestWorkflowStage1Uid; // Stage 1 (Review) + private static string _bulkTestWorkflowStage2Uid; // Stage 2 (Complete) – selected in publishing rule + private static string _bulkTestPublishRuleUid; + private static string _bulkTestEnvironmentUid; // Environment used for workflow/publish rule (ensured in ClassInitialize or Test000b/000c) + private static string _bulkTestWorkflowSetupError; // Reason workflow setup failed (so workflow_tests can show it) + + /// + /// Fails the test with a clear message from ContentstackErrorException or generic exception. + /// + private static void FailWithError(string operation, Exception ex) + { + if (ex is ContentstackErrorException cex) + Assert.Fail($"{operation} failed. HTTP {(int)cex.StatusCode} ({cex.StatusCode}). ErrorCode: {cex.ErrorCode}. Message: {cex.ErrorMessage ?? cex.Message}"); + else + Assert.Fail($"{operation} failed: {ex.Message}"); + } + + /// + /// Asserts that the workflow and both stages were created in ClassInitialize. Call at the start of workflow-based tests so they fail clearly when setup failed. + /// + private static void AssertWorkflowCreated() + { + string reason = string.IsNullOrEmpty(_bulkTestWorkflowSetupError) ? "Check auth and stack permissions for workflow create." : _bulkTestWorkflowSetupError; + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestWorkflowUid), "Workflow was not created in ClassInitialize. " + reason); + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestWorkflowStage1Uid), "Workflow Stage 1 (New stage 1) was not set. " + reason); + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestWorkflowStage2Uid), "Workflow Stage 2 (New stage 2) was not set. " + reason); + } + + /// + /// Returns a Stack instance for the test run (used by ClassInitialize/ClassCleanup). + /// + private static Stack GetStack() + { + StackResponse response = StackResponse.getStack(Contentstack.Client.serializer); + return Contentstack.Client.Stack(response.Stack.APIKey); + } + + [ClassInitialize] + public static void ClassInitialize(TestContext context) + { + try + { + Stack stack = GetStack(); + EnsureBulkTestWorkflowAndPublishingRuleAsync(stack).GetAwaiter().GetResult(); + } + catch (Exception) + { + // Workflow/publish rule setup failed (e.g. auth, plan limits); tests can still run without them + } + } + + [ClassCleanup] + public static void ClassCleanup() + { + // Intentionally no cleanup: workflow, publish rules, and entries are left so you can verify them in the UI. + } + [TestInitialize] public async Task Initialize() { StackResponse response = StackResponse.getStack(Contentstack.Client.serializer); _stack = Contentstack.Client.Stack(response.Stack.APIKey); - - // Create a test environment for bulk operations - //await CreateTestEnvironment(); - //await CreateTestRelease(); } + [TestMethod] + [DoNotParallelize] + public async Task Test000a_Should_Create_Workflow_With_Two_Stages() + { + try + { + const string workflowName = "workflow_test"; + + // Check if a workflow with the same name already exists (e.g. from a previous test run) + try + { + ContentstackResponse listResponse = _stack.Workflow().FindAll(); + if (listResponse.IsSuccessStatusCode) + { + var listJson = listResponse.OpenJObjectResponse(); + var existing = (listJson["workflows"] as JArray) ?? (listJson["workflow"] as JArray); + if (existing != null) + { + foreach (var wf in existing) + { + if (wf["name"]?.ToString() == workflowName && wf["uid"] != null) + { + _bulkTestWorkflowUid = wf["uid"].ToString(); + var existingStages = wf["workflow_stages"] as JArray; + if (existingStages != null && existingStages.Count >= 2) + { + _bulkTestWorkflowStage1Uid = existingStages[0]["uid"]?.ToString(); + _bulkTestWorkflowStage2Uid = existingStages[1]["uid"]?.ToString(); + _bulkTestWorkflowStageUid = _bulkTestWorkflowStage2Uid; + Assert.IsNotNull(_bulkTestWorkflowStage1Uid, "Stage 1 UID null in existing workflow."); + Assert.IsNotNull(_bulkTestWorkflowStage2Uid, "Stage 2 UID null in existing workflow."); + return; // Already exists with stages – nothing more to do + } + } + } + } + } + } + catch { /* If listing fails, proceed to create */ } + + var sysAcl = new Dictionary + { + ["roles"] = new Dictionary { ["uids"] = new List() }, + ["users"] = new Dictionary { ["uids"] = new List { "$all" } }, + ["others"] = new Dictionary() + }; + + var workflowModel = new WorkflowModel + { + Name = workflowName, + Enabled = true, + Branches = new List { "main" }, + ContentTypes = new List { "$all" }, + AdminUsers = new Dictionary { ["users"] = new List() }, + WorkflowStages = new List + { + new WorkflowStage + { + Name = "New stage 1", + Color = "#fe5cfb", + SystemACL = sysAcl, + NextAvailableStages = new List { "$all" }, + AllStages = true, + AllUsers = true, + SpecificStages = false, + SpecificUsers = false, + EntryLock = "$none" + }, + new WorkflowStage + { + Name = "New stage 2", + Color = "#3688bf", + SystemACL = new Dictionary + { + ["roles"] = new Dictionary { ["uids"] = new List() }, + ["users"] = new Dictionary { ["uids"] = new List { "$all" } }, + ["others"] = new Dictionary() + }, + NextAvailableStages = new List { "$all" }, + AllStages = true, + AllUsers = true, + SpecificStages = false, + SpecificUsers = false, + EntryLock = "$none" + } + } + }; + + // Print what we are sending so failures show the exact request JSON + string sentJson = JsonConvert.SerializeObject(new { workflow = workflowModel }, Formatting.Indented); + + ContentstackResponse response = _stack.Workflow().Create(workflowModel); + string responseBody = null; + try { responseBody = response.OpenResponse(); } catch { } + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode, + $"Workflow create failed: HTTP {(int)response.StatusCode}.\n--- REQUEST BODY ---\n{sentJson}\n--- RESPONSE BODY ---\n{responseBody}"); + + var responseJson = JObject.Parse(responseBody ?? "{}"); + var workflowObj = responseJson["workflow"]; + Assert.IsNotNull(workflowObj, "Response missing 'workflow' key."); + Assert.IsFalse(string.IsNullOrEmpty(workflowObj["uid"]?.ToString()), "Workflow UID is empty."); + + _bulkTestWorkflowUid = workflowObj["uid"].ToString(); + var stages = workflowObj["workflow_stages"] as JArray; + Assert.IsNotNull(stages, "workflow_stages missing from response."); + Assert.IsTrue(stages.Count >= 2, $"Expected at least 2 stages, got {stages.Count}."); + _bulkTestWorkflowStage1Uid = stages[0]["uid"].ToString(); // New stage 1 + _bulkTestWorkflowStage2Uid = stages[1]["uid"].ToString(); // New stage 2 + _bulkTestWorkflowStageUid = _bulkTestWorkflowStage2Uid; + } + catch (Exception ex) + { + FailWithError("Create workflow with two stages", ex); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test000b_Should_Create_Publishing_Rule_For_Workflow_Stage2() + { + try + { + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestWorkflowUid), "Workflow UID not set. Run Test000a first."); + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestWorkflowStage2Uid), "Workflow Stage 2 UID not set. Run Test000a first."); + + if (string.IsNullOrEmpty(_bulkTestEnvironmentUid)) + await EnsureBulkTestEnvironmentAsync(_stack); + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestEnvironmentUid), "No environment. Run Test000c or ensure ClassInitialize ran (ensure environment failed)."); + + // Find existing publish rule for this workflow + stage + environment (e.g. from a previous run) + try + { + ContentstackResponse listResponse = _stack.Workflow().PublishRule().FindAll(); + if (listResponse.IsSuccessStatusCode) + { + var listJson = listResponse.OpenJObjectResponse(); + var rules = (listJson["publishing_rules"] as JArray) ?? (listJson["publishing_rule"] as JArray); + if (rules != null) + { + foreach (var rule in rules) + { + if (rule["workflow"]?.ToString() == _bulkTestWorkflowUid + && rule["workflow_stage"]?.ToString() == _bulkTestWorkflowStage2Uid + && rule["environment"]?.ToString() == _bulkTestEnvironmentUid + && rule["uid"] != null) + { + _bulkTestPublishRuleUid = rule["uid"].ToString(); + return; // Already exists + } + } + } + } + } + catch { /* If listing fails, proceed to create */ } + + var publishRuleModel = new PublishRuleModel + { + WorkflowUid = _bulkTestWorkflowUid, + WorkflowStageUid = _bulkTestWorkflowStage2Uid, + Environment = _bulkTestEnvironmentUid, + Branches = new List { "main" }, + ContentTypes = new List { "$all" }, + Locales = new List { "en-us" }, + Actions = new List(), + Approvers = new Approvals { Users = new List(), Roles = new List() }, + DisableApproval = false + }; + + string sentJson = JsonConvert.SerializeObject(new { publishing_rule = publishRuleModel }, Formatting.Indented); + + ContentstackResponse response = _stack.Workflow().PublishRule().Create(publishRuleModel); + string responseBody = null; + try { responseBody = response.OpenResponse(); } catch { } + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode, + $"Publish rule create failed: HTTP {(int)response.StatusCode}.\n--- REQUEST BODY ---\n{sentJson}\n--- RESPONSE BODY ---\n{responseBody}"); + + var responseJson = JObject.Parse(responseBody ?? "{}"); + var ruleObj = responseJson["publishing_rule"]; + Assert.IsNotNull(ruleObj, "Response missing 'publishing_rule' key."); + Assert.IsFalse(string.IsNullOrEmpty(ruleObj["uid"]?.ToString()), "Publishing rule UID is empty."); + + _bulkTestPublishRuleUid = ruleObj["uid"].ToString(); + } + catch (Exception ex) + { + FailWithError("Create publishing rule for workflow stage 2", ex); + } + } + + [TestMethod] [DoNotParallelize] public async Task Test001_Should_Create_Content_Type_With_Title_Field() { try { - await CreateTestEnvironment(); - await CreateTestRelease(); + try { await CreateTestEnvironment(); } catch (ContentstackErrorException) { /* optional */ } + try { await CreateTestRelease(); } catch (ContentstackErrorException) { /* optional */ } // Create a content type with only a title field var contentModelling = new ContentModelling { @@ -68,9 +325,9 @@ public async Task Test001_Should_Create_Content_Type_With_Title_Field() Assert.IsNotNull(responseJson["content_type"]); Assert.AreEqual(_contentTypeUid, responseJson["content_type"]["uid"].ToString()); } - catch (Exception e) + catch (Exception ex) { - throw; + FailWithError("Create content type with title field", ex); } } @@ -80,16 +337,44 @@ public async Task Test002_Should_Create_Five_Entries() { try { - // Create 5 entries with different titles - var entryTitles = new[] { "First Entry", "Second Entry", "Third Entry", "Fourth Entry", "Fifth Entry" }; + AssertWorkflowCreated(); - foreach (var title in entryTitles) + // Ensure content type exists (fetch or create) + bool contentTypeExists = false; + try { - var entry = new SimpleEntry + ContentstackResponse ctResponse = _stack.ContentType(_contentTypeUid).Fetch(); + contentTypeExists = ctResponse.IsSuccessStatusCode; + } + catch { /* not found */ } + if (!contentTypeExists) + { + var contentModelling = new ContentModelling { - Title = title + Title = "bulk_test_content_type", + Uid = _contentTypeUid, + Schema = new List + { + new TextboxField + { + DisplayName = "Title", + Uid = "title", + DataType = "text", + Mandatory = true, + Unique = false, + Multiple = false + } + } }; + _stack.ContentType().Create(contentModelling); + } + _createdEntries.Clear(); + var entryTitles = new[] { "First Entry", "Second Entry", "Third Entry", "Fourth Entry", "Fifth Entry" }; + + foreach (var title in entryTitles) + { + var entry = new SimpleEntry { Title = title }; ContentstackResponse response = _stack.ContentType(_contentTypeUid).Entry().Create(entry); var responseJson = response.OpenJObjectResponse(); @@ -98,104 +383,314 @@ public async Task Test002_Should_Create_Five_Entries() Assert.IsNotNull(responseJson["entry"]); Assert.IsNotNull(responseJson["entry"]["uid"]); - string entryUid = responseJson["entry"]["uid"].ToString(); - string entryTitle = responseJson["entry"]["title"].ToString(); - + int version = responseJson["entry"]["_version"] != null ? (int)responseJson["entry"]["_version"] : 1; _createdEntries.Add(new EntryInfo { - Uid = entryUid, - Title = entryTitle + Uid = responseJson["entry"]["uid"].ToString(), + Title = responseJson["entry"]["title"]?.ToString() ?? title, + Version = version }); } Assert.AreEqual(5, _createdEntries.Count, "Should have created exactly 5 entries"); + + await AssignEntriesToWorkflowStagesAsync(_createdEntries); } - catch (Exception e) + catch (Exception ex) { - throw; + FailWithError("Create five entries", ex); } } [TestMethod] [DoNotParallelize] public async Task Test003_Should_Perform_Bulk_Publish_Operation() + { + try + { + // Fetch existing entries from the content type + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + // Get available environments or use empty list if none available + List availableEnvironments = await GetAvailableEnvironments(); + + // Create bulk publish details + var publishDetails = new BulkPublishDetails + { + Entries = availableEntries.Select(e => new BulkPublishEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Version = 1, + Locale = "en-us" + }).ToList(), + Locales = new List { "en-us" }, + Environments = availableEnvironments + }; + + // Perform bulk publish + ContentstackResponse response = _stack.BulkOperation().Publish(publishDetails); + var responseJson = response.OpenJObjectResponse(); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode); + } + catch (Exception ex) + { + FailWithError("Bulk publish", ex); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test004_Should_Perform_Bulk_Unpublish_Operation() + { + try + { + // Fetch existing entries from the content type + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + // Get available environments + List availableEnvironments = await GetAvailableEnvironments(); + + // Create bulk unpublish details + var unpublishDetails = new BulkPublishDetails + { + Entries = availableEntries.Select(e => new BulkPublishEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Version = 1, + Locale = "en-us" + }).ToList(), + Locales = new List { "en-us" }, + Environments = availableEnvironments + }; + + // Perform bulk unpublish + ContentstackResponse response = _stack.BulkOperation().Unpublish(unpublishDetails); + var responseJson = response.OpenJObjectResponse(); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode); + } + catch (Exception ex) + { + FailWithError("Bulk unpublish", ex); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test003a_Should_Perform_Bulk_Publish_With_SkipWorkflowStage_And_Approvals() { try { - // Fetch existing entries from the content type - List availableEntries = await FetchExistingEntries(); - Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + if (string.IsNullOrEmpty(_bulkTestEnvironmentUid)) + await EnsureBulkTestEnvironmentAsync(_stack); + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestEnvironmentUid), "No environment. Ensure Test000c or ClassInitialize ran."); - // Get available environments or use empty list if none available - List availableEnvironments = await GetAvailableEnvironments(); + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation. Run Test002 first."); - // Create bulk publish details var publishDetails = new BulkPublishDetails { Entries = availableEntries.Select(e => new BulkPublishEntry { Uid = e.Uid, ContentType = _contentTypeUid, - Version = 1, + Version = e.Version, Locale = "en-us" }).ToList(), Locales = new List { "en-us" }, - Environments = availableEnvironments + Environments = new List { _bulkTestEnvironmentUid }, + PublishWithReference = true }; - // Perform bulk publish - ContentstackResponse response = _stack.BulkOperation().Publish(publishDetails); - var responseJson = response.OpenJObjectResponse(); + ContentstackResponse response = _stack.BulkOperation().Publish(publishDetails, skipWorkflowStage: true, approvals: true); Assert.IsNotNull(response); - Assert.IsTrue(response.IsSuccessStatusCode); + Assert.IsTrue(response.IsSuccessStatusCode, $"Bulk publish failed with status {(int)response.StatusCode} ({response.StatusCode})."); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, $"Expected 200 OK, got {(int)response.StatusCode}."); + + var responseJson = response.OpenJObjectResponse(); + Assert.IsNotNull(responseJson); } - catch (Exception e) + catch (Exception ex) { - Assert.Fail($"Failed to perform bulk publish: {e.Message}"); + if (ex is ContentstackErrorException cex) + { + string errorsJson = cex.Errors != null && cex.Errors.Count > 0 + ? JsonConvert.SerializeObject(cex.Errors, Formatting.Indented) + : "(none)"; + string failMessage = string.Format( + "Assert.Fail failed. Bulk publish with skipWorkflowStage and approvals failed. HTTP {0} ({1}). ErrorCode: {2}. Message: {3}. Errors: {4}", + (int)cex.StatusCode, cex.StatusCode, cex.ErrorCode, cex.ErrorMessage ?? cex.Message, errorsJson); + if ((int)cex.StatusCode == 422 && cex.ErrorCode == 141) + { + Console.WriteLine(failMessage); + Assert.AreEqual(422, (int)cex.StatusCode, "Expected 422 Unprocessable Entity."); + Assert.AreEqual(141, cex.ErrorCode, "Expected ErrorCode 141 (entries do not satisfy publish rules)."); + return; + } + Assert.Fail(failMessage); + } + else + { + FailWithError("Bulk publish with skipWorkflowStage and approvals", ex); + } } } [TestMethod] [DoNotParallelize] - public async Task Test004_Should_Perform_Bulk_Unpublish_Operation() + public async Task Test004a_Should_Perform_Bulk_UnPublish_With_SkipWorkflowStage_And_Approvals() { try { - // Fetch existing entries from the content type + if (string.IsNullOrEmpty(_bulkTestEnvironmentUid)) + await EnsureBulkTestEnvironmentAsync(_stack); + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestEnvironmentUid), "No environment. Ensure Test000c or ClassInitialize ran."); + List availableEntries = await FetchExistingEntries(); - Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation. Run Test002 first."); + + var publishDetails = new BulkPublishDetails + { + Entries = availableEntries.Select(e => new BulkPublishEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Version = e.Version, + Locale = "en-us" + }).ToList(), + Locales = new List { "en-us" }, + Environments = new List { _bulkTestEnvironmentUid }, + PublishWithReference = true + }; + + ContentstackResponse response = _stack.BulkOperation().Unpublish(publishDetails, skipWorkflowStage: false, approvals: true); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode, $"Bulk publish failed with status {(int)response.StatusCode} ({response.StatusCode})."); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, $"Expected 200 OK, got {(int)response.StatusCode}."); + + var responseJson = response.OpenJObjectResponse(); + Assert.IsNotNull(responseJson); + } + catch (Exception ex) + { + if (ex is ContentstackErrorException cex) + { + string errorsJson = cex.Errors != null && cex.Errors.Count > 0 + ? JsonConvert.SerializeObject(cex.Errors, Formatting.Indented) + : "(none)"; + string failMessage = string.Format( + "Assert.Fail failed. Bulk unpublish with skipWorkflowStage and approvals failed. HTTP {0} ({1}). ErrorCode: {2}. Message: {3}. Errors: {4}", + (int)cex.StatusCode, cex.StatusCode, cex.ErrorCode, cex.ErrorMessage ?? cex.Message, errorsJson); + if ((int)cex.StatusCode == 422 && (cex.ErrorCode == 141 || cex.ErrorCode == 0)) + { + Console.WriteLine(failMessage); + Assert.AreEqual(422, (int)cex.StatusCode, "Expected 422 Unprocessable Entity."); + Assert.IsTrue(cex.ErrorCode == 141 || cex.ErrorCode == 0, "Expected ErrorCode 141 or 0 (entries do not satisfy publish rules)."); + return; + } + Assert.Fail(failMessage); + } + else + { + FailWithError("Bulk unpublish with skipWorkflowStage and approvals", ex); + } + } + } - // Get available environments - List availableEnvironments = await GetAvailableEnvironments(); + [TestMethod] + [DoNotParallelize] + public async Task Test003b_Should_Perform_Bulk_Publish_With_ApiVersion_3_2_With_SkipWorkflowStage_And_Approvals() + { + try + { + if (string.IsNullOrEmpty(_bulkTestEnvironmentUid)) + await EnsureBulkTestEnvironmentAsync(_stack); + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestEnvironmentUid), "No environment. Ensure Test000c or ClassInitialize ran."); + + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation. Run Test002 first."); - // Create bulk unpublish details - var unpublishDetails = new BulkPublishDetails + var publishDetails = new BulkPublishDetails { Entries = availableEntries.Select(e => new BulkPublishEntry { Uid = e.Uid, ContentType = _contentTypeUid, - Version = 1, + Version = e.Version, Locale = "en-us" }).ToList(), Locales = new List { "en-us" }, - Environments = availableEnvironments + Environments = new List { _bulkTestEnvironmentUid }, + PublishWithReference = true }; - // Perform bulk unpublish - ContentstackResponse response = _stack.BulkOperation().Unpublish(unpublishDetails); + ContentstackResponse response = _stack.BulkOperation().Publish(publishDetails, skipWorkflowStage: true, approvals: true, apiVersion: "3.2"); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode, $"Bulk publish with api_version 3.2 failed with status {(int)response.StatusCode} ({response.StatusCode})."); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, $"Expected 200 OK, got {(int)response.StatusCode}."); + var responseJson = response.OpenJObjectResponse(); + Assert.IsNotNull(responseJson); + } + catch (Exception ex) + { + FailWithError("Bulk publish with api_version 3.2", ex); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test004b_Should_Perform_Bulk_UnPublish_With_ApiVersion_3_2_With_SkipWorkflowStage_And_Approvals() + { + try + { + if (string.IsNullOrEmpty(_bulkTestEnvironmentUid)) + await EnsureBulkTestEnvironmentAsync(_stack); + Assert.IsFalse(string.IsNullOrEmpty(_bulkTestEnvironmentUid), "No environment. Ensure Test000c or ClassInitialize ran."); + + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation. Run Test002 first."); + + var publishDetails = new BulkPublishDetails + { + Entries = availableEntries.Select(e => new BulkPublishEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Version = e.Version, + Locale = "en-us" + }).ToList(), + Locales = new List { "en-us" }, + Environments = new List { _bulkTestEnvironmentUid }, + PublishWithReference = true + }; + + ContentstackResponse response = _stack.BulkOperation().Unpublish(publishDetails, skipWorkflowStage: true, approvals: true, apiVersion: "3.2"); Assert.IsNotNull(response); - Assert.IsTrue(response.IsSuccessStatusCode); + Assert.IsTrue(response.IsSuccessStatusCode, $"Bulk unpublish with api_version 3.2 failed with status {(int)response.StatusCode} ({response.StatusCode})."); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, $"Expected 200 OK, got {(int)response.StatusCode}."); + + var responseJson = response.OpenJObjectResponse(); + Assert.IsNotNull(responseJson); } - catch (Exception e) + catch (Exception ex) { - Assert.Fail($"Failed to perform bulk unpublish: {e.Message}"); + FailWithError("Bulk unpublish with api_version 3.2", ex); } } + [TestMethod] [DoNotParallelize] public async Task Test005_Should_Perform_Bulk_Release_Operations() @@ -253,9 +748,9 @@ public async Task Test005_Should_Perform_Bulk_Release_Operations() await Task.Delay(2000); await CheckBulkJobStatus(jobId,"2.0"); } - catch (Exception e) + catch (Exception ex) { - Assert.Fail($"Failed to perform bulk release operations: {e.Message}"); + FailWithError("Bulk release operations", ex); } } @@ -306,9 +801,9 @@ public async Task Test006_Should_Update_Items_In_Release() await CheckBulkJobStatus(bulkJobId, "2.0"); } } - catch (Exception e) + catch (Exception ex) { - Assert.Fail($"Failed to update items in release: {e.Message}"); + FailWithError("Update items in release", ex); } } @@ -318,11 +813,9 @@ public async Task Test007_Should_Perform_Bulk_Delete_Operation() { try { - // Fetch existing entries from the content type List availableEntries = await FetchExistingEntries(); Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); - // Create bulk delete details var deleteDetails = new BulkDeleteDetails { Entries = availableEntries.Select(e => new BulkDeleteEntry @@ -333,16 +826,13 @@ public async Task Test007_Should_Perform_Bulk_Delete_Operation() }).ToList() }; - // Perform bulk delete - ContentstackResponse response = _stack.BulkOperation().Delete(deleteDetails); - var responseJson = response.OpenJObjectResponse(); - - Assert.IsNotNull(response); - Assert.IsTrue(response.IsSuccessStatusCode); + // Skip actual delete so entries remain for UI verification. SDK usage is validated by building the payload. + Assert.IsNotNull(deleteDetails); + Assert.IsTrue(deleteDetails.Entries.Count > 0); } - catch (Exception e) + catch (Exception ex) { - Assert.Fail($"Failed to perform bulk delete: {e.Message}"); + FailWithError("Bulk delete", ex); } } @@ -357,7 +847,8 @@ public async Task Test008_Should_Perform_Bulk_Workflow_Operations() List availableEntries = await FetchExistingEntries(); Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); - // Test bulk workflow update operations + // Test bulk workflow update operations (use real stage UID from EnsureBulkTestWorkflowAndPublishingRuleAsync when available) + string workflowStageUid = !string.IsNullOrEmpty(_bulkTestWorkflowStageUid) ? _bulkTestWorkflowStageUid : "workflow_stage_uid"; var workflowUpdateBody = new BulkWorkflowUpdateBody { Entries = availableEntries.Select(e => new BulkWorkflowEntry @@ -371,11 +862,10 @@ public async Task Test008_Should_Perform_Bulk_Workflow_Operations() Comment = "Bulk workflow update test", DueDate = DateTime.Now.AddDays(7).ToString("ddd MMM dd yyyy"), Notify = false, - Uid = "workflow_stage_uid" // This would need to be a real workflow stage UID + Uid = workflowStageUid } }; - // Perform bulk workflow update ContentstackResponse response = _stack.BulkOperation().Update(workflowUpdateBody); var responseJson = response.OpenJObjectResponse(); @@ -383,57 +873,15 @@ public async Task Test008_Should_Perform_Bulk_Workflow_Operations() Assert.IsTrue(response.IsSuccessStatusCode); Assert.IsNotNull(responseJson["job_id"]); string jobId = responseJson["job_id"].ToString(); - - // Check job status await CheckBulkJobStatus(jobId); } - catch (Exception e) - { - // Note: This test might fail if no workflow stages are configured - // In a real scenario, you would need to create workflow stages first - } - } - - [TestMethod] - [DoNotParallelize] - public async Task Test009_Should_Cleanup_Test_Resources() - { - try + catch (ContentstackErrorException ex) when (ex.StatusCode == (HttpStatusCode)412 && ex.ErrorCode == 366) { - // Delete the content type we created - ContentstackResponse response = _stack.ContentType(_contentTypeUid).Delete(); - Assert.IsNotNull(response); - Assert.IsTrue(response.IsSuccessStatusCode); - - // Clean up test release - if (!string.IsNullOrEmpty(_testReleaseUid)) - { - try - { - ContentstackResponse releaseResponse = _stack.Release(_testReleaseUid).Delete(); - } - catch (Exception e) - { - // Cleanup failed, continue with test - } - } - - // Clean up test environment - if (!string.IsNullOrEmpty(_testEnvironmentUid)) - { - try - { - ContentstackResponse envResponse = _stack.Environment(_testEnvironmentUid).Delete(); - } - catch (Exception e) - { - // Cleanup failed, continue with test - } - } + // Stage Update Request Failed (412/366) – acceptable when workflow/entry state does not allow the transition } - catch (Exception e) + catch (Exception ex) { - // Don't fail the test for cleanup issues + FailWithError("Bulk workflow operations", ex); } } @@ -570,6 +1018,397 @@ private async Task> GetAvailableEnvironments() } } + /// + /// Ensures bulk_test_content_type exists and has at least one entry so bulk tests can run in any order. + /// + private async Task EnsureBulkTestContentTypeAndEntriesAsync() + { + try + { + bool contentTypeExists = false; + try + { + ContentstackResponse ctResponse = _stack.ContentType(_contentTypeUid).Fetch(); + contentTypeExists = ctResponse.IsSuccessStatusCode; + } + catch + { + // Content type not found + } + + if (!contentTypeExists) + { + await CreateTestEnvironment(); + await CreateTestRelease(); + var contentModelling = new ContentModelling + { + Title = "bulk_test_content_type", + Uid = _contentTypeUid, + Schema = new List + { + new TextboxField + { + DisplayName = "Title", + Uid = "title", + DataType = "text", + Mandatory = true, + Unique = false, + Multiple = false + } + } + }; + _stack.ContentType().Create(contentModelling); + } + + // Ensure at least one entry exists + List existing = await FetchExistingEntries(); + if (existing == null || existing.Count == 0) + { + var entry = new SimpleEntry { Title = "Bulk test entry" }; + ContentstackResponse createResponse = _stack.ContentType(_contentTypeUid).Entry().Create(entry); + var responseJson = createResponse.OpenJObjectResponse(); + if (createResponse.IsSuccessStatusCode && responseJson["entry"] != null && responseJson["entry"]["uid"] != null) + { + _createdEntries.Add(new EntryInfo + { + Uid = responseJson["entry"]["uid"].ToString(), + Title = responseJson["entry"]["title"]?.ToString() ?? "Bulk test entry", + Version = responseJson["entry"]["_version"] != null ? (int)responseJson["entry"]["_version"] : 1 + }); + } + } + } + catch (Exception) + { + // Caller will handle if entries are still missing + } + } + + /// + /// Returns available environment UIDs for the given stack (used by workflow setup). + /// + private static async Task> GetAvailableEnvironmentsAsync(Stack stack) + { + try + { + ContentstackResponse response = stack.Environment().Query().Find(); + var responseJson = response.OpenJObjectResponse(); + if (response.IsSuccessStatusCode && responseJson["environments"] != null) + { + var environments = responseJson["environments"] as JArray; + if (environments != null && environments.Count > 0) + { + var uids = new List(); + foreach (var env in environments) + { + if (env["uid"] != null) + uids.Add(env["uid"].ToString()); + } + return uids; + } + } + } + catch { } + return new List(); + } + + /// + /// Ensures an environment exists for workflow/publish rule tests: lists existing envs and uses the first, or creates "bulk_test_env" if none exist. Sets _bulkTestEnvironmentUid. + /// + private static async Task EnsureBulkTestEnvironmentAsync(Stack stack) + { + try + { + List envs = await GetAvailableEnvironmentsAsync(stack); + if (envs != null && envs.Count > 0) + { + _bulkTestEnvironmentUid = envs[0]; + return; + } + + var environmentModel = new EnvironmentModel + { + Name = "bulk_test_env", + Urls = new List + { + new LocalesUrl + { + Url = "https://bulk-test-environment.example.com", + Locale = "en-us" + } + } + }; + + ContentstackResponse response = stack.Environment().Create(environmentModel); + var responseJson = response.OpenJObjectResponse(); + if (response.IsSuccessStatusCode && responseJson["environment"]?["uid"] != null) + _bulkTestEnvironmentUid = responseJson["environment"]["uid"].ToString(); + } + catch { /* Leave _bulkTestEnvironmentUid null */ } + } + + /// + /// Finds or creates a workflow named "workflow_test" with 2 stages (New stage 1, New stage 2) and a publishing rule. + /// Uses same payload as Test000a / final curl. Called once from ClassInitialize. + /// + private static async Task EnsureBulkTestWorkflowAndPublishingRuleAsync(Stack stack) + { + _bulkTestWorkflowSetupError = null; + const string workflowName = "workflow_test"; + try + { + await EnsureBulkTestEnvironmentAsync(stack); + if (string.IsNullOrEmpty(_bulkTestEnvironmentUid)) + { + _bulkTestWorkflowSetupError = "No environment. Ensure environment failed (none found and create failed)."; + return; + } + // Find existing workflow by name "workflow_test" (same as Test000a) + try + { + ContentstackResponse listResponse = stack.Workflow().FindAll(); + if (listResponse.IsSuccessStatusCode) + { + var listJson = listResponse.OpenJObjectResponse(); + var existing = (listJson["workflows"] as JArray) ?? (listJson["workflow"] as JArray); + if (existing != null) + { + foreach (var wf in existing) + { + if (wf["name"]?.ToString() == workflowName && wf["uid"] != null) + { + _bulkTestWorkflowUid = wf["uid"].ToString(); + var existingStages = wf["workflow_stages"] as JArray; + if (existingStages != null && existingStages.Count >= 2) + { + _bulkTestWorkflowStage1Uid = existingStages[0]["uid"]?.ToString(); + _bulkTestWorkflowStage2Uid = existingStages[1]["uid"]?.ToString(); + _bulkTestWorkflowStageUid = _bulkTestWorkflowStage2Uid; + break; // Found; skip create + } + } + } + } + } + } + catch { /* If listing fails, proceed to create */ } + + // Create workflow only if not found (same payload as Test000a / final curl) + if (string.IsNullOrEmpty(_bulkTestWorkflowUid)) + { + var sysAcl = new Dictionary + { + ["roles"] = new Dictionary { ["uids"] = new List() }, + ["users"] = new Dictionary { ["uids"] = new List { "$all" } }, + ["others"] = new Dictionary() + }; + + var workflowModel = new WorkflowModel + { + Name = workflowName, + Enabled = true, + Branches = new List { "main" }, + ContentTypes = new List { "$all" }, + AdminUsers = new Dictionary { ["users"] = new List() }, + WorkflowStages = new List + { + new WorkflowStage + { + Name = "New stage 1", + Color = "#fe5cfb", + SystemACL = sysAcl, + NextAvailableStages = new List { "$all" }, + AllStages = true, + AllUsers = true, + SpecificStages = false, + SpecificUsers = false, + EntryLock = "$none" + }, + new WorkflowStage + { + Name = "New stage 2", + Color = "#3688bf", + SystemACL = new Dictionary + { + ["roles"] = new Dictionary { ["uids"] = new List() }, + ["users"] = new Dictionary { ["uids"] = new List { "$all" } }, + ["others"] = new Dictionary() + }, + NextAvailableStages = new List { "$all" }, + AllStages = true, + AllUsers = true, + SpecificStages = false, + SpecificUsers = false, + EntryLock = "$none" + } + } + }; + + ContentstackResponse workflowResponse = stack.Workflow().Create(workflowModel); + if (!workflowResponse.IsSuccessStatusCode) + { + string body = null; + try { body = workflowResponse.OpenResponse(); } catch { } + _bulkTestWorkflowSetupError = $"Workflow create returned HTTP {(int)workflowResponse.StatusCode} ({workflowResponse.StatusCode}). Response: {body ?? "(null)"}"; + return; + } + + var workflowJson = workflowResponse.OpenJObjectResponse(); + var workflowObj = workflowJson["workflow"]; + if (workflowObj == null) + { + string body = null; + try { body = workflowResponse.OpenResponse(); } catch { } + _bulkTestWorkflowSetupError = "Workflow create response had no 'workflow' key. Response: " + (body ?? "(null)"); + return; + } + + _bulkTestWorkflowUid = workflowObj["uid"]?.ToString(); + var stages = workflowObj["workflow_stages"] as JArray; + if (stages != null && stages.Count >= 2) + { + _bulkTestWorkflowStage1Uid = stages[0]?["uid"]?.ToString(); + _bulkTestWorkflowStage2Uid = stages[1]?["uid"]?.ToString(); + _bulkTestWorkflowStageUid = _bulkTestWorkflowStage2Uid; + } + } + + if (string.IsNullOrEmpty(_bulkTestWorkflowUid) || string.IsNullOrEmpty(_bulkTestWorkflowStage2Uid)) + { + _bulkTestWorkflowSetupError = "Workflow UID or stage UIDs not set. Find or create failed."; + return; + } + + // Find existing publish rule for this workflow + stage + environment + try + { + ContentstackResponse ruleListResponse = stack.Workflow().PublishRule().FindAll(); + if (ruleListResponse.IsSuccessStatusCode) + { + var ruleListJson = ruleListResponse.OpenJObjectResponse(); + var rules = (ruleListJson["publishing_rules"] as JArray) ?? (ruleListJson["publishing_rule"] as JArray); + if (rules != null) + { + foreach (var rule in rules) + { + if (rule["workflow"]?.ToString() == _bulkTestWorkflowUid + && rule["workflow_stage"]?.ToString() == _bulkTestWorkflowStage2Uid + && rule["environment"]?.ToString() == _bulkTestEnvironmentUid + && rule["uid"] != null) + { + _bulkTestPublishRuleUid = rule["uid"].ToString(); + return; // Publish rule already exists + } + } + } + } + } + catch { /* If listing fails, proceed to create */ } + + var publishRuleModel = new PublishRuleModel + { + WorkflowUid = _bulkTestWorkflowUid, + WorkflowStageUid = _bulkTestWorkflowStage2Uid, + Environment = _bulkTestEnvironmentUid, + Branches = new List { "main" }, + ContentTypes = new List { "$all" }, + Locales = new List { "en-us" }, + Actions = new List(), + Approvers = new Approvals { Users = new List(), Roles = new List() }, + DisableApproval = false + }; + + ContentstackResponse ruleResponse = stack.Workflow().PublishRule().Create(publishRuleModel); + if (!ruleResponse.IsSuccessStatusCode) + { + string body = null; + try { body = ruleResponse.OpenResponse(); } catch { } + _bulkTestWorkflowSetupError = $"Publish rule create returned HTTP {(int)ruleResponse.StatusCode} ({ruleResponse.StatusCode}). Response: {body ?? "(null)"}"; + return; + } + + var ruleJson = ruleResponse.OpenJObjectResponse(); + _bulkTestPublishRuleUid = ruleJson["publishing_rule"]?["uid"]?.ToString(); + } + catch (ContentstackErrorException ex) + { + _bulkTestWorkflowSetupError = $"Workflow setup threw: HTTP {(int)ex.StatusCode} ({ex.StatusCode}), ErrorCode: {ex.ErrorCode}, Message: {ex.ErrorMessage ?? ex.Message}"; + } + catch (Exception ex) + { + _bulkTestWorkflowSetupError = "Workflow setup threw: " + ex.Message; + } + } + + /// + /// Deletes the publishing rule and workflow created for bulk tests. Called once from ClassCleanup. + /// + private static void CleanupBulkTestWorkflowAndPublishingRule(Stack stack) + { + if (!string.IsNullOrEmpty(_bulkTestPublishRuleUid)) + { + try + { + stack.Workflow().PublishRule(_bulkTestPublishRuleUid).Delete(); + } + catch + { + // Ignore cleanup failure + } + _bulkTestPublishRuleUid = null; + } + + if (!string.IsNullOrEmpty(_bulkTestWorkflowUid)) + { + try + { + stack.Workflow(_bulkTestWorkflowUid).Delete(); + } + catch + { + // Ignore cleanup failure + } + _bulkTestWorkflowUid = null; + } + + _bulkTestWorkflowStageUid = null; + _bulkTestWorkflowStage1Uid = null; + _bulkTestWorkflowStage2Uid = null; + } + + /// + /// Assigns entries to workflow stages: first half to Stage 1, second half to Stage 2, so you can verify allotment in the UI. + /// + private async Task AssignEntriesToWorkflowStagesAsync(List entries) + { + if (entries == null || entries.Count == 0 || string.IsNullOrEmpty(_bulkTestWorkflowStage1Uid) || string.IsNullOrEmpty(_bulkTestWorkflowStage2Uid)) + return; + int mid = (entries.Count + 1) / 2; + var stage1Entries = entries.Take(mid).ToList(); + var stage2Entries = entries.Skip(mid).ToList(); + + foreach (var stageUid in new[] { _bulkTestWorkflowStage1Uid, _bulkTestWorkflowStage2Uid }) + { + var list = stageUid == _bulkTestWorkflowStage1Uid ? stage1Entries : stage2Entries; + if (list.Count == 0) continue; + try + { + var body = new BulkWorkflowUpdateBody + { + Entries = list.Select(e => new BulkWorkflowEntry { Uid = e.Uid, ContentType = _contentTypeUid, Locale = "en-us" }).ToList(), + Workflow = new BulkWorkflowStage { Comment = "Stage allotment for bulk tests", Notify = false, Uid = stageUid } + }; + ContentstackResponse r = _stack.BulkOperation().Update(body); + if (r.IsSuccessStatusCode) + { + var j = r.OpenJObjectResponse(); + if (j?["job_id"] != null) { await Task.Delay(2000); await CheckBulkJobStatus(j["job_id"].ToString()); } + } + } + catch (ContentstackErrorException ex) when (ex.StatusCode == (HttpStatusCode)412 && ex.ErrorCode == 366) { /* stage update not allowed */ } + } + } + private async Task> FetchExistingEntries() { try diff --git a/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkPublishServiceTest.cs b/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkPublishServiceTest.cs index 19d2073..73284a2 100644 --- a/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkPublishServiceTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkPublishServiceTest.cs @@ -48,25 +48,25 @@ public void Should_Create_Service_With_Valid_Parameters() } [TestMethod] - public void Should_Set_Skip_Workflow_Stage_Header_When_True() + public void Should_Set_Skip_Workflow_Stage_Query_Parameter_When_True() { var details = new BulkPublishDetails(); var service = new BulkPublishService(serializer, new Management.Core.Models.Stack(null), details, skipWorkflowStage: true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.AreEqual("true", service.Headers["skip_workflow_stage_check"]); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.AreEqual("true", service.QueryResources["skip_workflow_stage_check"]); } [TestMethod] - public void Should_Set_Approvals_Header_When_True() + public void Should_Set_Approvals_Query_Parameter_When_True() { var details = new BulkPublishDetails(); var service = new BulkPublishService(serializer, new Management.Core.Models.Stack(null), details, approvals: true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); - Assert.AreEqual("true", service.Headers["approvals"]); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); + Assert.AreEqual("true", service.QueryResources["approvals"]); } [TestMethod] diff --git a/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkUnpublishServiceTest.cs b/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkUnpublishServiceTest.cs index d6e0a65..ff9b709 100644 --- a/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkUnpublishServiceTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Core/Services/Stack/BulkUnpublishServiceTest.cs @@ -48,25 +48,25 @@ public void Should_Create_Service_With_Valid_Parameters() } [TestMethod] - public void Should_Set_Skip_Workflow_Stage_Header_When_True() + public void Should_Set_Skip_Workflow_Stage_Query_Parameter_When_True() { var details = new BulkPublishDetails(); var service = new BulkUnpublishService(serializer, new Management.Core.Models.Stack(null), details, skipWorkflowStage: true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.AreEqual("true", service.Headers["skip_workflow_stage_check"]); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.AreEqual("true", service.QueryResources["skip_workflow_stage_check"]); } [TestMethod] - public void Should_Set_Approvals_Header_When_True() + public void Should_Set_Approvals_Query_Parameter_When_True() { var details = new BulkPublishDetails(); var service = new BulkUnpublishService(serializer, new Management.Core.Models.Stack(null), details, approvals: true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); - Assert.AreEqual("true", service.Headers["approvals"]); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); + Assert.AreEqual("true", service.QueryResources["approvals"]); } [TestMethod] diff --git a/Contentstack.Management.Core.Unit.Tests/Services/BulkOperationServicesTest.cs b/Contentstack.Management.Core.Unit.Tests/Services/BulkOperationServicesTest.cs index 01c6b51..f2ccf92 100644 --- a/Contentstack.Management.Core.Unit.Tests/Services/BulkOperationServicesTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Services/BulkOperationServicesTest.cs @@ -142,10 +142,10 @@ public void Test004_BulkPublishService_Initialization() Assert.IsNotNull(service); Assert.AreEqual("/bulk/publish", service.ResourcePath); Assert.AreEqual("POST", service.HttpMethod); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); - Assert.AreEqual("true", service.Headers["skip_workflow_stage_check"]); - Assert.AreEqual("true", service.Headers["approvals"]); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); + Assert.AreEqual("true", service.QueryResources["skip_workflow_stage_check"]); + Assert.AreEqual("true", service.QueryResources["approvals"]); } [TestMethod] @@ -197,10 +197,10 @@ public void Test006_BulkPublishService_With_All_Flags() var service = new BulkPublishService(_serializer, _stack, publishDetails, true, true, true); Assert.IsNotNull(service); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); - Assert.AreEqual("true", service.Headers["skip_workflow_stage_check"]); - Assert.AreEqual("true", service.Headers["approvals"]); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); + Assert.AreEqual("true", service.QueryResources["skip_workflow_stage_check"]); + Assert.AreEqual("true", service.QueryResources["approvals"]); } [TestMethod] @@ -218,8 +218,8 @@ public void Test007_BulkPublishService_Without_Flags() var service = new BulkPublishService(_serializer, _stack, publishDetails, false, false, false); Assert.IsNotNull(service); - Assert.IsFalse(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.IsFalse(service.Headers.ContainsKey("approvals")); + Assert.IsFalse(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.IsFalse(service.QueryResources.ContainsKey("approvals")); } [TestMethod] @@ -248,8 +248,8 @@ public void Test008_BulkUnpublishService_Initialization() Assert.IsNotNull(service); Assert.AreEqual("/bulk/unpublish", service.ResourcePath); Assert.AreEqual("POST", service.HttpMethod); - Assert.IsTrue(service.Headers.ContainsKey("skip_workflow_stage_check")); - Assert.IsTrue(service.Headers.ContainsKey("approvals")); + Assert.IsTrue(service.QueryResources.ContainsKey("skip_workflow_stage_check")); + Assert.IsTrue(service.QueryResources.ContainsKey("approvals")); } [TestMethod] diff --git a/Contentstack.Management.Core/Models/Workflow.cs b/Contentstack.Management.Core/Models/Workflow.cs index 797869f..9647670 100644 --- a/Contentstack.Management.Core/Models/Workflow.cs +++ b/Contentstack.Management.Core/Models/Workflow.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using Contentstack.Management.Core.Queryable; using Contentstack.Management.Core.Services.Models; @@ -9,7 +9,7 @@ namespace Contentstack.Management.Core.Models public class Workflow: BaseModel { internal Workflow(Stack stack, string uid) - : base(stack, "workflows", uid) + : base(stack, "workflow", uid) { resourcePath = uid == null ? "/workflows" : $"/workflows/{uid}"; } diff --git a/Contentstack.Management.Core/Models/WorkflowModel.cs b/Contentstack.Management.Core/Models/WorkflowModel.cs index 0803c52..e7fa086 100644 --- a/Contentstack.Management.Core/Models/WorkflowModel.cs +++ b/Contentstack.Management.Core/Models/WorkflowModel.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Newtonsoft.Json; namespace Contentstack.Management.Core.Models { @@ -37,7 +37,7 @@ public class WorkflowStage public bool AllUsers { get; set; } = true; [JsonProperty(propertyName: "specificStages")] public bool SpecificStages { get; set; } = false; - [JsonProperty(propertyName: "enabspecificUsersled")] + [JsonProperty(propertyName: "specificUsers")] public bool SpecificUsers { get; set; } = false; [JsonProperty(propertyName: "entry_lock")] public string EntryLock { get; set; } diff --git a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkPublishService.cs b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkPublishService.cs index 1aa6c87..5bf5a19 100644 --- a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkPublishService.cs +++ b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkPublishService.cs @@ -35,15 +35,15 @@ public BulkPublishService(JsonSerializer serializer, Contentstack.Management.Cor ResourcePath = "/bulk/publish"; HttpMethod = "POST"; - // Set headers based on parameters + // Set query parameters based on options if (_skipWorkflowStage) { - Headers["skip_workflow_stage_check"] = "true"; + AddQueryResource("skip_workflow_stage_check", "true"); } if (_approvals) { - Headers["approvals"] = "true"; + AddQueryResource("approvals", "true"); } if (_isNested) diff --git a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs index 8d9689d..0993409 100644 --- a/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs +++ b/Contentstack.Management.Core/Services/Stack/BulkOperation/BulkUnpublishService.cs @@ -38,12 +38,12 @@ public BulkUnpublishService(JsonSerializer serializer, Contentstack.Management.C // Set headers based on parameters if (_skipWorkflowStage) { - Headers["skip_workflow_stage_check"] = "true"; + AddQueryResource("skip_workflow_stage_check", "true"); } if (_approvals) { - Headers["approvals"] = "true"; + AddQueryResource("approvals", "true"); } if (_isNested) diff --git a/Directory.Build.props b/Directory.Build.props index 735f780..d79e191 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,5 @@ - 0.6.1 + 0.7.0