diff --git a/src/Controllers/Storage/ProcessController.cs b/src/Controllers/Storage/ProcessController.cs
index 52d1b662..a778d500 100644
--- a/src/Controllers/Storage/ProcessController.cs
+++ b/src/Controllers/Storage/ProcessController.cs
@@ -21,6 +21,7 @@ public class ProcessController : ControllerBase
{
private readonly IInstanceRepository _instanceRepository;
private readonly IInstanceEventRepository _instanceEventRepository;
+ private readonly IInstanceAndEventsRepository _instanceAndEventsRepository;
private readonly string _storageBaseAndHost;
private readonly IAuthorization _authorizationService;
private readonly IInstanceEventService _instanceEventService;
@@ -30,18 +31,21 @@ public class ProcessController : ControllerBase
///
/// the instance repository handler
/// the instance event repository service
+ /// the instance and events repository
/// the general settings
/// the authorization service
/// the instance event service
public ProcessController(
IInstanceRepository instanceRepository,
IInstanceEventRepository instanceEventRepository,
+ IInstanceAndEventsRepository instanceAndEventsRepository,
IOptions generalsettings,
IAuthorization authorizationService,
IInstanceEventService instanceEventService)
{
_instanceRepository = instanceRepository;
_instanceEventRepository = instanceEventRepository;
+ _instanceAndEventsRepository = instanceAndEventsRepository;
_storageBaseAndHost = $"{generalsettings.Value.Hostname}/storage/api/v1/";
_authorizationService = authorizationService;
_instanceEventService = instanceEventService;
@@ -63,72 +67,94 @@ public ProcessController(
public async Task> PutProcess(
int instanceOwnerPartyId,
Guid instanceGuid,
- [FromBody] ProcessState processState
- )
+ [FromBody] ProcessState processState)
{
- Instance existingInstance = await _instanceRepository.GetOne(
- instanceOwnerPartyId,
- instanceGuid
- );
+ Instance existingInstance = await _instanceRepository.GetOne(instanceOwnerPartyId, instanceGuid);
if (existingInstance is null)
{
return NotFound();
}
- string taskId = null;
- string altinnTaskType = existingInstance.Process?.CurrentTask?.AltinnTaskType;
+ var (action, taskId) = ActionMapping(processState, existingInstance);
- if (processState?.CurrentTask?.FlowType == "AbandonCurrentMoveToNext")
- {
- altinnTaskType = "reject";
- }
- else if (
- processState?.CurrentTask?.FlowType is not null
- && processState.CurrentTask.FlowType != "CompleteCurrentMoveToNext"
- )
+ bool authorized = await _authorizationService.AuthorizeInstanceAction(existingInstance, action, taskId);
+
+ if (!authorized)
{
- altinnTaskType = processState.CurrentTask.AltinnTaskType;
- taskId = processState.CurrentTask.ElementId;
+ return Forbid();
}
- string action = altinnTaskType switch
+ UpdateInstance(existingInstance, processState, out _);
+
+ Instance updatedInstance = await _instanceRepository.Update(existingInstance);
+
+ if (processState?.CurrentTask?.AltinnTaskType == "signing")
{
- "data" or "feedback" => "write",
- "payment" => "pay",
- "confirmation" => "confirm",
- "signing" => "sign",
- _ => altinnTaskType,
- };
+ await _instanceEventService.DispatchEvent(InstanceEventType.SentToSign, updatedInstance);
+ }
- bool authorized = await _authorizationService.AuthorizeInstanceAction(existingInstance, action, taskId);
+ updatedInstance.SetPlatformSelfLinks(_storageBaseAndHost);
+ return Ok(updatedInstance);
+ }
+
+ ///
+ /// Updates the process state of an instance.
+ ///
+ /// The party id of the instance owner.
+ /// The id of the instance that should have its process updated.
+ /// The new process state of the instance (including instance events).
+ ///
+ [Authorize]
+ [HttpPut("instanceandevents")]
+ [Consumes("application/json")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [Produces("application/json")]
+ public async Task> PutInstanceAndEvents(
+ int instanceOwnerPartyId,
+ Guid instanceGuid,
+ [FromBody] ProcessStateUpdate processStateUpdate)
+ {
+ Instance existingInstance = await _instanceRepository.GetOne(instanceOwnerPartyId, instanceGuid);
- if (!authorized)
+ if (existingInstance is null)
{
- return Forbid();
+ return NotFound();
}
- // Archiving instance if process was ended
- if (existingInstance.Process?.Ended is null && processState?.Ended is not null)
+ foreach (var instanceEvent in processStateUpdate.Events ?? [])
{
- existingInstance.Status ??= new InstanceStatus();
- existingInstance.Status.IsArchived = true;
- existingInstance.Status.Archived = processState.Ended;
+ if (string.IsNullOrWhiteSpace(instanceEvent.InstanceId))
+ {
+ return BadRequest("Missing parameter values: instance event must exist and instanceId must be set");
+ }
+
+ instanceEvent.Created = instanceEvent.Created?.ToUniversalTime() ?? DateTime.UtcNow;
}
- existingInstance.Process = processState;
- existingInstance.LastChangedBy = User.GetUserOrOrgId();
- existingInstance.LastChanged = DateTime.UtcNow;
+ ProcessState processState = processStateUpdate.State;
- Instance updatedInstance;
+ var (action, taskId) = ActionMapping(processState, existingInstance);
- updatedInstance = await _instanceRepository.Update(existingInstance);
+ bool authorized = await _authorizationService.AuthorizeInstanceAction(existingInstance, action, taskId);
+ if (!authorized)
+ {
+ return Forbid();
+ }
+
+ processStateUpdate.Events ??= [];
+ UpdateInstance(existingInstance, processState, out var updateProperties);
if (processState?.CurrentTask?.AltinnTaskType == "signing")
{
- await _instanceEventService.DispatchEvent(InstanceEventType.SentToSign, updatedInstance);
+ InstanceEvent instanceEvent = _instanceEventService.BuildInstanceEvent(InstanceEventType.SentToSign, existingInstance);
+ processStateUpdate.Events.Add(instanceEvent);
}
+ Instance updatedInstance = await _instanceAndEventsRepository.Update(existingInstance, updateProperties, processStateUpdate.Events);
+
updatedInstance.SetPlatformSelfLinks(_storageBaseAndHost);
return Ok(updatedInstance);
}
@@ -156,5 +182,56 @@ public async Task> GetProcessHistory(
return Ok(processHistoryList);
}
+
+ private void UpdateInstance(Instance existingInstance, ProcessState processState, out List updateProperties)
+ {
+ // Archiving instance if process was ended
+ updateProperties = [
+ nameof(existingInstance.Process),
+ nameof(existingInstance.LastChanged),
+ nameof(existingInstance.LastChangedBy)
+ ];
+ if (existingInstance.Process?.Ended is null && processState?.Ended is not null)
+ {
+ existingInstance.Status ??= new InstanceStatus();
+ existingInstance.Status.IsArchived = true;
+ existingInstance.Status.Archived = processState.Ended;
+ updateProperties.Add(nameof(existingInstance.Status));
+ updateProperties.Add(nameof(existingInstance.Status.IsArchived));
+ updateProperties.Add(nameof(existingInstance.Status.Archived));
+ }
+
+ existingInstance.Process = processState;
+ existingInstance.LastChangedBy = User.GetUserOrOrgId();
+ existingInstance.LastChanged = DateTime.UtcNow;
+ }
+
+ private (string Action, string TaskId) ActionMapping(ProcessState processState, Instance existingInstance)
+ {
+ string taskId = null;
+ string altinnTaskType = existingInstance.Process?.CurrentTask?.AltinnTaskType;
+
+ if (processState?.CurrentTask?.FlowType == "AbandonCurrentMoveToNext")
+ {
+ altinnTaskType = "reject";
+ }
+ else if (processState?.CurrentTask?.FlowType is not null
+ && processState.CurrentTask.FlowType != "CompleteCurrentMoveToNext")
+ {
+ altinnTaskType = processState.CurrentTask.AltinnTaskType;
+ taskId = processState.CurrentTask.ElementId;
+ }
+
+ string action = altinnTaskType switch
+ {
+ "data" or "feedback" => "write",
+ "payment" => "pay",
+ "confirmation" => "confirm",
+ "signing" => "sign",
+ _ => altinnTaskType,
+ };
+
+ return (action, taskId);
+ }
}
}
diff --git a/src/LocalTest.csproj b/src/LocalTest.csproj
index 68052349..a3949524 100644
--- a/src/LocalTest.csproj
+++ b/src/LocalTest.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/src/Services/Storage/Implementation/InstanceAndEventsRepository.cs b/src/Services/Storage/Implementation/InstanceAndEventsRepository.cs
new file mode 100644
index 00000000..766b3d74
--- /dev/null
+++ b/src/Services/Storage/Implementation/InstanceAndEventsRepository.cs
@@ -0,0 +1,41 @@
+using System.Data;
+using Altinn.Platform.Storage.Interface.Models;
+
+namespace Altinn.Platform.Storage.Repository;
+
+///
+/// Represents an implementation of .
+///
+public class InstanceAndEventsRepository : IInstanceAndEventsRepository
+{
+ private readonly ILogger _logger;
+ private readonly IInstanceRepository _instanceRepository;
+ private readonly IInstanceEventRepository _instanceEventRepository;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger to use when writing to logs.
+ /// Instance repo
+ public InstanceAndEventsRepository(
+ ILogger logger,
+ IInstanceRepository instanceRepository,
+ IInstanceEventRepository instanceEventRepository)
+ {
+ _logger = logger;
+ _instanceRepository = instanceRepository;
+ _instanceEventRepository = instanceEventRepository;
+ }
+
+ ///
+ public async Task Update(Instance instance, List updateProperties, List events)
+ {
+ instance = await _instanceRepository.Update(instance);
+ foreach (var instanceEvent in events)
+ {
+ await _instanceEventRepository.InsertInstanceEvent(instanceEvent);
+ }
+
+ return instance;
+ }
+}
diff --git a/src/Services/Storage/Implementation/InstanceEventRepository.cs b/src/Services/Storage/Implementation/InstanceEventRepository.cs
index 6728bec5..5143188d 100644
--- a/src/Services/Storage/Implementation/InstanceEventRepository.cs
+++ b/src/Services/Storage/Implementation/InstanceEventRepository.cs
@@ -83,6 +83,14 @@ public Task> ListInstanceEvents(string instanceId, string[]
private string GetInstanceEventPath(string instanceId, Guid instanceEventID)
{
+ if (string.IsNullOrEmpty(instanceId))
+ {
+ throw new ArgumentException("Instance ID cannot be null or empty");
+ }
+ if (instanceEventID == Guid.Empty)
+ {
+ throw new ArgumentException("Instance event ID cannot be null or empty");
+ }
return GetInstanceEventFolder() + instanceId.Replace("/", "_") + "_" + instanceEventID.ToString() + ".json";
}
diff --git a/src/Services/Storage/Implementation/InstanceEventService.cs b/src/Services/Storage/Implementation/InstanceEventService.cs
index 438dab17..0f1abfb0 100644
--- a/src/Services/Storage/Implementation/InstanceEventService.cs
+++ b/src/Services/Storage/Implementation/InstanceEventService.cs
@@ -28,7 +28,7 @@ public InstanceEventService(IInstanceEventRepository repository, IHttpContextAcc
}
///
- public async Task DispatchEvent(InstanceEventType eventType, Instance instance)
+ public InstanceEvent BuildInstanceEvent(InstanceEventType eventType, Instance instance)
{
var user = _contextAccessor.HttpContext.User;
@@ -48,6 +48,14 @@ public async Task DispatchEvent(InstanceEventType eventType, Instance instance)
Created = DateTime.UtcNow,
};
+ return instanceEvent;
+ }
+
+ ///
+ public async Task DispatchEvent(InstanceEventType eventType, Instance instance)
+ {
+ var instanceEvent = BuildInstanceEvent(eventType, instance);
+
await _repository.InsertInstanceEvent(instanceEvent);
}
diff --git a/src/Services/Storage/Interface/IInstanceAndEventsRepository.cs b/src/Services/Storage/Interface/IInstanceAndEventsRepository.cs
new file mode 100644
index 00000000..a1c115fa
--- /dev/null
+++ b/src/Services/Storage/Interface/IInstanceAndEventsRepository.cs
@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Altinn.Platform.Storage.Interface.Models;
+
+namespace Altinn.Platform.Storage.Repository;
+
+///
+/// Represents an implementation of .
+///
+public interface IInstanceAndEventsRepository
+{
+ ///
+ /// update existing instance including instance events
+ ///
+ /// the instance to update
+ /// a list of which properties should be updated
+ /// the events to add
+ /// The updated instance
+ Task Update(Instance instance, List updateProperties, List events);
+}
diff --git a/src/Services/Storage/Interface/IInstanceEventService.cs b/src/Services/Storage/Interface/IInstanceEventService.cs
index e1878e88..d9534a74 100644
--- a/src/Services/Storage/Interface/IInstanceEventService.cs
+++ b/src/Services/Storage/Interface/IInstanceEventService.cs
@@ -10,6 +10,14 @@ namespace Altinn.Platform.Storage.Services
///
public interface IInstanceEventService
{
+ ///
+ /// Construct an instance event given a type
+ ///
+ /// Event type
+ /// Instance
+ ///
+ public InstanceEvent BuildInstanceEvent(InstanceEventType eventType, Instance instance);
+
///
/// Dispatch an instance event to the repository
///
diff --git a/src/Startup.cs b/src/Startup.cs
index 03ab99d8..8256fd43 100644
--- a/src/Startup.cs
+++ b/src/Startup.cs
@@ -92,6 +92,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();