Skip to content

Commit

Permalink
Feat #634 [Implement] The use of a toast/message notitication to indi…
Browse files Browse the repository at this point in the history
…cate success or failure to a user on Reference Data, Server Administration and Engineering Model pages (#638)
  • Loading branch information
joao4all authored May 23, 2024
1 parent 307859b commit 43f8a13
Show file tree
Hide file tree
Showing 52 changed files with 715 additions and 190 deletions.
Original file line number Diff line number Diff line change
@@ -1,57 +1,80 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="NotificationServiceTestFixture.cs" company="Starion Group S.A.">
// Copyright (c) 2023-2024 Starion Group S.A.
// Copyright (c) 2024 Starion Group S.A.
//
// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar
// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
//
// This file is part of CDP4-COMET WEB Community Edition
// The CDP4-COMET WEB Community Edition is the Starion Web Application implementation of ECSS-E-TM-10-25
// Annex A and Annex C.
// This file is part of COMET WEB Community Edition
// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
//
// 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
// The COMET WEB Community Edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// 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.
// The COMET WEB Community Edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace COMET.Web.Common.Tests.Services.NotificationService
{
using COMET.Web.Common.Services.NotificationService;

using DynamicData;

using FluentResults;

using NUnit.Framework;

[TestFixture]
public class NotificationServiceTestFixture
{
private NotificationService notificationService;

[SetUp]
public void Setup()
{
this.notificationService = new NotificationService();
}

[Test]
public void VerifyNotificationService()
{
var notificationService = new NotificationService();
Assert.That(notificationService.NotificationCount, Is.EqualTo(0));
Assert.That(this.notificationService.NotificationCount, Is.EqualTo(0));

this.notificationService.AddNotifications(-2);
Assert.That(this.notificationService.NotificationCount, Is.EqualTo(0));

this.notificationService.AddNotifications(4);
Assert.That(this.notificationService.NotificationCount, Is.EqualTo(4));

notificationService.AddNotifications(-2);
Assert.That(notificationService.NotificationCount, Is.EqualTo(0));
this.notificationService.RemoveNotifications(1);
Assert.That(this.notificationService.NotificationCount, Is.EqualTo(3));

notificationService.AddNotifications(4);
Assert.That(notificationService.NotificationCount, Is.EqualTo(4));
this.notificationService.RemoveNotifications(-1);
Assert.That(this.notificationService.NotificationCount, Is.EqualTo(3));

notificationService.RemoveNotifications(1);
Assert.That(notificationService.NotificationCount, Is.EqualTo(3));
this.notificationService.RemoveNotifications(5);
Assert.That(this.notificationService.NotificationCount, Is.EqualTo(0));
}

notificationService.RemoveNotifications(-1);
Assert.That(notificationService.NotificationCount, Is.EqualTo(3));
[Test]
public void VerifyResultsList()
{
Assert.Multiple(() =>
{
Assert.That(this.notificationService.Results, Is.Not.Null);
Assert.That(this.notificationService.Results, Has.Count.EqualTo(0));
});

notificationService.RemoveNotifications(5);
Assert.That(notificationService.NotificationCount, Is.EqualTo(0));
this.notificationService.Results.Add(new Result());
Assert.That(this.notificationService.Results, Has.Count.EqualTo(1));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace COMET.Web.Common.Tests.Services.SessionManagement

using CDP4Dal;

using COMET.Web.Common.Services.NotificationService;
using COMET.Web.Common.Services.SessionManagement;

using DynamicData;
Expand All @@ -46,13 +47,15 @@ public class SessionServiceTestFixture
private SessionService sessionService;
private readonly Uri uri = new("http://test.com/");
private CDPMessageBus messageBus;
private Mock<INotificationService> notificationService;

[SetUp]
public void Setup()
{
var logger = new Mock<ILogger<SessionService>>();
this.messageBus = new CDPMessageBus();
this.sessionService = new SessionService(logger.Object, this.messageBus);
this.notificationService = new Mock<INotificationService>();
this.sessionService = new SessionService(logger.Object, this.messageBus, this.notificationService.Object);

var engineeringModel = new EngineeringModel();
this.sessionService.OpenIterations.Add(new Iteration(){ Container = engineeringModel});
Expand All @@ -78,7 +81,12 @@ public void VerifyCreateOrUpdateThings()
var domain = new DomainOfExpertise();
siteDirectory.Domain.Add(domain);

Assert.That(async () => await this.sessionService.CreateOrUpdateThings(siteDirectory, [domain], ["file A"]), Throws.InvalidOperationException);
Assert.Multiple(() =>
{
Assert.That(async () => await this.sessionService.CreateOrUpdateThings(siteDirectory, [domain], ["file A"]), Throws.InvalidOperationException);
Assert.That(async () => await this.sessionService.CreateOrUpdateThingsWithNotification(siteDirectory, [domain], ["file A"]), Throws.InvalidOperationException);
Assert.That(async () => await this.sessionService.CreateOrUpdateThingsWithNotification(siteDirectory, [domain]), Throws.InvalidOperationException);
});
}

[Test]
Expand Down
2 changes: 0 additions & 2 deletions COMET.Web.Common/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,3 @@
</NotFound>
</Router>
</CascadingAuthenticationState>


Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

namespace COMET.Web.Common.Services.NotificationService
{
using DynamicData;

using FluentResults;

/// <summary>
/// The <see cref="INotificationService"/> provides notification information
/// </summary>
Expand All @@ -35,6 +39,11 @@ public interface INotificationService
/// </summary>
int NotificationCount { get; }

/// <summary>
/// A sourcelist with <see cref="Result"/>s
/// </summary>
SourceList<Result> Results { get; }

/// <summary>
/// Increases the <see cref="NotificationService.NotificationCount"/>
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="NotificationService.cs" company="Starion Group S.A.">
// Copyright (c) 2023-2024 Starion Group S.A.
// Copyright (c) 2024 Starion Group S.A.
//
// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, Nabil Abbar
// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua
//
// This file is part of CDP4-COMET WEB Community Edition
// The CDP4-COMET WEB Community Edition is the Starion Web Application implementation of ECSS-E-TM-10-25
// Annex A and Annex C.
// This file is part of COMET WEB Community Edition
// The COMET WEB Community Edition is the Starion Group Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C.
//
// 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
// The COMET WEB Community Edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// 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.
// The COMET WEB Community Edition is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace COMET.Web.Common.Services.NotificationService
{
using DynamicData;

using FluentResults;

using ReactiveUI;

/// <summary>
Expand All @@ -37,6 +40,11 @@ public class NotificationService : ReactiveObject, INotificationService
/// </summary>
private int notificationCount;

/// <summary>
/// A sourcelist with <see cref="Result" />s
/// </summary>
public SourceList<Result> Results { get; } = new();

/// <summary>
/// Gets the number of notification(s)
/// </summary>
Expand Down
20 changes: 20 additions & 0 deletions COMET.Web.Common/Services/SessionManagement/ISessionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ namespace COMET.Web.Common.Services.SessionManagement
using CDP4Dal;
using CDP4Dal.Operations;

using COMET.Web.Common.Services.NotificationService;

using DynamicData;

using FluentResults;
Expand Down Expand Up @@ -117,5 +119,23 @@ public interface ISessionService : CDP4Web.Services.SessionService.ISessionServi
/// <param name="files">A <see cref="IReadOnlyCollection{T}"/> of the file paths as <see cref="string"/> to create or update</param>
/// <returns>A <see cref="Task{T}" /> with the <see cref="Result" /> of the operation</returns>
Task<Result> WriteTransaction(OperationContainer operationContainer, IReadOnlyCollection<string> files);

/// <summary>
/// Creates or updates things, add new notifications to the <see cref="INotificationService"/>
/// </summary>
/// <param name="topContainer">The <see cref="Thing" /> top container to use for the transaction</param>
/// <param name="toUpdateOrCreate">A <see cref="IReadOnlyCollection{T}" /> of <see cref="Thing" /> to create or update</param>
/// <returns>A <see cref="Task{T}" /> with the <see cref="Result" /> of the operation</returns>
Task<Result> CreateOrUpdateThingsWithNotification(Thing topContainer, IReadOnlyCollection<Thing> toUpdateOrCreate);

/// <summary>
/// Creates or updates things, add new notifications to the <see cref="INotificationService"/>
/// </summary>
/// <param name="topContainer">The <see cref="Thing" /> top container to use for the transaction</param>
/// <param name="toUpdateOrCreate">A <see cref="IReadOnlyCollection{T}" /> of <see cref="Thing" /> to create or update</param>
/// <param name="files">A <see cref="IReadOnlyCollection{T}"/> of the file paths as <see cref="string"/> to create or update</param>
/// <returns>A <see cref="Task{T}" /> with the <see cref="Result" /> of the operation</returns>
/// <remarks>The <paramref name="topContainer" /> have to be a cloned <see cref="Thing" /></remarks>
Task<Result> CreateOrUpdateThingsWithNotification(Thing topContainer, IReadOnlyCollection<Thing> toUpdateOrCreate, IReadOnlyCollection<string> files);
}
}
39 changes: 38 additions & 1 deletion COMET.Web.Common/Services/SessionManagement/SessionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ namespace COMET.Web.Common.Services.SessionManagement
using CDP4Web.Enumerations;
using CDP4Web.Extensions;

using COMET.Web.Common.Services.NotificationService;

using DynamicData;

using FluentResults;
Expand All @@ -59,14 +61,21 @@ public class SessionService : CDP4Web.Services.SessionService.SessionService, IS
/// </summary>
private readonly ILogger<SessionService> logger;

/// <summary>
/// The <see cref="INotificationService"/>
/// </summary>
private readonly INotificationService notificationService;

/// <summary>
/// Creates a new instance of type <see cref="SessionService" />
/// </summary>
/// <param name="logger">the <see cref="ILogger{TCategoryName}" /></param>
/// <param name="messageBus">The <see cref="IMessageBus" /></param>
public SessionService(ILogger<SessionService> logger, ICDPMessageBus messageBus) : base(logger, messageBus)
/// <param name="notificationService">The <see cref="INotificationService"/></param>
public SessionService(ILogger<SessionService> logger, ICDPMessageBus messageBus, INotificationService notificationService) : base(logger, messageBus)
{
this.logger = logger;
this.notificationService = notificationService;
}

/// <summary>
Expand Down Expand Up @@ -154,6 +163,34 @@ public Task<Result> CreateOrUpdateThings(Thing topContainer, IReadOnlyCollection
return this.WriteTransaction(operationContainer, files);
}

/// <summary>
/// Creates or updates things, add new notifications to the <see cref="INotificationService"/>
/// </summary>
/// <param name="topContainer">The <see cref="Thing" /> top container to use for the transaction</param>
/// <param name="toUpdateOrCreate">A <see cref="IReadOnlyCollection{T}" /> of <see cref="Thing" /> to create or update</param>
/// <returns>A <see cref="Task{T}" /> with the <see cref="Result" /> of the operation</returns>
public async Task<Result> CreateOrUpdateThingsWithNotification(Thing topContainer, IReadOnlyCollection<Thing> toUpdateOrCreate)
{
var result = await this.CreateOrUpdateThings(topContainer, toUpdateOrCreate);
this.notificationService.Results.Add(result);
return result;
}

/// <summary>
/// Creates or updates things, add new notifications to the <see cref="INotificationService"/>
/// </summary>
/// <param name="topContainer">The <see cref="Thing" /> top container to use for the transaction</param>
/// <param name="toUpdateOrCreate">A <see cref="IReadOnlyCollection{T}" /> of <see cref="Thing" /> to create or update</param>
/// <param name="files">A <see cref="IReadOnlyCollection{T}"/> of the file paths as <see cref="string"/> to create or update</param>
/// <returns>A <see cref="Task{T}" /> with the <see cref="Result" /> of the operation</returns>
/// <remarks>The <paramref name="topContainer" /> have to be a cloned <see cref="Thing" /></remarks>
public async Task<Result> CreateOrUpdateThingsWithNotification(Thing topContainer, IReadOnlyCollection<Thing> toUpdateOrCreate, IReadOnlyCollection<string> files)
{
var result = await this.CreateOrUpdateThings(topContainer, toUpdateOrCreate, files);
this.notificationService.Results.Add(result);
return result;
}

/// <summary>
/// Reads the <see cref="EngineeringModel" /> instances from the data-source
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,15 +345,15 @@ public async Task VerifyAddOrEditCategory()
Assert.Multiple(() =>
{
Assert.That(this.viewModel.Rows.Count, Is.EqualTo(2));
this.sessionService.Verify(x => x.CreateOrUpdateThings(It.IsAny<Thing>(), It.IsAny<IReadOnlyCollection<Thing>>()), Times.Exactly(2));
this.sessionService.Verify(x => x.CreateOrUpdateThingsWithNotification(It.IsAny<Thing>(), It.IsAny<IReadOnlyCollection<Thing>>()), Times.Exactly(2));
});

this.sessionService.Setup(x => x.CreateOrUpdateThings(It.IsAny<Thing>(), It.IsAny<IReadOnlyCollection<Thing>>())).Throws(new Exception());
this.sessionService.Setup(x => x.CreateOrUpdateThingsWithNotification(It.IsAny<Thing>(), It.IsAny<IReadOnlyCollection<Thing>>())).Throws(new Exception());
await this.viewModel.CreateCategory(false);

Assert.Multiple(() =>
{
this.sessionService.Verify(x => x.CreateOrUpdateThings(It.IsAny<Thing>(), It.IsAny<IReadOnlyCollection<Thing>>()), Times.Exactly(3));
this.sessionService.Verify(x => x.CreateOrUpdateThingsWithNotification(It.IsAny<Thing>(), It.IsAny<IReadOnlyCollection<Thing>>()), Times.Exactly(3));
this.logger.Verify(LogLevel.Error, x => !string.IsNullOrWhiteSpace(x.ToString()), Times.Once());
});

Expand Down Expand Up @@ -422,7 +422,7 @@ public async Task VerifyGridActions()
var editForm = renderer.FindComponent<EditForm>();
await renderer.InvokeAsync(editForm.Instance.OnValidSubmit.InvokeAsync);

this.sessionService.Verify(x => x.CreateOrUpdateThings(It.IsAny<Thing>(), It.IsAny<IReadOnlyCollection<Thing>>()), Times.Once);
this.sessionService.Verify(x => x.CreateOrUpdateThingsWithNotification(It.IsAny<Thing>(), It.IsAny<IReadOnlyCollection<Thing>>()), Times.Once);
}
}
}
Loading

0 comments on commit 43f8a13

Please sign in to comment.