public async Task Handle(PeriodEndStartedEvent message, IMessageHandlerContext context)
        {
            logger.LogDebug($"Received period end started event. Details: {message.ToJson()}");

            var fabricClient       = new FabricClient();
            var serviceDescription = new DeleteServiceDescription(new Uri(ServiceNames.DatalockApprovalsService))
            {
                ForceDelete = true,
            };

            await fabricClient.ServiceManager.DeleteServiceAsync(serviceDescription);

            logger.LogInfo($"Finished period end started handler. Details: {message.ToJson()}");
        }
        public async Task Handle(PeriodEndStoppedEvent message, IMessageHandlerContext context)
        {
            logger.LogInfo($"Processing Month End Event for Message Id : {context.MessageId}");

            var currentExecutionContext = (ESFA.DC.Logging.ExecutionContext)executionContext;

            currentExecutionContext.JobId = message.JobId.ToString();

            logger.LogDebug($"Processing period end event. Collection: {message.CollectionPeriod.Period:00}-{message.CollectionPeriod.AcademicYear}, job: {message.JobId}");
            var commands = await periodEndService.GenerateProviderMonthEndCommands(message).ConfigureAwait(false);

            if (!commands.Any())
            {
                logger.LogWarning($"No Provider Ukprn found for period end payment {message.CollectionPeriod.Period:00}-{message.CollectionPeriod.AcademicYear}, job: {message.JobId}");
                return;
            }
            foreach (var command in commands)
            {
                logger.LogDebug($"Sending month end command for provider: {command.Ukprn}");
                await context.SendLocal(command).ConfigureAwait(false);
            }
            logger.LogInfo($"Successfully processed Period End Event for {message.CollectionPeriod.Period:00}-{message.CollectionPeriod.AcademicYear}, job: {message.JobId}");
        }
예제 #3
0
        public async Task <bool> WaitForJobToFinish(long jobId, CancellationToken cancellationToken)
        {
            //TODO: Temp brittle solution to wait for jobs to finish
            logger.LogDebug($"Waiting for job {jobId} to finish.");
            var endTime = DateTime.Now.Add(config.TimeToWaitForJobToComplete);

            while (DateTime.Now < endTime)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var job = await dataContext.GetJobByDcJobId(jobId).ConfigureAwait(false);

                if (job != null && (job.DataLocksCompletionTime != null ||
                                    job.Status != Monitoring.Jobs.Model.JobStatus.InProgress))
                {
                    logger.LogInfo($"DC Job {jobId} finished. Status: {job.Status:G}.  Finish time: {job.EndTime:G}");
                    return(true);
                }
                logger.LogVerbose($"DC Job {jobId} is still in progress");
                await Task.Delay(config.TimeToPauseBetweenChecks);
            }
            logger.LogWarning($"Waiting {config.TimeToWaitForJobToComplete} but Job {jobId} still not finished.");
            return(false);
        }
        public async Task Process(ApprenticeshipCreatedEvent createdEvent)
        {
            try
            {
                logger.LogDebug($"Now processing the apprenticeship created event. " +
                                $"Apprenticeship id: {createdEvent.ApprenticeshipId}, " +
                                $"employer account id: {createdEvent.AccountId}, " +
                                $"Ukprn: {createdEvent.ProviderId}.");
                var model      = mapper.Map <ApprenticeshipModel>(createdEvent);
                var duplicates = await apprenticeshipService.NewApprenticeship(model).ConfigureAwait(false);

                logger.LogDebug($"Apprenticeship saved to database. " +
                                $"Apprenticeship id: {createdEvent.ApprenticeshipId}, " +
                                $"employer account id: {createdEvent.AccountId}, " +
                                $"Ukprn: {createdEvent.ProviderId}.");

                var updatedEvent = mapper.Map <ApprenticeshipUpdated>(model);
                updatedEvent.Duplicates = duplicates.Select(duplicate => new ApprenticeshipDuplicate
                {
                    Ukprn = duplicate.Ukprn, ApprenticeshipId = duplicate.ApprenticeshipId
                }).ToList();
                var endpointInstance = await endpointInstanceFactory.GetEndpointInstance().ConfigureAwait(false);

                await endpointInstance.Publish(updatedEvent).ConfigureAwait(false);

                logger.LogInfo($"Finished processing the apprenticeship created event. " +
                               $"Apprenticeship id: {createdEvent.ApprenticeshipId}, " +
                               $"employer account id: {createdEvent.AccountId}, " +
                               $"Ukprn: {createdEvent.ProviderId}.");
            }
            catch (ApprenticeshipAlreadyExistsException e)
            {
                logger.LogWarning($"Apprenticeship already exists while trying to add a new apprenticeship: {e.Message}\n" +
                                  $"Apprenticeship id: {createdEvent.ApprenticeshipId}, " +
                                  $"employer account id: {createdEvent.AccountId}, " +
                                  $"Ukprn: {createdEvent.ProviderId}.");
            }
            catch (InvalidOperationException e)
            {
                logger.LogError($"Unhandled exception while adding apprenticeship: {e.Message}\n" +
                                $"Apprenticeship id: {createdEvent.ApprenticeshipId}, " +
                                $"employer account id: {createdEvent.AccountId}, " +
                                $"Ukprn: {createdEvent.ProviderId}.", e);
                throw;
            }
            catch (Exception ex)
            {
                logger.LogError($"Error processing the apprenticeship event. Error: {ex.Message}", ex);
                throw;
            }
        }
        public async Task ProcessEasAtMonthEnd(long jobId, int academicYear, int collectionPeriod)
        {
            var properties = new Dictionary <string, string>
            {
                { TelemetryKeys.JobId, jobId.ToString() },
                { TelemetryKeys.CollectionPeriod, collectionPeriod.ToString() },
                { TelemetryKeys.AcademicYear, academicYear.ToString() },
            };

            try
            {
                logger.LogInfo("Started the Provider Adjustments Processor.");

                var historicPayments = await repository.GetPreviousProviderAdjustments(academicYear).ConfigureAwait(false);

                logger.LogInfo($"Found {historicPayments.Count} historic payments");

                var currentPayments = await repository.GetCurrentProviderAdjustments(academicYear).ConfigureAwait(false);

                logger.LogInfo($"Found {currentPayments.Count} payments from API");

                var payments = calculator.CalculateDelta(historicPayments, currentPayments, academicYear, collectionPeriod);
                logger.LogInfo($"Calculated {payments.Count} new payments");

                await repository.AddProviderAdjustments(payments).ConfigureAwait(false);

                logger.LogInfo("Finished the Provider Adjustments Processor.");

                SendEASTelemetry(true, properties, historicPayments, currentPayments);
            }
            catch (Exception)
            {
                SendEASTelemetry(false, properties);
                throw;
            }
        }
