diff --git a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack011_GlobalFieldTest.cs b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack011_GlobalFieldTest.cs index 9cb0ec5..aa7d4b7 100644 --- a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack011_GlobalFieldTest.cs +++ b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack011_GlobalFieldTest.cs @@ -107,7 +107,19 @@ public void Test006_Should_Query_Global_Field() [TestMethod] [DoNotParallelize] - public async System.Threading.Tasks.Task Test007_Should_Update_Async_Global_Field() + public void Test006a_Should_Query_Global_Field_With_ApiVersion() + { + ContentstackResponse response = _stack.GlobalField(apiVersion: "3.2").Query().Find(); + GlobalFieldsModel globalField = response.OpenTResponse(); + Assert.IsNotNull(response); + Assert.IsNotNull(globalField); + Assert.IsNotNull(globalField.Modellings); + Assert.AreEqual(1, globalField.Modellings.Count); + } + + [TestMethod] + [DoNotParallelize] + public async System.Threading.Tasks.Task Test007_Should_Query_Async_Global_Field() { ContentstackResponse response = await _stack.GlobalField().Query().FindAsync(); GlobalFieldsModel globalField = response.OpenTResponse(); @@ -116,5 +128,17 @@ public async System.Threading.Tasks.Task Test007_Should_Update_Async_Global_Fiel Assert.IsNotNull(globalField.Modellings); Assert.AreEqual(1, globalField.Modellings.Count); } + + [TestMethod] + [DoNotParallelize] + public async System.Threading.Tasks.Task Test007a_Should_Query_Async_Global_Field_With_ApiVersion() + { + ContentstackResponse response = await _stack.GlobalField(apiVersion: "3.2").Query().FindAsync(); + GlobalFieldsModel globalField = response.OpenTResponse(); + Assert.IsNotNull(response); + Assert.IsNotNull(globalField); + Assert.IsNotNull(globalField.Modellings); + Assert.AreEqual(1, globalField.Modellings.Count); + } } } diff --git a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack012_NestedGlobalFieldTest.cs b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack012_NestedGlobalFieldTest.cs index a2efac1..545789a 100644 --- a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack012_NestedGlobalFieldTest.cs +++ b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack012_NestedGlobalFieldTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using AutoFixture; using Contentstack.Management.Core.Models; @@ -239,6 +240,8 @@ public void Test007_Should_Query_Nested_Global_Fields() Assert.AreEqual("nested_global_field_test", nestedGlobalField.Uid); } + + [TestMethod] [DoNotParallelize] public void Test009_Should_Delete_Referenced_Global_Field() @@ -259,6 +262,5 @@ public void Test008_Should_Delete_Nested_Global_Field() Assert.IsNotNull(response); } - } } \ No newline at end of file diff --git a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs new file mode 100644 index 0000000..cb595f9 --- /dev/null +++ b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack015_BulkOperationTest.cs @@ -0,0 +1,730 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Contentstack.Management.Core.Models; +using Contentstack.Management.Core.Models.Fields; +using Contentstack.Management.Core.Tests.Model; +using Contentstack.Management.Core.Abstractions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Contentstack.Management.Core.Tests.IntegrationTest +{ + [TestClass] + public class Contentstack015_BulkOperationTest + { + private Stack _stack; + private string _contentTypeUid = "bulk_test_content_type"; + private string _testEnvironmentUid = "bulk_test_environment"; + private string _testReleaseUid = "bulk_test_release"; + private List _createdEntries = new List(); + + [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 Test001_Should_Create_Content_Type_With_Title_Field() + { + try + { + await CreateTestEnvironment(); + await CreateTestRelease(); + // Create a content type with only a title field + 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 + } + } + }; + + // Create the content type + ContentstackResponse response = _stack.ContentType().Create(contentModelling); + var responseJson = response.OpenJObjectResponse(); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode); + Assert.IsNotNull(responseJson["content_type"]); + Assert.AreEqual(_contentTypeUid, responseJson["content_type"]["uid"].ToString()); + } + catch (Exception e) + { + throw; + } + } + + [TestMethod] + [DoNotParallelize] + 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" }; + + foreach (var title in entryTitles) + { + var entry = new SimpleEntry + { + Title = title + }; + + ContentstackResponse response = _stack.ContentType(_contentTypeUid).Entry().Create(entry); + var responseJson = response.OpenJObjectResponse(); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode); + Assert.IsNotNull(responseJson["entry"]); + Assert.IsNotNull(responseJson["entry"]["uid"]); + + string entryUid = responseJson["entry"]["uid"].ToString(); + string entryTitle = responseJson["entry"]["title"].ToString(); + + _createdEntries.Add(new EntryInfo + { + Uid = entryUid, + Title = entryTitle + }); + } + + Assert.AreEqual(5, _createdEntries.Count, "Should have created exactly 5 entries"); + } + catch (Exception e) + { + throw; + } + } + + [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 e) + { + Assert.Fail($"Failed to perform bulk publish: {e.Message}"); + } + } + + [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 e) + { + Assert.Fail($"Failed to perform bulk unpublish: {e.Message}"); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test005_Should_Perform_Bulk_Release_Operations() + { + try + { + // Fetch existing entries from the content type + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + // Fetch an available release + string availableReleaseUid = await FetchAvailableRelease(); + Assert.IsFalse(string.IsNullOrEmpty(availableReleaseUid), "No release available for bulk operations"); + + // First, add items to the release + var addItemsData = new BulkAddItemsData + { + Items = availableEntries.Select(e => new BulkAddItem + { + Uid = e.Uid, + ContentType = _contentTypeUid + }).ToList() + }; + + // Now perform bulk release operations using AddItems in deployment mode (only 4 entries) + var releaseData = new BulkAddItemsData + { + Release = availableReleaseUid, + Action = "publish", + Locale = new List { "en-us" }, + Reference = false, + Items = availableEntries.Take(4).Select(e => new BulkAddItem + { + Uid = e.Uid, + ContentType = _contentTypeUid, + ContentTypeUid = _contentTypeUid, + Version = e.Version, + Locale = "en-us", + Title = e.Title + }).ToList() + }; + + // Perform bulk release using AddItems in deployment mode + ContentstackResponse releaseResponse = _stack.BulkOperation().AddItems(releaseData, "2.0"); + var releaseResponseJson = releaseResponse.OpenJObjectResponse(); + + Assert.IsNotNull(releaseResponse); + Assert.IsTrue(releaseResponse.IsSuccessStatusCode); + + // Check if job was created + Assert.IsNotNull(releaseResponseJson["job_id"]); + string jobId = releaseResponseJson["job_id"].ToString(); + + // Wait a bit and check job status + await Task.Delay(2000); + await CheckBulkJobStatus(jobId,"2.0"); + } + catch (Exception e) + { + Assert.Fail($"Failed to perform bulk release operations: {e.Message}"); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test005A_Should_Update_Items_In_Release() + { + try + { + // Fetch existing entries from the content type + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + // Fetch an available release + string availableReleaseUid = await FetchAvailableRelease(); + Assert.IsFalse(string.IsNullOrEmpty(availableReleaseUid), "No release available for bulk operations"); + + // Alternative: Test bulk update items with version 2.0 for release items + var releaseData = new BulkAddItemsData + { + Release = availableReleaseUid, + Action = "publish", + Locale = new List { "en-us" }, + Reference = false, + Items = availableEntries.Skip(4).Take(1).Select(e => new BulkAddItem + { + Uid = e.Uid, + ContentType = _contentTypeUid, + ContentTypeUid = _contentTypeUid, + Version = e.Version, + Locale = "en-us", + Title = e.Title + }).ToList() + }; + + ContentstackResponse bulkUpdateResponse = _stack.BulkOperation().UpdateItems(releaseData, "2.0"); + var bulkUpdateResponseJson = bulkUpdateResponse.OpenJObjectResponse(); + + Assert.IsNotNull(bulkUpdateResponse); + Assert.IsTrue(bulkUpdateResponse.IsSuccessStatusCode); + + if (bulkUpdateResponseJson["job_id"] != null) + { + string bulkJobId = bulkUpdateResponseJson["job_id"].ToString(); + + // Check job status + await Task.Delay(2000); + await CheckBulkJobStatus(bulkJobId, "2.0"); + } + } + catch (Exception e) + { + Assert.Fail($"Failed to update items in release: {e.Message}"); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test006_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 + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Locale = "en-us" + }).ToList() + }; + + // Perform bulk delete + ContentstackResponse response = _stack.BulkOperation().Delete(deleteDetails); + var responseJson = response.OpenJObjectResponse(); + + Assert.IsNotNull(response); + Assert.IsTrue(response.IsSuccessStatusCode); + } + catch (Exception e) + { + Assert.Fail($"Failed to perform bulk delete: {e.Message}"); + } + } + + + [TestMethod] + [DoNotParallelize] + public async Task Test008_Should_Perform_Bulk_Workflow_Operations() + { + try + { + // Fetch existing entries from the content type + List availableEntries = await FetchExistingEntries(); + Assert.IsTrue(availableEntries.Count > 0, "No entries available for bulk operation"); + + // Test bulk workflow update operations + var workflowUpdateBody = new BulkWorkflowUpdateBody + { + Entries = availableEntries.Select(e => new BulkWorkflowEntry + { + Uid = e.Uid, + ContentType = _contentTypeUid, + Locale = "en-us" + }).ToList(), + Workflow = new BulkWorkflowStage + { + 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 + } + }; + + // Perform bulk workflow update + ContentstackResponse response = _stack.BulkOperation().Update(workflowUpdateBody); + var responseJson = response.OpenJObjectResponse(); + + Assert.IsNotNull(response); + 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 + { + // 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 + } + } + } + catch (Exception e) + { + // Don't fail the test for cleanup issues + } + } + + private async Task CheckBulkJobStatus(string jobId, string bulkVersion = null) + { + try + { + ContentstackResponse statusResponse = await _stack.BulkOperation().JobStatusAsync(jobId, bulkVersion); + var statusJson = statusResponse.OpenJObjectResponse(); + + Assert.IsNotNull(statusResponse); + Assert.IsTrue(statusResponse.IsSuccessStatusCode); + } + catch (Exception e) + { + // Failed to check job status + } + } + + private async Task CreateTestEnvironment() + { + try + { + // Create test environment + 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"] != null) + { + _testEnvironmentUid = responseJson["environment"]["uid"].ToString(); + } + } + catch (Exception e) + { + // Don't fail the test if environment creation fails + } + } + + private async Task CreateTestRelease() + { + try + { + // Create test release + var releaseModel = new ReleaseModel + { + Name = "bulk_test_release", + Description = "Release for testing bulk operations", + Locked = false, + Archived = false + }; + + ContentstackResponse response = _stack.Release().Create(releaseModel); + var responseJson = response.OpenJObjectResponse(); + + if (response.IsSuccessStatusCode && responseJson["release"] != null) + { + _testReleaseUid = responseJson["release"]["uid"].ToString(); + } + } + catch (Exception e) + { + // Don't fail the test if release creation fails + } + } + + private async Task> GetAvailableEnvironments() + { + try + { + // First try to use our test environment + if (!string.IsNullOrEmpty(_testEnvironmentUid)) + { + try + { + ContentstackResponse fetchResponse = _stack.Environment(_testEnvironmentUid).Fetch(); + if (fetchResponse.IsSuccessStatusCode) + { + return new List { _testEnvironmentUid }; + } + } + catch + { + // Test environment doesn't exist, fall back to available environments + } + } + + // Try to get available environments + 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 environmentUids = new List(); + foreach (var env in environments) + { + if (env["uid"] != null) + { + environmentUids.Add(env["uid"].ToString()); + } + } + return environmentUids; + } + } + } + catch (Exception e) + { + // Failed to get environments + } + + // Fallback to empty list if no environments found + return new List(); + } + catch (Exception e) + { + return new List(); + } + } + + private async Task> FetchExistingEntries() + { + try + { + // Query entries from the content type + ContentstackResponse response = _stack.ContentType(_contentTypeUid).Entry().Query().Find(); + var responseJson = response.OpenJObjectResponse(); + + if (response.IsSuccessStatusCode && responseJson["entries"] != null) + { + var entries = responseJson["entries"] as JArray; + if (entries != null && entries.Count > 0) + { + var entryList = new List(); + foreach (var entry in entries) + { + if (entry["uid"] != null && entry["title"] != null) + { + entryList.Add(new EntryInfo + { + Uid = entry["uid"].ToString(), + Title = entry["title"].ToString(), + Version = entry["_version"] != null ? (int)entry["_version"] : 1 + }); + } + } + return entryList; + } + } + + return new List(); + } + catch (Exception e) + { + return new List(); + } + } + + private async Task> GetAvailableReleases() + { + try + { + // First try to use our test release + if (!string.IsNullOrEmpty(_testReleaseUid)) + { + try + { + ContentstackResponse fetchResponse = _stack.Release(_testReleaseUid).Fetch(); + if (fetchResponse.IsSuccessStatusCode) + { + return new List { _testReleaseUid }; + } + } + catch + { + // Test release doesn't exist, fall back to available releases + } + } + + // Try to get available releases + try + { + ContentstackResponse response = _stack.Release().Query().Find(); + var responseJson = response.OpenJObjectResponse(); + + if (response.IsSuccessStatusCode && responseJson["releases"] != null) + { + var releases = responseJson["releases"] as JArray; + if (releases != null && releases.Count > 0) + { + var releaseUids = new List(); + foreach (var release in releases) + { + if (release["uid"] != null) + { + releaseUids.Add(release["uid"].ToString()); + } + } + return releaseUids; + } + } + } + catch (Exception e) + { + // Failed to get releases + } + + // Fallback to empty list if no releases found + return new List(); + } + catch (Exception e) + { + return new List(); + } + } + + private async Task FetchAvailableRelease() + { + try + { + // First try to use our test release if it exists + if (!string.IsNullOrEmpty(_testReleaseUid)) + { + try + { + ContentstackResponse fetchResponse = _stack.Release(_testReleaseUid).Fetch(); + if (fetchResponse.IsSuccessStatusCode) + { + return _testReleaseUid; + } + } + catch + { + // Test release not found, look for other releases + } + } + + // Query for available releases + ContentstackResponse response = _stack.Release().Query().Find(); + var responseJson = response.OpenJObjectResponse(); + + if (response.IsSuccessStatusCode && responseJson["releases"] != null) + { + var releases = responseJson["releases"] as JArray; + if (releases != null && releases.Count > 0) + { + // Get the first available release + var firstRelease = releases[0]; + if (firstRelease["uid"] != null) + { + string releaseUid = firstRelease["uid"].ToString(); + return releaseUid; + } + } + } + + return null; + } + catch (Exception e) + { + return null; + } + } + + public class SimpleEntry : IEntry + { + [JsonProperty(propertyName: "title")] + public string Title { get; set; } + } + + public class EntryInfo + { + public string Uid { get; set; } + public string Title { get; set; } + public int Version { get; set; } + } + } +} \ No newline at end of file diff --git a/Contentstack.Management.Core.Unit.Tests/Models/GlobalFieldTest.cs b/Contentstack.Management.Core.Unit.Tests/Models/GlobalFieldTest.cs index b9286ab..32e2973 100644 --- a/Contentstack.Management.Core.Unit.Tests/Models/GlobalFieldTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Models/GlobalFieldTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using AutoFixture; using Contentstack.Management.Core.Models; using Contentstack.Management.Core.Queryable; @@ -41,6 +41,23 @@ public void Initialize_GlobalField() Assert.AreEqual(globalField.Query().GetType(), typeof(Query)); } + [TestMethod] + public void Initialize_GlobalField_With_ApiVersion() + { + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, apiVersion: apiVersion); + + Assert.IsNull(globalField.Uid); + Assert.AreEqual($"/global_fields", globalField.resourcePath); + Assert.ThrowsException(() => globalField.Fetch()); + Assert.ThrowsExceptionAsync(() => globalField.FetchAsync()); + Assert.ThrowsException(() => globalField.Update(new ContentModelling())); + Assert.ThrowsExceptionAsync(() => globalField.UpdateAsync(new ContentModelling())); + Assert.ThrowsException(() => globalField.Delete()); + Assert.ThrowsExceptionAsync(() => globalField.DeleteAsync()); + Assert.AreEqual(globalField.Query().GetType(), typeof(Query)); + } + [TestMethod] public void Initialize_GlobalField_With_Uid() { @@ -53,6 +70,20 @@ public void Initialize_GlobalField_With_Uid() Assert.ThrowsExceptionAsync(() => globalField.CreateAsync(new ContentModelling())); Assert.ThrowsException(() => globalField.Query()); } + + [TestMethod] + public void Initialize_GlobalField_With_Uid_And_ApiVersion() + { + string uid = _fixture.Create(); + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, uid, apiVersion); + + Assert.AreEqual(uid, globalField.Uid); + Assert.AreEqual($"/global_fields/{globalField.Uid}", globalField.resourcePath); + Assert.ThrowsException(() => globalField.Create(new ContentModelling())); + Assert.ThrowsExceptionAsync(() => globalField.CreateAsync(new ContentModelling())); + Assert.ThrowsException(() => globalField.Query()); + } [TestMethod] public void Should_Create_Content_Type() { @@ -80,6 +111,15 @@ public void Should_Query_Content_Type() Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); } + [TestMethod] + public void Should_Query_Content_Type_With_ApiVersion() + { + ContentstackResponse response = _stack.GlobalField(apiVersion: "3.2").Query().Find(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + [TestMethod] public async System.Threading.Tasks.Task Should_Query_Content_Type_Async() { @@ -89,6 +129,15 @@ public async System.Threading.Tasks.Task Should_Query_Content_Type_Async() Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); } + [TestMethod] + public async System.Threading.Tasks.Task Should_Query_Content_Type_Async_With_ApiVersion() + { + ContentstackResponse response = await _stack.GlobalField(apiVersion: "3.2").Query().FindAsync(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + [TestMethod] public void Should_Fetch_Content_Type() { @@ -142,5 +191,137 @@ public async System.Threading.Tasks.Task Should_Delete_Content_Type_Async() Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); } + + [TestMethod] + public void Should_Use_Specialized_Service_For_Create_With_ApiVersion() + { + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, apiVersion: apiVersion); + + // This should use the specialized GlobalFieldService + ContentstackResponse response = globalField.Create(new ContentModelling()); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async System.Threading.Tasks.Task Should_Use_Specialized_Service_For_Create_Async_With_ApiVersion() + { + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, apiVersion: apiVersion); + + // This should use the specialized GlobalFieldService + ContentstackResponse response = await globalField.CreateAsync(new ContentModelling()); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Use_Specialized_Service_For_Update_With_ApiVersion() + { + string uid = _fixture.Create(); + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, uid, apiVersion); + + // This should use the specialized GlobalFieldService + ContentstackResponse response = globalField.Update(new ContentModelling()); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async System.Threading.Tasks.Task Should_Use_Specialized_Service_For_Update_Async_With_ApiVersion() + { + string uid = _fixture.Create(); + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, uid, apiVersion); + + // This should use the specialized GlobalFieldService + ContentstackResponse response = await globalField.UpdateAsync(new ContentModelling()); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Use_Specialized_Service_For_Fetch_With_ApiVersion() + { + string uid = _fixture.Create(); + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, uid, apiVersion); + + // This should use the specialized GlobalFieldFetchDeleteService + ContentstackResponse response = globalField.Fetch(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async System.Threading.Tasks.Task Should_Use_Specialized_Service_For_Fetch_Async_With_ApiVersion() + { + string uid = _fixture.Create(); + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, uid, apiVersion); + + // This should use the specialized GlobalFieldFetchDeleteService + ContentstackResponse response = await globalField.FetchAsync(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Use_Specialized_Service_For_Delete_With_ApiVersion() + { + string uid = _fixture.Create(); + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, uid, apiVersion); + + // This should use the specialized GlobalFieldFetchDeleteService + ContentstackResponse response = globalField.Delete(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async System.Threading.Tasks.Task Should_Use_Specialized_Service_For_Delete_Async_With_ApiVersion() + { + string uid = _fixture.Create(); + string apiVersion = "3.2"; + GlobalField globalField = new GlobalField(_stack, uid, apiVersion); + + // This should use the specialized GlobalFieldFetchDeleteService + ContentstackResponse response = await globalField.DeleteAsync(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Handle_Nested_GlobalField_Operations_Without_ApiVersion() + { + // Test that operations work normally without apiVersion + GlobalField globalField = new GlobalField(_stack); + + ContentstackResponse response = globalField.Create(new ContentModelling()); + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Handle_Nested_GlobalField_Operations_With_Empty_ApiVersion() + { + // Test that operations work normally with empty apiVersion + GlobalField globalField = new GlobalField(_stack, apiVersion: ""); + + ContentstackResponse response = globalField.Create(new ContentModelling()); + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } } } diff --git a/Contentstack.Management.Core.Unit.Tests/Models/StackTest.cs b/Contentstack.Management.Core.Unit.Tests/Models/StackTest.cs index 2830139..20de201 100644 --- a/Contentstack.Management.Core.Unit.Tests/Models/StackTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Models/StackTest.cs @@ -955,7 +955,7 @@ public async System.Threading.Tasks.Task Should_Add_Items_Bulk_Operation_Async() } }; - ContentstackResponse response = await stack.BulkOperation().AddItemsAsync(itemsData); + ContentstackResponse response = await stack.BulkOperation().AddItemsAsync(itemsData, "1.0"); Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); @@ -1009,7 +1009,34 @@ public void Should_Update_Items_Bulk_Operation() } }; - ContentstackResponse response = stack.BulkOperation().UpdateItems(itemsData); + ContentstackResponse response = stack.BulkOperation().UpdateItems(itemsData, "1.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Update_Items_Bulk_Operation_With_Version() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1" + } + } + }; + + ContentstackResponse response = stack.BulkOperation().UpdateItems(itemsData, "2.0"); Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); @@ -1036,12 +1063,339 @@ public async System.Threading.Tasks.Task Should_Update_Items_Bulk_Operation_Asyn } }; - ContentstackResponse response = await stack.BulkOperation().UpdateItemsAsync(itemsData); + ContentstackResponse response = await stack.BulkOperation().UpdateItemsAsync(itemsData, "1.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async System.Threading.Tasks.Task Should_Update_Items_Bulk_Operation_Async_With_Version() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1" + } + } + }; + + ContentstackResponse response = await stack.BulkOperation().UpdateItemsAsync(itemsData, "2.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Add_Items_With_Deployment_Mode_Bulk_Operation() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Release = "release_uid_123", + Action = "publish", + Locale = new List { "en-us" }, + Reference = true, + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1", + ContentTypeUid = "content_type_1", + Version = 1, + Locale = "en-us", + Title = "Test Entry" + } + } + }; + + ContentstackResponse response = stack.BulkOperation().AddItems(itemsData, "2.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async System.Threading.Tasks.Task Should_Add_Items_With_Deployment_Mode_Bulk_Operation_Async() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Release = "release_uid_123", + Action = "publish", + Locale = new List { "en-us" }, + Reference = true, + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1", + ContentTypeUid = "content_type_1", + Version = 1, + Locale = "en-us", + Title = "Test Entry" + } + } + }; + + ContentstackResponse response = await stack.BulkOperation().AddItemsAsync(itemsData, "2.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Update_Items_With_Deployment_Mode_Bulk_Operation() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Release = "release_uid_123", + Action = "unpublish", + Locale = new List { "en-us" }, + Reference = false, + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1", + ContentTypeUid = "content_type_1", + Version = 1, + Locale = "en-us", + Title = "Test Entry" + } + } + }; + + ContentstackResponse response = stack.BulkOperation().UpdateItems(itemsData, "2.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async System.Threading.Tasks.Task Should_Update_Items_With_Deployment_Mode_Bulk_Operation_Async() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Release = "release_uid_123", + Action = "unpublish", + Locale = new List { "en-us" }, + Reference = false, + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1", + ContentTypeUid = "content_type_1", + Version = 1, + Locale = "en-us", + Title = "Test Entry" + } + } + }; + + ContentstackResponse response = await stack.BulkOperation().UpdateItemsAsync(itemsData, "2.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Test_AddItems_Simple_Mode_With_Version() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1" + } + } + }; + + // Test simple mode (no release properties set) + Assert.IsFalse(itemsData.IsReleaseDeploymentMode()); + + ContentstackResponse response = stack.BulkOperation().AddItems(itemsData, "1.0"); Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); } + [TestMethod] + public async System.Threading.Tasks.Task Should_Test_AddItems_Simple_Mode_With_Version_Async() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1" + } + } + }; + + // Test simple mode (no release properties set) + Assert.IsFalse(itemsData.IsReleaseDeploymentMode()); + + ContentstackResponse response = await stack.BulkOperation().AddItemsAsync(itemsData, "2.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Test_UpdateItems_Simple_Mode_With_Version() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1" + } + } + }; + + // Test simple mode (no release properties set) + Assert.IsFalse(itemsData.IsReleaseDeploymentMode()); + + ContentstackResponse response = stack.BulkOperation().UpdateItems(itemsData, "1.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async System.Threading.Tasks.Task Should_Test_UpdateItems_Simple_Mode_With_Version_Async() + { + var contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + + Stack stack = new Stack(client, _fixture.Create()); + + var itemsData = new BulkAddItemsData + { + Items = new List + { + new BulkAddItem + { + Uid = "entry_uid_1", + ContentType = "content_type_1" + } + } + }; + + // Test simple mode (no release properties set) + Assert.IsFalse(itemsData.IsReleaseDeploymentMode()); + + ContentstackResponse response = await stack.BulkOperation().UpdateItemsAsync(itemsData, "2.0"); + + Assert.AreEqual(contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Test_Deployment_Mode_Detection() + { + // Test deployment mode detection + var deploymentData = new BulkAddItemsData + { + Release = "release_uid_123", + Action = "publish", + Items = new List + { + new BulkAddItem { Uid = "entry_uid", ContentType = "content_type" } + } + }; + + Assert.IsTrue(deploymentData.IsReleaseDeploymentMode()); + + // Test simple mode detection + var simpleData = new BulkAddItemsData + { + Items = new List + { + new BulkAddItem { Uid = "entry_uid", ContentType = "content_type" } + } + }; + + Assert.IsFalse(simpleData.IsReleaseDeploymentMode()); + + // Test partial data (missing action) + var partialData = new BulkAddItemsData + { + Release = "release_uid_123", + Items = new List + { + new BulkAddItem { Uid = "entry_uid", ContentType = "content_type" } + } + }; + + Assert.IsFalse(partialData.IsReleaseDeploymentMode()); // Should be false without action + } + [TestMethod] public void Should_Get_Job_Status_Bulk_Operation() { diff --git a/Contentstack.Management.Core.Unit.Tests/Mokes/MockHttpResponse.cs b/Contentstack.Management.Core.Unit.Tests/Mokes/MockHttpResponse.cs new file mode 100644 index 0000000..044de05 --- /dev/null +++ b/Contentstack.Management.Core.Unit.Tests/Mokes/MockHttpResponse.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Net; +using Contentstack.Management.Core; +using Newtonsoft.Json.Linq; + +namespace Contentstack.Management.Core.Unit.Tests.Mokes +{ + /// + /// Mock HTTP response for unit testing. + /// + public class MockHttpResponse : IResponse + { + private readonly HttpStatusCode _statusCode; + private readonly string _responseContent; + private readonly Dictionary _headers; + private readonly string[] _headerNames; + + public MockHttpResponse(int statusCode, string responseContent = null, Dictionary headers = null) + { + _statusCode = (HttpStatusCode)statusCode; + _responseContent = responseContent ?? string.Empty; + _headers = headers ?? new Dictionary(); + _headerNames = new string[_headers.Count]; + _headers.Keys.CopyTo(_headerNames, 0); + } + + public long ContentLength => _responseContent?.Length ?? 0; + + public string ContentType => "application/json"; + + public HttpStatusCode StatusCode => _statusCode; + + public bool IsSuccessStatusCode => (int)_statusCode >= 200 && (int)_statusCode < 300; + + public string GetHeaderValue(string headerName) + { + _headers.TryGetValue(headerName, out string value); + return value ?? string.Empty; + } + + public string[] GetHeaderNames() + { + return _headerNames; + } + + public bool IsHeaderPresent(string headerName) + { + return _headers.ContainsKey(headerName); + } + + public JObject OpenJObjectResponse() + { + if (string.IsNullOrEmpty(_responseContent)) + return new JObject(); + + try + { + return JObject.Parse(_responseContent); + } + catch + { + // Return empty JObject if parsing fails + return new JObject(); + } + } + + public string OpenResponse() + { + return _responseContent; + } + + public TResponse OpenTResponse() + { + if (string.IsNullOrEmpty(_responseContent)) + return default(TResponse); + + try + { + var jObject = OpenJObjectResponse(); + return jObject.ToObject(); + } + catch + { + return default(TResponse); + } + } + } +} \ No newline at end of file diff --git a/Contentstack.Management.Core.Unit.Tests/Queryable/QueryTest.cs b/Contentstack.Management.Core.Unit.Tests/Queryable/QueryTest.cs index 79b4c5a..a31bc6f 100644 --- a/Contentstack.Management.Core.Unit.Tests/Queryable/QueryTest.cs +++ b/Contentstack.Management.Core.Unit.Tests/Queryable/QueryTest.cs @@ -41,6 +41,16 @@ public void Initialize_Query() Assert.ThrowsExceptionAsync(() => query.FindAsync()); } + [TestMethod] + public void Initialize_Query_With_ApiVersion() + { + string apiVersion = "3.2"; + Query query = new Query(new Stack(new ContentstackClient(authtoken: _fixture.Create())), _fixture.Create(), apiVersion); + + Assert.ThrowsException(() => query.Find()); + Assert.ThrowsExceptionAsync(() => query.FindAsync()); + } + [TestMethod] public void Query_Pagination_Parameters() { @@ -49,5 +59,15 @@ public void Query_Pagination_Parameters() query.Skip(10); query.IncludeCount(); } + + [TestMethod] + public void Query_Pagination_Parameters_With_ApiVersion() + { + string apiVersion = "3.2"; + Query query = new Query(_stack, _fixture.Create(), apiVersion); + query.Limit(10); + query.Skip(10); + query.IncludeCount(); + } } } diff --git a/Contentstack.Management.Core.Unit.Tests/Services/Models/GlobalFieldFetchDeleteServiceTest.cs b/Contentstack.Management.Core.Unit.Tests/Services/Models/GlobalFieldFetchDeleteServiceTest.cs new file mode 100644 index 0000000..12c898f --- /dev/null +++ b/Contentstack.Management.Core.Unit.Tests/Services/Models/GlobalFieldFetchDeleteServiceTest.cs @@ -0,0 +1,278 @@ +using System; +using AutoFixture; +using Contentstack.Management.Core.Abstractions; +using Contentstack.Management.Core.Models; +using Contentstack.Management.Core.Services.Models; +using Contentstack.Management.Core.Unit.Tests.Mokes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; + +namespace Contentstack.Management.Core.Unit.Tests.Services.Models +{ + [TestClass] + public class GlobalFieldFetchDeleteServiceTest + { + private Stack _stack; + private readonly IFixture _fixture = new Fixture(); + private ContentstackResponse _contentstackResponse; + private MockHttpHandler _mockHandler; + + [TestInitialize] + public void Initialize() + { + var client = new ContentstackClient(); + _contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + _mockHandler = new MockHttpHandler(_contentstackResponse); + client.ContentstackPipeline.ReplaceHandler(_mockHandler); + client.contentstackOptions.Authtoken = _fixture.Create(); + _stack = new Stack(client, _fixture.Create()); + } + + [TestMethod] + public void Should_Create_GlobalFieldFetchDeleteService_Without_ApiVersion() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + + // Act + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, null); + + // Assert + Assert.IsNotNull(service); + Assert.AreEqual(resourcePath, service.ResourcePath); + } + + [TestMethod] + public void Should_Create_GlobalFieldFetchDeleteService_With_ApiVersion() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + + // Act + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Assert + Assert.IsNotNull(service); + Assert.AreEqual(resourcePath, service.ResourcePath); + } + + [TestMethod] + public void Should_Add_ApiVersion_Header_When_Provided() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + + // Act + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Assert + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + Assert.AreEqual(apiVersion, service.Headers["api_version"]); + } + + [TestMethod] + public void Should_Not_Add_ApiVersion_Header_When_Not_Provided() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + + // Act + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, null); + + // Assert + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Remove_ApiVersion_Header_After_Successful_Response() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Verify header is initially present + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + + // Act - simulate successful response + var mockResponse = new MockHttpResponse(200, "Success"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Remove_ApiVersion_Header_After_Successful_Delete_Response() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Verify header is initially present + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + + // Act - simulate successful delete response (204 No Content) + var mockResponse = new MockHttpResponse(204, "No Content"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Not_Remove_ApiVersion_Header_After_Failed_Response() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Verify header is initially present + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + + // Act - simulate failed response + var mockResponse = new MockHttpResponse(404, "Not Found"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert - header should still be present after failed response + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Not_Remove_ApiVersion_Header_When_No_ApiVersion_Was_Set() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, null); + + // Manually add api_version header (simulating it being added elsewhere) + service.Headers["api_version"] = "3.2"; + + // Act - simulate successful response + var mockResponse = new MockHttpResponse(200, "Success"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert - header should still be present since no apiVersion was passed to constructor + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Handle_Null_Response_Gracefully() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Act & Assert - should not throw exception + service.OnResponse(null, _stack.client.contentstackOptions); + + // Header should still be present since response was null + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Handle_Empty_ApiVersion_String() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = ""; + + // Act + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Assert + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Handle_Whitespace_ApiVersion_String() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = " "; + + // Act + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Assert + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Handle_Different_Success_Status_Codes() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Test with 201 Created + var mockResponse201 = new MockHttpResponse(201, "Created"); + service.OnResponse(mockResponse201, _stack.client.contentstackOptions); + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + + // Reset for next test + service.Headers["api_version"] = apiVersion; + + // Test with 202 Accepted + var mockResponse202 = new MockHttpResponse(202, "Accepted"); + service.OnResponse(mockResponse202, _stack.client.contentstackOptions); + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Not_Remove_ApiVersion_For_Client_Errors() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Test various client error codes + var errorCodes = new[] { 400, 401, 403, 404, 422 }; + + foreach (var errorCode in errorCodes) + { + // Reset header + service.Headers["api_version"] = apiVersion; + + // Act + var mockResponse = new MockHttpResponse(errorCode, $"Error {errorCode}"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert + Assert.IsTrue(service.Headers.ContainsKey("api_version"), $"Header should remain for status code {errorCode}"); + } + } + + [TestMethod] + public void Should_Not_Remove_ApiVersion_For_Server_Errors() + { + // Arrange + var resourcePath = "/global_fields/test_uid"; + var apiVersion = "3.2"; + var service = new GlobalFieldFetchDeleteService(JsonSerializer.CreateDefault(), _stack, resourcePath, apiVersion); + + // Test various server error codes + var errorCodes = new[] { 500, 502, 503, 504 }; + + foreach (var errorCode in errorCodes) + { + // Reset header + service.Headers["api_version"] = apiVersion; + + // Act + var mockResponse = new MockHttpResponse(errorCode, $"Error {errorCode}"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert + Assert.IsTrue(service.Headers.ContainsKey("api_version"), $"Header should remain for status code {errorCode}"); + } + } + } +} \ No newline at end of file diff --git a/Contentstack.Management.Core.Unit.Tests/Services/Models/GlobalFieldServiceTest.cs b/Contentstack.Management.Core.Unit.Tests/Services/Models/GlobalFieldServiceTest.cs new file mode 100644 index 0000000..5d9800a --- /dev/null +++ b/Contentstack.Management.Core.Unit.Tests/Services/Models/GlobalFieldServiceTest.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using AutoFixture; +using Contentstack.Management.Core.Abstractions; +using Contentstack.Management.Core.Models; +using Contentstack.Management.Core.Services.Models; +using Contentstack.Management.Core.Unit.Tests.Mokes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; + +namespace Contentstack.Management.Core.Unit.Tests.Services.Models +{ + [TestClass] + public class GlobalFieldServiceTest + { + private Stack _stack; + private readonly IFixture _fixture = new Fixture(); + private ContentstackResponse _contentstackResponse; + private MockHttpHandler _mockHandler; + + [TestInitialize] + public void Initialize() + { + var client = new ContentstackClient(); + _contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + _mockHandler = new MockHttpHandler(_contentstackResponse); + client.ContentstackPipeline.ReplaceHandler(_mockHandler); + client.contentstackOptions.Authtoken = _fixture.Create(); + _stack = new Stack(client, _fixture.Create()); + } + + [TestMethod] + public void Should_Create_GlobalFieldService_Without_ApiVersion() + { + // Arrange + var model = new ContentModelling { Title = "Test" }; + var uid = _fixture.Create(); + + // Act + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, null); + + // Assert + Assert.IsNotNull(service); + Assert.AreEqual("/global_fields", service.ResourcePath); + } + + [TestMethod] + public void Should_Create_GlobalFieldService_With_ApiVersion() + { + // Arrange + var model = new ContentModelling { Title = "Test" }; + var uid = _fixture.Create(); + var apiVersion = "3.2"; + + // Act + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, apiVersion); + + // Assert + Assert.IsNotNull(service); + Assert.AreEqual("/global_fields", service.ResourcePath); + } + + [TestMethod] + public void Should_Add_ApiVersion_Header_When_Provided() + { + // Arrange + var model = new ContentModelling { Title = "Test" }; + var uid = _fixture.Create(); + var apiVersion = "3.2"; + + // Act + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, apiVersion); + + // Assert + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + Assert.AreEqual(apiVersion, service.Headers["api_version"]); + } + + [TestMethod] + public void Should_Not_Add_ApiVersion_Header_When_Not_Provided() + { + // Arrange + var model = new ContentModelling { Title = "Test" }; + var uid = _fixture.Create(); + + // Act + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, null); + + // Assert + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Remove_ApiVersion_Header_After_Successful_Response() + { + // Arrange + var model = new ContentModelling { Title = "Test" }; + var uid = _fixture.Create(); + var apiVersion = "3.2"; + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, apiVersion); + + // Verify header is initially present + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + + // Act - simulate successful response + var mockResponse = new MockHttpResponse(200, "Success"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert + Assert.IsFalse(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Not_Remove_ApiVersion_Header_After_Failed_Response() + { + // Arrange + var model = new ContentModelling { Title = "Test" }; + var uid = _fixture.Create(); + var apiVersion = "3.2"; + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, apiVersion); + + // Verify header is initially present + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + + // Act - simulate failed response + var mockResponse = new MockHttpResponse(400, "Bad Request"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert - header should still be present after failed response + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Not_Remove_ApiVersion_Header_When_No_ApiVersion_Was_Set() + { + // Arrange + var model = new ContentModelling { Title = "Test" }; + var uid = _fixture.Create(); + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, null); + + // Manually add api_version header (simulating it being added elsewhere) + service.Headers["api_version"] = "3.2"; + + // Act - simulate successful response + var mockResponse = new MockHttpResponse(200, "Success"); + service.OnResponse(mockResponse, _stack.client.contentstackOptions); + + // Assert - header should still be present since no apiVersion was passed to constructor + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Handle_Null_Response_Gracefully() + { + // Arrange + var model = new ContentModelling { Title = "Test" }; + var uid = _fixture.Create(); + var apiVersion = "3.2"; + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, apiVersion); + + // Act & Assert - should not throw exception + service.OnResponse(null, _stack.client.contentstackOptions); + + // Header should still be present since response was null + Assert.IsTrue(service.Headers.ContainsKey("api_version")); + } + + [TestMethod] + public void Should_Create_Content_Body_Correctly() + { + // Arrange + var model = new ContentModelling { Title = "Test Global Field" }; + var uid = _fixture.Create(); + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", model, uid, null); + + // Act + service.ContentBody(); + + // Assert + Assert.IsNotNull(service.ByteContent); + var content = System.Text.Encoding.UTF8.GetString(service.ByteContent); + Assert.IsTrue(content.Contains("Test Global Field")); + } + + [TestMethod] + public void Should_Handle_Null_Model_In_ContentBody() + { + // Arrange + var uid = _fixture.Create(); + var service = new GlobalFieldService(JsonSerializer.CreateDefault(), _stack, "/global_fields", null, uid, null); + + // Act + service.ContentBody(); + + // Assert + Assert.IsNull(service.ByteContent); + } + } +} \ No newline at end of file diff --git a/Contentstack.Management.Core/ContentstackResponse.cs b/Contentstack.Management.Core/ContentstackResponse.cs index 95c3bac..0423f2a 100644 --- a/Contentstack.Management.Core/ContentstackResponse.cs +++ b/Contentstack.Management.Core/ContentstackResponse.cs @@ -140,6 +140,7 @@ internal ContentstackResponse(HttpResponseMessage response, JsonSerializer seria this.ContentType = response.Content.Headers.ContentType.MediaType; } CopyHeaderValues(response); + } /// diff --git a/Contentstack.Management.Core/Models/BulkOperation.cs b/Contentstack.Management.Core/Models/BulkOperation.cs index cf192cc..a66605a 100644 --- a/Contentstack.Management.Core/Models/BulkOperation.cs +++ b/Contentstack.Management.Core/Models/BulkOperation.cs @@ -236,9 +236,11 @@ public Task UpdateAsync(BulkWorkflowUpdateBody updateBody) } /// - /// Adds multiple items to a release in bulk. + /// Adds multiple items to a release in bulk with enhanced capabilities. + /// Automatically detects whether to perform simple add or deployment operation based on data properties. + /// When Release property is set in data, performs deployment operation (like JavaScript SDK). /// - /// The data containing items to be added to the release. + /// The data containing items. If Release property is set, performs deployment operation. /// The bulk version. /// The /// @@ -246,18 +248,39 @@ public Task UpdateAsync(BulkWorkflowUpdateBody updateBody) /// ContentstackClient client = new ContentstackClient("", ""); /// Stack stack = client.Stack(""); /// - /// var itemsData = new BulkAddItemsData + /// // Simple add mode + /// var simpleData = new BulkAddItemsData /// { /// Items = new List /// { /// new BulkAddItem { Uid = "entry_uid", ContentType = "content_type_uid" } /// } /// }; + /// ContentstackResponse response = stack.BulkOperation().AddItems(simpleData); /// - /// ContentstackResponse response = stack.BulkOperation().AddItems(itemsData, "1.0"); + /// // Deployment mode (like JavaScript SDK) + /// var deployData = new BulkAddItemsData + /// { + /// Release = "release_uid", + /// Action = "publish", + /// Locale = new List { "en-us" }, + /// Reference = true, + /// Items = new List + /// { + /// new BulkAddItem + /// { + /// Uid = "entry_uid", + /// ContentTypeUid = "content_type_uid", + /// Version = 1, + /// Locale = "en-us", + /// Title = "My Entry" + /// } + /// } + /// }; + /// ContentstackResponse response = stack.BulkOperation().AddItems(deployData, "2.0"); /// /// - public ContentstackResponse AddItems(BulkAddItemsData data, string bulkVersion = null) + public ContentstackResponse AddItems(BulkAddItemsData data, string bulkVersion = "1.0") { _stack.ThrowIfNotLoggedIn(); _stack.ThrowIfAPIKeyEmpty(); @@ -267,12 +290,63 @@ public ContentstackResponse AddItems(BulkAddItemsData data, string bulkVersion = } /// - /// Adds multiple items to a release in bulk asynchronously. + /// Adds multiple items to a release in bulk with enhanced deployment capabilities. + /// Supports both simple adding to release and complex release deployment operations (like JavaScript SDK). /// - /// The data containing items to be added to the release. + /// The data containing items and optional deployment configuration. + /// The release UID for deployment operations. If specified, enables deployment mode. + /// The action to perform (publish, unpublish, etc.). Required when releaseUid is specified. + /// The list of locales for deployment. Only used when releaseUid is specified. + /// Whether to include references. Only used when releaseUid is specified. + /// The bulk version. + /// The + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// Stack stack = client.Stack("");
+        /// 
+        /// // Enhanced deployment mode
+        /// var deployData = new BulkAddItemsData
+        /// {
+        ///     Items = new List
+        ///     {
+        ///         new BulkAddItem 
+        ///         { 
+        ///             Uid = "entry_uid", 
+        ///             ContentTypeUid = "content_type_uid",
+        ///             Version = 1,
+        ///             Locale = "en-us",
+        ///             Title = "My Entry"
+        ///         }
+        ///     }
+        /// };
+        /// ContentstackResponse response = stack.BulkOperation().AddItemsWithDeployment(deployData, "release_uid", "publish", new List { "en-us" }, true, "2.0");
+        /// 
+ ///
+ public ContentstackResponse AddItemsWithDeployment(BulkAddItemsData data, string releaseUid, string action, List locales = null, bool? reference = null, string bulkVersion = null) + { + _stack.ThrowIfNotLoggedIn(); + _stack.ThrowIfAPIKeyEmpty(); + + // Configure the data object for deployment + data.Release = releaseUid; + data.Action = action; + data.Locale = locales; + data.Reference = reference; + + var service = new BulkAddItemsService(_stack.client.serializer, _stack, data, bulkVersion); + return _stack.client.InvokeSync(service); + } + + /// + /// Adds multiple items to a release in bulk asynchronously with enhanced capabilities. + /// Automatically detects whether to perform simple add or deployment operation based on data properties. + /// When Release property is set in data, performs deployment operation (like JavaScript SDK). + /// + /// The data containing items. If Release property is set, performs deployment operation. /// The bulk version. /// The Task - public Task AddItemsAsync(BulkAddItemsData data, string bulkVersion = null) + public Task AddItemsAsync(BulkAddItemsData data, string bulkVersion = "1.0") { _stack.ThrowIfNotLoggedIn(); _stack.ThrowIfAPIKeyEmpty(); @@ -282,31 +356,175 @@ public Task AddItemsAsync(BulkAddItemsData data, string bu } /// - /// Updates multiple items in a release in bulk. + /// Adds multiple items to a release in bulk asynchronously with enhanced deployment capabilities. + /// Supports both simple adding to release and complex release deployment operations (like JavaScript SDK). /// - /// The data containing items to be updated in the release. + /// The data containing items and optional deployment configuration. + /// The release UID for deployment operations. Required for deployment mode. + /// The action to perform (publish, unpublish, etc.). Required when releaseUid is specified. + /// The list of locales for deployment. Only used when releaseUid is specified. + /// Whether to include references. Only used when releaseUid is specified. + /// The bulk version. + /// The Task + public Task AddItemsWithDeploymentAsync(BulkAddItemsData data, string releaseUid, string action, List locales = null, bool? reference = null, string bulkVersion = null) + { + _stack.ThrowIfNotLoggedIn(); + _stack.ThrowIfAPIKeyEmpty(); + + // Configure the data object for deployment + data.Release = releaseUid; + data.Action = action; + data.Locale = locales; + data.Reference = reference; + + var service = new BulkAddItemsService(_stack.client.serializer, _stack, data, bulkVersion); + return _stack.client.InvokeAsync(service); + } + + + + /// + /// Updates multiple items in a release in bulk with enhanced capabilities. + /// Automatically detects whether to perform simple update or deployment operation based on data properties. + /// When Release property is set in data, performs deployment operation (like JavaScript SDK). + /// + /// The data containing items. If Release property is set, performs deployment operation. /// The bulk version. /// The - public ContentstackResponse UpdateItems(BulkAddItemsData data, string bulkVersion = null) + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// Stack stack = client.Stack("");
+        /// 
+        /// // Simple update mode
+        /// var simpleData = new BulkAddItemsData
+        /// {
+        ///     Items = new List
+        ///     {
+        ///         new BulkAddItem { Uid = "entry_uid", ContentType = "content_type_uid" }
+        ///     }
+        /// };
+        /// ContentstackResponse response = stack.BulkOperation().UpdateItems(simpleData);
+        /// 
+        /// // Deployment mode (like JavaScript SDK)
+        /// var deployData = new BulkAddItemsData
+        /// {
+        ///     Release = "release_uid",
+        ///     Action = "unpublish",
+        ///     Locale = new List { "en-us" },
+        ///     Reference = false,
+        ///     Items = new List
+        ///     {
+        ///         new BulkAddItem 
+        ///         { 
+        ///             Uid = "entry_uid", 
+        ///             ContentTypeUid = "content_type_uid",
+        ///             Version = 1,
+        ///             Locale = "en-us",
+        ///             Title = "My Entry"
+        ///         }
+        ///     }
+        /// };
+        /// ContentstackResponse response = stack.BulkOperation().UpdateItems(deployData, "2.0");
+        /// 
+ ///
+ public ContentstackResponse UpdateItems(BulkAddItemsData data, string bulkVersion = "1.0") + { + _stack.ThrowIfNotLoggedIn(); + _stack.ThrowIfAPIKeyEmpty(); + + var service = new BulkUpdateItemsService(_stack.client.serializer, _stack, data, bulkVersion); + return _stack.client.InvokeSync(service); + } + + /// + /// Updates multiple items in a release in bulk with enhanced deployment capabilities. + /// Supports both simple updating in release and complex release deployment operations (like JavaScript SDK). + /// + /// The data containing items and optional deployment configuration. + /// The release UID for deployment operations. Required for deployment mode. + /// The action to perform (publish, unpublish, etc.). Required when releaseUid is specified. + /// The list of locales for deployment. Only used when releaseUid is specified. + /// Whether to include references. Only used when releaseUid is specified. + /// The bulk version. + /// The + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// Stack stack = client.Stack("");
+        /// 
+        /// // Enhanced deployment mode
+        /// var deployData = new BulkAddItemsData
+        /// {
+        ///     Items = new List
+        ///     {
+        ///         new BulkAddItem 
+        ///         { 
+        ///             Uid = "entry_uid", 
+        ///             ContentTypeUid = "content_type_uid",
+        ///             Version = 2,
+        ///             Locale = "en-us",
+        ///             Title = "Updated Entry"
+        ///         }
+        ///     }
+        /// };
+        /// ContentstackResponse response = stack.BulkOperation().UpdateItemsWithDeployment(deployData, "release_uid", "publish", new List { "en-us" }, true, "2.0");
+        /// 
+ ///
+ public ContentstackResponse UpdateItemsWithDeployment(BulkAddItemsData data, string releaseUid, string action, List locales = null, bool? reference = null, string bulkVersion = null) { _stack.ThrowIfNotLoggedIn(); _stack.ThrowIfAPIKeyEmpty(); + // Configure the data object for deployment + data.Release = releaseUid; + data.Action = action; + data.Locale = locales; + data.Reference = reference; + var service = new BulkUpdateItemsService(_stack.client.serializer, _stack, data, bulkVersion); return _stack.client.InvokeSync(service); } /// - /// Updates multiple items in a release in bulk asynchronously. + /// Updates multiple items in a release in bulk asynchronously with enhanced capabilities. + /// Automatically detects whether to perform simple update or deployment operation based on data properties. + /// When Release property is set in data, performs deployment operation (like JavaScript SDK). + /// + /// The data containing items. If Release property is set, performs deployment operation. + /// The bulk version. + /// The Task + public Task UpdateItemsAsync(BulkAddItemsData data, string bulkVersion = "1.0") + { + _stack.ThrowIfNotLoggedIn(); + _stack.ThrowIfAPIKeyEmpty(); + + var service = new BulkUpdateItemsService(_stack.client.serializer, _stack, data, bulkVersion); + return _stack.client.InvokeAsync(service); + } + + /// + /// Updates multiple items in a release in bulk asynchronously with enhanced deployment capabilities. + /// Supports both simple updating in release and complex release deployment operations (like JavaScript SDK). /// - /// The data containing items to be updated in the release. + /// The data containing items and optional deployment configuration. + /// The release UID for deployment operations. Required for deployment mode. + /// The action to perform (publish, unpublish, etc.). Required when releaseUid is specified. + /// The list of locales for deployment. Only used when releaseUid is specified. + /// Whether to include references. Only used when releaseUid is specified. /// The bulk version. /// The Task - public Task UpdateItemsAsync(BulkAddItemsData data, string bulkVersion = null) + public Task UpdateItemsWithDeploymentAsync(BulkAddItemsData data, string releaseUid, string action, List locales = null, bool? reference = null, string bulkVersion = null) { _stack.ThrowIfNotLoggedIn(); _stack.ThrowIfAPIKeyEmpty(); + // Configure the data object for deployment + data.Release = releaseUid; + data.Action = action; + data.Locale = locales; + data.Reference = reference; + var service = new BulkUpdateItemsService(_stack.client.serializer, _stack, data, bulkVersion); return _stack.client.InvokeAsync(service); } diff --git a/Contentstack.Management.Core/Models/BulkOperationModels.cs b/Contentstack.Management.Core/Models/BulkOperationModels.cs index b2f5faa..5b14e62 100644 --- a/Contentstack.Management.Core/Models/BulkOperationModels.cs +++ b/Contentstack.Management.Core/Models/BulkOperationModels.cs @@ -350,6 +350,7 @@ public class BulkWorkflowRole /// /// Represents data for bulk add/update items operations. + /// Enhanced to support both simple adding to release and complex release deployment operations. /// public class BulkAddItemsData { @@ -359,6 +360,34 @@ public class BulkAddItemsData [JsonProperty(propertyName: "items")] public List Items { get; set; } + /// + /// Gets or sets the release UID for deployment operations. + /// When specified, this enables release deployment mode (like JavaScript SDK). + /// + [JsonProperty(propertyName: "release")] + public string Release { get; set; } + + /// + /// Gets or sets the action to perform during deployment (publish, unpublish, etc.). + /// Only used when Release is specified. + /// + [JsonProperty(propertyName: "action")] + public string Action { get; set; } + + /// + /// Gets or sets the list of locales for deployment. + /// Only used when Release is specified. + /// + [JsonProperty(propertyName: "locale")] + public List Locale { get; set; } + + /// + /// Gets or sets the reference flag for deployment. + /// Only used when Release is specified. + /// + [JsonProperty(propertyName: "reference")] + public bool? Reference { get; set; } + /// /// Determines whether to serialize the Items property. /// @@ -367,10 +396,56 @@ public bool ShouldSerializeItems() { return Items != null && Items.Count > 0; } + + /// + /// Determines whether to serialize the Release property. + /// + /// True if Release should be serialized, false otherwise. + public bool ShouldSerializeRelease() + { + return !string.IsNullOrEmpty(Release); + } + + /// + /// Determines whether to serialize the Action property. + /// + /// True if Action should be serialized, false otherwise. + public bool ShouldSerializeAction() + { + return !string.IsNullOrEmpty(Action); + } + + /// + /// Determines whether to serialize the Locale property. + /// + /// True if Locale should be serialized, false otherwise. + public bool ShouldSerializeLocale() + { + return Locale != null && Locale.Count > 0; + } + + /// + /// Determines whether to serialize the Reference property. + /// + /// True if Reference should be serialized, false otherwise. + public bool ShouldSerializeReference() + { + return Reference.HasValue; + } + + /// + /// Gets a value indicating whether this instance is configured for release deployment mode. + /// + /// True if this is a release deployment operation, false if simple add operation. + public bool IsReleaseDeploymentMode() + { + return !string.IsNullOrEmpty(Release) && !string.IsNullOrEmpty(Action); + } } /// /// Represents an item for bulk add/update operations. + /// Enhanced to support both simple and complex release deployment properties. /// public class BulkAddItem { @@ -385,6 +460,70 @@ public class BulkAddItem ///
[JsonProperty(propertyName: "content_type")] public string ContentType { get; set; } + + /// + /// Gets or sets the content type UID for release deployment mode. + /// This is an alias for ContentType with a different JSON property name. + /// + [JsonProperty(propertyName: "content_type_uid")] + public string ContentTypeUid { get; set; } + + /// + /// Gets or sets the version number for release deployment mode. + /// Only used in enhanced release deployment operations. + /// + [JsonProperty(propertyName: "version")] + public int? Version { get; set; } + + /// + /// Gets or sets the locale for release deployment mode. + /// Only used in enhanced release deployment operations. + /// + [JsonProperty(propertyName: "locale")] + public string Locale { get; set; } + + /// + /// Gets or sets the title for release deployment mode. + /// Only used in enhanced release deployment operations. + /// + [JsonProperty(propertyName: "title")] + public string Title { get; set; } + + /// + /// Determines whether to serialize the ContentTypeUid property. + /// + /// True if ContentTypeUid should be serialized, false otherwise. + public bool ShouldSerializeContentTypeUid() + { + return !string.IsNullOrEmpty(ContentTypeUid); + } + + /// + /// Determines whether to serialize the Version property. + /// + /// True if Version should be serialized, false otherwise. + public bool ShouldSerializeVersion() + { + return Version.HasValue; + } + + /// + /// Determines whether to serialize the Locale property. + /// + /// True if Locale should be serialized, false otherwise. + public bool ShouldSerializeLocale() + { + return !string.IsNullOrEmpty(Locale); + } + + /// + /// Determines whether to serialize the Title property. + /// + /// True if Title should be serialized, false otherwise. + public bool ShouldSerializeTitle() + { + return !string.IsNullOrEmpty(Title); + } } /// diff --git a/Contentstack.Management.Core/Models/ContentModelling.cs b/Contentstack.Management.Core/Models/ContentModelling.cs index 90a9700..a43eac7 100644 --- a/Contentstack.Management.Core/Models/ContentModelling.cs +++ b/Contentstack.Management.Core/Models/ContentModelling.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Contentstack.Management.Core.Models.Fields; using Newtonsoft.Json; diff --git a/Contentstack.Management.Core/Models/Fields/Field.cs b/Contentstack.Management.Core/Models/Fields/Field.cs index 623da9a..02a603c 100644 --- a/Contentstack.Management.Core/Models/Fields/Field.cs +++ b/Contentstack.Management.Core/Models/Fields/Field.cs @@ -22,7 +22,7 @@ public class Field [JsonProperty(propertyName: "data_type")] public string DataType { get; set; } /// - /// Allows you to enter additional data about a field. Also, you can add additional values under ‘field_metadata’. + /// Allows you to enter additional data about a field. Also, you can add additional values under 'field_metadata'. /// [JsonProperty(propertyName: "field_metadata")] public FieldMetadata FieldMetadata { get; set; } diff --git a/Contentstack.Management.Core/Models/Fields/GlobalFieldReference.cs b/Contentstack.Management.Core/Models/Fields/GlobalFieldReference.cs index 0e5b9b6..05e7e49 100644 --- a/Contentstack.Management.Core/Models/Fields/GlobalFieldReference.cs +++ b/Contentstack.Management.Core/Models/Fields/GlobalFieldReference.cs @@ -32,11 +32,10 @@ public class GlobalFieldReference : Field /// [JsonProperty(propertyName: "unique")] public new bool Unique { get; set; } - /// /// Determines if this field is non-localizable. /// [JsonProperty(propertyName: "non_localizable")] public bool NonLocalizable { get; set; } } -} \ No newline at end of file +} diff --git a/Contentstack.Management.Core/Models/GlobalField.cs b/Contentstack.Management.Core/Models/GlobalField.cs index f23a8a9..e70f009 100644 --- a/Contentstack.Management.Core/Models/GlobalField.cs +++ b/Contentstack.Management.Core/Models/GlobalField.cs @@ -1,14 +1,18 @@ using System.Threading.Tasks; using Contentstack.Management.Core.Queryable; +using Contentstack.Management.Core.Services.Models; namespace Contentstack.Management.Core.Models { public class GlobalField : BaseModel { - internal GlobalField(Stack stack, string uid = null) + private readonly string apiVersion; + + internal GlobalField(Stack stack, string uid = null, string apiVersion = null) : base(stack, "global_field", uid) { resourcePath = uid == null ? "/global_fields" : $"/global_fields/{uid}"; + this.apiVersion = apiVersion; } /// @@ -24,7 +28,7 @@ internal GlobalField(Stack stack, string uid = null) public Query Query() { ThrowIfUidNotEmpty(); - return new Query(stack, resourcePath); + return new Query(stack, resourcePath, apiVersion); } /// @@ -41,7 +45,9 @@ public Query Query() /// The . public override ContentstackResponse Create(ContentModelling model, ParameterCollection collection = null) { - return base.Create(model, collection); + ThrowIfUidNotEmpty(); + var service = new GlobalFieldService(stack.client.serializer, stack, resourcePath, model, this.fieldName, apiVersion, collection: collection); + return stack.client.InvokeSync(service, apiVersion: apiVersion); } /// @@ -58,7 +64,10 @@ public override ContentstackResponse Create(ContentModelling model, ParameterCol /// The Task. public override Task CreateAsync(ContentModelling model, ParameterCollection collection = null) { - return base.CreateAsync(model, collection); + ThrowIfUidNotEmpty(); + stack.ThrowIfNotLoggedIn(); + var service = new GlobalFieldService(stack.client.serializer, stack, resourcePath, model, this.fieldName, apiVersion, collection: collection); + return stack.client.InvokeAsync(service, apiVersion: apiVersion); } /// @@ -75,7 +84,9 @@ public override Task CreateAsync(ContentModelling model, P /// The . public override ContentstackResponse Update(ContentModelling model, ParameterCollection collection = null) { - return base.Update(model, collection); + ThrowIfUidEmpty(); + var service = new GlobalFieldService(stack.client.serializer, stack, resourcePath, model, this.fieldName, apiVersion, "PUT", collection: collection); + return stack.client.InvokeSync(service, apiVersion: apiVersion); } /// @@ -92,7 +103,10 @@ public override ContentstackResponse Update(ContentModelling model, ParameterCol /// The Task. public override Task UpdateAsync(ContentModelling model, ParameterCollection collection = null) { - return base.UpdateAsync(model, collection); + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + var service = new GlobalFieldService(stack.client.serializer, stack, resourcePath, model, this.fieldName, apiVersion, "PUT", collection: collection); + return stack.client.InvokeAsync(service, apiVersion: apiVersion); } /// @@ -107,7 +121,10 @@ public override Task UpdateAsync(ContentModelling model, P /// The . public override ContentstackResponse Fetch(ParameterCollection collection = null) { - return base.Fetch(collection); + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + var service = new GlobalFieldFetchDeleteService(stack.client.serializer, stack, resourcePath, apiVersion, collection: collection); + return stack.client.InvokeSync(service, apiVersion: apiVersion); } /// @@ -122,7 +139,10 @@ public override ContentstackResponse Fetch(ParameterCollection collection = null /// The Task. public override Task FetchAsync(ParameterCollection collection = null) { - return base.FetchAsync(collection); + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + var service = new GlobalFieldFetchDeleteService(stack.client.serializer, stack, resourcePath, apiVersion, collection: collection); + return stack.client.InvokeAsync(service, apiVersion: apiVersion); } /// @@ -137,7 +157,10 @@ public override Task FetchAsync(ParameterCollection collec /// The . public override ContentstackResponse Delete(ParameterCollection collection = null) { - return base.Delete(collection); + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + var service = new GlobalFieldFetchDeleteService(stack.client.serializer, stack, resourcePath, apiVersion, "DELETE", collection: collection); + return stack.client.InvokeSync(service, apiVersion: apiVersion); } /// @@ -152,7 +175,10 @@ public override ContentstackResponse Delete(ParameterCollection collection = nul /// The Task. public override Task DeleteAsync(ParameterCollection collection = null) { - return base.DeleteAsync(collection); + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + var service = new GlobalFieldFetchDeleteService(stack.client.serializer, stack, resourcePath, apiVersion, "DELETE", collection: collection); + return stack.client.InvokeAsync(service, apiVersion: apiVersion); } } } diff --git a/Contentstack.Management.Core/Models/Stack.cs b/Contentstack.Management.Core/Models/Stack.cs index 8695e05..d876e4a 100644 --- a/Contentstack.Management.Core/Models/Stack.cs +++ b/Contentstack.Management.Core/Models/Stack.cs @@ -643,24 +643,23 @@ public Asset Asset(string uid = null) } /// - /// A is a reusable field (or group of fields) that you can define once and reuse in any content type within your stack. - /// This eliminates the need (and thereby time and efforts) to create the same set of fields repeatedly in multiple content types. + /// defines the structure or schema of a page or a section of your web or mobile property. To create global Fields for your application, you are required to first create a global field. Read more about Global Fields. /// - /// Optional global field uid. + /// Optional, global field uid. + /// Optional, API version for nested global field support. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
-        /// Stack stack = client.Stack("");
-        /// ContentstackResponse contentstackResponse = stack.GlobalField("").Fetch();
+        /// ContentstackResponse contentstackResponse = client.Stack("").GlobalField("").Fetch();
         /// 
///
- /// The - public GlobalField GlobalField(string uid = null) + /// The + public GlobalField GlobalField(string uid = null, string apiVersion = null) { ThrowIfNotLoggedIn(); ThrowIfAPIKeyEmpty(); - return new GlobalField(this, uid); + return new GlobalField(this, uid, apiVersion); } /// diff --git a/Contentstack.Management.Core/Queryable/Query.cs b/Contentstack.Management.Core/Queryable/Query.cs index 56ab212..eb8b6fe 100644 --- a/Contentstack.Management.Core/Queryable/Query.cs +++ b/Contentstack.Management.Core/Queryable/Query.cs @@ -11,7 +11,9 @@ public class Query private readonly Stack _stack; private readonly string _resourcePath; private readonly ParameterCollection _collection = new ParameterCollection(); - internal Query(Stack stack, string resourcePath) + private readonly string _apiVersion; + + internal Query(Stack stack, string resourcePath, string apiVersion = null) { if(stack == null) { @@ -24,6 +26,7 @@ internal Query(Stack stack, string resourcePath) } _stack = stack; _resourcePath = resourcePath; + _apiVersion = apiVersion; } #region Public /// @@ -93,7 +96,7 @@ public ContentstackResponse Find(ParameterCollection collection = null) } var service = new QueryService(_stack, _collection, _resourcePath); - return _stack.client.InvokeSync(service); + return _stack.client.InvokeSync(service, false, _apiVersion); } /// @@ -113,7 +116,7 @@ public Task FindAsync(ParameterCollection collection = nul } var service = new QueryService(_stack, _collection, _resourcePath); - return _stack.client.InvokeAsync(service); + return _stack.client.InvokeAsync(service, false, _apiVersion); } #endregion #region Throw Error diff --git a/Contentstack.Management.Core/Services/ContentstackService.cs b/Contentstack.Management.Core/Services/ContentstackService.cs index bcf2a34..771b7f1 100644 --- a/Contentstack.Management.Core/Services/ContentstackService.cs +++ b/Contentstack.Management.Core/Services/ContentstackService.cs @@ -199,7 +199,10 @@ internal void WriteContentByte() } } - public virtual void OnResponse(IResponse httpResponse, ContentstackClientOptions config) { } + public virtual void OnResponse(IResponse httpResponse, ContentstackClientOptions config) + { + // Base implementation - no general cleanup + } #region Dispose methods /// diff --git a/Contentstack.Management.Core/Services/Models/GlobalFieldFetchDeleteService.cs b/Contentstack.Management.Core/Services/Models/GlobalFieldFetchDeleteService.cs new file mode 100644 index 0000000..576a5e8 --- /dev/null +++ b/Contentstack.Management.Core/Services/Models/GlobalFieldFetchDeleteService.cs @@ -0,0 +1,64 @@ +using System; +using Contentstack.Management.Core.Queryable; +using Newtonsoft.Json; + +namespace Contentstack.Management.Core.Services.Models +{ + /// + /// Specialized service for GlobalField Fetch/Delete operations that handles api_version header cleanup. + /// + internal class GlobalFieldFetchDeleteService : ContentstackService + { + private readonly string _apiVersion; + + /// + /// Initializes a new instance of the class. + /// + internal GlobalFieldFetchDeleteService(JsonSerializer serializer, Core.Models.Stack stack, string resourcePath, string apiVersion, string httpMethod = "GET", ParameterCollection collection = null) + : base(serializer, stack: stack, collection) + { + if (stack.APIKey == null) + { + throw new ArgumentNullException("stack", "Should have API Key to perform this operation."); + } + if (resourcePath == null) + { + throw new ArgumentNullException("resourcePath", "Should resource path for service."); + } + + this.ResourcePath = resourcePath; + this.HttpMethod = httpMethod; + this._apiVersion = apiVersion; + + // Set api_version header if provided + if (!string.IsNullOrEmpty(apiVersion)) + { + this.Headers["api_version"] = apiVersion; + } + + if (collection != null && collection.Count > 0) + { + this.UseQueryString = true; + } + } + + /// + /// Handles response processing with api_version header cleanup for GlobalField operations. + /// This matches the JavaScript SDK parseData function where api_version is removed from stackHeaders. + /// + public override void OnResponse(IResponse httpResponse, ContentstackClientOptions config) + { + base.OnResponse(httpResponse, config); + + // Clean up api_version header after successful GlobalField operation + // (matching JavaScript SDK parseData function behavior) + if (httpResponse != null && httpResponse.IsSuccessStatusCode && !string.IsNullOrEmpty(_apiVersion)) + { + if (Headers.ContainsKey("api_version")) + { + Headers.Remove("api_version"); + } + } + } + } +} \ No newline at end of file diff --git a/Contentstack.Management.Core/Services/Models/GlobalFieldService.cs b/Contentstack.Management.Core/Services/Models/GlobalFieldService.cs new file mode 100644 index 0000000..b4471c2 --- /dev/null +++ b/Contentstack.Management.Core/Services/Models/GlobalFieldService.cs @@ -0,0 +1,91 @@ +using System; +using System.Globalization; +using System.IO; +using Contentstack.Management.Core.Models; +using Contentstack.Management.Core.Queryable; +using Newtonsoft.Json; + +namespace Contentstack.Management.Core.Services.Models +{ + /// + /// Specialized service for GlobalField Create/Update operations that handles api_version header cleanup. + /// + internal class GlobalFieldService : ContentstackService + { + private readonly ContentModelling _typedModel; + private readonly string fieldName; + private readonly string _apiVersion; + + /// + /// Initializes a new instance of the class. + /// + internal GlobalFieldService(JsonSerializer serializer, Core.Models.Stack stack, string resourcePath, ContentModelling dataModel, string fieldName, string apiVersion, string httpMethod = "POST", ParameterCollection collection = null) + : base(serializer, stack: stack, collection) + { + if (stack.APIKey == null) + { + throw new ArgumentNullException("stack", "Should have API Key to perform this operation."); + } + if (resourcePath == null) + { + throw new ArgumentNullException("resourcePath", "Should resource path for service."); + } + if (dataModel == null) + { + throw new ArgumentNullException("dataModel", "Data model is mandatory for service"); + } + if (fieldName == null) + { + throw new ArgumentNullException("fieldName", "Name mandatory for service"); + } + + this.ResourcePath = resourcePath; + this.HttpMethod = httpMethod; + this.fieldName = fieldName; + this._apiVersion = apiVersion; + + // Set api_version header if provided + if (!string.IsNullOrEmpty(apiVersion)) + { + this.Headers["api_version"] = apiVersion; + } + + if (collection != null && collection.Count > 0) + { + this.UseQueryString = true; + } + _typedModel = dataModel; + } + + public override void ContentBody() + { + using (StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture)) + { + JsonWriter writer = new JsonTextWriter(stringWriter); + + Serializer.Serialize(writer, _typedModel); + string snippet = $"{{\"{fieldName}\": {stringWriter.ToString()}}}"; + this.ByteContent = System.Text.Encoding.UTF8.GetBytes(snippet); + } + } + + /// + /// Handles response processing with api_version header cleanup for GlobalField operations. + /// This matches the JavaScript SDK parseData function where api_version is removed from stackHeaders. + /// + public override void OnResponse(IResponse httpResponse, ContentstackClientOptions config) + { + base.OnResponse(httpResponse, config); + + // Clean up api_version header after successful GlobalField operation + // (matching JavaScript SDK parseData function behavior) + if (httpResponse != null && httpResponse.IsSuccessStatusCode && !string.IsNullOrEmpty(_apiVersion)) + { + if (Headers.ContainsKey("api_version")) + { + Headers.Remove("api_version"); + } + } + } + } +} \ No newline at end of file