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

Commit a39c866

Browse files
supporting delete stream operation
didn't want multiple ways of setting expected version, so now it is an http header supporting one message or an array of messages in body on append
1 parent 44c99c5 commit a39c866

11 files changed

Lines changed: 341 additions & 123 deletions

src/SqlStreamStore.HAL.Tests/StreamAppendTests.cs

Lines changed: 98 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace SqlStreamStore.HAL.Tests
22
{
33
using System;
4+
using System.Collections.Generic;
45
using System.Net;
56
using System.Net.Http;
67
using System.Net.Http.Headers;
@@ -12,15 +13,15 @@
1213

1314
public class StreamAppendTests : IDisposable
1415
{
16+
private const string StreamId = "a-stream";
1517
private readonly SqlStreamStoreHalMiddlewareFixture _fixture;
1618

1719
public StreamAppendTests()
1820
{
1921
_fixture = new SqlStreamStoreHalMiddlewareFixture();
2022
}
2123

22-
[Fact]
23-
public async Task append_expected_version_any()
24+
public static IEnumerable<object[]> AppendCases()
2425
{
2526
var messageId = Guid.NewGuid();
2627

@@ -33,86 +34,85 @@ public async Task append_expected_version_any()
3334
{
3435
property = "metaValue"
3536
});
36-
37-
using(var response = await _fixture.HttpClient.PostAsync(
38-
"/streams/a-stream",
39-
new StringContent(JObject.FromObject(new
37+
38+
var bodies = new JToken[]
39+
{
40+
JObject.FromObject(new
4041
{
41-
expectedVersion = ExpectedVersion.Any,
42-
messages = new[]
43-
{
44-
new
45-
{
46-
messageId,
47-
type = "type",
48-
jsonData,
49-
jsonMetadata
50-
}
51-
}
52-
}).ToString())
42+
messageId,
43+
type = "type",
44+
jsonData,
45+
jsonMetadata
46+
}),
47+
JArray.FromObject(new[]
5348
{
54-
Headers =
49+
new
5550
{
56-
ContentType = new MediaTypeHeaderValue("application/json")
51+
messageId,
52+
type = "type",
53+
jsonData,
54+
jsonMetadata
5755
}
58-
}))
56+
})
57+
};
58+
59+
foreach(var body in bodies)
5960
{
60-
response.StatusCode.ShouldBe(HttpStatusCode.OK);
61+
yield return new object[]
62+
{
63+
body,
64+
ExpectedVersion.Any,
65+
HttpStatusCode.OK,
66+
messageId,
67+
jsonData,
68+
jsonMetadata
69+
};
70+
yield return new object[]
71+
{
72+
body,
73+
default(int?),
74+
HttpStatusCode.OK,
75+
messageId,
76+
jsonData,
77+
jsonMetadata
78+
};
79+
yield return new object[]
80+
{
81+
body,
82+
ExpectedVersion.NoStream,
83+
HttpStatusCode.Created,
84+
messageId,
85+
jsonData,
86+
jsonMetadata
87+
};
6188
}
62-
63-
var page = await _fixture.StreamStore.ReadStreamForwards("a-stream", 0, 1);
64-
65-
page.Status.ShouldBe(PageReadStatus.Success);
66-
page.Messages.Length.ShouldBe(1);
67-
page.Messages[0].MessageId.ShouldBe(messageId);
68-
page.Messages[0].Type.ShouldBe("type");
69-
JToken.DeepEquals(JObject.Parse(await page.Messages[0].GetJsonData()), jsonData).ShouldBeTrue();
70-
JToken.DeepEquals(JObject.Parse(page.Messages[0].JsonMetadata), jsonMetadata).ShouldBeTrue();
7189
}
7290

73-
[Fact]
74-
public async Task append_expected_version_no_stream()
91+
[Theory, MemberData(nameof(AppendCases))]
92+
public async Task expected_version(
93+
JToken body,
94+
int? expectedVersion,
95+
HttpStatusCode statusCode,
96+
Guid messageId,
97+
JObject jsonData,
98+
JObject jsonMetadata)
7599
{
76-
var messageId = Guid.NewGuid();
77-
78-
var jsonData = JObject.FromObject(new
100+
var request = new HttpRequestMessage(HttpMethod.Post, $"/streams/{StreamId}")
79101
{
80-
property = "value"
81-
});
102+
Content = new StringContent(body.ToString())
103+
};
82104