예제 #6
0
        public async Task Handle(IList <RecordPeriodEndSubmissionWindowValidationJob> messages, CancellationToken cancellationToken)
        {
            foreach (var message in messages)
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();

                logger.LogInfo($"Handling period end submission window validation job: {message.ToJson()}");

                await periodEndJobService.RecordPeriodEndJob(message, cancellationToken);

                var metricsValid = await submissionWindowValidationClient.Validate(message.JobId, message.CollectionYear, message.CollectionPeriod);

                var jobStatus = metricsValid ? JobStatus.Completed : JobStatus.CompletedWithErrors;

                await jobStorageService.SaveJobStatus(message.JobId, jobStatus, DateTimeOffset.Now, cancellationToken);

                logger.LogInfo($"Handled period end submission window validation job: {message.JobId}");

                stopwatch.Stop();

                SendTelemetry(message, jobStatus, stopwatch.Elapsed);
            }
        }
        /// <summary>
        /// This is the main entry point for your service instance.
        /// </summary>
        /// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service instance.</param>
        protected override async Task RunAsync(CancellationToken cancellationToken)
        {
            var initialised = false;

            try
            {
                logger.LogDebug("Starting the Provider Ajustments service.");
                initialised = true;
                logger.LogInfo("Started the Provider Adjustments service.");
                await Task.Delay(Timeout.Infinite, cancellationToken);
            }
            catch (Exception exception) when(!(exception is TaskCanceledException))
            {
                // Ignore, as an exception is only really thrown on cancellation of the token.
                logger.LogError($"Reference Data Stateless Service Exception. Error: {exception}.", exception);
            }
            finally
            {
                if (initialised)
                {
                    logger.LogInfo("Provider Adjustments Stateless Service End");
                }
            }
        }
