public async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Admin, "post", Route = "clearschedule")] ClearScheduleModel clearScheduleModel,
            [DurableClient] IDurableOrchestrationClient starter,
            ILogger log)
        {
            if (!clearScheduleModel.IsValid())
            {
                return(new BadRequestResult());
            }

            // ensure that the team's orchestrators will not execute by disabling them
            await _scheduleConnectorService.UpdateEnabledAsync(clearScheduleModel.TeamId, false).ConfigureAwait(false);

            // get the connection model as we need the time zone information for the team
            var connectionModel = await _scheduleConnectorService.GetConnectionAsync(clearScheduleModel.TeamId).ConfigureAwait(false);

            try
            {
                SetStartAndEndDates(clearScheduleModel, connectionModel.TimeZoneInfoId);
            }
            catch (ArgumentException ex)
            {
                return(new BadRequestObjectResult(ex.Message));
            }

            if (await starter.TryStartSingletonAsync(nameof(ClearScheduleOrchestrator), ClearScheduleOrchestrator.InstanceId(clearScheduleModel.TeamId), clearScheduleModel).ConfigureAwait(false))
            {
                return(new OkResult());
            }
            else
            {
                return(new ConflictResult());
            }
        }
        public async Task <ResultModel> Run([ActivityTrigger] ClearScheduleModel clearScheduleModel, ILogger log)
        {
            var resultModel = new ResultModel();

            try
            {
                var batchSize = clearScheduleModel.QueryEndDate.HasValue ? _options.ClearScheduleMaxBatchSize : _options.ClearScheduleBatchSize;

                var shifts = await _scheduleDestinationService.ListShiftsAsync(clearScheduleModel.TeamId, clearScheduleModel.StartDate, clearScheduleModel.QueryEndDate ?? clearScheduleModel.EndDate, batchSize);

                // restrict the shifts to delete to those that actually started between the start and end dates
                shifts = shifts.Where(s => s.StartDate < clearScheduleModel.EndDate).ToList();
                if (shifts.Count > 0)
                {
                    var tasks = shifts
                                .Select(shift => TryDeleteShiftAsync(clearScheduleModel, shift, log))
                                .ToArray();

                    var result = await Task.WhenAll(tasks);

                    resultModel.DeletedCount = result.Count(r => r == true);
                }

                resultModel.Finished = shifts.Count == 0;
            }
            catch (Exception ex)
            {
                log.LogShiftError(ex, clearScheduleModel, nameof(_scheduleDestinationService.ListShiftsAsync));
            }

            return(resultModel);
        }
        /// <summary>
        /// The purpose of this method is to set the StartDate and EndDate values on the
        /// ClearScheduleModel thus defining the full range of the dates in complete weeks that will
        /// be cleared.
        /// </summary>
        /// <param name="clearScheduleModel">The model sent by the caller.</param>
        /// <remarks>
        /// Users can supply these dates in which case we need to ensure that whatever they entered,
        /// the start date is the first day of that week and the end date is the last day of that
        /// week. If the dates are not supplied by the user but they did supply values for the
        /// PastWeeks and FutureWeeks then we will compute the start and end dates on the basis of
        /// those. Finally if the user supplied neither start and end dates nor past and future
        /// weeks then we will use the past and future weeks configured for the syncs to compute the
        /// dates. In addition to the start and end dates, this method also sets the utc start and
        /// end times of the date range because we have to use utc dates when calling the graph api
        /// to get the list of items to be deleted.
        /// </remarks>
        private void SetStartAndEndDates(ClearScheduleModel clearScheduleModel, string timeZoneInfoId)
        {
            var pastWeeks   = clearScheduleModel.PastWeeks ?? _options.PastWeeks;
            var futureWeeks = clearScheduleModel.FutureWeeks ?? _options.FutureWeeks;

            if (clearScheduleModel.StartDate == default)
            {
                // if the start and end dates have not been explicitly supplied then we must compute
                // them from the past and future weeks values relative to today
                clearScheduleModel.StartDate = _timeService.UtcNow
                                               .StartOfWeek(_options.StartDayOfWeek)
                                               .AddWeeks(-pastWeeks)
                                               .Date;

                clearScheduleModel.EndDate = _timeService.UtcNow
                                             .StartOfWeek(_options.StartDayOfWeek)
                                             .AddWeeks(futureWeeks + 1)
                                             .AddDays(-1)
                                             .Date;
            }
            else
            {
                // ensure that the start date is the start of the week of the start date because we
                // cannot handle periods of less than 1 full week because of the clearcacheactivity
                // which deletes the cache for the whole week
                clearScheduleModel.StartDate = clearScheduleModel.StartDate.StartOfWeek(_options.StartDayOfWeek);

                // because a start date has been specified, we need to ensure that an end date has
                // also been supplied or if not use the start date to default it to the end date of
                // the same week
                if (clearScheduleModel.EndDate == default)
                {
                    // e.g. startdate = 23/08/2020 then end date = 29/08/2020
                    clearScheduleModel.EndDate = clearScheduleModel.StartDate.AddDays(6);
                }
                else
                {
                    // make sure the end date is the end date for the week it appears within
                    clearScheduleModel.EndDate = clearScheduleModel.EndDate.StartOfWeek(_options.StartDayOfWeek).AddDays(6);
                }
            }

            // validate the start date is < end date
            if (clearScheduleModel.StartDate > clearScheduleModel.EndDate)
            {
                throw new ArgumentException("End date must be after start date.", nameof(clearScheduleModel));
            }

            // ensure that the end date is the end of the day i.e. 23:59:59 and not the start i.e. 00:00:00
            clearScheduleModel.EndDate = clearScheduleModel.EndDate.AddDays(1).AddSeconds(-1);

            clearScheduleModel.UtcStartDate = clearScheduleModel.StartDate.ConvertFromLocalTime(timeZoneInfoId, _timeService);
            clearScheduleModel.UtcEndDate   = clearScheduleModel.EndDate.ConvertFromLocalTime(timeZoneInfoId, _timeService);
        }
        public async Task Run([ActivityTrigger] ClearScheduleModel clearScheduleModel, ILogger log)
        {
            var pastWeeks   = clearScheduleModel.PastWeeks ?? _options.PastWeeks;
            var futureWeeks = clearScheduleModel.FutureWeeks ?? _options.FutureWeeks;

            var weeksRange = DateTime.Today
                             .Range(pastWeeks, futureWeeks, _options.StartDayOfWeek);

            foreach (var week in weeksRange)
            {
                await _scheduleCacheService.DeleteScheduleAsync(clearScheduleModel.TeamId, week);
            }
        }
