Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feats #43 Convert the application to blazor server side #44

Merged
merged 5 commits into from
Mar 20, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/CodeQuality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup dotnet
uses: actions/setup-dotnet@v3
with:
dotnet-version: '7.0.x'
dotnet-version: '8.0.x'

- name: install wasm-tools
run: dotnet workload install wasm-tools
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: install wasm-tools
run: dotnet workload install wasm-tools
- name: Install dependencies
Expand Down
101 changes: 101 additions & 0 deletions reqifviewer.Tests/Pages/Index/IndexPageTestFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// -------------------------------------------------------------------------------------------------
// <copyright file="IndexPageTestFixture.cs" company="RHEA System S.A.">
//
// Copyright 2023-2024 RHEA System S.A.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// </copyright>
// -------------------------------------------------------------------------------------------------

namespace ReqifViewer.Tests.Pages.Index
{
using System.IO;
using System.Threading;
using System.Threading.Tasks;

using Bunit;

using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.DependencyInjection;

using Moq;

using NUnit.Framework;

using Radzen.Blazor;

using ReqIFSharp;
using ReqIFSharp.Extensions.Services;

using reqifviewer.Pages.Index;

using TestContext = Bunit.TestContext;

/// <summary>
/// Suite of tests for the <see cref="IndexPageTestFixture"/>
/// </summary>
[TestFixture]
public class IndexPageTestFixture
{
private Mock<IReqIFLoaderService> reqIfLoaderService;
private TestContext context;
private const long MaxFileSize = 5 * 1024 * 1024;

[SetUp]
public void SetUp()
{
this.context = new TestContext();
this.reqIfLoaderService = new Mock<IReqIFLoaderService>();

this.context.Services.AddSingleton(this.reqIfLoaderService.Object);
}

[TearDown]
public void TearDown()
{
this.context.Dispose();
}

[Test]
public async Task VerifyComponent()
{
var renderer = this.context.RenderComponent<IndexPage>();
var uploadComponent = renderer.FindComponent<InputFile>();

var file = new Mock<IBrowserFile>();
file.Setup(x => x.Size).Returns(MaxFileSize + 1);
file.Setup(x => x.Name).Returns("file.reqif");
file.Setup(x => x.OpenReadStream(It.IsAny<long>(), It.IsAny<CancellationToken>())).Returns(new MemoryStream());

await renderer.InvokeAsync(() => uploadComponent.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs([file.Object])));
file.Verify(x => x.OpenReadStream(It.IsAny<long>(), It.IsAny<CancellationToken>()), Times.Never);

file.Setup(x => x.Size).Returns(MaxFileSize - 1);
await renderer.InvokeAsync(() => uploadComponent.Instance.OnChange.InvokeAsync(new InputFileChangeEventArgs([file.Object])));
file.Verify(x => x.OpenReadStream(It.IsAny<long>(), It.IsAny<CancellationToken>()), Times.Once);

var loadButton = renderer.FindComponent<RadzenButton>();
await renderer.InvokeAsync(loadButton.Instance.Click.InvokeAsync);
this.reqIfLoaderService.Verify(x => x.Load(It.IsAny<Stream>(), It.IsAny<SupportedFileExtensionKind>(), It.IsAny<CancellationToken>()), Times.Once);

var cancelButton = renderer.FindComponents<RadzenButton>()[1];
await renderer.InvokeAsync(cancelButton.Instance.Click.InvokeAsync);
Assert.That(renderer.Instance.IsLoading, Is.EqualTo(false));

var clearButton = renderer.FindComponents<RadzenButton>()[2];
await renderer.InvokeAsync(clearButton.Instance.Click.InvokeAsync);
this.reqIfLoaderService.Verify(x => x.Reset(), Times.AtLeastOnce);
}
}
}
6 changes: 1 addition & 5 deletions reqifviewer.Tests/reqifviewer.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Company>RHEA System S.A.</Company>
<Title>reqifviewer.Tests</Title>
<Description>reqifviewer test project</Description>
Expand Down Expand Up @@ -31,10 +31,6 @@
<ProjectReference Include="..\reqifviewer\reqifviewer.csproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="Pages\Index\" />
</ItemGroup>

