public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
 {
     // at the time of creation, we only support the delete of shifts which are sent to the
     // integration as a put because when deleting a shift, Teams puts the shift into a draft
     // delete state which becomes final when the changes are shared
     return(CanHandleChangeRequest(changeRequest, ShiftChangeUriTemplate, out changeItemRequest) && changeRequest.Requests.Length == 1 && changeItemRequest.Method.Equals("put", StringComparison.OrdinalIgnoreCase));
 }
        public ChangeSuccessResult(ChangeResponse changeResponse, ChangeItemRequest changeItemRequest, string eTag = null)
            : base(changeResponse)
        {
            var changeItemResponse = changeResponse.Responses.First(cir => cir.Id == changeItemRequest.Id);

            changeItemResponse.Status    = (int)HttpStatusCode.OK;
            changeItemResponse.Body.Etag = eTag ?? GenerateEtag(changeItemResponse.Id);
        }
Ejemplo n.º 3
0
        public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
        {
            changeItemRequest = null;
            if (changeRequest.Requests.Length == 1 && ShiftPreferenceRequestUriTemplate.TryMatch(changeRequest.Requests[0].Url, out _))
            {
                changeItemRequest = changeRequest.Requests[0];
            }

            return(changeItemRequest != null);
        }
        public ChangeSuccessResult(ChangeResponse changeResponse, ChangeItemRequest changeItemRequest, List <string> data)
            : base(changeResponse)
        {
            var changeItemResponse = changeResponse.Responses.First(cir => cir.Id == changeItemRequest.Id);

            changeItemResponse.Status = (int)HttpStatusCode.OK;
            changeItemResponse.Body   = new ChangeItemDataResponseBody
            {
                Data = data
            };
        }
        public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
        {
            changeItemRequest = null;
            if (CanHandleChangeRequest(changeRequest, OpenShiftRequestUriTemplate, out ChangeItemRequest itemRequest))
            {
                if (itemRequest.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
                {
                    changeItemRequest = itemRequest;
                }
            }

            return(changeItemRequest != null);
        }
        private bool IsDeletedShift(ChangeItemRequest changeItemRequest)
        {
            var shift = changeItemRequest.Body.ToObject <ShiftChangeRequest>();

            // when the user chooses delete for a shift, the SharedShift details are copied to the
            // DraftShift and the IsActive flag for the DraftShift is set to false. If the user then
            // shares the individual delete to commit it, the integration is called again and the
            // DraftShift is set to null and the IsActive flag of the SharedShift is set to false.
            // HOWEVER, if the user chooses to share the entire schedule then the drafts are
            // committed but the integration is not called again, therefore we must perform the work
            // of the delete in the draft stage but also be able to handle the secondary commit stage
            return(shift?.DraftShift?.IsActive == false || shift?.SharedShift?.IsActive == false);
        }
Ejemplo n.º 7
0
        public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
        {
            changeItemRequest = null;
            if (base.CanHandleChangeRequest(changeRequest, out ChangeItemRequest itemRequest))
            {
                // a sender swap request is identified by the fact that the method is a POST
                if (itemRequest.Method.Equals("POST", StringComparison.OrdinalIgnoreCase))
                {
                    changeItemRequest = itemRequest;
                }
            }

            return(changeItemRequest != null);
        }
Ejemplo n.º 8
0
        public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
        {
            changeItemRequest = null;
            // in order for this handler to be able to handle this open shift change, it must
            // contain exactly two requests, one for the open shift being assigned and one for the
            // shift that is to be created from it
            if (changeRequest.Requests.Length == 2 &&
                CanHandleChangeRequest(changeRequest, OpenShiftChangeHandler.OpenShiftChangeUriTemplate, out ChangeItemRequest itemRequest) &&
                CanHandleChangeRequest(changeRequest, ShiftChangeHandler.ShiftChangeUriTemplate, out _))
            {
                changeItemRequest = itemRequest;
            }

            return(changeItemRequest != null);
        }
        private bool TryValidateChangeItemRequest(ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, out IActionResult validationResponse)
        {
            validationResponse = null;

            if (changeItemRequest.Headers == null || changeItemRequest.Headers.Count == 0)
            {
                return(true);
            }

            if (changeItemRequest.Headers.Expires.HasValue && DateTime.UtcNow > changeItemRequest.Headers.Expires.Value)
            {
                validationResponse = new ChangeErrorResult(changeResponse, changeItemRequest, ErrorCodes.RequestExpired, _stringLocalizer[ErrorCodes.RequestExpired]);
                return(false);
            }

            return(true);
        }
        public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
        {
            changeItemRequest = null;
            if (base.CanHandleChangeRequest(changeRequest, out ChangeItemRequest itemRequest))
            {
                if (itemRequest.Method.Equals("PUT", StringComparison.OrdinalIgnoreCase))
                {
                    // this is an approval, but is it a recipient approval
                    var swapRequest = itemRequest.Body.ToObject <SwapRequest>();
                    var state       = swapRequest.EvaluateState(itemRequest.Method);
                    if (state == ChangeRequestState.RecipientApproved || state == ChangeRequestState.RecipientDeclined)
                    {
                        changeItemRequest = itemRequest;
                    }
                }
            }

            return(changeItemRequest != null);
        }
        public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
        {
            changeItemRequest = null;
            if (CanHandleChangeRequest(changeRequest, OpenShiftRequestUriTemplate, out ChangeItemRequest itemRequest))
            {
                if (itemRequest.Method.Equals("PUT", StringComparison.OrdinalIgnoreCase))
                {
                    // this is an approval/decline, but is it a manager approval/decline
                    var openShiftRequest = itemRequest.Body.ToObject <OpenShiftsChangeRequest>();
                    var state            = openShiftRequest.EvaluateState(itemRequest.Method);
                    if (state == ChangeRequestState.ManagerApproved || state == ChangeRequestState.ManagerDeclined)
                    {
                        changeItemRequest = itemRequest;
                    }
                }
            }

            return(changeItemRequest != null);
        }
Ejemplo n.º 12
0
        public ChangeErrorResult(ChangeResponse changeResponse, ChangeItemRequest changeItemRequest, string errorCode, string errorMessage, bool dataErrorResponse, HttpStatusCode statusCode = HttpStatusCode.BadRequest)
            : base(changeResponse)
        {
            var changeItemResponse = changeResponse.Responses.First(cir => cir.Id == changeItemRequest.Id);

            changeItemResponse.Status = (int)statusCode;
            if (dataErrorResponse)
            {
                changeItemResponse.Body = new ChangeItemDataResponseBody();
            }

            changeItemResponse.Body.Error = new ChangeErrorResponse
            {
                Code    = errorCode,
                Message = errorMessage
            };

            // notwithstanding that this is an error result, the status code must still be 200 OK
            this.StatusCode = (int)HttpStatusCode.OK;
        }
        private async Task <IActionResult> DeleteShiftAsync(ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter)
        {
            var shift = await ScheduleCacheHelper.FindShiftByTeamsShiftIdAsync(changeItemRequest.Id, teamId, _teamOptions.PastWeeks, _teamOptions.FutureWeeks, _teamOptions.StartDayOfWeek, _scheduleCacheService, _systemTimeService).ConfigureAwait(false);

            if (shift != null)
            {
                var policy = GetConflictRetryPolicy(_teamOptions.RetryMaxAttempts, _teamOptions.RetryIntervalSeconds);
                try
                {
                    await policy.ExecuteAsync(() => RemoveShiftFromScheduleAsync(shift, teamId)).ConfigureAwait(false);
                    await ShareDelete(shift, teamId, starter).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    log.LogDeleteShiftException(ex, shift, teamId);
                    return(new ChangeErrorResult(changeResponse, ErrorCodes.InternalError, _stringLocalizer[ErrorCodes.InternalError], HttpStatusCode.InternalServerError));
                }

                log.LogDeleteShiftSuccess(shift, teamId);
            }

            return(new ChangeSuccessResult(changeResponse));
        }
Ejemplo n.º 14
0
        public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
        {
            changeItemRequest = null;
            if (CanHandleChangeRequest(changeRequest, OpenShiftRequestUriTemplate, out ChangeItemRequest itemRequest))
            {
                if (itemRequest.Method.Equals("delete", StringComparison.OrdinalIgnoreCase))
                {
                    changeItemRequest = itemRequest;
                }
                else if (itemRequest.Body != null)
                {
                    var openShiftRequest = itemRequest.Body.ToObject <OpenShiftsChangeRequest>();

                    if (openShiftRequest.AssignedTo.Equals(ChangeRequestAssignedTo.System, StringComparison.OrdinalIgnoreCase) &&
                        openShiftRequest.State.Equals(ChangeRequestPhase.Declined, StringComparison.OrdinalIgnoreCase))
                    {
                        changeItemRequest = itemRequest;
                    }
                }
            }

            return(changeItemRequest != null);
        }
        protected async Task <T> ReadRequestObjectAsync <T>(ChangeItemRequest changeItemRequest, string teamId) where T : IHandledRequest
        {
            try
            {
                if (changeItemRequest.Body != null)
                {
                    var request       = changeItemRequest.Body.ToObject <T>();
                    var cachedRequest = await _requestCacheService.LoadRequestAsync <T>(teamId, changeItemRequest.Id).ConfigureAwait(false);

                    if (cachedRequest != null)
                    {
                        request.FillTargetIds(cachedRequest);
                    }
                    return(request);
                }
                else
                {
                    return(await _requestCacheService.LoadRequestAsync <T>(teamId, changeItemRequest.Id).ConfigureAwait(false));
                }
            }
            catch
            {
                return(default);
        public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
        {
            // we cannot use the base class method here as we need to handle the invalid url's that
            // Teams is sending with this request
            changeItemRequest = null;
            if (changeRequest.Requests.Length == 1)
            {
                // the url as supplied by Teams looks something like the following:
                //"/shifts/SHFT_bff7961c-1bff-4c39-b2b1-7808eca233de/requestableShifts?requestType=SwapRequest&startTime=5/31/2020 11:00:00 PM +00:00&endTime=6/30/2020 10:59:59 PM +00:00"
                // because the datetime values are not valid in a url we are having to strip them so that the url parser
                // does not fail in the TryMatch method
                var request = changeRequest.Requests[0];
                var url     = request.Url.Substring(0, request.Url.IndexOf("&"));
                if (ShiftSwapFilterRequestUriTemplate.TryMatch(url, out var changeItemParams))
                {
                    if (changeItemParams.ContainsKey("requestType") && changeItemParams["requestType"].ToString().Equals("SwapRequest", StringComparison.OrdinalIgnoreCase))
                    {
                        changeItemRequest = request;
                    }
                }
            }

            return(changeItemRequest != null);
        }
        public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter)
        {
            var openShiftRequest = await ReadRequestObjectAsync <OpenShiftsChangeRequest>(changeItemRequest, teamId).ConfigureAwait(false);

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

            if (!await MapOpenShiftRequestIdentitiesAsync(openShiftRequest).ConfigureAwait(false))
            {
                return(new ChangeErrorResult(changeResponse, ErrorCodes.UserCredentialsNotFound, _stringLocalizer[ErrorCodes.UserCredentialsNotFound]));
            }

            var openShift = await GetOpenShift(teamId, openShiftRequest.OpenShiftId).ConfigureAwait(false);

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

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

            var wfmOpenShiftRequest = openShiftRequest.AsWfmOpenShiftRequest();

            wfmOpenShiftRequest.BuId           = connectionModel.WfmBuId;
            wfmOpenShiftRequest.TimeZoneInfoId = connectionModel.TimeZoneInfoId;
            wfmOpenShiftRequest.WfmOpenShift   = openShift;

            var wfmResponse = await _wfmActionService.CreateOpenShiftRequestAsync(wfmOpenShiftRequest, log).ConfigureAwait(false);

            if (wfmResponse.Success)
            {
                await _requestCacheService.SaveRequestAsync(teamId, openShiftRequest.Id, openShiftRequest).ConfigureAwait(false);

                if (_featureOptions.EnableOpenShiftAutoApproval)
                {
                    await AutoApproveRequestAsync(openShiftRequest, teamId, starter).ConfigureAwait(false);
                }

                return(new ChangeSuccessResult(changeResponse));
            }

            return(new ChangeErrorResult(changeResponse, ErrorCodes.ShiftNotAvailableToUser, _stringLocalizer[ErrorCodes.ShiftNotAvailableToUser]));
        }
        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]));
            }

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

            var wfmSwapRequest = swapRequest.AsWfmSwapRequest();

            wfmSwapRequest.BuId = connectionModel.WfmBuId;

            var wfmResponse = await _wfmActionService.CancelShiftSwapRequestAsync(wfmSwapRequest, log).ConfigureAwait(false);

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

            if (wfmResponse.Success)
            {
                await _requestCacheService.SaveRequestAsync(teamId, changeItemRequest.Id, swapRequest).ConfigureAwait(false);
            }

            // whether we successfully cancelled the swap request or not, ensure we clear the cache data
            await DeleteChangeDataAsync(swapRequest).ConfigureAwait(false);

            return(actionResult);
        }
