private async Task AutoApproveRequestAsync(OpenShiftsChangeRequest openShiftRequest, string teamId, IDurableOrchestrationClient starter)
 {
     var deferredActionModel = new DeferredActionModel
     {
         ActionType   = DeferredActionModel.DeferredActionType.ApproveOpenShiftRequest,
         DelaySeconds = _teamOptions.DelayedActionSeconds,
         RequestId    = openShiftRequest.Id,
         TeamId       = teamId
     };
     await starter.StartNewAsync(nameof(DeferredActionOrchestrator), deferredActionModel).ConfigureAwait(false);
 }
 private async Task ShareDelete(ShiftModel shift, string teamId, IDurableOrchestrationClient starter)
 {
     var deferredActionModel = new DeferredActionModel
     {
         ActionType     = DeferredActionModel.DeferredActionType.ShareTeamSchedule,
         DelaySeconds   = _teamOptions.DelayedActionSeconds,
         ShareStartDate = shift.StartDate,
         ShareEndDate   = shift.EndDate,
         TeamId         = teamId
     };
     await starter.StartNewAsync(nameof(DeferredActionOrchestrator), deferredActionModel).ConfigureAwait(false);
 }
 public async Task Run([ActivityTrigger] DeferredActionModel delayedActionModel, ILogger log)
 {
     log.LogApproveSwapShiftsRequestActivity(delayedActionModel);
     try
     {
         if (delayedActionModel.ActionType == DeferredActionModel.DeferredActionType.ApproveSwapShiftsRequest)
         {
             await _teamsService.ApproveSwapShiftsRequest(delayedActionModel.Message, delayedActionModel.RequestId, delayedActionModel.TeamId).ConfigureAwait(false);
         }
     }
     catch (Exception ex)
     {
         log.LogApproveSwapShiftsRequestActivityFailure(ex, delayedActionModel);
     }
 }
 public async Task Run([ActivityTrigger] DeferredActionModel delayedActionModel, ILogger log)
 {
     log.LogReviewOpenShiftRequestActivity(delayedActionModel);
     try
     {
         if (delayedActionModel.ActionType == DeferredActionModel.DeferredActionType.ApproveOpenShiftRequest)
         {
             await _teamsService.ApproveOpenShiftRequest(delayedActionModel.RequestId, delayedActionModel.TeamId).ConfigureAwait(false);
         }
         else if (delayedActionModel.ActionType == DeferredActionModel.DeferredActionType.DeclineOpenShiftRequest)
         {
             await _teamsService.DeclineOpenShiftRequest(delayedActionModel.RequestId, delayedActionModel.TeamId, delayedActionModel.Message).ConfigureAwait(false);
         }
     }
     catch (Exception ex)
     {
         // the automated approval could fail because all slots have already been allocated
         // to other users by the time the approval is processed
         log.LogReviewOpenShiftRequestActivityFailure(ex, delayedActionModel);
     }
 }
        public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter)
        {
            var swapRequest = await ReadRequestObjectAsync <SwapRequest>(changeItemRequest, teamId).ConfigureAwait(false);

            if (swapRequest == null)
            {
                return(new ChangeErrorResult(changeResponse, ErrorCodes.SwapRequestNotFound, _stringLocalizer[ErrorCodes.SwapRequestNotFound]));
            }

            // are we already processing a request, if so block premature retries
            var changeData = await ReadChangeDataAsync(swapRequest).ConfigureAwait(false);

            if (changeData.RecipientStatus == ChangeData.RequestStatus.InProgress)
            {
                changeData = await WaitAndReadChangeDataAsync(swapRequest).ConfigureAwait(false);

                if (changeData.RecipientStatus == ChangeData.RequestStatus.InProgress)
                {
                    return(new ChangeErrorResult(changeResponse, ErrorCodes.RequestInProgress, _stringLocalizer[ErrorCodes.RequestInProgress], HttpStatusCode.Processing));
                }
            }

            if (changeData.RecipientStatus == ChangeData.RequestStatus.Complete)
            {
                if (changeData.RecipientResult.StatusCode == (int)HttpStatusCode.OK)
                {
                    return(new ChangeSuccessResult(changeResponse));
                }

                return(new ChangeErrorResult(changeResponse, changeItemRequest, changeData.RecipientResult.ErrorCode, changeData.RecipientResult.ErrorMessage));
            }

            changeData.RecipientStatus = ChangeData.RequestStatus.InProgress;
            // clear other statuses to avoid blocking subsequent requests for the same two shifts
            ResetChangeDataStatuses(changeData);
            await SaveChangeDataAsync(swapRequest, changeData).ConfigureAwait(false);

            var connectionModel = await _scheduleConnectorService.GetConnectionAsync(teamId).ConfigureAwait(false);

            var wfmSwapRequest = swapRequest.AsWfmSwapRequest();

            wfmSwapRequest.BuId = connectionModel.WfmBuId;

            var approve     = swapRequest.EvaluateState(changeItemRequest.Method) == ChangeRequestState.RecipientApproved;
            var wfmResponse = await _wfmActionService.RecipientApproveShiftSwapRequestAsync(wfmSwapRequest, approve, log).ConfigureAwait(false);

            var actionResult = WfmResponseToActionResult(wfmResponse, changeItemRequest, changeResponse);

            if (wfmResponse.Success)
            {
                await _requestCacheService.SaveRequestAsync(teamId, changeItemRequest.Id, swapRequest).ConfigureAwait(false);
                await SaveChangeResultAsync(swapRequest).ConfigureAwait(false);
            }
            else
            {
                await SaveChangeResultAsync(swapRequest, HttpStatusCode.BadRequest, wfmResponse.Error.Code, wfmResponse.Error.Message).ConfigureAwait(false);
            }

            await _requestCacheService.SaveRequestAsync(teamId, changeItemRequest.Id, swapRequest).ConfigureAwait(false);

            // flag that the recipient approval has now finished
            changeData = await ReadChangeDataAsync(swapRequest).ConfigureAwait(false);

            changeData.RecipientStatus = ChangeData.RequestStatus.Complete;
            await SaveChangeDataAsync(swapRequest, changeData).ConfigureAwait(false);

            // if the auto manager approval feature is enabled, issue it now
            if (actionResult is ChangeSuccessResult && _featureOptions.EnableShiftSwapAutoApproval)
            {
                var deferredActionModel = new DeferredActionModel
                {
                    ActionType   = DeferredActionModel.DeferredActionType.ApproveSwapShiftsRequest,
                    DelaySeconds = _teamOptions.DelayedActionSeconds,
                    RequestId    = swapRequest.Id,
                    TeamId       = teamId,
                    Message      = _stringLocalizer.GetString("ApproveSwapShifts")
                };
                await starter.StartNewAsync(nameof(DeferredActionOrchestrator), deferredActionModel).ConfigureAwait(false);
            }

            return(actionResult);
        }