<ItemGroup>
<None Update="TestData\ProR_Traceability-Template-v1.0.reqif">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
Expand Down
30 changes: 15 additions & 15 deletions reqifviewer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY Nuget.Config .
COPY reqifviewer reqifviewer
RUN dotnet restore --configfile Nuget.Config reqifviewer
RUN dotnet build --no-restore reqifviewer -c Release -o /app/build
FROM build AS publish
RUN dotnet publish reqifviewer -c Release -o /app/publish
FROM nginx:alpine AS final
WORKDIR /usr/share/nginx/html
COPY --from=publish /app/publish/wwwroot .
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build

WORKDIR /src

COPY Nuget.Config .
COPY reqifviewer reqifviewer
RUN dotnet restore --configfile Nuget.Config reqifviewer
RUN dotnet build --no-restore reqifviewer -c Release -o /app/build

FROM build AS publish
RUN dotnet publish reqifviewer -c Release -o /app/publish

FROM nginx:alpine AS final
WORKDIR /usr/share/nginx/html
COPY --from=publish /app/publish/wwwroot .
COPY reqifviewer/nginx.conf /etc/nginx/nginx.conf
162 changes: 11 additions & 151 deletions reqifviewer/Pages/Index/IndexPage.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<!------------------------------------------------------------------------------
@page "/"
<!------------------------------------------------------------------------------
Copyright 2021-2022 RHEA System S.A.

Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -14,17 +15,8 @@
limitations under the License.
------------------------------------------------------------------------------->

@page "/"

@using System.Diagnostics
@using System.Globalization
@using System.IO
@using System.Threading
@using ReqIFSharp
@using ReqIFSharp.Extensions.Services
@using Serilog;

@inject IReqIFLoaderService ReqIfLoaderService

<div class="row mt-5 pt-5 text-center">
<div class="col-md-12">
Expand All @@ -35,17 +27,18 @@

<div class="row">
<div class="col align-self-center">
<div class="input-group mb-3">
<InputFile OnChange="HandleSelection" id="inputGroupFile" class="form-control" accept=".reqif,.reqifz,.zip"></InputFile>
</div>
<div class="input-group mb-3">
<InputFile OnChange="HandleSelection" id="inputGroupFile" class="form-control" accept=".reqif,.reqifz,.zip"></InputFile>
</div>
</div>
<span class="text-danger pb-3">@(this.ErrorMessage)</span>
</div>

<RadzenButton style="margin: 0 1rem 1rem 0; width: 200px" Icon="update" Disabled="@(!reqifisAvailable)" BusyText="Loading ..." IsBusy=@this.isLoading Click=@OnLoadReqIF Text="Load" />
<RadzenButton style="margin: 0 1rem 1rem 0; width: 200px" Click="@OnCancel" Text="Cancel" Disabled="@(!this.isLoading)" />
<RadzenButton style="margin: 0 1rem 1rem 0; width: 200px" Click="@OnClear" Text="Clear" Disabled="@(this.reqIfs == null)" />
<RadzenButton style="margin: 0 1rem 1rem 0; width: 200px" Icon="update" Disabled="@(!reqifisAvailable)" BusyText="Loading ..." IsBusy="@this.IsLoading" Click="@(this.OnLoadReqIF)" Text="Load" />
<RadzenButton style="margin: 0 1rem 1rem 0; width: 200px" Click="@this.OnCancel" Text="Cancel" Disabled="@(!this.IsLoading)" />
<RadzenButton style="margin: 0 1rem 1rem 0; width: 200px" Click="@this.OnClear" Text="Clear" Disabled="@(this.reqIfs == null)" />

