Skip to content

Commit 13ca68b

Browse files
Thierry Habarthabarthierry-hue
authored andcommitted
Ticket ## : Add repetition rule
1 parent 3c1218b commit 13ca68b

66 files changed

Lines changed: 1986 additions & 345 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/CaseManagement.CMMN/Apis/CaseInstancesController.cs

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using CaseManagement.Workflow.Persistence;
99
using CaseManagement.Workflow.Persistence.Parameters;
1010
using CaseManagement.Workflow.Persistence.Responses;
11+
using Microsoft.AspNetCore.Authorization;
1112
using Microsoft.AspNetCore.Http;
1213
using Microsoft.AspNetCore.Mvc;
1314
using Newtonsoft.Json.Linq;
@@ -27,14 +28,18 @@ public class CaseInstancesController : Controller
2728
private readonly IConfirmFormCommandHandler _confirmFormCommandHandler;
2829
private readonly IStopCaseInstanceCommandHandler _stopCaseInstanceCommandHandler;
2930
private readonly IProcessFlowInstanceQueryRepository _processFlowInstanceQueryRepository;
31+
private readonly IActivateCommandHandler _activateCommandHandler;
32+
private readonly ITerminateCommandHandler _terminateCommandHandler;
3033

31-
public CaseInstancesController(ICreateCaseInstanceCommandHandler createCaseInstanceCommandHandler, ILaunchCaseInstanceCommandHandler launchCaseInstanceCommandHandler, IConfirmFormCommandHandler confirmFormCommandHandler, IStopCaseInstanceCommandHandler stopCaseInstanceCommandHandler, IProcessFlowInstanceQueryRepository processFlowInstanceQueryRepository)
34+
public CaseInstancesController(ICreateCaseInstanceCommandHandler createCaseInstanceCommandHandler, ILaunchCaseInstanceCommandHandler launchCaseInstanceCommandHandler, IConfirmFormCommandHandler confirmFormCommandHandler, IStopCaseInstanceCommandHandler stopCaseInstanceCommandHandler, IProcessFlowInstanceQueryRepository processFlowInstanceQueryRepository, IActivateCommandHandler activateCommandHandler, ITerminateCommandHandler terminateCommandHandler)
3235
{
3336
_createCaseInstanceCommandHandler = createCaseInstanceCommandHandler;
3437
_launchCaseInstanceCommandHandler = launchCaseInstanceCommandHandler;
3538
_confirmFormCommandHandler = confirmFormCommandHandler;
3639
_stopCaseInstanceCommandHandler = stopCaseInstanceCommandHandler;
3740
_processFlowInstanceQueryRepository = processFlowInstanceQueryRepository;
41+
_activateCommandHandler = activateCommandHandler;
42+
_terminateCommandHandler = terminateCommandHandler;
3843
}
3944

4045
[HttpGet(".search")]
@@ -71,12 +76,12 @@ public async Task<IActionResult> Stop(string id)
7176
}
7277

