Skip to content

Commit 74a42b7

Browse files
Ticket #21 : Add API to get the list of planning
1 parent cf5e210 commit 74a42b7

22 files changed

Lines changed: 827 additions & 243 deletions

Architecture.pptx

13 Bytes
Binary file not shown.

src/CaseManagement.CMMN.AspNet/Apis/CaseInstancesController.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ public class CaseInstancesController : ApiController
3232
private readonly ICloseCommandHandler _closeCommandHandler;
3333
private readonly IConfirmFormCommandHandler _confirmFormCommandHandler;
3434
private readonly IActivateCommandHandler _activateCommandHandler;
35+
private readonly IConfirmTableItemCommandHandler _confirmTableItemCommandHandler;
3536
private readonly ICaseInstanceQueryRepository _cmmnWorkflowInstanceQueryRepository;
3637
private readonly ICaseFileItemRepository _caseFileItemRepository;
3738

38-
public CaseInstancesController(ICreateCaseInstanceCommandHandler createCaseInstanceCommandHandler, ILaunchCaseInstanceCommandHandler launchCaseInstanceCommandHandler, ISuspendCommandHandler suspendCommandHandler, IResumeCommandHandler resumeCommandHandler, ITerminateCommandHandler terminateCommandHandler, IReactivateCommandHandler reactivateCommandHandler, ICloseCommandHandler closeCommandHandler, IConfirmFormCommandHandler confirmFormCommandHandler, IActivateCommandHandler activateCommandHandler, ICaseInstanceQueryRepository cmmnWorkflowInstanceQueryRepository, ICaseFileItemRepository caseFileItemRepository)
39+
public CaseInstancesController(ICreateCaseInstanceCommandHandler createCaseInstanceCommandHandler, ILaunchCaseInstanceCommandHandler launchCaseInstanceCommandHandler, ISuspendCommandHandler suspendCommandHandler, IResumeCommandHandler resumeCommandHandler, ITerminateCommandHandler terminateCommandHandler, IReactivateCommandHandler reactivateCommandHandler, ICloseCommandHandler closeCommandHandler, IConfirmFormCommandHandler confirmFormCommandHandler, IActivateCommandHandler activateCommandHandler, IConfirmTableItemCommandHandler confirmTableItemCommandHandler, ICaseInstanceQueryRepository cmmnWorkflowInstanceQueryRepository, ICaseFileItemRepository caseFileItemRepository)
3940
{
4041
_createCaseInstanceCommandHandler = createCaseInstanceCommandHandler;
4142
_launchCaseInstanceCommandHandler = launchCaseInstanceCommandHandler;
@@ -46,6 +47,7 @@ public CaseInstancesController(ICreateCaseInstanceCommandHandler createCaseInsta
4647
_closeCommandHandler = closeCommandHandler;
4748
_confirmFormCommandHandler = confirmFormCommandHandler;
4849
_activateCommandHandler = activateCommandHandler;
50+
_confirmTableItemCommandHandler = confirmTableItemCommandHandler;
4951
_cmmnWorkflowInstanceQueryRepository = cmmnWorkflowInstanceQueryRepository;
5052
_caseFileItemRepository = caseFileItemRepository;
5153
}
@@ -500,6 +502,50 @@ public async Task<IHttpActionResult> Activate(string id, string elt)
500502
}
501503
}
502504

