Skip to content

Commit ca9a54b

Browse files
committed
feat: Added the fix for Extention and role module
Fix 1 — Role serialization (13 tests + Test057 as side-effect) New file: Contentstack.Management.Core/Utils/RuleJsonConverter.cs System.Text.Json only serializes the declared type when iterating a List<Rule>, so subclass fields like module and branches were silently dropped. The custom converter serializes the actual runtime type on Write, and dispatches on the "module" discriminator on Read. It uses the existing WithoutConverter<T>() helper to avoid infinite recursion. Edit: ContentstackClient.cs Initialize() Added SerializerOptions.Converters.Add(new RuleJsonConverter()) alongside the existing NodeJsonConverter and TextNodeJsonConverter registrations. Fix 2 — Extension upload wrong file type (3 tests) The Contentstack Extensions API requires an HTML source file. All three tests were uploading contentTypeSchema.json, which the API rejected with error_code 344: "upload: Not a valid html file". New file: Contentstack.Management.Core.Tests/Mock/extension.html Minimal valid HTML file accepted by the Extensions API. Edit: Contentstack013_AssetTest.cs Test002, Test003, Test004 — changed path from contentTypeSchema.json → extension.html and content type from "application/json" → "text/html".
1 parent f1df975 commit ca9a54b

3 files changed

Lines changed: 59 additions & 6 deletions

File tree

Contentstack.Management.Core.Tests/IntegrationTest/Contentstack013_AssetTest.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,11 +410,11 @@ public async Task Test001_Should_Create_Asset()
410410
public async Task Test002_Should_Create_Dashboard()
411411
{
412412
TestOutputLogger.LogContext("TestScenario", "CreateDashboardWidget");
413-
var path = Path.Combine(System.Environment.CurrentDirectory, "../../../Mock/contentTypeSchema.json");
413+
var path = Path.Combine(System.Environment.CurrentDirectory, "../../../Mock/extension.html");
414414
try
415415
{
416416
DashboardWidgetModel dashboard = new DashboardWidgetModel(
417-
path, "application/json", "Integration Test Dashboard",
417+
path, "text/html", "Integration Test Dashboard",
418418
isEnable: true, defaultWidth: "half", tags: "dashboard,test");
419419
ContentstackResponse response = await _stack.Extension().UploadAsync(dashboard);
420420
TestOutputLogger.LogContext("StackAPIKey", _stack?.APIKey ?? "null");
@@ -439,12 +439,12 @@ public async Task Test002_Should_Create_Dashboard()
439439
public async Task Test003_Should_Create_Custom_Widget()
440440
{
441441
TestOutputLogger.LogContext("TestScenario", "CreateCustomWidget");
442-
var path = Path.Combine(System.Environment.CurrentDirectory, "../../../Mock/contentTypeSchema.json");
442+
var path = Path.Combine(System.Environment.CurrentDirectory, "../../../Mock/extension.html");
443443
try
444444
{
445445
var scope = new ExtensionScope { ContentTypes = new List<string> { "$all" } };
446446
CustomWidgetModel widget = new CustomWidgetModel(
447-
path, "application/json", "Integration Test Widget",
447+
path, "text/html", "Integration Test Widget",
448448
tags: "widget,test", scope: scope);
449449
ContentstackResponse response = await _stack.Extension().UploadAsync(widget);
450450
TestOutputLogger.LogContext("StackAPIKey", _stack?.APIKey ?? "null");
@@ -469,11 +469,11 @@ public async Task Test003_Should_Create_Custom_Widget()
469469
public async Task Test004_Should_Create_Custom_field()
470470
{
471471
TestOutputLogger.LogContext("TestScenario", "CreateCustomField");
472-
var path = Path.Combine(System.Environment.CurrentDirectory, "../../../Mock/contentTypeSchema.json");
472+
var path = Path.Combine(System.Environment.CurrentDirectory, "../../../Mock/extension.html");
473473
try
474474
{
475475
CustomFieldModel field = new CustomFieldModel(
476-
path, "application/json", "Integration Test Field",
476+
path, "text/html", "Integration Test Field",
477477
dataType: "text", isMultiple: false, tags: "field,test");
478478
ContentstackResponse response = await _stack.Extension().UploadAsync(field);
479479
TestOutputLogger.LogContext("StackAPIKey", _stack?.APIKey ?? "null");

Contentstack.Management.Core/ContentstackClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ protected void Initialize(HttpClient? httpClient = null)
203203
SerializerOptions.Converters.Add(new FieldJsonConverter()); // Re-enabled for ContentType support
204204
SerializerOptions.Converters.Add(new NodeJsonConverter());
205205
SerializerOptions.Converters.Add(new TextNodeJsonConverter());
206+
SerializerOptions.Converters.Add(new RuleJsonConverter());
206207
}
207208

208209
protected void BuildPipeline()
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
using Contentstack.Management.Core.Models;
5+
6+
namespace Contentstack.Management.Core.Utils
7+
{
8+
/// <summary>
9+
/// Polymorphic converter for <see cref="Rule"/> so that derived types
10+
/// (BranchRules, ContentTypeRules, etc.) are serialized with their own
11+
/// properties when stored in a <c>List&lt;Rule&gt;</c>.
12+
///
13+
/// System.Text.Json only serializes the declared type by default, which
14+
/// drops subclass-only fields like "module" and "branches" from the payload,
15+
/// causing the Management API to return HTTP 422.
16+
///
17+
/// Register this converter in JsonSerializerOptions.Converters (not via
18+
/// [JsonConverter] attribute) so that WithoutConverter&lt;T&gt;() can remove it
19+
/// during recursive serialization and prevent infinite recursion.
20+
/// </summary>
21+
internal class RuleJsonConverter : JsonConverter<Rule>
22+
{
23+
public override void Write(Utf8JsonWriter writer, Rule value, JsonSerializerOptions options)
24+
{
25+
var opts = options.WithoutConverter<RuleJsonConverter>();
26+
JsonSerializer.Serialize(writer, (object)value, value.GetType(), opts);
27+
}
28+
29+
public override Rule? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
30+
{
31+
using var doc = JsonDocument.ParseValue(ref reader);
32+
var opts = options.WithoutConverter<RuleJsonConverter>();
33+
34+
if (doc.RootElement.TryGetProperty("module", out var moduleProp))
35+
{
36+
return moduleProp.GetString() switch
37+
{
38+
"branch" => doc.RootElement.Deserialize<BranchRules>(opts),
39+
"branch_alias" => doc.RootElement.Deserialize<BranchAliasRules>(opts),
40+
"content_type" => doc.RootElement.Deserialize<ContentTypeRules>(opts),
41+
"asset" => doc.RootElement.Deserialize<AssetRules>(opts),
42+
"folder" => doc.RootElement.Deserialize<FolderRules>(opts),
43+
"environment" => doc.RootElement.Deserialize<EnvironmentRules>(opts),
44+
"taxonomy" => doc.RootElement.Deserialize<TaxonomyRules>(opts),
45+
_ => doc.RootElement.Deserialize<Rule>(opts),
46+
};
47+
}
48+
49+
return doc.RootElement.Deserialize<Rule>(opts);
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)