Skip to content

Commit 57cd52d

Browse files
committed
Add remaining SDKs support
1 parent 338b5a6 commit 57cd52d

File tree

20 files changed

+839
-403
lines changed

20 files changed

+839
-403
lines changed

examples/csharp/src/MessageHeaders/Iggy_SDK.Examples.MessageHeaders.Consumer/Utils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public static async Task ConsumeMessages(IIggyClient client, ILogger logger)
8989

9090
private static void HandleMessage(MessageResponse message, ILogger logger)
9191
{
92-
var headerKey = HeaderKey.New("message_type");
92+
var headerKey = HeaderKey.FromString("message_type");
9393
var headersMap = message.UserHeaders ?? throw new Exception("Missing headers map.");
9494
var messageType = Encoding.UTF8.GetString(headersMap[headerKey].Value);
9595

examples/csharp/src/MessageHeaders/Iggy_SDK.Examples.MessageHeaders.Producer/Utils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public static async Task ProduceMessages(IIggyClient client, ILogger logger)
106106
return new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(jsonEnvelope),
107107
new Dictionary<HeaderKey, HeaderValue>
108108
{
109-
{ HeaderKey.New("message_type"), HeaderValue.FromString(serializableMessage.MessageType) }
109+
{ HeaderKey.FromString("message_type"), HeaderValue.FromString(serializableMessage.MessageType) }
110110
});
111111
}
112112
).ToList();

foreign/csharp/Iggy_SDK.Tests.Integration/FetchMessagesTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ public async Task PollMessages_WithHeaders_Should_PollMessages_Successfully(Prot
105105
{
106106
responseMessage.UserHeaders.ShouldNotBeNull();
107107
responseMessage.UserHeaders.Count.ShouldBe(2);
108-
responseMessage.UserHeaders[HeaderKey.New("header1")].ToString().ShouldBe("value1");
109-
responseMessage.UserHeaders[HeaderKey.New("header2")].ToInt32().ShouldBeGreaterThan(0);
108+
responseMessage.UserHeaders[HeaderKey.FromString("header1")].ToString().ShouldBe("value1");
109+
responseMessage.UserHeaders[HeaderKey.FromString("header2")].ToInt32().ShouldBeGreaterThan(0);
110110
}
111111
}
112112
}

foreign/csharp/Iggy_SDK.Tests.Integration/Fixtures/FetchMessagesFixture.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ private static Message[] CreateMessagesWithHeader(int count)
9595
messages.Add(new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson),
9696
new Dictionary<HeaderKey, HeaderValue>
9797
{
98-
{ HeaderKey.New("header1"), HeaderValue.FromString("value1") },
99-
{ HeaderKey.New("header2"), HeaderValue.FromInt32(14 + i) }
98+
{ HeaderKey.FromString("header1"), HeaderValue.FromString("value1") },
99+
{ HeaderKey.FromString("header2"), HeaderValue.FromInt32(14 + i) }
100100
}));
101101
}
102102

foreign/csharp/Iggy_SDK.Tests.Integration/SendMessagesTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ public static Task Before()
5959
new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson),
6060
new Dictionary<HeaderKey, HeaderValue>
6161
{
62-
{ HeaderKey.New("header1"), HeaderValue.FromString("value1") },
63-
{ HeaderKey.New("header2"), HeaderValue.FromInt32(444) }
62+
{ HeaderKey.FromString("header1"), HeaderValue.FromString("value1") },
63+
{ HeaderKey.FromString("header2"), HeaderValue.FromInt32(444) }
6464
}),
6565
new Message(Guid.NewGuid(), Encoding.UTF8.GetBytes(dummyJson),
6666
new Dictionary<HeaderKey, HeaderValue>
6767
{
68-
{ HeaderKey.New("header1"), HeaderValue.FromString("value1") },
69-
{ HeaderKey.New("header2"), HeaderValue.FromInt32(444) }
68+
{ HeaderKey.FromString("header1"), HeaderValue.FromString("value1") },
69+
{ HeaderKey.FromString("header2"), HeaderValue.FromInt32(444) }
7070
})
7171
];
7272

foreign/csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ private static byte[] GetHeadersBytes(Dictionary<HeaderKey, HeaderValue>? header
554554
return [];
555555
}
556556

