From 5765b065b55bf27f467afe622390c358a306dd6b Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Tue, 16 Sep 2025 12:10:06 +0530 Subject: [PATCH 1/4] Add Variants and VariantGroup models with methods --- Contentstack.Management.Core/Models/Stack.cs | 61 ++++++++- .../Models/VariantGroup.cs | 12 ++ .../Models/Variants.cs | 125 ++++++++++++++++++ 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 Contentstack.Management.Core/Models/VariantGroup.cs create mode 100644 Contentstack.Management.Core/Models/Variants.cs diff --git a/Contentstack.Management.Core/Models/Stack.cs b/Contentstack.Management.Core/Models/Stack.cs index d876e4a..108a977 100644 --- a/Contentstack.Management.Core/Models/Stack.cs +++ b/Contentstack.Management.Core/Models/Stack.cs @@ -4,7 +4,6 @@ using Contentstack.Management.Core.Queryable; using Contentstack.Management.Core.Services.Stack; using Contentstack.Management.Core.Utils; -using Contentstack.Management.Core.Models; using Contentstack.Management.Core.Models.Token; namespace Contentstack.Management.Core.Models @@ -912,6 +911,66 @@ public BulkOperation BulkOperation() return new BulkOperation(this); } + + /// + /// Gets the bulk operation instance for performing bulk operations on entries and assets. + /// + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// Stack stack = client.Stack("");
+        /// 
+        /// var publishDetails = new BulkPublishDetails
+        /// {
+        ///     Entries = new List
+        ///     {
+        ///         new BulkPublishEntry { Uid = "entry_uid", ContentTypeUid = "content_type_uid", Locale = "en-us" }
+        ///     },
+        ///     Locales = new List { "en-us" },
+        ///     Environments = new List { "environment_uid" }
+        /// };
+        /// 
+        /// ContentstackResponse response = stack.BulkOperation().Publish(publishDetails);
+        /// 
+ ///
+ /// The + public Variants Variants(string uid = null) + { + ThrowIfNotLoggedIn(); + ThrowIfAPIKeyEmpty(); + + return new Variants(this, uid); + } + + /// + /// Gets the bulk operation instance for performing bulk operations on entries and assets. + /// + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// Stack stack = client.Stack("");
+        /// 
+        /// var publishDetails = new BulkPublishDetails
+        /// {
+        ///     Entries = new List
+        ///     {
+        ///         new BulkPublishEntry { Uid = "entry_uid", ContentTypeUid = "content_type_uid", Locale = "en-us" }
+        ///     },
+        ///     Locales = new List { "en-us" },
+        ///     Environments = new List { "environment_uid" }
+        /// };
+        /// 
+        /// ContentstackResponse response = stack.BulkOperation().Publish(publishDetails);
+        /// 
+ ///
+ /// The + public VariantGroup VariantGroup(string uid = null) + { + ThrowIfNotLoggedIn(); + ThrowIfAPIKeyEmpty(); + + return new VariantGroup(this, uid); + } #endregion #region Throw Error diff --git a/Contentstack.Management.Core/Models/VariantGroup.cs b/Contentstack.Management.Core/Models/VariantGroup.cs new file mode 100644 index 0000000..416d945 --- /dev/null +++ b/Contentstack.Management.Core/Models/VariantGroup.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; +using Contentstack.Management.Core.Queryable; +using Contentstack.Management.Core.Services.Models; + +namespace Contentstack.Management.Core.Models +{ + public class VariantGroup + { + + } +} diff --git a/Contentstack.Management.Core/Models/Variants.cs b/Contentstack.Management.Core/Models/Variants.cs new file mode 100644 index 0000000..b7792f3 --- /dev/null +++ b/Contentstack.Management.Core/Models/Variants.cs @@ -0,0 +1,125 @@ +using System; +using System.Threading.Tasks; +using Contentstack.Management.Core.Queryable; +using Contentstack.Management.Core.Services.Models; + +namespace Contentstack.Management.Core.Models +{ + public class Variants + { + internal Stack stack; + public string Uid { get; set; } + + internal string resourcePath; + + internal Variants(Stack stack, string uid = null) + { + stack.ThrowIfAPIKeyEmpty(); + + this.stack = stack; + Uid = uid; + resourcePath = uid == null ? "/variants" : $"/variants/{uid}"; + } + + /// + /// The Delete variants call is used to delete a specific variants. + /// + /// Query parameter + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// ContentstackResponse contentstackResponse = client.Stack("").Variants("").Delete();
+        /// 
+ ///
+ /// The . + public virtual ContentstackResponse Delete() + { + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + + var service = new FetchDeleteService(stack.client.serializer, stack, resourcePath, "DELETE"); + return stack.client.InvokeSync(service); + } + + /// + /// The Delete variants call is used to delete a specific variants. + /// + /// Query parameter + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// ContentstackResponse contentstackResponse = await client.Stack("").Variants("").DeleteAsync();
+        /// 
+ ///
+ /// The . + public virtual Task DeleteAsync() + { + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + + var service = new FetchDeleteService(stack.client.serializer, stack, resourcePath, "DELETE"); + return stack.client.InvokeAsync(service, true); + } + + /// + /// Retrieves a specific variant by UID. + /// + /// Optional query parameters. + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// ContentstackResponse contentstackResponse = client.Stack("").Variants("").Fetch();
+        /// 
+ ///
+ /// Variant data in . + public virtual ContentstackResponse Fetch(ParameterCollection collection = null) + { + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + + var service = new FetchDeleteService(stack.client.serializer, stack, resourcePath, collection: collection); + return stack.client.InvokeSync(service); + } + + /// + /// Asynchronously retrieves a specific variant by UID. + /// + /// Optional query parameters. + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// ContentstackResponse contentstackResponse = await client.Stack("").Variants("").FetchAsync();
+        /// 
+ ///
+ /// Task containing variant data in . + public virtual Task FetchAsync(ParameterCollection collection = null) + { + stack.ThrowIfNotLoggedIn(); + ThrowIfUidEmpty(); + + var service = new FetchDeleteService(stack.client.serializer, stack, resourcePath, collection: collection); + return stack.client.InvokeAsync(service); + } + + #region Internal Validation + + /// Validates no UID is set for collection operations. + internal void ThrowIfUidNotEmpty() + { + if (!string.IsNullOrEmpty(this.Uid)) + { + throw new InvalidOperationException("Operation not allowed."); + } + } + + /// Validates UID is set for specific variant operations. + internal void ThrowIfUidEmpty() + { + if (string.IsNullOrEmpty(this.Uid)) + { + throw new InvalidOperationException("Uid can not be empty."); + } + } + #endregion + } +} From 8c9064bd0b5533b0a08e24f67e3976df9daea237 Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Wed, 17 Sep 2025 13:09:07 +0530 Subject: [PATCH 2/4] Add integration and unit tests for Variants and VariantsModel, and remove VariantGroup model --- .../Contentstack016_VariantsTest.cs | 258 ++++++++++++++++++ .../Models/VariantsTest.cs | 258 ++++++++++++++++++ Contentstack.Management.Core/Models/Stack.cs | 30 -- .../Models/VariantGroup.cs | 12 - .../Models/Variants.cs | 101 +++++++ .../Models/VariantsModel.cs | 12 + 6 files changed, 629 insertions(+), 42 deletions(-) create mode 100644 Contentstack.Management.Core.Tests/IntegrationTest/Contentstack016_VariantsTest.cs create mode 100644 Contentstack.Management.Core.Unit.Tests/Models/VariantsTest.cs delete mode 100644 Contentstack.Management.Core/Models/VariantGroup.cs create mode 100644 Contentstack.Management.Core/Models/VariantsModel.cs diff --git a/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack016_VariantsTest.cs b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack016_VariantsTest.cs new file mode 100644 index 0000000..4d64f52 --- /dev/null +++ b/Contentstack.Management.Core.Tests/IntegrationTest/Contentstack016_VariantsTest.cs @@ -0,0 +1,258 @@ +using System; +using System.Threading.Tasks; +using Contentstack.Management.Core.Models; +using Contentstack.Management.Core.Tests.Model; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Contentstack.Management.Core.Tests.IntegrationTest +{ + [TestClass] + public class Contentstack016_VariantsTest + { + private Stack _stack; + + [TestInitialize] + public void Initialize() + { + StackResponse response = StackResponse.getStack(Contentstack.Client.serializer); + _stack = Contentstack.Client.Stack(response.Stack.APIKey); + } + + [TestMethod] + [DoNotParallelize] + public async Task Test001_Should_Create_Variants() + { + try + { + VariantsModel variantsModel = new VariantsModel(); + ContentstackResponse response = await _stack.Variants().CreateAsync(variantsModel); + Assert.AreEqual(System.Net.HttpStatusCode.Created, response.StatusCode); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test002_Should_Fetch_Variants_By_Uid() + { + try + { + // First create a variant to ensure we have something to fetch + VariantsModel variantsModel = new VariantsModel(); + ContentstackResponse createResponse = await _stack.Variants().CreateAsync(variantsModel); + Assert.AreEqual(System.Net.HttpStatusCode.Created, createResponse.StatusCode); + + // Extract UID from created variant + var createdVariant = createResponse.OpenJObjectResponse(); + string variantUid = createdVariant["variant"]["uid"].ToString(); + + // Test fetching by UID + ContentstackResponse fetchResponse = await _stack.Variants(variantUid).FetchAsync(); + Assert.IsTrue(fetchResponse.StatusCode == System.Net.HttpStatusCode.OK || + fetchResponse.StatusCode == System.Net.HttpStatusCode.NotFound); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test003_Should_FetchByUid_Multiple_Variants() + { + try + { + // Create multiple variants first + VariantsModel variantsModel1 = new VariantsModel(); + VariantsModel variantsModel2 = new VariantsModel(); + + ContentstackResponse createResponse1 = await _stack.Variants().CreateAsync(variantsModel1); + ContentstackResponse createResponse2 = await _stack.Variants().CreateAsync(variantsModel2); + + Assert.AreEqual(System.Net.HttpStatusCode.Created, createResponse1.StatusCode); + Assert.AreEqual(System.Net.HttpStatusCode.Created, createResponse2.StatusCode); + + // Extract UIDs from created variants + var createdVariant1 = createResponse1.OpenJObjectResponse(); + var createdVariant2 = createResponse2.OpenJObjectResponse(); + string variantUid1 = createdVariant1["variant"]["uid"].ToString(); + string variantUid2 = createdVariant2["variant"]["uid"].ToString(); + + // Test fetching multiple variants by UIDs + string[] uids = { variantUid1, variantUid2 }; + ContentstackResponse fetchResponse = await _stack.Variants().FetchByUidAsync(uids); + + Assert.IsTrue(fetchResponse.StatusCode == System.Net.HttpStatusCode.OK || + fetchResponse.StatusCode == System.Net.HttpStatusCode.NotFound); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test004_Should_FetchByUid_Single_Variant() + { + try + { + // Create a variant first + VariantsModel variantsModel = new VariantsModel(); + ContentstackResponse createResponse = await _stack.Variants().CreateAsync(variantsModel); + Assert.AreEqual(System.Net.HttpStatusCode.Created, createResponse.StatusCode); + + // Extract UID from created variant + var createdVariant = createResponse.OpenJObjectResponse(); + string variantUid = createdVariant["variant"]["uid"].ToString(); + + // Test fetching single variant using FetchByUid + string[] uids = { variantUid }; + ContentstackResponse fetchResponse = await _stack.Variants().FetchByUidAsync(uids); + + Assert.IsTrue(fetchResponse.StatusCode == System.Net.HttpStatusCode.OK || + fetchResponse.StatusCode == System.Net.HttpStatusCode.NotFound); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test005_Should_Handle_FetchByUid_With_Nonexistent_Uids() + { + try + { + // Test fetching with non-existent UIDs + string[] nonExistentUids = { "nonexistent_uid_1", "nonexistent_uid_2" }; + ContentstackResponse fetchResponse = await _stack.Variants().FetchByUidAsync(nonExistentUids); + + // Should return 404 or empty result, not crash + Assert.IsTrue(fetchResponse.StatusCode == System.Net.HttpStatusCode.NotFound || + fetchResponse.StatusCode == System.Net.HttpStatusCode.OK); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test006_Should_FetchByUid_Sync_Method() + { + try + { + // Create a variant first + VariantsModel variantsModel = new VariantsModel(); + ContentstackResponse createResponse = _stack.Variants().Create(variantsModel); + Assert.AreEqual(System.Net.HttpStatusCode.Created, createResponse.StatusCode); + + // Extract UID from created variant + var createdVariant = createResponse.OpenJObjectResponse(); + string variantUid = createdVariant["variant"]["uid"].ToString(); + + // Test synchronous FetchByUid + string[] uids = { variantUid }; + ContentstackResponse fetchResponse = _stack.Variants().FetchByUid(uids); + + Assert.IsTrue(fetchResponse.StatusCode == System.Net.HttpStatusCode.OK || + fetchResponse.StatusCode == System.Net.HttpStatusCode.NotFound); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test007_Should_Delete_Variant() + { + try + { + // Create a variant first + VariantsModel variantsModel = new VariantsModel(); + ContentstackResponse createResponse = await _stack.Variants().CreateAsync(variantsModel); + Assert.AreEqual(System.Net.HttpStatusCode.Created, createResponse.StatusCode); + + // Extract UID from created variant + var createdVariant = createResponse.OpenJObjectResponse(); + string variantUid = createdVariant["variant"]["uid"].ToString(); + + // Test deleting the variant + ContentstackResponse deleteResponse = await _stack.Variants(variantUid).DeleteAsync(); + Assert.IsTrue(deleteResponse.StatusCode == System.Net.HttpStatusCode.OK || + deleteResponse.StatusCode == System.Net.HttpStatusCode.NoContent || + deleteResponse.StatusCode == System.Net.HttpStatusCode.NotFound); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public void Test008_Should_Validate_FetchByUid_Parameters() + { + try + { + // Test with null UIDs + Assert.ThrowsException(() => _stack.Variants().FetchByUid(null)); + + // Test with empty UIDs array + string[] emptyUids = new string[0]; + Assert.ThrowsException(() => _stack.Variants().FetchByUid(emptyUids)); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public async Task Test009_Should_Validate_FetchByUidAsync_Parameters() + { + try + { + // Test with null UIDs + await Assert.ThrowsExceptionAsync(() => _stack.Variants().FetchByUidAsync(null)); + + // Test with empty UIDs array + string[] emptyUids = new string[0]; + await Assert.ThrowsExceptionAsync(() => _stack.Variants().FetchByUidAsync(emptyUids)); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + + [TestMethod] + [DoNotParallelize] + public void Test010_Should_Validate_Instance_With_Uid_Cannot_Use_FetchByUid() + { + try + { + // Test that an instance with UID cannot call FetchByUid + string instanceUid = "some_uid"; + string[] uids = { "uid1", "uid2" }; + + Assert.ThrowsException(() => + _stack.Variants(instanceUid).FetchByUid(uids)); + } + catch (Exception e) + { + Assert.Fail(e.Message); + } + } + } +} diff --git a/Contentstack.Management.Core.Unit.Tests/Models/VariantsTest.cs b/Contentstack.Management.Core.Unit.Tests/Models/VariantsTest.cs new file mode 100644 index 0000000..9dcfc95 --- /dev/null +++ b/Contentstack.Management.Core.Unit.Tests/Models/VariantsTest.cs @@ -0,0 +1,258 @@ +using System; +using System.Threading.Tasks; +using AutoFixture; +using Contentstack.Management.Core.Models; +using Contentstack.Management.Core.Queryable; +using Contentstack.Management.Core.Unit.Tests.Mokes; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Contentstack.Management.Core.Unit.Tests.Models +{ + [TestClass] + public class VariantsTest + { + private Stack _stack; + private readonly IFixture _fixture = new Fixture(); + private ContentstackResponse _contentstackResponse; + private VariantsModel _variantsModel = new VariantsModel(); + + [TestInitialize] + public void Initialize() + { + var client = new ContentstackClient(); + _contentstackResponse = MockResponse.CreateContentstackResponse("MockResponse.txt"); + client.ContentstackPipeline.ReplaceHandler(new MockHttpHandler(_contentstackResponse)); + client.contentstackOptions.Authtoken = _fixture.Create(); + _stack = new Stack(client, _fixture.Create()); + } + + #region Initialize Tests + + [TestMethod] + public void Initialize_Variants_Without_Uid() + { + Variants variants = new Variants(_stack, null); + + Assert.IsNull(variants.Uid); + Assert.AreEqual("/variants", variants.resourcePath); + Assert.ThrowsException(() => variants.Fetch()); + Assert.ThrowsExceptionAsync(() => variants.FetchAsync()); + Assert.ThrowsException(() => variants.Delete()); + Assert.ThrowsExceptionAsync(() => variants.DeleteAsync()); + } + + [TestMethod] + public void Initialize_Variants_With_Uid() + { + string uid = _fixture.Create(); + Variants variants = new Variants(_stack, uid); + + Assert.AreEqual(uid, variants.Uid); + Assert.AreEqual($"/variants/{uid}", variants.resourcePath); + Assert.ThrowsException(() => variants.Create(_variantsModel)); + Assert.ThrowsExceptionAsync(() => variants.CreateAsync(_variantsModel)); + Assert.ThrowsException(() => variants.FetchByUid(new string[] { "uid1", "uid2" })); + Assert.ThrowsExceptionAsync(() => variants.FetchByUidAsync(new string[] { "uid1", "uid2" })); + } + + #endregion + + #region Create Tests + + [TestMethod] + public void Should_Create_Variants() + { + ContentstackResponse response = _stack.Variants().Create(_variantsModel); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async Task Should_Create_Variants_Async() + { + ContentstackResponse response = await _stack.Variants().CreateAsync(_variantsModel); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + #endregion + + #region Fetch Tests + + [TestMethod] + public void Should_Fetch_Variants() + { + ContentstackResponse response = _stack.Variants(_fixture.Create()).Fetch(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async Task Should_Fetch_Variants_Async() + { + ContentstackResponse response = await _stack.Variants(_fixture.Create()).FetchAsync(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Fetch_Variants_With_Parameters() + { + ParameterCollection collection = new ParameterCollection(); + collection.Add("include_count", true); + + ContentstackResponse response = _stack.Variants(_fixture.Create()).Fetch(collection); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async Task Should_Fetch_Variants_With_Parameters_Async() + { + ParameterCollection collection = new ParameterCollection(); + collection.Add("include_count", true); + + ContentstackResponse response = await _stack.Variants(_fixture.Create()).FetchAsync(collection); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + #endregion + + #region FetchByUid Tests + + [TestMethod] + public void Should_FetchByUid_Variants() + { + string[] uids = { _fixture.Create(), _fixture.Create(), _fixture.Create() }; + ContentstackResponse response = _stack.Variants().FetchByUid(uids); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async Task Should_FetchByUid_Variants_Async() + { + string[] uids = { _fixture.Create(), _fixture.Create(), _fixture.Create() }; + ContentstackResponse response = await _stack.Variants().FetchByUidAsync(uids); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public void Should_Throw_Exception_When_FetchByUid_Called_With_Null_Uids() + { + Assert.ThrowsException(() => _stack.Variants().FetchByUid(null)); + } + + [TestMethod] + public async Task Should_Throw_Exception_When_FetchByUidAsync_Called_With_Null_Uids() + { + await Assert.ThrowsExceptionAsync(() => _stack.Variants().FetchByUidAsync(null)); + } + + [TestMethod] + public void Should_Throw_Exception_When_FetchByUid_Called_With_Empty_Uids() + { + string[] emptyUids = new string[0]; + Assert.ThrowsException(() => _stack.Variants().FetchByUid(emptyUids)); + } + + [TestMethod] + public async Task Should_Throw_Exception_When_FetchByUidAsync_Called_With_Empty_Uids() + { + string[] emptyUids = new string[0]; + await Assert.ThrowsExceptionAsync(() => _stack.Variants().FetchByUidAsync(emptyUids)); + } + + [TestMethod] + public void Should_Throw_Exception_When_FetchByUid_Called_On_Instance_With_Uid() + { + string[] uids = { _fixture.Create(), _fixture.Create() }; + Assert.ThrowsException(() => _stack.Variants(_fixture.Create()).FetchByUid(uids)); + } + + [TestMethod] + public async Task Should_Throw_Exception_When_FetchByUidAsync_Called_On_Instance_With_Uid() + { + string[] uids = { _fixture.Create(), _fixture.Create() }; + await Assert.ThrowsExceptionAsync(() => _stack.Variants(_fixture.Create()).FetchByUidAsync(uids)); + } + + [TestMethod] + public void Should_FetchByUid_Single_Uid() + { + string[] uids = { _fixture.Create() }; + ContentstackResponse response = _stack.Variants().FetchByUid(uids); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async Task Should_FetchByUid_Single_Uid_Async() + { + string[] uids = { _fixture.Create() }; + ContentstackResponse response = await _stack.Variants().FetchByUidAsync(uids); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + #endregion + + #region Delete Tests + + [TestMethod] + public void Should_Delete_Variants() + { + ContentstackResponse response = _stack.Variants(_fixture.Create()).Delete(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + [TestMethod] + public async Task Should_Delete_Variants_Async() + { + ContentstackResponse response = await _stack.Variants(_fixture.Create()).DeleteAsync(); + + Assert.AreEqual(_contentstackResponse.OpenResponse(), response.OpenResponse()); + Assert.AreEqual(_contentstackResponse.OpenJObjectResponse().ToString(), response.OpenJObjectResponse().ToString()); + } + + #endregion + + #region Validation Tests + + [TestMethod] + public void Should_Throw_Exception_When_APIKey_Is_Null() + { + var client = new ContentstackClient(); + client.contentstackOptions.Authtoken = _fixture.Create(); + var stackWithNullAPIKey = new Stack(client, null); + + Assert.ThrowsException(() => new Variants(stackWithNullAPIKey)); + } + + [TestMethod] + public void Should_Throw_Exception_When_APIKey_Is_Empty() + { + var client = new ContentstackClient(); + client.contentstackOptions.Authtoken = _fixture.Create(); + var stackWithEmptyAPIKey = new Stack(client, ""); + + Assert.ThrowsException(() => new Variants(stackWithEmptyAPIKey)); + } + + #endregion + } +} diff --git a/Contentstack.Management.Core/Models/Stack.cs b/Contentstack.Management.Core/Models/Stack.cs index 108a977..e182506 100644 --- a/Contentstack.Management.Core/Models/Stack.cs +++ b/Contentstack.Management.Core/Models/Stack.cs @@ -941,36 +941,6 @@ public Variants Variants(string uid = null) return new Variants(this, uid); } - - /// - /// Gets the bulk operation instance for performing bulk operations on entries and assets. - /// - /// - ///

