Skip to content

Commit

Permalink
Endpoint for process state and events in one transaction (#562)
Browse files Browse the repository at this point in the history
  • Loading branch information
martinothamar authored Nov 27, 2024
1 parent 8483440 commit d1cff20
Show file tree
Hide file tree
Showing 14 changed files with 1,243 additions and 169 deletions.
2 changes: 1 addition & 1 deletion src/DbTools/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static void Main(string[] args)
string scriptFile = GetScriptFile(versionDirectory);
string toolName = Assembly.GetExecutingAssembly().FullName ?? "DbTools";
File.WriteAllText(scriptFile, $"-- This script is autogenerated from the tool {toolName.Split(',')[0]}. Do not edit manually.{cr}{cr}");
foreach (string filename in (new DirectoryInfo(funcAndProcDirectory).GetFiles(("*.sql")).Select(f => f.FullName)))
foreach (string filename in (new DirectoryInfo(funcAndProcDirectory).GetFiles(("*.sql")).Select(f => f.FullName).OrderBy(f => f)))
{
File.AppendAllText(scriptFile, $"-- {filename.Split(Path.DirectorySeparatorChar)[^1]}:{cr}{File.ReadAllText(filename)}{cr}{cr}");
}
Expand Down
2 changes: 1 addition & 1 deletion src/Storage/Altinn.Platform.Storage.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<PackageReference Include="Altinn.Common.AccessTokenClient" Version="3.0.10" />
<PackageReference Include="Altinn.Platform.Models" Version="1.6.1" />
<PackageReference Include="Altinn.Platform.Storage.Interface" Version="4.0.3" />
<PackageReference Include="Altinn.Platform.Storage.Interface" Version="4.0.4" />
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.2" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" />
Expand Down
143 changes: 105 additions & 38 deletions src/Storage/Controllers/ProcessController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,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;
Expand All @@ -38,18 +39,21 @@ public class ProcessController : ControllerBase
/// </summary>
/// <param name="instanceRepository">the instance repository handler</param>
/// <param name="instanceEventRepository">the instance event repository service</param>
/// <param name="instanceAndEventsRepository">the instance and events repository</param>
/// <param name="generalsettings">the general settings</param>
/// <param name="authorizationService">the authorization service</param>
/// <param name="instanceEventService">the instance event service</param>
public ProcessController(
IInstanceRepository instanceRepository,
IInstanceEventRepository instanceEventRepository,
IInstanceAndEventsRepository instanceAndEventsRepository,
IOptions<GeneralSettings> generalsettings,
IAuthorization authorizationService,
IInstanceEventService instanceEventService)
{
_instanceRepository = instanceRepository;
_instanceEventRepository = instanceEventRepository;
_instanceAndEventsRepository = instanceAndEventsRepository;
_storageBaseAndHost = $"{generalsettings.Value.Hostname}/storage/api/v1/";
_authorizationService = authorizationService;
_instanceEventService = instanceEventService;
Expand Down Expand Up @@ -80,28 +84,57 @@ public async Task<ActionResult<Instance>> PutProcess(
return NotFound();
}

string taskId = null;
string altinnTaskType = existingInstance.Process?.CurrentTask?.AltinnTaskType;
var (action, taskId) = ActionMapping(processState, existingInstance);

if (processState?.CurrentTask?.FlowType == "AbandonCurrentMoveToNext")
bool authorized = await _authorizationService.AuthorizeInstanceAction(existingInstance, action, taskId);

if (!authorized)
{
altinnTaskType = "reject";
return Forbid();
}
else if (processState?.CurrentTask?.FlowType is not null
&& processState.CurrentTask.FlowType != "CompleteCurrentMoveToNext")

UpdateInstance(existingInstance, processState, out var updateProperties);

Instance updatedInstance = await _instanceRepository.Update(existingInstance, updateProperties);

if (processState?.CurrentTask?.AltinnTaskType == "signing")
{
altinnTaskType = processState.CurrentTask.AltinnTaskType;
taskId = processState.CurrentTask.ElementId;
await _instanceEventService.DispatchEvent(InstanceEventType.SentToSign, updatedInstance);
}

string action = altinnTaskType switch
updatedInstance.SetPlatformSelfLinks(_storageBaseAndHost);
return Ok(updatedInstance);
}

/// <summary>
/// Updates the process state of an instance.
/// </summary>
/// <param name="instanceOwnerPartyId">The party id of the instance owner.</param>
/// <param name="instanceGuid">The id of the instance that should have its process updated.</param>
/// <param name="processStateUpdate">The new process state of the instance (including instance events).</param>
/// <returns></returns>
[Authorize]
[HttpPut("instanceandevents")]
[Consumes("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Produces("application/json")]
public async Task<ActionResult<Instance>> PutInstanceAndEvents(
int instanceOwnerPartyId,
Guid instanceGuid,
[FromBody] ProcessStateUpdate processStateUpdate)
{
(Instance existingInstance, _) = await _instanceRepository.GetOne(instanceGuid, true);

if (existingInstance is null)
{
"data" or "feedback" => "write",
"payment" => "pay",
"confirmation" => "confirm",
"signing" => "sign",
_ => altinnTaskType,
};
return NotFound();
}

ProcessState processState = processStateUpdate.State;

var (action, taskId) = ActionMapping(processState, existingInstance);

bool authorized = await _authorizationService.AuthorizeInstanceAction(existingInstance, action, taskId);

Expand All @@ -110,33 +143,16 @@ public async Task<ActionResult<Instance>> PutProcess(
return Forbid();
}

// Archiving instance if process was ended
List<string> 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;

Instance updatedInstance = await _instanceRepository.Update(existingInstance, updateProperties);

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);
}
Expand Down Expand Up @@ -194,5 +210,56 @@ public async Task<ActionResult<AuthInfo>> GetForAuth(int instanceOwnerPartyId, G

return NotFound($"Unable to find instance {instanceOwnerPartyId}/{instanceGuid}: {message}");
}

private void UpdateInstance(Instance existingInstance, ProcessState processState, out List<string> 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

CREATE OR REPLACE PROCEDURE storage.insertinstanceevents(_instance UUID, _events JSONB)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
INSERT INTO storage.instanceevents (instance, alternateid, event)
SELECT _instance, (evs->>'Id')::UUID, jsonb_strip_nulls(evs)
FROM jsonb_array_elements(_events) evs;
END;
$BODY$;
Loading

0 comments on commit d1cff20

Please sign in to comment.