From 8cf09ffa8be0089ced9b2b904a1c0eb4f1ef04af Mon Sep 17 00:00:00 2001 From: Nic-dorman Date: Tue, 5 May 2026 09:59:35 +0100 Subject: [PATCH] feat(antd-csharp): expose new HealthStatus diagnostic fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors antd-go v0.5.0 / antd-py: HealthStatus now carries Version, EvmNetwork, UptimeSeconds, BuildCommit, PaymentTokenAddress, and PaymentVaultAddress. All have positional default values ("" / 0) so the record stays constructable from a pre-0.4.0 daemon's response and existing two-arg call sites keep compiling. REST routes through AntdRestClient.HealthStatusFromDto; the internal HealthResponseDto gains six nullable JSON-named properties so deserialization tolerates either old or new daemons. gRPC routes through AntdGrpcClient.HealthStatusFromResp pulling from the regenerated HealthCheckResponse proto. Proto stubs are regenerated automatically by Grpc.Tools during `dotnet build` — no separate regen step needed. UnitTests.HealthAsync_ReturnsOk extended to populate + assert all 6 fields. New HealthAsync_PreV0_4_0Daemon_LeavesDiagnosticsEmpty exercises the empty-defaults path. Part of #37. Co-Authored-By: Claude Opus 4.7 (1M context) --- antd-csharp/Antd.Sdk.Tests/UnitTests.cs | 36 ++++++++++++++++++++++++- antd-csharp/Antd.Sdk/AntdGrpcClient.cs | 17 +++++++++++- antd-csharp/Antd.Sdk/AntdRestClient.cs | 31 ++++++++++++++++++--- antd-csharp/Antd.Sdk/Models.cs | 20 ++++++++++++-- antd-csharp/README.md | 4 +-- 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/antd-csharp/Antd.Sdk.Tests/UnitTests.cs b/antd-csharp/Antd.Sdk.Tests/UnitTests.cs index 18469dd..9637ddf 100644 --- a/antd-csharp/Antd.Sdk.Tests/UnitTests.cs +++ b/antd-csharp/Antd.Sdk.Tests/UnitTests.cs @@ -124,13 +124,47 @@ public void Dispose() [Fact] public async Task HealthAsync_ReturnsOk() { - _server.RouteOk("GET", "/health", new { status = "ok", network = "testnet" }); + _server.RouteOk("GET", "/health", new + { + status = "ok", + network = "testnet", + version = "0.4.0", + evm_network = "local", + uptime_seconds = 42, + build_commit = "abcdef123456", + payment_token_address = "0xtoken", + payment_vault_address = "0xvault", + }); _server.Start(); var result = await _client.HealthAsync(); Assert.True(result.Ok); Assert.Equal("testnet", result.Network); + Assert.Equal("0.4.0", result.Version); + Assert.Equal("local", result.EvmNetwork); + Assert.Equal(42UL, result.UptimeSeconds); + Assert.Equal("abcdef123456", result.BuildCommit); + Assert.Equal("0xtoken", result.PaymentTokenAddress); + Assert.Equal("0xvault", result.PaymentVaultAddress); + } + + [Fact] + public async Task HealthAsync_PreV0_4_0Daemon_LeavesDiagnosticsEmpty() + { + // Older daemons reply with just status + network; the optional DTO + // properties default to null, and HealthStatusFromDto fills "" / 0. + _server.RouteOk("GET", "/health", new { status = "ok", network = "default" }); + _server.Start(); + + var result = await _client.HealthAsync(); + + Assert.True(result.Ok); + Assert.Equal("default", result.Network); + Assert.Equal("", result.Version); + Assert.Equal("", result.EvmNetwork); + Assert.Equal(0UL, result.UptimeSeconds); + Assert.Equal("", result.BuildCommit); } [Fact] diff --git a/antd-csharp/Antd.Sdk/AntdGrpcClient.cs b/antd-csharp/Antd.Sdk/AntdGrpcClient.cs index 814486b..a1a7238 100644 --- a/antd-csharp/Antd.Sdk/AntdGrpcClient.cs +++ b/antd-csharp/Antd.Sdk/AntdGrpcClient.cs @@ -43,7 +43,7 @@ public async Task HealthAsync() try { var resp = await _health.CheckAsync(new HealthCheckRequest()); - return new HealthStatus(resp.Status == "ok", resp.Network ?? "unknown"); + return HealthStatusFromResp(resp); } catch (RpcException ex) when (ex.StatusCode == StatusCode.Unavailable) { @@ -59,6 +59,21 @@ public async Task HealthAsync() } } + /// + /// Convert a gRPC into a typed + /// . + /// + internal static HealthStatus HealthStatusFromResp(HealthCheckResponse resp) => + new( + resp.Status == "ok", + resp.Network ?? "unknown", + resp.Version ?? "", + resp.EvmNetwork ?? "", + resp.UptimeSeconds, + resp.BuildCommit ?? "", + resp.PaymentTokenAddress ?? "", + resp.PaymentVaultAddress ?? ""); + // ── Data ── public async Task DataPutPublicAsync(byte[] data, string? paymentMode = null) diff --git a/antd-csharp/Antd.Sdk/AntdRestClient.cs b/antd-csharp/Antd.Sdk/AntdRestClient.cs index 2862235..932da3c 100644 --- a/antd-csharp/Antd.Sdk/AntdRestClient.cs +++ b/antd-csharp/Antd.Sdk/AntdRestClient.cs @@ -81,7 +81,7 @@ public async Task HealthAsync() if (!resp.IsSuccessStatusCode) return new HealthStatus(false, "unknown"); var json = await resp.Content.ReadFromJsonAsync(JsonOpts); - return new HealthStatus(json?.Status == "ok", json?.Network ?? "unknown"); + return HealthStatusFromDto(json); } catch { @@ -89,6 +89,25 @@ public async Task HealthAsync() } } + /// + /// Convert a parsed into a typed + /// . Diagnostic fields default to empty / 0 + /// when talking to a pre-0.4.0 daemon that omits them. + /// + internal static HealthStatus HealthStatusFromDto(HealthResponseDto? dto) + { + if (dto is null) return new HealthStatus(false, "unknown"); + return new HealthStatus( + dto.Status == "ok", + dto.Network ?? "unknown", + dto.Version ?? "", + dto.EvmNetwork ?? "", + dto.UptimeSeconds ?? 0, + dto.BuildCommit ?? "", + dto.PaymentTokenAddress ?? "", + dto.PaymentVaultAddress ?? ""); + } + // ── Data ── public async Task DataPutPublicAsync(byte[] data, string? paymentMode = null) @@ -258,9 +277,15 @@ private static PrepareUploadResult MapPrepareUpload(PrepareUploadDto resp) // ── Internal DTOs for JSON deserialization ── - private sealed record HealthResponseDto( + internal sealed record HealthResponseDto( [property: JsonPropertyName("status")] string Status, - [property: JsonPropertyName("network")] string? Network); + [property: JsonPropertyName("network")] string? Network, + [property: JsonPropertyName("version")] string? Version = null, + [property: JsonPropertyName("evm_network")] string? EvmNetwork = null, + [property: JsonPropertyName("uptime_seconds")] ulong? UptimeSeconds = null, + [property: JsonPropertyName("build_commit")] string? BuildCommit = null, + [property: JsonPropertyName("payment_token_address")] string? PaymentTokenAddress = null, + [property: JsonPropertyName("payment_vault_address")] string? PaymentVaultAddress = null); private sealed record DataPutPublicDto( [property: JsonPropertyName("cost")] string Cost, diff --git a/antd-csharp/Antd.Sdk/Models.cs b/antd-csharp/Antd.Sdk/Models.cs index 67ece62..e05f041 100644 --- a/antd-csharp/Antd.Sdk/Models.cs +++ b/antd-csharp/Antd.Sdk/Models.cs @@ -1,7 +1,23 @@ namespace Antd.Sdk; -/// Health check result from the antd daemon. -public sealed record HealthStatus(bool Ok, string Network); +/// +/// Health check result from the antd daemon. +/// +/// The diagnostic fields (, , +/// , , +/// , ) were +/// added in antd 0.4.0. They default to "" / 0 so the record +/// stays constructable from a pre-0.4.0 daemon's response. +/// +public sealed record HealthStatus( + bool Ok, + string Network, + string Version = "", + string EvmNetwork = "", + ulong UptimeSeconds = 0, + string BuildCommit = "", + string PaymentTokenAddress = "", + string PaymentVaultAddress = ""); /// Result of a put/create operation that stores data on the network. public sealed record PutResult(string Cost, string Address); diff --git a/antd-csharp/README.md b/antd-csharp/README.md index 2f3b64c..1e997bb 100644 --- a/antd-csharp/README.md +++ b/antd-csharp/README.md @@ -69,7 +69,7 @@ All methods are async and return `Task`. The client implements `IDisposable`. | Method | Returns | Description | |--------|---------|-------------| -| `HealthAsync()` | `HealthStatus` | Check daemon health | +| `HealthAsync()` | `HealthStatus` | Check daemon health — also reports antd version, EVM network, uptime, build commit, and payment contract addresses (antd ≥ 0.4.0) | ### Data @@ -104,7 +104,7 @@ All models are sealed records (immutable). | Model | Fields | Description | |-------|--------|-------------| -| `HealthStatus` | `Ok`, `Network` | Health check result | +| `HealthStatus` | `Ok`, `Network`, `Version`, `EvmNetwork`, `UptimeSeconds`, `BuildCommit`, `PaymentTokenAddress`, `PaymentVaultAddress` | Health check result (diagnostic fields require antd ≥ 0.4.0) | | `PutResult` | `Cost`, `Address` | Write operation result | | `FileUploadResult` | `Address`, `StorageCostAtto`, `GasCostWei`, `ChunksStored`, `PaymentModeUsed` | File/dir upload result |