-        /// ContentstackClient client = new ContentstackClient("", "");
-        /// Stack stack = client.Stack("");
-        /// 
-        /// var publishDetails = new BulkPublishDetails
-        /// {
-        ///     Entries = new List
-        ///     {
-        ///         new BulkPublishEntry { Uid = "entry_uid", ContentTypeUid = "content_type_uid", Locale = "en-us" }
-        ///     },
-        ///     Locales = new List { "en-us" },
-        ///     Environments = new List { "environment_uid" }
-        /// };
-        /// 
-        /// ContentstackResponse response = stack.BulkOperation().Publish(publishDetails);
-        /// 
- ///
- /// The - public VariantGroup VariantGroup(string uid = null) - { - ThrowIfNotLoggedIn(); - ThrowIfAPIKeyEmpty(); - - return new VariantGroup(this, uid); - } #endregion #region Throw Error diff --git a/Contentstack.Management.Core/Models/VariantGroup.cs b/Contentstack.Management.Core/Models/VariantGroup.cs deleted file mode 100644 index 416d945..0000000 --- a/Contentstack.Management.Core/Models/VariantGroup.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Threading.Tasks; -using Contentstack.Management.Core.Queryable; -using Contentstack.Management.Core.Services.Models; - -namespace Contentstack.Management.Core.Models -{ - public class VariantGroup - { - - } -} diff --git a/Contentstack.Management.Core/Models/Variants.cs b/Contentstack.Management.Core/Models/Variants.cs index b7792f3..f24649b 100644 --- a/Contentstack.Management.Core/Models/Variants.cs +++ b/Contentstack.Management.Core/Models/Variants.cs @@ -1,7 +1,9 @@ using System; +using System.Linq; using System.Threading.Tasks; using Contentstack.Management.Core.Queryable; using Contentstack.Management.Core.Services.Models; +using Contentstack.Management.Core.Abstractions; namespace Contentstack.Management.Core.Models { @@ -100,6 +102,105 @@ public virtual Task FetchAsync(ParameterCollection collect var service = new FetchDeleteService(stack.client.serializer, stack, resourcePath, collection: collection); return stack.client.InvokeAsync(service); } + + /// + /// The Create is used to create an entry variant in the stack. + /// + /// with details. + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// VariantsModel model = new VariantsModel(); 
+        /// ContentstackResponse contentstackResponse = client.Stack("").Variants().Create(model);
+        /// 
+ ///
+ /// The . + public virtual ContentstackResponse Create(VariantsModel model) + { + ThrowIfUidNotEmpty(); + + var service = new CreateUpdateService(stack.client.serializer, stack, resourcePath, model, "entry"); + return stack.client.InvokeSync(service); + } + + /// + /// The Create is used to create an entry variant in the stack. + /// + /// with details. + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// VariantsModel model = new VariantsModel();
+        /// ContentstackResponse contentstackResponse = await client.Stack("").Variants().CreateAsync(model);
+        /// 
+ ///
+ /// The Task. + public virtual Task CreateAsync(VariantsModel model) + { + ThrowIfUidNotEmpty(); + stack.ThrowIfNotLoggedIn(); + + var service = new CreateUpdateService(stack.client.serializer, stack, resourcePath, model, "entry"); + return stack.client.InvokeAsync, ContentstackResponse>(service); + } + + /// + /// Retrieves multiple variants by their UIDs. + /// + /// Array of variant UIDs to fetch. + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// string[] variantUids = {"uid1", "uid2", "uid3"};
+        /// ContentstackResponse contentstackResponse = client.Stack("").Variants().FetchByUid(variantUids);
+        /// 
+ ///
+ /// Variants data in . + public virtual ContentstackResponse FetchByUid(string[] uids) + { + stack.ThrowIfNotLoggedIn(); + ThrowIfUidNotEmpty(); + + if (uids == null || uids.Length == 0) + { + throw new ArgumentException("UIDs array cannot be null or empty.", nameof(uids)); + } + + var collection = new ParameterCollection(); + collection.Add("uid", uids.ToList()); + + var service = new FetchDeleteService(stack.client.serializer, stack, resourcePath, collection: collection); + return stack.client.InvokeSync(service); + } + + /// + /// Asynchronously retrieves multiple variants by their UIDs. + /// + /// Array of variant UIDs to fetch. + /// + ///