505+
[HttpGet]
506+
[Route("{id:guid}/confirmplanitem/{elt}")]
507+
[Authorize]
508+
public async Task<IHttpActionResult> ConfirmPlanItem(string id, string elt)
509+
{
510+
try
511+
{
512+
await _confirmTableItemCommandHandler.Handle(new ConfirmTableItemCommand { CaseInstanceId = id, CaseElementDefinitionId = elt, User = this.GetNameIdentifier() });
513+
return Ok();
514+
}
515+
catch (UnknownCaseInstanceException)
516+
{
517+
return ToError(new Dictionary<string, string>
518+
{
519+
{ "bad_request", "case instance doesn't exist" }
520+
}, HttpStatusCode.NotFound, Request);
521+
}
522+
catch (UnknownCaseElementDefinitionException)
523+
{
524+
return ToError(new Dictionary<string, string>
525+
{
526+
{ "bad_request", "case element doesn't exist" }
527+
}, HttpStatusCode.NotFound, Request);
528+
}
529+
catch (UnauthorizedCaseWorkerException)
530+
{
531+
return ToError(new Dictionary<string, string>
532+
{
533+
{ "unauthorized_request", "you're not authorized to confirm the human task" }
534+
}, HttpStatusCode.Unauthorized, Request);
535+
}
536+
catch (AggregateValidationException ex)
537+
{
538+
return ToError(ex.Errors, HttpStatusCode.BadRequest, Request);
539+
}
540+
catch (Exception ex)
541+
{
542+
return ToError(new Dictionary<string, string>
543+
{
544+
{ "invalid_request", ex.Message }
545+
}, HttpStatusCode.BadRequest, Request);
546+
}
547+
}
548+
503549
private static JObject ToDto(FindResponse<Domains.CaseInstance> resp)
504550
{
505551
return new JObject
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using CaseManagement.CMMN.AspNet.Extensions;
2+
using CaseManagement.CMMN.Domains;
3+
using CaseManagement.CMMN.Extensions;
4+
using CaseManagement.CMMN.Persistence;
5+
using CaseManagement.CMMN.Persistence.Parameters;
6+
using CaseManagement.CMMN.Persistence.Responses;
7+
using Newtonsoft.Json.Linq;
8+
using System.Collections.Generic;
9+
using System.Linq;
10+
using System.Net.Http;
11+
using System.Threading.Tasks;
12+
using System.Web.Http;
13+
14+
namespace CaseManagement.CMMN.AspNet.Apis
15+
{
16+
[RoutePrefix(CMMNConstants.RouteNames.CasePlanifications)]
17+
public class CasePlanificationsController : ApiController
18+
{
19+
private readonly ICasePlanificationQueryRepository _casePlanificationQueryRepository;
20+
private readonly IRoleQueryRepository _roleQueryRepository;
21+
22+
public CasePlanificationsController(ICasePlanificationQueryRepository casePlanificationQueryRepository, IRoleQueryRepository roleQueryRepository)
23+
{
24+
_casePlanificationQueryRepository = casePlanificationQueryRepository;
25+
_roleQueryRepository = roleQueryRepository;
26+
}
27+
28+
[HttpGet]
29+
[Route("search")]
30+
[Authorize]
31+
public async Task<IHttpActionResult> Search()
32+
{
33+
var query = this.Request.GetQueryNameValuePairs();
34+
var nameIdentifier = this.GetNameIdentifier();
35+
var roles = (await _roleQueryRepository.FindRolesByUser(nameIdentifier)).Select(r => r.Id);
36+
var result = await _casePlanificationQueryRepository.Find(ExtractFindParameter(query, null));
37+
return Ok(ToDto(result));
38+
}
39+
40+
private static FindCasePlanificationParameter ExtractFindParameter(IEnumerable<KeyValuePair<string, string>> query, IEnumerable<string> roleIds)
41+
{
42+
string groupBy;
43+
string caseInstanceId;
44+
var parameter = new FindCasePlanificationParameter();
45+
parameter.ExtractFindParameter(query);
46+
if (query.TryGet("group_by", out groupBy))
47+
{
48+
parameter.GroupBy = groupBy;
49+
}
50+
51+
if (query.TryGet("case_instance_id", out caseInstanceId))
52+
{
53+
parameter.CaseInstanceId = caseInstanceId;
54+
}
55+
56+
parameter.Roles = roleIds;
57+
return parameter;
58+
}
59+
60+
private static JObject ToDto(FindResponse<CasePlanificationAggregate> resp)
61+
{
62+
return new JObject
63+
{
64+
{ "start_index", resp.StartIndex },
65+
{ "total_length", resp.TotalLength },
66+
{ "count", resp.Count },
67+
{ "content", new JArray(resp.Content.Select(r => {
68+
var result = new JObject
69+
{
70+
{ "case_instance_id", r.CaseInstanceId},
71+
{ "case_instance_name", r.CaseName },
72+
{ "case_instance_description", r.CaseDescription },
73+
{ "case_element_id", r.CaseElementId },
74+
{ "case_element_name", r.CaseElementName },
75+
{ "create_datetime", r.CreateDateTime }
76+
};
77+
return result;
78+
})) }
79+
};
80+
}
81+
}
82+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using CaseManagement.CMMN.AspNetCore.Extensions;
2+
using CaseManagement.CMMN.Domains;
3+
using CaseManagement.CMMN.Extensions;
4+
using CaseManagement.CMMN.Persistence;
5+
using CaseManagement.CMMN.Persistence.Parameters;
6+
using CaseManagement.CMMN.Persistence.Responses;
7+
using Microsoft.AspNetCore.Authorization;
8+
using Microsoft.AspNetCore.Mvc;
9+
using Newtonsoft.Json.Linq;
10+
using System.Collections.Generic;
11+
using System.Linq;
12+
using System.Threading.Tasks;
13+
14+
namespace CaseManagement.CMMN.AspNetCore.Apis
15+
{
16+
[Route(CMMNConstants.RouteNames.CasePlanifications)]
17+
public class CasePlanificationsController : Controller
18+
{
19+
private readonly ICasePlanificationQueryRepository _casePlanificationQueryRepository;
20+
private readonly IRoleQueryRepository _roleQueryRepository;
21+
22+
public CasePlanificationsController(ICasePlanificationQueryRepository casePlanificationQueryRepository, IRoleQueryRepository roleQueryRepository)
23+
{
24+
_casePlanificationQueryRepository = casePlanificationQueryRepository;
25+
_roleQueryRepository = roleQueryRepository;
26+
}
27+
28+
[HttpGet("search")]
29+
[Authorize("IsConnected")]
30+
public async Task<IActionResult> Search()
31+
{
32+
var query = HttpContext.Request.Query.ToEnumerable();
33+
var nameIdentifier = this.GetNameIdentifier();
34+
var roles = (await _roleQueryRepository.FindRolesByUser(nameIdentifier)).Select(r => r.Id);
35+
var result = await _casePlanificationQueryRepository.Find(ExtractFindParameter(query, null));
36+
return new OkObjectResult(ToDto(result));
37+
}
38+
39+
private static FindCasePlanificationParameter ExtractFindParameter(IEnumerable<KeyValuePair<string, string>> query, IEnumerable<string> roleIds)
40+
{
41+
string groupBy;
42+
string caseInstanceId;
43+
var parameter = new FindCasePlanificationParameter();
44+
parameter.ExtractFindParameter(query);
45+
if (query.TryGet("group_by", out groupBy))
46+
{
47+
parameter.GroupBy = groupBy;
48+
}
49+
50+
if (query.TryGet("case_instance_id", out caseInstanceId))
51+
{
52+
parameter.CaseInstanceId = caseInstanceId;
53+
}
54+
55+
parameter.Roles = roleIds;
56+
return parameter;
57+
}
58+
59+
private static JObject ToDto(FindResponse<CasePlanificationAggregate> resp)
60+
{
61+
return new JObject
62+
{
63+
{ "start_index", resp.StartIndex },
64+
{ "total_length", resp.TotalLength },
65+
{ "count", resp.Count },
66+
{ "content", new JArray(resp.Content.Select(r => {
67+
var result = new JObject
68+
{
69+
{ "case_instance_id", r.CaseInstanceId},
70+
{ "case_instance_name", r.CaseName },
71+
{ "case_instance_description", r.CaseDescription },
72+
{ "case_element_id", r.CaseElementId },
73+
{ "case_element_name", r.CaseElementName },
74+
{ "create_datetime", r.CreateDateTime }
75+
};
76+
return result;
77+
})) }
78+
};
79+
}
80+
}
81+
}

src/CaseManagement.CMMN/CMMNConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public static class RouteNames
1717
public const string CaseProcesses = "case-processes";
1818
public const string CaseFormInstances = "case-form-instances";
1919
public const string CaseActivations = "case-activations";
20+
public const string CasePlanifications = "case-planifications";
2021
public const string Statistics = "statistics";
2122
}
2223

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using CaseManagement.CMMN.Domains;
2+
using CaseManagement.CMMN.Domains.Events;
3+
using CaseManagement.CMMN.Infrastructures;
4+
using CaseManagement.CMMN.Persistence;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
namespace CaseManagement.CMMN.CaseInstance.EventHandlers
9+
{
10+
public class CasePlanificationHandler : IDomainEventHandler<CaseElementPlanificationConfirmedEvent>, IDomainEventHandler<CaseElementPlannedEvent>
11+
{
12+
private readonly ICasePlanificationQueryRepository _casePlanificationQueryRepository;
13+
private readonly ICasePlanificationCommandRepository _casePlanificationCommandRepository;
14+
private readonly ICaseInstanceQueryRepository _caseInstanceQueryRepository;
15+
private readonly ICaseDefinitionQueryRepository _caseDefinitionQueryRepository;
16+
17+
public CasePlanificationHandler(ICasePlanificationQueryRepository casePlanificationQueryRepository, ICasePlanificationCommandRepository casePlanificationCommandRepository, ICaseInstanceQueryRepository caseInstanceQueryRepository, ICaseDefinitionQueryRepository caseDefinitionQueryRepository)
18+
{
19+
_casePlanificationQueryRepository = casePlanificationQueryRepository;
20+
_casePlanificationCommandRepository = casePlanificationCommandRepository;
21+
_caseInstanceQueryRepository = caseInstanceQueryRepository;
22+
_caseDefinitionQueryRepository = caseDefinitionQueryRepository;
23+
}
24+
25+
public async Task Handle(CaseElementPlanificationConfirmedEvent @event, CancellationToken cancellationToken)
26+
{
27+
var record = await _casePlanificationQueryRepository.FindById(@event.AggregateId, @event.CaseElementDefinitionId);
28+
_casePlanificationCommandRepository.Delete(record);
29+
await _casePlanificationCommandRepository.SaveChanges();
30+
}
31+
32+
public async Task Handle(CaseElementPlannedEvent @event, CancellationToken cancellationToken)
33+
{
34+
var caseInstance = await _caseInstanceQueryRepository.FindFlowInstanceById(@event.AggregateId);
35+
var caseDefinition = await _caseDefinitionQueryRepository.FindById(caseInstance.CaseDefinitionId);
36+
var record = new CasePlanificationAggregate
37+
{
38+
CaseElementId = @event.CaseElementDefinitionId,
39+
CaseInstanceId = @event.AggregateId,
40+
CreateDateTime = @event.CreateDateTime,
41+
UserRole = @event.UserRole,
42+
CaseName = caseDefinition.Name,
43+
CaseDescription = caseDefinition.Description,
44+
CaseElementName = caseDefinition.GetElement(@event.CaseElementDefinitionId).Name
45+
};
46+
_casePlanificationCommandRepository.Add(record);
47+
await _casePlanificationCommandRepository.SaveChanges();
48+
}
49+
}
50+
}

src/CaseManagement.CMMN/CaseInstance/EventHandlers/WorkflowInstanceHandler.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace CaseManagement.CMMN.CaseInstance.EventHandlers
1010
public class WorkflowInstanceHandler : IDomainEventHandler<CaseInstanceCreatedEvent>, IDomainEventHandler<CaseElementCreatedEvent>, IDomainEventHandler<CaseElementFinishedEvent>,
1111
IDomainEventHandler<CaseElementInstanceFormCreatedEvent>, IDomainEventHandler<CaseElementInstanceFormSubmittedEvent>, IDomainEventHandler<CaseElementStartedEvent>,
1212
IDomainEventHandler<CaseElementTransitionRaisedEvent>, IDomainEventHandler<CaseTransitionRaisedEvent>, IDomainEventHandler<CaseInstanceVariableAddedEvent>,
13-
IDomainEventHandler<CaseElementPlanificationConfirmedEvent>
13+
IDomainEventHandler<CaseElementPlanificationConfirmedEvent>, IDomainEventHandler<CaseElementPlannedEvent>
1414
{
1515
private readonly ICaseInstanceCommandRepository _cmmnWorkflowInstanceCommandRepository;
1616
private readonly ICaseInstanceQueryRepository _cmmnWorkflowInstanceQueryRepository;
@@ -102,5 +102,13 @@ public async Task Handle(CaseElementPlanificationConfirmedEvent @event, Cancella
102102
_cmmnWorkflowInstanceCommandRepository.Update(flowInstance);
103103
await _cmmnWorkflowInstanceCommandRepository.SaveChanges();
104104
}
105+
106+
public async Task Handle(CaseElementPlannedEvent @event, CancellationToken cancellationToken)
107+
{
108+
var flowInstance = await _cmmnWorkflowInstanceQueryRepository.FindFlowInstanceById(@event.AggregateId);
109+
flowInstance.Handle(@event);
110+
_cmmnWorkflowInstanceCommandRepository.Update(flowInstance);
111+
await _cmmnWorkflowInstanceCommandRepository.SaveChanges();
112+
}
105113
}
106114
}

