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

Read(Stream)HeadMessage #426

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/SqlStreamStore/IReadonlyStreamStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,19 @@ IAllStreamSubscription SubscribeToAll(
/// The cancellation instruction.
/// </param>
/// <returns>
/// The head position.
/// The head position, or <c>-1</c> if there is none.
/// </returns>
Task<long> ReadHeadPosition(CancellationToken cancellationToken = default);
/// <summary>
/// Reads the head message (the very latest message).
/// </summary>
/// <param name="cancellationToken">
/// The cancellation instruction.
/// </param>
/// <returns>
/// The head message, or <c>null</c> if there is no head message.
/// </returns>
Task<StreamMessage?> ReadHeadMessage(CancellationToken cancellationToken = default);

/// <summary>
/// Reads the head position (the position of the very latest message) of a particular stream.
Expand Down Expand Up @@ -242,6 +252,20 @@ IAllStreamSubscription SubscribeToAll(
/// </returns>
Task<int> ReadStreamHeadVersion(StreamId streamId, CancellationToken cancellationToken = default);

/// <summary>
/// Reads the head message (the very latest message) of a particular stream.
/// </summary>
/// <param name="streamId">
/// The stream to read the head message of.
/// </param>
/// <param name="cancellationToken">
/// The cancellation instruction.
/// </param>
/// <returns>
/// The head message of the stream, or <c>null</c> if no such stream or an empty stream.
/// </returns>
Task<StreamMessage?> ReadStreamHeadMessage(StreamId streamId, CancellationToken cancellationToken = default);

/// <summary>
/// Gets the stream metadata.
/// </summary>
Expand Down
29 changes: 29 additions & 0 deletions src/SqlStreamStore/InMemory/InMemoryStreamStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,35 @@ protected override Task<int> ReadStreamHeadVersionInternal(string streamId, Canc
}
}

protected override Task<StreamMessage?> ReadHeadMessageInternal(CancellationToken cancellationToken)
{
var message = _allStream.LastOrDefault();
return message == null
? Task.FromResult<StreamMessage?>(default)
: Task.FromResult<StreamMessage?>(
new StreamMessage(
message.StreamId, message.MessageId,
message.StreamVersion, message.Position,
message.Created, message.Type,
message.JsonMetadata, message.JsonData));
}

protected override Task<StreamMessage?> ReadStreamHeadMessageInternal(string streamId, CancellationToken cancellationToken)
{
using(_lock.UseReadLock())
{
if(!_streams.TryGetValue(streamId, out InMemoryStream stream))
return Task.FromResult<StreamMessage?>(default);
var message = stream.Messages[stream.Messages.Count - 1];
return Task.FromResult<StreamMessage?>(
new StreamMessage(
message.StreamId, message.MessageId,
message.StreamVersion, message.Position,
message.Created, message.Type,
message.JsonMetadata, message.JsonData));
}
}

protected override IAllStreamSubscription SubscribeToAllInternal(
long? fromPosition,
AllStreamMessageReceived streamMessageReceived,
Expand Down
18 changes: 18 additions & 0 deletions src/SqlStreamStore/Infrastructure/ReadonlyStreamStoreBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,22 @@ public Task<long> ReadStreamHeadPosition(StreamId streamId, CancellationToken ca
return ReadStreamHeadPositionInternal(streamId, cancellationToken);
}

public Task<StreamMessage?> ReadHeadMessage(CancellationToken cancellationToken = default)
{
GuardAgainstDisposed();

return ReadHeadMessageInternal(cancellationToken);
}

public Task<StreamMessage?> ReadStreamHeadMessage(StreamId streamId, CancellationToken cancellationToken = default)
{
if (streamId == null) throw new ArgumentNullException(nameof(streamId));

GuardAgainstDisposed();

return ReadStreamHeadMessageInternal(streamId, cancellationToken);
}

public Task<int> ReadStreamHeadVersion(StreamId streamId, CancellationToken cancellationToken = default)
{
if (streamId == null) throw new ArgumentNullException(nameof(streamId));
Expand Down Expand Up @@ -318,6 +334,8 @@ protected abstract Task<ReadStreamPage> ReadStreamBackwardsInternal(
protected abstract Task<long> ReadHeadPositionInternal(CancellationToken cancellationToken);
protected abstract Task<long> ReadStreamHeadPositionInternal(string streamId, CancellationToken cancellationToken);
protected abstract Task<int> ReadStreamHeadVersionInternal(string streamId, CancellationToken cancellationToken);
protected abstract Task<StreamMessage?> ReadHeadMessageInternal(CancellationToken cancellationToken);
protected abstract Task<StreamMessage?> ReadStreamHeadMessageInternal(string streamId, CancellationToken cancellationToken);

protected abstract IStreamSubscription SubscribeToStreamInternal(
string streamId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,65 @@ public async Task Given_filled_stream_when_get_head_version_then_returns_expecte

head.ShouldBe(2);
}

[Fact]
public async Task Given_empty_store_when_get_head_message_Then_should_be_minus_one()
{
var head = await Store.ReadHeadMessage();

head.ShouldBe(default);
}

[Fact]
public async Task Given_store_with_empty_stream_when_get_head_message_Then_should_be_minus_one()
{
await Store.AppendToStream("stream-1", ExpectedVersion.NoStream, CreateNewStreamMessages());

var head = await Store.ReadHeadMessage();

head.ShouldBe(default);
}

[Fact]
public async Task Given_store_with_messages_then_can_get_head_message()
{
var messages = CreateNewStreamMessages(1, 2, 3);
await Store.AppendToStream("stream-1", ExpectedVersion.NoStream, messages);

var head = await Store.ReadHeadMessage();

head.ShouldBe(messages[3]);
}

[Fact]
public async Task Given_no_stream_when_get_stream_head_message_then_returns_expected_result()
{
await Store.AppendToStream("other-stream", ExpectedVersion.NoStream, CreateNewStreamMessages(1));

var head = await Store.ReadStreamHeadMessage("this-stream");

head.ShouldBe(default);
}

[Fact]
public async Task Given_empty_stream_when_get_stream_head_message_then_returns_expected_result()
{
await Store.AppendToStream("this-stream", ExpectedVersion.NoStream, CreateNewStreamMessages());

var head = await Store.ReadStreamHeadMessage("this-stream");

head.ShouldBe(default);
}

[Fact]
public async Task Given_filled_stream_when_get_stream_head_message_then_returns_expected_result()
{
var messages = CreateNewStreamMessages(1, 2, 3);
await Store.AppendToStream("this-stream", ExpectedVersion.NoStream, messages);

var head = await Store.ReadStreamHeadMessage("this-stream");

head.ShouldBe(messages[3]);
}
}
}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy