-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
157 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,79 @@ | ||
using EvenireDB.Common; | ||
using EvenireDB.Extents; | ||
using EvenireDB.Server.DTO; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace EvenireDB.Server.Routes | ||
namespace EvenireDB.Server.Routes; | ||
public static class StreamsRoutes | ||
{ | ||
//TODO: add endpoint to get all streams | ||
public static class StreamsRoutes | ||
public static WebApplication MapEventsRoutes(this WebApplication app) | ||
{ | ||
public static WebApplication MapEventsRoutes(this WebApplication app) | ||
{ | ||
var api = app.NewVersionedApi(); | ||
var v1 = api.MapGroup("/api/v{version:apiVersion}/streams") | ||
.HasApiVersion(1.0); | ||
v1.MapGet("/{streamId:guid}/events", GetEvents).WithName(nameof(GetEvents)); | ||
v1.MapPost("/{streamId:guid}/events", SaveEvents).WithName(nameof(SaveEvents)); | ||
var api = app.NewVersionedApi(); | ||
var v1 = api.MapGroup("/api/v{version:apiVersion}/streams") | ||
.HasApiVersion(1.0); | ||
v1.MapGet("", GetStreams).WithName(nameof(GetStreams)); | ||
v1.MapGet("/{streamId:guid}", GetStreamInfo).WithName(nameof(GetStreamInfo)); | ||
v1.MapGet("/{streamId:guid}/events", GetEvents).WithName(nameof(GetEvents)); | ||
v1.MapPost("/{streamId:guid}/events", SaveEvents).WithName(nameof(SaveEvents)); | ||
|
||
return app; | ||
} | ||
return app; | ||
} | ||
|
||
private static async IAsyncEnumerable<EventDTO> GetEvents( | ||
[FromServices] IEventsReader reader, | ||
Guid streamId, | ||
[FromQuery(Name = "pos")] uint startPosition = 0, | ||
[FromQuery(Name = "dir")] Direction direction = Direction.Forward) | ||
{ | ||
await foreach (var @event in reader.ReadAsync(streamId, direction: direction, startPosition: startPosition).ConfigureAwait(false)) | ||
yield return EventDTO.FromModel(@event); | ||
} | ||
private static IResult GetStreams( | ||
[FromServices] IStreamInfoProvider provider) | ||
{ | ||
var streams = provider.GetStreamsInfo(); | ||
return Results.Ok(streams); | ||
} | ||
|
||
private static async ValueTask<IResult> SaveEvents( | ||
[FromServices] EventMapper mapper, | ||
[FromServices] IEventsWriter writer, | ||
Guid streamId, | ||
[FromQuery(Name = "version")] int? expectedVersion, | ||
[FromBody] EventDataDTO[]? dtos) | ||
{ | ||
if(dtos is null) | ||
return Results.BadRequest(new ApiError(ErrorCodes.BadRequest, "No events provided")); | ||
private static IResult GetStreamInfo( | ||
[FromServices] IStreamInfoProvider provider, | ||
Guid streamId) | ||
{ | ||
var result = provider.GetStreamInfo(streamId); | ||
return Results.Ok(result); | ||
} | ||
|
||
EventData[] events; | ||
private static async IAsyncEnumerable<EventDTO> GetEvents( | ||
[FromServices] IEventsReader reader, | ||
Guid streamId, | ||
[FromQuery(Name = "pos")] uint startPosition = 0, | ||
[FromQuery(Name = "dir")] Direction direction = Direction.Forward) | ||
{ | ||
await foreach (var @event in reader.ReadAsync(streamId, direction: direction, startPosition: startPosition).ConfigureAwait(false)) | ||
yield return EventDTO.FromModel(@event); | ||
} | ||
|
||
try | ||
{ | ||
events = mapper.ToModels(dtos); | ||
} | ||
catch(Exception ex) | ||
{ | ||
return Results.BadRequest(new ApiError(ErrorCodes.BadRequest, ex.Message)); | ||
} | ||
private static async ValueTask<IResult> SaveEvents( | ||
[FromServices] EventMapper mapper, | ||
[FromServices] IEventsWriter writer, | ||
Guid streamId, | ||
[FromQuery(Name = "version")] int? expectedVersion, | ||
[FromBody] EventDataDTO[]? dtos) | ||
{ | ||
if (dtos is null) | ||
return Results.BadRequest(new ApiError(ErrorCodes.BadRequest, "No events provided")); | ||
|
||
EventData[] events; | ||
|
||
var result = await writer.AppendAsync(streamId, events, expectedVersion) | ||
.ConfigureAwait(false); | ||
return result switch | ||
{ | ||
FailureResult { Code: ErrorCodes.DuplicateEvent } d => Results.Conflict(new ApiError(ErrorCodes.DuplicateEvent, d.Message)), | ||
FailureResult { Code: ErrorCodes.VersionMismatch } d => Results.BadRequest(new ApiError(ErrorCodes.VersionMismatch, d.Message)), | ||
FailureResult { Code: ErrorCodes.BadRequest } d => Results.BadRequest(new ApiError(ErrorCodes.BadRequest, d.Message)), | ||
FailureResult => Results.StatusCode(500), | ||
_ => Results.AcceptedAtRoute(nameof(GetEvents), new { streamId }) | ||
}; | ||
try | ||
{ | ||
events = mapper.ToModels(dtos); | ||
} | ||
} | ||
catch (Exception ex) | ||
{ | ||
return Results.BadRequest(new ApiError(ErrorCodes.BadRequest, ex.Message)); | ||
} | ||
|
||
var result = await writer.AppendAsync(streamId, events, expectedVersion) | ||
.ConfigureAwait(false); | ||
return result switch | ||
{ | ||
FailureResult { Code: ErrorCodes.DuplicateEvent } d => Results.Conflict(new ApiError(ErrorCodes.DuplicateEvent, d.Message)), | ||
FailureResult { Code: ErrorCodes.VersionMismatch } d => Results.BadRequest(new ApiError(ErrorCodes.VersionMismatch, d.Message)), | ||
FailureResult { Code: ErrorCodes.BadRequest } d => Results.BadRequest(new ApiError(ErrorCodes.BadRequest, d.Message)), | ||
FailureResult => Results.StatusCode(500), | ||
_ => Results.AcceptedAtRoute(nameof(GetEvents), new { streamId }) | ||
}; | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,7 @@ | ||
namespace EvenireDB.Extents | ||
namespace EvenireDB.Extents; | ||
|
||
public readonly struct ExtentInfo | ||
{ | ||
public readonly struct ExtentInfo | ||
{ | ||
public readonly required string DataPath { get; init; } | ||
public readonly required string HeadersPath { get; init; } | ||
} | ||
} | ||
public readonly required string DataPath { get; init; } | ||
public readonly required string HeadersPath { get; init; } | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace EvenireDB.Extents; | ||
|
||
public interface IStreamInfoProvider | ||
{ | ||
ExtentInfo GetExtentInfo(Guid streamId); | ||
IEnumerable<StreamInfo> GetStreamsInfo(); | ||
StreamInfo GetStreamInfo(Guid streamId); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace EvenireDB.Extents; | ||
|
||
public readonly record struct StreamInfo( | ||
Guid StreamId, | ||
long EventsCount, | ||
DateTimeOffset CreatedAt, | ||
DateTimeOffset LastAccessedAt); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
using System.Runtime.InteropServices; | ||
|
||
namespace EvenireDB.Extents; | ||
|
||
internal record StreamInfoProviderConfig(string BasePath); | ||
|
||
internal class StreamInfoProvider : IStreamInfoProvider | ||
{ | ||
private readonly StreamInfoProviderConfig _config; | ||
private readonly int _headerSize = Marshal.SizeOf<RawHeader>(); | ||
|
||
public StreamInfoProvider(StreamInfoProviderConfig config) | ||
{ | ||
_config = config ?? throw new ArgumentNullException(nameof(config)); | ||
if (!Directory.Exists(_config.BasePath)) | ||
Directory.CreateDirectory(config.BasePath); | ||
} | ||
|
||
public ExtentInfo GetExtentInfo(Guid streamId) | ||
{ | ||
// TODO: tests | ||
var key = streamId.ToString("N"); | ||
int extentNumber = 0; // TODO: calculate | ||
return new ExtentInfo | ||
{ | ||
DataPath = Path.Combine(_config.BasePath, $"{key}_{extentNumber}_data.dat"), | ||
HeadersPath = Path.Combine(_config.BasePath, $"{key}_{extentNumber}_headers.dat"), | ||
}; | ||
} | ||
|
||
public StreamInfo GetStreamInfo(Guid streamId) | ||
{ | ||
var extent = this.GetExtentInfo(streamId); | ||
|
||
var fileInfo = new FileInfo(extent.HeadersPath); | ||
var headersCount = fileInfo.Length / _headerSize; | ||
return new StreamInfo( | ||
streamId, | ||
headersCount, | ||
fileInfo.CreationTimeUtc, | ||
fileInfo.LastWriteTimeUtc); | ||
} | ||
|
||
public IEnumerable<StreamInfo> GetStreamsInfo() | ||
{ | ||
var headersFiles = Directory.GetFiles(_config.BasePath, "*_headers.dat"); | ||
foreach(var headerFile in headersFiles) | ||
{ | ||
var key = Path.GetFileNameWithoutExtension(headerFile).Split('_')[0]; | ||
yield return GetStreamInfo(Guid.Parse(key)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters