Skip to content

Commit f5f637d

Browse files
committed
trying the conditional streams for the api client
1 parent 72ff4ae commit f5f637d

File tree

5 files changed

+77
-34
lines changed

5 files changed

+77
-34
lines changed

HarmonyDB.Index/HarmonyDB.Index.Api.Client/IndexApiClient.cs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using HarmonyDB.Index.Api.Model;
22
using HarmonyDB.Index.Api.Model.VExternal1;
33
using HarmonyDB.Index.Api.Model.VInternal;
4+
using HarmonyDB.Source.Api.Model;
45
using Microsoft.Extensions.Options;
56
using OneShelf.Common.Api.Client;
67

@@ -25,24 +26,36 @@ public async Task<TryImportResponse> TryImport(string url)
2526
Url = url,
2627
});
2728

29+
public async Task PingAll()
30+
{
31+
if (Options.ConditionalStreams?.Any() != true
32+
&& Options.Endpoint == null)
33+
{
34+
return;
35+
}
36+
37+
await Task.WhenAll((Options.ConditionalStreams?.Keys ?? Enumerable.Empty<string>())
38+
.Select(id => Ping(SourceApiUrls.V1Ping, id)));
39+
}
40+
2841
public async Task<SongsByChordsResponse> SongsByChords(SongsByChordsRequest request, ApiTraceBag? apiTraceBag = null)
29-
=> await PostWithCode<SongsByChordsRequest, SongsByChordsResponse>(IndexApiUrls.VExternal1SongsByChords, request, apiTraceBag: apiTraceBag);
42+
=> await PostWithCode<SongsByChordsRequest, SongsByChordsResponse>(IndexApiUrls.VExternal1SongsByChords, request, apiTraceBag: apiTraceBag, conditionalStreamId: "B");
3043

3144
public async Task<SongsByHeaderResponse> SongsByHeader(SongsByHeaderRequest request, ApiTraceBag? apiTraceBag = null)
32-
=> await PostWithCode<SongsByHeaderRequest, SongsByHeaderResponse>(IndexApiUrls.VExternal1SongsByHeader, request, apiTraceBag: apiTraceBag);
45+
=> await PostWithCode<SongsByHeaderRequest, SongsByHeaderResponse>(IndexApiUrls.VExternal1SongsByHeader, request, apiTraceBag: apiTraceBag, conditionalStreamId: "B");
3346

3447
public async Task<LoopsResponse> Loops(LoopsRequest request, ApiTraceBag? apiTraceBag = null)
35-
=> await PostWithCode<LoopsRequest, LoopsResponse>(IndexApiUrls.VExternal1Loops, request, apiTraceBag: apiTraceBag);
48+
=> await PostWithCode<LoopsRequest, LoopsResponse>(IndexApiUrls.VExternal1Loops, request, apiTraceBag: apiTraceBag, conditionalStreamId: "B");
3649

3750
public async Task<StructureLoopsResponse> StructureLoops(StructureLoopsRequest request, ApiTraceBag? apiTraceBag = null)
38-
=> await PostWithCode<StructureLoopsRequest, StructureLoopsResponse>(IndexApiUrls.VExternal1StructureLoops, request, apiTraceBag: apiTraceBag);
51+
=> await PostWithCode<StructureLoopsRequest, StructureLoopsResponse>(IndexApiUrls.VExternal1StructureLoops, request, apiTraceBag: apiTraceBag, conditionalStreamId: "A");
3952

4053
public async Task<StructureSongsResponse> StructureSongs(StructureSongsRequest request, ApiTraceBag? apiTraceBag = null)
41-
=> await PostWithCode<StructureSongsRequest, StructureSongsResponse>(IndexApiUrls.VExternal1StructureSongs, request, apiTraceBag: apiTraceBag);
54+
=> await PostWithCode<StructureSongsRequest, StructureSongsResponse>(IndexApiUrls.VExternal1StructureSongs, request, apiTraceBag: apiTraceBag, conditionalStreamId: "A");
4255