+        /// ContentstackClient client = new ContentstackClient("", "");
+        /// string[] variantUids = {"uid1", "uid2", "uid3"};
+        /// ContentstackResponse contentstackResponse = await client.Stack("").Variants().FetchByUidAsync(variantUids);
+        /// 
+ ///
+ /// Task containing variants data in . + public virtual Task FetchByUidAsync(string[] uids) + { + stack.ThrowIfNotLoggedIn(); + ThrowIfUidNotEmpty(); + + if (uids == null || uids.Length == 0) + { + throw new ArgumentException("UIDs array cannot be null or empty.", nameof(uids)); + } + + var collection = new ParameterCollection(); + collection.Add("uid", uids.ToList()); + + var service = new FetchDeleteService(stack.client.serializer, stack, resourcePath, collection: collection); + return stack.client.InvokeAsync(service); + } #region Internal Validation diff --git a/Contentstack.Management.Core/Models/VariantsModel.cs b/Contentstack.Management.Core/Models/VariantsModel.cs new file mode 100644 index 0000000..0abae0c --- /dev/null +++ b/Contentstack.Management.Core/Models/VariantsModel.cs @@ -0,0 +1,12 @@ +using System; +using Newtonsoft.Json; + +namespace Contentstack.Management.Core.Models +{ + [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] + public class VariantsModel + { + [JsonProperty(propertyName: "title")] + string Title { get; set; } + } +} From 59a621f4ed8aab3bbf4ce43e320a7da0a0d045d3 Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Wed, 17 Sep 2025 13:27:30 +0530 Subject: [PATCH 3/4] docs: Refine documentation for Stack and Variants models, correcting terminology and enhancing clarity for method descriptions. --- Contentstack.Management.Core/Models/Stack.cs | 28 +++------- .../Models/Variants.cs | 56 +++++++++---------- 2 files changed, 37 insertions(+), 47 deletions(-) diff --git a/Contentstack.Management.Core/Models/Stack.cs b/Contentstack.Management.Core/Models/Stack.cs index e182506..90a14ac 100644 --- a/Contentstack.Management.Core/Models/Stack.cs +++ b/Contentstack.Management.Core/Models/Stack.cs @@ -622,7 +622,7 @@ public ContentType ContentType(string uid = null) return new ContentType(this, uid); } /// - /// efer to all the media files (images, videos, PDFs, audio files, and so on) uploaded in your Contentstack repository for future use. + /// refer to all the media files (images, videos, PDFs, audio files, and so on) uploaded in your Contentstack repository for future use. /// /// Optional asset uid. /// @@ -762,7 +762,7 @@ public ManagementToken ManagementTokens(string uid = null) } /// - /// A collection of permissions that will be applicable to all the users who are assigned this role. + /// A is a collection of permissions that will be applicable to all the users who are assigned this role. /// /// Optional, role uid. /// @@ -772,7 +772,7 @@ public ManagementToken ManagementTokens(string uid = null) /// ContentstackResponse contentstackResponse = stack.Role("").Fetch(); /// /// - /// The + /// The public Role Role(string uid = null) { ThrowIfNotLoggedIn(); @@ -843,9 +843,9 @@ public PublishQueue PublishQueue(string uid = null) return new PublishQueue(this, uid); } /// - /// A a mechanism that sends real-time information to any third-party app or service to keep your application in sync with your Contentstack account. + /// A is a mechanism that sends real-time information to any third-party app or service to keep your application in sync with your Contentstack account. /// - /// Optional, webhook uid uid. + /// Optional, webhook uid. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
@@ -853,7 +853,7 @@ public PublishQueue PublishQueue(string uid = null)
         /// ContentstackResponse contentstackResponse = stack.Webhook("").Fetch();
         /// 
