Skip to content

Commit 1c25035

Browse files
Get HookHash (#37)
* Fixed check on actual deserialized `XummApiError` with filled Error * Added HookHash models * Added HookHash regex * Account and SHA512H regex * Renamed models * Added hook info requests to `IXummMiscClient` * Added Unit tests * Added HookHash blazor example
1 parent 057cf39 commit 1c25035

File tree

14 files changed

+313
-6
lines changed

14 files changed

+313
-6
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
@page "/hookhash"
2+
@using XUMM.NET.SDK.Models.Misc
3+
4+
@inject IOptions<XrplConfig> Config
5+
@inject IXummMiscClient MiscClient
6+
7+
<PageTitle>Hook Hash(es)</PageTitle>
8+
9+
<ResponseAlertBox @ref="_responseAlertBox"></ResponseAlertBox>
10+
11+
<h1>Hook Hash(es)</h1>
12+
13+
<div class="row align-items-start">
14+
<div class="col-md-6">
15+
<div class="row">
16+
<div class="mb-3">
17+
<label for="hookhash" class="form-label">Hook Hash</label>
18+
<input id="hookhash" type="text" class="form-control" placeholder="Hook Hash" aria-label="Hook Hash" aria-describedby="basic-addon2" @bind="_hookHash">
19+
</div>
20+
<div class="btn-group mb-3" role="group">
21+
<button class="btn btn-primary" type="button" @onclick="GetHookInfoAsync">Fetch Hook Info</button>
22+
<button class="btn btn-secondary" type="button" @onclick="GetHookInfosAsync">Fetch all</button>
23+
</div>
24+
</div>
25+
</div>
26+
27+
</div>
28+
29+
@if (_hookInfos != null)
30+
{
31+
<div class="row">
32+
<div class="mt-3">
33+
<table class="table">
34+
<thead>
35+
<tr>
36+
<th scope="col">Name</th>
37+
<th scope="col">Hash</th>
38+
<th scope="col">xApp</th>
39+
<th scope="col">Description</th>
40+
</tr>
41+
</thead>
42+
<tbody>
43+
@foreach (var kvp in _hookInfos.OrderBy(x => x.HookInfo.Name))
44+
{
45+
<tr>
46+
<td><img src="@kvp.HookInfo.Icon" alt="@kvp.HookInfo.Name" height="24" /> @kvp.HookInfo.Name</td>
47+
<td>@kvp.HookHash</td>
48+
<td>@kvp.HookInfo.Xapp</td>
49+
<td>@kvp.HookInfo.Description</td>
50+
</tr>
51+
}
52+
</tbody>
53+
</table>
54+
</div>
55+
</div>
56+
}
57+
58+
@code {
59+
private ResponseAlertBox? _responseAlertBox;
60+
private string _hookHash = default!;
61+
private List<XummHookInfoResponse>? _hookInfos;
62+
63+
private async Task GetHookInfoAsync()
64+
{
65+
var hookInfo = await _responseAlertBox!.GetResponseAndSetAlertAsync(() => MiscClient.GetHookInfoAsync(_hookHash));
66+
if (hookInfo != null)
67+
{
68+
_hookInfos = new List<XummHookInfoResponse>
69+
{
70+
hookInfo
71+
};
72+
}
73+
else
74+
{
75+
_hookInfos = null;
76+
}
77+
78+
_responseAlertBox.SetAlert("Hook Hash", hookInfo != null);
79+
}
80+
81+
private async Task GetHookInfosAsync()
82+
{
83+
_hookHash = string.Empty;
84+
_hookInfos = await _responseAlertBox!.GetResponseAndSetAlertAsync(() => MiscClient.GetAllHookInfosAsync());
85+
_responseAlertBox.SetAlert("Hook Hashes", _hookInfos != null);
86+
}
87+
}

examples/XUMM.Net.ServerApp/Shared/NavMenu.razor

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
<NavLink class="nav-link ps-4" href="/curatedassets">
2727
<span class="oi oi-check" aria-hidden="true"></span> Curated Assets
2828
</NavLink>
29+
<NavLink class="nav-link ps-4" href="/hookhash">
30+
<span class="oi oi-bolt" aria-hidden="true"></span> Hook Hash(es)
31+
</NavLink>
2932
<NavLink class="nav-link ps-4" href="/transaction">
3033
<span class="oi oi-document" aria-hidden="true"></span> Transaction
3134
</NavLink>

src/XUMM.NET.SDK.Tests/Clients/XummMiscClientTests.cs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Net;
34
using System.Net.Http;
45
using System.Threading.Tasks;
@@ -167,6 +168,63 @@ public void GetRatesAsync_WithInvalidCurrencyCode_ShouldThrowExceptionAsync(stri
167168
Assert.That(ex!.Message, Is.EqualTo("Value cannot be null or white space (Parameter 'currencyCode')"));
168169
}
169170

171+
[Test]
172+
[TestCase("31C3EC186C367DA66DFBD0E576D6170A2C1AB846BFC35FC0B49D202F2A8CDFD8")]
173+
public async Task GetHookInfoAsync_WithValidHookHash_ShouldReturnHookInfoAsync(string hookHash)
174+
{
175+
// Arrange
176+
_httpMessageHandlerMock.SetFixtureMessage(HttpStatusCode.OK, "hookhash");
177+
178+
// Act
179+
var result = await _subject.GetHookInfoAsync(hookHash);
180+
181+
// Assert
182+
AssertExtensions.AreEqual(hookHash, result.HookHash);
183+
AssertExtensions.AreEqual(MiscFixtures.XummHookInfo, result.HookInfo);
184+
}
185+
186+
[Test]
187+
[TestCase("31C3EC186C367DA66DFBD0E576D6170A2C1AB846BFC35FC0B49D202F2A8CDFD8")]
188+
public async Task GetHookInfoAsync_WithValidHookHash_ShouldContainHookHashInRequestUriAsync(string hookHash)
189+
{
190+
// Arrange
191+
_httpMessageHandlerMock.SetFixtureMessage(HttpStatusCode.OK, "hookhash");
192+
193+
// Act
194+
_ = await _subject.GetHookInfoAsync(hookHash);
195+
196+
// Assert
197+
_httpMessageHandlerMock.AssertRequestUri(HttpMethod.Get, $"/hookhash/{hookHash}");
198+
}
199+
200+
[Test]
201+
[TestCase(null)]
202+
[TestCase("")]
203+
[TestCase(" ")]
204+
public void GetHookInfoAsync_WithInvalidTxHash_ShouldThrowExceptionAsync(string hookHash)
205+
{
206+
// Act
207+
var ex = Assert.ThrowsAsync<ArgumentException>(() => _subject.GetHookInfoAsync(hookHash));
208+
209+
// Assert
210+
Assert.IsNotNull(ex);
211+
Assert.That(ex!.Message, Is.EqualTo("Invalid Hook Hash (expecting SHA-512Half) (Parameter 'hookHash')"));
212+
}
213+
214+
[Test]
215+
public async Task GetAllHookInfosAsync_ShouldReturnHookInfosAsync()
216+
{
217+
// Arrange
218+
_httpMessageHandlerMock.SetFixtureMessage(HttpStatusCode.OK, "hookhashes");
219+
220+
// Act
221+
var result = await _subject.GetAllHookInfosAsync();
222+
223+
// Assert
224+
AssertExtensions.AreEqual(MiscFixtures.XummHookHash, result.First().HookHash);
225+
AssertExtensions.AreEqual(MiscFixtures.XummHookInfo, result.First().HookInfo);
226+
}
227+
170228
[Test]
171229
[TestCase("C3951A3229506DB2C505ED248EFD3BBD8F232C7684732F38270BE9DE90F75134")]
172230
public async Task GetTransactionAsync_WithValidTxHash_ShouldReturnTransactionAsync(string txHash)
@@ -206,7 +264,7 @@ public void GetTransactionAsync_WithInvalidTxHash_ShouldThrowExceptionAsync(stri
206264

207265
// Assert
208266
Assert.IsNotNull(ex);
209-
Assert.That(ex!.Message, Is.EqualTo("Value cannot be null or white space (Parameter 'txHash')"));
267+
Assert.That(ex!.Message, Is.EqualTo("Invalid Transaction Hash (expecting SHA-512Half) (Parameter 'txHash')"));
210268
}
211269

212270
[Test]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "Savings",
3+
"description": "Efficiently manage your finances with the savings hook by automatically transferring a percentage of your transaction amount to a separate savings account to prevent accidental spending",
4+
"creator": {
5+
"name": "XRPL Labs",
6+
"mail": "support@xrpl-labs.com",
7+
"site": "xrpl-labs.com"
8+
},
9+
"xapp": "hooks.savings",
10+
"appuuid": "d21fe83d-64b9-4aef-8908-9a4952d8922c",
11+
"icon": "https://cdn.xumm.pro/cdn-cgi/image/width=500,height=500,quality=75,fit=crop/app-logo/1857a4e1-6711-4c37-bb54-7664e129e9bf.png",
12+
"verifiedAccounts": [],
13+
"audits": []
14+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"31C3EC186C367DA66DFBD0E576D6170A2C1AB846BFC35FC0B49D202F2A8CDFD8": {
3+
"name": "Savings",
4+
"description":
5+
"Efficiently manage your finances with the savings hook by automatically transferring a percentage of your transaction amount to a separate savings account to prevent accidental spending",
6+
"creator": {
7+
"name": "XRPL Labs",
8+
"mail": "support@xrpl-labs.com",
9+
"site": "xrpl-labs.com"
10+
},
11+
"xapp": "hooks.savings",
12+
"appuuid": "d21fe83d-64b9-4aef-8908-9a4952d8922c",
13+
"icon":
14+
"https://cdn.xumm.pro/cdn-cgi/image/width=500,height=500,quality=75,fit=crop/app-logo/1857a4e1-6711-4c37-bb54-7664e129e9bf.png",
15+
"verifiedAccounts": [],
16+
"audits": []
17+
}
18+
}

