Skip to content

Commit 0a3727d

Browse files
fix: Serialize ConsoleSize as JSON array to match Docker API format (#67)
Co-authored-by: Andre Hofmeister <[email protected]>
1 parent d2217b8 commit 0a3727d

9 files changed

+200
-3
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
namespace Docker.DotNet;
2+
3+
internal sealed class ConsoleSizeConverter : JsonConverter<ConsoleSize?>
4+
{
5+
public override ConsoleSize? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
6+
{
7+
if (reader.TokenType == JsonTokenType.Null)
8+
{
9+
return null;
10+
}
11+
12+
if (reader.TokenType != JsonTokenType.StartArray)
13+
{
14+
throw new JsonException("Expected a JSON array for ConsoleSize.");
15+
}
16+
17+
var height = ReadArrayElement(ref reader, "height");
18+
19+
var width = ReadArrayElement(ref reader, "width");
20+
21+
if (!reader.Read())
22+
{
23+
throw new JsonException("Expected the ConsoleSize array to end after two numeric elements.");
24+
}
25+
26+
if (reader.TokenType != JsonTokenType.EndArray)
27+
{
28+
throw new JsonException("Expected the ConsoleSize array to contain exactly two numeric elements.");
29+
}
30+
31+
return new ConsoleSize
32+
{
33+
Height = height,
34+
Width = width
35+
};
36+
}
37+
38+
private static ulong ReadArrayElement(ref Utf8JsonReader reader, string elementName)
39+
{
40+
if (!reader.Read())
41+
{
42+
throw new JsonException($"Expected a numeric '{elementName}' element in the ConsoleSize array.");
43+
}
44+
45+
if (reader.TokenType != JsonTokenType.Number)
46+
{
47+
throw new JsonException($"Expected the ConsoleSize '{elementName}' element to be a number.");
48+
}
49+
50+
return reader.GetUInt64();
51+
}
52+
53+
public override void Write(Utf8JsonWriter writer, ConsoleSize? value, JsonSerializerOptions options)
54+
{
55+
if (value == null)
56+
{
57+
writer.WriteNullValue();
58+
return;
59+
}
60+
61+
writer.WriteStartArray();
62+
writer.WriteNumberValue(value.Height);
63+
writer.WriteNumberValue(value.Width);
64+
writer.WriteEndArray();
65+
}
66+
}

src/Docker.DotNet/Endpoints/DockerSwarmNodeAlreadyParticipatingException.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Docker.DotNet;
1+
namespace Docker.DotNet;
22

33
public class DockerSwarmNodeAlreadyParticipatingException : DockerApiException
44
{

src/Docker.DotNet/Endpoints/DockerSwarmNodeNotParticipatingException.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Docker.DotNet;
1+
namespace Docker.DotNet;
22

33
public class DockerSwarmNodeNotParticipatingException : DockerApiException
44
{

src/Docker.DotNet/Models/ContainerExecCreateParameters.Generated.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class ContainerExecCreateParameters // (main.ContainerExecCreateParameter
1212
public bool TTY { get; set; }
1313

1414
[JsonPropertyName("ConsoleSize")]
15+
[JsonConverter(typeof(ConsoleSizeConverter))]
1516
public ConsoleSize ConsoleSize { get; set; }
1617

1718
[JsonPropertyName("AttachStdin")]

src/Docker.DotNet/Models/ContainerExecStartParameters.Generated.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class ContainerExecStartParameters // (main.ContainerExecStartParameters)
99
public bool TTY { get; set; }
1010

1111
[JsonPropertyName("ConsoleSize")]
12+
[JsonConverter(typeof(ConsoleSizeConverter))]
1213
public ConsoleSize ConsoleSize { get; set; }
1314
}
1415
}

src/Docker.DotNet/Models/HostConfig.Generated.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public HostConfig(Resources Resources)
7070
public IList<string> VolumesFrom { get; set; }
7171

7272
[JsonPropertyName("ConsoleSize")]
73-
public ulong[] ConsoleSize { get; set; }
73+
[JsonConverter(typeof(ConsoleSizeConverter))]
74+
public ConsoleSize ConsoleSize { get; set; }
7475

7576
[JsonPropertyName("Annotations")]
7677
public IDictionary<string, string> Annotations { get; set; }
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
namespace Docker.DotNet.Tests;
2+
3+
public sealed class ConsoleSizeConverterTests
4+
{
5+
private const ulong Height = 24;
6+
7+
private const ulong Width = 80;
8+
9+
private const string ConsoleSizeArrayFragment = "\"ConsoleSize\":[24,80]";
10+
11+
private const string ConsoleSizeJson = "{\"ConsoleSize\":[24,80]}";
12+
13+
[Fact]
14+
public void Serialize_StandaloneConsoleSize_ProducesJsonObject()
15+
{
16+
var consoleSize = new ConsoleSize { Height = Height, Width = Width };
17+
18+
var jsonString = JsonSerializer.Instance.Serialize(consoleSize);
19+
20+
Assert.Equal("{\"Height\":24,\"Width\":80}", jsonString);
21+
}
22+
23+
[Fact]
24+
public void Serialize_ContainerExecCreateParameters_WithConsoleSizeProperty_ProducesArrayFormat()
25+
{
26+
var parameters = new ContainerExecCreateParameters
27+
{
28+
ConsoleSize = new ConsoleSize { Height = Height, Width = Width },
29+
};
30+
31+
var jsonString = JsonSerializer.Instance.Serialize(parameters);
32+
33+
Assert.Contains(ConsoleSizeArrayFragment, jsonString);
34+
}
35+
36+
[Fact]
37+
public void Deserialize_ContainerExecCreateParameters_WithArrayConsoleSize_Succeeds()
38+
{
39+
var parameters = JsonSerializer.Instance.Deserialize<ContainerExecCreateParameters>(Encoding.UTF8.GetBytes(ConsoleSizeJson));
40+
Assert.NotNull(parameters);
41+
Assert.NotNull(parameters.ConsoleSize);
42+
Assert.Equal(Height, parameters.ConsoleSize.Height);
43+
Assert.Equal(Width, parameters.ConsoleSize.Width);
44+
}
45+
46+
[Fact]
47+
public void Serialize_ContainerExecStartParameters_WithConsoleSizeProperty_ProducesArrayFormat()
48+
{
49+
var parameters = new ContainerExecStartParameters
50+
{
51+
ConsoleSize = new ConsoleSize { Height = Height, Width = Width }
52+
};
53+
54+
var jsonString = JsonSerializer.Instance.Serialize(parameters);
55+
56+
Assert.Contains(ConsoleSizeArrayFragment, jsonString);
57+
}
58+
59+
[Fact]
60+
public void Deserialize_ContainerExecStartParameters_WithArrayConsoleSize_Succeeds()
61+
{
62+
var parameters = JsonSerializer.Instance.Deserialize<ContainerExecStartParameters>(Encoding.UTF8.GetBytes(ConsoleSizeJson));
63+
Assert.NotNull(parameters);
64+
Assert.NotNull(parameters.ConsoleSize);
65+
Assert.Equal(Height, parameters.ConsoleSize.Height);
66+
Assert.Equal(Width, parameters.ConsoleSize.Width);
67+
}
68+
69+
[Fact]
70+
public void Serialize_HostConfig_WithConsoleSizeProperty_ProducesArrayFormat()
71+
{
72+
var hostConfig = new HostConfig
73+
{
74+
ConsoleSize = new ConsoleSize { Height = Height, Width = Width }
75+
};
76+
77+
var jsonString = JsonSerializer.Instance.Serialize(hostConfig);
78+
79+
Assert.Contains(ConsoleSizeArrayFragment, jsonString);
80+
}
81+
82+
[Fact]
83+
public void Deserialize_HostConfig_WithArrayConsoleSize_Succeeds()
84+
{
85+
var hostConfig = JsonSerializer.Instance.Deserialize<HostConfig>(Encoding.UTF8.GetBytes(ConsoleSizeJson));
86+
Assert.NotNull(hostConfig);
87+
Assert.NotNull(hostConfig.ConsoleSize);
88+
Assert.Equal(Height, hostConfig.ConsoleSize.Height);
89+
Assert.Equal(Width, hostConfig.ConsoleSize.Width);
90+
}
91+
92+
[Theory]
93+
[InlineData("{\"ConsoleSize\":[24]}")]
94+
[InlineData("{\"ConsoleSize\":[24,80,1]}")]
95+
[InlineData("{\"ConsoleSize\":[\"24\",80]}")]
96+
public void Deserialize_ContainerExecCreateParameters_WithInvalidArrayConsoleSize_ThrowsJsonException(string jsonString)
97+
{
98+
Assert.Throws<JsonException>(() => JsonSerializer.Instance.Deserialize<ContainerExecCreateParameters>(Encoding.UTF8.GetBytes(jsonString)));
99+
}
100+
}

test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<Using Include="System.Reflection" />
2828
<Using Include="System.Security.Cryptography.X509Certificates" />
2929
<Using Include="System.Text" />
30+
<Using Include="System.Text.Json" />
3031
<Using Include="System.Threading" />
3132
<Using Include="System.Threading.Tasks" />
3233
<Using Include="Docker.DotNet.Handler.Abstractions" />

tools/specgen/specgen.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ var typesToDisambiguate = map[string]*CSModelType{
5555
},
5656
},
5757
},
58+
typeToKey(reflect.TypeOf(container.HostConfig{})): {
59+
Properties: []CSProperty{
60+
{
61+
Name: "ConsoleSize",
62+
Type: CSType{"", "ConsoleSize", true},
63+
Attributes: []CSAttribute{{Type: CSType{"System.Text.Json.Serialization", "JsonConverter", false}, Arguments: []CSArgument{{Value: "typeof(ConsoleSizeConverter)"}}}},
64+
},
65+
},
66+
},
5867
typeToKey(reflect.TypeOf(container.CreateResponse{})): {Name: "CreateContainerResponse"},
5968
typeToKey(reflect.TypeOf(container.HealthConfig{})): {
6069
Properties: []CSProperty{
@@ -101,6 +110,24 @@ var typesToDisambiguate = map[string]*CSModelType{
101110
},
102111
},
103112
},
113+
typeToKey(reflect.TypeOf(ContainerExecCreateParameters{})): {
114+
Properties: []CSProperty{
115+
{
116+
Name: "ConsoleSize",
117+
Type: CSType{"", "ConsoleSize", true},
118+
Attributes: []CSAttribute{{Type: CSType{"System.Text.Json.Serialization", "JsonConverter", false}, Arguments: []CSArgument{{Value: "typeof(ConsoleSizeConverter)"}}}},
119+
},
120+
},
121+
},
122+
typeToKey(reflect.TypeOf(ContainerExecStartParameters{})): {
123+
Properties: []CSProperty{
124+
{
125+
Name: "ConsoleSize",
126+
Type: CSType{"", "ConsoleSize", true},
127+
Attributes: []CSAttribute{{Type: CSType{"System.Text.Json.Serialization", "JsonConverter", false}, Arguments: []CSArgument{{Value: "typeof(ConsoleSizeConverter)"}}}},
128+
},
129+
},
130+
},
104131
typeToKey(reflect.TypeOf(volume.AccessMode{})): {Name: "VolumeAccessMode"},
105132
typeToKey(reflect.TypeOf(volume.Info{})): {Name: "VolumeInfo"},
106133
typeToKey(reflect.TypeOf(volume.Secret{})): {Name: "VolumeSecret"},

0 commit comments

Comments
 (0)