///
- /// The + /// The public Webhook Webhook(string uid = null) { ThrowIfNotLoggedIn(); @@ -913,24 +913,14 @@ public BulkOperation BulkOperation() } /// - /// Gets the bulk operation instance for performing bulk operations on entries and assets. + /// A represents different versions or variations of content entries that allow you to create and manage multiple variants of the same content. /// + /// Optional, variant uid. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
         /// Stack stack = client.Stack("");
-        /// 
-        /// var publishDetails = new BulkPublishDetails
-        /// {
-        ///     Entries = new List
-        ///     {
-        ///         new BulkPublishEntry { Uid = "entry_uid", ContentTypeUid = "content_type_uid", Locale = "en-us" }
-        ///     },
-        ///     Locales = new List { "en-us" },
-        ///     Environments = new List { "environment_uid" }
-        /// };
-        /// 
-        /// ContentstackResponse response = stack.BulkOperation().Publish(publishDetails);
+        /// ContentstackResponse contentstackResponse = stack.Variants("").Fetch();
         /// 
///
/// The diff --git a/Contentstack.Management.Core/Models/Variants.cs b/Contentstack.Management.Core/Models/Variants.cs index f24649b..4d5f781 100644 --- a/Contentstack.Management.Core/Models/Variants.cs +++ b/Contentstack.Management.Core/Models/Variants.cs @@ -24,16 +24,15 @@ internal Variants(Stack stack, string uid = null) } /// - /// The Delete variants call is used to delete a specific variants. + /// The Delete call is used to delete a specific variant. /// - /// Query parameter /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
-        /// ContentstackResponse contentstackResponse = client.Stack("").Variants("").Delete();
+        /// ContentstackResponse contentstackResponse = client.Stack("").Variants("").Delete();
         /// 