예제 #8
0
        public async Task Handle(ProcessProviderMonthEndAct1CompletionPaymentCommand message, IMessageHandlerContext context)
        {
            paymentLogger.LogInfo($"Processing Provider Month End Act1 Completion Payment Command with ukprn: {message.Ukprn}, Message Id : {context.MessageId}");

            var paymentEvents = await completionPaymentService.GetAct1CompletionPaymentEvents(message);

            if (!paymentEvents.Any())
            {
                paymentLogger.LogWarning($"No Act1 Completion Payment Event Found for ukprn: {message.Ukprn}, Collection: {message.CollectionPeriod.Period:00}-{message.CollectionPeriod.AcademicYear}, job: {message.JobId}");

                return;
            }

            var dasEndPoint = await dasEndpointFactory.GetEndpointInstanceAsync();

            foreach (var paymentEvent in paymentEvents)
            {
                paymentLogger.LogDebug($"Processing Act1 Completion Payment Event. Ukprn: {message.Ukprn}, Collection: {message.CollectionPeriod.Period:00}-{message.CollectionPeriod.AcademicYear}, job: {message.JobId}");

                await dasEndPoint.Publish(paymentEvent);
            }

            paymentLogger.LogInfo($"Successfully Processed Month End Act1 Completion Payment Command for  ukprn: {message.Ukprn}, Collection:{message.CollectionPeriod.Period:00}-{message.CollectionPeriod.AcademicYear}, job: {message.JobId}");
        }
예제 #9
0
        /// <summary>
        /// Opens the asynchronous.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns></returns>
        public async Task <string> OpenAsync(CancellationToken cancellationToken)
        {
            try
            {
                logger.LogDebug($"Opening endpoint: {config.EndpointName}");
                endpointInstance = await endpointInstanceFactory.GetEndpointInstance().ConfigureAwait(false);

                logger.LogInfo($"Finished opening endpoint listener: {config.EndpointName}");
                return(config.EndpointName);
            }
            catch (Exception e)
            {
                logger.LogFatal($"Cannot start the endpoint: '{config.EndpointName}'.  Error: {e.Message}", e);
                throw;
            }
        }
        public async Task Handle(ResetActorsCommand message, IMessageHandlerContext context)
        {
            logger.LogDebug("Resetting datalock actors.");
            var resetTasks = new List <Task>();

            foreach (var uln in message.Ulns)
            {
                var actorId = new ActorId(uln.ToString());
                var actor   = proxyFactory.CreateActorProxy <IDataLockService>(new Uri("fabric:/SFA.DAS.Payments.DataLocks.ServiceFabric/DataLockServiceActorService"), actorId);
                resetTasks.Add(actor.Reset());
            }

            await Task.WhenAll(resetTasks).ConfigureAwait(false);

            logger.LogInfo("Finished resetting the datalock actors");
        }
 public async Task StoreDataLocks(List <DataLockEventModel> models, CancellationToken cancellationToken)
 {
     try
     {
         await repository.SaveDataLockEvents(models, cancellationToken);
     }
     catch (Exception e)
     {
         if (!e.IsUniqueKeyConstraintException() && !e.IsDeadLockException())
         {
             throw;
         }
         logger.LogInfo($"Batch contained a duplicate DataLock.  Will store each individually and discard duplicate.");
         await repository.SaveDataLocksIndividually(models.Select(model => mapper.Map(model)).ToList(), cancellationToken).ConfigureAwait(false);
     }
 }
예제 #12
0
        public async Task <bool> HandleAsync(JobContextMessage message, CancellationToken cancellationToken)
        {
            try
            {
                logger.LogDebug("Getting task type from period end message.");
                var taskType = GetTask(message);
                logger.LogDebug("Got period end type now create the period end event.");
                var periodEndEvent = CreatePeriodEndEvent(taskType);
                logger.LogDebug($"Created period end event. Type: {periodEndEvent.GetType().Name}");
                periodEndEvent.JobId            = message.JobId;
                periodEndEvent.CollectionPeriod = new CollectionPeriod
                {
                    AcademicYear = Convert.ToInt16(GetMessageValue(message, JobContextMessageConstants.KeyValuePairs.CollectionYear)),
                    Period       = Convert.ToByte(GetMessageValue(message, JobContextMessageConstants.KeyValuePairs.ReturnPeriod))
                };

                logger.LogDebug($"Got period end event: {periodEndEvent.ToJson()}");
                await RecordPeriodEndJob(taskType, periodEndEvent).ConfigureAwait(false);

                var endpointInstance = await endpointInstanceFactory.GetEndpointInstance();

                await endpointInstance.Publish(periodEndEvent);

                logger.LogInfo($"Finished publishing the period end event. Name: {periodEndEvent.GetType().Name}, JobId: {periodEndEvent.JobId}, Collection Period: {periodEndEvent.CollectionPeriod.Period}-{periodEndEvent.CollectionPeriod.AcademicYear}.");

                // TODO: This is a temporary workaround to enable the PeriodEndStart and PeriodEndStop messages to return true as otherwise the service will
                // TODO: just hang as there is nothing implemented to handle the Start and Stop events and so the job status service will never get a completion and so this will never return true.
                // PV2-1345 will handle PeriodEndStart
                // PeriodEndStoppedEvent will be handled by the PeriodEndStoppedEventHandler which in turn is handled by the ProcessProviderMonthEndCommandHandler but we don't want to wait for it


                if (periodEndEvent is PeriodEndStartedEvent || periodEndEvent is PeriodEndStoppedEvent)
                {
                    logger.LogDebug("Returning as this is either a PeriodEndStart or PeriodEndStop event");
                    return(true);
                }

                await jobStatusService.WaitForJobToFinish(message.JobId, cancellationToken);

                return(true);
            }
            catch (Exception ex)
            {
                logger.LogError($"Failed to process job context message. Message: {message.ToJson()}", ex);
                throw;
            }
        }