Ejemplo n.º 19
0
        public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string userId, ILogger log, IDurableOrchestrationClient starter)
        {
            var preferenceRequest = changeItemRequest.Body.ToObject <ShiftPreferenceResponse>();

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

            var employee = await _cacheService.GetKeyAsync <EmployeeModel>(ApplicationConstants.TableNameEmployees, userId).ConfigureAwait(false);

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

            // get the conection object which contains TimeZoneInfoId to populate below availabilityModel.
            var connection = await _scheduleConnectorService.GetConnectionAsync(employee.TeamIds[0]).ConfigureAwait(false);

            // map the preference request to an availability model
            var availabilityModel = _availabilityMap.MapAvailability(preferenceRequest.Availability, userId);

            availabilityModel.WfmEmployeeId  = employee.WfmEmployeeId;
            availabilityModel.TimeZoneInfoId = connection.TimeZoneInfoId;

            var wfmResponse = await _wfmActionService.UpdateEmployeeAvailabilityAsync(availabilityModel, log).ConfigureAwait(false);

            return(WfmResponseToActionResult(wfmResponse, changeItemRequest, changeResponse));
        }
 public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter)
 {
     if (IsDeletedShift(changeItemRequest))
     {
         return(await DeleteShiftAsync(changeItemRequest, changeResponse, teamId, log, starter).ConfigureAwait(false));
     }
     else
     {
         // the connector does not currently support this operation
         return(new ChangeErrorResult(changeResponse, ErrorCodes.UnsupportedOperation, _stringLocalizer[ErrorCodes.UnsupportedOperation], HttpStatusCode.Forbidden));
     }
 }
        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);
        }
 public abstract bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest);
        protected static bool CanHandleChangeRequest(ChangeRequest changeRequest, UriTemplate uriTemplate, out ChangeItemRequest changeItemRequest)
        {
            changeItemRequest = null;
            foreach (var request in changeRequest.Requests)
            {
                if (uriTemplate.TryMatch(request.Url, out var changeItemParams))
                {
                    changeItemRequest = request;
                    break;
                }
            }

            return(changeItemRequest != null);
        }
 public abstract Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string entityId, ILogger log, IDurableOrchestrationClient starter);
