diff --git a/COMET.Web.Common.Tests/Services/SessionManagement/SessionServiceTestFixture.cs b/COMET.Web.Common.Tests/Services/SessionManagement/SessionServiceTestFixture.cs index d2e84bce..63864c32 100644 --- a/COMET.Web.Common.Tests/Services/SessionManagement/SessionServiceTestFixture.cs +++ b/COMET.Web.Common.Tests/Services/SessionManagement/SessionServiceTestFixture.cs @@ -33,6 +33,8 @@ namespace COMET.Web.Common.Tests.Services.SessionManagement using CDP4Dal; using CDP4Dal.DAL; + using CDP4Dal.Exceptions; + using CDP4Dal.Operations; using COMET.Web.Common.Enumerations; using COMET.Web.Common.Services.SessionManagement; @@ -335,10 +337,23 @@ public void VerifyUpdateThings() Owner = this.sessionService.GetDomainOfExpertise(this.iteration) }; + this.iteration.Element.Add(element); + var clone = element.Clone(false); clone.Name = "Satellite"; thingsToUpdate.Add(clone); - Assert.DoesNotThrow(() => this.sessionService.UpdateThings(this.iteration, thingsToUpdate)); + Assert.That(async () => await this.sessionService.UpdateThings(this.iteration, thingsToUpdate), Throws.Nothing); + + var filesToUpload = new List { this.uri.LocalPath }; + + Assert.Multiple(() => + { + Assert.That(async () => await this.sessionService.UpdateThings(this.iteration, thingsToUpdate, filesToUpload), Throws.Nothing); + this.session.Verify(x => x.Write(It.IsAny(), It.IsAny>()), Times.Once); + }); + + this.session.Setup(x => x.Write(It.IsAny(), It.IsAny>())).Throws(new DalWriteException()); + Assert.That(async () => await this.sessionService.UpdateThings(this.iteration, thingsToUpdate, filesToUpload), Throws.Nothing); } [Test] diff --git a/COMET.Web.Common/Services/SessionManagement/ISessionService.cs b/COMET.Web.Common/Services/SessionManagement/ISessionService.cs index c17ebbcc..8c398dc6 100644 --- a/COMET.Web.Common/Services/SessionManagement/ISessionService.cs +++ b/COMET.Web.Common/Services/SessionManagement/ISessionService.cs @@ -170,6 +170,15 @@ public interface ISessionService /// An asynchronous operation with a Task UpdateThings(Thing container, IEnumerable thingsToUpdate); + /// + /// Write updated Things in an and uploads the given files to the filestore + /// + /// The where the s should be updated + /// List of Things to update in the session + /// >A collection of file paths for files to be send to the file store + /// An asynchronous operation with a + Task UpdateThings(Thing container, IEnumerable thingsToUpdate, IEnumerable files); + /// /// Deletes a from it's container /// diff --git a/COMET.Web.Common/Services/SessionManagement/SessionService.cs b/COMET.Web.Common/Services/SessionManagement/SessionService.cs index 92da3c6c..21981873 100644 --- a/COMET.Web.Common/Services/SessionManagement/SessionService.cs +++ b/COMET.Web.Common/Services/SessionManagement/SessionService.cs @@ -396,6 +396,63 @@ public async Task UpdateThings(Thing container, IEnumerable thing return result; } + /// + /// Write updated Things in an and uploads the given files to the filestore + /// + /// The where the s should be updated + /// List of Things to update in the session + /// >A collection of file paths for files to be send to the file store + /// An asynchronous operation with a + public async Task UpdateThings(Thing container, IEnumerable thingsToUpdate, IEnumerable files) + { + var result = new Result(); + + if (thingsToUpdate == null) + { + result.Errors.Add(new Error("The things to update can't be null")); + return result; + } + + var sw = Stopwatch.StartNew(); + + // CreateThings a shallow clone of the thing. The cached Thing object should not be changed, so we record the change on a clone. + var thingClone = container; + + if (container.Original == null) + { + thingClone = container.Clone(false); + } + + // set the context of the transaction to the thing changes need to be added to. + var context = TransactionContextResolver.ResolveContext(thingClone); + var transaction = new ThingTransaction(context); + + // register all updates with the transaction. + thingsToUpdate.ToList().ForEach(transaction.CreateOrUpdate); + + // finalize the transaction, the result is an OperationContainer that the session class uses to write the changes to the Thing object. + var operationContainer = transaction.FinalizeTransaction(); + result = new Result(); + + try + { + await this.Session.Write(operationContainer, files); + this.logger.LogInformation("Update writing done in {swElapsedMilliseconds} [ms]", sw.ElapsedMilliseconds); + result.Successes.Add(new Success($"Update writing done in {sw.ElapsedMilliseconds} [ms]")); + } + catch (DalWriteException ex) + { + this.logger.LogError("The update operation failed: {exMessage}", ex.Message); + result.Errors.Add(new Error($"The update operation failed: {ex.Message}")); + } + finally + { + sw.Stop(); + } + + return result; + } + /// /// Deletes a from it's container /// diff --git a/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileFormTestFixture.cs b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileFormTestFixture.cs new file mode 100644 index 00000000..b2bf9271 --- /dev/null +++ b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileFormTestFixture.cs @@ -0,0 +1,100 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Components.EngineeringModel.FileStore +{ + using System.Linq; + + using Bunit; + + using CDP4Common.EngineeringModelData; + + using COMET.Web.Common.Test.Helpers; + + using COMETwebapp.Components.EngineeringModel.FileStore; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler; + + using DevExpress.Blazor; + + using Microsoft.AspNetCore.Components.Forms; + + using Moq; + + using NUnit.Framework; + + using TestContext = Bunit.TestContext; + + [TestFixture] + public class FileFormTestFixture + { + private TestContext context; + private IRenderedComponent renderer; + private Mock viewModel; + + [SetUp] + public void SetUp() + { + this.context = new TestContext(); + this.viewModel = new Mock(); + + this.viewModel.Setup(x => x.File).Returns(new File()); + this.context.ConfigureDevExpressBlazor(); + + this.renderer = this.context.RenderComponent(parameters => + { + parameters.Add(p => p.ViewModel, this.viewModel.Object); + }); + } + + [TearDown] + public void Teardown() + { + this.context.CleanContext(); + this.context.Dispose(); + } + + [Test] + public async Task VerifyOnValidSubmitAndDelete() + { + Assert.That(this.renderer.Instance.IsDeletePopupVisible, Is.EqualTo(false)); + + var form = this.renderer.FindComponent(); + await this.renderer.InvokeAsync(form.Instance.OnValidSubmit.InvokeAsync); + this.viewModel.Verify(x => x.CreateOrEditFile(It.IsAny()), Times.Once); + + var deleteFileButton = this.renderer.FindComponents().First(x => x.Instance.Id == "deleteFileButton"); + await this.renderer.InvokeAsync(deleteFileButton.Instance.Click.InvokeAsync); + Assert.That(this.renderer.Instance.IsDeletePopupVisible, Is.EqualTo(true)); + + var deleteFilePopupButton = this.renderer.FindComponents().First(x => x.Instance.Id == "deleteFilePopupButton"); + await this.renderer.InvokeAsync(deleteFilePopupButton.Instance.Click.InvokeAsync); + + Assert.Multiple(() => + { + Assert.That(this.renderer.Instance.IsDeletePopupVisible, Is.EqualTo(false)); + this.viewModel.Verify(x => x.DeleteFile(), Times.Once); + }); + } + } +} diff --git a/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileRevisionsTableTestFixture.cs b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileRevisionsTableTestFixture.cs new file mode 100644 index 00000000..9be2ef36 --- /dev/null +++ b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileRevisionsTableTestFixture.cs @@ -0,0 +1,124 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Components.EngineeringModel.FileStore +{ + using System.Linq; + + using Bunit; + + using CDP4Common.EngineeringModelData; + + using COMET.Web.Common.Test.Helpers; + + using COMETwebapp.Components.EngineeringModel.FileStore; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler; + + using DevExpress.Blazor; + + using Microsoft.AspNetCore.Components.Forms; + + using Moq; + + using NUnit.Framework; + + using TestContext = Bunit.TestContext; + + [TestFixture] + public class FileRevisionsTableTestFixture + { + private TestContext context; + private IRenderedComponent renderer; + private Mock viewModel; + private List fileRevisions; + + [SetUp] + public void SetUp() + { + this.context = new TestContext(); + this.viewModel = new Mock(); + this.fileRevisions = [new FileRevision()]; + + this.viewModel.Setup(x => x.FileRevision).Returns(new FileRevision()); + this.viewModel.Setup(x => x.CurrentFile).Returns(new File()); + this.context.ConfigureDevExpressBlazor(); + + this.renderer = this.context.RenderComponent(parameters => + { + parameters.Add(p => p.ViewModel, this.viewModel.Object); + parameters.Add(p => p.FileRevisions, this.fileRevisions); + }); + } + + [TearDown] + public void Teardown() + { + this.context.CleanContext(); + this.context.Dispose(); + } + + [Test] + public async Task VerifyRowActions() + { + var timesFileRevisionsWasChanged = 0; + + this.renderer.SetParametersAndRender(parameters => + { + parameters.Add(p => p.FileRevisionsChanged, () => { timesFileRevisionsWasChanged += 1; }); + }); + + var downloadButton = this.renderer.FindComponents().First(x => x.Instance.Id == "downloadFileRevisionButton"); + await this.renderer.InvokeAsync(downloadButton.Instance.Click.InvokeAsync); + this.viewModel.Verify(x => x.DownloadFileRevision(It.IsAny()), Times.Once); + + var removeFileRevisionButton = this.renderer.FindComponents().First(x => x.Instance.Id == "removeFileRevisionButton"); + await this.renderer.InvokeAsync(removeFileRevisionButton.Instance.Click.InvokeAsync); + Assert.That(timesFileRevisionsWasChanged, Is.EqualTo(1)); + } + + [Test] + public async Task VerifyFileRevisionCreation() + { + var timesFileRevisionsWasChanged = 0; + + this.renderer.SetParametersAndRender(parameters => + { + parameters.Add(p => p.FileRevisionsChanged, () => { timesFileRevisionsWasChanged += 1; }); + }); + + var addFileRevisionButton = this.renderer.FindComponents().First(x => x.Instance.Id == "addFileRevisionButton"); + await this.renderer.InvokeAsync(addFileRevisionButton.Instance.Click.InvokeAsync); + + var fileInput = this.renderer.FindComponent(); + var fileMock = new Mock(); + var changeArgs = new InputFileChangeEventArgs([fileMock.Object]); + await this.renderer.InvokeAsync(() => fileInput.Instance.OnChange.InvokeAsync(changeArgs)); + this.viewModel.Verify(x => x.UploadFile(fileMock.Object), Times.Once); + + var grid = this.renderer.FindComponent(); + await this.renderer.InvokeAsync(grid.Instance.EditModelSaving.InvokeAsync); + Assert.That(timesFileRevisionsWasChanged, Is.EqualTo(1)); + } + } +} diff --git a/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileTypesTableTestFixture.cs b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileTypesTableTestFixture.cs new file mode 100644 index 00000000..afb026c8 --- /dev/null +++ b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FileTypesTableTestFixture.cs @@ -0,0 +1,160 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Components.EngineeringModel.FileStore +{ + using System.Linq; + + using Bunit; + + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using COMET.Web.Common.Test.Helpers; + + using COMETwebapp.Components.EngineeringModel.FileStore; + + using DevExpress.Blazor; + + using NUnit.Framework; + + using TestContext = Bunit.TestContext; + + [TestFixture] + public class FileTypesTableTestFixture + { + private TestContext context; + private IRenderedComponent renderer; + private FileRevision fileRevision; + + [SetUp] + public void SetUp() + { + this.context = new TestContext(); + this.fileRevision = new FileRevision(); + + var pdfFileType = new FileType() + { + Name = "application/pdf", + ShortName = "application/pdf", + Extension = "pdf" + }; + + var jsonFileType = new FileType() + { + Name = "application/json", + ShortName = "application/json", + Extension = "json" + }; + + var textFileType = new FileType() + { + Name = "application/txt", + ShortName = "application/txt", + Extension = "txt" + }; + + var siteRdl = new SiteReferenceDataLibrary() + { + FileType = { pdfFileType, jsonFileType, textFileType } + }; + + this.fileRevision.FileType.AddRange([pdfFileType, jsonFileType]); + this.context.ConfigureDevExpressBlazor(); + + this.renderer = this.context.RenderComponent(parameters => + { + parameters.Add(p => p.FileTypes, siteRdl.FileType); + parameters.Add(p => p.SelectedFileTypes, this.fileRevision.FileType); + }); + } + + [TearDown] + public void Teardown() + { + this.context.CleanContext(); + this.context.Dispose(); + } + + [Test] + public async Task VerifyRowActions() + { + var timesSelectedFileTypesWasChanged = 0; + + this.renderer.SetParametersAndRender(parameters => + { + parameters.Add(p => p.SelectedFileTypesChanged, () => { timesSelectedFileTypesWasChanged += 1; }); + }); + + Assert.Multiple(() => + { + Assert.That(this.fileRevision.FileType[0].Extension, Is.EqualTo("pdf")); + Assert.That(this.fileRevision.FileType[1].Extension, Is.EqualTo("json")); + Assert.That(timesSelectedFileTypesWasChanged, Is.EqualTo(0)); + }); + + var moveUpButton = this.renderer.FindComponents().First(x => x.Instance.Id == "moveUpButton" && x.Instance.Enabled); + await this.renderer.InvokeAsync(moveUpButton.Instance.Click.InvokeAsync); + + Assert.Multiple(() => + { + Assert.That(this.fileRevision.FileType[0].Extension, Is.EqualTo("json")); + Assert.That(this.fileRevision.FileType[1].Extension, Is.EqualTo("pdf")); + Assert.That(timesSelectedFileTypesWasChanged, Is.EqualTo(1)); + }); + + var moveDownButton = this.renderer.FindComponents().First(x => x.Instance.Id == "moveDownButton" && x.Instance.Enabled); + await this.renderer.InvokeAsync(moveDownButton.Instance.Click.InvokeAsync); + + Assert.Multiple(() => + { + Assert.That(this.fileRevision.FileType[0].Extension, Is.EqualTo("pdf")); + Assert.That(this.fileRevision.FileType[1].Extension, Is.EqualTo("json")); + Assert.That(timesSelectedFileTypesWasChanged, Is.EqualTo(2)); + }); + + var removeFileTypeButton = this.renderer.FindComponents().First(x => x.Instance.Id == "removeFileTypeButton"); + await this.renderer.InvokeAsync(removeFileTypeButton.Instance.Click.InvokeAsync); + Assert.That(timesSelectedFileTypesWasChanged, Is.EqualTo(3)); + } + + [Test] + public async Task VerifyFileTypeCreation() + { + var timesSelectedFileTypesWasChanged = 0; + + this.renderer.SetParametersAndRender(parameters => + { + parameters.Add(p => p.SelectedFileTypesChanged, () => { timesSelectedFileTypesWasChanged += 1; }); + }); + + var addFileTypeButton = this.renderer.FindComponents().First(x => x.Instance.Id == "addFileTypeButton"); + await this.renderer.InvokeAsync(addFileTypeButton.Instance.Click.InvokeAsync); + + var grid = this.renderer.FindComponent(); + await this.renderer.InvokeAsync(grid.Instance.EditModelSaving.InvokeAsync); + Assert.That(timesSelectedFileTypesWasChanged, Is.EqualTo(1)); + } + } +} diff --git a/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FolderFileStructureTestFixture.cs b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FolderFileStructureTestFixture.cs new file mode 100644 index 00000000..20901304 --- /dev/null +++ b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FolderFileStructureTestFixture.cs @@ -0,0 +1,208 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Components.EngineeringModel.FileStore +{ + using System.Linq; + + using Bunit; + + using CDP4Common.EngineeringModelData; + + using COMET.Web.Common.Test.Helpers; + + using COMETwebapp.Components.EngineeringModel.FileStore; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler; + + using DevExpress.Blazor; + + using Microsoft.AspNetCore.Components.Web; + + using Moq; + + using NUnit.Framework; + + using TestContext = Bunit.TestContext; + + [TestFixture] + public class FolderFileStructureTestFixture + { + private TestContext context; + private IRenderedComponent renderer; + private Mock viewModel; + private Mock fileHandlerViewModel; + private Mock folderHandlerViewModel; + private List structure; + + [SetUp] + public void SetUp() + { + this.context = new TestContext(); + this.viewModel = new Mock(); + this.folderHandlerViewModel = new Mock(); + this.fileHandlerViewModel = new Mock(); + + var file = new File(); + + file.FileRevision.Add(new FileRevision() + { + Name = "File Revision 1" + }); + + this.structure = + [ + new FileFolderNodeViewModel() + { + Content = + { + new FileFolderNodeViewModel(file), + new FileFolderNodeViewModel(new Folder(), [new FileFolderNodeViewModel(file)]) + } + } + ]; + + this.fileHandlerViewModel.Setup(x => x.File).Returns(new File()); + this.folderHandlerViewModel.Setup(x => x.Folder).Returns(new Folder()); + + this.viewModel.Setup(x => x.Structure).Returns(this.structure); + this.viewModel.Setup(x => x.FileHandlerViewModel).Returns(this.fileHandlerViewModel.Object); + this.viewModel.Setup(x => x.FolderHandlerViewModel).Returns(this.folderHandlerViewModel.Object); + + this.context.ConfigureDevExpressBlazor(); + + this.renderer = this.context.RenderComponent(parameters => + { + parameters.Add(p => p.ViewModel, this.viewModel.Object); + }); + } + + [TearDown] + public void Teardown() + { + this.context.CleanContext(); + this.context.Dispose(); + } + + [Test] + public void VerifyOnInitialized() + { + Assert.Multiple(() => + { + Assert.That(this.renderer.Instance.ViewModel, Is.EqualTo(this.viewModel.Object)); + Assert.That(this.renderer.Markup, Does.Contain(this.structure.First().Name)); + }); + } + + [Test] + public async Task VerifyEditFile() + { + var treeNodeEventMock = new Mock(); + var rootNode = this.structure[0]; + + treeNodeEventMock.Setup(x => x.DataItem).Returns(rootNode.Content.First(x => x.Thing is not File)); + this.renderer.Instance.OnNodeClick(treeNodeEventMock.Object); + Assert.That(this.renderer.Instance.IsFileFormVisibile, Is.EqualTo(false)); + + treeNodeEventMock.Setup(x => x.DataItem).Returns(rootNode.Content.First(x => x.Thing is File)); + this.renderer.Instance.OnNodeClick(treeNodeEventMock.Object); + this.renderer.Render(); + + var form = this.renderer.FindComponent(); + + Assert.Multiple(() => + { + Assert.That(form, Is.Not.Null); + Assert.That(form.Instance.ShouldCreate, Is.EqualTo(false)); + Assert.That(this.renderer.Instance.IsFileFormVisibile, Is.EqualTo(true)); + Assert.That(this.renderer.Instance.IsFolderFormVisibile, Is.EqualTo(false)); + }); + + var popup = this.renderer.FindComponent(); + await this.renderer.InvokeAsync(popup.Instance.Closed.InvokeAsync); + + Assert.Multiple(() => + { + Assert.That(this.renderer.Instance.IsFileFormVisibile, Is.EqualTo(false)); + Assert.That(this.renderer.Instance.IsFolderFormVisibile, Is.EqualTo(false)); + }); + } + + [Test] + public void VerifyEditFolder() + { + var rootNode = this.structure[0]; + var notFolderRow = rootNode.Content.First(x => x.Thing is not Folder); + this.renderer.Instance.OnEditFolderClick(notFolderRow); + Assert.That(this.renderer.Instance.IsFolderFormVisibile, Is.EqualTo(false)); + + var folderRow = rootNode.Content.First(x => x.Thing is Folder); + this.renderer.Instance.OnEditFolderClick(folderRow); + + this.renderer.Render(); + + var form = this.renderer.FindComponent(); + + Assert.Multiple(() => + { + Assert.That(form, Is.Not.Null); + Assert.That(form.Instance.ShouldCreate, Is.EqualTo(false)); + Assert.That(this.renderer.Instance.IsFileFormVisibile, Is.EqualTo(false)); + Assert.That(this.renderer.Instance.IsFolderFormVisibile, Is.EqualTo(true)); + }); + } + + [Test] + public async Task VerifyDragAndDrop() + { + var rootNode = this.structure.First(); + + // Test drag a file into a folder + var draggedFileNode = rootNode.Content[0]; + var draggableFile = this.renderer.FindAll("div").Where(x => x.Attributes["draggable"]?.Value == "true").ElementAt(1); + await this.renderer.InvokeAsync(() => draggableFile.DragStartAsync(new DragEventArgs())); + Assert.That(this.renderer.Instance.DraggedNode, Is.EqualTo(draggedFileNode)); + + var droppableDiv = this.renderer.FindAll("div").Where(x => x.Attributes["draggable"]?.Value == "true").ElementAt(0); + await this.renderer.InvokeAsync(() => droppableDiv.DropAsync(new DragEventArgs())); + this.fileHandlerViewModel.Verify(x => x.MoveFile((File)draggedFileNode.Thing, (Folder)rootNode.Thing)); + + // Test drag a folder into a folder + var draggedFolderNode = rootNode.Content[1]; + var draggableFolder = this.renderer.FindAll("div").Where(x => x.Attributes["draggable"]?.Value == "true").ElementAt(2); + await this.renderer.InvokeAsync(() => draggableFolder.DragStartAsync(new DragEventArgs())); + Assert.That(this.renderer.Instance.DraggedNode, Is.EqualTo(draggedFolderNode)); + + droppableDiv = this.renderer.FindAll("div").Where(x => x.Attributes["draggable"]?.Value == "true").ElementAt(0); + await this.renderer.InvokeAsync(() => droppableDiv.DropAsync(new DragEventArgs())); + this.folderHandlerViewModel.Verify(x => x.MoveFolder((Folder)draggedFolderNode.Thing, (Folder)rootNode.Thing)); + + // Test drop something in a file - invalid + var invalidDroppableDiv = this.renderer.FindAll("div").Where(x => x.Attributes["draggable"]?.Value == "true").ElementAt(1); + await this.renderer.InvokeAsync(() => invalidDroppableDiv.DropAsync(new DragEventArgs())); + this.fileHandlerViewModel.Verify(x => x.MoveFile((File)draggedFileNode.Thing, (Folder)rootNode.Thing)); + } + } +} diff --git a/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FolderFormTestFixture.cs b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FolderFormTestFixture.cs new file mode 100644 index 00000000..fc0a6eb9 --- /dev/null +++ b/COMETwebapp.Tests/Components/EngineeringModel/FileStore/FolderFormTestFixture.cs @@ -0,0 +1,100 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Components.EngineeringModel.FileStore +{ + using System.Linq; + + using Bunit; + + using CDP4Common.EngineeringModelData; + + using COMET.Web.Common.Test.Helpers; + + using COMETwebapp.Components.EngineeringModel.FileStore; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler; + + using DevExpress.Blazor; + + using Microsoft.AspNetCore.Components.Forms; + + using Moq; + + using NUnit.Framework; + + using TestContext = Bunit.TestContext; + + [TestFixture] + public class FolderFormTestFixture + { + private TestContext context; + private IRenderedComponent renderer; + private Mock viewModel; + + [SetUp] + public void SetUp() + { + this.context = new TestContext(); + this.viewModel = new Mock(); + + this.viewModel.Setup(x => x.Folder).Returns(new Folder()); + this.context.ConfigureDevExpressBlazor(); + + this.renderer = this.context.RenderComponent(parameters => + { + parameters.Add(p => p.ViewModel, this.viewModel.Object); + }); + } + + [TearDown] + public void Teardown() + { + this.context.CleanContext(); + this.context.Dispose(); + } + + [Test] + public async Task VerifyOnValidSubmitAndDelete() + { + Assert.That(this.renderer.Instance.IsDeletePopupVisible, Is.EqualTo(false)); + + var form = this.renderer.FindComponent(); + await this.renderer.InvokeAsync(form.Instance.OnValidSubmit.InvokeAsync); + this.viewModel.Verify(x => x.CreateOrEditFolder(It.IsAny()), Times.Once); + + var deleteFolderButton = this.renderer.FindComponents().First(x => x.Instance.Id == "deleteFolderButton"); + await this.renderer.InvokeAsync(deleteFolderButton.Instance.Click.InvokeAsync); + Assert.That(this.renderer.Instance.IsDeletePopupVisible, Is.EqualTo(true)); + + var deleteFolderPopupButton = this.renderer.FindComponents().First(x => x.Instance.Id == "deleteFolderPopupButton"); + await this.renderer.InvokeAsync(deleteFolderPopupButton.Instance.Click.InvokeAsync); + + Assert.Multiple(() => + { + Assert.That(this.renderer.Instance.IsDeletePopupVisible, Is.EqualTo(false)); + this.viewModel.Verify(x => x.DeleteFolder(), Times.Once); + }); + } + } +} diff --git a/COMETwebapp.Tests/Components/EngineeringModel/FolderFileStructureTestFixture.cs b/COMETwebapp.Tests/Components/EngineeringModel/FolderFileStructureTestFixture.cs deleted file mode 100644 index 1bc8645f..00000000 --- a/COMETwebapp.Tests/Components/EngineeringModel/FolderFileStructureTestFixture.cs +++ /dev/null @@ -1,167 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2023-2024 RHEA System S.A. -// -// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua -// -// This file is part of CDP4-COMET WEB Community Edition -// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. -// -// The CDP4-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. -// -// The CDP4-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 . -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace COMETwebapp.Tests.Components.EngineeringModel -{ - using System.Linq; - using System.Threading.Tasks; - - using Bunit; - - using CDP4Common.EngineeringModelData; - - using COMET.Web.Common.Test.Helpers; - - using COMETwebapp.Components.EngineeringModel; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; - - using DevExpress.Blazor; - - using Microsoft.AspNetCore.Components.Forms; - - using Moq; - - using NUnit.Framework; - - using TestContext = Bunit.TestContext; - - [TestFixture] - public class FolderFileStructureTestFixture - { - private TestContext context; - private IRenderedComponent renderer; - private Mock viewModel; - private List structure; - - [SetUp] - public void SetUp() - { - this.context = new TestContext(); - this.viewModel = new Mock(); - - var file = new File(); - - file.FileRevision.Add(new FileRevision() - { - Name = "File Revision 1" - }); - - this.structure = - [ - new FileFolderNodeViewModel(file), - new FileFolderNodeViewModel(new Folder(), [new FileFolderNodeViewModel(file)]), - ]; - - this.viewModel.Setup(x => x.File).Returns(new File()); - this.viewModel.Setup(x => x.Folder).Returns(new Folder()); - this.viewModel.Setup(x => x.Structure).Returns(this.structure); - - this.context.ConfigureDevExpressBlazor(); - - this.renderer = this.context.RenderComponent(parameters => - { - parameters.Add(p => p.ViewModel, this.viewModel.Object); - }); - } - - [TearDown] - public void Teardown() - { - this.context.CleanContext(); - this.context.Dispose(); - } - - [Test] - public void VerifyOnInitialized() - { - Assert.Multiple(() => - { - Assert.That(this.renderer.Instance.ViewModel, Is.EqualTo(this.viewModel.Object)); - Assert.That(this.renderer.Markup, Does.Contain(this.structure.First().Name)); - }); - } - - [Test] - public async Task VerifyEditFile() - { - var treeNodeEventMock = new Mock(); - treeNodeEventMock.Setup(x => x.DataItem).Returns(this.structure.First(x => x.Thing is not File)); - this.renderer.Instance.OnNodeClick(treeNodeEventMock.Object); - Assert.That(this.renderer.Instance.SelectedFile, Is.Null); - - treeNodeEventMock.Setup(x => x.DataItem).Returns(this.structure.First(x => x.Thing is File)); - this.renderer.Instance.OnNodeClick(treeNodeEventMock.Object); - this.renderer.Render(); - - var form = this.renderer.FindComponent(); - - Assert.Multiple(() => - { - Assert.That(form, Is.Not.Null); - Assert.That(form.Instance.ShouldCreate, Is.EqualTo(false)); - Assert.That(this.renderer.Instance.SelectedFile, Is.Not.Null); - Assert.That(this.renderer.Instance.SelectedFolder, Is.Null); - }); - - // Test behavior in case the submmit is valid => next step - var editForm = form.FindComponent(); - await form.InvokeAsync(editForm.Instance.OnValidSubmit.InvokeAsync); - - var cancelButton = editForm.FindComponents().First(x => x.Instance.Id == "cancelFileButton"); - await editForm.InvokeAsync(cancelButton.Instance.Click.InvokeAsync); - Assert.That(this.renderer.Instance.SelectedFile, Is.Null); - } - - [Test] - public async Task VerifyEditFolder() - { - var notFolderRow = this.structure.First(x => x.Thing is not Folder); - this.renderer.Instance.OnEditFolderClick(notFolderRow); - Assert.That(this.renderer.Instance.SelectedFolder, Is.Null); - - var folderRow = this.structure.First(x => x.Thing is Folder); - this.renderer.Instance.OnEditFolderClick(folderRow); - - this.renderer.Render(); - - var form = this.renderer.FindComponent(); - - Assert.Multiple(() => - { - Assert.That(form, Is.Not.Null); - Assert.That(form.Instance.ShouldCreate, Is.EqualTo(false)); - Assert.That(this.renderer.Instance.SelectedFile, Is.Null); - Assert.That(this.renderer.Instance.SelectedFolder, Is.Not.Null); - }); - - // Test behavior in case the submmit is valid => next step - var editForm = form.FindComponent(); - await form.InvokeAsync(editForm.Instance.OnValidSubmit.InvokeAsync); - - var cancelButton = editForm.FindComponents().First(x => x.Instance.Id == "cancelFolderButton"); - await editForm.InvokeAsync(cancelButton.Instance.Click.InvokeAsync); - Assert.That(this.renderer.Instance.SelectedFolder, Is.Null); - } - } -} diff --git a/COMETwebapp.Tests/Services/Interoperability/JsUtilitiesServiceTestFixture.cs b/COMETwebapp.Tests/Services/Interoperability/JsUtilitiesServiceTestFixture.cs new file mode 100644 index 00000000..e453c299 --- /dev/null +++ b/COMETwebapp.Tests/Services/Interoperability/JsUtilitiesServiceTestFixture.cs @@ -0,0 +1,61 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Services.Interoperability +{ + using COMETwebapp.Services.Interoperability; + + using Microsoft.JSInterop; + + using Moq; + + using NUnit.Framework; + + [TestFixture] + public class JsUtilitiesServiceTestFixture + { + private Mock jsRuntimeMock; + private JsUtilitiesService service; + + [SetUp] + public void SetUp() + { + this.jsRuntimeMock = new Mock(); + this.service = new JsUtilitiesService(this.jsRuntimeMock.Object); + } + + [Test] + public async Task VerifyFileDownloadUtility() + { + Assert.Multiple(() => + { + Assert.That(async () => await this.service.DownloadFileFromStreamAsync(null, null), Throws.ArgumentNullException); + Assert.That(async () => await this.service.DownloadFileFromStreamAsync(new MemoryStream(), null), Throws.ArgumentNullException); + }); + + await this.service.DownloadFileFromStreamAsync(new MemoryStream(), "fileTest"); + Assert.That(this.jsRuntimeMock.Invocations.Count, Is.EqualTo(1)); + } + } +} diff --git a/COMETwebapp.Tests/Validators/EngineeringModel/CommonFileStoreValidatorTestFixture.cs b/COMETwebapp.Tests/Validators/EngineeringModel/CommonFileStoreValidatorTestFixture.cs new file mode 100644 index 00000000..59a2793f --- /dev/null +++ b/COMETwebapp.Tests/Validators/EngineeringModel/CommonFileStoreValidatorTestFixture.cs @@ -0,0 +1,62 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Validators.EngineeringModel +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + using CDP4Common.Validation; + + using COMETwebapp.Validators.EngineeringModel; + + using NUnit.Framework; + + [TestFixture] + public class CommonFileStoreValidatorTestFixture + { + private CommonFileStoreValidator validator; + + [SetUp] + public void SetUp() + { + var validationService = new ValidationService(); + this.validator = new CommonFileStoreValidator(validationService); + } + + [Test] + public void VerifyValidationScenarios() + { + var commonFileStore = new CommonFileStore(); + Assert.That(this.validator.Validate(commonFileStore).IsValid, Is.EqualTo(false)); + + commonFileStore = new CommonFileStore() + { + Name = "name1", + Owner = new DomainOfExpertise() + }; + + Assert.That(this.validator.Validate(commonFileStore).IsValid, Is.EqualTo(true)); + } + } +} diff --git a/COMETwebapp.Tests/Validators/EngineeringModel/FileRevisionValidatorTestFixture.cs b/COMETwebapp.Tests/Validators/EngineeringModel/FileRevisionValidatorTestFixture.cs new file mode 100644 index 00000000..1b9ac743 --- /dev/null +++ b/COMETwebapp.Tests/Validators/EngineeringModel/FileRevisionValidatorTestFixture.cs @@ -0,0 +1,64 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Validators.EngineeringModel +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + using CDP4Common.Validation; + + using COMETwebapp.Validators.EngineeringModel; + + using NUnit.Framework; + + [TestFixture] + public class FileRevisionValidatorTestFixture + { + private FileRevisionValidator validator; + + [SetUp] + public void SetUp() + { + var validationService = new ValidationService(); + this.validator = new FileRevisionValidator(validationService); + } + + [Test] + public void VerifyValidationScenarios() + { + var fileRevision = new FileRevision(); + Assert.That(this.validator.Validate(fileRevision).IsValid, Is.EqualTo(false)); + + fileRevision = new FileRevision() + { + Name = "name1", + LocalPath = "/path" + }; + + Assert.That(this.validator.Validate(fileRevision).IsValid, Is.EqualTo(false)); + fileRevision.FileType.Add(new FileType()); + Assert.That(this.validator.Validate(fileRevision).IsValid, Is.EqualTo(true)); + } + } +} diff --git a/COMETwebapp.Tests/Validators/EngineeringModel/FileTypeValidatorTestFixture.cs b/COMETwebapp.Tests/Validators/EngineeringModel/FileTypeValidatorTestFixture.cs new file mode 100644 index 00000000..4fdba8d5 --- /dev/null +++ b/COMETwebapp.Tests/Validators/EngineeringModel/FileTypeValidatorTestFixture.cs @@ -0,0 +1,62 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Validators.EngineeringModel +{ + using CDP4Common.SiteDirectoryData; + using CDP4Common.Validation; + + using COMETwebapp.Validators.EngineeringModel; + + using NUnit.Framework; + + [TestFixture] + public class FileTypeValidatorTestFixture + { + private FileTypeValidator validator; + + [SetUp] + public void SetUp() + { + var validationService = new ValidationService(); + this.validator = new FileTypeValidator(validationService); + } + + [Test] + public void VerifyValidationScenarios() + { + var fileType = new FileType(); + Assert.That(this.validator.Validate(fileType).IsValid, Is.EqualTo(false)); + + fileType = new FileType() + { + Name = "name1", + }; + + Assert.That(this.validator.Validate(fileType).IsValid, Is.EqualTo(false)); + fileType.Extension = "ext"; + Assert.That(this.validator.Validate(fileType).IsValid, Is.EqualTo(true)); + } + } +} diff --git a/COMETwebapp.Tests/Validators/EngineeringModel/FileValidatorTestFixture.cs b/COMETwebapp.Tests/Validators/EngineeringModel/FileValidatorTestFixture.cs new file mode 100644 index 00000000..9a58910f --- /dev/null +++ b/COMETwebapp.Tests/Validators/EngineeringModel/FileValidatorTestFixture.cs @@ -0,0 +1,62 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Validators.EngineeringModel +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + using CDP4Common.Validation; + + using COMETwebapp.Validators.EngineeringModel; + + using NUnit.Framework; + + [TestFixture] + public class FileValidatorTestFixture + { + private FileValidator validator; + + [SetUp] + public void SetUp() + { + var validationService = new ValidationService(); + this.validator = new FileValidator(validationService); + } + + [Test] + public void VerifyValidationScenarios() + { + var file = new File(); + Assert.That(this.validator.Validate(file).IsValid, Is.EqualTo(false)); + + file = new File() + { + Owner = new DomainOfExpertise(), + LockedBy = null + }; + + Assert.That(this.validator.Validate(file).IsValid, Is.EqualTo(true)); + } + } +} diff --git a/COMETwebapp.Tests/Validators/EngineeringModel/FolderValidatorTestFixture.cs b/COMETwebapp.Tests/Validators/EngineeringModel/FolderValidatorTestFixture.cs new file mode 100644 index 00000000..6311fff5 --- /dev/null +++ b/COMETwebapp.Tests/Validators/EngineeringModel/FolderValidatorTestFixture.cs @@ -0,0 +1,63 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.Validators.EngineeringModel +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + using CDP4Common.Validation; + + using COMETwebapp.Validators.EngineeringModel; + + using NUnit.Framework; + + [TestFixture] + public class FolderValidatorTestFixture + { + private FolderValidator validator; + + [SetUp] + public void SetUp() + { + var validationService = new ValidationService(); + this.validator = new FolderValidator(validationService); + } + + [Test] + public void VerifyValidationScenarios() + { + var folder = new Folder(); + Assert.That(this.validator.Validate(folder).IsValid, Is.EqualTo(false)); + + folder = new Folder() + { + Name = "folder A", + Owner = new DomainOfExpertise(), + ContainingFolder = null + }; + + Assert.That(this.validator.Validate(folder).IsValid, Is.EqualTo(true)); + } + } +} diff --git a/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/CommonFileStoreTableViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/CommonFileStoreTableViewModelTestFixture.cs index 157b5353..78d7333e 100644 --- a/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/CommonFileStoreTableViewModelTestFixture.cs +++ b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/CommonFileStoreTableViewModelTestFixture.cs @@ -35,7 +35,7 @@ namespace COMETwebapp.Tests.ViewModels.Components.EngineeringModel using COMET.Web.Common.Services.SessionManagement; using COMETwebapp.ViewModels.Components.EngineeringModel.CommonFileStore; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore; using Microsoft.Extensions.Logging; diff --git a/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FileHandlerViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FileHandlerViewModelTestFixture.cs new file mode 100644 index 00000000..923f331b --- /dev/null +++ b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FileHandlerViewModelTestFixture.cs @@ -0,0 +1,172 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.ViewModels.Components.EngineeringModel.FileStore +{ + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using CDP4Dal; + + using COMET.Web.Common.Services.SessionManagement; + + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler; + + using Microsoft.Extensions.Logging; + + using Moq; + + using NUnit.Framework; + + [TestFixture] + public class FileHandlerViewModelTestFixture + { + private FileHandlerViewModel viewModel; + private CDPMessageBus messageBus; + private Mock sessionService; + private Mock> logger; + private Mock fileRevisionHandlerViewModel; + private CommonFileStore commonFileStore; + + [SetUp] + public void Setup() + { + this.messageBus = new CDPMessageBus(); + this.sessionService = new Mock(); + this.logger = new Mock>(); + this.fileRevisionHandlerViewModel = new Mock(); + + var person = new Person(); + + var siteDirectory = new SiteDirectory() + { + Domain = + { + new DomainOfExpertise() + { + ShortName = "doe", + Name = "Domain Of Expertise" + } + } + }; + + var engineeringModelSetup = new EngineeringModelSetup() + { + Participant = { new Participant { Person = person } } + }; + + var folder1 = new Folder() + { + Iid = Guid.NewGuid(), + Name = "folder 1" + }; + + var file1 = new File() + { + Iid = Guid.NewGuid(), + CurrentContainingFolder = folder1 + }; + + var file2 = new File() + { + Iid = Guid.NewGuid(), + CurrentContainingFolder = folder1 + }; + + file1.FileRevision.Add(new FileRevision()); + + this.commonFileStore = new CommonFileStore() + { + Name = "CFS", + Folder = { folder1 }, + File = { file1, file2 }, + Owner = siteDirectory.Domain.First(), + Container = new EngineeringModel { EngineeringModelSetup = engineeringModelSetup } + }; + + this.sessionService.Setup(x => x.GetSiteDirectory()).Returns(siteDirectory); + this.sessionService.Setup(x => x.Session.ActivePerson).Returns(person); + this.viewModel = new FileHandlerViewModel(this.sessionService.Object, this.messageBus, this.logger.Object, this.fileRevisionHandlerViewModel.Object); + } + + [TearDown] + public void TearDown() + { + this.viewModel.Dispose(); + this.messageBus.Dispose(); + } + + [Test] + public void VerifyInitializeViewModel() + { + this.viewModel.InitializeViewModel(this.commonFileStore); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.Folders, Has.Count.EqualTo(this.commonFileStore.Folder.Count + 1)); + Assert.That(this.viewModel.Folders, Contains.Item(null)); + Assert.That(this.viewModel.DomainsOfExpertise, Has.Count.EqualTo(1)); + }); + } + + [Test] + public async Task VerifyMoveAndDeleteFile() + { + this.viewModel.InitializeViewModel(this.commonFileStore); + await this.viewModel.MoveFile(this.commonFileStore.File[0], this.commonFileStore.Folder[0]); + this.sessionService.Verify(x => x.UpdateThings(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + + this.viewModel.SelectFile(this.commonFileStore.File[0]); + await this.viewModel.DeleteFile(); + this.sessionService.Verify(x => x.DeleteThing(It.IsAny(), It.IsAny()), Times.Once); + } + + [Test] + public async Task VerifyCreateOrEditFolder() + { + this.viewModel.InitializeViewModel(this.commonFileStore); + this.viewModel.SelectFile(this.commonFileStore.File[0]); + + await this.viewModel.CreateOrEditFile(false); + this.sessionService.Verify(x => x.UpdateThings(It.IsAny(), It.Is>(c => !c.OfType().Any()), It.IsAny>()), Times.Once); + + this.viewModel.SelectedFileRevisions = [new FileRevision(){ LocalPath = "/localpath" }]; + this.viewModel.IsLocked = true; + await this.viewModel.CreateOrEditFile(true); + + Assert.Multiple(() => + { + this.sessionService.Verify(x => x.UpdateThings( + It.IsAny(), + It.Is>(c => c.OfType().Any()), + It.Is>(c => c.Contains("/localpath"))) + , Times.Once); + + Assert.That(this.viewModel.File.LockedBy, Is.Not.Null); + }); + } + } +} diff --git a/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandlerViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandlerViewModelTestFixture.cs new file mode 100644 index 00000000..25a760f8 --- /dev/null +++ b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandlerViewModelTestFixture.cs @@ -0,0 +1,190 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.ViewModels.Components.EngineeringModel.FileStore +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using CDP4Dal; + + using COMET.Web.Common.Services.SessionManagement; + + using COMETwebapp.Services.Interoperability; + using COMETwebapp.Utilities; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler; + + using Microsoft.AspNetCore.Components.Forms; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.Logging; + + using Moq; + + using NUnit.Framework; + + [TestFixture] + public class FileRevisionHandlerViewModelTestFixture + { + private FileRevisionHandlerViewModel viewModel; + private CDPMessageBus messageBus; + private Mock sessionService; + private Mock> logger; + private Mock jsUtilitiesService; + private CommonFileStore commonFileStore; + private File file; + + [SetUp] + public void Setup() + { + this.messageBus = new CDPMessageBus(); + this.sessionService = new Mock(); + this.logger = new Mock>(); + this.jsUtilitiesService = new Mock(); + + var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary + { + [Constants.MaxUploadFileSizeInMbConfigurationKey] = "500" + }) + .Build(); + + var person = new Person(); + + var siteDirectory = new SiteDirectory() + { + Domain = + { + new DomainOfExpertise() + { + ShortName = "doe", + Name = "Domain Of Expertise" + } + }, + }; + + siteDirectory.SiteReferenceDataLibrary.Add(new SiteReferenceDataLibrary() + { + FileType = + { + new FileType() + { + Extension = "txt", + Name = "text/txt", + ShortName = "text/txt" + } + } + }); + + var engineeringModelSetup = new EngineeringModelSetup() + { + Participant = { new Participant { Person = person } } + }; + + var folder1 = new Folder() + { + Iid = Guid.NewGuid(), + Name = "folder 1" + }; + + this.file = new File() + { + Iid = Guid.NewGuid(), + CurrentContainingFolder = folder1 + }; + + var fileRevision = new FileRevision() { Name = "file rev 1", }; + fileRevision.FileType.AddRange(siteDirectory.SiteReferenceDataLibrary.First().FileType); + this.file.FileRevision.Add(fileRevision); + + this.commonFileStore = new CommonFileStore() + { + Name = "CFS", + Folder = { folder1 }, + File = { this.file }, + Owner = siteDirectory.Domain.First(), + Container = new EngineeringModel { EngineeringModelSetup = engineeringModelSetup } + }; + + this.sessionService.Setup(x => x.GetSiteDirectory()).Returns(siteDirectory); + this.sessionService.Setup(x => x.Session.ActivePerson).Returns(person); + this.viewModel = new FileRevisionHandlerViewModel(this.sessionService.Object, this.messageBus, this.logger.Object, this.jsUtilitiesService.Object, configuration); + } + + [TearDown] + public void TearDown() + { + this.viewModel.Dispose(); + this.messageBus.Dispose(); + } + + [Test] + public void VerifyInitializeViewModel() + { + this.viewModel.InitializeViewModel(this.file, this.commonFileStore); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.CurrentFile, Is.EqualTo(this.file)); + Assert.That(this.viewModel.FileRevision, Is.Not.Null); + Assert.That(this.viewModel.ErrorMessage, Is.Empty); + }); + } + + [Test] + public async Task VerifyUploadFile() + { + this.viewModel.InitializeViewModel(this.file, this.commonFileStore); + + Assert.That(this.viewModel.ErrorMessage, Is.Empty); + + var fileMock = new Mock(); + fileMock.Setup(x => x.Size).Returns(1000 * 1024 * 1024); + await this.viewModel.UploadFile(fileMock.Object); + Assert.That(this.viewModel.ErrorMessage, Is.Not.Empty); + + fileMock.Setup(x => x.Size).Returns(1); + fileMock.Setup(x => x.Name).Returns("file.txt"); + fileMock.Setup(x => x.OpenReadStream(It.IsAny(), It.IsAny())).Returns(new MemoryStream()); + await this.viewModel.UploadFile(fileMock.Object); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.ErrorMessage, Is.Empty); + Assert.That(this.viewModel.FileRevision.Name, Is.EqualTo("file")); + Assert.That(this.viewModel.FileRevision.FileType.First().Extension, Is.EqualTo("txt")); + }); + } + + [Test] + public async Task VerifyDownloadFile() + { + this.viewModel.InitializeViewModel(this.file, this.commonFileStore); + await this.viewModel.DownloadFileRevision(this.file.CurrentFileRevision); + this.jsUtilitiesService.Verify(x => x.DownloadFileFromStreamAsync(It.IsAny(), It.IsAny()), Times.Once); + + this.sessionService.Setup(x => x.Session.ReadFile(It.IsAny())).Throws(new Exception()); + await this.viewModel.DownloadFileRevision(this.file.CurrentFileRevision); + this.jsUtilitiesService.Verify(x => x.DownloadFileFromStreamAsync(It.IsAny(), It.IsAny()), Times.Once); + } + } +} diff --git a/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FolderFileStructureViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FolderFileStructureViewModelTestFixture.cs similarity index 53% rename from COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FolderFileStructureViewModelTestFixture.cs rename to COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FolderFileStructureViewModelTestFixture.cs index 19fedb3f..fbf1fe66 100644 --- a/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FolderFileStructureViewModelTestFixture.cs +++ b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FolderFileStructureViewModelTestFixture.cs @@ -22,14 +22,20 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace COMETwebapp.Tests.ViewModels.Components.EngineeringModel +namespace COMETwebapp.Tests.ViewModels.Components.EngineeringModel.FileStore { using CDP4Common.EngineeringModelData; using CDP4Common.SiteDirectoryData; + using CDP4Dal; + using CDP4Dal.Events; + + using COMET.Web.Common.Enumerations; using COMET.Web.Common.Services.SessionManagement; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler; using Moq; @@ -39,13 +45,19 @@ namespace COMETwebapp.Tests.ViewModels.Components.EngineeringModel public class FolderFileStructureViewModelTestFixture { private FolderFileStructureViewModel viewModel; + private CDPMessageBus messageBus; private Mock sessionService; + private Mock fileHandlerViewModel; + private Mock folderHandlerViewModel; private CommonFileStore commonFileStore; [SetUp] public void Setup() { + this.messageBus = new CDPMessageBus(); this.sessionService = new Mock(); + this.fileHandlerViewModel = new Mock(); + this.folderHandlerViewModel = new Mock(); var siteDirectory = new SiteDirectory() { @@ -61,16 +73,16 @@ public void Setup() var folder1 = new Folder() { + Iid = Guid.NewGuid(), Name = "folder 1" }; - var folder2 = new Folder() + var file = new File() { - Name = "folder 2", - ContainingFolder = folder1 + Iid = Guid.NewGuid(), + CurrentContainingFolder = folder1 }; - var file = new File() { CurrentContainingFolder = folder1 }; file.FileRevision.Add(new FileRevision()); this.commonFileStore = new CommonFileStore() @@ -82,7 +94,14 @@ public void Setup() }; this.sessionService.Setup(x => x.GetSiteDirectory()).Returns(siteDirectory); - this.viewModel = new FolderFileStructureViewModel(this.sessionService.Object); + this.viewModel = new FolderFileStructureViewModel(this.sessionService.Object, this.messageBus, this.fileHandlerViewModel.Object, this.folderHandlerViewModel.Object); + } + + [TearDown] + public void TearDown() + { + this.viewModel.Dispose(); + this.messageBus.Dispose(); } [Test] @@ -99,9 +118,51 @@ public void VerifyInitializeViewModel() */ Assert.Multiple(() => { - Assert.That(this.viewModel.DomainsOfExpertise.Count(), Is.GreaterThan(0)); Assert.That(this.viewModel.Structure, Has.Count.EqualTo(1)); Assert.That(this.viewModel.Structure.First().Content, Has.Count.EqualTo(2)); + this.fileHandlerViewModel.Verify(x => x.InitializeViewModel(this.commonFileStore)); + this.folderHandlerViewModel.Verify(x => x.InitializeViewModel(this.commonFileStore)); + }); + } + + [Test] + public void VerifySessionRefresh() + { + this.viewModel.InitializeViewModel(this.commonFileStore); + var rootNodeContent = this.viewModel.Structure.First().Content; + Assert.That(rootNodeContent, Has.Count.EqualTo(2)); + + this.messageBus.SendMessage(SessionStateKind.RefreshEnded); + Assert.That(rootNodeContent, Has.Count.EqualTo(2)); + + var newFile = new File(); + this.messageBus.SendObjectChangeEvent(newFile, EventKind.Added); + this.messageBus.SendMessage(SessionStateKind.RefreshEnded); + + Assert.Multiple(() => + { + Assert.That(rootNodeContent.Select(x => x.Thing), Contains.Item(newFile)); + Assert.That(rootNodeContent, Has.Count.EqualTo(3)); + }); + + var existingFile = this.commonFileStore.File.First(); + existingFile.LockedBy = new Person() { ShortName = "locker" }; + this.messageBus.SendObjectChangeEvent(newFile, EventKind.Updated); + this.messageBus.SendMessage(SessionStateKind.RefreshEnded); + + Assert.Multiple(() => + { + Assert.That(rootNodeContent.Select(x => x.Thing).OfType().Any(x => x.LockedBy.ShortName == "locker"), Is.EqualTo(true)); + Assert.That(rootNodeContent, Has.Count.EqualTo(3)); + }); + + this.messageBus.SendObjectChangeEvent(newFile, EventKind.Removed); + this.messageBus.SendMessage(SessionStateKind.RefreshEnded); + + Assert.Multiple(() => + { + Assert.That(rootNodeContent.Select(x => x.Thing), Does.Not.Contain(newFile)); + Assert.That(rootNodeContent, Has.Count.EqualTo(2)); }); } } diff --git a/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FolderHandlerViewModelTestFixture.cs b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FolderHandlerViewModelTestFixture.cs new file mode 100644 index 00000000..1d565b99 --- /dev/null +++ b/COMETwebapp.Tests/ViewModels/Components/EngineeringModel/FileStore/FolderHandlerViewModelTestFixture.cs @@ -0,0 +1,152 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Tests.ViewModels.Components.EngineeringModel.FileStore +{ + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using CDP4Dal; + + using COMET.Web.Common.Services.SessionManagement; + + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler; + + using Moq; + + using NUnit.Framework; + + [TestFixture] + public class FolderHandlerViewModelTestFixture + { + private FolderHandlerViewModel viewModel; + private CDPMessageBus messageBus; + private Mock sessionService; + private CommonFileStore commonFileStore; + + [SetUp] + public void Setup() + { + this.messageBus = new CDPMessageBus(); + this.sessionService = new Mock(); + + var person = new Person(); + + var siteDirectory = new SiteDirectory() + { + Domain = + { + new DomainOfExpertise() + { + ShortName = "doe", + Name = "Domain Of Expertise" + } + } + }; + + var engineeringModelSetup = new EngineeringModelSetup() + { + Participant = { new Participant { Person = person } } + }; + + var folder1 = new Folder() + { + Iid = Guid.NewGuid(), + Name = "folder 1" + }; + + var folder2 = new Folder() + { + Iid = Guid.NewGuid(), + Name = "folder 2" + }; + + var file = new File() + { + Iid = Guid.NewGuid(), + CurrentContainingFolder = folder1 + }; + + file.FileRevision.Add(new FileRevision()); + + this.commonFileStore = new CommonFileStore() + { + Name = "CFS", + Folder = { folder1, folder2 }, + File = { file }, + Owner = siteDirectory.Domain.First(), + Container = new EngineeringModel { EngineeringModelSetup = engineeringModelSetup } + }; + + this.sessionService.Setup(x => x.GetSiteDirectory()).Returns(siteDirectory); + this.sessionService.Setup(x => x.Session.ActivePerson).Returns(person); + this.viewModel = new FolderHandlerViewModel(this.sessionService.Object, this.messageBus); + } + + [TearDown] + public void TearDown() + { + this.viewModel.Dispose(); + this.messageBus.Dispose(); + } + + [Test] + public void VerifyInitializeViewModel() + { + this.viewModel.InitializeViewModel(this.commonFileStore); + + Assert.Multiple(() => + { + Assert.That(this.viewModel.Folders, Has.Count.EqualTo(this.commonFileStore.Folder.Count + 1)); + Assert.That(this.viewModel.Folders, Contains.Item(null)); + Assert.That(this.viewModel.DomainsOfExpertise, Has.Count.EqualTo(1)); + }); + } + + [Test] + public async Task VerifyMoveAndDeleteFolder() + { + this.viewModel.InitializeViewModel(this.commonFileStore); + await this.viewModel.MoveFolder(this.commonFileStore.Folder[0], this.commonFileStore.Folder[1]); + this.sessionService.Verify(x => x.UpdateThings(It.IsAny(), It.IsAny()), Times.Once); + + this.viewModel.SelectFolder(this.commonFileStore.Folder[0]); + await this.viewModel.DeleteFolder(); + this.sessionService.Verify(x => x.DeleteThing(It.IsAny(), It.IsAny()), Times.Once); + } + + [Test] + public async Task VerifyCreateOrEditFolder() + { + this.viewModel.InitializeViewModel(this.commonFileStore); + this.viewModel.SelectFolder(this.commonFileStore.Folder[0]); + await this.viewModel.CreateOrEditFolder(false); + this.sessionService.Verify(x => x.UpdateThings(It.IsAny(), It.Is>(c => !c.OfType().Any())), Times.Once); + + await this.viewModel.CreateOrEditFolder(true); + this.sessionService.Verify(x => x.UpdateThings(It.IsAny(), It.Is>(c => c.OfType().Any())), Times.Once); + } + } +} diff --git a/COMETwebapp/Components/EngineeringModel/CommonFileStoresTable.razor b/COMETwebapp/Components/EngineeringModel/CommonFileStoresTable.razor index e4d773bc..5bfd0f5a 100644 --- a/COMETwebapp/Components/EngineeringModel/CommonFileStoresTable.razor +++ b/COMETwebapp/Components/EngineeringModel/CommonFileStoresTable.razor @@ -14,6 +14,8 @@ Copyright (c) 2023-2024 RHEA System S.A. 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/. -------------------------------------------------------------------------------> + +@using COMETwebapp.Components.EngineeringModel.FileStore @inherits SelectedDataItemBase diff --git a/COMETwebapp/Components/EngineeringModel/FileForm.razor b/COMETwebapp/Components/EngineeringModel/FileStore/FileForm.razor similarity index 54% rename from COMETwebapp/Components/EngineeringModel/FileForm.razor rename to COMETwebapp/Components/EngineeringModel/FileStore/FileForm.razor index e310b44b..cbb25fb2 100644 --- a/COMETwebapp/Components/EngineeringModel/FileForm.razor +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FileForm.razor @@ -14,9 +14,10 @@ Copyright (c) 2023-2024 RHEA System S.A. 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/. -------------------------------------------------------------------------------> +@using COMETwebapp.Extensions @inherits SelectedDataItemForm - + @@ -29,15 +30,40 @@ Copyright (c) 2023-2024 RHEA System S.A. @bind-Value="@this.ViewModel.File.Owner" CssClass="cw-480" /> + + @if (this.ShouldCreate) + { + + + + @itemTemplateContext.GetFolderPath() + + + + } + + + + +
- - Download + + Delete - - + Save @@ -47,4 +73,12 @@ Copyright (c) 2023-2024 RHEA System S.A. Cancel
-
\ No newline at end of file +
+ + + You are about to delete the File: @(this.ViewModel.File.CurrentFileRevision?.Name) +
+ + +
+
diff --git a/COMETwebapp/Components/EngineeringModel/FileForm.razor.cs b/COMETwebapp/Components/EngineeringModel/FileStore/FileForm.razor.cs similarity index 63% rename from COMETwebapp/Components/EngineeringModel/FileForm.razor.cs rename to COMETwebapp/Components/EngineeringModel/FileStore/FileForm.razor.cs index 3aedf626..33dcd675 100644 --- a/COMETwebapp/Components/EngineeringModel/FileForm.razor.cs +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FileForm.razor.cs @@ -22,10 +22,10 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace COMETwebapp.Components.EngineeringModel +namespace COMETwebapp.Components.EngineeringModel.FileStore { using COMETwebapp.Components.Common; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler; using Microsoft.AspNetCore.Components; @@ -37,17 +37,35 @@ namespace COMETwebapp.Components.EngineeringModel public partial class FileForm : SelectedDataItemForm { /// - /// The for this component + /// The for this component /// [Parameter, Required] - public IFolderFileStructureViewModel ViewModel { get; set; } + public IFileHandlerViewModel ViewModel { get; set; } /// - /// Downloads a the selected file from + /// Gets the value to check if the deletion popup is visible /// - public void DownloadFile() + public bool IsDeletePopupVisible { get; private set; } + + /// + /// Method that is executed when there is a valid submit + /// + /// A + protected override async Task OnValidSubmit() + { + await this.ViewModel.CreateOrEditFile(this.ShouldCreate); + await base.OnValidSubmit(); + } + + /// + /// Method that is executed when a deletion is done + /// + /// A + private async Task OnDelete() { - // downloads a file => to be done + this.IsDeletePopupVisible = false; + await this.ViewModel.DeleteFile(); + await base.OnCancel(); } } } diff --git a/COMETwebapp/Components/EngineeringModel/FileStore/FileRevisionsTable.razor b/COMETwebapp/Components/EngineeringModel/FileStore/FileRevisionsTable.razor new file mode 100644 index 00000000..7cf9428f --- /dev/null +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FileRevisionsTable.razor @@ -0,0 +1,77 @@ + +@using COMETwebapp.ViewModels.Components.ReferenceData.Rows +@inherits DisposableComponent + + + + + + + + + + + + @{ + var row = (FileRevisionRowViewModel)context.DataItem; + + + + } + + + + + + + + + $".{x.Extension}")))"> + + + + + + + + + + + +
+ @if (!string.IsNullOrWhiteSpace(this.ViewModel.ErrorMessage)) + { + @this.ViewModel.ErrorMessage + } + +
+ +
diff --git a/COMETwebapp/Components/EngineeringModel/FileStore/FileRevisionsTable.razor.cs b/COMETwebapp/Components/EngineeringModel/FileStore/FileRevisionsTable.razor.cs new file mode 100644 index 00000000..fd15e4c3 --- /dev/null +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FileRevisionsTable.razor.cs @@ -0,0 +1,132 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// 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 RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Components.EngineeringModel.FileStore +{ + using CDP4Common.EngineeringModelData; + + using COMET.Web.Common.Components; + + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler; + using COMETwebapp.ViewModels.Components.EngineeringModel.Rows; + + using DevExpress.Blazor; + + using Microsoft.AspNetCore.Components; + using Microsoft.AspNetCore.Components.Forms; + + /// + /// Support class for the + /// + public partial class FileRevisionsTable : DisposableComponent + { + /// + /// Gets or sets the + /// + [Parameter] + public IFileRevisionHandlerViewModel ViewModel { get; set; } + + /// + /// A collection of file revisions to display for selection + /// + [Parameter] + public IEnumerable FileRevisions { get; set; } + + /// + /// The method that is executed when the file revisions change + /// + [Parameter] + public EventCallback> FileRevisionsChanged { get; set; } + + /// + /// Gets or sets the grid control that is being customized. + /// + private IGrid Grid { get; set; } + + /// + /// Method that is invoked when the edit/add file revision form is being saved + /// + private async Task OnEditFileRevisionSaving() + { + var listOfFileRevisions = this.FileRevisions.ToList(); + listOfFileRevisions.Add(this.ViewModel.FileRevision); + + this.FileRevisions = listOfFileRevisions; + await this.FileRevisionsChanged.InvokeAsync(this.FileRevisions); + } + + /// + /// Method that is invoked when a file revision row is being removed + /// + private async Task RemoveFileRevision(FileRevisionRowViewModel row) + { + var listOfFileRevisions = this.FileRevisions.ToList(); + listOfFileRevisions.Remove(row.Thing); + + this.FileRevisions = listOfFileRevisions; + await this.FileRevisionsChanged.InvokeAsync(this.FileRevisions); + } + + /// + /// Method invoked when creating a new file revision + /// + /// A + private void CustomizeEditFileRevision(GridCustomizeEditModelEventArgs e) + { + this.ViewModel.FileRevision = new FileRevision + { + ContainingFolder = this.ViewModel.CurrentFile.CurrentContainingFolder + }; + + e.EditModel = this.ViewModel.FileRevision; + } + + /// + /// Method used to retrieve the available rows, given the from + /// + /// A collection of s to display + private List GetRows() + { + return this.FileRevisions.Select(x => new FileRevisionRowViewModel(x)).ToList(); + } + + /// + /// Method that is invoked when a file is uploaded to server + /// + private async Task OnFileUpload(InputFileChangeEventArgs e) + { + await this.ViewModel.UploadFile(e.File); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// Value asserting if this component should dispose or not + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + this.ViewModel.Dispose(); + } + } +} diff --git a/COMETwebapp/Components/EngineeringModel/FileStore/FileTypesTable.razor b/COMETwebapp/Components/EngineeringModel/FileStore/FileTypesTable.razor new file mode 100644 index 00000000..8e94dfe7 --- /dev/null +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FileTypesTable.razor @@ -0,0 +1,87 @@ + +@using COMETwebapp.ViewModels.Components.ReferenceData.Rows +@inherits DisposableComponent + + + + + + + + + + + + @{ + var row = (FileTypeRowViewModel)context.DataItem; + + + + + } + + + + + + + + + + + + + +
+ +
+ + Save + + + + Cancel + +
+
+
+
diff --git a/COMETwebapp/Components/EngineeringModel/FileStore/FileTypesTable.razor.cs b/COMETwebapp/Components/EngineeringModel/FileStore/FileTypesTable.razor.cs new file mode 100644 index 00000000..213de604 --- /dev/null +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FileTypesTable.razor.cs @@ -0,0 +1,136 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// 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 RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Components.EngineeringModel.FileStore +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + using CDP4Common.Types; + + using COMET.Web.Common.Components; + + using COMETwebapp.ViewModels.Components.EngineeringModel.Rows; + + using DevExpress.Blazor; + + using Microsoft.AspNetCore.Components; + + /// + /// Support class for the + /// + public partial class FileTypesTable : DisposableComponent + { + /// + /// A collection of file types to display for selection + /// + [Parameter] + public IEnumerable FileTypes { get; set; } + + /// + /// A collection of selected file types to display for selection + /// + [Parameter] + public OrderedItemList SelectedFileTypes { get; set; } + + /// + /// The method that is executed when the selected file types change + /// + [Parameter] + public EventCallback> SelectedFileTypesChanged { get; set; } + + /// + /// The file type that will be added + /// + public FileType FileType { get; private set; } + + /// + /// Gets or sets the grid control that is being customized. + /// + private IGrid Grid { get; set; } + + /// + /// Method that is invoked when the edit/add file type form is being saved + /// + private async Task OnEditFileTypesSaving() + { + var listOfFileTypes = this.SelectedFileTypes; + listOfFileTypes.Add(this.FileType); + + this.SelectedFileTypes = listOfFileTypes; + await this.SelectedFileTypesChanged.InvokeAsync(this.SelectedFileTypes); + } + + /// + /// Moves the selected row up + /// + /// The row to be moved + /// A + private async Task MoveUp(FileTypeRowViewModel row) + { + var currentIndex = this.SelectedFileTypes.IndexOf(row.Thing); + this.SelectedFileTypes.Move(currentIndex, currentIndex - 1); + await this.SelectedFileTypesChanged.InvokeAsync(this.SelectedFileTypes); + } + + /// + /// Moves the selected row down + /// + /// The row to be moved + /// A + private async Task MoveDown(FileTypeRowViewModel row) + { + var currentIndex = this.SelectedFileTypes.IndexOf(row.Thing); + this.SelectedFileTypes.Move(currentIndex, currentIndex + 1); + await this.SelectedFileTypesChanged.InvokeAsync(this.SelectedFileTypes); + } + + /// + /// Method that is invoked when a file type row is being removed + /// + private async Task RemoveFileType(FileTypeRowViewModel row) + { + this.SelectedFileTypes.Remove(row.Thing); + await this.SelectedFileTypesChanged.InvokeAsync(this.SelectedFileTypes); + } + + /// + /// Method invoked when creating a new file type + /// + /// A + private void CustomizeEditFileType(GridCustomizeEditModelEventArgs e) + { + this.FileType = new FileType(); + e.EditModel = this.FileType; + } + + /// + /// Method used to retrieve the available rows, given the + /// + /// A collection of s to display + private List GetRows() + { + return this.SelectedFileTypes.Select(x => new FileTypeRowViewModel(x)).ToList(); + } + } +} diff --git a/COMETwebapp/Components/EngineeringModel/FolderFileStructure.razor b/COMETwebapp/Components/EngineeringModel/FileStore/FolderFileStructure.razor similarity index 57% rename from COMETwebapp/Components/EngineeringModel/FolderFileStructure.razor rename to COMETwebapp/Components/EngineeringModel/FileStore/FolderFileStructure.razor index 1901d9b2..e2d9cc2a 100644 --- a/COMETwebapp/Components/EngineeringModel/FolderFileStructure.razor +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FolderFileStructure.razor @@ -14,9 +14,19 @@ Copyright (c) 2023-2024 RHEA System S.A. 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/. -------------------------------------------------------------------------------> -@using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure +@using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore @inherits DisposableComponent + + Create Folder + + + + Create File + + - @nodeContext.Text @{ var row = (FileFolderNodeViewModel)nodeContext.DataItem; +
+ + @nodeContext.Text - if (row.Thing is Folder) - { - - } + @if (row.Thing is Folder) + { + + } +
}
- - + Width="40%"> + - - + Width="40%"> + diff --git a/COMETwebapp/Components/EngineeringModel/FolderFileStructure.razor.cs b/COMETwebapp/Components/EngineeringModel/FileStore/FolderFileStructure.razor.cs similarity index 57% rename from COMETwebapp/Components/EngineeringModel/FolderFileStructure.razor.cs rename to COMETwebapp/Components/EngineeringModel/FileStore/FolderFileStructure.razor.cs index 75d13f94..876aa204 100644 --- a/COMETwebapp/Components/EngineeringModel/FolderFileStructure.razor.cs +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FolderFileStructure.razor.cs @@ -22,18 +22,22 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace COMETwebapp.Components.EngineeringModel +namespace COMETwebapp.Components.EngineeringModel.FileStore { using System.ComponentModel.DataAnnotations; using CDP4Common.EngineeringModelData; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; + using COMET.Web.Common.Extensions; + + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore; using DevExpress.Blazor; using Microsoft.AspNetCore.Components; + using ReactiveUI; + /// /// Support class for the /// @@ -46,14 +50,29 @@ public partial class FolderFileStructure public IFolderFileStructureViewModel ViewModel { get; set; } /// - /// Gets the selected file from the + /// Gets the condition to verify if a file should be created + /// + public bool ShouldCreateFile { get; private set; } + + /// + /// Gets the condition to check the visibility of the file form /// - public File SelectedFile { get; private set; } + public bool IsFileFormVisibile { get; private set; } /// - /// Gets the selected folder from the + /// Gets the condition to verify if a folder should be created /// - public Folder SelectedFolder { get; private set; } + public bool ShouldCreateFolder { get; private set; } + + /// + /// Gets the condition to check the visibility of the folder form + /// + public bool IsFolderFormVisibile { get; private set; } + + /// + /// Gets the dragged node used in drag and drop interactions + /// + public FileFolderNodeViewModel DraggedNode { get; private set; } /// /// Gets or sets the used to display the folder-file structure @@ -67,29 +86,49 @@ public partial class FolderFileStructure public void OnNodeClick(ITreeViewNodeInfo e) { var dataItem = (FileFolderNodeViewModel)e.DataItem; + this.OnEditFileClick(dataItem); + } - if (dataItem.Thing is not File file) + /// + /// Method that is triggered every time the edit folder is clicked + /// + /// The selected row + public void OnEditFolderClick(FileFolderNodeViewModel row = null) + { + if (row is { Thing: not Folder }) { return; } - this.ViewModel.File = file; - this.SelectedFile = this.ViewModel.File; + this.ViewModel.FolderHandlerViewModel.SelectFolder(row == null ? new Folder() : (Folder)row.Thing); + this.ShouldCreateFolder = row == null; + this.IsFolderFormVisibile = true; } /// - /// Method that is triggered every time the edit folder icon is clicked + /// Method that is triggered every time the edit file is clicked /// /// The selected row - public void OnEditFolderClick(FileFolderNodeViewModel row) + public void OnEditFileClick(FileFolderNodeViewModel row = null) { - if (row.Thing is not Folder folder) + if (row is { Thing: not File }) { return; } - this.ViewModel.Folder = folder; - this.SelectedFolder = this.ViewModel.Folder; + this.ViewModel.FileHandlerViewModel.SelectFile(row == null ? new File() : (File)row.Thing); + this.ShouldCreateFile = row == null; + this.IsFileFormVisibile = true; + } + + /// + /// Method invoked when the component is ready to start, having received its + /// initial parameters from its parent in the render tree. + /// + protected override void OnInitialized() + { + base.OnInitialized(); + this.Disposables.Add(this.WhenAnyValue(x => x.ViewModel.IsLoading).SubscribeAsync(_ => this.InvokeAsync(this.StateHasChanged))); } /// @@ -124,8 +163,42 @@ protected override void OnAfterRender(bool firstRender) /// private void OnClosedFormPopup() { - this.SelectedFile = null; - this.SelectedFolder = null; + this.IsFileFormVisibile = false; + this.IsFolderFormVisibile = false; + } + + /// + /// Method invoked when a node is dragged + /// + /// The dragged node + private void OnDragNode(FileFolderNodeViewModel node) + { + this.DraggedNode = node; + } + + /// + /// Method invoked when a node is dropped + /// + /// The target node where the has been dropped + /// A + private async Task OnDropNode(FileFolderNodeViewModel targetNode) + { + if (targetNode.Thing is not Folder and not null) + { + return; + } + + var targetFolder = (Folder)targetNode.Thing; + + switch (this.DraggedNode.Thing) + { + case File file: + await this.ViewModel.FileHandlerViewModel.MoveFile(file, targetFolder); + break; + case Folder folder: + await this.ViewModel.FolderHandlerViewModel.MoveFolder(folder, targetFolder); + break; + } } } } diff --git a/COMETwebapp/Components/EngineeringModel/FolderForm.razor b/COMETwebapp/Components/EngineeringModel/FileStore/FolderForm.razor similarity index 57% rename from COMETwebapp/Components/EngineeringModel/FolderForm.razor rename to COMETwebapp/Components/EngineeringModel/FileStore/FolderForm.razor index 0adcc479..b74e5afe 100644 --- a/COMETwebapp/Components/EngineeringModel/FolderForm.razor +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FolderForm.razor @@ -14,28 +14,51 @@ Copyright (c) 2023-2024 RHEA System S.A. 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/. -------------------------------------------------------------------------------> +@using COMETwebapp.Extensions @inherits SelectedDataItemForm - - + + + CssClass="cw-480"/> + + @if (this.ShouldCreate) + { + + + + @itemTemplateContext.GetFolderPath() + + + + } +
+ + Delete + + Save - + @@ -43,3 +66,11 @@ Copyright (c) 2023-2024 RHEA System S.A.
+ + + You are about to delete the Folder: @(this.ViewModel.Folder.Name) +
+ + +
+
diff --git a/COMETwebapp/Components/EngineeringModel/FileStore/FolderForm.razor.cs b/COMETwebapp/Components/EngineeringModel/FileStore/FolderForm.razor.cs new file mode 100644 index 00000000..31f2cc9a --- /dev/null +++ b/COMETwebapp/Components/EngineeringModel/FileStore/FolderForm.razor.cs @@ -0,0 +1,73 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// 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 RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Components.EngineeringModel.FileStore +{ + using COMETwebapp.Components.Common; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler; + + using Microsoft.AspNetCore.Components; + + using System.ComponentModel.DataAnnotations; + + using CDP4Common.EngineeringModelData; + + /// + /// Support class for the + /// + public partial class FolderForm : SelectedDataItemForm + { + /// + /// The for this component + /// + [Parameter, Required] + public IFolderHandlerViewModel ViewModel { get; set; } + + /// + /// Gets the value to check if the deletion popup is visible + /// + public bool IsDeletePopupVisible { get; private set; } + + /// + /// Method that is executed when there is a valid submit + /// + /// A + protected override async Task OnValidSubmit() + { + await this.ViewModel.CreateOrEditFolder(this.ShouldCreate); + await base.OnValidSubmit(); + } + + /// + /// Method that is executed when a deletion is done + /// + /// A + private async Task OnDelete() + { + this.IsDeletePopupVisible = false; + await this.ViewModel.DeleteFolder(); + await base.OnCancel(); + } + } +} diff --git a/COMETwebapp/Extensions/FolderExtensions.cs b/COMETwebapp/Extensions/FolderExtensions.cs new file mode 100644 index 00000000..ff9c5c6e --- /dev/null +++ b/COMETwebapp/Extensions/FolderExtensions.cs @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// 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 RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Extensions +{ + using CDP4Common.EngineeringModelData; + + /// + /// Extension class for Folder + /// + public static class FolderExtensions + { + /// + /// Gets the given folder path, including the folder itself + /// + /// The folder to get the path + /// The path + public static string GetFolderPath(this Folder folder) + { + return Path.Combine(folder.Path, folder.Name); + } + } +} diff --git a/COMETwebapp/Extensions/ServiceCollectionExtensions.cs b/COMETwebapp/Extensions/ServiceCollectionExtensions.cs index ba2ed37d..0b7d4e18 100644 --- a/COMETwebapp/Extensions/ServiceCollectionExtensions.cs +++ b/COMETwebapp/Extensions/ServiceCollectionExtensions.cs @@ -51,7 +51,10 @@ namespace COMETwebapp.Extensions using COMETwebapp.ViewModels.Components.UserManagement; using COMETwebapp.ViewModels.Components.Viewer; using COMETwebapp.ViewModels.Shared.TopMenuEntry; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler; /// /// Extension class for the @@ -71,6 +74,7 @@ public static void RegisterServices(this IServiceCollection serviceCollection) serviceCollection.AddScoped(); serviceCollection.AddScoped(); serviceCollection.AddScoped(); + serviceCollection.AddScoped(); serviceCollection.AddHttpClient(); serviceCollection.AddAntDesign(); } @@ -112,6 +116,9 @@ public static void RegisterViewModels(this IServiceCollection serviceCollection) serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); } } } diff --git a/COMETwebapp/Pages/_Host.cshtml b/COMETwebapp/Pages/_Host.cshtml index d6cba627..d13aba18 100644 --- a/COMETwebapp/Pages/_Host.cshtml +++ b/COMETwebapp/Pages/_Host.cshtml @@ -102,5 +102,6 @@ + \ No newline at end of file diff --git a/COMETwebapp/Services/Interoperability/IJsUtilitiesService.cs b/COMETwebapp/Services/Interoperability/IJsUtilitiesService.cs new file mode 100644 index 00000000..56ffcc62 --- /dev/null +++ b/COMETwebapp/Services/Interoperability/IJsUtilitiesService.cs @@ -0,0 +1,40 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Services.Interoperability +{ + /// + /// The service used to provide js utilities + /// + public interface IJsUtilitiesService + { + /// + /// Downloads a file from a stream asynchronously + /// + /// the stream + /// the file name + /// an asynchronous operation + Task DownloadFileFromStreamAsync(Stream stream, string fileName); + } +} diff --git a/COMETwebapp/Services/Interoperability/JsUtilitiesService.cs b/COMETwebapp/Services/Interoperability/JsUtilitiesService.cs new file mode 100644 index 00000000..ab506e24 --- /dev/null +++ b/COMETwebapp/Services/Interoperability/JsUtilitiesService.cs @@ -0,0 +1,78 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Services.Interoperability +{ + using Microsoft.JSInterop; + + /// + /// The service used to provide js utilities + /// + public class JsUtilitiesService : InteroperabilityService, IJsUtilitiesService + { + /// + /// Creates a new instance of + /// + /// the + public JsUtilitiesService(IJSRuntime jsRuntime) : base(jsRuntime) + { + } + + /// + /// Downloads a file from a stream asynchronously + /// + /// the stream + /// the file name + /// an asynchronous operation + public async Task DownloadFileFromStreamAsync(Stream stream, string fileName) + { + ValidateDownloadParameters(stream, fileName); + + using var streamRef = new DotNetStreamReference(stream: stream); + await this.DownloadFileAsync(fileName, streamRef); + } + + /// + /// Validates the download file parameters + /// + /// the stream + /// the file name + private static void ValidateDownloadParameters(Stream stream, string fileName) + { + ArgumentNullException.ThrowIfNull(stream); + ArgumentNullException.ThrowIfNull(fileName); + } + + /// + /// Effectively downloads the file + /// + /// the file name + /// the stream + /// A + private async Task DownloadFileAsync(string fileName, DotNetStreamReference streamRef) + { + await this.JsRuntime.InvokeVoidAsync("DownloadFileFromStream", fileName, streamRef); + } + } +} diff --git a/COMETwebapp/Components/EngineeringModel/FolderForm.razor.cs b/COMETwebapp/Utilities/Constants.cs similarity index 69% rename from COMETwebapp/Components/EngineeringModel/FolderForm.razor.cs rename to COMETwebapp/Utilities/Constants.cs index 306ac08c..ac27daf9 100644 --- a/COMETwebapp/Components/EngineeringModel/FolderForm.razor.cs +++ b/COMETwebapp/Utilities/Constants.cs @@ -1,5 +1,5 @@ // -------------------------------------------------------------------------------------------------------------------- -// +// // Copyright (c) 2023-2024 RHEA System S.A. // // Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Théate Antoine, João Rua @@ -22,24 +22,16 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace COMETwebapp.Components.EngineeringModel +namespace COMETwebapp.Utilities { - using COMETwebapp.Components.Common; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; - - using Microsoft.AspNetCore.Components; - - using System.ComponentModel.DataAnnotations; - /// - /// Support class for the + /// Contains constant values that can be shared across the application /// - public partial class FolderForm : SelectedDataItemForm + public static class Constants { /// - /// The for this component + /// The name of the configuration key used to retrieve the max upload file size, in megabytes /// - [Parameter, Required] - public IFolderFileStructureViewModel ViewModel { get; set; } + public const string MaxUploadFileSizeInMbConfigurationKey = "MaxUploadFileSizeInMb"; } } diff --git a/COMETwebapp/Validators/EngineeringModel/CommonFileStoreValidator.cs b/COMETwebapp/Validators/EngineeringModel/CommonFileStoreValidator.cs new file mode 100644 index 00000000..b05afabf --- /dev/null +++ b/COMETwebapp/Validators/EngineeringModel/CommonFileStoreValidator.cs @@ -0,0 +1,48 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Validators.EngineeringModel +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.Validation; + + using COMET.Web.Common.Extensions; + + using FluentValidation; + + /// + /// A class to validate the + /// + public class CommonFileStoreValidator : AbstractValidator + { + /// + /// Instantiates a new + /// + public CommonFileStoreValidator(IValidationService validationService) : base() + { + this.RuleFor(x => x.Name).Validate(validationService, nameof(CommonFileStore.Name)); + this.RuleFor(x => x.Owner).Validate(validationService, nameof(CommonFileStore.Owner)); + } + } +} diff --git a/COMETwebapp/Validators/EngineeringModel/FileRevisionValidator.cs b/COMETwebapp/Validators/EngineeringModel/FileRevisionValidator.cs new file mode 100644 index 00000000..436a33a5 --- /dev/null +++ b/COMETwebapp/Validators/EngineeringModel/FileRevisionValidator.cs @@ -0,0 +1,51 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Validators.EngineeringModel +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.Validation; + + using COMET.Web.Common.Extensions; + + using FluentValidation; + + /// + /// A class to validate the + /// + public class FileRevisionValidator : AbstractValidator + { + /// + /// Instantiates a new + /// + public FileRevisionValidator(IValidationService validationService) : base() + { + this.RuleFor(x => x.Name).Validate(validationService, nameof(FileRevision.Name)); + this.RuleFor(x => x.ContainingFolder).Validate(validationService, nameof(FileRevision.ContainingFolder)); + this.RuleFor(x => x.FileType).NotEmpty().Validate(validationService, nameof(FileRevision.FileType)); + this.RuleFor(x => x.LocalPath).NotEmpty().Validate(validationService, nameof(FileRevision.LocalPath)); + this.RuleFor(x => x.Path).NotEmpty().Validate(validationService, nameof(FileRevision.Path)); + } + } +} diff --git a/COMETwebapp/Validators/EngineeringModel/FileTypeValidator.cs b/COMETwebapp/Validators/EngineeringModel/FileTypeValidator.cs new file mode 100644 index 00000000..0e9ca2f0 --- /dev/null +++ b/COMETwebapp/Validators/EngineeringModel/FileTypeValidator.cs @@ -0,0 +1,47 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Validators.EngineeringModel +{ + using CDP4Common.SiteDirectoryData; + using CDP4Common.Validation; + + using COMET.Web.Common.Extensions; + + using FluentValidation; + + /// + /// A class to validate the + /// + public class FileTypeValidator : AbstractValidator + { + /// + /// Instantiates a new + /// + public FileTypeValidator(IValidationService validationService) : base() + { + this.RuleFor(x => x.Extension).Validate(validationService, nameof(FileType.Extension)); + } + } +} diff --git a/COMETwebapp/Validators/EngineeringModel/FileValidator.cs b/COMETwebapp/Validators/EngineeringModel/FileValidator.cs new file mode 100644 index 00000000..02c81b18 --- /dev/null +++ b/COMETwebapp/Validators/EngineeringModel/FileValidator.cs @@ -0,0 +1,48 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Validators.EngineeringModel +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.Validation; + + using COMET.Web.Common.Extensions; + + using FluentValidation; + + /// + /// A class to validate the + /// + public class FileValidator : AbstractValidator + { + /// + /// Instantiates a new + /// + public FileValidator(IValidationService validationService) : base() + { + this.RuleFor(x => x.Owner).NotEmpty().Validate(validationService, nameof(File.Owner)); + this.RuleFor(x => x.LockedBy).Validate(validationService, nameof(File.LockedBy)); + } + } +} diff --git a/COMETwebapp/Validators/EngineeringModel/FolderValidator.cs b/COMETwebapp/Validators/EngineeringModel/FolderValidator.cs new file mode 100644 index 00000000..3eb72249 --- /dev/null +++ b/COMETwebapp/Validators/EngineeringModel/FolderValidator.cs @@ -0,0 +1,49 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.Validators.EngineeringModel +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.Validation; + + using COMET.Web.Common.Extensions; + + using FluentValidation; + + /// + /// A class to validate the + /// + public class FolderValidator : AbstractValidator + { + /// + /// Instantiates a new + /// + public FolderValidator(IValidationService validationService) : base() + { + this.RuleFor(x => x.Name).Validate(validationService, nameof(Folder.Name)); + this.RuleFor(x => x.Owner).Validate(validationService, nameof(Folder.Owner)); + this.RuleFor(x => x.ContainingFolder).Validate(validationService, nameof(Folder.ContainingFolder)); + } + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/CommonFileStore/CommonFileStoreTableViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/CommonFileStore/CommonFileStoreTableViewModel.cs index ef75713b..7b31e98a 100644 --- a/COMETwebapp/ViewModels/Components/EngineeringModel/CommonFileStore/CommonFileStoreTableViewModel.cs +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/CommonFileStore/CommonFileStoreTableViewModel.cs @@ -33,7 +33,7 @@ namespace COMETwebapp.ViewModels.Components.EngineeringModel.CommonFileStore using COMET.Web.Common.Services.SessionManagement; using COMETwebapp.ViewModels.Components.Common.DeletableDataItemTable; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore; using COMETwebapp.ViewModels.Components.EngineeringModel.Rows; /// diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/CommonFileStore/ICommonFileStoreTableViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/CommonFileStore/ICommonFileStoreTableViewModel.cs index 73887b0b..22a436b4 100644 --- a/COMETwebapp/ViewModels/Components/EngineeringModel/CommonFileStore/ICommonFileStoreTableViewModel.cs +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/CommonFileStore/ICommonFileStoreTableViewModel.cs @@ -28,7 +28,7 @@ namespace COMETwebapp.ViewModels.Components.EngineeringModel.CommonFileStore using CDP4Common.SiteDirectoryData; using COMETwebapp.ViewModels.Components.Common.DeletableDataItemTable; - using COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore; using COMETwebapp.ViewModels.Components.EngineeringModel.Rows; /// diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/FileFolderNodeViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileFolderNodeViewModel.cs similarity index 53% rename from COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/FileFolderNodeViewModel.cs rename to COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileFolderNodeViewModel.cs index a9bd7b59..02287d3a 100644 --- a/COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/FileFolderNodeViewModel.cs +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileFolderNodeViewModel.cs @@ -22,16 +22,23 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore { using CDP4Common.CommonData; using CDP4Common.EngineeringModelData; + using ReactiveUI; + /// /// ViewModel for a node in the File-Folder structure tree /// - public class FileFolderNodeViewModel + public class FileFolderNodeViewModel : ReactiveObject { + /// + /// The backing field for + /// + private string name; + /// /// The list containing the sub content of the current node /// @@ -40,12 +47,16 @@ public class FileFolderNodeViewModel /// /// The thing associated with the model, which can be either a or a /// - public Thing Thing { get; private set; } + public Thing Thing { get; set; } /// /// The name of the current node /// - public string Name { get; private set; } + public string Name + { + get => this.name; + set => this.RaiseAndSetIfChanged(ref this.name, value); + } /// /// The icon css class of the current node, used to display the icon as a html class @@ -55,34 +66,77 @@ public class FileFolderNodeViewModel /// /// Creates a new instance of type /// - /// The file to set the current + /// The thing to set the current , which can be both or /// A collection containing all the node's content - public FileFolderNodeViewModel(File file, IEnumerable content = null) + public FileFolderNodeViewModel(Thing thing, IEnumerable content = null) { - this.Name = file.CurrentFileRevision.Name; - this.IconCssClass = "oi oi-file"; - this.InitializeProperties(file, content); + this.InitializeProperties(thing, content); } /// - /// Creates a new instance of type + /// Creates a new instance of type , as a root node /// - /// The folder to set the current - /// A collection containing all the node's content - public FileFolderNodeViewModel(Folder folder, IEnumerable content = null) + public FileFolderNodeViewModel() { - this.Name = folder.Name; - this.IconCssClass = "oi oi-folder"; - this.InitializeProperties(folder, content); + this.Name = "root"; + this.IconCssClass = "oi oi-target"; } /// - /// Creates a new instance of type , as a root node + /// Updates the contained /// - public FileFolderNodeViewModel() + /// The thing to be set + public void UpdateThing(Thing thing) { - this.Name = "root"; - this.IconCssClass = "oi oi-target"; + if (this.Thing.GetType() != thing.GetType()) + { + throw new InvalidCastException("Cannot change current thing type"); + } + + this.InitializeProperties(thing); + } + + /// + /// Removes a given node from the nested content + /// + /// The node to be removed + public void RemoveChildNode(FileFolderNodeViewModel node) + { + var wasNodeRemoved = this.Content.Remove(node); + + if (wasNodeRemoved) + { + return; + } + + foreach (var folderNodes in this.Content.Where(x => x.Thing is Folder)) + { + folderNodes.RemoveChildNode(node); + } + } + + /// + /// Gets a flat list with all the children nodes + /// + /// The value to set if current node should be included in the result + /// The collection of children + public IEnumerable GetFlatListOfChildrenNodes(bool includeSelf = false) + { + var children = new List(); + + if (includeSelf) + { + children.Add(this); + } + + children.AddRange(this.Content); + + foreach (var content in this.Content) + { + children.AddRange(content.GetFlatListOfChildrenNodes()); + } + + return children; } /// @@ -92,6 +146,20 @@ public FileFolderNodeViewModel() /// The collection of to set the current private void InitializeProperties(Thing thing, IEnumerable content = null) { + switch (thing) + { + case File file: + this.Name = file.CurrentFileRevision?.Name; + this.IconCssClass = "oi oi-file"; + break; + case Folder folder: + this.Name = folder.Name; + this.IconCssClass = "oi oi-folder"; + break; + default: + throw new InvalidDataException("The given thing should be either a File or a Folder"); + } + this.Thing = thing; if (content != null) diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileHandler/FileHandlerViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileHandler/FileHandlerViewModel.cs new file mode 100644 index 00000000..510a2824 --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileHandler/FileHandlerViewModel.cs @@ -0,0 +1,234 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler +{ + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using CDP4Dal; + + using COMET.Web.Common.Services.SessionManagement; + using COMET.Web.Common.ViewModels.Components.Applications; + + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler; + + /// + /// View model used to manage the files in Filestores + /// + public class FileHandlerViewModel : ApplicationBaseViewModel, IFileHandlerViewModel + { + /// + /// Gets or sets the current + /// + private FileStore CurrentFileStore { get; set; } + + /// + /// Gets the + /// + private readonly ILogger logger; + + /// + /// Initializes a new instance of the class. + /// + /// The + /// The + /// The + /// The + public FileHandlerViewModel(ISessionService sessionService, ICDPMessageBus messageBus, ILogger logger, IFileRevisionHandlerViewModel fileRevisionHandlerViewModel) + : base(sessionService, messageBus) + { + this.logger = logger; + this.FileRevisionHandlerViewModel = fileRevisionHandlerViewModel; + } + + /// + /// Gets the + /// + public IFileRevisionHandlerViewModel FileRevisionHandlerViewModel { get; private set; } + + /// + /// Gets a collection of the available + /// + public IEnumerable DomainsOfExpertise { get; private set; } + + /// + /// Gets a collection of the available + /// + public IEnumerable FileTypes { get; private set; } + + /// + /// Gets a collection of the available s + /// + public IEnumerable Folders { get; private set; } + + /// + /// Gets or sets the file to be created/edited + /// + public File File { get; set; } = new(); + + /// + /// Gets or sets a collection of the file revisions to be created/edited + /// + public IEnumerable SelectedFileRevisions { get; set; } + + /// + /// Gets or sets the selected folder to create a file revision + /// + public Folder SelectedFolder { get; set; } + + /// + /// Gets or sets the condition to check if the file or folder to be created is locked + /// + public bool IsLocked { get; set; } + + /// + /// Initializes the current + /// + /// The to be set + public void InitializeViewModel(FileStore fileStore) + { + this.CurrentFileStore = fileStore; + this.DomainsOfExpertise = this.SessionService.GetSiteDirectory().Domain; + this.FileTypes = this.SessionService.GetSiteDirectory().AvailableReferenceDataLibraries().SelectMany(x => x.FileType); + + var folders = this.CurrentFileStore.Folder.ToList(); + folders.Add(null); + this.Folders = folders; + } + + /// + /// Selects the current + /// + /// The file to be set + public void SelectFile(File file) + { + this.File = file.Iid != Guid.Empty ? file.Clone(true) : file; + this.IsLocked = this.File.LockedBy is not null; + this.SelectedFileRevisions = this.File.FileRevision; + this.FileRevisionHandlerViewModel.InitializeViewModel(this.File, this.CurrentFileStore); + this.SelectedFolder = null; + } + + /// + /// Moves a file to a target folder + /// + /// The file to be moved + /// The target folder + /// A + public async Task MoveFile(File file, Folder targetFolder) + { + this.IsLoading = true; + + var fileClone = file.Clone(true); + var newFileRevision = fileClone.CurrentFileRevision.Clone(true); + + newFileRevision.Iid = Guid.NewGuid(); + newFileRevision.CreatedOn = DateTime.UtcNow; + newFileRevision.ContainingFolder = targetFolder; + + fileClone.FileRevision.Add(newFileRevision); + + await this.SessionService.UpdateThings(this.CurrentFileStore.Clone(true), fileClone, newFileRevision); + await this.SessionService.RefreshSession(); + + this.IsLoading = false; + } + + /// + /// Creates or edits a file + /// + /// The condition to check if the file should be created + /// A + public async Task CreateOrEditFile(bool shouldCreate) + { + this.IsLoading = true; + this.logger.LogInformation("Creating or editing file"); + + var thingsToUpdate = new List(); + var fileStoreClone = this.CurrentFileStore.Clone(true); + + this.File.LockedBy = this.IsLocked switch + { + true when this.File.LockedBy == null => this.SessionService.Session.ActivePerson, + false when this.File.LockedBy != null => null, + _ => this.File.LockedBy + }; + + var newFileRevisions = this.SelectedFileRevisions.Where(x => !this.File.FileRevision.Contains(x)).ToList(); + + foreach (var fileRevision in newFileRevisions) + { + if (shouldCreate) + { + fileRevision.ContainingFolder = this.SelectedFolder; + } + + this.File.FileRevision.Add(fileRevision); + thingsToUpdate.Add(fileRevision); + } + + var fileRevisionsToRemove = this.File.FileRevision.Where(x => !this.SelectedFileRevisions.Contains(x)).ToList(); + + foreach (var fileRevisionToRemove in fileRevisionsToRemove) + { + this.File.FileRevision.Remove(fileRevisionToRemove); + thingsToUpdate.Add(fileRevisionToRemove); + } + + if (shouldCreate) + { + fileStoreClone.File.Add(this.File); + thingsToUpdate.Add(fileStoreClone); + } + + thingsToUpdate.Add(this.File); + + await this.SessionService.UpdateThings(fileStoreClone, thingsToUpdate, newFileRevisions.Select(x => x.LocalPath)); + await this.SessionService.RefreshSession(); + + this.logger.LogInformation("File with iid {iid} updated successfully", this.File.Iid); + this.IsLoading = false; + } + + /// + /// Deletes the current file + /// + /// A + public async Task DeleteFile() + { + var clonedContainer = this.File.Container.Clone(false); + + await this.SessionService.DeleteThing(clonedContainer, this.File); + await this.SessionService.RefreshSession(); + } + + /// + /// Handles the refresh of the current + /// + /// A + protected override Task OnSessionRefreshed() => Task.CompletedTask; + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileHandler/IFileHandlerViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileHandler/IFileHandlerViewModel.cs new file mode 100644 index 00000000..be821cf8 --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileHandler/IFileHandlerViewModel.cs @@ -0,0 +1,112 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using COMET.Web.Common.ViewModels.Components.Applications; + + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler; + + /// + /// View model used to manage the files in Filestores + /// + public interface IFileHandlerViewModel : IApplicationBaseViewModel + { + /// + /// Initializes the current + /// + /// The to be set + void InitializeViewModel(FileStore fileStore); + + /// + /// Gets or sets the file to be created/edited + /// + File File { get; set; } + + /// + /// Gets a collection of the available + /// + IEnumerable DomainsOfExpertise { get; } + + /// + /// Gets or sets the condition to check if the file or folder to be created is locked + /// + bool IsLocked { get; set; } + + /// + /// Gets a collection of the available s + /// + IEnumerable Folders { get; } + + /// + /// Gets or sets the selected folder to create a file revision + /// + Folder SelectedFolder { get; set; } + + /// + /// Gets or sets a collection of the file revisions to be created/edited + /// + IEnumerable SelectedFileRevisions { get; set; } + + /// + /// Gets a collection of the available + /// + IEnumerable FileTypes { get; } + + /// + /// Gets the + /// + IFileRevisionHandlerViewModel FileRevisionHandlerViewModel { get; } + + /// + /// Moves a file to a target folder + /// + /// The file to be moved + /// The target folder + /// A + Task MoveFile(File file, Folder targetFolder); + + /// + /// Creates a file into a target folder + /// + /// + /// A + Task CreateOrEditFile(bool shouldCreate); + + /// + /// Selects the current + /// + /// The file to be set + void SelectFile(File file); + + /// + /// Deletes the current file + /// + /// A + Task DeleteFile(); + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandler/FileRevisionHandlerViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandler/FileRevisionHandlerViewModel.cs new file mode 100644 index 00000000..0019b3a7 --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandler/FileRevisionHandlerViewModel.cs @@ -0,0 +1,236 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using CDP4Dal; + + using COMET.Web.Common.Services.SessionManagement; + using COMET.Web.Common.ViewModels.Components.Applications; + + using COMETwebapp.Services.Interoperability; + using COMETwebapp.Utilities; + + using Microsoft.AspNetCore.Components.Forms; + + using System.Globalization; + + /// + /// View model used to manage the files revisions in Filestores + /// + public class FileRevisionHandlerViewModel : ApplicationBaseViewModel, IFileRevisionHandlerViewModel + { + /// + /// Gets the + /// + private readonly ILogger logger; + + /// + /// Gets or sets the + /// + private readonly IConfiguration configuration; + + /// + /// Gets or sets the directory where uploaded files for the current file are stored + /// + private string UploadsDirectory { get; set; } + + /// + /// Gets or sets the current file store + /// + private FileStore CurrentFileStore { get; set; } + + /// + /// The maximum file size to upload in megabytes + /// + private double MaxUploadFileSizeInMb => double.Parse(this.configuration.GetSection(Constants.MaxUploadFileSizeInMbConfigurationKey).Value!, CultureInfo.InvariantCulture); + + /// + /// Initializes a new instance of the class. + /// + /// The + /// The + /// The + /// The + /// The + public FileRevisionHandlerViewModel(ISessionService sessionService, ICDPMessageBus messageBus, ILogger logger, IJsUtilitiesService jsUtilitiesService, + IConfiguration configuration) : base(sessionService, messageBus) + { + this.JsUtilitiesService = jsUtilitiesService; + this.logger = logger; + this.configuration = configuration; + } + + /// + /// Gets or sets the current + /// + public File CurrentFile { get; private set; } + + /// + /// Gets or sets the + /// + public IJsUtilitiesService JsUtilitiesService { get; private set; } + + /// + /// Gets a collection of the selected + /// + public IEnumerable SelectedFileTypes { get; private set; } = Enumerable.Empty(); + + /// + /// Gets a collection of the available s + /// + public IEnumerable FileTypes { get; private set; } + + /// + /// The file revision that will be handled for both edit and add forms + /// + public FileRevision FileRevision { get; set; } = new(); + + /// + /// Gets or sets the error message that is displayed in the component + /// + public string ErrorMessage { get; private set; } + + /// + /// Initializes the current + /// + /// The to be set + /// + public void InitializeViewModel(File file, FileStore fileStore) + { + this.CurrentFile = file; + this.CurrentFileStore = fileStore; + this.FileTypes = this.SessionService.GetSiteDirectory().AvailableReferenceDataLibraries().SelectMany(x => x.FileType); + this.ErrorMessage = string.Empty; + this.UploadsDirectory = Path.Combine("wwwroot", "uploads", Guid.NewGuid().ToString()); + } + + /// + /// Downloads a file revision + /// + /// the file revision + /// A + public async Task DownloadFileRevision(FileRevision fileRevision) + { + this.logger.LogInformation("Starting File Revision download..."); + var fileRevisionNameWithExtension = $"{fileRevision.Name}.{string.Join(".", fileRevision.FileType.Select(x => x.Extension))}"; + + try + { + var bytes = await this.SessionService.Session.ReadFile(fileRevision); + var stream = new MemoryStream(bytes); + await this.JsUtilitiesService.DownloadFileFromStreamAsync(stream, fileRevisionNameWithExtension); + this.logger.LogInformation("Downloading File Revision..."); + } + catch (Exception ex) + { + this.logger.LogError(ex,"File Revision could not be downloaded") ; + } + } + + /// + /// Uploads a file to server and creates a file revision + /// + /// The file to upload + /// A + public async Task UploadFile(IBrowserFile file) + { + this.ErrorMessage = string.Empty; + var maxUploadFileSizeInBytes = (long)(this.MaxUploadFileSizeInMb * 1024 * 1024); + + if (file.Size > maxUploadFileSizeInBytes) + { + this.ErrorMessage = $"The max file size is {this.MaxUploadFileSizeInMb} MB"; + return; + } + + if (!Directory.Exists(this.UploadsDirectory)) + { + Directory.CreateDirectory(this.UploadsDirectory); + } + + var uploadedFilePath = Path.Combine(this.UploadsDirectory, Guid.NewGuid().ToString()); + + await using (var fileStream = new FileStream(uploadedFilePath, FileMode.Create)) + { + await file.OpenReadStream(maxUploadFileSizeInBytes).CopyToAsync(fileStream); + } + + var engineeringModel = this.CurrentFileStore.GetContainerOfType(); + + this.FileRevision.Name = Path.GetFileNameWithoutExtension(file.Name); + this.FileRevision.LocalPath = uploadedFilePath; + this.FileRevision.ContentHash = CalculateContentHash(this.FileRevision.LocalPath); + this.FileRevision.Creator = engineeringModel.GetActiveParticipant(this.SessionService.Session.ActivePerson); + this.FileRevision.CreatedOn = DateTime.UtcNow; + + var fileExtension = Path.GetExtension(file.Name); + var fileType = this.FileTypes.FirstOrDefault(x => $".{x.Extension}" == fileExtension); + + if (fileType != null) + { + this.FileRevision.FileType.Add(fileType); + } + } + + /// + /// Handles the refresh of the current + /// + /// A + protected override Task OnSessionRefreshed() => Task.CompletedTask; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// Value asserting if this component should dispose or not + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (Directory.Exists(this.UploadsDirectory)) + { + Directory.Delete(this.UploadsDirectory, true); + } + } + + /// + /// Calculates the hash of the file's content + /// + /// the path to the file + /// the hash of the content + private static string CalculateContentHash(string filePath) + { + if (filePath == null) + { + return null; + } + + using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + return StreamToHashComputer.CalculateSha1HashFromStream(fileStream); + } + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandler/IFileRevisionHandlerViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandler/IFileRevisionHandlerViewModel.cs new file mode 100644 index 00000000..866a9632 --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FileRevisionHandler/IFileRevisionHandlerViewModel.cs @@ -0,0 +1,85 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileRevisionHandler +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using COMET.Web.Common.ViewModels.Components.Applications; + + using Microsoft.AspNetCore.Components.Forms; + + /// + /// View model used to manage the files revisions in Filestores + /// + public interface IFileRevisionHandlerViewModel : IApplicationBaseViewModel + { + /// + /// Sets the file for the + /// + /// The to be set + /// + void InitializeViewModel(File file, FileStore fileStore); + + /// + /// Gets a collection of the selected + /// + IEnumerable SelectedFileTypes { get; } + + /// + /// The file revision that will be handled for both edit and add forms + /// + FileRevision FileRevision { get; set; } + + /// + /// Gets a collection of the available s + /// + IEnumerable FileTypes { get; } + + /// + /// Gets or sets the error message that is displayed in the component + /// + string ErrorMessage { get; } + + /// + /// Gets or sets the current + /// + File CurrentFile { get; } + + /// + /// Downloads a file revision + /// + /// the file revision + /// A + Task DownloadFileRevision(FileRevision fileRevision); + + /// + /// Uploads a file to server and creates a file revision + /// + /// The file to upload + /// A + Task UploadFile(IBrowserFile file); + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderFileStructureViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderFileStructureViewModel.cs new file mode 100644 index 00000000..070c3d1a --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderFileStructureViewModel.cs @@ -0,0 +1,197 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore +{ + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; + + using CDP4Dal; + + using COMET.Web.Common.Services.SessionManagement; + using COMET.Web.Common.ViewModels.Components.Applications; + + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler; + + /// + /// View model used to manage the folder file structure + /// + public class FolderFileStructureViewModel : ApplicationBaseViewModel, IFolderFileStructureViewModel + { + /// + /// Gets or sets the current + /// + private FileStore CurrentFileStore { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The + /// The + /// The + /// The + public FolderFileStructureViewModel(ISessionService sessionService, ICDPMessageBus messageBus, IFileHandlerViewModel fileHandlerViewModel, IFolderHandlerViewModel folderHandlerViewModel) + : base(sessionService, messageBus) + { + this.FileHandlerViewModel = fileHandlerViewModel; + this.FolderHandlerViewModel = folderHandlerViewModel; + this.InitializeSubscriptions([typeof(File), typeof(Folder)]); + } + + /// + /// Gets the + /// + public IFileHandlerViewModel FileHandlerViewModel { get; private set; } + + /// + /// Gets the + /// + public IFolderHandlerViewModel FolderHandlerViewModel { get; private set; } + + /// + /// The folder-file hierarchically structured + /// + public List Structure { get; set; } = []; + + /// + /// Initializes the current + /// + /// The to be set + public void InitializeViewModel(FileStore fileStore) + { + this.CurrentFileStore = fileStore; + this.FileHandlerViewModel.InitializeViewModel(this.CurrentFileStore); + this.FolderHandlerViewModel.InitializeViewModel(this.CurrentFileStore); + this.CreateStructureTree(); + } + + /// + /// Handles the refresh of the current + /// + /// A + protected override Task OnSessionRefreshed() + { + if (this.AddedThings.Count == 0 && this.UpdatedThings.Count == 0 && this.DeletedThings.Count == 0) + { + return Task.CompletedTask; + } + + this.IsLoading = true; + + var rootNode = this.Structure.First(); + var flatListOfNodes = rootNode.GetFlatListOfChildrenNodes(true).ToList(); + + foreach (var updatedThing in this.UpdatedThings) + { + var nodeToUpdate = flatListOfNodes.First(x => x.Thing?.Iid == updatedThing?.Iid); + var parentNode = GetContainingFolderNodeFromList(flatListOfNodes, updatedThing); + + rootNode.RemoveChildNode(nodeToUpdate); + nodeToUpdate.UpdateThing(updatedThing); + parentNode.Content.Add(nodeToUpdate); + } + + foreach (var nodeToDelete in this.DeletedThings.Select(deletedThing => flatListOfNodes.First(x => x.Thing?.Iid == deletedThing?.Iid))) + { + rootNode.RemoveChildNode(nodeToDelete); + } + + foreach (var addedThing in this.AddedThings) + { + var parentNode = GetContainingFolderNodeFromList(flatListOfNodes, addedThing); + var nodeToAdd = new FileFolderNodeViewModel(addedThing); + parentNode.Content.Add(nodeToAdd); + } + + this.ClearRecordedChanges(); + this.IsLoading = false; + + return Task.CompletedTask; + } + + /// + /// Creates the structure tree, present in + /// + private void CreateStructureTree() + { + this.Structure = []; + + var rootNode = new FileFolderNodeViewModel(); + this.LoadFolderContent(rootNode); + + this.Structure.Add(rootNode); + } + + /// + /// The method used to load a folder's content, including files and subfolders + /// + /// The folder node + private void LoadFolderContent(FileFolderNodeViewModel folderNode) + { + var nestedFiles = this.CurrentFileStore.File + .Where(x => x.CurrentContainingFolder?.Iid == folderNode.Thing?.Iid) + .Select(x => new FileFolderNodeViewModel(x)) + .ToList(); + + var nestedFolders = this.CurrentFileStore.Folder + .Where(x => x.ContainingFolder?.Iid == folderNode.Thing?.Iid) + .Select(x => new FileFolderNodeViewModel(x)) + .ToList(); + + folderNode.Content.AddRange(nestedFolders); + folderNode.Content.AddRange(nestedFiles); + + foreach (var nestedFolder in nestedFolders) + { + this.LoadFolderContent(nestedFolder); + } + } + + /// + /// Gets the containing folder node for a given node, search in a given flat list of nodes + /// + /// The collection of nodes to search for the containing folder node + /// The node for which the containing folder will be searched for + /// The containing folder node + private static FileFolderNodeViewModel GetContainingFolderNodeFromList(IEnumerable listOfNodes, Thing node) + { + FileFolderNodeViewModel parentNode; + + switch (node) + { + case File file: + parentNode = listOfNodes.First(x => x.Thing?.Iid == file.CurrentContainingFolder?.Iid); + break; + case Folder folder: + parentNode = listOfNodes.First(x => x.Thing?.Iid == folder.ContainingFolder?.Iid); + break; + default: + return null; + } + + return parentNode; + } + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderHandler/FolderHandlerViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderHandler/FolderHandlerViewModel.cs new file mode 100644 index 00000000..4b029404 --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderHandler/FolderHandlerViewModel.cs @@ -0,0 +1,159 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler +{ + using CDP4Common.CommonData; + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using CDP4Dal; + + using COMET.Web.Common.Services.SessionManagement; + using COMET.Web.Common.ViewModels.Components.Applications; + + /// + /// View model used to manage the folders in Filestores + /// + public class FolderHandlerViewModel : ApplicationBaseViewModel, IFolderHandlerViewModel + { + /// + /// Gets or sets the current + /// + private FileStore CurrentFileStore { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The + /// The + public FolderHandlerViewModel(ISessionService sessionService, ICDPMessageBus messageBus) : base(sessionService, messageBus) + { + } + + /// + /// Gets a collection of the available + /// + public IEnumerable DomainsOfExpertise { get; private set; } + + /// + /// Gets a collection of the available s + /// + public IEnumerable Folders { get; private set; } + + /// + /// Gets or sets the folder to be created/edited + /// + public Folder Folder { get; set; } = new(); + + /// + /// Initializes the current + /// + /// The to be set + public void InitializeViewModel(FileStore fileStore) + { + this.CurrentFileStore = fileStore; + this.DomainsOfExpertise = this.SessionService.GetSiteDirectory().Domain; + + var folders = this.CurrentFileStore.Folder.ToList(); + folders.Add(null); + this.Folders = folders; + } + + /// + /// Selects the current + /// + /// The folder to be set + public void SelectFolder(Folder folder) + { + this.Folder = folder.Clone(true); + } + + /// + /// Moves a folder to a target folder + /// + /// The folder to be moved + /// the target folders + /// A + public async Task MoveFolder(Folder folder, Folder targetFolder) + { + this.IsLoading = true; + + var folderClone = folder.Clone(true); + folderClone.ContainingFolder = targetFolder; + + await this.SessionService.UpdateThings(this.CurrentFileStore.Clone(true), folderClone); + await this.SessionService.RefreshSession(); + + this.IsLoading = false; + } + + /// + /// Creates or edits a folder + /// + /// the value to check if the should be created or edited + /// A + public async Task CreateOrEditFolder(bool shouldCreate) + { + this.IsLoading = true; + + var thingsToCreate = new List(); + var fileStoreClone = this.CurrentFileStore.Clone(false); + + if (shouldCreate) + { + var engineeringModel = this.CurrentFileStore.GetContainerOfType(); + this.Folder.Creator = engineeringModel.GetActiveParticipant(this.SessionService.Session.ActivePerson); + + fileStoreClone.Folder.Add(this.Folder); + thingsToCreate.Add(fileStoreClone); + } + + thingsToCreate.Add(this.Folder); + + await this.SessionService.UpdateThings(fileStoreClone, thingsToCreate); + await this.SessionService.RefreshSession(); + + this.IsLoading = false; + } + + /// + /// Deletes the current folder + /// + /// A + public async Task DeleteFolder() + { + var clonedContainer = this.Folder.Container.Clone(false); + + await this.SessionService.DeleteThing(clonedContainer, this.Folder); + await this.SessionService.RefreshSession(); + } + + /// + /// Handles the refresh of the current + /// + /// A + protected override Task OnSessionRefreshed() => Task.CompletedTask; + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderHandler/IFolderHandlerViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderHandler/IFolderHandlerViewModel.cs new file mode 100644 index 00000000..c1f8b5a5 --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/FolderHandler/IFolderHandlerViewModel.cs @@ -0,0 +1,85 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using COMET.Web.Common.ViewModels.Components.Applications; + + /// + /// View model used to manage the folders in Filestores + /// + public interface IFolderHandlerViewModel : IApplicationBaseViewModel + { + /// + /// Initializes the current + /// + /// The to be set + void InitializeViewModel(FileStore fileStore); + + /// + /// Gets a collection of the available + /// + IEnumerable DomainsOfExpertise { get; } + + /// + /// Gets or sets the folder to be created/edited + /// + Folder Folder { get; set; } + + /// + /// Gets a collection of the available s + /// + IEnumerable Folders { get; } + + /// + /// Selects the current + /// + /// The folder to be set + public void SelectFolder(Folder folder); + + /// + /// Creates or edits a folder + /// + /// the value to check if the should be created or edited + /// A + Task CreateOrEditFolder(bool shouldCreate); + + /// + /// Moves a folder to a target folder + /// + /// The folder to be moved + /// the target folders + /// A + Task MoveFolder(Folder folder, Folder targetFolder); + + /// + /// Deletes the current folder + /// + /// A + Task DeleteFolder(); + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/IFolderFileStructureViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/IFolderFileStructureViewModel.cs similarity index 74% rename from COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/IFolderFileStructureViewModel.cs rename to COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/IFolderFileStructureViewModel.cs index 082d3964..d4436d5a 100644 --- a/COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/IFolderFileStructureViewModel.cs +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/FileStore/IFolderFileStructureViewModel.cs @@ -22,15 +22,19 @@ // // -------------------------------------------------------------------------------------------------------------------- -namespace COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure +namespace COMETwebapp.ViewModels.Components.EngineeringModel.FileStore { using CDP4Common.EngineeringModelData; - using CDP4Common.SiteDirectoryData; + + using COMET.Web.Common.ViewModels.Components.Applications; + + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FileHandler; + using COMETwebapp.ViewModels.Components.EngineeringModel.FileStore.FolderHandler; /// /// View model used to manage the folder file structure /// - public interface IFolderFileStructureViewModel + public interface IFolderFileStructureViewModel : IApplicationBaseViewModel { /// /// Initializes the current @@ -44,23 +48,13 @@ public interface IFolderFileStructureViewModel List Structure { get; set; } /// - /// Gets or sets the file to be created/edited - /// - File File { get; set; } - - /// - /// Gets or sets the folder to be created/edited - /// - Folder Folder { get; set; } - - /// - /// Gets a collection of the available + /// Gets the /// - IEnumerable DomainsOfExpertise { get; } + IFileHandlerViewModel FileHandlerViewModel { get; } /// - /// Gets or sets the condition to check if the file or folder to be created is locked + /// Gets the /// - bool IsLocked { get; set; } + IFolderHandlerViewModel FolderHandlerViewModel { get; } } } diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/FolderFileStructureViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/FolderFileStructureViewModel.cs deleted file mode 100644 index 8504d417..00000000 --- a/COMETwebapp/ViewModels/Components/EngineeringModel/FolderFileStructure/FolderFileStructureViewModel.cs +++ /dev/null @@ -1,121 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2023-2024 RHEA System S.A. -// -// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua -// -// This file is part of CDP4-COMET WEB Community Edition -// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. -// -// The CDP4-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. -// -// The CDP4-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 . -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace COMETwebapp.ViewModels.Components.EngineeringModel.FolderFileStructure -{ - using CDP4Common.EngineeringModelData; - using CDP4Common.SiteDirectoryData; - - using COMET.Web.Common.Services.SessionManagement; - - /// - /// View model used to manage the folder file structure - /// - public class FolderFileStructureViewModel : IFolderFileStructureViewModel - { - /// - /// Gets or sets the current - /// - private FileStore CurrentFileStore { get; set; } - - /// - /// Gets or sets the - /// - private readonly ISessionService sessionService; - - /// - /// Initializes a new instance of the class. - /// - /// The - public FolderFileStructureViewModel(ISessionService sessionService) - { - this.sessionService = sessionService; - } - - /// - /// Gets a collection of the available - /// - public IEnumerable DomainsOfExpertise { get; private set; } - - /// - /// The folder-file hierarchically structured - /// - public List Structure { get; set; } = []; - - /// - /// Gets or sets the file to be created/edited - /// - public File File { get; set; } = new(); - - /// - /// Gets or sets the folder to be created/edited - /// - public Folder Folder { get; set; } = new(); - - /// - /// Gets or sets the condition to check if the file or folder to be created is locked - /// - public bool IsLocked { get; set; } - - /// - /// Initializes the current - /// - /// The to be set - public void InitializeViewModel(FileStore fileStore) - { - this.CurrentFileStore = fileStore; - this.DomainsOfExpertise = this.sessionService.GetSiteDirectory().Domain; - this.Structure = []; - - var rootNode = new FileFolderNodeViewModel(); - this.LoadFolderContent(rootNode); - this.Structure.Add(rootNode); - } - - /// - /// The method used to load a folder's content, including files and subfolders - /// - /// The folder node - private void LoadFolderContent(FileFolderNodeViewModel folderNode) - { - var nestedFiles = this.CurrentFileStore.File - .Where(x => x.CurrentContainingFolder?.Iid == folderNode.Thing?.Iid) - .Select(x => new FileFolderNodeViewModel(x)) - .ToList(); - - var nestedFolders = this.CurrentFileStore.Folder - .Where(x => x.ContainingFolder?.Iid == folderNode.Thing?.Iid) - .Select(x => new FileFolderNodeViewModel(x)) - .ToList(); - - folderNode.Content.AddRange(nestedFolders); - folderNode.Content.AddRange(nestedFiles); - - foreach (var nestedFolder in nestedFolders) - { - this.LoadFolderContent(nestedFolder); - } - } - } -} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/Rows/FileRevisionRowViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/Rows/FileRevisionRowViewModel.cs new file mode 100644 index 00000000..6a0ed94a --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/Rows/FileRevisionRowViewModel.cs @@ -0,0 +1,97 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.Rows +{ + using CDP4Common.EngineeringModelData; + + using COMETwebapp.ViewModels.Components.Common.Rows; + + using ReactiveUI; + + /// + /// Row View Model for + /// + public class FileRevisionRowViewModel : ReactiveObject + { + /// + /// The backing field for + /// + private string createdOn; + + /// + /// The backing field for + /// + private string path; + + /// + /// The backing field for + /// + private string name; + + /// + /// Initializes a new instance of the class. + /// + /// The associated + public FileRevisionRowViewModel(FileRevision fileRevision) + { + this.Thing = fileRevision; + this.CreatedOn = fileRevision.CreatedOn.ToString("dd/MM/yyyy HH:mm:ss"); + this.Path = fileRevision.Path; + this.Name = fileRevision.Name; + } + + /// + /// Gets or sets the current + /// + public FileRevision Thing { get; set; } + + /// + /// The date and time when the was created, as a + /// + public string CreatedOn + { + get => this.createdOn; + set => this.RaiseAndSetIfChanged(ref this.createdOn, value); + } + + /// + /// The path of the + /// + public string Path + { + get => this.path; + set => this.RaiseAndSetIfChanged(ref this.path, value); + } + + /// + /// The name of the + /// + public string Name + { + get => this.name; + set => this.RaiseAndSetIfChanged(ref this.name, value); + } + } +} diff --git a/COMETwebapp/ViewModels/Components/EngineeringModel/Rows/FileTypeRowViewModel.cs b/COMETwebapp/ViewModels/Components/EngineeringModel/Rows/FileTypeRowViewModel.cs new file mode 100644 index 00000000..78c34768 --- /dev/null +++ b/COMETwebapp/ViewModels/Components/EngineeringModel/Rows/FileTypeRowViewModel.cs @@ -0,0 +1,62 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2023-2024 RHEA System S.A. +// +// Authors: Sam Gerené, Alex Vorobiev, Alexander van Delft, Jaime Bernar, Antoine Théate, João Rua +// +// This file is part of CDP4-COMET WEB Community Edition +// The CDP4-COMET WEB Community Edition is the RHEA Web Application implementation of ECSS-E-TM-10-25 Annex A and Annex C. +// +// The CDP4-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. +// +// The CDP4-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 . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace COMETwebapp.ViewModels.Components.EngineeringModel.Rows +{ + using CDP4Common.EngineeringModelData; + using CDP4Common.SiteDirectoryData; + + using COMETwebapp.ViewModels.Components.Common.Rows; + + using ReactiveUI; + + /// + /// Row View Model for + /// + public class FileTypeRowViewModel : BaseDataItemRowViewModel + { + /// + /// The backing field for + /// + private string extension; + + /// + /// Initializes a new instance of the class. + /// + /// The associated + public FileTypeRowViewModel(FileType fileType) : base(fileType) + { + this.Extension = fileType.Extension; + } + + /// + /// The file type extension + /// + public string Extension + { + get => this.extension; + set => this.RaiseAndSetIfChanged(ref this.extension, value); + } + } +} diff --git a/COMETwebapp/appsettings.json b/COMETwebapp/appsettings.json index c966947d..28e33cb3 100644 --- a/COMETwebapp/appsettings.json +++ b/COMETwebapp/appsettings.json @@ -1,6 +1,7 @@ { "AllowedHosts": "*", "StringTablePath": "wwwroot/DefaultTextConfiguration.json", + "MaxUploadFileSizeInMb": 500, "ServerConfiguration": { "ServerAddress": "", "BookInputConfiguration": { diff --git a/COMETwebapp/wwwroot/Scripts/utilities.js b/COMETwebapp/wwwroot/Scripts/utilities.js new file mode 100644 index 00000000..7dd68a94 --- /dev/null +++ b/COMETwebapp/wwwroot/Scripts/utilities.js @@ -0,0 +1,20 @@ +/** + * Downloads a given file by its name + * @param {string} fileName + * @param {any} contentStreamReference + */ +async function DownloadFileFromStream(fileName, contentStreamReference) { + const arrayBuffer = await contentStreamReference.arrayBuffer(); + const blob = new Blob([arrayBuffer], { type: 'text/plain;charset=utf-8' }); + + const url = URL.createObjectURL(blob); + + const anchorElement = document.createElement('a'); + + anchorElement.href = url; + anchorElement.download = fileName ?? ''; + anchorElement.target = "_blank"; + anchorElement.click(); + anchorElement.remove(); + URL.revokeObjectURL(url); +} \ No newline at end of file