///
- /// The . + /// The containing the deletion result. public virtual ContentstackResponse Delete() { stack.ThrowIfNotLoggedIn(); @@ -44,16 +43,15 @@ public virtual ContentstackResponse Delete() } /// - /// The Delete variants call is used to delete a specific variants. + /// The DeleteAsync call is used to asynchronously delete a specific variant. /// - /// Query parameter /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
-        /// ContentstackResponse contentstackResponse = await client.Stack("").Variants("").DeleteAsync();
+        /// ContentstackResponse contentstackResponse = await client.Stack("").Variants("").DeleteAsync();
         /// 
///
- /// The . + /// The Task containing with the deletion result. public virtual Task DeleteAsync() { stack.ThrowIfNotLoggedIn(); @@ -64,16 +62,16 @@ public virtual Task DeleteAsync() } /// - /// Retrieves a specific variant by UID. + /// The Fetch call retrieves a specific variant by UID. /// /// Optional query parameters. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
-        /// ContentstackResponse contentstackResponse = client.Stack("").Variants("").Fetch();
+        /// ContentstackResponse contentstackResponse = client.Stack("").Variants("").Fetch();
         /// 
///
- /// Variant data in . + /// The containing the variant data. public virtual ContentstackResponse Fetch(ParameterCollection collection = null) { stack.ThrowIfNotLoggedIn(); @@ -84,16 +82,16 @@ public virtual ContentstackResponse Fetch(ParameterCollection collection = null) } /// - /// Asynchronously retrieves a specific variant by UID. + /// The FetchAsync call asynchronously retrieves a specific variant by UID. /// /// Optional query parameters. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
-        /// ContentstackResponse contentstackResponse = await client.Stack("").Variants("").FetchAsync();
+        /// ContentstackResponse contentstackResponse = await client.Stack("").Variants("").FetchAsync();
         /// 