Ejemplo n.º 25
0
        public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter)
        {
            log.LogTrace($"{nameof(SenderSwapRequestHandler)}:{nameof(HandleRequest)}:Started");

            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.SenderStatus == ChangeData.RequestStatus.InProgress)
            {
                changeData = await WaitAndReadChangeDataAsync(swapRequest).ConfigureAwait(false);

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

            if (changeData.SenderStatus == ChangeData.RequestStatus.Complete)
            {
                // we have already processed this request and it is complete so just return the
                // result we received the first time

                // before doing so, however, because Teams creates a new swap request ID each time,
                // get the previous version of the swap request and update the targetid's of this
                // new request and save it and delete the old one
                var oldSwapRequest = await _requestCacheService.LoadRequestAsync <SwapRequest>(teamId, changeData.SwapRequestId).ConfigureAwait(false);

                swapRequest.FillTargetIds(oldSwapRequest);
                await _requestCacheService.DeleteRequestAsync(teamId, changeData.SwapRequestId).ConfigureAwait(false);

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

                if (changeData.SenderResult.StatusCode == (int)HttpStatusCode.OK)
                {
                    return(new ChangeSuccessResult(changeResponse));
                }

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

            await MapSwapRequestIdentitiesAsync(swapRequest, teamId, log).ConfigureAwait(false);

            if (string.IsNullOrEmpty(swapRequest.TargetSenderShiftId))
            {
                return(new ChangeErrorResult(changeResponse, ErrorCodes.SenderShiftNotFound, _stringLocalizer[ErrorCodes.SenderShiftNotFound]));
            }
            else if (string.IsNullOrEmpty(swapRequest.TargetSenderLoginName))
            {
                return(new ChangeErrorResult(changeResponse, ErrorCodes.UserCredentialsNotFound, _stringLocalizer[ErrorCodes.UserCredentialsNotFound]));
            }
            else if (string.IsNullOrEmpty(swapRequest.TargetRecipientShiftId))
            {
                return(new ChangeErrorResult(changeResponse, ErrorCodes.RecipientShiftNotFound, _stringLocalizer[ErrorCodes.RecipientShiftNotFound]));
            }
            else if (string.IsNullOrEmpty(swapRequest.TargetRecipientLoginName))
            {
                return(new ChangeErrorResult(changeResponse, ErrorCodes.UserCredentialsNotFound, _stringLocalizer[ErrorCodes.UserCredentialsNotFound]));
            }

            // set the request in progress and store the swap request id in cache in case this call
            // times out and Teams makes a second request
            changeData.SenderStatus = ChangeData.RequestStatus.InProgress;
            // clear other statuses to avoid blocking subsequent requests for the same two shifts
            ResetChangeDataStatuses(changeData);
            changeData.SwapRequestId = changeItemRequest.Id;
            await SaveChangeDataAsync(swapRequest, changeData).ConfigureAwait(false);

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

            var wfmSwapRequest = swapRequest.AsWfmSwapRequest();

            wfmSwapRequest.BuId = connectionModel.WfmBuId;

            var wfmResponse = await _wfmActionService.CreateShiftSwapRequestAsync(wfmSwapRequest, log).ConfigureAwait(false);

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

            if (wfmResponse.Success)
            {
                swapRequest.TargetSwapRequestId = wfmSwapRequest.SwapRequestId;
                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);
            }

            // flag that the swap request creation has now finished
            changeData = await ReadChangeDataAsync(swapRequest).ConfigureAwait(false);

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

            return(actionResult);
        }
