diff --git a/Pushy.sln.DotSettings.user b/Pushy.sln.DotSettings.user index 1280799..79bc171 100644 --- a/Pushy.sln.DotSettings.user +++ b/Pushy.sln.DotSettings.user @@ -1,2 +1,3 @@  + ForceIncluded ForceIncluded \ No newline at end of file diff --git a/Pushy/Pushy.Client/Pushy.Client.csproj b/Pushy/Pushy.Client/Pushy.Client.csproj index b9d7c6f..606449b 100644 --- a/Pushy/Pushy.Client/Pushy.Client.csproj +++ b/Pushy/Pushy.Client/Pushy.Client.csproj @@ -12,6 +12,7 @@ + diff --git a/Pushy/Pushy.Client/TestClientForm.razor b/Pushy/Pushy.Client/TestClientForm.razor deleted file mode 100644 index 25ef659..0000000 --- a/Pushy/Pushy.Client/TestClientForm.razor +++ /dev/null @@ -1,77 +0,0 @@ -@page "/test" -@rendermode InteractiveWebAssembly -@inject PersistentComponentState ApplicationState -@inject ILogger Logger - -

@Data - @RendererInfo.Name

- - - - - - -@code { - - private bool doingStuff = false; - - private PersistingComponentStateSubscription persistingSubscription; - - public sealed class FormInput - { - public required string Text { get; set; } - public required string[] Existing { get; set; } - } - - [SupplyParameterFromForm] public FormInput Item { get; set; } = new() - { - Text = string.Empty, - Existing = [] - }; - - public Guid Data = Guid.NewGuid(); - - protected override Task OnInitializedAsync() - { - persistingSubscription = - ApplicationState.RegisterOnPersisting(PersistData); - - if (!ApplicationState.TryTakeFromJson("TOKEN", out Data)) - { - Data = Guid.NewGuid(); - Logger.LogInformation("Data needs to be persisted! {Data}", Data); - } - else - { - Logger.LogInformation("Using persisted DATA"); - } - - return Task.CompletedTask; - } - - private Task PersistData() - { - Logger.LogInformation("Persisting! {Data}", Data); - ApplicationState.PersistAsJson("TOKEN", Data); - - return Task.CompletedTask; - } - - private async Task Callback() - { - Item = new FormInput - { - Text = string.Empty, - Existing = [..Item.Existing, Item.Text, RendererInfo.Name] - }; - } -} \ No newline at end of file diff --git a/Pushy/Pushy/Components/Pages/Home.razor b/Pushy/Pushy/Components/Pages/Home.razor index d382f59..9a41617 100644 --- a/Pushy/Pushy/Components/Pages/Home.razor +++ b/Pushy/Pushy/Components/Pages/Home.razor @@ -27,8 +27,8 @@ @{ string key = CreatedItems.GetPrimaryKeyString(); } - @Nav.ToAbsoluteUri($"/t/{key}") - + @Nav.ToAbsoluteUri($"/t/{key}") + } @@ -38,19 +38,21 @@ [Inject] public ILogger Logger { get; set; } = null!; - [Inject] public LinkGenerator ItemGenerator { get; set; } = null!; + [Inject] public ILinkGenerator ItemGenerator { get; set; } = null!; - [Inject] public IClusterClient ClusterClient { get; set; } = null!; + [Inject] public IGrainFactory ClusterClient { get; set; } = null!; public ITextItem? CreatedItems; private async Task SubmitInput() { - ITextItem item = await ItemGenerator.GenerateTextShare(Form.InputText); - - CreatedItems = item; - - Form = new UploadForm(); + LinkResult result = await ItemGenerator.GenerateTextShare(Form.InputText); + + if (result.Item is not null) + { + CreatedItems = result.Item; + Form = new UploadForm(); + } Logger.LogInformation("A new item was created!"); } diff --git a/Pushy/Pushy/Grains/LinkGenerator.cs b/Pushy/Pushy/Grains/LinkGenerator.cs new file mode 100644 index 0000000..0b975aa --- /dev/null +++ b/Pushy/Pushy/Grains/LinkGenerator.cs @@ -0,0 +1,49 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Pushy.Grains; + +[Immutable] +[GenerateSerializer] +[Alias("Pushy.Grains.LinkResult")] +public record LinkResult(ITextItem? Item); + +public interface ILinkGenerator : IGrainWithGuidKey +{ + [Alias("GenerateLink")] + public ValueTask GenerateTextShare([Immutable] string text); +} + +public sealed class LinkGenerator : IGrainBase, ILinkGenerator +{ + private readonly ILogger _logger; + private readonly IClusterClient _clusterClient; + + public IGrainContext GrainContext { get; } + + public LinkGenerator( + IGrainContext grainContext, + IClusterClient clusterClient, + ILogger logger) + { + _clusterClient = clusterClient; + _logger = logger; + GrainContext = grainContext; + } + + public async ValueTask GenerateTextShare(string text) + { + string item = $"{Guid.CreateVersion7():N}"[9..]; + var textGrain = _clusterClient.GetGrain(item); + try + { + await textGrain.SetText(text); + return new LinkResult(textGrain); + } + catch (Exception) + { + //TODO: Write log message of why it fails? + //Swallow small change of error + return await GenerateTextShare(text); + } + } +} \ No newline at end of file diff --git a/Pushy/Pushy/Grains/TextItemGrain.cs b/Pushy/Pushy/Grains/TextItemGrain.cs index 590e273..1d926b4 100644 --- a/Pushy/Pushy/Grains/TextItemGrain.cs +++ b/Pushy/Pushy/Grains/TextItemGrain.cs @@ -2,6 +2,13 @@ using Orleans.Concurrency; namespace Pushy.Grains; +[GenerateSerializer] +public enum TextSetResult +{ + Failure, + Success +} + public sealed class TextItem { public string? Text { get; set; } @@ -9,11 +16,8 @@ public sealed class TextItem public interface ITextItem : IGrainWithStringKey { - [Alias("IsAvailable")] - public ValueTask IsAvailable(); - [Alias("SetText")] - public ValueTask SetText(string text); + public ValueTask SetText(string text); [ReadOnly] [return: Immutable] @@ -21,44 +25,51 @@ public interface ITextItem : IGrainWithStringKey ValueTask GetText(); } -public sealed class TextItemGrain : Grain, ITextItem, IRemindable +public sealed class TextItemGrain : IGrainBase, ITextItem, IRemindable { private readonly IPersistentState _state; private IGrainReminder? _reminder; public TextItemGrain( + IGrainContext grainContext, [PersistentState("text")] IPersistentState state) { _state = state; + GrainContext = grainContext; } + public IGrainContext GrainContext { get; } + /// - public override async Task OnActivateAsync(CancellationToken cancellationToken) + public async Task OnActivateAsync(CancellationToken cancellationToken) { _reminder = await this.RegisterOrUpdateReminder("clear", TimeSpan.FromDays(10), TimeSpan.FromDays(10)); } /// - public ValueTask IsAvailable() + public async ValueTask SetText(string text) { - return ValueTask.FromResult(_state.State.Text is null); - } + try + { + if (_state.State.Text is not null) + return TextSetResult.Failure; - /// - public async ValueTask SetText(string text) - { - if(_state.State.Text is not null) - throw new InvalidOperationException("Text is already set"); - - _state.State.Text = text; - await _state.WriteStateAsync(); + _state.State.Text = text; + await _state.WriteStateAsync(); + + return TextSetResult.Success; + } + catch (Exception) + { + return TextSetResult.Failure; + } } /// public ValueTask GetText() { - return ValueTask.FromResult(_state.State.Text!); + return ValueTask.FromResult(_state.State.Text ?? throw new InvalidOperationException("No text was available")); } /// @@ -68,6 +79,7 @@ public sealed class TextItemGrain : Grain, ITextItem, IRemindable { _state.State.Text = null; await _state.WriteStateAsync(); + this.DeactivateOnIdle(); } } } \ No newline at end of file diff --git a/Pushy/Pushy/LinkGenerator.cs b/Pushy/Pushy/LinkGenerator.cs deleted file mode 100644 index 600bc1e..0000000 --- a/Pushy/Pushy/LinkGenerator.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Security.Cryptography; -using System.Text; -using Pushy.Grains; - -namespace Pushy; - -public sealed class LinkGenerator -{ - private readonly ILogger _logger; - private readonly IClusterClient _clusterClient; - - public LinkGenerator( - IClusterClient clusterClient, - ILogger logger) - { - _clusterClient = clusterClient; - _logger = logger; - } - - public async Task GenerateTextShare(string text) - { - string item = $"{Guid.CreateVersion7():N}"[6..]; - var sharedGrain = _clusterClient.GetGrain(item); - if (await sharedGrain.IsAvailable()) - { - await sharedGrain.SetText(text); - return sharedGrain; - } - - return await GenerateTextShare(text); - } -} \ No newline at end of file diff --git a/Pushy/Pushy/Program.cs b/Pushy/Pushy/Program.cs index c96bb75..d329835 100644 --- a/Pushy/Pushy/Program.cs +++ b/Pushy/Pushy/Program.cs @@ -3,8 +3,8 @@ using Elastic.Extensions.Logging.Options; using Elastic.Transport; using Microsoft.AspNetCore.DataProtection; using Pushy.Components; +using Pushy.Grains; using StackExchange.Redis; -using LinkGenerator = Pushy.LinkGenerator; var builder = WebApplication.CreateBuilder(args); @@ -51,7 +51,8 @@ if (builder.Environment.IsProduction()) }); } -builder.Services.AddScoped(); +builder.Services.AddScoped(service => + service.GetRequiredService().GetGrain(Guid.Empty)); builder.Services.AddAllElasticApm(); builder.Services.AddHealthChecks(); diff --git a/Pushy/Pushy/Pushy.csproj b/Pushy/Pushy/Pushy.csproj index b7ef52c..b4c3193 100644 --- a/Pushy/Pushy/Pushy.csproj +++ b/Pushy/Pushy/Pushy.csproj @@ -9,6 +9,11 @@ d8fe2296-80f7-4812-b26a-ccaa6167a6e1 + + true + true + + @@ -23,6 +28,7 @@ +