예제 #13
0
        public async Task <List <ProviderAdjustment> > GetCurrentProviderAdjustments(int academicYear)
        {
            logger.LogInfo("Getting Current Provider Adjustments - Getting Token");

            var token = await GetToken();

            logger.LogInfo("Token retrieved");

            var providerAdjustments = new List <ProviderAdjustment>();
            var pageNumber          = 1;

            while (true)
            {
                var request = new HttpRequestMessage(HttpMethod.Get, $"api/v1/Eas/{academicYear}?pagenumber={pageNumber}&pagesize={pageSize}");
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

                logger.LogInfo($"Getting page {pageNumber} of data from API");
                var httpResponse = await client.SendAsync(request).ConfigureAwait(false);

                logger.LogInfo($"Successfully connected to the API");

                var responseContent = await httpResponse.Content.ReadAsStringAsync();

                logger.LogDebug($"Response code: {httpResponse.StatusCode}, Reason: {httpResponse.ReasonPhrase}, " +
                                $"Request: {httpResponse.RequestMessage.RequestUri}");

                if (httpResponse.IsSuccessStatusCode)
                {
                    var batch = JsonConvert.DeserializeObject <List <ProviderAdjustment> >(responseContent);
                    if (batch.Count == 0)
                    {
                        logger.LogInfo($"No messages on page {pageNumber}");
                        break;
                    }
                    logger.LogInfo($"Successfully retrieved {batch.Count} records from API");
                    providerAdjustments.AddRange(batch);
                }
                else
                {
                    logger.LogError($"Error getting EAS records: {responseContent}, {httpResponse}");
                    throw new InvalidOperationException($"Error getting EAS records: {responseContent}");
                }

                pageNumber++;
            }

            logger.LogInfo($"Finished reading records from the API. Got {providerAdjustments.Count} records");
            return(providerAdjustments);
        }
        public async Task Handle(IList <CalculatedRequiredLevyAmount> messages, CancellationToken cancellationToken)
        {
            logger.LogInfo($"Received {messages.Count} messages");
            await levyTransactionBatchStorageService.StoreLevyTransactions(messages, cancellationToken)
            .ConfigureAwait(false);

            var monitoringClient = monitoringClientFactory.Create();

            foreach (var calculatedRequiredLevyAmount in messages)
            {
                await monitoringClient.ProcessedJobMessage(calculatedRequiredLevyAmount.JobId,
                                                           calculatedRequiredLevyAmount.EventId,
                                                           calculatedRequiredLevyAmount.GetType().ToString(),
                                                           new List <GeneratedMessage>()
                                                           );
            }
        }
        public async Task <bool> IsDuplicate(IPaymentsEvent earningEvent, CancellationToken cancellationToken)
        {
            logger.LogDebug($"Checking if earning event of type {earningEvent.GetType().Name} with guid: {earningEvent.EventId} has already been received.");
            var earningEventKey = new EarningEventKey(earningEvent);

            logger.LogDebug($"Earning event key: {earningEventKey.LogSafeKey}");
            if (await cache.Contains(earningEventKey.Key, cancellationToken).ConfigureAwait(false))
            {
                logger.LogWarning($"Key: {earningEventKey.LogSafeKey} found in the cache and is probably a duplicate.");
                return(true);
            }
            logger.LogDebug($"New earnings event. Event key: {earningEventKey.LogSafeKey}, event id: {earningEvent.EventId}");
            await cache.Add(earningEventKey.Key, earningEventKey, cancellationToken);

            logger.LogInfo($"Added new earnings event to cache. Key: {earningEventKey.LogSafeKey}");
            return(false);
        }
