From b83085e4e3870f2d054cc6fe4c25a40712edf3b4 Mon Sep 17 00:00:00 2001 From: Nina Kylstad Date: Thu, 7 Dec 2023 10:22:56 +0100 Subject: [PATCH 1/5] Endpoint for updating task name --- .../ProcessModeling/ProcessModelingService.cs | 37 +++++++++++++++- .../Interfaces/IProcessModelingService.cs | 10 +++++ .../Services/ProcessModelingServiceTests.cs | 34 +++++++++++++++ .../App/config/process/process.bpmn | 42 +++++++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-process/App/config/process/process.bpmn diff --git a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs index be1a47188df..c9348cc9b84 100644 --- a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs +++ b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -8,6 +9,9 @@ using Altinn.Studio.Designer.Models; using Altinn.Studio.Designer.Services.Interfaces; +using Altinn.App.Core.Internal.Process.Elements; +using System.Xml.Serialization; + namespace Altinn.Studio.Designer.Services.Implementation.ProcessModeling { public class ProcessModelingService : IProcessModelingService @@ -52,6 +56,37 @@ public Stream GetProcessDefinitionStream(AltinnRepoEditingContext altinnRepoEdit return altinnAppGitRepository.GetProcessDefinitionFile(); } + public async Task UpdateProcessTaskNameAsync(AltinnRepoEditingContext altinnRepoEditingContext, string taskId, string taskName, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + AltinnAppGitRepository altinnAppGitRepository = _altinnGitRepositoryFactory.GetAltinnAppGitRepository(altinnRepoEditingContext.Org, altinnRepoEditingContext.Repo, altinnRepoEditingContext.Developer); + XmlSerializer serializer = new(typeof(Definitions)); + Definitions? definitions; + using (Stream processDefinitionStream = GetProcessDefinitionStream(altinnRepoEditingContext)) + { + definitions = (Definitions?)serializer.Deserialize(processDefinitionStream); + } + + if (definitions == null) + { + throw new InvalidOperationException("Could not deserialize process definition."); + } + + ProcessTask? processTask = (definitions.Process.Tasks?.FirstOrDefault(t => t.Id == taskId)) ?? throw new ArgumentException($"Could not find task with id {taskId}."); + processTask.Name = taskName; + + Stream processStream = new MemoryStream(); + serializer.Serialize(processStream, definitions); + + // Reset stream position to beginning after serialization + processStream.Seek(0, SeekOrigin.Begin); + await altinnAppGitRepository.SaveProcessDefinitionFileAsync(processStream, cancellationToken); + + // Reset stream position to beginning after saving + processStream.Seek(0, SeekOrigin.Begin); + return processStream; + } + private IEnumerable EnumerateTemplateResources(Version version) { return typeof(ProcessModelingService).Assembly.GetManifestResourceNames() diff --git a/backend/src/Designer/Services/Interfaces/IProcessModelingService.cs b/backend/src/Designer/Services/Interfaces/IProcessModelingService.cs index 490adac1a71..db1a533a6b0 100644 --- a/backend/src/Designer/Services/Interfaces/IProcessModelingService.cs +++ b/backend/src/Designer/Services/Interfaces/IProcessModelingService.cs @@ -39,5 +39,15 @@ public interface IProcessModelingService /// An . /// A of a process definition file. Stream GetProcessDefinitionStream(AltinnRepoEditingContext altinnRepoEditingContext); + + /// + /// Updates the name of a task in the process definition file. + /// + /// n . + /// The ID of the task to update + /// The name to set for the task + /// A that observes if operation is cancelled. + /// + Task UpdateProcessTaskNameAsync(AltinnRepoEditingContext altinnRepoEditingContext, string taskId, string taskName, CancellationToken cancellationToken = default); } } diff --git a/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs b/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs index 2e2a3930aa4..a1ae446e3a5 100644 --- a/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs +++ b/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs @@ -1,8 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Xml.Serialization; +using Altinn.App.Core.Internal.Process.Elements; +using Altinn.Studio.Designer.Factories; +using Altinn.Studio.Designer.Models; using Altinn.Studio.Designer.Services.Implementation.ProcessModeling; using Altinn.Studio.Designer.Services.Interfaces; +using Designer.Tests.Utils; using FluentAssertions; using Moq; using SharedResources.Tests; @@ -30,6 +36,34 @@ public void GetProcessDefinitionTemplates_GivenVersion_ReturnsListOfTemplates(st } } + [Theory] + [InlineData("ttd", "app-with-process", "testUser", "Task_1", "NewTaskName", "Utfylling")] + public async void UpdateProcessTaskNameAsync_GivenTaskIdAndTaskName_UpdatesTaskName(string org, string repo, string developer, string taskId, string newTaskName, string oldTaskName) + { + // Arrange + string targetRepository = TestDataHelper.GenerateTestRepoName(); + var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, targetRepository, developer); + await TestDataHelper.CopyRepositoryForTest(org, repo, developer, targetRepository); + + try + { + var altinnGitRepositoryFactory = new AltinnGitRepositoryFactory(TestDataHelper.GetTestDataRepositoriesRootDirectory()); + IProcessModelingService processModelingService = new ProcessModelingService(altinnGitRepositoryFactory); + + // Act + using Stream result = await processModelingService.UpdateProcessTaskNameAsync(editingContext, taskId, newTaskName); + XmlSerializer serializer = new(typeof(Definitions)); + Definitions definitions = (Definitions)serializer.Deserialize(result); + + // Assert + definitions.Process.Tasks.First(t => t.Id == taskId).Name.Should().Be(newTaskName); + } + finally + { + TestDataHelper.DeleteAppRepository(org, targetRepository, developer); + } + } + public static IEnumerable TemplatesTestData => new List { new object[] diff --git a/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-process/App/config/process/process.bpmn b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-process/App/config/process/process.bpmn new file mode 100644 index 00000000000..48ce80fb026 --- /dev/null +++ b/backend/tests/Designer.Tests/_TestData/Repositories/testUser/ttd/app-with-process/App/config/process/process.bpmn @@ -0,0 +1,42 @@ + + + + SequenceFlow_1n56yn5 + + + Flow_1htunjv + + + + + + data + + + SequenceFlow_1n56yn5 + Flow_1htunjv + + + + + + + + + + + + + + + + + + + + + + + + + From f157e5783d75804cbefc2c806e02f26f3fcf874a Mon Sep 17 00:00:00 2001 From: Nina Kylstad Date: Thu, 7 Dec 2023 10:26:24 +0100 Subject: [PATCH 2/5] Added the actual endpoint --- .../Designer/Controllers/ProcessModelingController.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/src/Designer/Controllers/ProcessModelingController.cs b/backend/src/Designer/Controllers/ProcessModelingController.cs index 7a6031b0c99..fcd74b27df4 100644 --- a/backend/src/Designer/Controllers/ProcessModelingController.cs +++ b/backend/src/Designer/Controllers/ProcessModelingController.cs @@ -73,5 +73,14 @@ public async Task SaveProcessDefinitionFromTemplate(string org Stream processDefinitionStream = _processModelingService.GetProcessDefinitionStream(editingContext); return new FileStreamResult(processDefinitionStream, MediaTypeNames.Text.Plain); } + + [HttpPut("tasks/{taskId}/{taskName}")] + public async Task UpdateProcessTaskName(string org, string repo, string taskId, string taskName, CancellationToken cancellationToken) + { + string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); + var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer); + using Stream updatedProcessDefinitionStream = await _processModelingService.UpdateProcessTaskNameAsync(editingContext, taskId, taskName, cancellationToken); + return new FileStreamResult(updatedProcessDefinitionStream, MediaTypeNames.Text.Plain); + } } } From 2ae8edc6dde1cb84ef57da29a7b3ac2644ffb3a3 Mon Sep 17 00:00:00 2001 From: Nina Kylstad Date: Thu, 7 Dec 2023 11:02:53 +0100 Subject: [PATCH 3/5] exception handling and update tests --- .../Controllers/ProcessModelingController.cs | 19 +++++-- .../UpdateProcessTaskNameTests.cs | 54 +++++++++++++++++++ .../Services/ProcessModelingServiceTests.cs | 4 +- 3 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs diff --git a/backend/src/Designer/Controllers/ProcessModelingController.cs b/backend/src/Designer/Controllers/ProcessModelingController.cs index fcd74b27df4..6f5f9a18c93 100644 --- a/backend/src/Designer/Controllers/ProcessModelingController.cs +++ b/backend/src/Designer/Controllers/ProcessModelingController.cs @@ -75,12 +75,25 @@ public async Task SaveProcessDefinitionFromTemplate(string org } [HttpPut("tasks/{taskId}/{taskName}")] - public async Task UpdateProcessTaskName(string org, string repo, string taskId, string taskName, CancellationToken cancellationToken) + public async Task UpdateProcessTaskName(string org, string repo, string taskId, string taskName, CancellationToken cancellationToken) { + Guard.AssertArgumentNotNull(taskId, nameof(taskId)); + Guard.AssertArgumentNotNull(taskName, nameof(taskName)); string developer = AuthenticationHelper.GetDeveloperUserName(HttpContext); var editingContext = AltinnRepoEditingContext.FromOrgRepoDeveloper(org, repo, developer); - using Stream updatedProcessDefinitionStream = await _processModelingService.UpdateProcessTaskNameAsync(editingContext, taskId, taskName, cancellationToken); - return new FileStreamResult(updatedProcessDefinitionStream, MediaTypeNames.Text.Plain); + try + { + Stream updatedProcessDefinitionStream = await _processModelingService.UpdateProcessTaskNameAsync(editingContext, taskId, taskName, cancellationToken); + return new FileStreamResult(updatedProcessDefinitionStream, MediaTypeNames.Text.Plain); + } + catch (InvalidOperationException) + { + return BadRequest("Could not deserialize process definition."); + } + catch (ArgumentException) + { + return BadRequest("Could not find task with given id."); + } } } } diff --git a/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs b/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs new file mode 100644 index 00000000000..01133691277 --- /dev/null +++ b/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs @@ -0,0 +1,54 @@ +using System.Net; +using System.Net.Http; +using System.Net.Mime; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Designer.Tests.Controllers.ApiTests; +using Designer.Tests.Utils; +using FluentAssertions; +using Microsoft.AspNetCore.Mvc.Testing; +using Xunit; + +namespace Designer.Tests.Controllers.ProcessModelingController +{ + public class UpdateProcessTaskName : DisagnerEndpointsTestsBase, IClassFixture> + { + private static string Url(string org, string repository, string taskId, string taskName) => $"/designer/api/{org}/{repository}/process-modelling/tasks/{taskId}/{taskName}"; + + public UpdateProcessTaskName(WebApplicationFactory factory) : base(factory) + { + } + + [Theory] + [InlineData("ttd", "app-with-process", "testUser", "Task_1", "NewTaskName")] + public async Task UpdateProcessTaskName_ShouldReturnUpdatedProcess(string org, string app, string developer, string taskId, string taskName) + { + string targetRepository = TestDataHelper.GenerateTestRepoName(); + await CopyRepositoryForTest(org, app, developer, targetRepository); + + string url = Url(org, targetRepository, taskId, taskName); + + using var response = await HttpClient.PutAsync(url, null); + response.StatusCode.Should().Be(HttpStatusCode.OK); + + string responseContent = await response.Content.ReadAsStringAsync(); + + responseContent.Should().NotBeNullOrEmpty(); + responseContent.Should().Contain(taskName); + } + + [Theory] + [InlineData("ttd", "app-with-process", "testUser", "Does_not_exist", "NewTaskName")] + public async Task InvalidTaskId_ShouldReturnBadRequest(string org, string app, string developer, string taskId, string taskName) + { + string targetRepository = TestDataHelper.GenerateTestRepoName(); + await CopyRepositoryForTest(org, app, developer, targetRepository); + + string url = Url(org, targetRepository, taskId, taskName); + + using var response = await HttpClient.PutAsync(url, null); + response.StatusCode.Should().Be(HttpStatusCode.BadRequest); + } + } +} diff --git a/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs b/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs index a1ae446e3a5..b9e0ae1252c 100644 --- a/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs +++ b/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs @@ -37,8 +37,8 @@ public void GetProcessDefinitionTemplates_GivenVersion_ReturnsListOfTemplates(st } [Theory] - [InlineData("ttd", "app-with-process", "testUser", "Task_1", "NewTaskName", "Utfylling")] - public async void UpdateProcessTaskNameAsync_GivenTaskIdAndTaskName_UpdatesTaskName(string org, string repo, string developer, string taskId, string newTaskName, string oldTaskName) + [InlineData("ttd", "app-with-process", "testUser", "Task_1", "NewTaskName")] + public async void UpdateProcessTaskNameAsync_GivenTaskIdAndTaskName_UpdatesTaskName(string org, string repo, string developer, string taskId, string newTaskName) { // Arrange string targetRepository = TestDataHelper.GenerateTestRepoName(); From 27e7823c69c3e84c02549916e551559fb602d15d Mon Sep 17 00:00:00 2001 From: Nina Kylstad Date: Thu, 7 Dec 2023 11:20:02 +0100 Subject: [PATCH 4/5] Format changes --- .../Implementation/ProcessModeling/ProcessModelingService.cs | 3 +-- .../ProcessModelingController/UpdateProcessTaskNameTests.cs | 2 +- .../Designer.Tests/Services/ProcessModelingServiceTests.cs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs index c9348cc9b84..f050f928e93 100644 --- a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs +++ b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs @@ -1,5 +1,4 @@ -#nullable enable -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs b/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs index 01133691277..f2ae3fb0fa2 100644 --- a/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs +++ b/backend/tests/Designer.Tests/Controllers/ProcessModelingController/UpdateProcessTaskNameTests.cs @@ -40,7 +40,7 @@ public async Task UpdateProcessTaskName_ShouldReturnUpdatedProcess(string org, s [Theory] [InlineData("ttd", "app-with-process", "testUser", "Does_not_exist", "NewTaskName")] - public async Task InvalidTaskId_ShouldReturnBadRequest(string org, string app, string developer, string taskId, string taskName) + public async Task InvalidTaskId_ShouldReturnBadRequest(string org, string app, string developer, string taskId, string taskName) { string targetRepository = TestDataHelper.GenerateTestRepoName(); await CopyRepositoryForTest(org, app, developer, targetRepository); diff --git a/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs b/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs index b9e0ae1252c..8a0e04bdaae 100644 --- a/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs +++ b/backend/tests/Designer.Tests/Services/ProcessModelingServiceTests.cs @@ -38,7 +38,7 @@ public void GetProcessDefinitionTemplates_GivenVersion_ReturnsListOfTemplates(st [Theory] [InlineData("ttd", "app-with-process", "testUser", "Task_1", "NewTaskName")] - public async void UpdateProcessTaskNameAsync_GivenTaskIdAndTaskName_UpdatesTaskName(string org, string repo, string developer, string taskId, string newTaskName) + public async void UpdateProcessTaskNameAsync_GivenTaskIdAndTaskName_UpdatesTaskName(string org, string repo, string developer, string taskId, string newTaskName) { // Arrange string targetRepository = TestDataHelper.GenerateTestRepoName(); From 8bbef5d96347e55fab28f4220fdd5aafced5208a Mon Sep 17 00:00:00 2001 From: Nina Kylstad Date: Thu, 7 Dec 2023 11:49:30 +0100 Subject: [PATCH 5/5] imports ordering --- .../ProcessModeling/ProcessModelingService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs index f050f928e93..df29ba7aea6 100644 --- a/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs +++ b/backend/src/Designer/Services/Implementation/ProcessModeling/ProcessModelingService.cs @@ -1,16 +1,16 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.Xml.Serialization; +using Altinn.App.Core.Internal.Process.Elements; using Altinn.Studio.Designer.Infrastructure.GitRepository; using Altinn.Studio.Designer.Models; using Altinn.Studio.Designer.Services.Interfaces; -using Altinn.App.Core.Internal.Process.Elements; -using System.Xml.Serialization; - namespace Altinn.Studio.Designer.Services.Implementation.ProcessModeling { public class ProcessModelingService : IProcessModelingService