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

Commit b41f5c5

Browse files
moved link generation back into each relevant resource; ensure that all self links have a matching streamStore: link
1 parent 1fd8d4e commit b41f5c5

9 files changed

Lines changed: 349 additions & 299 deletions

File tree

src/SqlStreamStore.HAL.Tests/AllStreamMessageTests.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public AllStreamMessageTests()
1515

1616
public void Dispose() => _fixture.Dispose();
1717
private readonly SqlStreamStoreHalMiddlewareFixture _fixture;
18-
private const string HeadOfAll = "stream?d=b&m=20&p=-1";
18+
private const string HeadOfAll = "../stream?d=b&m=20&p=-1";
1919

2020
[Fact]
2121
public async Task read_single_message_all_stream()
@@ -29,10 +29,16 @@ public async Task read_single_message_all_stream()
2929

3030
var resource = await response.AsHal();
3131

32-
resource.Links.Keys.ShouldBe(new[] { Constants.Relations.Self, "streamStore:feed" });
32+
resource.Links.Keys.ShouldBe(new[]
33+
{
34+
Constants.Relations.Self,
35+
Constants.Relations.Message,
36+
Constants.Relations.Feed
37+
});
3338

34-
resource.ShouldLink(Constants.Relations.Self, "/stream/0");
35-
resource.ShouldLink("streamStore:feed", HeadOfAll);
39+
resource.ShouldLink(Constants.Relations.Self, "0");
40+
resource.ShouldLink(Constants.Relations.Message, "0");
41+
resource.ShouldLink(Constants.Relations.Feed, HeadOfAll);
3642
}
3743
}
3844

src/SqlStreamStore.HAL.Tests/StreamMessageTests.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,22 @@ public async Task read_single_message_stream()
2929

3030
var resource = await response.AsHal();
3131

32-
resource.Links.Keys.ShouldBe(new[] { Constants.Relations.Self, Constants.Relations.First, Constants.Relations.Next, Constants.Relations.Last, "streamStore:feed" });
32+
resource.Links.Keys.ShouldBe(new[]
33+
{
34+
Constants.Relations.Self,
35+
Constants.Relations.First,
36+
Constants.Relations.Next,
37+
Constants.Relations.Last,
38+
Constants.Relations.Feed,
39+
Constants.Relations.Message
40+
});
3341

3442
resource.ShouldLink(Constants.Relations.Self, "0");
3543
resource.ShouldLink(Constants.Relations.First, "0");
3644
resource.ShouldLink(Constants.Relations.Next, "1");
3745
resource.ShouldLink(Constants.Relations.Last, "-1");
3846
resource.ShouldLink(Constants.Relations.Feed, HeadOfStream);
47+
resource.ShouldLink(Constants.Relations.Message, "0");
3948
}
4049
}
4150

@@ -48,12 +57,20 @@ public async Task read_single_message_does_not_exist_stream()
4857

4958
var resource = await response.AsHal();
5059

51-
resource.Links.Keys.ShouldBe(new[] { Constants.Relations.Self, Constants.Relations.First, Constants.Relations.Last, Constants.Relations.Feed });
60+
resource.Links.Keys.ShouldBe(new[]
61+
{
62+
Constants.Relations.Self,
63+
Constants.Relations.First,
64+
Constants.Relations.Last,
65+
Constants.Relations.Feed,
66+
Constants.Relations.Message
67+
});
5268

5369
resource.ShouldLink(Constants.Relations.Self, "0");
5470
resource.ShouldLink(Constants.Relations.First, "0");
5571
resource.ShouldLink(Constants.Relations.Last, "-1");
5672
resource.ShouldLink(Constants.Relations.Feed, HeadOfStream);
73+
resource.ShouldLink(Constants.Relations.Message, "0");
5774
}
5875
}
5976

@@ -66,6 +83,7 @@ public async Task delete_single_message_by_version()
6683
{
6784
response.StatusCode.ShouldBe(HttpStatusCode.OK);
6885
}
86+
6987
using(var response = await _fixture.HttpClient.GetAsync("/streams/a-stream/0"))
7088
{
7189
response.StatusCode.ShouldBe(HttpStatusCode.NotFound);

src/SqlStreamStore.HAL.Tests/StreamNavigationTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public async Task read_first_link(string stream, string baseAddress)
105105

106106
resource.ShouldLink(Constants.Relations.First, $"{stream}?{FirstLinkQuery}");
107107

108-
resource.ShouldLink(Constants.Relations.Feed, $"{stream}?{LastLinkQuery}");
108+
resource.ShouldLink(Constants.Relations.Feed, $"{stream}?{FirstLinkQuery}");
109109

110110
if(!IsAllStream($"{baseAddress}{stream}"))
111111
{
@@ -133,7 +133,7 @@ public async Task read_first_link_when_multiple_pages(string stream, string base
133133

134134
resource.ShouldLink(Constants.Relations.First, $"{stream}?{FirstLinkQuery}");
135135

136-
resource.ShouldLink(Constants.Relations.Feed, $"{stream}?{LastLinkQuery}");
136+
resource.ShouldLink(Constants.Relations.Feed, $"{stream}?{FirstLinkQuery}");
137137

138138
if(!IsAllStream($"{baseAddress}{stream}"))
139139
{

src/SqlStreamStore.HAL/Resources/AllStreamMessageResource.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Threading;
66
using System.Threading.Tasks;
77
using Halcyon.HAL;
8+
using SqlStreamStore.Streams;
89

910
internal class AllStreamMessageResource : IResource
1011
{
@@ -23,7 +24,7 @@ public AllStreamMessageResource(IStreamStore streamStore)
2324
throw new ArgumentNullException(nameof(streamStore));
2425
_streamStore = streamStore;
2526
}
26-
27+
2728
public async Task<Response> GetMessage(
2829
ReadAllStreamMessageOperation operation,
2930
CancellationToken cancellationToken)
@@ -34,7 +35,7 @@ public async Task<Response> GetMessage(
3435
{
3536
return new Response(
3637
new HALResponse(new HALModelConfig())
37-
.AddLinks(Links.All.Feed(operation)),
38+
.AddLinks(Links.Feed()),
3839
404);
3940
}
4041

@@ -52,8 +53,30 @@ public async Task<Response> GetMessage(
5253
payload,
5354
metadata = message.JsonMetadata
5455
}).AddLinks(
55-
Links.All.SelfAll(message),
56-
Links.All.Feed(operation)));
56+
Links.Self(message),
57+
Links.Message(message),
58+
Links.Feed()));
59+
}
60+
61+
private static class Links
62+
{
63+
public static Link Last()
64+
=> new Link(
65+
Constants.Relations.Last,
66+
LinkFormatter.FormatBackwardLink(
67+
$"../{Constants.Streams.All}",
68+
Constants.MaxCount,
69+
Position.End,
70+
false));
71+
72+
public static Link Self(StreamMessage message)
73+
=> new Link(Constants.Relations.Self, $"{message.Position}");
74+
75+
public static Link Message(StreamMessage message)
76+
=> new Link(Constants.Relations.Message, $"{message.Position}");
77+
78+
public static Link Feed()
79+
=> new Link(Constants.Relations.Feed, Last().Href);
5780
}
5881
}
5982
}