예제 #16
0
        public async Task Handle(FundingSourcePaymentEvent message, IMessageHandlerContext context)
        {
            paymentLogger.LogDebug($"Processing Funding Source Payment Event for Message Id : {context.MessageId}");
            var paymentModel = mapper.Map <ProviderPaymentEventModel>(message);
            await paymentsService.ProcessPayment(paymentModel, default(CancellationToken)).ConfigureAwait(false);

            var afterMonthEndPayment = await afterMonthEndPaymentService.GetPaymentEvent(message);

            if (afterMonthEndPayment != null)
            {
                paymentLogger.LogInfo($"Sent {afterMonthEndPayment.GetType().Name} for {message.JobId} and Message Type {message.GetType().Name}");
                paymentLogger.LogDebug($"Sending Provider Payment Event {JsonConvert.SerializeObject(afterMonthEndPayment)} for Message Id : {context.MessageId}.  {message.ToDebug()}");
                await context.Publish(afterMonthEndPayment).ConfigureAwait(false);
            }

            paymentLogger.LogDebug($"Finished processing Funding Source Payment Event for Message Id : {context.MessageId}.  {message.ToDebug()}");
        }
예제 #17
0
        public async Task RecordPeriodEndJob(RecordPeriodEndJob periodEndJob, CancellationToken cancellationToken)
        {
            logger.LogDebug($"Sending request to record {periodEndJob.GetType().Name}. Job Id: {periodEndJob.JobId}, collection period: {periodEndJob.CollectionYear}-{periodEndJob.CollectionPeriod}");
            var jobDetails = new JobModel
            {
                JobType          = GetJobType(periodEndJob),
                CollectionPeriod = periodEndJob.CollectionPeriod,
                AcademicYear     = periodEndJob.CollectionYear,
                DcJobId          = periodEndJob.JobId,
                Status           = JobStatus.InProgress,
                StartTime        = DateTimeOffset.UtcNow
            };

            await RecordNewJob(jobDetails, periodEndJob.GeneratedMessages, cancellationToken).ConfigureAwait(false);

            logger.LogInfo($"Sent request to record {periodEndJob.GetType().Name}. Job Id: {periodEndJob.JobId}, collection period: {periodEndJob.CollectionYear}-{periodEndJob.CollectionPeriod}");
        }
예제 #18
0
        public async Task <bool> IsDuplicate(ProcessLearnerCommand processLearnerCommand, CancellationToken cancellationToken)
        {
            logger.LogDebug($"Checking if command of type {processLearnerCommand.GetType().Name} with guid: {processLearnerCommand.CommandId} has already been received.");

            var learnerKey = new LearnerKey(processLearnerCommand);

            logger.LogDebug($"learner key: {learnerKey.LogSafeKey}");
            if (await cache.Contains(learnerKey.Key, cancellationToken).ConfigureAwait(false))
            {
                logger.LogWarning($"Key: {learnerKey.LogSafeKey} found in the cache and is probably a duplicate.");
                return(true);
            }
            logger.LogDebug($"New learner command. Command key: {learnerKey.LogSafeKey}, command id: {processLearnerCommand.CommandId}");
            await cache.Add(learnerKey.Key, learnerKey, cancellationToken);

            logger.LogInfo($"Added new earnings event to cache. Key: {learnerKey.LogSafeKey}");
            return(false);
        }
        public async Task Handle(PeriodEndStoppedEvent message, IMessageHandlerContext context)
        {
            logger.LogDebug($"Received period end stopped event. Details: {message.ToJson()}");

            var fabricClient       = new FabricClient();
            var serviceDescription = new StatelessServiceDescription
            {
                ApplicationName            = new Uri("fabric:/SFA.DAS.Payments.DataLocks.ServiceFabric"),
                PartitionSchemeDescription = new SingletonPartitionSchemeDescription(),
                ServiceName     = new Uri(ServiceNames.DatalockApprovalsService),
                ServiceTypeName = "SFA.DAS.Payments.DataLocks.ApprovalsServiceType",
                InstanceCount   = instanceCount,
            };

            await fabricClient.ServiceManager.CreateServiceAsync(serviceDescription);

            logger.LogInfo($"Finished period end stopped handler. Details: {message.ToJson()}");
        }
        public async Task <SubmissionsSummaryModel> ValidateSubmissionWindow(long jobId, short academicYear, byte collectionPeriod, CancellationToken cancellationToken)
        {
            var isEstimatingMetrics = jobId == 0;
            var logMessage          = isEstimatingMetrics ? "estimating" : "building";

            try
            {
                logger.LogDebug($"Started {logMessage} metrics for job: {jobId}, Academic year: {academicYear}, Collection period: {collectionPeriod}");

                var stopwatch = Stopwatch.StartNew();

                var submissionSummaries = await submissionMetricsRepository.GetSubmissionsSummaryMetrics(jobId, academicYear, collectionPeriod, cancellationToken);

                var metrics = submissionsSummary.GetMetrics(jobId, academicYear, collectionPeriod, submissionSummaries);

                var collectionPeriodTolerance = await submissionMetricsRepository.GetCollectionPeriodTolerance(collectionPeriod, academicYear, cancellationToken);

                submissionsSummary.CalculateIsWithinTolerance(collectionPeriodTolerance?.SubmissionToleranceLower, collectionPeriodTolerance?.SubmissionToleranceUpper);

                cancellationToken.ThrowIfCancellationRequested();

                var dataDuration = stopwatch.ElapsedMilliseconds;

                logger.LogDebug($"finished getting data from databases for job: {jobId}, Took: {dataDuration}ms.");

                if (!isEstimatingMetrics)
                {
                    await submissionMetricsRepository.SaveSubmissionsSummaryMetrics(metrics, cancellationToken);
                }

                stopwatch.Stop();

                SendTelemetry(metrics, stopwatch.ElapsedMilliseconds);

                logger.LogInfo($"Finished {logMessage} Submissions Summary Metrics for job: {jobId}, Academic year: {academicYear}, Collection period: {collectionPeriod}. Took: {stopwatch.ElapsedMilliseconds}ms");

                return(metrics);
            }
            catch (Exception e)
            {
                logger.LogWarning($"Error {logMessage} the Submissions Summary metrics report for job: {jobId}, Error: {e}");
                throw;
            }
        }
