Skip to content

Commit

Permalink
Added UnitOfWrite (UnitOfWork) and UnitOfRead
Browse files Browse the repository at this point in the history
Simplified Vogen because most features were backported into it
More compliant OpenAPI schema
Added partial support for NexusMods OAuth2
Removed EFPlus
  • Loading branch information
Aragas committed May 26, 2024
1 parent 7d8079b commit d31e5fe
Show file tree
Hide file tree
Showing 278 changed files with 5,026 additions and 2,940 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ MigrationBackup/
.ionide/
*.sqlite3
!/build/*
/src/swagger.json
/src/BUTR.Site.NexusMods.Server/BUTR.Site.NexusMods.Server.json
/src/BUTR.Site.NexusMods.Server/.config/dotnet-tools.json
/**/*.g.cs
/src/BUTR.Site.NexusMods.ServerClient/.config/dotnet-tools.json
Expand Down
32 changes: 16 additions & 16 deletions src/BUTR.Site.NexusMods.Client/BUTR.Site.NexusMods.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@
<ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
<PackageReference Include="Blazored.SessionStorage" Version="2.4.0" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.5.1" />
<PackageReference Include="Blazorise.Charts" Version="1.5.1" />
<PackageReference Include="Blazorise.Components" Version="1.5.1" />
<PackageReference Include="Blazorise.DataGrid" Version="1.5.1" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.5.1" />
<PackageReference Include="Blazorise.LoadingIndicator" Version="1.5.1" />
<PackageReference Include="Blazorise.QRCode" Version="1.5.1" />
<PackageReference Include="Blazorise.Snackbar" Version="1.5.1" />
<PackageReference Include="Blazorise.TreeView" Version="1.5.1" />
<PackageReference Include="BUTR.CrashReport.Models" Version="13.0.0.74" />
<PackageReference Include="BUTR.CrashReport.Bannerlord.Parser" Version="13.0.0.74" />
<PackageReference Include="CsvHelper" Version="31.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.3" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.3" />
<PackageReference Include="Blazorise.Bootstrap5" Version="1.5.2" />
<PackageReference Include="Blazorise.Charts" Version="1.5.2" />
<PackageReference Include="Blazorise.Components" Version="1.5.2" />
<PackageReference Include="Blazorise.DataGrid" Version="1.5.2" />
<PackageReference Include="Blazorise.Icons.FontAwesome" Version="1.5.2" />
<PackageReference Include="Blazorise.LoadingIndicator" Version="1.5.2" />
<PackageReference Include="Blazorise.QRCode" Version="1.5.2" />
<PackageReference Include="Blazorise.Snackbar" Version="1.5.2" />
<PackageReference Include="Blazorise.TreeView" Version="1.5.2" />
<PackageReference Include="BUTR.CrashReport.Models" Version="13.0.0.81" />
<PackageReference Include="BUTR.CrashReport.Bannerlord.Parser" Version="13.0.0.81" />
<PackageReference Include="CsvHelper" Version="32.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.5" PrivateAssets="all" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.5" />
<PackageReference Include="Octokit" Version="10.0.0" />
<PackageReference Include="Octokit" Version="11.0.1" />
<PackageReference Include="System.Text.Encodings.Web" Version="8.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
SelectedRowChanged="@(model => { DataGridUtils.SelectDeselect(model, ref Value, ref _dataGridRef); })"
ReadData="@OnReadData"
TotalItems="@Metadata.TotalCount"
PageSize="@Metadata.PageSize"
PageSizes="@PageSizes"
SelectionMode="@DataGridSelectionMode.Single"
ShowPager
ShowPageSizes
PagerOptions="@(new DataGridPagerOptions { PaginationPosition = PagerElementPosition.Center, ButtonRowPosition = PagerElementPosition.Start, TotalItemsPosition = PagerElementPosition.End })"
PageSizes="@PageSizes"
Filterable="@Filterable"
Sortable="@Sortable"
Responsive
Expand Down Expand Up @@ -133,6 +132,7 @@
.Select(x => new Sorting(x.SortField, x.SortDirection.ToSortingType()))
.ToArray();
var filterings = GetFilters is not null ? GetFilters(e.Columns).ToArray() : Array.Empty<Filtering>();

