Skip to content
This repository was archived by the owner on Sep 3, 2024. It is now read-only.

Commit a5e7c22

Browse files
stream browser resource
1 parent 7f6fe73 commit a5e7c22

5 files changed

Lines changed: 157 additions & 0 deletions

File tree

src/SqlStreamStore.HAL/Constants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public static class Relations
6060
public const string DeleteStream = StreamStorePrefix + ":delete-stream";
6161
public const string DeleteMessage = StreamStorePrefix + ":delete-message";
6262
public const string Find = StreamStorePrefix + ":find";
63+
public const string ListStreams = StreamStorePrefix + ":list-streams";
6364
}
6465

6566
public static class Streams
@@ -71,6 +72,7 @@ public static class Streams
7172
public static PathString AllStreamPath = new PathString($"/{All}");
7273
public static PathString StreamsPath = new PathString($"/{Stream}");
7374
public static PathString IndexPath = new PathString("/");
75+
public static PathString StreamBrowserPath = AllStreamPath.Add("/list");
7476
}
7577

7678
public static class ReadDirection

src/SqlStreamStore.HAL/SqlStreamStoreHalMiddleware.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
using SqlStreamStore.HAL.Docs;
1212
using SqlStreamStore.HAL.Index;
1313
using SqlStreamStore.HAL.Logging;
14+
using SqlStreamStore.HAL.Resources;
15+
using SqlStreamStore.HAL.StreamBrowser;
1416
using SqlStreamStore.HAL.StreamMessage;
1517
using SqlStreamStore.HAL.StreamMetadata;
1618
using SqlStreamStore.HAL.Streams;
@@ -62,6 +64,7 @@ public static IApplicationBuilder UseSqlStreamStoreHal(
6264
var index = new IndexResource(streamStore);
6365
var allStream = new AllStreamResource(streamStore, options.UseCanonicalUrls);
6466
var allStreamMessages = new AllStreamMessageResource(streamStore);
67+
var streamBrowser = new StreamBrowserResource(streamStore);
6568
var streams = new StreamResource(streamStore);
6669
var streamMetadata = new StreamMetadataResource(streamStore);
6770
var streamMessages = new StreamMessageResource(streamStore);
@@ -83,6 +86,7 @@ public static IApplicationBuilder UseSqlStreamStoreHal(
8386
.UseIndex(index)
8487
.UseAllStream(allStream)
8588
.UseAllStreamMessage(allStreamMessages)
89+
.UseStreamBrowser(streamBrowser)
8690
.UseStreams(streams)
8791
.UseStreamMetadata(streamMetadata)
8892
.UseStreamMessages(streamMessages);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
namespace SqlStreamStore.HAL.StreamBrowser
2+
{
3+
using System;
4+
using System.Net.Http;
5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Http;
7+
using SqlStreamStore.HAL.Resources;
8+
using MidFunc = System.Func<
9+
Microsoft.AspNetCore.Http.HttpContext,
10+
System.Func<System.Threading.Tasks.Task>,
11+
System.Threading.Tasks.Task
12+
>;
13+
14+
internal static class StreamBrowserMiddleware
15+
{
16+
public static IApplicationBuilder UseStreamBrowser(
17+
this IApplicationBuilder builder,
18+
StreamBrowserResource streamBrowser)
19+
=> builder.MapWhen(IsMatch, Configure(streamBrowser));
20+
21+
private static bool IsMatch(HttpContext context)
22+
=> context.Request.Path.IsStreamBrowser();
23+
24+
public static bool IsStreamBrowser(this PathString requestPath)
25+
=> requestPath == Constants.Streams.StreamBrowserPath;
26+
27+
private static Action<IApplicationBuilder> Configure(StreamBrowserResource streamBrowser)
28+
=> builder => builder
29+
.UseMiddlewareLogging(typeof(StreamBrowserMiddleware))
30+
.MapWhen(HttpMethod.Get, inner => inner.Use(BrowseStreams(streamBrowser)));
31+
32+
private static MidFunc BrowseStreams(StreamBrowserResource streamBrowser)
33+
=> async (context, next) =>
34+
{
35+
var response = await streamBrowser.Get(
36+
new ListStreamsOperation(context.Request),
37+
context.RequestAborted);
38+
39+
await context.WriteResponse(response);
40+
};
41+
}
42+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
namespace SqlStreamStore.HAL.StreamBrowser
2+
{
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Http;
6+
using SqlStreamStore.Streams;
7+
8+
internal class ListStreamsOperation : IStreamStoreOperation<ListStreamsPage>
9+
{
10+
public Pattern Pattern { get; }
11+
public string ContinuationToken { get; }
12+
public int MaxCount { get; }
13+
public string PatternType { get; }
14+
public PathString Path { get; }
15+
16+
public ListStreamsOperation(HttpRequest request)
17+
{
18+
Path = request.Path;
19+
if(request.Query.TryGetValueCaseInsensitive('t', out var patternType))
20+
{
21+
PatternType = patternType;
22+
}
23+
24+
if(!request.Query.TryGetValueCaseInsensitive('p', out var pattern))
25+
{
26+
Pattern = Pattern.Anything();
27+
}
28+
else
29+
{
30+
switch(PatternType)
31+
{
32+
case "s":
33+
Pattern = Pattern.StartsWith(pattern);
34+
break;
35+
case "e":
36+
Pattern = Pattern.EndsWith(pattern);
37+
break;
38+
default:
39+
Pattern = Pattern.Anything();
40+
break;
41+
}
42+
}
43+
44+
if(request.Query.TryGetValueCaseInsensitive('c', out var continuationToken))
45+
{
46+
ContinuationToken = continuationToken;
47+
}
48+
49+
MaxCount = request.Query.TryGetValueCaseInsensitive('m', out var m)
50+
&& int.TryParse(m, out var maxCount)
51+
? maxCount
52+
: 100;
53+
}
54+
55+
public Task<ListStreamsPage> Invoke(IStreamStore streamStore, CancellationToken cancellationToken)
56+
=> streamStore.ListStreams(Pattern, MaxCount, ContinuationToken, cancellationToken);
57+
}
58+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
namespace SqlStreamStore.HAL.Resources
2+
{
3+
using System;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Halcyon.HAL;
7+
using SqlStreamStore.HAL.StreamBrowser;
8+
9+
internal class StreamBrowserResource
10+
{
11+
private readonly IStreamStore _streamStore;
12+
13+
public StreamBrowserResource(IStreamStore streamStore)
14+
{
15+
_streamStore = streamStore;
16+
}
17+
18+
public async Task<Response> Get(ListStreamsOperation operation, CancellationToken cancellationToken)
19+
{
20+
var listStreamsPage = await operation.Invoke(_streamStore, cancellationToken);
21+
22+
var halResponse = new HALResponse(new
23+
{
24+
listStreamsPage.ContinuationToken
25+
})
26+
.AddEmbeddedCollection(
27+
Constants.Relations.Feed,
28+
Array.ConvertAll(
29+
listStreamsPage.StreamIds,
30+
streamId =>
31+
{
32+
var href = $"../../streams/{streamId}";
33+
return new HALResponse(null)
34+
.AddLinks(
35+
new Link(Constants.Relations.Self, href, streamId),
36+
new Link(Constants.Relations.Feed, href, streamId));
37+
}
38+
));
39+
40+
if(listStreamsPage.ContinuationToken != null)
41+
{
42+
halResponse.AddLinks(
43+
new Link(
44+
Constants.Relations.Next,
45+
$"browser?p={operation.Pattern.Value}&t={operation.PatternType}&c={listStreamsPage.ContinuationToken}&m={operation.MaxCount}"));
46+
}
47+
48+
return new HalJsonResponse(halResponse);
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)