예제 #21
0
        public async Task Handle(ResetCacheCommand message, IMessageHandlerContext context)
        {
            logger.LogDebug($"Resetting cache for provider :{message.Ukprn}");
            var ulns = await repository.ApprenticeshipUlnsByProvider(message.Ukprn);

            var resetTasks = new List <Task>();

            foreach (var uln in ulns)
            {
                var actorId = new ActorId(uln.ToString());
                var actor   = proxyFactory.CreateActorProxy <IDataLockService>(new Uri("fabric:/SFA.DAS.Payments.DataLocks.ServiceFabric/DataLockServiceActorService"), actorId);
                logger.LogVerbose($"Actor proxy created, now resetting the cache.");
                resetTasks.Add(actor.Reset());
            }

            await Task.WhenAll(resetTasks).ConfigureAwait(false);

            logger.LogInfo($"Finished resetting the cache for provider: {message.Ukprn}");
        }
예제 #22
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", Route = "v1/PeriodEnd/Submission/Successful")] HttpRequest req,
            [Inject] IProvidersRequiringReprocessingService providersRequiringReprocessingService,
            [Inject] IPaymentLogger logger)
        {
            if (!short.TryParse(req.Query["academicYear"], out var academicYear) ||
                !byte.TryParse(req.Query["collectionPeriod"], out var collectionPeriod))
            {
                return(new StatusCodeResult(400));
            }

            logger.LogDebug($"Entering {nameof(SuccessfulSubmissions)} Function for AcademicYear: {academicYear} and Collection Period: {collectionPeriod}");

            var submissionJobs = await providersRequiringReprocessingService.SuccessfulSubmissions(academicYear, collectionPeriod);

            logger.LogInfo("Successfully retrieved latest successful submission jobs");

            return(new OkObjectResult(submissionJobs));
        }
