Skip to content

Commit

Permalink
Feat/process tasks (#353)
Browse files Browse the repository at this point in the history
* feat: add ProcessTasks to AppProcessState

* wip: add test for process controller

* feat: add ProcessTasks to AppProcessState

* wip: add test for process controller

* Adding first test for processcontroller

* Fix bad case for roles folder in testsetup

* User ArgumentNullException.ThrowIfNull instead of custom method

* Fix codesmells

---------

Co-authored-by: Vemund Gaukstad <tjololo@users.noreply.github.com>
  • Loading branch information
mikaelrss and tjololo authored Nov 30, 2023
1 parent 118723c commit a088662
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 22 deletions.
12 changes: 12 additions & 0 deletions src/Altinn.App.Api/Controllers/ProcessController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,18 @@ private async Task<AppProcessState> ConvertAndAuthorizeActions(Instance instance
appProcessState.CurrentTask.UserActions = authDecisions;
}
}

var processTasks = new List<AppProcessTaskTypeInfo>();
foreach (var processElement in _processReader.GetAllFlowElements().OfType<ProcessTask>())
{
processTasks.Add(new AppProcessTaskTypeInfo
{
ElementId = processElement.Id,
AltinnTaskType = processElement.ExtensionElements?.TaskExtension?.TaskType
});
}

appProcessState.ProcessTasks = processTasks;

return appProcessState;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public AppProcessState(ProcessState? processState)
{
return;
}

Started = processState.Started;
StartEvent = processState.StartEvent;
if (processState.CurrentTask != null)
Expand All @@ -35,10 +34,16 @@ public AppProcessState(ProcessState? processState)
}
Ended = processState.Ended;
EndEvent = processState.EndEvent;

}

/// <summary>
/// Gets or sets a status object containing the task info of the currentTask of an ongoing process.
/// </summary>
public new AppProcessElementInfo? CurrentTask { get; set; }