83-
var jsonMetadata = JObject.FromObject(new
105+
if(expectedVersion.HasValue)
84106
{
85-
property = "metaValue"
86-
});
87-
88-
using(var response = await _fixture.HttpClient.PostAsync(
89-
"/streams/a-stream",
90-
new StringContent(JObject.FromObject(new
91-
{
92-
expectedVersion = ExpectedVersion.NoStream,
93-
messages = new[]
94-
{
95-
new
96-
{
97-
messageId,
98-
type = "type",
99-
jsonData,
100-
jsonMetadata
101-
}
102-
}
103-
}).ToString())
104-
{
105-
Headers =
106-
{
107-
ContentType = new MediaTypeHeaderValue("application/json")
108-
}
109-
}))
107+
request.Headers.Add(Constants.Headers.ExpectedVersion, $"{expectedVersion}");
108+
}
109+
110+
using(var response = await _fixture.HttpClient.SendAsync(request))
110111
{
111-
response.StatusCode.ShouldBe(HttpStatusCode.Created);
112-
response.Headers.Location.ToString().ShouldBe("streams/a-stream");
112+
response.StatusCode.ShouldBe(statusCode);
113113
}
114114

115-
var page = await _fixture.StreamStore.ReadStreamForwards("a-stream", 0, 1);
115+
var page = await _fixture.StreamStore.ReadStreamForwards(StreamId, 0, int.MaxValue);
116116

117117
page.Status.ShouldBe(PageReadStatus.Success);
118118
page.Messages.Length.ShouldBe(1);
@@ -139,58 +139,58 @@ public async Task wrong_expected_version(int[] expectedVersions)
139139

140140
for(var i = 0; i < expectedVersions.Length - 1; i++)
141141
{
142-
using(await _fixture.HttpClient.PostAsync(
143-
"/streams/a-stream",
144-
new StringContent(JObject.FromObject(new
142+
using(await _fixture.HttpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, $"/streams/{StreamId}")
143+
{
144+
Headers =
145145
{
146-
expectedVersion = expectedVersions[i],
147-
messages = new[]
148-
{
149-
new
150-
{
151-
messageId = Guid.NewGuid(),
152-
type = "type",
153-
jsonData,
154-
jsonMetadata
155-
}
156-
}
146+
{Constants.Headers.ExpectedVersion, $"{expectedVersions[i]}"}
147+
},
148+
Content = new StringContent(JObject.FromObject(new
149+
{
150+
messageId = Guid.NewGuid(),
151+
type = "type",
152+
jsonData,
153+
jsonMetadata
157154
}).ToString())
158155
{
159156
Headers =
160157
{
161158
ContentType = new MediaTypeHeaderValue("application/json")
162159
}
163-
}))
160+
}
161+
}))
164162
{ }
165163
}
166164

167-
using(var response = await _fixture.HttpClient.PostAsync(
168-
"/streams/a-stream",
169-
new StringContent(JObject.FromObject(new
165+
using(var response = await _fixture.HttpClient.SendAsync(new HttpRequestMessage(HttpMethod.Post, $"/streams/{StreamId}")
166+
{
167+
Headers =
170168
{
171-
expectedVersion = expectedVersions[expectedVersions.Length-1],
172-
messages = new[]
173-
{
174-
new
175-
{
176-
messageId = Guid.NewGuid(),
177-
type = "type",
178-
jsonData,
179-
jsonMetadata
180-
}
181-
}
169+
{Constants.Headers.ExpectedVersion, $"{expectedVersions[expectedVersions.Length - 1]}"}
170+
},
171+
Content = new StringContent(JObject.FromObject(new
172+
{
173+
messageId = Guid.NewGuid(),
174+
type = "type",
175+
jsonData,
176+
jsonMetadata
182177
}).ToString())
183178
{
184179
Headers =
185180
{
186181
ContentType = new MediaTypeHeaderValue("application/json")
187182
}
188-
}))
183+
}
184+
}))
189185
{
190186
response.StatusCode.ShouldBe(HttpStatusCode.Conflict);
191-
response.Content.Headers.ContentType.ShouldBe(new MediaTypeHeaderValue("application/problem+json"));
187+
response.Content.Headers.ContentType.ShouldBe(new MediaTypeHeaderValue(
188+
Constants.Headers.ContentTypes.ProblemDetails));
192189
}
190+
var page = await _fixture.StreamStore.ReadStreamForwards(StreamId, 0, int.MaxValue);
193191

192+
page.Status.ShouldBe(PageReadStatus.Success);
193+
page.Messages.Length.ShouldBe(expectedVersions.Length - 1);
194194
}
195195