예제 #23
0
        public async Task <List <LevyAccountModel> > GetDasLevyAccountDetails()
        {
            logger.LogDebug("Started Importing DAS Employer Accounts");

            var dasLevyAccountDetails = new List <LevyAccountModel>();

            var totalPages = await GetTotalPageSize();

            for (var pageNumber = 1; pageNumber <= totalPages; pageNumber++)
            {
                var levyAccountDetails = await GetPageOfLevyAccounts(pageNumber);

                dasLevyAccountDetails.AddRange(levyAccountDetails);
            }

            logger.LogInfo("Finished Importing DAS Employer Accounts");

            return(dasLevyAccountDetails.IsNullOrEmpty() ? null : dasLevyAccountDetails);
        }
 /// <summary>
 /// Opens the asynchronous.
 /// </summary>
 /// <param name="cancellationToken">The cancellation token.</param>
 /// <returns></returns>
 public async Task <string> OpenAsync(CancellationToken cancellationToken)
 {
     try
     {
         logger.LogDebug($"Opening endpoint: {config.EndpointName}");
         if (endpointInstance == null)
         {
             var endpointConfiguration = CreateEndpointConfiguration();
             //endpointConfiguration.UseContainer<AutofacBuilder>(c => c.ExistingLifetimeScope(lifetimeScope));
             endpointInstance = await Endpoint.Start(endpointConfiguration).ConfigureAwait(false);
         }
         logger.LogInfo($"Finished opening endpoint listener: {config.EndpointName}");
         return("das endpoint comms listener");
     }
     catch (Exception e)
     {
         logger.LogFatal($"Cannot start the endpoint: '{config.EndpointName}'.  Error: {e.Message}", e);
         throw;
     }
 }
        public async Task Handle(IdentifiedRemovedLearningAim message, IMessageHandlerContext context)
        {
            logger.LogDebug("Processing 'IdentifiedRemovedLearningAim' message.");
            ((ExecutionContext)executionContext).JobId = message.JobId.ToString();

            var calculatedRequiredLevyAmount = await clawbackRemovedLearnerAimsProcessor.GenerateClawbackForRemovedLearnerAim(message, CancellationToken.None).ConfigureAwait(false);

            logger.LogDebug($"Got {calculatedRequiredLevyAmount?.Count ?? 0} Calculated Required Levy Amount events.");

            if (calculatedRequiredLevyAmount != null)
            {
                await Task.WhenAll(calculatedRequiredLevyAmount.Select(context.Publish)).ConfigureAwait(false);
            }

            logger.LogInfo("Successfully processed IdentifiedRemovedLearningAim event for " +
                           $"jobId:{message.JobId}, learnerRef:{message.Learner.ReferenceNumber}, frameworkCode:{message.LearningAim.FrameworkCode}, " +
                           $"pathwayCode:{message.LearningAim.PathwayCode}, programmeType:{message.LearningAim.ProgrammeType}, " +
                           $"standardCode:{message.LearningAim.StandardCode}, learningAimReference:{message.LearningAim.Reference}, " +
                           $"academicYear:{message.CollectionPeriod.AcademicYear}, contractType:{message.ContractType}");
        }
        protected async Task CleanUpJob(object state)
        {
            var lastMessageTime = await GetLastMessageStatusTme().ConfigureAwait(false);

            if (lastMessageTime == null || lastMessageTime.Value.AddMinutes(MaxIdleMinutesForJob) > DateTimeOffset.UtcNow)
            {
                logger.LogVerbose(lastMessageTime.HasValue
                    ? $"Job timeout not triggered.  Is due to timeout at: {lastMessageTime.Value.AddMinutes(MaxIdleMinutesForJob)}"
                    : "No job messages have been received so cannot start job timeout.");
                return;
            }
            logger.LogInfo($"Job has timed out due to max idle time. Job: {Id.GetStringId()}");
            var statusService = lifetimeScope.Resolve <IJobStatusService>();
            await statusService.StopJob();

            if (jobCleanUpTimer != null)
            {
                UnregisterTimer(jobCleanUpTimer);
            }
        }
예제 #27
0
        public async Task <List <EarningEvent> > CreateLearnerEarningEvents(ProcessLearnerCommand processLearnerCommand)
        {
            var earningEvents = new List <EarningEvent>();

            if (await duplicateLearnerService.IsDuplicate(processLearnerCommand, CancellationToken.None))
            {
                return(earningEvents);
            }

            logger.LogDebug($"Handling ILR learner submission. Job: {processLearnerCommand.JobId}, Collection year: {processLearnerCommand.CollectionYear}, Learner: {processLearnerCommand.Learner.LearnRefNumber}");
            var processorResult = learnerSubmissionProcessor.GenerateEarnings(processLearnerCommand);

            if (processorResult.Validation.Failed)
            {
                logger.LogInfo($"ILR Learner Submission failed validation. Job: {processLearnerCommand.JobId}, Collection year: {processLearnerCommand.CollectionYear}, Learner: {processLearnerCommand.Learner.LearnRefNumber}");
                return(earningEvents);
            }

            earningEvents = processorResult.EarningEvents;
            return(earningEvents);
        }