@if (this.isLoading)
@if (this.IsLoading)
{
<RadzenProgressBar Value="100" ShowValue="false" Mode="ProgressBarMode.Indeterminate" Style="margin-bottom: 20px" />
}
Expand Down Expand Up @@ -83,137 +76,4 @@ else
</Columns>
</RadzenGrid>
}
}

@code {

private string fileSelectionText = "Select a file";

private MemoryStream reqifStream;

private bool reqifisAvailable = false;

private bool isLoading = false;

private IEnumerable<ReqIF> reqIfs;

private CancellationTokenSource cancellationTokenSource;

/// <summary>
/// Invoked when the component is initialized after having received its initial parameters
/// </summary>
protected override void OnInitialized()
{
if (this.ReqIfLoaderService.ReqIFData == null || !this.ReqIfLoaderService.ReqIFData.Any())
{
this.reqIfs = null;

Log.ForContext<IndexPage>().Debug("no ReqIF loaded");
}
else
{
this.reqIfs = this.ReqIfLoaderService.ReqIFData;

Log.ForContext<IndexPage>().Debug("a Total of {amount} ReqIF loaded", this.reqIfs.Count());
}
}

/// <summary>
/// handles file selection
/// </summary>
/// <param name="e">
/// The <see cref="InputFileChangeEventArgs"/> to be used to handle the selected file
/// </param>
/// <returns>
/// an awaitable <see cref="Task"/>
/// </returns>
private async Task HandleSelection(InputFileChangeEventArgs e)
{
var sw = Stopwatch.StartNew();

this.reqifisAvailable = false;

this.reqifStream = new MemoryStream();

await e.File.OpenReadStream(long.MaxValue).CopyToAsync(this.reqifStream);

this.fileSelectionText = e.File.Name;

this.reqifisAvailable = true;

Log.ForContext<IndexPage>().Information("file read into stream in {time} [ms]" , sw.ElapsedMilliseconds);
}

/// <summary>
/// Loads the <see cref="ReqIF"/> from the selected file
/// </summary>
/// <returns>
/// an awaitable <see cref="Task"/>
/// </returns>
private async Task OnLoadReqIF()
{
try
{
var sw = Stopwatch.StartNew();

this.isLoading = true;

this.StateHasChanged();

await Task.Delay(500);

this.cancellationTokenSource = new CancellationTokenSource();

this.reqIfs = null;

if (this.reqifStream.Position != 0)
{
this.reqifStream.Seek(0, SeekOrigin.Begin);
}

var convertPathToSupportedFileExtensionKind = this.fileSelectionText.ConvertPathToSupportedFileExtensionKind();

await this.ReqIfLoaderService.Load(this.reqifStream,convertPathToSupportedFileExtensionKind, this.cancellationTokenSource.Token);
this.reqIfs = this.ReqIfLoaderService.ReqIFData;

Log.ForContext<IndexPage>().Information("a total of {amount} ReqIF objects deserialized in {time} [ms]", this.reqIfs.Count(), sw.ElapsedMilliseconds);
}
catch (TaskCanceledException)
{
Log.ForContext<IndexPage>().Information("Load was cancelled");
}
catch (Exception e)
{
Log.ForContext<IndexPage>().Error(e, "load reqif failed");
}
finally
{
isLoading = false;
this.StateHasChanged();
}
}

/// <summary>
/// Cancel loading the ReqIF file
/// </summary>
private void OnCancel()
{
if (this.cancellationTokenSource != null)
{
this.cancellationTokenSource.Cancel();
isLoading = false;
this.StateHasChanged();
}
}

/// <summary>
/// Clear the ReqIF file and reset the <see cref="IReqIFLoaderService"/>
/// </summary>
private void OnClear()
{
this.reqIfs = null;
this.ReqIfLoaderService.Reset();
isLoading = false;
this.StateHasChanged();
}
}
}
Loading
Loading