feat: add system event DTOs and JSON source generator context
This commit is contained in:
12
src/NATS.Server/Events/EventJsonContext.cs
Normal file
12
src/NATS.Server/Events/EventJsonContext.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace NATS.Server.Events;
|
||||||
|
|
||||||
|
[JsonSerializable(typeof(ConnectEventMsg))]
|
||||||
|
[JsonSerializable(typeof(DisconnectEventMsg))]
|
||||||
|
[JsonSerializable(typeof(AccountNumConns))]
|
||||||
|
[JsonSerializable(typeof(ServerStatsMsg))]
|
||||||
|
[JsonSerializable(typeof(ShutdownEventMsg))]
|
||||||
|
[JsonSerializable(typeof(LameDuckEventMsg))]
|
||||||
|
[JsonSerializable(typeof(AuthErrorEventMsg))]
|
||||||
|
internal partial class EventJsonContext : JsonSerializerContext;
|
||||||
270
src/NATS.Server/Events/EventTypes.cs
Normal file
270
src/NATS.Server/Events/EventTypes.cs
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace NATS.Server.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server identity block embedded in all system events.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EventServerInfo
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("host")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Host { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("cluster")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Cluster { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("domain")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Domain { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("ver")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Version { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("seq")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public ulong Seq { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("tags")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public Dictionary<string, string>? Tags { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Client identity block for connect/disconnect events.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class EventClientInfo
|
||||||
|
{
|
||||||
|
[JsonPropertyName("start")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public DateTime Start { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("stop")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public DateTime Stop { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("host")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Host { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public ulong Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("acc")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Account { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("lang")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Lang { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("ver")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? Version { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("rtt")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public long RttNanos { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class DataStats
|
||||||
|
{
|
||||||
|
[JsonPropertyName("msgs")]
|
||||||
|
public long Msgs { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("bytes")]
|
||||||
|
public long Bytes { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Client connect advisory. Go events.go:155-160.</summary>
|
||||||
|
public sealed class ConnectEventMsg
|
||||||
|
{
|
||||||
|
public const string EventType = "io.nats.server.advisory.v1.client_connect";
|
||||||
|
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; } = EventType;
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("timestamp")]
|
||||||
|
public DateTime Time { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("server")]
|
||||||
|
public EventServerInfo Server { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("client")]
|
||||||
|
public EventClientInfo Client { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Client disconnect advisory. Go events.go:167-174.</summary>
|
||||||
|
public sealed class DisconnectEventMsg
|
||||||
|
{
|
||||||
|
public const string EventType = "io.nats.server.advisory.v1.client_disconnect";
|
||||||
|
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; } = EventType;
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("timestamp")]
|
||||||
|
public DateTime Time { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("server")]
|
||||||
|
public EventServerInfo Server { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("client")]
|
||||||
|
public EventClientInfo Client { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("sent")]
|
||||||
|
public DataStats Sent { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("received")]
|
||||||
|
public DataStats Received { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("reason")]
|
||||||
|
public string Reason { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Account connection count heartbeat. Go events.go:210-214.</summary>
|
||||||
|
public sealed class AccountNumConns
|
||||||
|
{
|
||||||
|
public const string EventType = "io.nats.server.advisory.v1.account_connections";
|
||||||
|
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; } = EventType;
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("timestamp")]
|
||||||
|
public DateTime Time { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("server")]
|
||||||
|
public EventServerInfo Server { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("acc")]
|
||||||
|
public string AccountName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("conns")]
|
||||||
|
public int Connections { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("total_conns")]
|
||||||
|
public long TotalConnections { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("subs")]
|
||||||
|
public int Subscriptions { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("sent")]
|
||||||
|
public DataStats Sent { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("received")]
|
||||||
|
public DataStats Received { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Server stats broadcast. Go events.go:150-153.</summary>
|
||||||
|
public sealed class ServerStatsMsg
|
||||||
|
{
|
||||||
|
[JsonPropertyName("server")]
|
||||||
|
public EventServerInfo Server { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("statsz")]
|
||||||
|
public ServerStatsData Stats { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ServerStatsData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("start")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public DateTime Start { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("mem")]
|
||||||
|
public long Mem { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("cores")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public int Cores { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("connections")]
|
||||||
|
public int Connections { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("total_connections")]
|
||||||
|
public long TotalConnections { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("active_accounts")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public int ActiveAccounts { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("subscriptions")]
|
||||||
|
public long Subscriptions { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("in_msgs")]
|
||||||
|
public long InMsgs { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("out_msgs")]
|
||||||
|
public long OutMsgs { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("in_bytes")]
|
||||||
|
public long InBytes { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("out_bytes")]
|
||||||
|
public long OutBytes { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("slow_consumers")]
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
|
||||||
|
public long SlowConsumers { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Server shutdown notification.</summary>
|
||||||
|
public sealed class ShutdownEventMsg
|
||||||
|
{
|
||||||
|
[JsonPropertyName("server")]
|
||||||
|
public EventServerInfo Server { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("reason")]
|
||||||
|
public string Reason { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Lame duck mode notification.</summary>
|
||||||
|
public sealed class LameDuckEventMsg
|
||||||
|
{
|
||||||
|
[JsonPropertyName("server")]
|
||||||
|
public EventServerInfo Server { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Auth error advisory.</summary>
|
||||||
|
public sealed class AuthErrorEventMsg
|
||||||
|
{
|
||||||
|
public const string EventType = "io.nats.server.advisory.v1.client_auth";
|
||||||
|
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type { get; set; } = EventType;
|
||||||
|
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("timestamp")]
|
||||||
|
public DateTime Time { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("server")]
|
||||||
|
public EventServerInfo Server { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("client")]
|
||||||
|
public EventClientInfo Client { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonPropertyName("reason")]
|
||||||
|
public string Reason { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="NATS.Server.Tests" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||||
<PackageReference Include="NATS.NKeys" />
|
<PackageReference Include="NATS.NKeys" />
|
||||||
|
|||||||
68
tests/NATS.Server.Tests/EventSystemTests.cs
Normal file
68
tests/NATS.Server.Tests/EventSystemTests.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using NATS.Server.Events;
|
||||||
|
|
||||||
|
namespace NATS.Server.Tests;
|
||||||
|
|
||||||
|
public class EventSystemTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ConnectEventMsg_serializes_with_correct_type()
|
||||||
|
{
|
||||||
|
var evt = new ConnectEventMsg
|
||||||
|
{
|
||||||
|
Type = ConnectEventMsg.EventType,
|
||||||
|
Id = "test123",
|
||||||
|
Time = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc),
|
||||||
|
Server = new EventServerInfo { Name = "test-server", Id = "SRV1" },
|
||||||
|
Client = new EventClientInfo { Id = 1, Account = "$G" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(evt, EventJsonContext.Default.ConnectEventMsg);
|
||||||
|
json.ShouldContain("\"type\":\"io.nats.server.advisory.v1.client_connect\"");
|
||||||
|
json.ShouldContain("\"server\":");
|
||||||
|
json.ShouldContain("\"client\":");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DisconnectEventMsg_serializes_with_reason()
|
||||||
|
{
|
||||||
|
var evt = new DisconnectEventMsg
|
||||||
|
{
|
||||||
|
Type = DisconnectEventMsg.EventType,
|
||||||
|
Id = "test456",
|
||||||
|
Time = DateTime.UtcNow,
|
||||||
|
Server = new EventServerInfo { Name = "test-server", Id = "SRV1" },
|
||||||
|
Client = new EventClientInfo { Id = 2, Account = "myacc" },
|
||||||
|
Reason = "Client Closed",
|
||||||
|
Sent = new DataStats { Msgs = 10, Bytes = 1024 },
|
||||||
|
Received = new DataStats { Msgs = 5, Bytes = 512 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(evt, EventJsonContext.Default.DisconnectEventMsg);
|
||||||
|
json.ShouldContain("\"reason\":\"Client Closed\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ServerStatsMsg_serializes()
|
||||||
|
{
|
||||||
|
var evt = new ServerStatsMsg
|
||||||
|
{
|
||||||
|
Server = new EventServerInfo { Name = "srv1", Id = "ABC" },
|
||||||
|
Stats = new ServerStatsData
|
||||||
|
{
|
||||||
|
Connections = 10,
|
||||||
|
TotalConnections = 100,
|
||||||
|
InMsgs = 5000,
|
||||||
|
OutMsgs = 4500,
|
||||||
|
InBytes = 1_000_000,
|
||||||
|
OutBytes = 900_000,
|
||||||
|
Mem = 50 * 1024 * 1024,
|
||||||
|
Subscriptions = 42,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(evt, EventJsonContext.Default.ServerStatsMsg);
|
||||||
|
json.ShouldContain("\"connections\":10");
|
||||||
|
json.ShouldContain("\"in_msgs\":5000");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user