Skip to content

Commit

Permalink
v13.6.0 - Implement new API features of IL2 Node
Browse files Browse the repository at this point in the history
  • Loading branch information
monoman committed Apr 11, 2024
1 parent 806bc98 commit 2d4a2ac
Show file tree
Hide file tree
Showing 45 changed files with 1,004 additions and 414 deletions.
1 change: 1 addition & 0 deletions InterlockLedger.Rest.Client/Abstractions/Globals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal static class Globals
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
ReadCommentHandling = JsonCommentHandling.Skip,
WriteIndented = true,
PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace InterlockLedger.Rest.Client.Abstractions;
internal sealed class InterlockingsImplementation : IRestInterlockings
{
public InterlockingsImplementation(RestAbstractChain parent) {
_parent = parent.Required(nameof(parent));
_parent = parent.Required();
_rest = _parent._rest;
_id = _parent.Id;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace InterlockLedger.Rest.Client.Abstractions;
internal sealed class RecordsAsJsonImplementation : IRestRecordsAsJson
{
public RecordsAsJsonImplementation(RestAbstractChain parent) {
_parent = parent.Required(nameof(parent));
_parent = parent.Required();
_rest = _parent._rest;
_id = _parent.Id;
}
Expand All @@ -49,11 +49,11 @@ public Task<RecordModelAsJson> AddAsync(ulong applicationId, ulong payloadTagId,
public Task<RecordModelAsJson> AddAsync(ulong applicationId, ulong payloadTagId, RecordType type, object payload)
=> _rest.PostAsync<RecordModelAsJson>($"records@{_id}/asJson?applicationId={applicationId}&payloadTagId={payloadTagId}&type={type}", payload);

public Task<PageOf<RecordModelAsJson>> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false)
=> _rest.GetAsync<PageOf<RecordModelAsJson>>($"records@{_id}/asJson?firstSerial={firstSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}");
public Task<PageOf<RecordModelAsJson>> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false)
=> _rest.GetAsync<PageOf<RecordModelAsJson>>($"records@{_id}/asJson?firstSerial={firstSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}");

public Task<PageOf<RecordModelAsJson>> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false)
=> _rest.GetAsync<PageOf<RecordModelAsJson>>($"records@{_id}/asJson?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}");
public Task<PageOf<RecordModelAsJson>> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false)
=> _rest.GetAsync<PageOf<RecordModelAsJson>>($"records@{_id}/asJson?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}");

private readonly string _id;
private readonly RestAbstractChain _parent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace InterlockLedger.Rest.Client.Abstractions;
internal sealed class RecordsImplementation : IRestRecords
{
public RecordsImplementation(RestAbstractChain parent) {
_parent = parent.Required(nameof(parent));
_parent = parent.Required();
_rest = _parent._rest;
_id = _parent.Id;
}
Expand All @@ -49,11 +49,11 @@ public Task<RecordModel> AddRecordAsync(ulong applicationId, ulong payloadTagId,
public Task<RecordModel> AddRecordAsync(ulong applicationId, ulong payloadTagId, RecordType type, byte[] bytes)
=> _rest.PostRawAsync<RecordModel>($"records@{_id}/with?applicationId={applicationId}&payloadTagId={payloadTagId}&type={type}", bytes, "application/interlockledger");

public Task<PageOf<RecordModel>> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10)
=> _rest.GetAsync<PageOf<RecordModel>>($"records@{_id}?firstSerial={firstSerial}&page={page}&pageSize={pageSize}");
public Task<PageOf<RecordModel>> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false)
=> _rest.GetAsync<PageOf<RecordModel>>($"records@{_id}?firstSerial={firstSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}");

public Task<PageOf<RecordModel>> RecordsFromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10)
=> _rest.GetAsync<PageOf<RecordModel>>($"records@{_id}?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}");
public Task<PageOf<RecordModel>> RecordsFromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false)
=> _rest.GetAsync<PageOf<RecordModel>>($"records@{_id}?firstSerial={firstSerial}&lastSerial={lastSerial}&page={page}&pageSize={pageSize}&lastToFirst={lastToFirst}&ommitPayload={ommitPayload}");

