refactor: Refactoring of text creation

This commit is contained in:
henrik
2024-12-10 21:42:22 +01:00
parent 41a174e1cb
commit b4b96b3f4e
9 changed files with 101 additions and 138 deletions

View File

@@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIGrainBase_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F7b522cc30a53bd25a33ab94497f733ec9a145f18911e0c8716e1c70af232e26_003FIGrainBase_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIRemindable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fade617b5b5cd50987d2ed8207d608c80393a95d88b90c3a2e0e9335bc19c4b0_003FIRemindable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIRemindable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fade617b5b5cd50987d2ed8207d608c80393a95d88b90c3a2e0e9335bc19c4b0_003FIRemindable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@@ -12,6 +12,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.0" />
<PackageReference Include="UnitsNet" Version="5.60.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,77 +0,0 @@
@page "/test"
@rendermode InteractiveWebAssembly
@inject PersistentComponentState ApplicationState
@inject ILogger<TestClientForm> Logger
<p>@Data - @RendererInfo.Name</p>
<EditForm Enhance FormName="TestForm" OnSubmit="Callback" Model="Item">
<InputText @bind-Value="@Item.Text"/>
<button class="btn " type="submit" disabled="@(doingStuff || !RendererInfo.IsInteractive)">
@if (doingStuff || !RendererInfo.IsInteractive)
{
<span class="loading loading-spinner"></span>
<a>Loading...</a>
}
else
{
<a>Test</a>
}
</button>
</EditForm>
@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<Guid>("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]
};
}
}

View File

@@ -27,7 +27,7 @@
@{ @{
string key = CreatedItems.GetPrimaryKeyString(); string key = CreatedItems.GetPrimaryKeyString();
} }
<a href="@Nav.ToAbsoluteUri($"/t/{key}")">@Nav.ToAbsoluteUri($"/t/{key}")</a> <a class="link" href="@Nav.ToAbsoluteUri($"/t/{key}")">@Nav.ToAbsoluteUri($"/t/{key}")</a>
</div> </div>
} }
</div> </div>
@@ -38,19 +38,21 @@
[Inject] public ILogger<Home> Logger { get; set; } = null!; [Inject] public ILogger<Home> 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; public ITextItem? CreatedItems;
private async Task SubmitInput() private async Task SubmitInput()
{ {
ITextItem item = await ItemGenerator.GenerateTextShare(Form.InputText); LinkResult result = await ItemGenerator.GenerateTextShare(Form.InputText);
CreatedItems = item; if (result.Item is not null)
{
Form = new UploadForm(); CreatedItems = result.Item;
Form = new UploadForm();
}
Logger.LogInformation("A new item was created!"); Logger.LogInformation("A new item was created!");
} }

View File

@@ -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<LinkResult> GenerateTextShare([Immutable] string text);
}
public sealed class LinkGenerator : IGrainBase, ILinkGenerator
{
private readonly ILogger<LinkGenerator> _logger;
private readonly IClusterClient _clusterClient;
public IGrainContext GrainContext { get; }
public LinkGenerator(
IGrainContext grainContext,
IClusterClient clusterClient,
ILogger<LinkGenerator> logger)
{
_clusterClient = clusterClient;
_logger = logger;
GrainContext = grainContext;
}
public async ValueTask<LinkResult> GenerateTextShare(string text)
{
string item = $"{Guid.CreateVersion7():N}"[9..];
var textGrain = _clusterClient.GetGrain<ITextItem>(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);
}
}
}

View File