Ejemplo n.º 26
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 override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
 {
     return(CanHandleChangeRequest(changeRequest, OpenShiftChangeUriTemplate, out changeItemRequest) && changeRequest.Requests.Length == 1);
 }
Ejemplo n.º 28
0
 public override bool CanHandleChangeRequest(ChangeRequest changeRequest, out ChangeItemRequest changeItemRequest)
 {
     return(CanHandleChangeRequest(changeRequest, SwapRequestUriTemplate, out changeItemRequest));
 }
Ejemplo n.º 29
0
        public override async Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter)
        {
            var openShiftRequest = await ReadRequestObjectAsync <OpenShiftsChangeRequest>(changeItemRequest, teamId).ConfigureAwait(false);

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

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

            var wfmOpenShiftRequest = openShiftRequest.AsWfmOpenShiftRequest();

            wfmOpenShiftRequest.BuId = connectionModel.WfmBuId;

            var wfmResponse = await _wfmActionService.CancelOpenShiftRequestAsync(wfmOpenShiftRequest, log).ConfigureAwait(false);

            if (wfmResponse.Success)
            {
                await _requestCacheService.DeleteRequestAsync(teamId, changeItemRequest.Id).ConfigureAwait(false);

                return(new ChangeSuccessResult(changeResponse));
            }

            return(WfmErrorToActionResult(wfmResponse.Error, changeItemRequest, changeResponse));
        }
 public override Task <IActionResult> HandleRequest(ChangeRequest changeRequest, ChangeItemRequest changeItemRequest, ChangeResponse changeResponse, string teamId, ILogger log, IDurableOrchestrationClient starter)
 {
     return(Task.FromResult <IActionResult>(new ChangeErrorResult(changeResponse, ErrorCodes.UnsupportedOperation, _stringLocalizer[ErrorCodes.UnsupportedOperation], HttpStatusCode.Forbidden)));
 }