196196
public void Dispose() => _fixture.Dispose();
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
namespace SqlStreamStore.HAL.Tests
2+
{
3+
using System;
4+
using System.Net;
5+
using System.Net.Http;
6+
using System.Net.Http.Headers;
7+
using System.Threading.Tasks;
8+
using Shouldly;
9+
using SqlStreamStore.Streams;
10+
using Xunit;
11+
12+
public class StreamDeleteTests : IDisposable
13+
{
14+
private const string StreamId = "a-stream";
15+
private readonly SqlStreamStoreHalMiddlewareFixture _fixture;
16+
17+
public StreamDeleteTests()
18+
{
19+
_fixture = new SqlStreamStoreHalMiddlewareFixture();
20+
}
21+
22+
[Theory, InlineData(ExpectedVersion.Any), InlineData(0), InlineData(null)]
23+
public async Task expected_version(int? expectedVersion)
24+
{
25+
await _fixture.WriteNMessages(StreamId, 1);
26+
27+
var request = new HttpRequestMessage(HttpMethod.Delete, $"/streams/{StreamId}");
28+
29+
if(expectedVersion.HasValue)
30+
{
31+
request.Headers.Add(Constants.Headers.ExpectedVersion, $"{expectedVersion}");
32+
}
33+
34+
using(var response = await _fixture.HttpClient.SendAsync(request))
35+
{
36+
response.StatusCode.ShouldBe(HttpStatusCode.OK);
37+
}
38+
39+
var page = await _fixture.StreamStore.ReadStreamForwards(StreamId, 0, 1);
40+
41+
page.Status.ShouldBe(PageReadStatus.StreamNotFound);
42+
}
43+
44+
[Theory, InlineData(ExpectedVersion.NoStream), InlineData(2)]
45+
public async Task wrong_expected_version(int expectedVersion)
46+
{
47+
await _fixture.WriteNMessages(StreamId, 1);
48+
var request = new HttpRequestMessage(HttpMethod.Delete, $"/streams/{StreamId}")
49+
{
50+
Headers =
51+
{
52+
{ Constants.Headers.ExpectedVersion, $"{expectedVersion}" }
53+
}
54+
};
55+
56+
using(var response = await _fixture.HttpClient.SendAsync(request))
57+
{
58+
response.StatusCode.ShouldBe(HttpStatusCode.Conflict);
59+
response.Content.Headers.ContentType.ShouldBe(new MediaTypeHeaderValue(
60+
Constants.Headers.ContentTypes.ProblemDetails));
61+
}
62+
63+
var page = await _fixture.StreamStore.ReadStreamForwards(StreamId, 0, 1);
64+
65+
page.Status.ShouldBe(PageReadStatus.Success);
66+
67+
}
68+
69+
public void Dispose() => _fixture.Dispose();
70+
}
71+
}

src/SqlStreamStore.HAL/AppendStreamMiddleware.cs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
namespace SqlStreamStore.HAL
22
{
3-
using System;
43
using Microsoft.Owin;
54
using Microsoft.Owin.Builder;
6-
using Newtonsoft.Json.Linq;
75
using Owin;
86
using SqlStreamStore.Streams;
97
using MidFunc = System.Func<System.Func<System.Collections.Generic.IDictionary<string, object>,
@@ -49,25 +47,11 @@ private static MidFunc AppendStream(StreamResource stream) => next => async env
4947
}
5048

5149
await context.WriteHalResponse(response);
52-
5350
}
5451
catch(WrongExpectedVersionException ex)
5552
{
56-
context.Response.StatusCode = 409;
57-
context.Response.ReasonPhrase = "Conflict";
58-
context.Response.ContentType = "application/problem+json";
59-
60-
await context.Response.WriteAsync(ConvertToProblemDetails(ex));
53+
await context.WriteProblemDetailsResponse(ex);
6154
}
6255
};
63-
64-
private static string ConvertToProblemDetails(Exception ex)
65-
=> JObject.FromObject(new
66-
{
67-
type = "WrongExpectedVersion",
68-
title = "Wrong expected version.",
69-
detail = ex.Message
70-
}).ToString();
71-
7256
}
7357
}

0 commit comments

Comments
 (0)