示例#1
0
        public async Task <ResultModel> Run([ActivityTrigger] WeekModel weekModel, ILogger log)
        {
            log.LogWeek(weekModel);

            // initialise source service
            var credentials = await _secretsService.GetCredentialsAsync(weekModel.TeamId);

            _scheduleSourceService.SetCredentials(weekModel.TeamId, credentials);

            // get the current set of shifts from JDA
            var shifts = await _scheduleSourceService.ListWeekShiftsAsync(weekModel.TeamId, weekModel.StoreId, weekModel.StartDate, weekModel.TimeZoneInfoId);

            log.LogShifts(weekModel, shifts);

            // get the last saved set of shifts
            var savedShifts = await _scheduleCacheService.LoadScheduleAsync(weekModel.TeamId, weekModel.StartDate);

            // compute the delta
            var delta = _scheduleDeltaService.ComputeDelta(savedShifts.Tracked, shifts);

            log.LogFullDelta(weekModel, delta);

            if (delta.HasChanges)
            {
                delta.RemoveSkipped(savedShifts.Skipped);
                delta.ApplyMaximum(_options.MaximumDelta);

                log.LogPartialDelta(weekModel, delta);

                var allShifts = delta.All;

                // set teams employee
                var employeeLookup = BuildEmployeeLookup(savedShifts.Tracked);
                await SetTeamsEmployeeIds(allShifts.Where(s => string.IsNullOrEmpty(s.TeamsEmployeeId)), employeeLookup, weekModel, log);

                // set job & department name
                var jobLookup = BuildJobLookup(savedShifts.Tracked);
                await SetJobAndDepartmentName(allShifts, jobLookup, weekModel, log);

                // set teams schedule group (N.B this must be set after teams employee id and jobs)
                var groupLookup = BuildScheduleGroupLookup(savedShifts.Tracked);
                await SetTeamsSchedulingGroupId(allShifts.Where(s => string.IsNullOrEmpty(s.TeamsSchedulingGroupId)), groupLookup, weekModel, log);
                await AddEmployeesToSchedulingGroups(delta, groupLookup, weekModel, log);

                // update teams
                await ApplyDeltaAsync(weekModel, delta, log);

                log.LogAppliedDelta(weekModel, delta);

                // apply the final delta to the savedShifts
                delta.ApplyChanges(savedShifts.Tracked);
                delta.ApplySkipped(savedShifts.Skipped);

                await _scheduleCacheService.SaveScheduleAsync(weekModel.TeamId, weekModel.StartDate, savedShifts);
            }

            return(delta.AsResult());
        }