557-
var headersLength = headers.Sum(header => 4 + header.Key.Value.Length + 1 + 4 + header.Value.Value.Length);
557+
var headersLength = headers.Sum(header => 1 + 4 + header.Key.Value.Length + 1 + 4 + header.Value.Value.Length);
558558
Span<byte> headersBytes = stackalloc byte[headersLength];
559559
var position = 0;
560560
foreach (var (headerKey, headerValue) in headers)
@@ -589,19 +589,22 @@ private static byte HeaderKindToByte(HeaderKind kind)
589589

590590
private static byte[] GetBytesFromHeader(HeaderKey headerKey, HeaderValue headerValue)
591591
{
592-
var headerBytesLength = 4 + headerKey.Value.Length + 1 + 4 + headerValue.Value.Length;
592+
var headerBytesLength = 1 + 4 + headerKey.Value.Length + 1 + 4 + headerValue.Value.Length;
593593
Span<byte> headerBytes = stackalloc byte[headerBytesLength];
594+
var pos = 0;
594595

595-
BinaryPrimitives.WriteInt32LittleEndian(headerBytes[..4], headerKey.Value.Length);
596-
var headerKeyBytes = Encoding.UTF8.GetBytes(headerKey.Value);
597-
headerKeyBytes.CopyTo(headerBytes[4..(4 + headerKey.Value.Length)]);
596+
headerBytes[pos++] = HeaderKindToByte(headerKey.Kind);
598597

599-
headerBytes[4 + headerKey.Value.Length] = HeaderKindToByte(headerValue.Kind);
598+
BinaryPrimitives.WriteInt32LittleEndian(headerBytes[pos..(pos + 4)], headerKey.Value.Length);
599+
pos += 4;
600+
headerKey.Value.CopyTo(headerBytes[pos..(pos + headerKey.Value.Length)]);
601+
pos += headerKey.Value.Length;
600602

601-
BinaryPrimitives.WriteInt32LittleEndian(
602-
headerBytes[(4 + headerKey.Value.Length + 1)..(4 + headerKey.Value.Length + 1 + 4)],
603-
headerValue.Value.Length);
604-
headerValue.Value.CopyTo(headerBytes[(4 + headerKey.Value.Length + 1 + 4)..]);
603+
headerBytes[pos++] = HeaderKindToByte(headerValue.Kind);
604+
605+
BinaryPrimitives.WriteInt32LittleEndian(headerBytes[pos..(pos + 4)], headerValue.Value.Length);
606+
pos += 4;
607+
headerValue.Value.CopyTo(headerBytes[pos..]);
605608

606609
return headerBytes.ToArray();
607610
}

foreign/csharp/Iggy_SDK/Headers/HeaderKey.cs

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,48 +15,73 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
using System.Text;
1819
using System.Text.Json.Serialization;
1920
using Apache.Iggy.JsonConverters;
2021

2122
namespace Apache.Iggy.Headers;
2223

