Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion antd-csharp/Antd.Sdk.Tests/UnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
17 changes: 16 additions & 1 deletion antd-csharp/Antd.Sdk/AntdGrpcClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public async Task<HealthStatus> 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)
{
Expand All @@ -59,6 +59,21 @@ public async Task<HealthStatus> HealthAsync()
}
}

/// <summary>
/// Convert a gRPC <see cref="HealthCheckResponse"/> into a typed
/// <see cref="HealthStatus"/>.
/// </summary>
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<PutResult> DataPutPublicAsync(byte[] data, string? paymentMode = null)
Expand Down
31 changes: 28 additions & 3 deletions antd-csharp/Antd.Sdk/AntdRestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,33 @@ public async Task<HealthStatus> HealthAsync()
if (!resp.IsSuccessStatusCode)
return new HealthStatus(false, "unknown");
var json = await resp.Content.ReadFromJsonAsync<HealthResponseDto>(JsonOpts);
return new HealthStatus(json?.Status == "ok", json?.Network ?? "unknown");
return HealthStatusFromDto(json);
}
catch
{
return new HealthStatus(false, "unknown");
}
}

/// <summary>
/// Convert a parsed <see cref="HealthResponseDto"/> into a typed
/// <see cref="HealthStatus"/>. Diagnostic fields default to empty / 0
/// when talking to a pre-0.4.0 daemon that omits them.
/// </summary>
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<PutResult> DataPutPublicAsync(byte[] data, string? paymentMode = null)
Expand Down Expand Up @@ -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,
Expand Down
20 changes: 18 additions & 2 deletions antd-csharp/Antd.Sdk/Models.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
namespace Antd.Sdk;

/// <summary>Health check result from the antd daemon.</summary>
public sealed record HealthStatus(bool Ok, string Network);
/// <summary>
/// Health check result from the antd daemon.
///
/// The diagnostic fields (<see cref="Version"/>, <see cref="EvmNetwork"/>,
/// <see cref="UptimeSeconds"/>, <see cref="BuildCommit"/>,
/// <see cref="PaymentTokenAddress"/>, <see cref="PaymentVaultAddress"/>) were
/// added in antd 0.4.0. They default to <c>""</c> / <c>0</c> so the record
/// stays constructable from a pre-0.4.0 daemon's response.
/// </summary>
public sealed record HealthStatus(
bool Ok,
string Network,
string Version = "",
string EvmNetwork = "",
ulong UptimeSeconds = 0,
string BuildCommit = "",
string PaymentTokenAddress = "",
string PaymentVaultAddress = "");

/// <summary>Result of a put/create operation that stores data on the network.</summary>
public sealed record PutResult(string Cost, string Address);
Expand Down
4 changes: 2 additions & 2 deletions antd-csharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ All methods are async and return `Task<T>`. 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

Expand Down Expand Up @@ -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 |

Expand Down