Example #6
0
        public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter)
        {
            // get the open shift from the change item
            var openShift = changeItemRequest.Body.ToObject <OpenShiftResponse>();
            // get the proposed shift to be created from the open shift
            var proposedShift = changeRequest.Requests
                                .Where(r => r.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
                                .Select(r => r.Body.ToObject <ShiftResponse>())
                                .First();

            var connectionModel = await _scheduleConnectorService.GetConnectionAsync(teamId).ConfigureAwait(false);

            // get the corresponding open shift from cache
            var localStartDate = openShift.SharedOpenShift.StartDateTime.ApplyTimeZoneOffset(connectionModel.TimeZoneInfoId);
            var weekStartDate  = localStartDate.StartOfWeek(_teamOptions.StartDayOfWeek);
            var scheduleId     = teamId + ApplicationConstants.OpenShiftsSuffix;
            var cacheModel     = await _scheduleCacheService.LoadScheduleAsync(scheduleId, weekStartDate).ConfigureAwait(false);

            var assignedShift = cacheModel.Tracked.FirstOrDefault(o => o.TeamsShiftId == openShift.Id);

            if (assignedShift == null)
            {
                // we didn't find an open shift with this ID
                return(new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.NoOpenShiftsFound, _stringLocalizer[ErrorCodes.NoOpenShiftsFound]));
            }

            // get the employee object for the manager who initiated the change
            var manager = await _cacheService.GetKeyAsync <EmployeeModel>(ApplicationConstants.TableNameEmployees, openShift.LastModifiedBy.User.Id).ConfigureAwait(false);

            if (manager == null)
            {
                return(new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.UserCredentialsNotFound, _stringLocalizer[ErrorCodes.UserCredentialsNotFound]));
            }

            // get the employee object for the user the open shift is assigned to
            var employee = await _cacheService.GetKeyAsync <EmployeeModel>(ApplicationConstants.TableNameEmployees, proposedShift.UserId).ConfigureAwait(false);

            if (employee == null)
            {
                return(new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.UserCredentialsNotFound, _stringLocalizer[ErrorCodes.UserCredentialsNotFound]));
            }

            var wfmResponse = await _wfmActionService.ManagerAssignOpenShiftAsync(assignedShift, manager, employee, connectionModel.WfmBuId, connectionModel.TimeZoneInfoId, log).ConfigureAwait(false);

            if (!wfmResponse.Success)
            {
                return(new ChangeErrorResult(changeResponse, changeItemRequest, wfmResponse.Error.Code, wfmResponse.Error.Message));
            }

            var policy = GetConflictRetryPolicy(_teamOptions.RetryMaxAttempts, _teamOptions.RetryIntervalSeconds);

            // as it has been assigned successfully, decrement the quantity
            assignedShift.Quantity--;

            // update the open shift in the cache
            await policy.ExecuteAsync(() => UpdateCachedOpenShiftsAsync(scheduleId, assignedShift, weekStartDate)).ConfigureAwait(false);

            // convert the open shift to a shift and add it to the week shifts cache
            assignedShift.Quantity        = 1;
            assignedShift.WfmEmployeeId   = employee.WfmEmployeeId;
            assignedShift.TeamsEmployeeId = employee.TeamsEmployeeId;
            assignedShift.TeamsShiftId    = proposedShift.Id;
            assignedShift.WfmShiftId      = wfmResponse.NewEntityId;

            await policy.ExecuteAsync(() => UpdateCachedShiftsAsync(teamId, assignedShift, weekStartDate)).ConfigureAwait(false);

            // finally, set up a deferred action to share the schedule
            var deferredActionModel = new DeferredActionModel
            {
                ActionType     = DeferredActionModel.DeferredActionType.ShareTeamSchedule,
                DelaySeconds   = _teamOptions.DelayedActionSeconds,
                ShareStartDate = openShift.SharedOpenShift.StartDateTime.Date,
                ShareEndDate   = openShift.SharedOpenShift.EndDateTime.Date.AddHours(23).AddMinutes(59),
                TeamId         = teamId
            };
            await starter.StartNewAsync(nameof(DeferredActionOrchestrator), deferredActionModel).ConfigureAwait(false);

            return(new ChangeSuccessResult(changeResponse));
        }
 public static void LogApproveSwapShiftsRequestActivityFailure(this ILogger log, Exception ex, DeferredActionModel delayedActionModel)
 {
     log.LogError(EventIds.ApproveSwapShiftsRequest, ex, "ApproveSwapShiftsRequest: Status={status} RequestId={requestId}, TeamId={teamId}, Message={message}", Status.Failed, delayedActionModel.RequestId, delayedActionModel.TeamId, delayedActionModel.Message);
 }
 public static void LogApproveSwapShiftsRequestActivity(this ILogger log, DeferredActionModel delayedActionModel)
 {
     log.LogInformation(EventIds.ApproveSwapShiftsRequest, "ApproveSwapShiftsRequest: Stage={stage} RequestId={requestId}, TeamId={teamId}, Message={message}", Stage.Start, delayedActionModel.RequestId, delayedActionModel.TeamId, delayedActionModel.Message);
 }