-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
V8 feature/304 support custom button (#346)
* Make multi decision request for actions * Prepare code for useraction in task * add controller endpoint * trigger userdefined task actions * Add authorization check * Add some tests * controller tests for actions * added more tests for actionscontroller * Change how errors are returned * Add tests for MultiDecisionHelper and add xml comments * Add tests for AuthorizationClient * Fix some codesmells and add some more tests * Fix style error * Action Controller return 404 if no implementations found Fix comments from review * Apply suggestions from code review Co-authored-by: Ivar Nesje <[email protected]> * Apply suggestion from code review Remove IUserActionService and rename UserActionFactory to UserActionService Add JsonPropertyName to classes returned through the ActionsController * Remove ValidationGroup from IUserAction * Apply suggestions from code review Co-authored-by: Ivar Nesje <[email protected]> * Remove the need for NullUserAction --------- Co-authored-by: Ivar Nesje <[email protected]>
- Loading branch information
Showing
53 changed files
with
3,220 additions
and
236 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
#nullable enable | ||
using Altinn.App.Api.Infrastructure.Filters; | ||
using Altinn.App.Api.Models; | ||
using Altinn.App.Core.Extensions; | ||
using Altinn.App.Core.Features.Action; | ||
using Altinn.App.Core.Internal.Exceptions; | ||
using Altinn.App.Core.Internal.Instances; | ||
using Altinn.App.Core.Models; | ||
using Altinn.App.Core.Models.UserAction; | ||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
using IAuthorizationService = Altinn.App.Core.Internal.Auth.IAuthorizationService; | ||
|
||
namespace Altinn.App.Api.Controllers; | ||
|
||
/// <summary> | ||
/// Controller that handles actions performed by users | ||
/// </summary> | ||
[AutoValidateAntiforgeryTokenIfAuthCookie] | ||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] | ||
[Route("{org}/{app}/instances/{instanceOwnerPartyId:int}/{instanceGuid:guid}/actions")] | ||
public class ActionsController : ControllerBase | ||
{ | ||
private readonly IAuthorizationService _authorization; | ||
private readonly IInstanceClient _instanceClient; | ||
private readonly UserActionService _userActionService; | ||
|
||
/// <summary> | ||
/// Create new instance of the <see cref="ActionsController"/> class | ||
/// </summary> | ||
/// <param name="authorization">The authorization service</param> | ||
/// <param name="instanceClient">The instance client</param> | ||
/// <param name="userActionService">The user action service</param> | ||
public ActionsController(IAuthorizationService authorization, IInstanceClient instanceClient, UserActionService userActionService) | ||
{ | ||
_authorization = authorization; | ||
_instanceClient = instanceClient; | ||
_userActionService = userActionService; | ||
} | ||
|
||
/// <summary> | ||
/// Perform a task action on an instance | ||
/// </summary> | ||
/// <param name="org">unique identfier of the organisation responsible for the app</param> | ||
/// <param name="app">application identifier which is unique within an organisation</param> | ||
/// <param name="instanceOwnerPartyId">unique id of the party that this the owner of the instance</param> | ||
/// <param name="instanceGuid">unique id to identify the instance</param> | ||
/// <param name="actionRequest">user action request</param> | ||
/// <returns><see cref="UserActionResponse"/></returns> | ||
[HttpPost] | ||
[Authorize] | ||
[ProducesResponseType(typeof(UserActionResponse), 200)] | ||
[ProducesResponseType(typeof(ProblemDetails), 400)] | ||
[ProducesResponseType(401)] | ||
public async Task<ActionResult<UserActionResponse>> Perform( | ||
[FromRoute] string org, | ||
[FromRoute] string app, | ||
[FromRoute] int instanceOwnerPartyId, | ||
[FromRoute] Guid instanceGuid, | ||
[FromBody] UserActionRequest actionRequest) | ||
{ | ||
var action = actionRequest.Action; | ||
if (action == null) | ||
{ | ||
return new BadRequestObjectResult(new ProblemDetails() | ||
{ | ||
Instance = instanceGuid.ToString(), | ||
Status = 400, | ||
Title = "Action is missing", | ||
Detail = "Action is missing in the request" | ||
}); | ||
} | ||
|
||
var instance = await _instanceClient.GetInstance(app, org, instanceOwnerPartyId, instanceGuid); | ||
|
||
if (instance?.Process == null) | ||
{ | ||
return Conflict($"Process is not started."); | ||
} | ||
|
||
if (instance.Process.Ended.HasValue) | ||
{ | ||
return Conflict($"Process is ended."); | ||
} | ||
|
||
var userId = HttpContext.User.GetUserIdAsInt(); | ||
if (userId == null) | ||
{ | ||
return Unauthorized(); | ||
} | ||
|
||
var authorized = await _authorization.AuthorizeAction(new AppIdentifier(org, app), new InstanceIdentifier(instanceOwnerPartyId, instanceGuid), HttpContext.User, action, instance.Process?.CurrentTask?.ElementId); | ||
if (!authorized) | ||
{ | ||
return Forbid(); | ||
} | ||
|
||
UserActionContext userActionContext = new UserActionContext(instance, userId.Value, actionRequest.ButtonId, actionRequest.Metadata); | ||
var actionHandler = _userActionService.GetActionHandler(action); | ||
if (actionHandler == null) | ||
{ | ||
return new NotFoundObjectResult(new UserActionResponse() | ||
{ | ||
Error = new ActionError() | ||
{ | ||
Code = "ActionNotFound", | ||
Message = $"Action handler with id {action} not found", | ||
} | ||
}); | ||
} | ||
|
||
var result = await actionHandler.HandleAction(userActionContext); | ||
|
||
if (!result.Success) | ||
{ | ||
return new BadRequestObjectResult(new UserActionResponse() | ||
{ | ||
FrontendActions = result.FrontendActions, | ||
Error = result.Error | ||
}); | ||
} | ||
|
||
return new OkObjectResult(new UserActionResponse() | ||
{ | ||
FrontendActions = result.FrontendActions, | ||
UpdatedDataModels = result.UpdatedDataModels | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#nullable enable | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Altinn.App.Api.Models; | ||
|
||
/// <summary> | ||
/// Request model for user action | ||
/// </summary> | ||
public class UserActionRequest | ||
{ | ||
/// <summary> | ||
/// Action performed | ||
/// </summary> | ||
[JsonPropertyName("action")] | ||
public string? Action { get; set; } | ||
|
||
/// <summary> | ||
/// The id of the button that was clicked | ||
/// </summary> | ||
[JsonPropertyName("buttonId")] | ||
public string? ButtonId { get; set; } | ||
|
||
/// <summary> | ||
/// Additional metadata for the action | ||
/// </summary> | ||
[JsonPropertyName("metadata")] | ||
public Dictionary<string, string>? Metadata { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#nullable enable | ||
using System.Text.Json.Serialization; | ||
using Altinn.App.Core.Models.UserAction; | ||
|
||
namespace Altinn.App.Api.Models; | ||
|
||
/// <summary> | ||
/// Response object from action endpoint | ||
/// </summary> | ||
public class UserActionResponse | ||
{ | ||
/// <summary> | ||
/// Data models that have been updated | ||
/// </summary> | ||
[JsonPropertyName("updatedDataModels")] | ||
public Dictionary<string, object?>? UpdatedDataModels { get; set; } | ||
|
||
/// <summary> | ||
/// Actions frontend should perform after action has been performed backend | ||
/// </summary> | ||
[JsonPropertyName("frontendActions")] | ||
public List<FrontendAction>? FrontendActions { get; set; } | ||
|
||
/// <summary> | ||
/// Validation issues that occured when processing action | ||
/// </summary> | ||
[JsonPropertyName("error")] | ||
public ActionError? Error { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.