示例#2
0
        public async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "subscribe")] SubscribeModel subscribeModel,
            [OrchestrationClient] DurableOrchestrationClient starter,
            ILogger log)
        {
            // validate model
            if (!subscribeModel.IsValid())
            {
                log.LogError("Validating model failed.");
                return(new BadRequestResult());
            }

            // validate with JDA
            var credentials = subscribeModel.AsCredentialsModel();

            _scheduleSourceService.SetCredentials(subscribeModel.TeamId, credentials);

            StoreModel store;

            try
            {
                store = await _scheduleSourceService.GetStoreAsync(subscribeModel.TeamId, subscribeModel.StoreId).ConfigureAwait(false);
            }
            catch (ArgumentException e)
            {
                log.LogError(e, "Subscribe failed - JDA store id incorrect.");
                return(new BadRequestResult());
            }
            catch (KeyNotFoundException e)
            {
                log.LogError(e, "Subscribe failed - JDA store not found.");
                return(new NotFoundResult());
            }
            catch (UnauthorizedAccessException e)
            {
                log.LogError(e, "Subscribe failed - Invalid url or credentials.");
                return(new UnauthorizedResult());
            }

            // ensure that we can map the timezone for the store
            var timeZoneInfoId = await TimeZoneHelper.GetTimeZoneAsync(subscribeModel.TeamId, store.TimeZoneId, _timeZoneService, _scheduleSourceService, _scheduleConnectorService, log).ConfigureAwait(false);

            if (timeZoneInfoId == null)
            {
                log.LogError($"Subscribe failed - No time zone mapping found for store TimeZoneId={store.TimeZoneId}.");
                return(new InternalServerErrorResult());
            }

            // exchange and save access token
            if (!string.IsNullOrEmpty(subscribeModel.AuthorizationCode))
            {
                var tokenResponse = await _httpClientFactory.Client.RequestTokenAsync(_options, subscribeModel.RedirectUri, subscribeModel.AuthorizationCode).ConfigureAwait(false);

                if (tokenResponse.IsError)
                {
                    log.LogError("Subscribe failed - Invalid authorization code.");
                    return(new ForbidResult());
                }

                var tokenModel = tokenResponse.AsTokenModel();

                await _secretsService.SaveTokenAsync(subscribeModel.TeamId, tokenModel).ConfigureAwait(false);
            }
            else if (!string.IsNullOrEmpty(subscribeModel.AccessToken))
            {
                var tokenModel = subscribeModel.AsTokenModel();

                await _secretsService.SaveTokenAsync(subscribeModel.TeamId, tokenModel).ConfigureAwait(false);
            }

            // save JDA creds
            await _secretsService.SaveCredentialsAsync(subscribeModel.TeamId, credentials).ConfigureAwait(false);

            // get the team from Teams
            GroupModel team;

            try
            {
                team = await _scheduleDestinationService.GetTeamAsync(subscribeModel.TeamId).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                log.LogError(e, "Subscribe failed - Not authorized to access details for the team.");
                return(new ForbidResult());
            }

            var teamModel = subscribeModel.AsTeamModel();

            teamModel.TimeZoneInfoId = timeZoneInfoId;

            var connectionModel = subscribeModel.AsConnectionModel();

            connectionModel.TimeZoneInfoId = timeZoneInfoId;
            connectionModel.StoreName      = store.StoreName;
            connectionModel.TeamName       = team.Name;

            try
            {
                // ensure that if the team is re-subscribing, that they haven't changed the store
                // that they are connecting to
                var existingModel = await _scheduleConnectorService.GetConnectionAsync(subscribeModel.TeamId).ConfigureAwait(false);

                if (connectionModel.StoreId != existingModel.StoreId)
                {
                    log.LogError("Re-subscribe failed - JDA store id changed.");
                    return(new BadRequestResult());
                }
                else
                {
                    // as the team is re-subscribing, ensure that the schedule is not re-initialized
                    teamModel.Initialized = true;
                }
            }
            catch { /* nothing to do - new subscription */ }

            // save connection settings
            await _scheduleConnectorService.SaveConnectionAsync(connectionModel).ConfigureAwait(false);

            // start singleton team orchestrator
            await starter.TryStartSingletonAsync(nameof(TeamOrchestrator), teamModel.TeamId, teamModel).ConfigureAwait(false);

            return(new OkObjectResult(new StoreModel
            {
                StoreId = connectionModel.StoreId,
                StoreName = connectionModel.StoreName
            }));
        }
示例#3
0
        public async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Admin, "get", Route = "teamHealth/{teamId}")] HttpRequest req,
            [OrchestrationClient] DurableOrchestrationClient starter,
            string teamId,
            ILogger log)
        {
            try
            {
                var connection = await _scheduleConnectorService.GetConnectionAsync(teamId);

                var credentials = await _secretsService.GetCredentialsAsync(connection.TeamId);

                _scheduleSourceService.SetCredentials(connection.TeamId, credentials);

                var date = DateTime.Today;
                if (req.Query.ContainsKey("date"))
                {
                    DateTime.TryParse(req.Query["date"], out date);
                }
                var weekStartDate = date.StartOfWeek(_options.StartDayOfWeek);

                var jdaEmployees = await _scheduleSourceService.GetEmployeesAsync(connection.TeamId, connection.StoreId, weekStartDate);
                await UpdateEmployeesWithTeamsData(connection.TeamId, jdaEmployees);

                var cachedShifts = await GetCachedShiftsAsync(connection, weekStartDate);

                var jobIds = cachedShifts.SelectMany(s => s.Jobs).Select(j => j.JdaJobId).Distinct().ToList();
                var jobs   = await GetJobsAsync(connection, jobIds);

                ExpandIds(cachedShifts, jdaEmployees, jobs);

                var missingUsers = jdaEmployees.Where(e => string.IsNullOrEmpty(e.DestinationId)).ToList();

                var missingShifts = await GetMissingShifts(connection, weekStartDate, cachedShifts);

                jobIds = missingShifts.SelectMany(s => s.Jobs).Select(j => j.JdaJobId).Distinct().ToList();
                jobs   = await GetJobsAsync(connection, jobIds);

                ExpandIds(missingShifts, missingUsers, jobs);

                var teamHealthResponseModel = new TeamHealthResponseModel
                {
                    TeamId                 = connection.TeamId,
                    WeekStartDate          = weekStartDate,
                    TeamOrchestratorStatus = await starter.GetStatusAsync(connection.TeamId),
                    MissingUsers           = missingUsers,
                    MissingShifts          = missingShifts,
                    CachedShifts           = cachedShifts
                };

                return(new JsonResult(teamHealthResponseModel, new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore
                }));
            }
            catch (Exception ex)
            {
                log.LogError(ex, "Team health failed!");
                return(new ContentResult
                {
                    Content = $"Unexpected exception: {ex.Message}",
                    StatusCode = StatusCodes.Status500InternalServerError
                });
            }
        }