2324
/// <summary>
24-
/// A key for a header.
25+
/// Represents a message header key with a kind and binary value.
2526
/// </summary>
2627
[JsonConverter(typeof(HeaderKeyConverter))]
2728
public readonly struct HeaderKey : IEquatable<HeaderKey>
2829
{
2930
/// <summary>
30-
/// Header key value.
31+
/// The kind of the header key.
3132
/// </summary>
32-
public required string Value { get; init; }
33+
public required HeaderKind Kind { get; init; }
3334

3435
/// <summary>
35-
/// Creates a new header key from a string.
36+
/// The binary value of the header key.
3637
/// </summary>
37-
/// <param name="val">Key value</param>
38-
/// <returns></returns>
39-
/// <exception cref="ArgumentException"></exception>
40-
public static HeaderKey New(string val)
38+
public required byte[] Value { get; init; }
39+
40+
/// <summary>
41+
/// Creates a HeaderKey from a string value.
42+
/// </summary>
43+
/// <param name="val">The string value (must be 1-255 characters).</param>
44+
/// <returns>A new HeaderKey with String kind.</returns>
45+
/// <exception cref="ArgumentException">Thrown when value length is invalid.</exception>
46+
public static HeaderKey FromString(string val)
4147
{
48+
if (val.Length is 0 or > 255)
49+
{
50+
throw new ArgumentException("Value has incorrect size, must be between 1 and 255", nameof(val));
51+
}
52+
4253
return new HeaderKey
4354
{
44-
Value = val.Length is 0 or > 255
45-
? throw new ArgumentException("Value has incorrect size, must be between 1 and 255", nameof(val))
46-
: val
55+
Kind = HeaderKind.String,
56+
Value = Encoding.UTF8.GetBytes(val)
4757
};
4858
}
4959

60+
/// <summary>
61+
/// Gets the value as a UTF-8 string.
62+
/// </summary>
63+
/// <returns>The string value.</returns>
64+
/// <exception cref="InvalidOperationException">Thrown when kind is not String.</exception>
65+
public string AsString()
66+
{
67+
if (Kind is not HeaderKind.String)
68+
{
69+
throw new InvalidOperationException("HeaderKey is not of String kind");
70+
}
71+
72+
return Encoding.UTF8.GetString(Value);
73+
}
74+
5075
/// <inheritdoc />
5176
public override string ToString()
5277
{
53-
return Value;
78+
return Kind == HeaderKind.String ? Encoding.UTF8.GetString(Value) : Convert.ToBase64String(Value);
5479
}
5580

5681
/// <inheritdoc />
5782
public bool Equals(HeaderKey other)
5883
{
59-
return StringComparer.Ordinal.Equals(Value, other.Value);
84+
return Kind == other.Kind && Value.SequenceEqual(other.Value);
6085
}
6186

6287
/// <inheritdoc />
@@ -68,26 +93,27 @@ public override bool Equals(object? obj)
6893
/// <inheritdoc />
6994
public override int GetHashCode()
7095
{
71-
return StringComparer.Ordinal.GetHashCode(Value);
96+
var hash = new HashCode();
97+
hash.Add(Kind);
98+
foreach (var b in Value)
99+
{
100+
hash.Add(b);
101+
}
102+
103+
return hash.ToHashCode();
72104
}
73105

74106
/// <summary>
75-
/// Determines whether two specified <see cref="HeaderKey" /> objects are equal.
107+
/// Determines whether two HeaderKey instances are equal.
76108
/// </summary>
77-
/// <param name="left">The first <see cref="HeaderKey" /> to compare.</param>
78-
/// <param name="right">The second <see cref="HeaderKey" /> to compare.</param>
79-
/// <returns>True if the two <see cref="HeaderKey" /> objects are equal; otherwise, false.</returns>
80109
public static bool operator ==(HeaderKey left, HeaderKey right)
81110
{
82111
return left.Equals(right);
83112
}
84113

85114
/// <summary>
86-
/// Determines whether two specified <see cref="HeaderKey" /> objects are not equal.
115+
/// Determines whether two HeaderKey instances are not equal.
87116
/// </summary>
88-
/// <param name="left">The first <see cref="HeaderKey" /> to compare.</param>
89-
/// <param name="right">The second <see cref="HeaderKey" /> to compare.</param>
90-
/// <returns>True if the two <see cref="HeaderKey" /> objects are not equal; otherwise, false.</returns>
91117
public static bool operator !=(HeaderKey left, HeaderKey right)
92118
{
93119
return !left.Equals(right);

foreign/csharp/Iggy_SDK/Iggy_SDK.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
88
<AssemblyName>Apache.Iggy</AssemblyName>
99
<RootNamespace>Apache.Iggy</RootNamespace>
10-
<PackageVersion>0.6.1-edge.1</PackageVersion>
10+
<PackageVersion>0.6.2-edge.1</PackageVersion>
1111
<GenerateDocumentationFile>true</GenerateDocumentationFile>
1212
</PropertyGroup>
1313

foreign/csharp/Iggy_SDK/JsonConverters/HeaderKeyConverter.cs

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,95 @@ internal class HeaderKeyConverter : JsonConverter<HeaderKey>
2525
{
2626
public override HeaderKey Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
2727
{
28-
return HeaderKey.New(reader.GetString() ?? throw new JsonException("Header key cannot be null or empty."));
28+
if (reader.TokenType != JsonTokenType.StartObject)
29+
{
30+
throw new JsonException("Expected start of object for HeaderKey.");
31+
}
32+
33+
HeaderKind? kind = null;
34+
byte[]? value = null;
35+
36+
while (reader.Read())
37+
{
38+
if (reader.TokenType == JsonTokenType.EndObject)
39+
{
40+
break;
41+
}
42+
43+
if (reader.TokenType != JsonTokenType.PropertyName)
44+
{
45+
throw new JsonException("Expected property name.");
46+
}
47+
48+
var propertyName = reader.GetString();
49+
reader.Read();
50+
51+
switch (propertyName)
52+
{
53+
case "kind":
54+
var kindStr = reader.GetString();
55+
kind = kindStr switch
56+
{
57+
"raw" => HeaderKind.Raw,
58+
"string" => HeaderKind.String,
59+
"bool" => HeaderKind.Bool,
60+
"int32" => HeaderKind.Int32,
61+
"int64" => HeaderKind.Int64,
62+
"int128" => HeaderKind.Int128,
63+
"uint32" => HeaderKind.Uint32,
64+
"uint64" => HeaderKind.Uint64,
65+
"uint128" => HeaderKind.Uint128,
66+
"float32" => HeaderKind.Float,
67+
"float64" => HeaderKind.Double,
68+
_ => throw new JsonException($"Unknown header kind: {kindStr}")
69+
};
70+
break;
71+
case "value":
72+
var base64 = reader.GetString();
73+
value = base64 is not null ? Convert.FromBase64String(base64) : null;
74+
break;
75+
}
76+
}
77+
78+
if (kind is null || value is null)
79+
{
80+
throw new JsonException("HeaderKey must have both 'kind' and 'value' properties.");
81+
}
82+
83+
return new HeaderKey { Kind = kind.Value, Value = value };
2984
}
3085

3186
public override void Write(Utf8JsonWriter writer, HeaderKey value, JsonSerializerOptions options)
3287
{
33-
writer.WriteStringValue(value.Value);
88+
writer.WriteStartObject();
89+
writer.WriteString("kind", value.Kind switch
90+
{
91+
HeaderKind.Raw => "raw",
92+
HeaderKind.String => "string",
93+
HeaderKind.Bool => "bool",
94+
HeaderKind.Int32 => "int32",
95+
HeaderKind.Int64 => "int64",
96+
HeaderKind.Int128 => "int128",
97+
HeaderKind.Uint32 => "uint32",
98+
HeaderKind.Uint64 => "uint64",
99+
HeaderKind.Uint128 => "uint128",
100+
HeaderKind.Float => "float32",
101+
HeaderKind.Double => "float64",
102+
_ => throw new JsonException($"Unknown header kind: {value.Kind}")
103+
});
104+
writer.WriteString("value", Convert.ToBase64String(value.Value));
105+
writer.WriteEndObject();
34106
}
35107

36108
public override HeaderKey ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert,
37109
JsonSerializerOptions options)
38110
{
39-
return HeaderKey.New(reader.GetString() ?? throw new JsonException("Header key cannot be null or empty."));
111+
var keyStr = reader.GetString() ?? throw new JsonException("Header key cannot be null or empty.");
112+
return HeaderKey.FromString(keyStr);
40113
}
41114

42115
public override void WriteAsPropertyName(Utf8JsonWriter writer, HeaderKey value, JsonSerializerOptions options)
43116
{
44-
writer.WritePropertyName(value.Value);
117+
writer.WritePropertyName(value.ToString());
45118
}
46119
}

foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -428,17 +428,22 @@ private static Dictionary<HeaderKey, HeaderValue> MapHeaders(ReadOnlySpan<byte>
428428

429429
while (position < payload.Length)
430430
{
431+
var keyKind = MapHeaderKind(payload, position);
432+
position++;
433+
431434
var keyLength = BinaryPrimitives.ReadInt32LittleEndian(payload[position..(position + 4)]);
432435
if (keyLength is 0 or > 255)
433436
{
434437
throw new ArgumentException("Key has incorrect size, must be between 1 and 255", nameof(keyLength));
435438
}
436439

437-
var key = Encoding.UTF8.GetString(payload[(position + 4)..(position + 4 + keyLength)]);
438-
position += 4 + keyLength;
440+
position += 4;
441+
var keyValue = payload[position..(position + keyLength)].ToArray();
442+
position += keyLength;
439443

440-
var headerKind = MapHeaderKind(payload, position);
444+
var valueKind = MapHeaderKind(payload, position);
441445
position++;
446+
442447
var valueLength = BinaryPrimitives.ReadInt32LittleEndian(payload[position..(position + 4)]);
443448
if (valueLength is 0 or > 255)
444449
{
@@ -448,9 +453,10 @@ private static Dictionary<HeaderKey, HeaderValue> MapHeaders(ReadOnlySpan<byte>
448453
position += 4;
449454
ReadOnlySpan<byte> value = payload[position..(position + valueLength)];
450455
position += valueLength;
451-
headers.Add(HeaderKey.New(key), new HeaderValue
456+
457+
headers.Add(new HeaderKey { Kind = keyKind, Value = keyValue }, new HeaderValue
452458
{
453-
Kind = headerKind,
459+
Kind = valueKind,
454460
Value = value.ToArray()
455461
});
456462
}

0 commit comments

Comments
 (0)