Esempio n. 5
0
        private async Task <bool> TryDeleteOpenShiftAsync(ClearScheduleModel clearScheduleModel, ShiftModel shift, ILogger log)
        {
            try
            {
                await _teamsService.DeleteOpenShiftAsync(clearScheduleModel.TeamId, shift, true).ConfigureAwait(false);

                return(true);
            }
            catch (Exception ex)
            {
                log.LogShiftError(ex, clearScheduleModel, nameof(_teamsService.DeleteOpenShiftAsync), shift, "OpenShift");
                return(false);
            }
        }
        private async Task <bool> TryDeleteTimeOffAsync(ClearScheduleModel clearScheduleModel, TimeOffModel timeOff, ILogger log)
        {
            try
            {
                await _teamsService.DeleteTimeOffAsync(clearScheduleModel.TeamId, timeOff, true).ConfigureAwait(false);

                return(true);
            }
            catch (Exception ex)
            {
                log.LogTimeOffError(ex, clearScheduleModel, nameof(_teamsService.DeleteTimeOffAsync), timeOff);
                return(false);
            }
        }
        private async Task <bool> TryDeleteShiftAsync(ClearScheduleModel clearScheduleModel, ShiftModel shift, ILogger log)
        {
            try
            {
                await _scheduleDestinationService.DeleteShiftAsync(clearScheduleModel.TeamId, shift);

                return(true);
            }
            catch (Exception ex)
            {
                log.LogShiftError(ex, clearScheduleModel, nameof(_scheduleDestinationService.DeleteShiftAsync), shift);
                return(false);
            }
        }
        public async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Admin, "post", Route = "clearschedule")] ClearScheduleModel clearScheduleModel,
            [OrchestrationClient] DurableOrchestrationClient starter,
            ILogger log)
        {
            if (!clearScheduleModel.IsValid())
            {
                return(new BadRequestResult());
            }

            if (await starter.TryStartSingletonAsync(nameof(ClearScheduleOrchestrator), clearScheduleModel.InstanceId, clearScheduleModel))
            {
                return(new OkResult());
            }
            else
            {
                return(new ConflictResult());
            }
        }
