diff --git a/sdks/dotnet/bin/copy-constants.php b/sdks/dotnet/bin/copy-constants.php
index 885380501..d99fb7960 100755
--- a/sdks/dotnet/bin/copy-constants.php
+++ b/sdks/dotnet/bin/copy-constants.php
@@ -13,17 +13,26 @@
});
/**
- * Between openapi-generator v7.8.0 and v7.12.0 a change was made to the way
- * a few generators create constant names from values. The original way was
- * actually broken. For example "change-field-visibility" would generate a
- * constant name of "TYPE_FIELD_VISIBILITY", dropping the "change" part.
+ * Post-generation patch for SubFormFieldRuleAction.cs.
*
- * The fix now generates the correct name, "TYPE_CHANGE_FIELD_VISIBILITY".
- * However, the fix also gets rid of the previous (incorrect) constant names,
- * making the fix a BC break.
+ * Between openapi-generator v7.8.0 and v7.12.0 a change was made to the
+ * way a few generators create constant names from values. The original
+ * way was broken: "change-field-visibility" generated a constant named
+ * "TYPE_FIELD_VISIBILITY", dropping the "change" part. The fix generates
+ * the correct name, "TYPE_CHANGE_FIELD_VISIBILITY", but also removes the
+ * previous (incorrect) names, which is a BC break.
*
- * This simple script just adds the old constant names back, alongside the new
- * ones.
+ * To preserve source compatibility this script adds the old names back
+ * as aliases (FieldVisibility = ChangeFieldVisibility, etc.). The aliases
+ * share a numeric value with their canonical counterparts, which breaks
+ * StringEnumConverter: Enum.GetName is not guaranteed to pick the
+ * canonical name for ties, and Newtonsoft rejects duplicate
+ * [EnumMember] values on the same enum. To make the aliases serialize
+ * to the correct OpenAPI wire values under all circumstances, this
+ * script also:
+ * - swaps StringEnumConverter for a dedicated TypeEnumJsonConverter
+ * that maps by underlying numeric value, and
+ * - injects that nested converter class into SubFormFieldRuleAction.
*/
class CopyConstants
{
@@ -32,6 +41,57 @@ public function run(): void
$file = __DIR__ . '/../src/Dropbox.Sign/Model/SubFormFieldRuleAction.cs';
$contents = file_get_contents($file);
+ $contents = $this->addAliases($contents);
+ $contents = $this->swapConverter($contents);
+ $contents = $this->injectConverterClass($contents);
+
+ file_put_contents($file, $contents);
+
+ $this->stopEmittingDefaultIntValues(__DIR__ . '/../src/Dropbox.Sign/Model');
+ }
+
+ /**
+ * For optional non-nullable int properties on request-side models
+ * openapi-generator emits
+ * [DataMember(Name = "foo", EmitDefaultValue = true)]
+ * public int Foo { get; set; }
+ * which forces `"foo": 0` onto the wire whenever the caller never
+ * set a value (C# default for int is 0). For fields like font_size
+ * the server rejects the default with a 400 ("'font_size' must be
+ * between 7 and 49"), and more generally any optional int whose
+ * server-side default is not 0 is misrepresented.
+ *
+ * Scope:
+ * - Only request-side models are patched. Response models keep
+ * EmitDefaultValue = true so that round-trip serialization
+ * preserves explicit zero values that the server sent (tests
+ * under Dropbox.Sign.Test.Api rely on this fidelity). Response
+ * models are identified by the "Response" substring in the
+ * file name, which is the convention for all generated
+ * response types in this SDK.
+ * - Required int properties are left alone: openapi-generator
+ * inserts `IsRequired = true,` between the Name and
+ * EmitDefaultValue tokens, which this regex does not match.
+ */
+ private function stopEmittingDefaultIntValues(string $modelDir): void
+ {
+ $pattern = '/(\[DataMember\(Name = "[^"]+", EmitDefaultValue = )true(\)\]\s*\r?\n\s+public int [A-Za-z_][A-Za-z0-9_]* \{ get; set; \})/';
+
+ foreach (glob($modelDir . '/*.cs') as $path) {
+ if (strpos(basename($path), 'Response') !== false) {
+ continue;
+ }
+
+ $contents = file_get_contents($path);
+ $patched = preg_replace($pattern, '$1false$2', $contents);
+ if ($patched !== null && $patched !== $contents) {
+ file_put_contents($path, $patched);
+ }
+ }
+ }
+
+ private function addAliases(string $contents): string
+ {
$constant_1 = " ChangeFieldVisibility = 1,";
$replace_1 = implode("\n", [
$constant_1,
@@ -44,21 +104,99 @@ public function run(): void
' GroupVisibility = ChangeGroupVisibility',
]);
- $contents = str_replace(
- $constant_1,
- $replace_1,
- $contents,
- );
+ $contents = str_replace($constant_1, $replace_1, $contents);
+ $contents = str_replace($constant_2, $replace_2, $contents);
- $contents = str_replace(
- $constant_2,
- $replace_2,
- $contents,
- );
+ return $contents;
+ }
- file_put_contents($file, $contents);
+ private function swapConverter(string $contents): string
+ {
+ $needle = " [JsonConverter(typeof(StringEnumConverter))]\n"
+ . " public enum TypeEnum";
+
+ $replacement = <<<'CS'
+ // A dedicated converter is used instead of StringEnumConverter
+ // because this enum intentionally carries alias members
+ // (FieldVisibility, GroupVisibility) that share a numeric value
+ // with their canonical counterparts. StringEnumConverter goes
+ // through Enum.GetName, which is not guaranteed to pick the
+ // canonical name when multiple members share a value, and
+ // Newtonsoft rejects duplicate [EnumMember] values on the same
+ // enum. Mapping by numeric value here is unambiguous: both the
+ // canonical name and its alias produce the same wire string.
+ [JsonConverter(typeof(TypeEnumJsonConverter))]
+ public enum TypeEnum
+CS;
+
+ return str_replace($needle, $replacement, $contents);
+ }
+
+ private function injectConverterClass(string $contents): string
+ {
+ // Inject the nested converter immediately after the enum's
+ // closing brace. Anchor on the enum's tail, which is unique in
+ // the file.
+ $needle = " GroupVisibility = ChangeGroupVisibility\n"
+ . " }\n";
+
+ $converter = <<<'CS'
+ GroupVisibility = ChangeGroupVisibility
+ }
+
+ ///
+ /// Serializes SubFormFieldRuleAction.TypeEnum to and from the
+ /// OpenAPI wire values (change-field-visibility,
+ /// change-group-visibility). The switch is driven by the
+ /// underlying numeric value so that legacy alias members
+ /// (FieldVisibility, GroupVisibility) serialize identically to
+ /// their canonical counterparts.
+ ///
+ public class TypeEnumJsonConverter : JsonConverter
+ {
+ public override void WriteJson(JsonWriter writer, TypeEnum value, JsonSerializer serializer)
+ {
+ switch (value)
+ {
+ case TypeEnum.ChangeFieldVisibility:
+ writer.WriteValue("change-field-visibility");
+ return;
+ case TypeEnum.ChangeGroupVisibility:
+ writer.WriteValue("change-group-visibility");
+ return;
+ default:
+ throw new JsonSerializationException(
+ $"Unknown value for SubFormFieldRuleAction.TypeEnum: {(int)value}");
+ }
+ }
+
+ public override TypeEnum ReadJson(JsonReader reader, Type objectType, TypeEnum existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.Null)
+ {
+ throw new JsonSerializationException(
+ "Cannot deserialize null into SubFormFieldRuleAction.TypeEnum");
+ }
+
+ var raw = reader.Value?.ToString();
+ switch (raw)
+ {
+ case "change-field-visibility":
+ return TypeEnum.ChangeFieldVisibility;
+ case "change-group-visibility":
+ return TypeEnum.ChangeGroupVisibility;
+ default:
+ throw new JsonSerializationException(
+ $"Unknown wire value for SubFormFieldRuleAction.TypeEnum: {raw}");
+ }
+ }
+ }
+
+CS;
+
+ return str_replace($needle, $converter, $contents);
}
}
$copier = new CopyConstants();
-$copier->run();
\ No newline at end of file
+$copier->run();
diff --git a/sdks/dotnet/src/Dropbox.Sign.Test/Model/SubFormFieldRuleActionTests.cs b/sdks/dotnet/src/Dropbox.Sign.Test/Model/SubFormFieldRuleActionTests.cs
new file mode 100644
index 000000000..6e5a65cd6
--- /dev/null
+++ b/sdks/dotnet/src/Dropbox.Sign.Test/Model/SubFormFieldRuleActionTests.cs
@@ -0,0 +1,84 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Xunit;
+using Dropbox.Sign.Model;
+
+namespace Dropbox.Sign.Test.Model
+{
+ public class SubFormFieldRuleActionTests
+ {
+ // SubFormFieldRuleAction.TypeEnum must serialize to the exact
+ // wire values accepted by the Dropbox Sign API
+ // (change-field-visibility, change-group-visibility). The enum
+ // intentionally carries legacy alias members (FieldVisibility,
+ // GroupVisibility) that share a numeric value with the canonical
+ // members, so at runtime they are indistinguishable (xUnit also
+ // dedups the alias rows because [InlineData] compares enums by
+ // their underlying int). The dedicated TypeEnumJsonConverter
+ // maps by underlying value so both names produce the same wire
+ // string; pinning the canonical members is therefore sufficient.
+ [Theory]
+ [InlineData(SubFormFieldRuleAction.TypeEnum.ChangeFieldVisibility, "change-field-visibility")]
+ [InlineData(SubFormFieldRuleAction.TypeEnum.ChangeGroupVisibility, "change-group-visibility")]
+ public void TypeEnum_Serializes_To_EnumMember_Value(
+ SubFormFieldRuleAction.TypeEnum value,
+ string expected)
+ {
+ var json = JsonConvert.SerializeObject(value);
+
+ Assert.Equal($"\"{expected}\"", json);
+ }
+
+ [Theory]
+ [InlineData(SubFormFieldRuleAction.TypeEnum.ChangeFieldVisibility, "change-field-visibility")]
+ [InlineData(SubFormFieldRuleAction.TypeEnum.ChangeGroupVisibility, "change-group-visibility")]
+ public void Action_Payload_Uses_EnumMember_Value_For_Type(
+ SubFormFieldRuleAction.TypeEnum value,
+ string expected)
+ {
+ var action = new SubFormFieldRuleAction(
+ fieldId: "api_id_2",
+ hidden: true,
+ type: value
+ );
+
+ var json = JObject.Parse(action.ToJson());
+
+ Assert.Equal(expected, (string)json["type"]);
+ }
+
+ [Theory]
+ [InlineData("change-field-visibility", SubFormFieldRuleAction.TypeEnum.ChangeFieldVisibility)]
+ [InlineData("change-group-visibility", SubFormFieldRuleAction.TypeEnum.ChangeGroupVisibility)]
+ public void Action_Payload_Deserializes_EnumMember_Value_For_Type(
+ string wireValue,
+ SubFormFieldRuleAction.TypeEnum expected)
+ {
+ var payload = new JObject(
+ new JProperty("field_id", "api_id_2"),
+ new JProperty("hidden", true),
+ new JProperty("type", wireValue)
+ ).ToString();
+
+ var action = SubFormFieldRuleAction.Init(payload);
+
+ Assert.Equal(expected, action.Type);
+ }
+
+ [Theory]
+ [InlineData("FieldVisibility")]
+ [InlineData("GroupVisibility")]
+ [InlineData("change-unknown-thing")]
+ [InlineData("")]
+ public void Action_Payload_Rejects_Unknown_Type_Values(string wireValue)
+ {
+ var payload = new JObject(
+ new JProperty("field_id", "api_id_2"),
+ new JProperty("hidden", true),
+ new JProperty("type", wireValue)
+ ).ToString();
+
+ Assert.ThrowsAny(() => SubFormFieldRuleAction.Init(payload));
+ }
+ }
+}
diff --git a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldRuleAction.cs b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldRuleAction.cs
index b6af36b3d..34634d199 100644
--- a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldRuleAction.cs
+++ b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldRuleAction.cs
@@ -36,7 +36,16 @@ public partial class SubFormFieldRuleAction : IEquatable
///
/// Defines Type
///
- [JsonConverter(typeof(StringEnumConverter))]
+ // A dedicated converter is used instead of StringEnumConverter
+ // because this enum intentionally carries alias members
+ // (FieldVisibility, GroupVisibility) that share a numeric value
+ // with their canonical counterparts. StringEnumConverter goes
+ // through Enum.GetName, which is not guaranteed to pick the
+ // canonical name when multiple members share a value, and
+ // Newtonsoft rejects duplicate [EnumMember] values on the same
+ // enum. Mapping by numeric value here is unambiguous: both the
+ // canonical name and its alias produce the same wire string.
+ [JsonConverter(typeof(TypeEnumJsonConverter))]
public enum TypeEnum
{
///
@@ -54,6 +63,54 @@ public enum TypeEnum
GroupVisibility = ChangeGroupVisibility
}
+ ///
+ /// Serializes SubFormFieldRuleAction.TypeEnum to and from the
+ /// OpenAPI wire values (change-field-visibility,
+ /// change-group-visibility). The switch is driven by the
+ /// underlying numeric value so that legacy alias members
+ /// (FieldVisibility, GroupVisibility) serialize identically to
+ /// their canonical counterparts.
+ ///
+ public class TypeEnumJsonConverter : JsonConverter
+ {
+ public override void WriteJson(JsonWriter writer, TypeEnum value, JsonSerializer serializer)
+ {
+ switch (value)
+ {
+ case TypeEnum.ChangeFieldVisibility:
+ writer.WriteValue("change-field-visibility");
+ return;
+ case TypeEnum.ChangeGroupVisibility:
+ writer.WriteValue("change-group-visibility");
+ return;
+ default:
+ throw new JsonSerializationException(
+ $"Unknown value for SubFormFieldRuleAction.TypeEnum: {(int)value}");
+ }
+ }
+
+ public override TypeEnum ReadJson(JsonReader reader, Type objectType, TypeEnum existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.Null)
+ {
+ throw new JsonSerializationException(
+ "Cannot deserialize null into SubFormFieldRuleAction.TypeEnum");
+ }
+
+ var raw = reader.Value?.ToString();
+ switch (raw)
+ {
+ case "change-field-visibility":
+ return TypeEnum.ChangeFieldVisibility;
+ case "change-group-visibility":
+ return TypeEnum.ChangeGroupVisibility;
+ default:
+ throw new JsonSerializationException(
+ $"Unknown wire value for SubFormFieldRuleAction.TypeEnum: {raw}");
+ }
+ }
+ }
+
///
/// Gets or Sets Type
diff --git a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentDateSigned.cs b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentDateSigned.cs
index 485c02d9a..83cfccf2e 100644
--- a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentDateSigned.cs
+++ b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentDateSigned.cs
@@ -215,7 +215,7 @@ public static SubFormFieldsPerDocumentDateSigned Init(string jsonData)
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
///
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
- [DataMember(Name = "font_size", EmitDefaultValue = true)]
+ [DataMember(Name = "font_size", EmitDefaultValue = false)]
public int FontSize { get; set; }
///
diff --git a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentDropdown.cs b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentDropdown.cs
index 467103717..91869c0dc 100644
--- a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentDropdown.cs
+++ b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentDropdown.cs
@@ -238,7 +238,7 @@ public static SubFormFieldsPerDocumentDropdown Init(string jsonData)
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
///
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
- [DataMember(Name = "font_size", EmitDefaultValue = true)]
+ [DataMember(Name = "font_size", EmitDefaultValue = false)]
public int FontSize { get; set; }
///
diff --git a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentHyperlink.cs b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentHyperlink.cs
index d766865ca..7ab930ec9 100644
--- a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentHyperlink.cs
+++ b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentHyperlink.cs
@@ -243,7 +243,7 @@ public static SubFormFieldsPerDocumentHyperlink Init(string jsonData)
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
///
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
- [DataMember(Name = "font_size", EmitDefaultValue = true)]
+ [DataMember(Name = "font_size", EmitDefaultValue = false)]
public int FontSize { get; set; }
///
diff --git a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentText.cs b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentText.cs
index c514d0f50..429b9c711 100644
--- a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentText.cs
+++ b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentText.cs
@@ -353,7 +353,7 @@ public static SubFormFieldsPerDocumentText Init(string jsonData)
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
///
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
- [DataMember(Name = "font_size", EmitDefaultValue = true)]
+ [DataMember(Name = "font_size", EmitDefaultValue = false)]
public int FontSize { get; set; }
///
diff --git a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentTextMerge.cs b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentTextMerge.cs
index d2d0c6412..ac96ff2cc 100644
--- a/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentTextMerge.cs
+++ b/sdks/dotnet/src/Dropbox.Sign/Model/SubFormFieldsPerDocumentTextMerge.cs
@@ -215,7 +215,7 @@ public static SubFormFieldsPerDocumentTextMerge Init(string jsonData)
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
///
/// The initial px font size for the field contents. Can be any integer value between `7` and `49`. **NOTE:** Font size may be reduced during processing in order to fit the contents within the dimensions of the field.
- [DataMember(Name = "font_size", EmitDefaultValue = true)]
+ [DataMember(Name = "font_size", EmitDefaultValue = false)]
public int FontSize { get; set; }
///