///
- /// Task containing variant data in . + /// The Task containing with the variant data. public virtual Task FetchAsync(ParameterCollection collection = null) { stack.ThrowIfNotLoggedIn(); @@ -104,9 +102,9 @@ public virtual Task FetchAsync(ParameterCollection collect } /// - /// The Create is used to create an entry variant in the stack. + /// The Create call is used to create an entry variant in the stack. /// - /// with details. + /// The containing variant details. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
@@ -114,7 +112,7 @@ public virtual Task FetchAsync(ParameterCollection collect
         /// ContentstackResponse contentstackResponse = client.Stack("").Variants().Create(model);
         /// 
///
- /// The . + /// The containing the created variant data. public virtual ContentstackResponse Create(VariantsModel model) { ThrowIfUidNotEmpty(); @@ -124,9 +122,9 @@ public virtual ContentstackResponse Create(VariantsModel model) } /// - /// The Create is used to create an entry variant in the stack. + /// The CreateAsync call is used to asynchronously create an entry variant in the stack. /// - /// with details. + /// The containing variant details. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
@@ -134,7 +132,7 @@ public virtual ContentstackResponse Create(VariantsModel model)
         /// ContentstackResponse contentstackResponse = await client.Stack("").Variants().CreateAsync(model);
         /// 
///
- /// The Task. + /// The Task containing with the created variant data. public virtual Task CreateAsync(VariantsModel model) { ThrowIfUidNotEmpty(); @@ -145,17 +143,18 @@ public virtual Task CreateAsync(VariantsModel model) } /// - /// Retrieves multiple variants by their UIDs. + /// The FetchByUid call retrieves multiple variants by passing an array of their UIDs. + /// This method allows you to fetch multiple variants in a single API call. /// - /// Array of variant UIDs to fetch. + /// Array of variant UIDs to fetch. Cannot be null or empty. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
-        /// string[] variantUids = {"uid1", "uid2", "uid3"};
+        /// string[] variantUids = {"bltvariant123", "bltvariant456", "bltvariant789"};
         /// ContentstackResponse contentstackResponse = client.Stack("").Variants().FetchByUid(variantUids);
         /// 
///
- /// Variants data in . + /// The containing the requested variants data. public virtual ContentstackResponse FetchByUid(string[] uids) { stack.ThrowIfNotLoggedIn(); @@ -174,17 +173,18 @@ public virtual ContentstackResponse FetchByUid(string[] uids) } /// - /// Asynchronously retrieves multiple variants by their UIDs. + /// The FetchByUidAsync call asynchronously retrieves multiple variants by passing an array of their UIDs. + /// This method allows you to fetch multiple variants in a single API call asynchronously. /// - /// Array of variant UIDs to fetch. + /// Array of variant UIDs to fetch. Cannot be null or empty. /// ///

         /// ContentstackClient client = new ContentstackClient("", "");
-        /// string[] variantUids = {"uid1", "uid2", "uid3"};
+        /// string[] variantUids = {"bltvariant123", "bltvariant456", "bltvariant789"};
         /// ContentstackResponse contentstackResponse = await client.Stack("").Variants().FetchByUidAsync(variantUids);
         /// 
///
- /// Task containing variants data in . + /// The Task containing with the requested variants data. public virtual Task FetchByUidAsync(string[] uids) { stack.ThrowIfNotLoggedIn(); From a5c4c63acc5ba9ed5737023d52a157c7295f3476 Mon Sep 17 00:00:00 2001 From: Nadeem Patwekar Date: Wed, 17 Sep 2025 14:59:07 +0530 Subject: [PATCH 4/4] refactor: Change method access modifiers from virtual to public for Variants class methods --- Contentstack.Management.Core/Models/Variants.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Contentstack.Management.Core/Models/Variants.cs b/Contentstack.Management.Core/Models/Variants.cs index 4d5f781..b9afa58 100644 --- a/Contentstack.Management.Core/Models/Variants.cs +++ b/Contentstack.Management.Core/Models/Variants.cs @@ -33,7 +33,7 @@ internal Variants(Stack stack, string uid = null) /// ///
/// The containing the deletion result. - public virtual ContentstackResponse Delete() + public ContentstackResponse Delete() { stack.ThrowIfNotLoggedIn(); ThrowIfUidEmpty(); @@ -52,7 +52,7 @@ public virtual ContentstackResponse Delete() /// /// /// The Task containing with the deletion result. - public virtual Task DeleteAsync() + public Task DeleteAsync() { stack.ThrowIfNotLoggedIn(); ThrowIfUidEmpty(); @@ -72,7 +72,7 @@ public virtual Task DeleteAsync() /// /// /// The containing the variant data. - public virtual ContentstackResponse Fetch(ParameterCollection collection = null) + public ContentstackResponse Fetch(ParameterCollection collection = null) { stack.ThrowIfNotLoggedIn(); ThrowIfUidEmpty(); @@ -92,7 +92,7 @@ public virtual ContentstackResponse Fetch(ParameterCollection collection = null) /// /// /// The Task containing with the variant data. - public virtual Task FetchAsync(ParameterCollection collection = null) + public Task FetchAsync(ParameterCollection collection = null) { stack.ThrowIfNotLoggedIn(); ThrowIfUidEmpty(); @@ -113,7 +113,7 @@ public virtual Task FetchAsync(ParameterCollection collect /// /// /// The containing the created variant data. - public virtual ContentstackResponse Create(VariantsModel model) + public ContentstackResponse Create(VariantsModel model) { ThrowIfUidNotEmpty(); @@ -133,7 +133,7 @@ public virtual ContentstackResponse Create(VariantsModel model) /// /// /// The Task containing with the created variant data. - public virtual Task CreateAsync(VariantsModel model) + public Task CreateAsync(VariantsModel model) { ThrowIfUidNotEmpty(); stack.ThrowIfNotLoggedIn(); @@ -155,7 +155,7 @@ public virtual Task CreateAsync(VariantsModel model) /// /// /// The containing the requested variants data. - public virtual ContentstackResponse FetchByUid(string[] uids) + public ContentstackResponse FetchByUid(string[] uids) { stack.ThrowIfNotLoggedIn(); ThrowIfUidNotEmpty(); @@ -185,7 +185,7 @@ public virtual ContentstackResponse FetchByUid(string[] uids) /// /// /// The Task containing with the requested variants data. - public virtual Task FetchByUidAsync(string[] uids) + public Task FetchByUidAsync(string[] uids) { stack.ThrowIfNotLoggedIn(); ThrowIfUidNotEmpty();