Skip to content

Commit 41a1755

Browse files
xApp (#28)
* Implemented `IXummXAppClient` * Added OTT Event and Push notification examples in the Blazor Server App. * Added `GetOneTimeTokenDataAsync` unit tests * Added `ReFetchOneTimeTokenDataAsync` unit tests * Added Event/Push unit tests
1 parent 2d7c4d1 commit 41a1755

26 files changed

+921
-74
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
@page "/onetimetoken"
2+
@using XUMM.NET.SDK.Models.XApp
3+
@using System.Text.Json
4+
5+
<PageTitle>One Time Token</PageTitle>
6+
7+
<ResponseAlertBox @ref="_responseAlertBox"></ResponseAlertBox>
8+
9+
<h1>One Time Token</h1>
10+
11+
<div class="row">
12+
<div class="col-md-6">
13+
<XummSdkCredentials @ref="_xummSdkCredentials"></XummSdkCredentials>
14+
15+
<div class="mb-3">
16+
<label for="onetimetoken" class="form-label">One Time Token</label>
17+
<input id="onetimetoken" type="text" class="form-control" placeholder="UUID (token) received (URL get param.) when Xumm launches your xApp URL" aria-label="One Time Token" aria-describedby="basic-addon2" @bind="_oneTimeToken">
18+
</div>
19+
<div class="btn-group mb-3" role="group">
20+
<button class="btn btn-primary" type="button" @onclick="GetOneTimeTokenDataAsync">Get OTT data</button>
21+
</div>
22+
</div>
23+
</div>
24+
25+
@if (_ottData != null)
26+
{
27+
<div class="row">
28+
<div class="mb-3">
29+
<h2>One Time Token Data</h2>
30+
<div class="text-break">
31+
<code>@JsonSerializer.Serialize(_ottData)</code>
32+
</div>
33+
</div>
34+
</div>
35+
}
36+
37+
@code {
38+
private ResponseAlertBox? _responseAlertBox;
39+
private XummSdkCredentials? _xummSdkCredentials;
40+
private XummXAppOttResponse? _ottData;
41+
42+
private string _oneTimeToken = default!;
43+
44+
private async Task GetOneTimeTokenDataAsync()
45+
{
46+
if (string.IsNullOrWhiteSpace(_oneTimeToken))
47+
{
48+
return;
49+
}
50+
51+
_ottData = await _responseAlertBox!.GetResponseAndSetAlertAsync(() => _xummSdkCredentials!.XApp.GetOneTimeTokenDataAsync(_oneTimeToken));
52+
_responseAlertBox.SetAlert("One Time Token Data", _ottData != null);
53+
}
54+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
@page "/onetimetokenevent"
2+
@using XUMM.NET.SDK.Models.XApp
3+
@using System.Text.Json
4+
5+
<PageTitle>One Time Token Event</PageTitle>
6+
7+
<ResponseAlertBox @ref="_responseAlertBox"></ResponseAlertBox>
8+
9+
<h1>One Time Token Event</h1>
10+
11+
<div class="row">
12+
<div class="col-md-6">
13+
<XummSdkCredentials @ref="_xummSdkCredentials"></XummSdkCredentials>
14+
15+
<div class="mb-3">
16+
<label for="usertoken" class="form-label">User Token</label>
17+
<input id="usertoken" type="text" class="form-control" placeholder="The User Token to send the event & push notification to. Obtained with a Sign Request (payload)" aria-label="usertoken" aria-describedby="basic-addon2" @bind="_userToken">
18+
</div>
19+
<div class="mb-3">
20+
<label for="subtitle" class="form-label">Subtitle</label>
21+
<input id="subtitle" type="text" class="form-control" placeholder="Push notification subtitle & subtitle in the Event list (Request tab)" aria-label="subtitle" aria-describedby="basic-addon2" @bind="_subtitle">
22+
</div>
23+
<div class="mb-3">
24+
<label for="body" class="form-label">Body</label>
25+
<textarea id="body" class="form-control" placeholder="Description (text) for the push notification" aria-label="Body" aria-describedby="basic-addon2" rows="10" @bind="_body"></textarea>
26+
</div>
27+
<div class="mb-3">
28+
<label for="data" class="form-label">Data</label>
29+
<textarea id="data" class="form-control" placeholder="Free form JSON to pass to the Request & push notification context (passed to the JSON received when calling the ott endpoint)" aria-label="Data" aria-describedby="basic-addon2" rows="10" @bind="_data"></textarea>
30+
</div>
31+
<div class="mb-3 form-check">
32+
<input class="form-check-input" type="checkbox" value="" id="silent" @bind="_silent">
33+
<label class="form-check-label" for="silent">
34+
Silent (Only create the event in the user's Event list, don't send a push notification)
35+
</label>
36+
</div>
37+
<div class="btn-group mb-3" role="group">
38+
<button class="btn btn-primary" type="button" @onclick="GetOneTimeTokenDataAsync">Send Event</button>
39+
</div>
40+
</div>
41+
@if (_eventResponse != null)
42+
{
43+
<div class="col-md-6">
44+
<div class="mb-3">
45+
<h2>One Time Token Event Data</h2>
46+
<div class="text-break">
47+
<code>@JsonSerializer.Serialize(_eventResponse)</code>
48+
</div>
49+
</div>
50+
</div>
51+
}
52+
</div>
53+
54+
@code {
55+
private ResponseAlertBox? _responseAlertBox;
56+
private XummSdkCredentials? _xummSdkCredentials;
57+
private XummXAppEventResponse? _eventResponse;
58+
59+
private string _userToken = default!;
60+
private string? _subtitle;
61+
private string _body = default!;
62+
private string? _data;
63+
private bool _silent;
64+
65+
private async Task GetOneTimeTokenDataAsync()
66+
{
67+
if (string.IsNullOrWhiteSpace(_userToken) || string.IsNullOrWhiteSpace(_body))
68+
{
69+
return;
70+
}
71+
var request = new XummXAppEventRequest
72+
{
73+
UserToken = _userToken,
74+
Subtitle = _subtitle,
75+
Body = _body,
76+
Data = !string.IsNullOrWhiteSpace(_data) ? JsonDocument.Parse(_data) : null,
77+
Silent = _silent
78+
};
79+
80+
_eventResponse = await _responseAlertBox!.GetResponseAndSetAlertAsync(() => _xummSdkCredentials!.XApp.EventAsync(request));
81+
_responseAlertBox.SetAlert("One Time Token Event", _eventResponse != null);
82+
}
83+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
@page "/onetimetokenpush"
2+
@using XUMM.NET.SDK.Models.XApp
3+
@using System.Text.Json
4+
5+
<PageTitle>One Time Token Push</PageTitle>
6+
7+
<ResponseAlertBox @ref="_responseAlertBox"></ResponseAlertBox>
8+
9+
<h1>One Time Token Push</h1>
10+
11+
<div class="row">
12+
<div class="col-md-6">
13+
<XummSdkCredentials @ref="_xummSdkCredentials"></XummSdkCredentials>
14+
15+
<div class="mb-3">
16+
<label for="usertoken" class="form-label">User Token</label>
17+
<input id="usertoken" type="text" class="form-control" placeholder="The User Token to send the push & push notification to. Obtained with a Sign Request (payload)" aria-label="usertoken" aria-describedby="basic-addon2" @bind="_userToken">
18+
</div>
19+
<div class="mb-3">
20+
<label for="subtitle" class="form-label">Subtitle</label>
21+
<input id="subtitle" type="text" class="form-control" placeholder="Push notification subtitle & subtitle in the Push list (Request tab)" aria-label="subtitle" aria-describedby="basic-addon2" @bind="_subtitle">
22+
</div>
23+
<div class="mb-3">
24+
<label for="body" class="form-label">Body</label>
25+
<textarea id="body" class="form-control" placeholder="Description (text) for the push notification" aria-label="Body" aria-describedby="basic-addon2" rows="10" @bind="_body"></textarea>
26+
</div>
27+
<div class="mb-3">
28+
<label for="data" class="form-label">Data</label>
29+
<textarea id="data" class="form-control" placeholder="Free form JSON to pass to the Request & push notification context (passed to the JSON received when calling the ott endpoint)" aria-label="Data" aria-describedby="basic-addon2" rows="10" @bind="_data"></textarea>
30+
</div>
31+
<div class="btn-group mb-3" role="group">
32+
<button class="btn btn-primary" type="button" @onclick="GetOneTimeTokenDataAsync">Send Push</button>
33+
</div>
34+
</div>
35+
@if (_pushResponse != null)
36+
{
37+
<div class="col-md-6">
38+
<div class="mb-3">
39+
<h2>One Time Token Push Data</h2>
40+
<div class="text-break">
41+
<code>@JsonSerializer.Serialize(_pushResponse)</code>
42+
</div>
43+
</div>
44+
</div>
45+
}
46+
</div>
47+
48+
@code {
49+
private ResponseAlertBox? _responseAlertBox;
50+
private XummSdkCredentials? _xummSdkCredentials;
51+
private XummXAppPushResponse? _pushResponse;
52+
53+
private string _userToken = default!;
54+
private string? _subtitle;
55+
private string _body = default!;
56+
private string? _data;
57+
58+
private async Task GetOneTimeTokenDataAsync()
59+
{
60+
if (string.IsNullOrWhiteSpace(_userToken) || string.IsNullOrWhiteSpace(_body))
61+
{
62+
return;
63+
}
64+
65+
var request = new XummXAppPushRequest
66+
{
67+
UserToken = _userToken,
68+
Subtitle = _subtitle,
69+
Body = _body,
70+
Data = !string.IsNullOrWhiteSpace(_data) ? JsonDocument.Parse(_data) : null
71+
};
72+
73+
_pushResponse = await _responseAlertBox!.GetResponseAndSetAlertAsync(() => _xummSdkCredentials!.XApp.PushAsync(request));
74+
_responseAlertBox.SetAlert("One Time Token Push", _pushResponse != null);
75+
}
76+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
@page "/refetchonetimetoken"
2+
@using XUMM.NET.SDK.Models.XApp
3+
@using System.Text.Json
4+
5+
<PageTitle>Re-Fetch One Time Token</PageTitle>
6+
7+
<ResponseAlertBox @ref="_responseAlertBox"></ResponseAlertBox>
8+
9+
<h1>One Time Token</h1>
10+
11+
<div class="row">
12+
<div class="col-md-6">
13+
<XummSdkCredentials @ref="_xummSdkCredentials"></XummSdkCredentials>
14+
15+
<div class="mb-3">
16+
<label for="onetimetoken" class="form-label">One Time Token</label>
17+
<input id="onetimetoken" type="text" class="form-control" placeholder="UUID (token) received (URL get param.) when Xumm launches your xApp URL" aria-label="One Time Token" aria-describedby="basic-addon2" @bind="_oneTimeToken">
18+
</div>
19+
<div class="mb-3">
20+
<label for="deviceid" class="form-label">Device ID</label>
21+
<input id="deviceid" type="text" class="form-control" placeholder="The device ID that retrieved the One Time Token data." aria-label="Device ID" aria-describedby="basic-addon2" @bind="_deviceId">
22+
</div>
23+
<div class="btn-group mb-3" role="group">
24+
<button class="btn btn-primary" type="button" @onclick="GetOneTimeTokenDataAsync">Get OTT data</button>
25+
</div>
26+
</div>
27+
</div>
28+
29+
@if (_ottData != null)
30+
{
31+
<div class="row">
32+
<div class="mb-3">
33+
<h2>One Time Token Data</h2>
34+
<div class="text-break">
35+
<code>@JsonSerializer.Serialize(_ottData)</code>
36+
</div>
37+
</div>
38+
</div>
39+
}
40+
41+
@code {
42+
private ResponseAlertBox? _responseAlertBox;
43+
private XummSdkCredentials? _xummSdkCredentials;
44+
private XummXAppOttResponse? _ottData;
45+
46+
private string _oneTimeToken = default!;
47+
private string _deviceId = default!;
48+
49+
private async Task GetOneTimeTokenDataAsync()
50+
{
51+
if (string.IsNullOrWhiteSpace(_oneTimeToken) && string.IsNullOrWhiteSpace(_deviceId))
52+
{
53+
return;
54+
}
55+
56+
_ottData = await _responseAlertBox!.GetResponseAndSetAlertAsync(() => _xummSdkCredentials!.XApp.ReFetchOneTimeTokenDataAsync(_oneTimeToken, _deviceId));
57+
_responseAlertBox.SetAlert("Re-fetch One Time Token Data", _ottData != null);
58+
}
59+
}

examples/XUMM.NET.ServerApp/Pages/XummSdkCredentials.razor

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
@inject IXummMiscClient MiscClient;
55
@inject IXummMiscAppStorageClient MiscAppStorageClient;
66
@inject IXummPayloadClient PayloadClient;
7+
@inject IXummXAppClient XAppClient;
78

89
<div class="mb-3">
910
<label for="apikey" class="form-label">API Key <small>(Optional)</small></label>
@@ -47,4 +48,5 @@
4748
public IXummMiscAppStorageClient AppStorage => GetXummSdk()?.AppStorage ?? MiscAppStorageClient;
4849
public IXummMiscClient Miscellaneous => GetXummSdk()?.Miscellaneous ?? MiscClient;
4950
public IXummPayloadClient Payload => GetXummSdk()?.Payload ?? PayloadClient;
51+
public IXummXAppClient XApp => GetXummSdk()?.XApp ?? XAppClient;
5052
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,26 @@
7474
</NavLink>
7575
}
7676
</div>
77+
<div class="nav-item px-3">
78+
<NavLink class="nav-link" @onclick='(() => ToggleSubNavMenu("XApp"))'>
79+
<span class="oi oi-chevron-@(IsSubNavMenuExpanded("XApp") ? "bottom": "right")" aria-hidden="true"></span> xApp
80+
</NavLink>
81+
@if (IsSubNavMenuExpanded("XApp"))
82+
{
83+
<NavLink class="nav-link ps-4" href="/onetimetoken">
84+
<span class="oi oi-clock" aria-hidden="true"></span> OTT Data
85+
</NavLink>
86+
<NavLink class="nav-link ps-4" href="/refetchonetimetoken">
87+
<span class="oi oi-reload" aria-hidden="true"></span> Re-Fetch OTT Data
88+
</NavLink>
89+
<NavLink class="nav-link ps-4" href="/onetimetokenevent">
90+
<span class="oi oi-task" aria-hidden="true"></span> Event
91+
</NavLink>
92+
<NavLink class="nav-link ps-4" href="/onetimetokenpush">
93+
<span class="oi oi-bolt" aria-hidden="true"></span> Push
94+
</NavLink>
95+
}
96+
</div>
7797
</nav>
7898
</div>
7999

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

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ namespace XUMM.NET.SDK.Tests.Clients;
1717
[TestFixture]
1818
public class XummMiscClientTests
1919
{
20+
private Mock<XummHttpClient> _xummHttpClient = default!;
21+
private Mock<HttpMessageHandler> _httpMessageHandlerMock = default!;
22+
private Mock<IHttpClientFactory> _httpClientFactory = default!;
23+
private XummMiscClient _subject = default!;
24+
2025
[SetUp]
2126
public void SetUp()
2227
{
@@ -38,11 +43,6 @@ public void SetUp()
3843
_subject = new XummMiscClient(_xummHttpClient.Object);
3944
}
4045

41-
private Mock<XummHttpClient> _xummHttpClient = default!;
42-
private Mock<HttpMessageHandler> _httpMessageHandlerMock = default!;
43-
private Mock<IHttpClientFactory> _httpClientFactory = default!;
44-
private XummMiscClient _subject = default!;
45-
4646
[Test]
4747
public async Task GetPingAsync_ShouldReturnPongAsync()
4848
{
@@ -95,9 +95,7 @@ public void GetKycStatusAsync_WithUserTokenAndInvalidStatus_ShouldThrowException
9595

9696
// Assert
9797
Assert.IsNotNull(ex);
98-
Assert.That(ex!.Message,
99-
Is.EqualTo(
100-
$"Specified argument was out of the range of valid values. (Parameter 'name'){Environment.NewLine}Actual value was INVALID_STATUS."));
98+
Assert.That(ex!.Message, Is.EqualTo($"Specified argument was out of the range of valid values. (Parameter 'name'){Environment.NewLine}Actual value was INVALID_STATUS."));
10199
}
102100

103101
[Test]
@@ -131,8 +129,7 @@ public void GetKycStatusAsync_WithInvalidUserTokenAndAccount_ShouldThrowExceptio
131129
[TestCase(null)]
132130
[TestCase("")]
133131
[TestCase(" ")]
134-
public void GetKycStatusAsync_WithNullOrWhiteSpaceUserTokenAndAccount_ShouldThrowExceptionAsync(
135-
string userTokenOrAccount)
132+
public void GetKycStatusAsync_WithNullOrWhiteSpaceUserTokenAndAccount_ShouldThrowExceptionAsync(string userTokenOrAccount)
136133
{
137134
// Act
138135
var ex = Assert.ThrowsAsync<ArgumentException>(() => _subject.GetKycStatusAsync(userTokenOrAccount));
@@ -255,12 +252,8 @@ public void VerifyUserTokenAsync_WithInvalidUserToken_ShouldThrowExceptionAsync(
255252
}
256253

257254
[Test]
258-
[TestCase("user-tokens", new[]
259-
{
260-
"691d5ae8-968b-44c8-8835-f25da1214f35", "b12b59a8-83c8-4bc0-8acb-1d1d743871f1"
261-
})]
262-
public async Task VerifyUserTokensAsync_WithValidUserTokens_ShouldReturnUserTokensAsync(string fixture,
263-
string[] userTokens)
255+
[TestCase("user-tokens", new[] { "691d5ae8-968b-44c8-8835-f25da1214f35", "b12b59a8-83c8-4bc0-8acb-1d1d743871f1" })]
256+
public async Task VerifyUserTokensAsync_WithValidUserTokens_ShouldReturnUserTokensAsync(string fixture, string[] userTokens)
264257
{
265258
// Arrange
266259
_httpMessageHandlerMock.SetFixtureMessage(HttpStatusCode.OK, fixture);
@@ -368,8 +361,7 @@ public void GetAvatarUrl_WithInvalidPadding_ShouldThrowException(string account,
368361
[TestCase("rBLomsmaSJ1ttBmS3WPmPpWLAUDKFwiF9Q", 200, 5, "https://xumm.app/avatar/rBLomsmaSJ1ttBmS3WPmPpWLAUDKFwiF9Q_200_5.png")]
369362
[TestCase("rBLomsmaSJ1ttBmS3WPmPpWLAUDKFwiF9Q", 250, 0, "https://xumm.app/avatar/rBLomsmaSJ1ttBmS3WPmPpWLAUDKFwiF9Q_250_0.png")]
370363
[TestCase("rBLomsmaSJ1ttBmS3WPmPpWLAUDKFwiF9Q", 500, 2, "https://xumm.app/avatar/rBLomsmaSJ1ttBmS3WPmPpWLAUDKFwiF9Q_500_2.png")]
371-
public void GetAvatarUrl_WithValidDimensions_ShouldReturnAvatarUrl(string account, int dimensions, int padding,
372-
string expected)
364+
public void GetAvatarUrl_WithValidDimensions_ShouldReturnAvatarUrl(string account, int dimensions, int padding, string expected)
373365
{
374366
// Act
375367
var result = _subject.GetAvatarUrl(account, dimensions, padding);

0 commit comments

Comments
 (0)