Esempio n. 9
0
        public async Task Run([ActivityTrigger] ClearScheduleModel clearScheduleModel, ILogger log)
        {
            try
            {
                var shifts = await _teamsService.ListOpenShiftsAsync(clearScheduleModel.TeamId, clearScheduleModel.UtcStartDate, clearScheduleModel.QueryEndDate ?? clearScheduleModel.UtcEndDate, _options.ClearScheduleBatchSize).ConfigureAwait(false);

                // restrict the shifts to delete to those that actually started between the start
                // and end dates
                shifts = shifts.Where(s => s.StartDate < clearScheduleModel.UtcEndDate).ToList();
                if (shifts.Count > 0)
                {
                    var tasks = shifts
                                .Select(shift => TryDeleteOpenShiftAsync(clearScheduleModel, shift, log))
                                .ToArray();

                    await Task.WhenAll(tasks).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                log.LogShiftError(ex, clearScheduleModel, nameof(_teamsService.ListOpenShiftsAsync), "OpenShift");
            }
        }
Esempio n. 10
0
        public async Task Run([ActivityTrigger] ClearScheduleModel clearScheduleModel, ILogger log)
        {
            var weeksRange = clearScheduleModel.StartDate
                             .Range(clearScheduleModel.EndDate, _options.StartDayOfWeek);

            foreach (var week in weeksRange)
            {
                if (clearScheduleModel.ClearShifts)
                {
                    await _scheduleCacheService.DeleteScheduleAsync(clearScheduleModel.TeamId, week).ConfigureAwait(false);
                }

                if (_featureOptions.EnableOpenShiftSync && clearScheduleModel.ClearOpenShifts)
                {
                    await _scheduleCacheService.DeleteScheduleAsync(clearScheduleModel.TeamId + ApplicationConstants.OpenShiftsSuffix, week).ConfigureAwait(false);
                }

                if (_featureOptions.EnableTimeOffSync && clearScheduleModel.ClearTimeOff)
                {
                    await _timeOffCacheService.DeleteTimeOffAsync(clearScheduleModel.TeamId, week).ConfigureAwait(false);
                }
            }
        }
        public async Task Run([ActivityTrigger] ClearScheduleModel clearScheduleModel, ILogger log)
        {
            try
            {
                var timeOffRecs = await _teamsService.ListTimeOffAsync(clearScheduleModel.TeamId, clearScheduleModel.UtcStartDate, clearScheduleModel.QueryEndDate ?? clearScheduleModel.UtcEndDate, _options.ClearScheduleBatchSize).ConfigureAwait(false);

                // restrict the time off to delete those that actually started between the start and
                // end dates
                timeOffRecs = timeOffRecs.Where(s => s.StartDate < clearScheduleModel.UtcEndDate).ToList();
                if (timeOffRecs.Count > 0)
                {
                    var tasks = timeOffRecs
                                .Select(timeOff => TryDeleteTimeOffAsync(clearScheduleModel, timeOff, log))
                                .ToArray();

                    await Task.WhenAll(tasks).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                log.LogTimeOffError(ex, clearScheduleModel, nameof(_teamsService.ListTimeOffAsync));
            }
        }
Esempio n. 12
0
 public static void LogShiftError(this ILogger log, Exception ex, ClearScheduleModel clear, string operationName)
 {
     log.LogError(new EventId(9, "Shift"), ex, "Shift: Status={status}, OperationName={operationName}, TeamId={teamId}, DayDate={dayDate}", Status.Failed, operationName, clear.TeamId, clear.StartDate.AsDateString());
 }
Esempio n. 13
0
 public static void LogShiftError(this ILogger log, Exception ex, ClearScheduleModel clear, string operationName, ShiftModel shift)
 {
     log.LogError(new EventId(9, "Shift"), ex, "Shift: Status={status}, OperationName={operationName}, SourceId={sourceId}, DestinationId={destinationId}, EmployeeId={employeeId}, TeamId={teamId}, DayDate={dayDate}", Status.Failed, operationName, shift.JdaShiftId, shift.TeamsShiftId, shift.JdaEmployeeId, clear.TeamId, clear.StartDate.AsDateString());
 }
Esempio n. 14
0
 public static void LogClearEnd(this ILogger log, ClearScheduleModel clear, ResultModel resultModel)
 {
     log.Log(resultModel.LogLevel, new EventId(12, "Clear"), "Clear: Stage={stage}, TeamId={teamId}, StartDate={startDate}, DeletedCount={deletedCount}, IterationCount={iterationCount}, IsFinished={isFinished}", Stage.End, clear.TeamId, clear.StartDate.AsDateString(), resultModel.DeletedCount, resultModel.IterationCount, resultModel.Finished);
 }
Esempio n. 15
0
 public static void LogClearStart(this ILogger log, ClearScheduleModel clear)
 {
     log.LogInformation(new EventId(12, "Clear"), "Clear: Stage={stage}, TeamId={teamId}, StartDate={startDate}, EndDate={endDate}", Stage.Start, clear.TeamId, clear.StartDate.AsDateString(), clear.EndDate.AsDateString());
 }
 public static void LogClearStart(this ILogger log, ClearScheduleModel clear, string clearType)
 {
     log.LogInformation(EventIds.ClearSchedule, "Clear{clearType}: Stage={stage}, TeamId={teamId}, StartDate={startDate}, EndDate={endDate}, UtcStartDate={utcStartDate}, UtcEndDate={utcEndDate}, ClearOpenShifts={clearOpenShifts}, ClearSchedulingGroups={clearSchedulingGroups}, ClearShifts={clearShifts}, ClearTimeOff={clearTimeOff}", clearType, Stage.Start, clear.TeamId, clear.StartDate.AsDateString(), clear.EndDate.AsDateString(), clear.UtcStartDate.AsDateString(), clear.UtcEndDate.AsDateString(), clear.ClearOpenShifts, clear.ClearSchedulingGroups, clear.ClearShifts, clear.ClearTimeOff);
 }
 public static void LogTimeOffError(this ILogger log, Exception ex, ClearScheduleModel clearModel, string operationName, TimeOffModel timeOff)
 {
     log.LogError(EventIds.TimeOff, ex, "TimeOff: Status={status}, OperationName={operationName}, DestinationId={destinationId}, EmployeeId={employeeId}, TeamId={teamId}, StartDate={startDate}, EndDate={endDate}", Status.Failed, operationName, timeOff.TeamsTimeOffId, timeOff.TeamsEmployeeId, clearModel.TeamId, clearModel.StartDate.AsDateString(), clearModel.EndDate.AsDateString());
 }
 public static void LogClearEnd(this ILogger log, ClearScheduleModel clear, string clearType, ResultModel resultModel)
 {
     log.Log(resultModel.LogLevel, EventIds.ClearSchedule, "Clear{clearType}: Stage={stage}, TeamId={teamId}, StartDate={startDate}, EndDate={endDate}, UtcStartDate={utcStartDate}, UtcEndDate={utcEndDate}, DeletedCount={deletedCount}", clearType, Stage.End, clear.TeamId, clear.StartDate.AsDateString(), clear.EndDate.AsDateString(), clear.UtcStartDate.AsDateString(), clear.UtcEndDate.AsDateString(), resultModel.DeletedCount);
 }
 public static void LogTimeOffError(this ILogger log, Exception ex, ClearScheduleModel clear, string operationName)
 {
     log.LogError(EventIds.ClearSchedule, ex, "TimeOff: Status={status}, OperationName={operationName}, TeamId={teamId}, StartDate={startDate}, EndDate={endDate}, UtcStartDate={utcStartDate}, UtcEndDate={utcEndDate}", Status.Failed, operationName, clear.TeamId, clear.StartDate.AsDateString(), clear.EndDate.AsDateString(), clear.UtcStartDate.AsDateString(), clear.UtcEndDate.AsDateString());
 }