src/SqlStreamStore.HAL/Resources/AllStreamResource.cs

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace SqlStreamStore.HAL.Resources
22
{
33
using System;
4+
using System.Collections.Generic;
45
using System.Linq;
56
using System.Net.Http;
67
using System.Threading;
@@ -47,9 +48,9 @@ public async Task<Response> GetPage(
4748
page.NextPosition,
4849
page.IsEnd
4950
})
50-
.AddLinks(Links.All.SelfFeed(operation))
51-
.AddLinks(Links.All.Navigation(page, operation))
52-
.AddLinks(Links.All.Feed(operation))
51+
.AddLinks(Links.Self(operation))
52+
.AddLinks(Links.Navigation(page, operation))
53+
.AddLinks(Links.Feed(operation))
5354
.AddEmbeddedCollection(
5455
Constants.Relations.Message,
5556
streamMessages.Zip(
@@ -65,7 +66,7 @@ public async Task<Response> GetPage(
6566
payload,
6667
metadata = message.JsonMetadata
6768
})
68-
.AddLinks(Links.All.Self(message)))));
69+
.AddLinks(Links.Message.Self(message)))));
6970

7071
if(operation.FromPositionInclusive == Position.End)
7172
{
@@ -78,5 +79,85 @@ public async Task<Response> GetPage(
7879

7980
return response;
8081
}
82+
83+
private static class Links
84+
{
85+
public static Link Self(ReadAllStreamOperation operation)
86+
=> new Link(Constants.Relations.Self, operation.Self);
87+
88+
public static Link First(ReadAllStreamOperation operation)
89+
=> new Link(
90+
Constants.Relations.First,
91+
LinkFormatter.FormatForwardLink(
92+
Constants.Streams.All,
93+
operation.MaxCount,
94+
Position.Start,
95+
operation.EmbedPayload));
96+
97+
public static Link Last(ReadAllStreamOperation operation)
98+
=> new Link(
99+
Constants.Relations.Last,
100+
LinkFormatter.FormatBackwardLink(
101+
Constants.Streams.All,
102+
operation.MaxCount,
103+
Position.End,
104+
operation.EmbedPayload));
105+
106+
public static Link Last()
107+
=> new Link(
108+
Constants.Relations.Last,
109+
LinkFormatter.FormatBackwardLink(
110+
Constants.Streams.All,
111+
Constants.MaxCount,
112+
Position.End,
113+
false));
114+
115+
public static Link Feed(ReadAllStreamOperation operation)
116+
=> new Link(Constants.Relations.Feed, operation.Self);
117+
118+
public static Link Feed()
119+
=> new Link(Constants.Relations.Feed, Last().Href);
120+
121+
public static Link Previous(ReadAllPage page, ReadAllStreamOperation operation)
122+
=> new Link(
123+
Constants.Relations.Previous,
124+
LinkFormatter.FormatBackwardLink(
125+
Constants.Streams.All,
126+
operation.MaxCount,
127+
page.Messages.Min(m => m.Position) - 1,
128+
operation.EmbedPayload));
129+
130+
public static Link Next(ReadAllPage page, ReadAllStreamOperation operation)
131+
=> new Link(
132+
Constants.Relations.Next,
133+
LinkFormatter.FormatForwardLink(
134+
Constants.Streams.All,
135+
operation.MaxCount,
136+
page.Messages.Max(m => m.Position) + 1,
137+
operation.EmbedPayload));
138+
139+
public static IEnumerable<Link> Navigation(ReadAllPage page, ReadAllStreamOperation operation)
140+
{
141+
var first = First(operation);
142+
var last = Last(operation);
143+
144+
yield return first;
145+
146+
if(operation.Self != first.Href && !page.IsEnd)
147+
yield return Previous(page, operation);
148+
149+
if(operation.Self != last.Href && !page.IsEnd)
150+
yield return Next(page, operation);
151+
152+
yield return last;
153+
}
154+
155+
public static class Message
156+
{
157+
public static Link Self(StreamMessage message) => new Link(
158+
Constants.Relations.Self,
159+
$"streams/{message.StreamId}/{message.StreamVersion}");
160+
}
161+
}
81162
}
82163
}

0 commit comments

Comments
 (0)