4356
public async Task<StructureLoopResponse> StructureLoop(StructureLoopRequest request, ApiTraceBag? apiTraceBag = null)
44-
=> await PostWithCode<StructureLoopRequest, StructureLoopResponse>(IndexApiUrls.VExternal1StructureLoop, request, apiTraceBag: apiTraceBag);
57+
=> await PostWithCode<StructureLoopRequest, StructureLoopResponse>(IndexApiUrls.VExternal1StructureLoop, request, apiTraceBag: apiTraceBag, conditionalStreamId: "A");
4558

4659
public async Task<StructureSongResponse> StructureSong(StructureSongRequest request, ApiTraceBag? apiTraceBag = null)
47-
=> await PostWithCode<StructureSongRequest, StructureSongResponse>(IndexApiUrls.VExternal1StructureSong, request, apiTraceBag: apiTraceBag);
60+
=> await PostWithCode<StructureSongRequest, StructureSongResponse>(IndexApiUrls.VExternal1StructureSong, request, apiTraceBag: apiTraceBag, conditionalStreamId: "A");
4861
}

HarmonyDB.Index/HarmonyDB.Index.Api/Functions/V1/Ping.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Net;
2+
using HarmonyDB.Index.Api.Client;
23
using HarmonyDB.Index.DownstreamApi.Client;
34
using HarmonyDB.Source.Api.Client;
45
using HarmonyDB.Source.Api.Model;
@@ -13,10 +14,12 @@ public class Ping
1314
{
1415
private readonly ILogger _logger;
1516
private readonly DownstreamApiClient _downstreamApiClient;
17+
private readonly IndexApiClient _indexApiClient;
1618

17-
public Ping(ILoggerFactory loggerFactory, DownstreamApiClient downstreamApiClient)
19+
public Ping(ILoggerFactory loggerFactory, DownstreamApiClient downstreamApiClient, IndexApiClient indexApiClient)
1820
{
1921
_downstreamApiClient = downstreamApiClient;
22+
_indexApiClient = indexApiClient;
2023
_logger = loggerFactory.CreateLogger<Ping>();
2124
}
2225

@@ -25,7 +28,9 @@ public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymou
2528
{
2629
_logger.LogInformation("C# HTTP trigger function processed a request.");
2730

28-
await _downstreamApiClient.PingAll();
31+
await Task.WhenAll(
32+
_downstreamApiClient.PingAll(),
33+
_indexApiClient.PingAll());
2934

3035
var response = req.CreateResponse(HttpStatusCode.OK);
3136
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");

OneShelf.Common/OneShelf.Common.Api.Client/ApiClientBase.cs

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ namespace OneShelf.Common.Api.Client;
1010
public class ApiClientBase<TClient>
1111
where TClient : ApiClientBase<TClient>
1212
{
13-
private readonly ApiClientOptions<TClient> _options;
1413
private readonly IHttpClientFactory _httpClientFactory;
1514

1615
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
@@ -20,27 +19,29 @@ public class ApiClientBase<TClient>
2019

2120
public ApiClientBase(IOptions<ApiClientOptions<TClient>> options, IHttpClientFactory httpClientFactory)
2221
{
23-
_options = options.Value;
22+
Options = options.Value;
2423
_httpClientFactory = httpClientFactory;
2524
}
2625

2726
protected ApiClientBase(ApiClientOptions<TClient> options, IHttpClientFactory httpClientFactory)
2827
{
29-
_options = options;
28+
Options = options;
3029
_httpClientFactory = httpClientFactory;
3130
}
31+
32+
protected ApiClientOptions<TClient> Options { get; }
3233

33-
public Identity GetServiceIdentity()
34+
public Identity GetServiceIdentity(string? conditionalStreamId = null)
3435
=> new()
3536
{
36-
Hash = _options.ServiceCode,
37+
Hash = Options.GetServiceCode(conditionalStreamId) ?? throw new("The service identity code is not configured."),
3738
};
3839

39-
protected async Task<TResponse> PostWithCode<TRequest, TResponse>(string url, TRequest request, CancellationToken cancellationToken = default, ApiTraceBag? apiTraceBag = null)
40+
protected async Task<TResponse> PostWithCode<TRequest, TResponse>(string url, TRequest request, CancellationToken cancellationToken = default, ApiTraceBag? apiTraceBag = null, string? conditionalStreamId = null)
4041
{
4142
var started = DateTime.Now;
4243
using var client = _httpClientFactory.CreateClient();
43-
using var httpResponseMessage = await client.PostAsJsonAsync(new Uri(_options.Endpoint, WithCode(url)), request, _jsonSerializerOptions, cancellationToken);
44+
using var httpResponseMessage = await client.PostAsJsonAsync(new Uri(Options.GetEndpoint(conditionalStreamId), WithCode(url)), request, _jsonSerializerOptions, cancellationToken);
4445
if (httpResponseMessage.StatusCode == HttpStatusCode.Unauthorized)
4546
{
4647
throw new UnauthorizedException(await httpResponseMessage.Content.ReadAsStringAsync(cancellationToken));
@@ -64,17 +65,17 @@ protected async Task<TResponse> PostWithCode<TRequest, TResponse>(string url, TR
6465
Method = "POST",
6566
Request = MaskIdentity(request),
6667
Response = response,
67-
Url = new Uri(_options.Endpoint, $"{url}?code={{code}}").ToString(),
68+
Url = new Uri(Options.GetEndpoint(conditionalStreamId), $"{url}?code={{code}}").ToString(),
6869
TimeTaken = DateTime.Now - started,
6970
});
7071

7172
return response;
7273
}
7374

74-
protected async Task<byte[]> PostWithCode<TRequest>(string url, TRequest request)
75+
protected async Task<byte[]> PostWithCode<TRequest>(string url, TRequest request, string? conditionalStreamId = null)
7576
{
7677
using var client = _httpClientFactory.CreateClient();
77-
using var response = await client.PostAsJsonAsync(new Uri(_options.Endpoint, WithCode(url)), request, _jsonSerializerOptions);
78+
using var response = await client.PostAsJsonAsync(new Uri(Options.GetEndpoint(conditionalStreamId), WithCode(url)), request, _jsonSerializerOptions);
7879
if (response.StatusCode == HttpStatusCode.Unauthorized)
7980
{
8081
throw new UnauthorizedException(await response.Content.ReadAsStringAsync());
@@ -94,11 +95,11 @@ protected async Task<byte[]> PostWithCode<TRequest>(string url, TRequest request
9495
return await response.Content.ReadAsByteArrayAsync();
9596
}
9697

97-
protected async Task<TResponse> Post<TRequest, TResponse>(string url, TRequest request, Action? unauthorized = null, CancellationToken cancellationToken = default, ApiTraceBag? apiTraceBag = null)
98+
protected async Task<TResponse> Post<TRequest, TResponse>(string url, TRequest request, Action? unauthorized = null, CancellationToken cancellationToken = default, ApiTraceBag? apiTraceBag = null, string? conditionalStreamId = null)
9899
{
99100
var started = DateTime.Now;
100101
using var client = _httpClientFactory.CreateClient();
101-
var uri = new Uri(_options.Endpoint, url);
102+
var uri = new Uri(Options.GetEndpoint(conditionalStreamId), url);
102103
using var httpResponseMessage = await client.PostAsJsonAsync(uri, request, _jsonSerializerOptions, cancellationToken);
103104
if (httpResponseMessage.StatusCode == HttpStatusCode.Unauthorized)
104105
{
@@ -131,11 +132,11 @@ protected async Task<TResponse> Post<TRequest, TResponse>(string url, TRequest r
131132
return response;
132133
}
133134

134-
protected async Task<TResponse> Get<TResponse>(string url, CancellationToken cancellationToken = default, ApiTraceBag? apiTraceBag = null)
135+
protected async Task<TResponse> Get<TResponse>(string url, CancellationToken cancellationToken = default, ApiTraceBag? apiTraceBag = null, string? conditionalStreamId = null)
135136
{
136137
var started = DateTime.Now;
137138
using var client = _httpClientFactory.CreateClient();
138-
var uri = new Uri(_options.Endpoint, url);
139+
var uri = new Uri(Options.GetEndpoint(conditionalStreamId), url);
139140
using var httpResponseMessage = await client.GetAsync(uri, cancellationToken);
140141

141142
if (httpResponseMessage.StatusCode == HttpStatusCode.TooManyRequests)
@@ -162,11 +163,11 @@ protected async Task<TResponse> Get<TResponse>(string url, CancellationToken can
162163
return response;
163164
}
164165

165-
protected async Task<string> PostDirect(string url, string request, Action? unauthorized = null, ApiTraceBag? apiTraceBag = null)
166+
protected async Task<string> PostDirect(string url, string request, Action? unauthorized = null, ApiTraceBag? apiTraceBag = null, string? conditionalStreamId = null)
166167
{
167168
var started = DateTime.Now;
168169
using var client = _httpClientFactory.CreateClient();
169-
using var httpResponseMessage = await client.PostAsync(new Uri(_options.Endpoint, url), new StringContent(request, MediaTypeHeaderValue.Parse("application/json")));
170+
using var httpResponseMessage = await client.PostAsync(new Uri(Options.GetEndpoint(conditionalStreamId), url), new StringContent(request, MediaTypeHeaderValue.Parse("application/json")));
170171
if (httpResponseMessage.StatusCode == HttpStatusCode.Unauthorized)
171172
{
172173
unauthorized?.Invoke();
@@ -191,23 +192,23 @@ protected async Task<string> PostDirect(string url, string request, Action? unau
191192
Method = "POST",
192193
Request = MaskIdentity(request),
193194
Response = response,
194-
Url = new Uri(_options.Endpoint, $"{url}?code={{code}}").ToString(),
195+
Url = new Uri(Options.GetEndpoint(conditionalStreamId), $"{url}?code={{code}}").ToString(),
195196
TimeTaken = DateTime.Now - started,
196197
});
197198

198199
return response;
199200
}
200201

201-
protected async Task Ping(string url)
202+
protected async Task Ping(string url, string? conditionalStreamId = null)
202203
{
203204
using var client = _httpClientFactory.CreateClient();
204-
using var response = await client.GetAsync(new Uri(_options.Endpoint, url));
205+
using var response = await client.GetAsync(new Uri(Options.GetEndpoint(conditionalStreamId), url));
205206
response.EnsureSuccessStatusCode();
206207
}
207208

208-
private string WithCode(string url)
209+
private string WithCode(string url, string? conditionalStreamId = null)
209210
{
210-
return $"{url}?code={_options.MasterCode}";
211+
return $"{url}?code={Options.GetMasterCode(conditionalStreamId)}";
211212
}
212213

213214
private static TRequest MaskIdentity<TRequest>(TRequest request)
Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
namespace OneShelf.Common.Api.Client;
22

3-
public class ApiClientOptions<TClient>
3+
public class ApiClientOptions<TClient> : ApiClientOptionsEndpoint<TClient>
44
{
5-
public required Uri Endpoint { get; set; }
5+
public Dictionary<string, ApiClientOptionsEndpoint<TClient>>? ConditionalStreams { get; set; }
66

7-
public required string MasterCode { get; set; }
7+
public Uri GetEndpoint(string? conditionalStreamId)
8+
=> (conditionalStreamId == null
9+
? Endpoint
10+
: ConditionalStreams?.GetValueOrDefault(conditionalStreamId)?.Endpoint ?? Endpoint)
11+
?? throw new($"The endpoint for conditional stream '{conditionalStreamId}' is not configured, the default fallback value is not available, either.");
812

9-
public required string ServiceCode { get; set; }
13+
public string GetMasterCode(string? conditionalStreamId)
14+
=> (conditionalStreamId == null
15+
? MasterCode
16+
: ConditionalStreams?.GetValueOrDefault(conditionalStreamId)?.MasterCode ?? MasterCode)
17+
?? throw new($"The master code for conditional stream '{conditionalStreamId}' is not configured, the default fallback value is not available, either.");
18+
19+
public string GetServiceCode(string? conditionalStreamId)
20+
=> (conditionalStreamId == null
21+
? ServiceCode
22+
: ConditionalStreams?.GetValueOrDefault(conditionalStreamId)?.ServiceCode ?? ServiceCode)
23+
?? throw new($"The service code for conditional stream '{conditionalStreamId}' is not configured, the default fallback value is not available, either.");
1024
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace OneShelf.Common.Api.Client;
2+
3+
public class ApiClientOptionsEndpoint<TClient>
4+
{
5+
public Uri? Endpoint { get; set; }
6+
7+
public string? MasterCode { get; set; }
8+
9+
public string? ServiceCode { get; set; }
10+
}

0 commit comments

Comments
 (0)