예제 #28
0
        private async Task Run(string partitionEndpointName)
        {
            await LoadExistingJobs().ConfigureAwait(false);

            while (!cancellationToken.IsCancellationRequested)
            {
                var tasks = currentJobs.Select(job => CheckJobStatus(partitionEndpointName, job.Key)).ToList();
                await Task.WhenAll(tasks).ConfigureAwait(false);

                var completedJobs = currentJobs.Where(item => item.Value).ToList();
                foreach (var completedJob in completedJobs)
                {
                    logger.LogInfo($"Found completed job.  Will now stop monitoring job: {completedJob.Key}, ThreadId {Thread.CurrentThread.ManagedThreadId}, PartitionId {partitionEndpointName}");
                    if (!currentJobs.TryRemove(completedJob.Key, out _))
                    {
                        logger.LogWarning($"Couldn't remove completed job from jobs list. ThreadId {Thread.CurrentThread.ManagedThreadId}, PartitionId {partitionEndpointName},  Job: {completedJob.Key}, status: {completedJob.Value}");
                    }
                }
                await Task.Delay(interval, cancellationToken).ConfigureAwait(false);
            }
        }
예제 #29
0
        public async Task ProcessPayment(ProviderPaymentEventModel payment, CancellationToken cancellationToken)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            var isCurrentProviderIlr = await IsCurrentProviderIlr(payment.JobId, payment.Ukprn, payment.IlrSubmissionDateTime, cancellationToken).ConfigureAwait(false);

            if (!isCurrentProviderIlr)
            {
                paymentLogger.LogWarning($"Received out of sequence payment with Job Id {payment.JobId} for Ukprn {payment.Ukprn} ");
                telemetry.TrackEvent("Provider payments service received out of sequence payment");
                return;
            }

            paymentLogger.LogVerbose($"Received valid payment with Job Id {payment.JobId} for Ukprn {payment.Ukprn} ");
            await paymentCache.AddPayment(payment, cancellationToken);

            stopwatch.Stop();
            telemetry.TrackDuration(GetType().FullName + ".ProcessPayment", stopwatch.Elapsed);
            paymentLogger.LogInfo($"Finished adding the payment to the cache. EventId: {payment.EventId}, FundingSourceId: {payment.FundingSourceId}, UKPRN: {payment.Ukprn}");
        }
        public async Task <ReadOnlyCollection <FundingSourcePaymentEvent> > ProcessReceiverTransferPayment(ProcessUnableToFundTransferFundingSourcePayment message)
        {
            if (!message.AccountId.HasValue)
            {
                throw new InvalidOperationException($"Invalid ProcessUnableToFundTransferFundingSourcePayment event.  No account id populated on message.  Event id: {message.EventId}");
            }

            paymentLogger.LogDebug($"Converting the unable to fund transfer payment to a levy payment.  Event id: {message.EventId}, account id: {message.AccountId}, job id: {message.JobId}");
            var requiredPayment = mapper.Map <CalculatedRequiredLevyAmount>(message);

            paymentLogger.LogVerbose("Mapped ProcessUnableToFundTransferFundingSourcePayment to CalculatedRequiredLevyAmount");
            var payments = new List <FundingSourcePaymentEvent>();
            var monthEndStartedForThisAccount = await monthEndCache.TryGet(CacheKeys.MonthEndStartedForThisAccountCacheKey);

            if (!monthEndStartedForThisAccount.HasValue || !monthEndStartedForThisAccount.Value)
            {
                paymentLogger.LogDebug("Month end has not been started yet so adding the payment to the cache.");
                await AddRequiredPayment(requiredPayment);
            }
            else
            {
                var levyAccountCacheItem = await levyAccountCache.TryGet(CacheKeys.LevyBalanceKey, CancellationToken.None)
                                           .ConfigureAwait(false);

                if (!levyAccountCacheItem.HasValue)
                {
                    throw new InvalidOperationException($"The last levy account balance has not been stored in the reliable for account: {message.AccountId}");
                }

                levyBalanceService.Initialise(levyAccountCacheItem.Value.Balance, levyAccountCacheItem.Value.TransferAllowance);
                paymentLogger.LogDebug("Service has finished month end processing so now generating the payments for the ProcessUnableToFundTransferFundingSourcePayment event.");
                payments.AddRange(fundingSourcePaymentEventBuilder.BuildFundingSourcePaymentsForRequiredPayment(requiredPayment, message.AccountId.Value, message.JobId));
                var remainingBalance = mapper.Map <LevyAccountModel>(levyAccountCacheItem.Value);
                remainingBalance.Balance           = levyBalanceService.RemainingBalance;
                remainingBalance.TransferAllowance = levyBalanceService.RemainingTransferAllowance;
                await levyAccountCache.AddOrReplace(CacheKeys.LevyBalanceKey, remainingBalance);
            }
            paymentLogger.LogInfo($"Finished processing the ProcessUnableToFundTransferFundingSourcePayment. Event id: {message.EventId}, account id: {message.AccountId}, job id: {message.JobId}");
            return(payments.AsReadOnly());
        }