7378
[HttpPost("{id}/confirm/{elt}")]
79+
[Authorize("IsConnected")]
7480
public async Task<IActionResult> ConfirmForm(string id, string elt, [FromBody] JObject jObj)
7581
{
76-
// TODO : CHECK THE ROLE.
7782
try
7883
{
79-
await _confirmFormCommandHandler.Handle(new ConfirmFormCommand { CaseInstanceId = id, CaseElementInstanceId = elt, Content = jObj });
84+
await _confirmFormCommandHandler.Handle(new ConfirmFormCommand { CaseInstanceId = id, CaseElementInstanceId = elt, Content = jObj, UserIdentifier = this.GetNameIdentifier() });
8085
return new OkResult();
8186
}
8287
catch(UnknownCaseInstanceException)
@@ -97,6 +102,13 @@ public async Task<IActionResult> ConfirmForm(string id, string elt, [FromBody] J
97102
{
98103
return ToError(ex.Errors, HttpStatusCode.BadRequest, Request);
99104
}
105+
catch(UnauthorizedCaseWorkerException)
106+
{
107+
return ToError(new Dictionary<string, string>
108+
{
109+
{ "unauthorized_request", "you're not authorized to confirm the human task" }
110+
}, HttpStatusCode.Unauthorized, Request);
111+
}
100112
catch(Exception ex)
101113
{
102114
return ToError(new Dictionary<string, string>
@@ -106,6 +118,68 @@ public async Task<IActionResult> ConfirmForm(string id, string elt, [FromBody] J
106118
}
107119
}
108120

121+
[HttpGet("{id}/activate/{elt}")]
122+
public async Task<IActionResult> Activate(string id, string elt)
123+
{
124+
try
125+
{
126+
await _activateCommandHandler.Handle(new ActivateCommand(id, elt));
127+
return new OkResult();
128+
}
129+
catch (UnknownCaseInstanceException)
130+
{
131+
return ToError(new Dictionary<string, string>
132+
{
133+
{ "bad_request", "case instance doesn't exist" }
134+
}, HttpStatusCode.NotFound, Request);
135+
}
136+
catch (UnknownCaseInstanceElementException)
137+
{
138+
return ToError(new Dictionary<string, string>
139+
{
140+
{ "bad_request", "case instance element doesn't exist" }
141+
}, HttpStatusCode.NotFound, Request);
142+
}
143+
catch (Exception ex)
144+
{
145+
return ToError(new Dictionary<string, string>
146+
{
147+
{ "invalid_request", ex.Message }
148+
}, HttpStatusCode.BadRequest, Request);
149+
}
150+
}
151+
152+
[HttpGet("{id}/terminate/{elt}")]
153+
public async Task<IActionResult> Terminate(string id, string elt)
154+
{
155+
try
156+
{
157+
await _terminateCommandHandler.Handle(new TerminateCommand(id, elt));
158+
return new OkResult();
159+
}
160+
catch (UnknownCaseInstanceException)
161+
{
162+
return ToError(new Dictionary<string, string>
163+
{
164+
{ "bad_request", "case instance doesn't exist" }
165+
}, HttpStatusCode.NotFound, Request);
166+
}
167+
catch (UnknownCaseInstanceElementException)
168+
{
169+
return ToError(new Dictionary<string, string>
170+
{
171+
{ "bad_request", "case instance element doesn't exist" }
172+
}, HttpStatusCode.NotFound, Request);
173+
}
174+
catch (Exception ex)
175+
{
176+
return ToError(new Dictionary<string, string>
177+
{
178+
{ "invalid_request", ex.Message }
179+
}, HttpStatusCode.BadRequest, Request);
180+
}
181+
}
182+
109183
[HttpGet("{id}")]
110184
public async Task<IActionResult> Get(string id)
111185
{
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using CaseManagement.CMMN.CaseInstance.Commands;
2+
using CaseManagement.CMMN.CaseInstance.Exceptions;
3+
using CaseManagement.CMMN.Domains;
4+
using CaseManagement.Workflow.Domains;
5+
using CaseManagement.Workflow.Infrastructure.Bus;
6+
using CaseManagement.Workflow.Infrastructure.EvtStore;
7+
using System.Linq;
8+
using System.Threading.Tasks;
9+
10+
namespace CaseManagement.CMMN.CaseInstance.CommandHandlers
11+
{
12+
public class ActivateCommandHandler : IActivateCommandHandler
13+
{
14+
private readonly IEventStoreRepository _eventStoreRepository;
15+
private readonly IQueueProvider _queueProvider;
16+
17+
public ActivateCommandHandler(IEventStoreRepository eventStoreRepository, IQueueProvider queueProvider)
18+
{
19+
_eventStoreRepository = eventStoreRepository;
20+
_queueProvider = queueProvider;
21+
}
22+
23+
public async Task<bool> Handle(ActivateCommand activateCommand)
24+
{
25+
var caseInstance = await _eventStoreRepository.GetLastAggregate<CMMNProcessFlowInstance>(activateCommand.CaseInstanceId, CMMNProcessFlowInstance.GetCMMNStreamName(activateCommand.CaseInstanceId));
26+
if (caseInstance == null || string.IsNullOrWhiteSpace(caseInstance.Id))
27+
{
28+
throw new UnknownCaseInstanceException(activateCommand.CaseInstanceId);
29+
}
30+
31+
var flowInstanceElt = caseInstance.Elements.FirstOrDefault(e => e.Id == activateCommand.CaseElementInstanceId) as CMMNPlanItem;
32+
if (flowInstanceElt == null)
33+
{
34+
throw new UnknownCaseInstanceElementException(caseInstance.Id, activateCommand.CaseElementInstanceId);
35+
}
36+
37+
if (flowInstanceElt.PlanItemDefinitionType != CMMNPlanItemDefinitionTypes.HumanTask &&
38+
flowInstanceElt.PlanItemDefinitionType != CMMNPlanItemDefinitionTypes.ProcessTask &&
39+
flowInstanceElt.PlanItemDefinitionType != CMMNPlanItemDefinitionTypes.Task)
40+
{
41+
throw new NotSupportedTaskException("Element must be a Task");
42+
}
43+
44+
if (flowInstanceElt.ManualActivationRule == null || (flowInstanceElt.ManualActivationRule != null && !IsEnabled(flowInstanceElt)))
45+
{
46+
throw new CaseInvalidOperationException("Element must be activated first");
47+
}
48+
49+
caseInstance.ManuallyStartPlanItem(flowInstanceElt.Id);
50+
await _queueProvider.QueueRaiseEvent(caseInstance.Id, caseInstance.DomainEvents.Last());
51+
return true;
52+
}
53+
54+
private bool IsEnabled(CMMNPlanItem planItem)
55+
{
56+
switch(planItem.PlanItemDefinitionType)
57+
{
58+
case CMMNPlanItemDefinitionTypes.HumanTask:
59+
return planItem.PlanItemDefinitionHumanTask.State == CMMNTaskStates.Enabled;
60+
case CMMNPlanItemDefinitionTypes.Task:
61+
return planItem.PlanItemDefinitionTask.State == CMMNTaskStates.Enabled;
62+
case CMMNPlanItemDefinitionTypes.ProcessTask:
63+
return planItem.PlanItemDefinitionProcessTask.State == CMMNTaskStates.Enabled;
64+
}
65+
66+
return false;
67+
}
68+
}
69+
}

src/CaseManagement.CMMN/CaseInstance/CommandHandlers/ConfirmFormCommandHandler.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ public class ConfirmFormCommandHandler : IConfirmFormCommandHandler
1818
private readonly IQueueProvider _queueProvider;
1919
private readonly IFormQueryRepository _formQueryRepository;
2020
private readonly IEventStoreRepository _eventStoreRepository;
21+
private readonly IRoleQueryRepository _roleQueryRepository;
2122

22-
public ConfirmFormCommandHandler(IQueueProvider queueProvider, IFormQueryRepository formQueryRepository, IEventStoreRepository eventStoreRepository)
23+
public ConfirmFormCommandHandler(IQueueProvider queueProvider, IFormQueryRepository formQueryRepository, IEventStoreRepository eventStoreRepository, IRoleQueryRepository roleQueryRepository)
2324
{
2425
_queueProvider = queueProvider;
2526
_formQueryRepository = formQueryRepository;
2627
_eventStoreRepository = eventStoreRepository;
28+
_roleQueryRepository = roleQueryRepository;
2729
}
2830

2931
public async Task<bool> Handle(ConfirmFormCommand confirmFormCommand)
@@ -46,6 +48,15 @@ public async Task<bool> Handle(ConfirmFormCommand confirmFormCommand)
4648
}
4749

4850
var humanTask = flowInstanceElt.PlanItemDefinitionHumanTask;
51+
if (!string.IsNullOrWhiteSpace(humanTask.PerformerRef))
52+
{
53+
var roles = await _roleQueryRepository.FindRolesByUser(confirmFormCommand.UserIdentifier);
54+
if (!roles.Any(r => r.Name == humanTask.PerformerRef))
55+
{
56+
throw new UnauthorizedCaseWorkerException(confirmFormCommand.UserIdentifier, caseInstance.Id, flowInstanceElt.Id);
57+
}
58+
}
59+
4960
var form = await _formQueryRepository.FindFormById(humanTask.FormId);
5061
if (form == null)
5162
{

src/CaseManagement.CMMN/CaseInstance/CommandHandlers/CreateCaseInstanceCommandHandler.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,26 @@ public static ProcessFlowInstance BuildProcessFlowInstance(tCase tCase, string i
8989
}
9090
}
9191

92+
if (planItem.itemControl != null && planItem.itemControl.manualActivationRule != null)
93+
{
94+
var manualActivationRule = planItem.itemControl.manualActivationRule;
95+
flowInstanceElt.ActivationRule = CMMNActivationRuleTypes.ManualActivation;
96+
flowInstanceElt.ManualActivationRule = new CMMNManualActivationRule(manualActivationRule.name)
97+
{
98+
Expression = manualActivationRule.condition == null ? null : new CMMNExpression(manualActivationRule.condition.language, manualActivationRule.condition.Text.First())
99+
};
100+
}
101+
102+
if (planItem.itemControl != null && planItem.itemControl.repetitionRule != null)
103+
{
104+
var repetitionRule = planItem.itemControl.repetitionRule;
105+
flowInstanceElt.ActivationRule = CMMNActivationRuleTypes.Repetition;
106+
flowInstanceElt.RepetitionRule = new CMMNRepetitionRule(repetitionRule.name)
107+
{
108+
Condition = repetitionRule.condition == null ? null : new CMMNExpression(repetitionRule.condition.language, repetitionRule.condition.Text.First())
109+
};
110+
}
111+
92112
flowInstanceElements.Add(flowInstanceElt);
93113
}
94114

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using CaseManagement.CMMN.CaseInstance.Commands;
2+
using System.Threading.Tasks;
3+
4+
namespace CaseManagement.CMMN.CaseInstance.CommandHandlers
5+
{
6+
public interface IActivateCommandHandler
7+
{
8+
Task<bool> Handle(ActivateCommand command);
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using CaseManagement.CMMN.CaseInstance.Commands;
2+
using System.Threading.Tasks;
3+
4+
namespace CaseManagement.CMMN.CaseInstance.CommandHandlers
5+
{
6+
public interface ITerminateCommandHandler
7+
{
8+
Task<bool> Handle(TerminateCommand terminateCommand);
9+
}
10+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using CaseManagement.CMMN.CaseInstance.Commands;
2+
using CaseManagement.CMMN.CaseInstance.Exceptions;
3+
using CaseManagement.CMMN.Domains;
4+
using CaseManagement.Workflow.Domains;
5+
using CaseManagement.Workflow.Infrastructure.Bus;
6+
using CaseManagement.Workflow.Infrastructure.EvtStore;
7+
using System.Linq;
8+
using System.Threading.Tasks;
9+
10+
namespace CaseManagement.CMMN.CaseInstance.CommandHandlers
11+
{
12+
public class TerminateCommandHandler : ITerminateCommandHandler
13+
{
14+
private readonly IEventStoreRepository _eventStoreRepository;
15+
private readonly IQueueProvider _queueProvider;
16+
17+
public TerminateCommandHandler(IEventStoreRepository eventStoreRepository, IQueueProvider queueProvider)
18+
{
19+
_eventStoreRepository = eventStoreRepository;
20+
_queueProvider = queueProvider;
21+
}
22+
23+
public async Task<bool> Handle(TerminateCommand terminateCommand)
24+
{
25+
var caseInstance = await _eventStoreRepository.GetLastAggregate<CMMNProcessFlowInstance>(terminateCommand.CaseInstanceId, CMMNProcessFlowInstance.GetCMMNStreamName(terminateCommand.CaseInstanceId));
26+
if (caseInstance == null || string.IsNullOrWhiteSpace(caseInstance.Id))
27+
{
28+
throw new UnknownCaseInstanceException(terminateCommand.CaseInstanceId);
29+
}
30+
31+
var flowInstanceElt = caseInstance.Elements.FirstOrDefault(e => e.Id == terminateCommand.CaseElementInstanceId) as CMMNPlanItem;
32+
if (flowInstanceElt == null)
33+
{
34+
throw new UnknownCaseInstanceElementException(caseInstance.Id, terminateCommand.CaseElementInstanceId);
35+
}
36+
37+
if (flowInstanceElt.PlanItemDefinitionType != CMMNPlanItemDefinitionTypes.HumanTask &&
38+
flowInstanceElt.PlanItemDefinitionType != CMMNPlanItemDefinitionTypes.ProcessTask &&
39+
flowInstanceElt.PlanItemDefinitionType != CMMNPlanItemDefinitionTypes.Task)
40+
{
41+
throw new NotSupportedTaskException("Element must be a Task");
42+
}
43+
44+
if (!IsActivated(flowInstanceElt))
45+
{
46+
throw new CaseInvalidOperationException("Element must be activated first");
47+
}
48+
49+
caseInstance.TerminatePlanItem(flowInstanceElt.Id);
50+
await _queueProvider.QueueRaiseEvent(caseInstance.Id, caseInstance.DomainEvents.Last());
51+
return true;
52+
}
53+
54+
private bool IsActivated(CMMNPlanItem planItem)
55+
{
56+
switch (planItem.PlanItemDefinitionType)
57+
{
58+
case CMMNPlanItemDefinitionTypes.HumanTask:
59+
return planItem.PlanItemDefinitionHumanTask.State == CMMNTaskStates.Active;
60+
case CMMNPlanItemDefinitionTypes.Task:
61+
return planItem.PlanItemDefinitionTask.State == CMMNTaskStates.Active;
62+
case CMMNPlanItemDefinitionTypes.ProcessTask:
63+
return planItem.PlanItemDefinitionProcessTask.State == CMMNTaskStates.Active;
64+
}
65+
66+
return false;
67+
}
68+
}
69+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace CaseManagement.CMMN.CaseInstance.Commands
2+
{
3+
public class ActivateCommand
4+
{
5+
public ActivateCommand(string caseInstanceId, string caseElementInstanceId)
6+
{
7+
CaseInstanceId = caseInstanceId;
8+
CaseElementInstanceId = caseElementInstanceId;
9+
}
10+
11+
public string CaseInstanceId { get; set; }
12+
public string CaseElementInstanceId { get; set; }
13+
}
14+
}

src/CaseManagement.CMMN/CaseInstance/Commands/ConfirmFormCommand.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ public class ConfirmFormCommand
77
public string CaseInstanceId { get; set; }
88
public string CaseElementInstanceId { get; set; }
99
public JObject Content { get; set; }
10+
public string UserIdentifier { get; set; }
1011
}
1112
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace CaseManagement.CMMN.CaseInstance.Commands
2+
{
3+
public class TerminateCommand
4+
{
5+
public TerminateCommand(string caseInstanceId, string caseElementInstanceId)
6+
{
7+
CaseInstanceId = caseInstanceId;
8+
CaseElementInstanceId = caseElementInstanceId;
9+
}
10+
11+
public string CaseInstanceId { get; set; }
12+
public string CaseElementInstanceId { get; set; }
13+
}
14+
}

0 commit comments

Comments
 (0)