private readonly string _id;
private readonly RestAbstractChain _parent;
Expand Down
61 changes: 34 additions & 27 deletions InterlockLedger.Rest.Client/Abstractions/RestAbstractNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,45 +96,40 @@ Task<bool> IRestNodeInternals.PostStreamAsync(string url, Stream body, string co

public override string ToString() => $"Node [{BaseUri}] connected with certificate '{CertificateName}'";

protected readonly X509Certificate2 _certificate;

protected static async Task<HttpWebResponse> GetResponseAsync(HttpWebRequest req) {
return await ValidatedAsync(await GetInnerResponseAsync(req).ConfigureAwait(false));

static async Task<HttpWebResponse> ValidatedAsync(HttpWebResponse resp) => await IfOkOrCreatedReturnAsync(resp, () => Task.FromResult(resp));
static async Task<HttpWebResponse> GetInnerResponseAsync(HttpWebRequest req) {
try {
return (HttpWebResponse)await req.GetResponseAsync().ConfigureAwait(false);
} catch (WebException e) {
return (HttpWebResponse)e.Response
?? throw new InvalidOperationException($"Could not retrieve response at {req.Address}", e);
}
protected internal readonly X509Certificate2 _certificate;

protected internal static async Task<HttpWebResponse> GetResponseAsync(HttpWebRequest req) {
try {
return (HttpWebResponse)await req.GetResponseAsync().ConfigureAwait(false);
} catch (WebException e) {
return (HttpWebResponse)e.Response
?? throw new InvalidOperationException($"Could not retrieve response at {req.Address}", e);
}
}

protected static async Task<string> GetStringResponseAsync(HttpWebRequest req) => await ReadAsStringAsync(await GetResponseAsync(req));
protected internal static async Task<string> GetStringResponseAsync(HttpWebRequest req) => await ReadAsStringAsync(await GetResponseAsync(req));

protected static async Task<string> ReadAsStringAsync(HttpWebResponse resp) {
protected internal static async Task<string> ReadAsStringAsync(HttpWebResponse resp) {
if (resp == null)
return string.Empty;
using var readStream = new StreamReader(resp.GetResponseStream());
return await readStream.ReadToEndAsync();
}

protected abstract T BuildChain(ChainIdModel c);
protected internal abstract T BuildChain(ChainIdModel c);

protected async Task<string> CallApiAsync(string url, string method, string accept = "application/json")
protected internal async Task<string> CallApiAsync(string url, string method, string accept = "application/json")
=> await GetStringResponseAsync(PrepareRequest(url, method, accept));

protected async Task<string> CallApiPlainDocAsync(string url, string method, string accept = "plain/text")
protected internal async Task<string> CallApiPlainDocAsync(string url, string method, string accept = "plain/text")
=> await GetStringResponseAsync(PrepareRequest(url, method, accept));

protected async Task<RawDocumentModel> CallApiRawDocAsync(string url, string method, string accept = "*")
protected internal async Task<RawDocumentModel> CallApiRawDocAsync(string url, string method, string accept = "*")
=> await GetRawResponseAsync(PrepareRequest(url, method, accept));

protected async Task<TR> GetAsync<TR>(string url) => Deserialize<TR>(await CallApiAsync(url, "GET").ConfigureAwait(false));
protected internal async Task<TR> GetAsync<TR>(string url) => Deserialize<TR>(await CallApiAsync(url, "GET").ConfigureAwait(false));

protected async Task<(string Name, string ContentType, Stream Content)> GetFileReadStreamAsync(string url, string accept = "*/*", string method = "GET") {
protected internal async Task<(string Name, string ContentType, Stream Content)> GetFileReadStreamAsync(string url, string accept = "*/*", string method = "GET") {
if (string.IsNullOrWhiteSpace(url))
throw new ArgumentException($"'{nameof(url)}' cannot be null or empty", nameof(url));
if (string.IsNullOrWhiteSpace(accept))
Expand All @@ -144,19 +139,23 @@ protected async Task<RawDocumentModel> CallApiRawDocAsync(string url, string met
var resp = await GetResponseAsync(PrepareRequest(url, method, accept));
return (ParseFileName(resp), resp.ContentType, resp.GetResponseStream());
}
protected internal async Task<(ulong AppId, ulong PayloadTypeId, Stream Content)> GetOpaqueStreamAsync(string url) {
var resp = await GetResponseAsync(PrepareRequest(url.Required(), "GET", "application/octet-stream"));
return (ulong.Parse(resp.Headers["x-app-id"].WithDefault("0")), ulong.Parse(resp.Headers["x-payload-type-id"].WithDefault("0")), resp.GetResponseStream());
}

protected async Task<TR> PostAsync<TR>(string url, object body)
protected internal async Task<TR> PostAsync<TR>(string url, object body)
=> Deserialize<TR>(await GetStringResponseAsync(PreparePostRequest(url, body, accept: "application/json")));

protected async Task<TR> PostRawAsync<TR>(string url, byte[] body, string contentType)
protected internal async Task<TR> PostRawAsync<TR>(string url, byte[] body, string contentType)
=> Deserialize<TR>(await GetStringResponseAsync(PreparePostRawRequest(url, body, accept: "application/json", contentType)));

protected async Task<TR> PostStreamAsync<TR>(string url, Stream body, string contentType) {
protected internal async Task<TR> PostStreamAsync<TR>(string url, Stream body, string contentType) {
var resp = await GetResponseAsync(PreparePostStreamRequest(url, body, accept: "*/*", contentType));
return await IfOkOrCreatedReturnAsync(resp, async () => Deserialize<TR>(await ReadAsStringAsync(resp)));
}

protected HttpWebRequest PrepareRequest(string url, string method, string accept) {
protected internal HttpWebRequest PrepareRequest(string url, string method, string accept) {
#pragma warning disable SYSLIB0014 // Type or member is obsolete
var req = (HttpWebRequest)WebRequest.Create(new Uri(BaseUri, url));
#pragma warning restore SYSLIB0014 // Type or member is obsolete
Expand All @@ -170,7 +169,7 @@ protected HttpWebRequest PrepareRequest(string url, string method, string accept
return req;
}

protected bool ServerCertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
protected internal bool ServerCertificateValidation(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
=> true;

private static readonly Encoding _utf8WithoutBOM = new UTF8Encoding(false);
Expand All @@ -189,7 +188,15 @@ private static async Task<TR> CheckMessageAsync<TR>(HttpWebResponse resp) {
: throw new InvalidDataException($"API error: {resp.StatusCode}(#{(int)resp.StatusCode}) {resp.StatusDescription}{Environment.NewLine}{content}");
}

private static TR Deserialize<TR>(string s) => JsonSerializer.Deserialize<TR>(s, Globals.JsonSettings);
private static TR Deserialize<TR>(string s) {
try {
return JsonSerializer.Deserialize<TR>(s, Globals.JsonSettings);
} catch (Exception e) {
Console.Error.WriteLine(typeof(TR).FullName);
Console.Error.WriteLine(e);
return default(TR);
}
}

private static X509Certificate2 GetCertFromFile(string certPath, string certPassword)
=> new(certPath, certPassword, X509KeyStorageFlags.PersistKeySet);
Expand Down
4 changes: 2 additions & 2 deletions InterlockLedger.Rest.Client/Interfaces/IRestRecords.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public interface IRestRecords

Task<RecordModel> AddRecordAsync(ulong applicationId, ulong payloadTagId, RecordType type, byte[] bytes);

Task<PageOf<RecordModel>> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10);
Task<PageOf<RecordModel>> RecordsFromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false);

Task<PageOf<RecordModel>> RecordsFromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10);
Task<PageOf<RecordModel>> RecordsFromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false);
}
4 changes: 2 additions & 2 deletions InterlockLedger.Rest.Client/Interfaces/IRestRecordsAsJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public interface IRestRecordsAsJson

Task<RecordModelAsJson> AddAsync(ulong applicationId, ulong payloadTagId, RecordType type, object payload);

Task<PageOf<RecordModelAsJson>> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false);
Task<PageOf<RecordModelAsJson>> FromAsync(ulong firstSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false);

Task<PageOf<RecordModelAsJson>> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false);
Task<PageOf<RecordModelAsJson>> FromToAsync(ulong firstSerial, ulong lastSerial, ushort page = 0, byte pageSize = 10, bool lastToFirst = false, bool ommitPayload = false);
}
109 changes: 57 additions & 52 deletions InterlockLedger.Rest.Client/InterlockLedger.Rest.Client.csproj
Original file line number Diff line number Diff line change
@@ -1,59 +1,64 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<Version>7.0.1</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Copyright>Copyright (c) 2018-2022 InterlockLedger Network</Copyright>
<Description>
This library implements a C# wrapper around the InterlockLedger Node REST API.
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<Version>13.6.0</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Copyright>Copyright (c) 2018-2024 InterlockLedger Network</Copyright>
<Description>
This library implements a C# wrapper around the InterlockLedger Node REST API.

BSD 3-Clause License
</Description>
<PackageLicenseUrl></PackageLicenseUrl>
<PackageProjectUrl>https://interlockledger.network/</PackageProjectUrl>
<RepositoryUrl>https://github.com/interlockledger/interlockledger-rest-client-csharp.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageTags>REST Client InterlockLedger</PackageTags>
<PackageReleaseNotes>Drop diagram - Update to square icon</PackageReleaseNotes>
<PackageIcon>il2.png</PackageIcon>
<Authors>InterlockLedger Network</Authors>
<Product>InterlockLedger</Product>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<ImplicitUsings>Enable</ImplicitUsings>
</PropertyGroup>
BSD 3-Clause License
</Description>
<PackageLicenseUrl></PackageLicenseUrl>
<PackageProjectUrl>https://interlockledger.network/</PackageProjectUrl>
<RepositoryUrl>https://github.com/interlockledger/interlockledger-rest-client-csharp.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageTags>REST Client InterlockLedger</PackageTags>
<PackageReleaseNotes>Implement new API features of IL2 Node</PackageReleaseNotes>
<PackageIcon>il2.png</PackageIcon>
<Authors>InterlockLedger Network</Authors>
<Product>InterlockLedger</Product>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<ImplicitUsings>Enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<Using Include="InterlockLedger.Rest.Client.Abstractions" />
<Using Include="InterlockLedger.Tags" />
<Using Include="System.Security.Cryptography" />
<Using Include="System.Security.Cryptography.X509Certificates" />
<Using Include="System.Text" />
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="System.Text.RegularExpressions" />
<Using Include="System.Web" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="InterlockLedger.Tags.ILInt" Version="5.0.2" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<Using Include="InterlockLedger.Rest.Client.Abstractions" />
<Using Include="InterlockLedger.Tags" />
<Using Include="System.Security.Cryptography" />
<Using Include="System.Security.Cryptography.X509Certificates" />
<Using Include="System.Text" />
<Using Include="System.Text.Json" />
<Using Include="System.Text.Json.Serialization" />
<Using Include="System.Text.RegularExpressions" />
<Using Include="System.Web" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="InterlockLedger.Commons" Version="18.2.0" />
<PackageReference Include="InterlockLedger.Tags.ILInt" Version="14.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
</ItemGroup>

<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\il2.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\il2.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<Folder Include="Models\Types\" />
</ItemGroup>
<Target Name="TagSources">
<Exec Command="git tag v$(Version)" ContinueOnError="true" StandardErrorImportance="low" StandardOutputImportance="low" IgnoreExitCode="true" />
<Message Importance="high" Text="Recent tags:" />
Expand Down
Loading

0 comments on commit 2d4a2ac

Please sign in to comment.