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

Commit 75c0e71

Browse files
Merge pull request #30 from michael-freidgeim-webjet/LittleHalHost
Include src/LittleHalHost.Example/Example.csproj from yreynhout's gist
2 parents 944b42d + 5478795 commit 75c0e71

File tree

6 files changed

+259
-5
lines changed

6 files changed

+259
-5
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Fully RESTful HTTP Server for [SQL Stream Store](https://github.com/SQLStreamStore/SQLStreamStore). Uses [application/hal+json](https://tools.ietf.org/html/draft-kelly-json-hal-08) for resource navigation, with [JSON Hyper-Schema](https://json-schema.org/latest/json-schema-hypermedia.html) as [embedded resources](https://tools.ietf.org/html/draft-kelly-json-hal-08#section-4.1.2).
44

5+
The solution includes example of use LittleHalHost.Example project. The url structure and parameters are described in LittleHalHost.Example/readme.md
6+
57
# Clients
68

79
- [Official dotnet client](https://github.com/SQLStreamStore/SQLStreamStore)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp2.1</TargetFramework>
6+
<LangVersion>latest</LangVersion>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.1.1" />
11+
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.1.3" />
12+
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.1.1" />
13+
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
14+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.1.1" />
15+
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.1.1" />
16+
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
17+
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
18+
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
19+
<!--<PackageReference Include="SqlStreamStore" Version="1.2.0-*" />-->
20+
<!--<PackageReference Include="SqlstreamStore" Version="1.1.2" />-->
21+
<PackageReference Include="System.IO.Pipelines" Version="4.5.1" />
22+
<!-- this one is on the CI feed -->
23+
</ItemGroup>
24+
25+
<ItemGroup>
26+
<ProjectReference Include="..\SqlStreamStore.HAL\SqlStreamStore.HAL.csproj" />
27+
</ItemGroup>
28+
29+
<!--<ItemGroup>
30+
<Reference Include="SqlStreamStore.HAL">
31+
<HintPath>..\SQLStreamStore.HAL\src\SqlStreamStore.HAL\bin\Debug\netstandard2.0\SqlStreamStore.HAL.dll</HintPath>
32+
</Reference>
33+
</ItemGroup>-->
34+
35+
</Project>

src/LittleHalHost.Example/Program.cs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Builder;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Newtonsoft.Json;
8+
using Serilog;
9+
using SqlStreamStore;
10+
using SqlStreamStore.HAL;
11+
using SqlStreamStore.Streams;
12+
using MidFunc = System.Func<
13+
Microsoft.AspNetCore.Http.HttpContext,
14+
System.Func<System.Threading.Tasks.Task>,
15+
System.Threading.Tasks.Task
16+
>;
17+
18+
namespace Example
19+
{
20+
class Program
21+
{
22+
public static async Task Main(string[] args)
23+
{
24+
Log.Logger = new LoggerConfiguration()
25+
.MinimumLevel.Debug()
26+
.Enrich.FromLogContext()
27+
.WriteTo.Console()
28+
.CreateLogger();
29+
30+
using(var streamStore = new InMemoryStreamStore())
31+
using(var host = new WebHostBuilder()
32+
.UseKestrel()
33+
.UseStartup(new Startup(streamStore, new SqlStreamStoreMiddlewareOptions
34+
{
35+
UseCanonicalUrls = false
36+
}))
37+
.UseSerilog()
38+
.UseUrls("http://localhost:5050")
39+
.Build())
40+
{
41+
// Simulating some messages in the store here (only one stream)
42+
var random = new Random();
43+
var id = new Guid("cbf68be34d9547eb9b4a390fd2aa417b");
44+
var messages = new NewStreamMessage[random.Next(1000, 2000)];
45+
for(var index = 0; index < messages.Length; index++)
46+
{
47+
messages[index] = new NewStreamMessage(
48+
Guid.NewGuid(),
49+
"ItineraryPublished",
50+
JsonConvert.SerializeObject(new {
51+
ItineraryId = id,
52+
Data = index
53+
})
54+
);
55+
}
56+
await streamStore.AppendToStream(id.ToString("N"), ExpectedVersion.NoStream, messages);
57+
58+
// bootstrapping the server
59+
var source = new CancellationTokenSource();
60+
var serverTask = host.RunAsync(source.Token);
61+
// press enter to exit
62+
Console.WriteLine("Running ...");
63+
Console.ReadLine();
64+
source.Cancel();
65+
66+
await serverTask;
67+
}
68+
}
69+
}
70+
71+
internal static class WebHostBuilderExtensions
72+
{
73+
public static IWebHostBuilder UseStartup(this IWebHostBuilder builder, IStartup startup)
74+
=> builder
75+
.ConfigureServices(services => services.AddSingleton(startup))
76+
.UseSetting(WebHostDefaults.ApplicationKey, startup.GetType().AssemblyQualifiedName);
77+
}
78+
79+
internal class Startup : IStartup
80+
{
81+
private readonly IStreamStore _streamStore;
82+
private readonly SqlStreamStoreMiddlewareOptions _options;
83+
84+
public Startup(
85+
IStreamStore streamStore,
86+
SqlStreamStoreMiddlewareOptions options)
87+
{
88+
_streamStore = streamStore;
89+
_options = options;
90+
}
91+
92+
public IServiceProvider ConfigureServices(IServiceCollection services) => services
93+
.AddResponseCompression(options => options.MimeTypes = new[] { "application/hal+json" })
94+
.BuildServiceProvider();
95+
96+
public void Configure(IApplicationBuilder app) => app
97+
.UseResponseCompression()
98+
.Use(CatchAndDisplayErrors)
99+
.UseSqlStreamStoreHal(_streamStore, _options);
100+
101+
private static MidFunc CatchAndDisplayErrors => async (context, next) =>
102+
{
103+
try
104+
{
105+
await next();
106+
}
107+
catch(Exception ex)
108+
{
109+
Log.Warning(ex, "Error during request.");
110+
}
111+
};
112+
}
113+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"fromPosition": 0,
3+
"nextPosition": 3,
4+
"isEnd": false,
5+
"_links": {
6+
"self": {
7+
"href": "stream?d=f&m=3&p=0&e=1"
8+
},
9+
"first": {
10+
"href": "stream?d=f&m=3&p=0&e=1"
11+
},
12+
"next": {
13+
"href": "stream?d=f&m=3&p=3&e=1"
14+
},
15+
"last": {
16+
"href": "stream?d=b&m=3&p=-1&e=1"
17+
},
18+
"streamStore:feed": {
19+
"href": "stream?d=f&m=3&p=0&e=1"
20+
},
21+
"streamStore:find": {
22+
"href": "../streams/{streamId}",
23+
"templated": true,
24+
"title": "Find a Stream"
25+
}
26+
},
27+
"_embedded": {
28+
"streamStore:message": [{
29+
"messageId": "91e9d8b3-7b96-4cfa-84cc-0c51739db462",
30+
"createdUtc": "2018-10-06T11:54:48.1042417Z",
31+
"position": 2,
32+
"streamId": "cbf68be34d9547eb9b4a390fd2aa417b",
33+
"streamVersion": 2,
34+
"type": "ItineraryPublished",
35+
"payload": "{\"ItineraryId\":\"cbf68be3-4d95-47eb-9b4a-390fd2aa417b\",\"Data\":2}",
36+
"metadata": "",
37+
"_links": {
38+
"self": {
39+
"href": "streams/cbf68be34d9547eb9b4a390fd2aa417b/2"
40+
},
41+
"streamStore:feed": {
42+
"href": "streams/cbf68be34d9547eb9b4a390fd2aa417b"
43+
}
44+
}
45+
}, {
46+
"messageId": "f240201c-d844-449d-8f31-6e1980f1e3d5",
47+
"createdUtc": "2018-10-06T11:54:48.1042383Z",
48+
"position": 1,
49+
"streamId": "cbf68be34d9547eb9b4a390fd2aa417b",
50+
"streamVersion": 1,
51+
"type": "ItineraryPublished",
52+
"payload": "{\"ItineraryId\":\"cbf68be3-4d95-47eb-9b4a-390fd2aa417b\",\"Data\":1}",
53+
"metadata": "",
54+
"_links": {
55+
"self": {
56+
"href": "streams/cbf68be34d9547eb9b4a390fd2aa417b/1"
57+
},
58+
"streamStore:feed": {
59+
"href": "streams/cbf68be34d9547eb9b4a390fd2aa417b"
60+
}
61+
}
62+
}, {
63+
"messageId": "7a405cc3-0d45-4a21-9182-929d5782d244",
64+
"createdUtc": "2018-10-06T11:54:48.104178Z",
65+
"position": 0,
66+
"streamId": "cbf68be34d9547eb9b4a390fd2aa417b",
67+
"streamVersion": 0,
68+
"type": "ItineraryPublished",
69+
"payload": "{\"ItineraryId\":\"cbf68be3-4d95-47eb-9b4a-390fd2aa417b\",\"Data\":0}",
70+
"metadata": "",
71+
"_links": {
72+
"self": {
73+
"href": "streams/cbf68be34d9547eb9b4a390fd2aa417b/0"
74+
},
75+
"streamStore:feed": {
76+
"href": "streams/cbf68be34d9547eb9b4a390fd2aa417b"
77+
}
78+
}
79+
}
80+
]
81+
}
82+
}

src/LittleHalHost.Example/readme.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
A minimal example here simply appends a few messages to an InMemoryStreamStore and exposes that over HTTP.
2+
You can get all messages http://localhost:5050/stream?d=f&m=20&p=0&e=1
3+
or get messages from known stream http://localhost:5050/streams/cbf68be34d9547eb9b4a390fd2aa417b?d=f&m=20&p=0&e=1
4+
This will fetch the first page worth of messages (_embedded) and have a links collection with a next link you can follow to fetch the next page.
5+
Supported parameters:
6+
'd', readDirection, possible values F/B (Forward/Backward)
7+
'p', position Inclusive
8+
'm' maxCount
9+
'e', embedPayload -possible values 0/1
10+
11+
See example of response in ResponseExample.json
12+
13+

src/SqlStreamStore.HAL.sln

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 14
4-
VisualStudioVersion = 14.0.25123.0
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.27130.2027
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlStreamStore.HAL", "SqlStreamStore.HAL\SqlStreamStore.HAL.csproj", "{022329C2-F59B-4350-8242-003CE480CD84}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlStreamStore.HAL", "SqlStreamStore.HAL\SqlStreamStore.HAL.csproj", "{022329C2-F59B-4350-8242-003CE480CD84}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlStreamStore.HAL.Tests", "SqlStreamStore.HAL.Tests\SqlStreamStore.HAL.Tests.csproj", "{A2F4657C-2C11-44E8-8E19-A44AF9AE543D}"
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlStreamStore.HAL.Tests", "SqlStreamStore.HAL.Tests\SqlStreamStore.HAL.Tests.csproj", "{A2F4657C-2C11-44E8-8E19-A44AF9AE543D}"
99
EndProject
10-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlStreamStore.HAL.DevServer", "SqlStreamStore.HAL.DevServer\SqlStreamStore.HAL.DevServer.csproj", "{2A6776A3-1495-4106-8C6B-9AE1A1E63976}"
10+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlStreamStore.HAL.DevServer", "SqlStreamStore.HAL.DevServer\SqlStreamStore.HAL.DevServer.csproj", "{2A6776A3-1495-4106-8C6B-9AE1A1E63976}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LittleHalHost.Example", "LittleHalHost.Example\LittleHalHost.Example.csproj", "{41C59355-DADF-4D62-9552-F298967D27A9}"
1113
EndProject
1214
Global
1315
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -27,8 +29,15 @@ Global
2729
{2A6776A3-1495-4106-8C6B-9AE1A1E63976}.Debug|Any CPU.Build.0 = Debug|Any CPU
2830
{2A6776A3-1495-4106-8C6B-9AE1A1E63976}.Release|Any CPU.ActiveCfg = Release|Any CPU
2931
{2A6776A3-1495-4106-8C6B-9AE1A1E63976}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{41C59355-DADF-4D62-9552-F298967D27A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{41C59355-DADF-4D62-9552-F298967D27A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{41C59355-DADF-4D62-9552-F298967D27A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{41C59355-DADF-4D62-9552-F298967D27A9}.Release|Any CPU.Build.0 = Release|Any CPU
3036
EndGlobalSection
3137
GlobalSection(SolutionProperties) = preSolution
3238
HideSolutionNode = FALSE
3339
EndGlobalSection
40+
GlobalSection(ExtensibilityGlobals) = postSolution
41+
SolutionGuid = {B73A9198-258A-4A4B-BDCA-F10648311761}
42+
EndGlobalSection
3443
EndGlobal

0 commit comments

Comments
 (0)