/// <summary>
/// Gets or sets a list of all tasks. The list contains information about the task Id
/// and the task type.
/// </summary>
public List<AppProcessTaskTypeInfo>? ProcessTasks { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#nullable enable
using System.Xml.Serialization;
using System.Text.Json.Serialization;

namespace Altinn.App.Core.Internal.Process.Elements;

/// <summary>
/// Representation of a task's id and type. Used by the frontend to determine which tasks
/// exist, and their type.
/// </summary>
public class AppProcessTaskTypeInfo
{
/// <summary>
/// Gets or sets the task type
/// </summary>
[XmlElement("altinnTaskType", Namespace = "http://altinn.no/process")]
public string? AltinnTaskType { get; set; }


/// <summary>
/// Gets or sets a reference to the current task/event element id as given in the process definition.
/// </summary>
[JsonPropertyName(name: "elementId")]
public string? ElementId { get; set; }
}
6 changes: 5 additions & 1 deletion src/Altinn.App.Core/Internal/Process/IProcessReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ namespace Altinn.App.Core.Internal.Process;
/// </summary>
public interface IProcessReader
{

/// <summary>
/// Get all defined StartEvents in the process
/// </summary>
Expand Down Expand Up @@ -111,4 +110,9 @@ public interface IProcessReader
/// <returns><see cref="ProcessElement"/> or null</returns>
public ProcessElement? GetFlowElement(string? elementId);

/// <summary>
/// Returns all available ProcessElements
/// </summary>
/// <returns><see cref="ProcessElement"/></returns>
public List<ProcessElement> GetAllFlowElements();
}
13 changes: 4 additions & 9 deletions src/Altinn.App.Core/Internal/Process/ProcessReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public List<string> GetSequenceFlowIds()
/// <inheritdoc />
public ProcessElement? GetFlowElement(string? elementId)
{
EnsureArgumentNotNull(elementId, nameof(elementId));
ArgumentNullException.ThrowIfNull(elementId);

ProcessTask? task = _definitions.Process.Tasks.Find(t => t.Id == elementId);
if (task != null)
Expand All @@ -131,7 +131,7 @@ public List<string> GetSequenceFlowIds()
/// <inheritdoc />
public List<ProcessElement> GetNextElements(string? currentElementId)
{
EnsureArgumentNotNull(currentElementId, nameof(currentElementId));
ArgumentNullException.ThrowIfNull(currentElementId);
List<ProcessElement> nextElements = new List<ProcessElement>();
List<ProcessElement> allElements = GetAllFlowElements();
if (!allElements.Exists(e => e.Id == currentElementId))
Expand All @@ -158,13 +158,8 @@ public List<SequenceFlow> GetOutgoingSequenceFlows(ProcessElement? flowElement)
return GetSequenceFlows().FindAll(sf => flowElement.Outgoing.Contains(sf.Id)).ToList();
}

private static void EnsureArgumentNotNull(object? argument, string paramName)
{
if (argument == null)
throw new ArgumentNullException(paramName);
}

private List<ProcessElement> GetAllFlowElements()
/// <inheritdoc />
public List<ProcessElement> GetAllFlowElements()
{
List<ProcessElement> flowElements = new List<ProcessElement>();
flowElements.AddRange(GetStartEvents());
Expand Down
96 changes: 96 additions & 0 deletions test/Altinn.App.Api.Tests/Controllers/ProcessControllerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System.Net;
using System.Net.Http.Headers;
using System.Text.Json;
using Altinn.App.Api.Models;
using Altinn.App.Api.Tests.Data;
using Altinn.App.Api.Tests.Utils;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using Xunit;

namespace Altinn.App.Api.Tests.Controllers;

public class ProcessControllerTests : ApiTestBase, IClassFixture<WebApplicationFactory<Program>>
{
public ProcessControllerTests(WebApplicationFactory<Program> factory) : base(factory)
{
}

[Fact]
public async Task Get_ShouldReturnProcessTasks()
{
string org = "tdd";
string app = "contributer-restriction";
int partyId = 500000;
Guid instanceId = new Guid("5d9e906b-83ed-44df-85a7-2f104c640bff");
HttpClient client = GetRootedClient(org, app);

TestData.DeleteInstance(org, app, partyId, instanceId);
TestData.PrepareInstance(org, app, partyId, instanceId);

string token = PrincipalUtil.GetToken(1337, 500000, 3);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

string url = $"/{org}/{app}/instances/{partyId}/{instanceId}/process";
HttpResponseMessage response = await client.GetAsync(url);
TestData.DeleteInstance(org, app, partyId, instanceId);

response.StatusCode.Should().Be(HttpStatusCode.OK);
var content = await response.Content.ReadAsStringAsync();
var expectedString = """
{
"currentTask": {
"actions": {
"read": true,
"write": true
},
"userActions": [
{
"id": "read",
"authorized": true,
"type": "ProcessAction"
},
{
"id": "write",
"authorized": true,
"type": "ProcessAction"
}
],
"read": true,
"write": true,
"flow": 2,
"started": "2019-12-05T13:24:34.9196661Z",
"elementId": "Task_1",
"name": "Utfylling",
"altinnTaskType": "data",
"ended": null,
"validated": {
"timestamp": "2020-02-07T10:46:36.985894+01:00",
"canCompleteTask": false
},
"flowType": null
},
"processTasks": [
{
"altinnTaskType": "data",
"elementId": "Task_1"
}
],
"started": "2019-12-05T13:24:34.8412179Z",
"startEvent": "StartEvent_1",
"ended": null,
"endEvent": null
}
""";
CompareResult<AppProcessState>(expectedString, content);
}


//TODO: replace this assertion with a proper one once fluentassertions has a json compare feature scheduled for v7 https://github.com/fluentassertions/fluentassertions/issues/2205
private static void CompareResult<T>(string expectedString, string actualString)
{
T? expected = JsonSerializer.Deserialize<T>(expectedString);
T? actual = JsonSerializer.Deserialize<T>(actualString);
actual.Should().BeEquivalentTo(expected);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"id": "500000/5d9e906b-83ed-44df-85a7-2f104c640bff",
"instanceOwner": {
"partyId": "500000",
"personNumber": "01039012345"
},
"appId": "tdd/contributer-restriction",
"org": "tdd",
"process": {
"started": "2019-12-05T13:24:34.8412179Z",
"startEvent": "StartEvent_1",
"currentTask": {
"flow": 2,
"started": "2019-12-05T13:24:34.9196661Z",
"elementId": "Task_1",
"name": "Utfylling",
"altinnTaskType": "data",
"validated": {
"timestamp": "2020-02-07T10:46:36.985894+01:00",
"canCompleteTask": false
}
}
},
"status": {
"isArchived": false,
"isSoftDeleted": false,
"isHardDeleted": false,
"readStatus": "Read"
},
"data": [
{
"id": "de288942-a8af-4f77-a1f1-6e1ede1cd502",
"dataType": "default",
"contentType": "application/xml",
"size": 0,
"locked": false
}
]
}
2 changes: 1 addition & 1 deletion test/Altinn.App.Api.Tests/Data/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static string GetDataBlobPath(string org, string app, int instanceOwnerId
public static string GetTestDataRolesFolder(int userId, int resourcePartyId)
{
string testDataDirectory = GetTestDataRootDirectory();
return Path.Combine(testDataDirectory, @"authorization/Roles/User_" + userId, "party_" + resourcePartyId, "roles.json");
return Path.Combine(testDataDirectory, "authorization","roles", "User_" + userId, "party_" + resourcePartyId, "roles.json");
}

public static string GetAltinnAppsPolicyPath(string org, string app)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions id="Altinn_SingleDataTask_Process_Definition"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:altinn="http://altinn.no"
xmlns:altinn="http://altinn.no/process"
xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
Expand All @@ -11,9 +11,14 @@ targetNamespace="http://bpmn.io/schema/bpmn" >
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>SequenceFlow_1n56yn5</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:task id="Task_1" name="Utfylling" altinn:tasktype="data">
<bpmn:task id="Task_1" name="Utfylling">
<bpmn:incoming>SequenceFlow_1n56yn5</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_1oot28q</bpmn:outgoing>
<bpmn:extensionElements>
<altinn:taskExtension>
<altinn:taskType>data</altinn:taskType>
</altinn:taskExtension>
</bpmn:extensionElements>
</bpmn:task>
<bpmn:endEvent id="EndEvent_1">
<bpmn:incoming>SequenceFlow_1oot28q</bpmn:incoming>
Expand Down
18 changes: 16 additions & 2 deletions test/Altinn.App.Api.Tests/Mocks/AuthorizationMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,23 @@ public async Task<bool> AuthorizeAction(AppIdentifier appIdentifier, InstanceIde
return true;
}

public Task<Dictionary<string, bool>> AuthorizeActions(Instance instance, ClaimsPrincipal user, List<string> actions)
public async Task<Dictionary<string, bool>> AuthorizeActions(Instance instance, ClaimsPrincipal user, List<string> actions)
{
throw new NotImplementedException();
await Task.CompletedTask;
Dictionary<string, bool> authorizedActions = new Dictionary<string, bool>();
foreach (var action in actions)
{
if(action.EndsWith("_unauthorized"))
{
authorizedActions.Add(action, false);
}
else
{
authorizedActions.Add(action, true);
}
}

return authorizedActions;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
using Prometheus;
using Xunit;

namespace Altinn.App.Core.Tests.InfrastrucZture.Clients.Storage;
namespace Altinn.App.Core.Tests.Infrastructure.Clients.Storage;

public class InstanceClientMetricsDecoratorTests
{
Expand Down Expand Up @@ -192,16 +192,12 @@ public async Task GetInstance_instance_calls_decorated_service()
// Arrange
var instanceClient = new Mock<IInstanceClient>();
var instanceClientMetricsDecorator = new InstanceClientMetricsDecorator(instanceClient.Object);
var preUpdateMetrics = await PrometheusTestHelper.ReadPrometheusMetricsToString();
var instance = new Instance();

// Act
await instanceClientMetricsDecorator.GetInstance(instance);
var postUpdateMetrics = await PrometheusTestHelper.ReadPrometheusMetricsToString();

// Assert
var diff = GetDiff(preUpdateMetrics, postUpdateMetrics);
diff.Should().BeEmpty();
instanceClient.Verify(i => i.GetInstance(instance));
instanceClient.VerifyNoOtherCalls();
}
Expand Down

0 comments on commit a088662

Please sign in to comment.