src/CaseManagement.CMMN/Domains/CaseInstance/CaseInstance.cs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ public CaseElementInstance GetLastWorkflowElementInstance(string workflowItemDef
261261
}
262262

263263
public bool IsPlanned(string caseElementDefinitionId)
264+
{
265+
return ElementPlanificationLst.Any(e => e.CaseElementDefinitionId == caseElementDefinitionId && e.IsConfirmed);
266+
}
267+
268+
public bool IsInPlanning(string caseElementDefinitionId)
264269
{
265270
return ElementPlanificationLst.Any(e => e.CaseElementDefinitionId == caseElementDefinitionId);
266271
}
@@ -656,11 +661,11 @@ public void SetVariable(string key, string value)
656661
}
657662
}
658663

659-
public void Plan(string caseElementDefinitionId, IEnumerable<string> userRoles)
664+
public void CreateTableItem(string caseElementDefinitionId, string userRole)
660665
{
661666
lock (DomainEvents)
662667
{
663-
var evt = new CaseElementPlannedEvent(Guid.NewGuid().ToString(), Id, Version + 1, caseElementDefinitionId, userRoles);
668+
var evt = new CaseElementPlannedEvent(Guid.NewGuid().ToString(), Id, Version + 1, caseElementDefinitionId, userRole, DateTime.UtcNow);
664669
Handle(evt);
665670
DomainEvents.Add(evt);
666671
}
@@ -830,21 +835,41 @@ private void Handle(CaseElementFinishedEvent evt)
830835

831836
private void Handle(CaseElementPlannedEvent evt)
832837
{
838+
if (ElementPlanificationLst.Any(e => e.CaseElementDefinitionId == evt.CaseElementDefinitionId))
839+
{
840+
throw new AggregateValidationException(new Dictionary<string, string>
841+
{
842+
{ "tableitem", "case element already exists in the list of possible planned items" }
843+
});
844+
}
845+
846+
ElementPlanificationLst.Add(new CaseElementInstancePlanification(evt.CaseElementDefinitionId, evt.CreateDateTime));
833847
Version++;
834848
RaiseEvent(evt);
835849
}
836850

837851
private void Handle(CaseElementPlanificationConfirmedEvent evt)
838852
{
839-
if (ElementPlanificationLst.Any(e => e.CaseElementDefinitionId == evt.CaseElementDefinitionId))
853+
var elt = ElementPlanificationLst.FirstOrDefault(e => e.CaseElementDefinitionId == evt.CaseElementDefinitionId);
854+
if (elt == null)
855+
{
856+
throw new AggregateValidationException(new Dictionary<string, string>
857+
{
858+
{ "tableitem", "case element doesn't exist in the list of possible planned items" }
859+
});
860+
}
861+
862+
if (elt.IsConfirmed)
840863
{
841864
throw new AggregateValidationException(new Dictionary<string, string>
842865
{
843-
{ "confirm", "case element is already planned" }
866+
{ "tableitem", "case element is already planned" }
844867
});
845868
}
846869

847-
ElementPlanificationLst.Add(new CaseElementInstancePlanification(evt.User, evt.CaseElementDefinitionId, evt.CreateDateTime));
870+
elt.IsConfirmed = true;
871+
elt.ConfirmationDateTime = evt.CreateDateTime;
872+
elt.User = evt.User;
848873
Version++;
849874
RaiseEvent(evt);
850875
}

0 commit comments

Comments
 (0)