@@ -2,6 +2,13 @@ using Orleans.Concurrency;
namespace Pushy.Grains; namespace Pushy.Grains;
[GenerateSerializer]
public enum TextSetResult
{
Failure,
Success
}
public sealed class TextItem public sealed class TextItem
{ {
public string? Text { get; set; } public string? Text { get; set; }
@@ -9,11 +16,8 @@ public sealed class TextItem
public interface ITextItem : IGrainWithStringKey public interface ITextItem : IGrainWithStringKey
{ {
[Alias("IsAvailable")]
public ValueTask<bool> IsAvailable();
[Alias("SetText")] [Alias("SetText")]
public ValueTask SetText(string text); public ValueTask<TextSetResult> SetText(string text);
[ReadOnly] [ReadOnly]
[return: Immutable] [return: Immutable]
@@ -21,44 +25,51 @@ public interface ITextItem : IGrainWithStringKey
ValueTask<string> GetText(); ValueTask<string> GetText();
} }
public sealed class TextItemGrain : Grain, ITextItem, IRemindable public sealed class TextItemGrain : IGrainBase, ITextItem, IRemindable
{ {
private readonly IPersistentState<TextItem> _state; private readonly IPersistentState<TextItem> _state;
private IGrainReminder? _reminder; private IGrainReminder? _reminder;
public TextItemGrain( public TextItemGrain(
IGrainContext grainContext,
[PersistentState("text")] IPersistentState<TextItem> state) [PersistentState("text")] IPersistentState<TextItem> state)
{ {
_state = state; _state = state;
GrainContext = grainContext;
} }
public IGrainContext GrainContext { get; }
/// <inheritdoc /> /// <inheritdoc />
public override async Task OnActivateAsync(CancellationToken cancellationToken) public async Task OnActivateAsync(CancellationToken cancellationToken)
{ {
_reminder = await this.RegisterOrUpdateReminder("clear", TimeSpan.FromDays(10), TimeSpan.FromDays(10)); _reminder = await this.RegisterOrUpdateReminder("clear", TimeSpan.FromDays(10), TimeSpan.FromDays(10));
} }
/// <inheritdoc /> /// <inheritdoc />
public ValueTask<bool> IsAvailable() public async ValueTask<TextSetResult> SetText(string text)
{ {
return ValueTask.FromResult(_state.State.Text is null); try
} {
if (_state.State.Text is not null)
return TextSetResult.Failure;
/// <inheritdoc /> _state.State.Text = text;
public async ValueTask SetText(string text) await _state.WriteStateAsync();
{
if(_state.State.Text is not null)
throw new InvalidOperationException("Text is already set");
_state.State.Text = text; return TextSetResult.Success;
await _state.WriteStateAsync(); }
catch (Exception)
{
return TextSetResult.Failure;
}
} }
/// <inheritdoc /> /// <inheritdoc />
public ValueTask<string> GetText() public ValueTask<string> GetText()
{ {
return ValueTask.FromResult(_state.State.Text!); return ValueTask.FromResult(_state.State.Text ?? throw new InvalidOperationException("No text was available"));
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -68,6 +79,7 @@ public sealed class TextItemGrain : Grain, ITextItem, IRemindable
{ {
_state.State.Text = null; _state.State.Text = null;
await _state.WriteStateAsync(); await _state.WriteStateAsync();
this.DeactivateOnIdle();
} }
} }
} }

View File

@@ -1,32 +0,0 @@
using System.Security.Cryptography;
using System.Text;
using Pushy.Grains;
namespace Pushy;
public sealed class LinkGenerator
{
private readonly ILogger<LinkGenerator> _logger;
private readonly IClusterClient _clusterClient;
public LinkGenerator(
IClusterClient clusterClient,
ILogger<LinkGenerator> logger)
{
_clusterClient = clusterClient;
_logger = logger;
}
public async Task<ITextItem> GenerateTextShare(string text)
{
string item = $"{Guid.CreateVersion7():N}"[6..];
var sharedGrain = _clusterClient.GetGrain<ITextItem>(item);
if (await sharedGrain.IsAvailable())
{
await sharedGrain.SetText(text);
return sharedGrain;
}
return await GenerateTextShare(text);
}
}

View File

@@ -3,8 +3,8 @@ using Elastic.Extensions.Logging.Options;
using Elastic.Transport; using Elastic.Transport;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Pushy.Components; using Pushy.Components;
using Pushy.Grains;
using StackExchange.Redis; using StackExchange.Redis;
using LinkGenerator = Pushy.LinkGenerator;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -51,7 +51,8 @@ if (builder.Environment.IsProduction())
}); });
} }
builder.Services.AddScoped<LinkGenerator>(); builder.Services.AddScoped(service =>
service.GetRequiredService<IGrainFactory>().GetGrain<ILinkGenerator>(Guid.Empty));
builder.Services.AddAllElasticApm(); builder.Services.AddAllElasticApm();
builder.Services.AddHealthChecks(); builder.Services.AddHealthChecks();

View File

@@ -9,6 +9,11 @@
<UserSecretsId>d8fe2296-80f7-4812-b26a-ccaa6167a6e1</UserSecretsId> <UserSecretsId>d8fe2296-80f7-4812-b26a-ccaa6167a6e1</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Elastic.Apm.NetCoreAll" Version="1.30.0" /> <PackageReference Include="Elastic.Apm.NetCoreAll" Version="1.30.0" />
<PackageReference Include="Elastic.Extensions.Logging" Version="8.12.2" /> <PackageReference Include="Elastic.Extensions.Logging" Version="8.12.2" />
@@ -23,6 +28,7 @@
<PackageReference Include="Microsoft.Orleans.Sdk" Version="8.2.0" /> <PackageReference Include="Microsoft.Orleans.Sdk" Version="8.2.0" />
<PackageReference Include="Microsoft.Orleans.Server" Version="8.2.0" /> <PackageReference Include="Microsoft.Orleans.Server" Version="8.2.0" />
<PackageReference Include="System.Text.Json" Version="9.0.0" /> <PackageReference Include="System.Text.Json" Version="9.0.0" />
<PackageReference Include="UnitsNet" Version="5.60.0" />
<ProjectReference Include="..\Pushy.Client\Pushy.Client.csproj"/> <ProjectReference Include="..\Pushy.Client\Pushy.Client.csproj"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.0" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.0" />
</ItemGroup> </ItemGroup>