await LoadItems(e.Page, e.PageSize, filterings, sortings, CancellationToken.None);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
SelectedRowChanged="@(model => { DataGridUtils.SelectDeselect(model, ref Value, ref _dataGridRef); })"
ReadData="@OnReadData"
CurrentPage="@Metadata.CurrentPage"
PageSize="@Metadata.PageSize"
PageSizes="@PageSizes"
TotalItems="@Metadata.TotalCount"
SelectionMode="@DataGridSelectionMode.Single"
Expand Down
136 changes: 136 additions & 0 deletions src/BUTR.Site.NexusMods.Client/Components/Grid/DataGridVirtual.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
@typeparam TItem where TItem : class

@inject ILocalStorageService _localStorage

<DataGrid @ref="@_dataGridRef" @attributes="@AdditionalAttributes"
TItem="TItem"
SelectedRowChanged="@(model => { DataGridUtils.SelectDeselect(model, ref Value, ref _dataGridRef); })"
SelectionMode="@DataGridSelectionMode.Single"
Data="@Values"
ReadData="@OnReadData"
TotalItems="@Metadata.TotalCount"
Virtualize
VirtualizeOptions="@virtualizeOptions"
ShowPager
ShowPageSizes
Filterable="@Filterable"
Sortable="@Sortable"
Responsive
Editable="@Editable"
EditMode="@EditMode"
Resizable="@Resizable"
ResizeMode="@ResizeMode"
DataGridColumns="@DataGridColumns"
ButtonRowTemplate="@ButtonRowTemplate"
DetailRowTemplate="@DetailRowTemplate"
DetailRowTrigger="@DetailRowTrigger"
FixedHeader="@FixedHeader">
</DataGrid>