src/XUMM.NET.SDK.Tests/Fixtures/MiscFixtures.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,25 @@ internal static class MiscFixtures
113113
}
114114
};
115115

116+
internal static string XummHookHash => "31C3EC186C367DA66DFBD0E576D6170A2C1AB846BFC35FC0B49D202F2A8CDFD8";
117+
118+
internal static XummHookInfo XummHookInfo => new()
119+
{
120+
Name = "Savings",
121+
Description = "Efficiently manage your finances with the savings hook by automatically transferring a percentage of your transaction amount to a separate savings account to prevent accidental spending",
122+
Creator=new XummHookInfoCreator
123+
{
124+
Name = "XRPL Labs",
125+
Mail = "support@xrpl-labs.com",
126+
Site = "xrpl-labs.com"
127+
},
128+
Xapp = "hooks.savings",
129+
AppUuid = "d21fe83d-64b9-4aef-8908-9a4952d8922c",
130+
Icon = "https://cdn.xumm.pro/cdn-cgi/image/width=500,height=500,quality=75,fit=crop/app-logo/1857a4e1-6711-4c37-bb54-7664e129e9bf.png",
131+
Audits = new List<string>(),
132+
VerifiedAccounts = new List<string>()
133+
};
134+
116135
internal static XummTransaction XummTransaction => new()
117136
{
118137
Txid = "A17E4DEAD62BF705D9B73B4EAD2832F1C55C6C5A0067327A45E497FD8D31C0E3",

src/XUMM.NET.SDK.Tests/XUMM.NET.SDK.Tests.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
<Content Include="Data\account-meta.json">
1212
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
1313
</Content>
14+
<Content Include="Data\hookhashes.json">
15+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
16+
</Content>
17+
<Content Include="Data\hookhash.json">
18+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
19+
</Content>
1420
<Content Include="Data\xapp-event.json">
1521
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
1622
</Content>

src/XUMM.NET.SDK/Clients/Interfaces/IXummMiscClient.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Threading.Tasks;
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
23
using XUMM.NET.SDK.Enums;
34
using XUMM.NET.SDK.Models.Misc;
45

@@ -20,6 +21,17 @@ public interface IXummMiscClient
2021
/// </summary>
2122
Task<XummCuratedAssets> GetCuratedAssetsAsync();
2223

24+
/// <summary>
25+
/// This method allows you to get meta information for a specific Hook hash
26+
/// <param name="hookHash">The hook hash (64 hexadecimal characters).</param>
27+
/// </summary>
28+
Task<XummHookInfoResponse> GetHookInfoAsync(string hookHash);
29+
30+
/// <summary>
31+
/// This allows you to get all meta information for all Hooks known to Xumm.
32+
/// </summary>
33+
Task<List<XummHookInfoResponse>> GetAllHookInfosAsync();
34+
2335
/// <summary>
2436
/// Fetch transaction and outcome live from XRP ledger full history nodes (through the Xumm platform) containing parsed
2537
/// transaction outcome balance mutations.

src/XUMM.NET.SDK/Clients/XummHttpClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ private HttpRequestException GetHttpRequestException(HttpResponseMessage respons
155155
if (exception == null)
156156
{
157157
var apiError = JsonSerializer.Deserialize<XummApiError>(responseText);
158-
if (apiError != null)
158+
if (apiError?.Error != null)
159159
{
160160
var message = $"Error code {apiError.Error.Code}, see XUMM Dev Console, reference: '{apiError.Error.Reference}'.";
161161

src/XUMM.NET.SDK/Clients/XummMiscClient.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Threading.Tasks;
45
using XUMM.NET.SDK.Clients.Interfaces;
56
using XUMM.NET.SDK.Enums;
@@ -37,12 +38,31 @@ public async Task<XummCuratedAssets> GetCuratedAssetsAsync()
3738
return await _httpClient.GetAsync<XummCuratedAssets>("platform/curated-assets");
3839
}
3940

41+
/// <inheritdoc />
42+
public async Task<XummHookInfoResponse> GetHookInfoAsync(string hookHash)
43+
{
44+
if (!hookHash.IsSHA512H())
45+
{
46+
throw new ArgumentException("Invalid Hook Hash (expecting SHA-512Half)", nameof(hookHash));
47+
}
48+
49+
var result = await _httpClient.GetAsync<XummHookInfo>($"platform/hookhash/{hookHash}");
50+
return new XummHookInfoResponse(hookHash, result);
51+
}
52+
53+
/// <inheritdoc />
54+
public async Task<List<XummHookInfoResponse>> GetAllHookInfosAsync()
55+
{
56+
var result = await _httpClient.GetAsync<Dictionary<string, XummHookInfo>>("platform/hookhash");
57+
return result.Select(x => new XummHookInfoResponse(x.Key, x.Value)).ToList();
58+
}
59+
4060
/// <inheritdoc />
4161
public async Task<XummTransaction> GetTransactionAsync(string txHash)
4262
{
43-
if (string.IsNullOrWhiteSpace(txHash))
63+
if (!txHash.IsSHA512H())
4464
{
45-
throw new ArgumentException("Value cannot be null or white space", nameof(txHash));
65+
throw new ArgumentException("Invalid Transaction Hash (expecting SHA-512Half)", nameof(txHash));
4666
}
4767

4868
return await _httpClient.GetAsync<XummTransaction>($"platform/xrpl-tx/{txHash}");

0 commit comments

Comments
 (0)