public async static Task <TResult> CreateAsync <TResult>(Guid processStageId, Guid actorId, Guid processStageTypeId, string title, Guid[] viewableActorIds, Guid[] editableActorIds, Guid[] completableActorIds, KeyValuePair <Guid[], Guid>[] confirmableActorIdsNexts, Func <TResult> onCreated, Func <TResult> onAlreadyExists, Func <TResult> onTypeDoesNotExist, Func <Guid, TResult> onStageDoesNotExist, Func <TResult> onActorDoesNotExist, Func <string, TResult> onFailure) { return(await await ProcessStageTypes.FindIdAsync(processStageTypeId, async (stageType) => { var computedTitle = title.IsNullOrWhiteSpace() ? stageType.title : title; // TODO: Validate the stage // TODO: Validate the actors return await await Persistence.ProcessStageDocument.FindByIdsAsync( confirmableActorIdsNexts.Select(confirmableActorIdsNext => confirmableActorIdsNext.Value), async(confirmableNextStages, missingNextStageIds) => { if (missingNextStageIds.Any()) { return onStageDoesNotExist(missingNextStageIds.First()); } return await Persistence.ProcessStageDocument.CreateAsync(processStageId, actorId, processStageTypeId, computedTitle, confirmableActorIdsNexts, editableActorIds, viewableActorIds, onCreated, onAlreadyExists); }); }, () => onTypeDoesNotExist().ToTask())); }
public static async Task <TResult> CreateAsync <TResult>(Guid processId, Guid processStageId, Guid resourceId, DateTime createdOn, KeyValuePair <string, Guid>[] resourceIds, Guid?previousStepId, DateTime?confirmedWhen, Guid?confirmedBy, EastFive.Api.Security security, Func <TResult> onCreated, Func <TResult> onAlreadyExists, Func <TResult> onStageDoesNotExist, Func <string, TResult> onFailure) { return(await await Persistence.ProcessStageDocument.FindByIdAsync(processStageId, async stage => { return await await ProcessStageTypes.FindIdAsync(stage.processStageTypeId, async(stageType) => { var resourceType = stageType.resourceType; return await stageType.resourceKeys .FlatMap(resourceIds.ToDictionary(), async(kvp, unusedResourceKvps, next, skip, tail) => { if (!unusedResourceKvps.ContainsKey(kvp.Key)) { if (confirmedWhen.HasValue) { return await tail(onFailure($"Missing resource for `{kvp.Key}`").ToTask()); } return await skip(unusedResourceKvps); } return await next( new Process.ProcessStageResource() { key = kvp.Key, resourceId = unusedResourceKvps[kvp.Key], type = kvp.Value, }, unusedResourceKvps .Where(kvpR => kvpR.Key != kvp.Key) .ToDictionary()); }, async(Process.ProcessStageResource[] procStageResources, Dictionary <string, Guid> unusedResourceIds) => { if (unusedResourceIds.Any()) { return onFailure($"`{unusedResourceIds.First().Key}` is not valid resource key for a stage of type `{stageType.processStageTypeId}`"); } var applicableActors = stage.completableIds .Concat(stage.confirmableIds.SelectKeys().SelectMany()) .Concat(stage.editableIds).Concat(stage.viewableIds) .Distinct() .ToArray(); if (previousStepId.HasValue) { return await await Persistence.ProcessDocument.FindByIdAsync(previousStepId.Value, async previousStep => { if (!(previousStep.confirmedBy.HasValue && previousStep.confirmedWhen.HasValue)) { return onFailure("Previous step has not been confirmed."); } //TODO: if(previousStep.nextStep.HasValue) return await Persistence.ProcessDocument.CreateAsync(processId, processStageId, stage.ownerId, resourceId, resourceType, createdOn, procStageResources, previousStepId, confirmedWhen, confirmedBy, applicableActors, onCreated, onAlreadyExists); }, () => onFailure("Previous step does not exist").ToTask()); } // TODO: If confirmed is set, ensure that the security actor posesses a position that is authorized to move the process forward return await Persistence.ProcessDocument.CreateAsync(processId, processStageId, stage.ownerId, resourceId, resourceType, createdOn, procStageResources, previousStepId, confirmedWhen, confirmedBy, applicableActors, onCreated, onAlreadyExists); }); }, onStageDoesNotExist.AsAsyncFunc()); }, onStageDoesNotExist.AsAsyncFunc())); }
public async static Task <TResult> FindByResourceAsync <TResult>(Guid actorId, Type resourceType, EastFive.Api.Security security, Func <ProcessResourceView[], TResult> onFound, Func <TResult> onResourceNotFound, Func <TResult> onUnauthorized) { return(await await Persistence.ProcessDocument.FindAllInFlowByActorAsync(actorId, resourceType, (processSteps) => { return processSteps .Select(ps => ps.processStageId) .Distinct() .FlatMap( async(processStageId, next, skip) => await await Persistence.ProcessStageDocument.FindByIdAsync <Task <TResult> >(processStageId, processStage => next(processStageId.PairWithValue(processStage)), () => skip()), (IEnumerable <KeyValuePair <Guid, ProcessStage> > processStages) => { var processStageLookup = processStages.ToDictionary(); return processStages .SelectValues(stage => stage.processStageTypeId) .Distinct() .FlatMap <Guid, Guid[], KeyValuePair <Guid, ProcessStageType>, Task <TResult> >( new Guid[] { }, async(processStageTypeId, kvpsAggr, next, skip) => await await ProcessStageTypes.FindIdAsync(processStageTypeId, stageType => next(processStageTypeId.PairWithValue(stageType), kvpsAggr), () => skip(kvpsAggr.Append(processStageTypeId).ToArray())), async(KeyValuePair <Guid, ProcessStageType>[] stageTypes, Guid[] missingValues) => { var processStageTypeLookup = stageTypes.ToDictionary(); return processSteps .GroupBy(processStep => processStep.resourceId) .FlatMap( (stepsUnordered, next, skip) => { var steps = stepsUnordered .OrderWith(default(Guid?), (carry, step) => step.previousStep == carry, step => step.processId) .ToArray(); if (!steps.Any()) { return skip(); } var viewableSteps = steps .Where(step => processStageLookup[step.processStageId].viewableIds.Contains(actorId)) .ToArray(); var activeStep = viewableSteps.Last(); var stage = processStageLookup[activeStep.processStageId]; var possibleResourceKeys = viewableSteps .SelectMany(step => processStageTypeLookup[processStageLookup[step.processStageId].processStageTypeId].resourceKeys.SelectKeys()) .Distinct() .Select( key => key.PairWithValue(new Process.ProcessStageResource { key = key, type = processStageTypeLookup.SelectValues().First( (v, nextStageType) => { return v.resourceKeys.First( (v2, nextKey) => { if (v2.Key == key) { return v2.Value; } return nextKey(); }, () => nextStageType()); }, () => default(Type)), })) .ToDictionary(); try { var view = new ProcessResourceView() { processViewId = activeStep.processId, resourceId = activeStep.resourceId, actorId = actorId, currentProcessStepId = activeStep.processId, titles = viewableSteps .Select(step => processStageLookup[step.processStageId].title) .ToArray(), completions = viewableSteps .Select(step => step.confirmedWhen) .ToArray(), invalidations = viewableSteps .Select(step => step.invalidatedWhen) .ToArray(), resourceType = resourceType, resourcesProvided = steps .Where(step => step.confirmedWhen.HasValue) .Aggregate( possibleResourceKeys, (aggr, step) => { // TODO: This probably does not work foreach (var resource in step.resources) { // In case it is not a resource that is // referenced in a viewable step if (!aggr.ContainsKey(resource.key)) { continue; } var aggrv = aggr[resource.key]; if (resource.resourceId.HasValue) { aggrv.resourceId = resource.resourceId; } aggrv.type = resource.type; } return aggr; }) .SelectValues() .ToArray(), nextStages = stage.confirmableIds .Where(actorKvps => actorKvps.Key.Contains(actorId)) .Select(actorKvps => new ProcessStage() { // TODO: Rest of these values processStageId = actorKvps.Value, }) .ToArray(), displayResources = processStageLookup[activeStep.processStageId] .resourcesDisplayed .NullToEmpty() .Intersect(possibleResourceKeys.SelectKeys()) .ToArray(), editable = processStageLookup[activeStep.processStageId].editableIds.Contains(actorId), completable = processStageLookup[activeStep.processStageId].completableIds.Contains(actorId), }; return next(view); } catch (Exception ex) { return skip(); } }, (IEnumerable <ProcessResourceView> views) => onFound(views.ToArray())); }); }); }, () => onFound(new ProcessResourceView[] { }).ToTask())); }