@code {

public sealed record ItemsResponse(PagingMetadata Metadata, ICollection<TItem> Items, PagingAdditionalMetadata AdditionalMetadata);

[Parameter]
public bool Resizable { get; set; } = false;
[Parameter]
public TableResizeMode ResizeMode { get; set; }

[Parameter]
public bool Editable { get; set; } = false;
[Parameter]
public DataGridEditMode EditMode { get; set; }

[Parameter]
public bool Filterable { get; set; } = false;

[Parameter]
public bool Sortable { get; set; } = false;

[Parameter]
public bool FixedHeader { get; set; } = false;

[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string, object>? AdditionalAttributes { get; set; }

[Parameter]
public RenderFragment? DataGridColumns { get; set; }

[Parameter]
public RenderFragment<ButtonRowContext<TItem>>? ButtonRowTemplate { get; set; }

[Parameter]
public RenderFragment<TItem>? DetailRowTemplate { get; set; }

[Parameter]
public Func<DetailRowTriggerEventArgs<TItem>, bool>? DetailRowTrigger { get; set; }

[Parameter]
public Func<IEnumerable<DataGridColumnInfo>, IEnumerable<Filtering>>? GetFilters { get; set; }

[Parameter]
public Func<int, int, ICollection<Filtering>, ICollection<Sorting>, CancellationToken, Task<ItemsResponse>>? GetItems { get; set; }

[Parameter]
public int DefaultPageSize { get; set; } = UserSettings.DefaultPageSize;

[Parameter]
public IEnumerable<int> PageSizes { get; set; } = UserSettings.AvailablePageSizes;

[Parameter]
public Func<UserSettings, int>? GetPageSize { get; set; } = (settings => settings.PageSize);

[Parameter]
public PagingMetadata Metadata { get; set; }

[Parameter]
public PagingAdditionalMetadata AdditionalMetadata { get; set; }

public TItem? Value;
public ICollection<TItem> Values = default!;

private int _progressValue = default!;

private DataGrid<TItem> _dataGridRef = default!;
private VirtualizeOptions virtualizeOptions;

public DataGridVirtual()
{
Metadata = new PagingMetadata(1, 0, DefaultPageSize, 0);
AdditionalMetadata = PagingAdditionalMetadata.Empty;
}

protected override async Task OnInitializedAsync()
{
virtualizeOptions = new() { DataGridHeight = "500px" };
Metadata = new(1, 0, 0, 0);
}

public Task Reload() => _dataGridRef.Reload();

private async Task OnReadData(DataGridReadDataEventArgs<TItem> e)
{
if (!e.CancellationToken.IsCancellationRequested)
{
var sortings = e.Columns
.Where(x => x.SortIndex != -1)
.OrderBy(x => x.SortIndex)
.Select(x => new Sorting(x.SortField, x.SortDirection.ToSortingType()))
.ToArray();
var filterings = GetFilters is not null ? GetFilters(e.Columns).ToArray() : Array.Empty<Filtering>();

var page = e.VirtualizeOffset / e.VirtualizeCount + 1;
var pageSize = e.VirtualizeCount;
await LoadItems(page, pageSize, filterings, sortings, CancellationToken.None);
}
}

private async Task LoadItems(int page, int pageSize, ICollection<Filtering> filterings, ICollection<Sorting> sortings, CancellationToken ct = default)
{
var response = (GetItems is not null ? await GetItems(page, pageSize, filterings, sortings, ct) : null) ?? new(PagingMetadata.Empty, Array.Empty<TItem>(), PagingAdditionalMetadata.Empty);
Metadata = response.Metadata;
Values = response.Items;
AdditionalMetadata = response.AdditionalMetadata;
}

}
20 changes: 10 additions & 10 deletions src/BUTR.Site.NexusMods.Client/Models/DemoUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ public static class DemoUser
isSupporter: true,
isPremium: true,
role: ApplicationRoles.User,
steamUserId: null,
gogUserId: null,
discordUserId: null,
gitHubUserId: null,
steamUserId: null!,
gogUserId: null!,
discordUserId: null!,
gitHubUserId: null!,
hasTenantGame: true,
availableTenants: new List<ProfileTenantModel> { new(tenantId: 1, name: "Bannerlord") });
private static readonly List<NexusModsModModel> _mods = new()
private static readonly List<UserLinkedModModel> _mods = new()
{
new(nexusModsModId: 1, name: "Demo Mod 1", allowedNexusModsUserIds: Array.Empty<int>(), manuallyLinkedNexusModsUserIds: Array.Empty<int>(), knownModuleIds: Array.Empty<string>(), manuallyLinkedModuleIds: Array.Empty<string>()),
new(nexusModsModId: 2, name: "Demo Mod 2", allowedNexusModsUserIds: Array.Empty<int>(), manuallyLinkedNexusModsUserIds: Array.Empty<int>(), knownModuleIds: Array.Empty<string>(), manuallyLinkedModuleIds: Array.Empty<string>()),
new(nexusModsModId: 3, name: "Demo Mod 3", allowedNexusModsUserIds: Array.Empty<int>(), manuallyLinkedNexusModsUserIds: Array.Empty<int>(), knownModuleIds: Array.Empty<string>(), manuallyLinkedModuleIds: Array.Empty<string>()),
new(nexusModsModId: 4, name: "Demo Mod 4", allowedNexusModsUserIds: Array.Empty<int>(), manuallyLinkedNexusModsUserIds: Array.Empty<int>(), knownModuleIds: Array.Empty<string>(), manuallyLinkedModuleIds: Array.Empty<string>()),
new(nexusModsModId: 1, name: "Demo Mod 1", ownerNexusModsUserIds: Array.Empty<int>(), allowedNexusModsUserIds: Array.Empty<int>(), manuallyLinkedNexusModsUserIds: Array.Empty<int>(), knownModuleIds: Array.Empty<string>(), manuallyLinkedModuleIds: Array.Empty<string>()),
new(nexusModsModId: 2, name: "Demo Mod 2", ownerNexusModsUserIds: Array.Empty<int>(), allowedNexusModsUserIds: Array.Empty<int>(), manuallyLinkedNexusModsUserIds: Array.Empty<int>(), knownModuleIds: Array.Empty<string>(), manuallyLinkedModuleIds: Array.Empty<string>()),
new(nexusModsModId: 3, name: "Demo Mod 3", ownerNexusModsUserIds: Array.Empty<int>(), allowedNexusModsUserIds: Array.Empty<int>(), manuallyLinkedNexusModsUserIds: Array.Empty<int>(), knownModuleIds: Array.Empty<string>(), manuallyLinkedModuleIds: Array.Empty<string>()),
new(nexusModsModId: 4, name: "Demo Mod 4", ownerNexusModsUserIds: Array.Empty<int>(), allowedNexusModsUserIds: Array.Empty<int>(), manuallyLinkedNexusModsUserIds: Array.Empty<int>(), knownModuleIds: Array.Empty<string>(), manuallyLinkedModuleIds: Array.Empty<string>()),
};
private static List<CrashReportModel2>? _crashReports;

public static Task<ProfileModel> GetProfile() => Task.FromResult(_profile);
public static IAsyncEnumerable<NexusModsModModel> GetMods() => _mods.ToAsyncEnumerable();
public static IAsyncEnumerable<UserLinkedModModel> GetMods() => _mods.ToAsyncEnumerable();
public static async IAsyncEnumerable<CrashReportModel2> GetCrashReports(IHttpClientFactory factory)
{
static string GetException(ExceptionModel? exception, bool inner = false) => exception is null ? string.Empty : $"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
</CardTitle>
</CardHeader>
<CardBody>
<DataGridPaging @ref="@_dataGridRef" TItem="QuartzExecutionLogEntity" DetailRowTrigger="@(x => x.Item.LogId == _dataGridRef?.Value?.LogId)" GetFilters="@GetFilters" GetItems="@Paging" Sortable Filterable FixedHeader>
<DataGridVirtual @ref="@_dataGridRef" TItem="QuartzExecutionLogEntity" DetailRowTrigger="@(x => x.Item.LogId == _dataGridRef?.Value?.LogId)" GetFilters="@GetFilters" GetItems="@Paging" Sortable Filterable FixedHeader>
<DataGridColumns>
<DataGridColumn TItem="QuartzExecutionLogEntity" ElementId="job-name" Field="@nameof(QuartzExecutionLogEntity.JobName)" Caption="" Filterable="@false" Sortable="@false" >
<DisplayTemplate>
Expand All @@ -60,7 +60,7 @@
<DataGridColumnText TItem="QuartzExecutionLogEntity" ElementId="quartz-job" Field="@nameof(QuartzExecutionLogEntity.JobName)" Caption="Job" Filterable Sortable SortField="@nameof(EntityFields.JobName)" />
<DataGridColumnText TItem="QuartzExecutionLogEntity" Field="@nameof(QuartzExecutionLogEntity.TriggerName)" Caption="Trigger" Filterable="@false" Sortable="@false" />
<DataGridColumnDateTime TItem="QuartzExecutionLogEntity" Field="@nameof(QuartzExecutionLogEntity.ScheduleFireTimeUtc)" Caption="Schedule Fire Time" Filterable="@false" Sortable="@false" >
<DisplayTemplate>@(context.ScheduleFireTimeUtc?.ToString("yyyy-MM-dd HH:mm:ss") ?? string.Empty)</DisplayTemplate>
<DisplayTemplate>@(context.ScheduleFireTimeUtc?.ToString("yyyy-MM-dd HH:mm:ss"))</DisplayTemplate>
</DataGridColumnDateTime>
<DataGridColumnDateTime TItem="QuartzExecutionLogEntity" ElementId="quartz-actual-fire-time" Field="@nameof(QuartzExecutionLogEntity.FireTimeUtc)" Caption="Actual Fire Time" Filterable Sortable SortField="@nameof(EntityFields.FireTimeUtc)" >
<DisplayTemplate>@(context.FireTimeUtc.ToString("yyyy-MM-dd HH:mm:ss"))</DisplayTemplate>
Expand All @@ -86,7 +86,7 @@
<MemoEdit Text="@(context.ExecutionLogDetail?.ErrorStackTrace ?? string.Empty)" AutoSize Disabled />
</Field>
</DetailRowTemplate>
</DataGridPaging>
</DataGridVirtual>
</CardBody>
</Card>

Expand All @@ -99,19 +99,19 @@

private string _jobId = string.Empty;

private DataGridPaging<QuartzExecutionLogEntity>? _dataGridRef;
private DataGridVirtual<QuartzExecutionLogEntity>? _dataGridRef;

private async Task<DataGridPaging<QuartzExecutionLogEntity>.ItemsResponse?> Paging(int page, int pageSize, ICollection<Filtering> filters, ICollection<Sorting> sortings, CancellationToken ct)
private async Task<DataGridVirtual<QuartzExecutionLogEntity>.ItemsResponse?> Paging(int page, int pageSize, ICollection<Filtering> filters, ICollection<Sorting> sortings, CancellationToken ct)
{
var response = await _quartzClient.HistoryPaginatedAsync(new(page: page, pageSize: pageSize, filters: filters, sortings: sortings), cancellationToken: ct);
var response = await _quartzClient.JobsPaginatedAsync(new(page: page, pageSize: pageSize, filters: filters, sortings: sortings), cancellationToken: ct);
return response is { Value: { } data } ? new(data.Metadata, data.Items, data.AdditionalMetadata) : null;
}

private async Task OnSubmit()
{
if (string.IsNullOrEmpty(_jobId)) return;

await _quartzClient.TriggerJobAsync(jobId: _jobId);
await _quartzClient.AddTriggerAsync(_jobId);

await Task.Delay(1000);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
{
try
{
if (await _userClient.SetRoleAsync(nexusModsUserId: (int) _model.UserId, role: _model.Role) is { Error: not null })
if (await _userClient.SetRoleAsync(userId: (int) _model.UserId, role: _model.Role) is { Error: not null })
{
await _notificationService.Success(
$"Assigned '{_model.Role}' to user with id '{_model.UserId}'!",
Expand Down
6 changes: 3 additions & 3 deletions src/BUTR.Site.NexusMods.Client/Pages/Basic/Login.razor
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
TextColor="@TextColor.White"
Type="@ButtonType.Link"
To="@NexusModsTokenUrl">
Log In via NexusMods Token
Log In via NexusMods API Key
</Button>
</Row>
</CardBody>
Expand All @@ -67,11 +67,11 @@


private string NexusModsSSOUrl => $"login-nexusmods-sso{new Uri(_navigationManager.Uri).Query}";
private string NexusModsTokenUrl => $"login-nexusmods-token{new Uri(_navigationManager.Uri).Query}";
private string NexusModsTokenUrl => $"login-nexusmods-apikey{new Uri(_navigationManager.Uri).Query}";

private async void OnDemoLogin()
{
if (await _authenticationProvider.AuthenticateAsync("", "demo") is not null)
if (await _authenticationProvider.AuthenticateWithApiKeyAsync("", "demo") is not null)
{
_navigationManager.NavigateTo(_navigationManager.QueryString("returnUrl") ?? "");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@attribute [Authorize(Roles = $"{ApplicationRoles.Anonymous}")]
@page "/login-nexusmods-token"
@page "/login-nexusmods-apikey"

@inject NavigationManager _navigationManager
@inject AuthenticationProvider _authenticationProvider
Expand Down Expand Up @@ -46,7 +46,7 @@

try
{
if (await _authenticationProvider.AuthenticateAsync(_model.Input, "nexusmods") is null)
if (await _authenticationProvider.AuthenticateWithApiKeyAsync(_model.Input, "nexusmods") is null)
{
_isLoading = false;
StateHasChanged();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@attribute [Authorize(Roles = $"{ApplicationRoles.Anonymous}")]
@page "/login-nexusmods-oauth2"

@inject IAuthenticationClient _authenticationClient;
@inject ILocalStorageService _localStorage;
@inject NavigationManager _navigationManager;

@code {

protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();

var response = await _authenticationClient.GetOAuthUrlAsync();
if (response.Value?.Url is null)
{
_navigationManager.NavigateTo("login");
return;
}

await _localStorage.SetItemAsync("nexusmods_state", response.Value.State);
_navigationManager.NavigateTo(response.Value.Url);
}

}
Loading

0 comments on commit d31e5fe

Please sign in to comment.