public async Task ThenIfFileHasMissingFieldsReturnError(string header)
        {
            var inputData = $"{header}" +
                            @"
                            Abba123,1113335559,Froberg,Chris,1998-12-08,SE123321C,25,,,2,2120-08,2125-08,1500,,Employer ref,Provider ref";

            var textStream = new MemoryStream(UTF8.GetBytes(inputData));

            _file.Setup(m => m.InputStream).Returns(textStream);

            BulkUploadApprenticeshipsCommand commandArgument = null;

            _mockMediator.Setup(x => x.Send(It.IsAny <BulkUploadApprenticeshipsCommand>(), new CancellationToken()))
            .ReturnsAsync(new Unit())
            .Callback((object x) => commandArgument = x as BulkUploadApprenticeshipsCommand);

            _mockMediator.Setup(m => m.Send(It.IsAny <GetCommitmentQueryRequest>(), new CancellationToken()))
            .Returns(Task.FromResult(new GetCommitmentQueryResponse
            {
                Commitment = new CommitmentView
                {
                    AgreementStatus = AgreementStatus.NotAgreed,
                    EditStatus      = EditStatus.ProviderOnly
                }
            }));

            _mockMediator.Setup(m => m.Send(It.IsAny <GetOverlappingApprenticeshipsQueryRequest>(), It.IsAny <CancellationToken>()))
            .Returns(
                Task.Run(() => new GetOverlappingApprenticeshipsQueryResponse
            {
                Overlaps = new List <ApprenticeshipOverlapValidationResult>
                {
                    new ApprenticeshipOverlapValidationResult
                    {
                        OverlappingApprenticeships = new List <OverlappingApprenticeship>
                        {
                            new OverlappingApprenticeship
                            {
                                Apprenticeship = new Apprenticeship {
                                    ULN = "1113335559"
                                },
                                ValidationFailReason = ValidationFailReason.DateEmbrace
                            }
                        }
                    }
                }
            }));

            var model = new UploadApprenticeshipsViewModel {
                Attachment = _file.Object, HashedCommitmentId = "ABBA123", ProviderId = 111
            };
            var file = await _sut.UploadFile("user123", model, new SignInUserModel());

            //Assert
            Assert.IsTrue(file.HasFileLevelErrors);

            _logger.Verify(x => x.Info(It.IsAny <string>(), It.IsAny <long?>(), It.IsAny <long?>(), It.IsAny <long?>()), Times.Once);
            _logger.Verify(x => x.Error(It.IsAny <Exception>(), It.IsAny <string>(), It.IsAny <long?>(), It.IsAny <long?>(), It.IsAny <long?>()), Times.Never);
        }
        public async Task ThenIfAnyRecordsOverlapWithActiveApprenticeshipsThenReturnError()
        {
            const string dataLine     = "\n\rABBA123,Chris,Froberg,1998-12-08,,,25,2,2020-08,2025-08,1500,,Employer ref,Provider ref,1113335559";
            const string fileContents = HeaderLine + dataLine;
            var          textStream   = new MemoryStream(UTF8.GetBytes(fileContents));

            _file.Setup(m => m.InputStream).Returns(textStream);

            BulkUploadApprenticeshipsCommand commandArgument = null;

            _mockMediator.Setup(x => x.Send(It.IsAny <BulkUploadApprenticeshipsCommand>(), new CancellationToken()))
            .ReturnsAsync(new Unit())
            .Callback((object x) => commandArgument = x as BulkUploadApprenticeshipsCommand);

            _mockMediator.Setup(m => m.Send(It.IsAny <GetCommitmentQueryRequest>(), new CancellationToken()))
            .Returns(Task.FromResult(new GetCommitmentQueryResponse
            {
                Commitment = new CommitmentView
                {
                    AgreementStatus = AgreementStatus.NotAgreed,
                    EditStatus      = EditStatus.ProviderOnly
                }
            }));

            _mockMediator.Setup(m => m.Send(It.IsAny <GetOverlappingApprenticeshipsQueryRequest>(), It.IsAny <CancellationToken>()))
            .Returns(
                Task.Run(() => new GetOverlappingApprenticeshipsQueryResponse
            {
                Overlaps = new List <ApprenticeshipOverlapValidationResult>
                {
                    new ApprenticeshipOverlapValidationResult
                    {
                        OverlappingApprenticeships = new List <OverlappingApprenticeship>
                        {
                            new OverlappingApprenticeship
                            {
                                Apprenticeship = new Apprenticeship {
                                    ULN = "1113335559"
                                },
                                ValidationFailReason = ValidationFailReason.DateEmbrace
                            }
                        }
                    }
                }
            }));

            var model = new UploadApprenticeshipsViewModel {
                Attachment = _file.Object, HashedCommitmentId = "ABBA123", ProviderId = 111
            };
            var file = await _sut.UploadFile("user123", model, new SignInUserModel());

            //Assert
            Assert.IsTrue(file.HasRowLevelErrors);
        }
        public async Task ShouldCallMediatorPassingInMappedApprenticeships()
        {
            const string dataLine     = "\n\rABBA123,Chris,Froberg,1998-12-08,,,25,2,2020-08,2025-08,1500,,Employer ref,Provider ref,1113335559";
            const string fileContents = HeaderLine + dataLine;
            var          textStream   = new MemoryStream(UTF8.GetBytes(fileContents));

            _file.Setup(m => m.InputStream).Returns(textStream);

            BulkUploadApprenticeshipsCommand commandArgument = null;

            _mockMediator.Setup(x => x.Send(It.IsAny <BulkUploadApprenticeshipsCommand>(), new CancellationToken()))
            .ReturnsAsync(new Unit())
            .Callback <BulkUploadApprenticeshipsCommand, CancellationToken>((command, token) => commandArgument = command);

            _mockMediator.Setup(m => m.Send(It.IsAny <GetCommitmentQueryRequest>(), new CancellationToken()))
            .Returns(Task.FromResult(new GetCommitmentQueryResponse
            {
                Commitment = new CommitmentView
                {
                    AgreementStatus = AgreementStatus.NotAgreed,
                    EditStatus      = EditStatus.ProviderOnly
                }
            }));

            var model = new UploadApprenticeshipsViewModel {
                Attachment = _file.Object, HashedCommitmentId = "ABBA123", ProviderId = 111
            };
            var signinUser = new SignInUserModel {
                DisplayName = "Bob", Email = "*****@*****.**"
            };

            await _sut.UploadFile("user123", model, signinUser);

            _mockMediator.Verify(x => x.Send(It.IsAny <BulkUploadApprenticeshipsCommand>(), new CancellationToken()), Times.Once);

            commandArgument.ProviderId.Should().Be(111);
            commandArgument.CommitmentId.Should().Be(123);

            commandArgument.Apprenticeships.Should().NotBeEmpty();
            commandArgument.Apprenticeships.ToList()[0].FirstName.Should().Be("Chris");
            commandArgument.Apprenticeships.ToList()[0].LastName.Should().Be("Froberg");
            commandArgument.Apprenticeships.ToList()[0].DateOfBirth.Should().Be(new DateTime(1998, 12, 8));
            commandArgument.Apprenticeships.ToList()[0].TrainingType.Should().Be(DAS.Commitments.Api.Types.Apprenticeship.Types.TrainingType.Standard);
            commandArgument.Apprenticeships.ToList()[0].TrainingCode.Should().Be("2");
            commandArgument.Apprenticeships.ToList()[0].StartDate.Should().Be(new DateTime(2020, 8, 1));
            commandArgument.Apprenticeships.ToList()[0].EndDate.Should().Be(new DateTime(2025, 8, 1));
            commandArgument.Apprenticeships.ToList()[0].Cost.Should().Be(1500);
            commandArgument.Apprenticeships.ToList()[0].ProviderRef.Should().Be("Provider ref");
            commandArgument.Apprenticeships.ToList()[0].ULN.Should().Be("1113335559");
            commandArgument.UserEmailAddress.Should().Be(signinUser.Email);
            commandArgument.UserDisplayName.Should().Be(signinUser.DisplayName);
            commandArgument.UserId.Should().Be("user123");
        }
        public async Task TestPerformance()
        {
            _mockMediator.Setup(m => m.Send(It.IsAny <GetCommitmentQueryRequest>(), It.IsAny <CancellationToken>()))
            .Returns(Task.FromResult(new GetCommitmentQueryResponse
            {
                Commitment = new CommitmentView
                {
                    AgreementStatus = AgreementStatus.NotAgreed,
                    EditStatus      = EditStatus.ProviderOnly
                }
            }));

            const int upper    = 40 * 1000;
            var       testData = new List <string>();

            for (int i = 0; i < upper; i++)
            {
                var uln = (1000000000 + i).ToString();
                testData.Add("\n\rABBA123,Chris,Froberg,1998-12-08,,,,2,2020-08,2025-08,1500,,Employer ref,Provider ref," + uln);
            }
            var str = HeaderLine + string.Join("", testData);

            var textStream = new MemoryStream(UTF8.GetBytes(str));

            _file.Setup(m => m.InputStream).Returns(textStream);

            var model = new UploadApprenticeshipsViewModel {
                Attachment = _file.Object, HashedCommitmentId = "ABBA123", ProviderId = 1234L
            };
            var stopwatch = Stopwatch.StartNew();
            var r1        = await _sut.UploadFile("user123", model, new SignInUserModel());

            stopwatch.Stop(); Console.WriteLine($"Time TOTAL: {stopwatch.Elapsed.Seconds}");
            r1.RowLevelErrors.Count().Should().Be(80 * 1000);
            stopwatch.Elapsed.Seconds.Should().BeLessThan(7);
        }
        public async Task <ActionResult> UploadApprenticeships(UploadApprenticeshipsViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return(View(model));
            }

            var result = await _bulkUploadOrchestrator.UploadFile(CurrentUserId, model, GetSignedInUser());

            if (result.HasFileLevelErrors)
            {
                var error = result.FileLevelErrors.FirstOrDefault();
                ModelState.AddModelError("Attachment", error?.Message);
                return(View(model));
            }

            if (result.HasRowLevelErrors)
            {
                return(RedirectToAction("UploadApprenticeshipsUnsuccessful", new { model.ProviderId, model.HashedCommitmentId, result.BulkUploadReference }));
            }

            // ToDo: Flash message, or other feedback to customer
            return(RedirectToAction("Details", "Commitment", new { model.ProviderId, model.HashedCommitmentId }));
        }
예제 #6
0
        public async Task <BulkUploadResultViewModel> UploadFile(string userId, UploadApprenticeshipsViewModel uploadApprenticeshipsViewModel, SignInUserModel signInUser)
        {
            var commitmentId = HashingService.DecodeValue(uploadApprenticeshipsViewModel.HashedCommitmentId);
            var providerId   = uploadApprenticeshipsViewModel.ProviderId;
            var fileName     = uploadApprenticeshipsViewModel.Attachment?.FileName ?? "<unknown>";

            var commitment = await GetCommitment(providerId, commitmentId);

            AssertCommitmentStatus(commitment);
            await AssertAutoReservationEnabled(commitment);

            Logger.Info($"Uploading File - Filename:{fileName}", uploadApprenticeshipsViewModel.ProviderId, commitmentId);

            var fileValidationResult = await _bulkUploader.ValidateFileStructure(uploadApprenticeshipsViewModel, providerId, commitment);

            if (fileValidationResult.Errors.Any())
            {
                return(new BulkUploadResultViewModel
                {
                    BulkUploadId = fileValidationResult.BulkUploadId,
                    HasFileLevelErrors = true,
                    FileLevelErrors = fileValidationResult.Errors
                });
            }

            Logger.Info("Uploading file of apprentices.", providerId, commitmentId);

            var rowValidationResult = await _bulkUploader.ValidateFileRows(fileValidationResult.Data, providerId, fileValidationResult.BulkUploadId);

            var sw            = Stopwatch.StartNew();
            var overlapErrors = await GetOverlapErrors(fileValidationResult.Data.ToList());

            Logger.Trace($"Validating overlaps took {sw.ElapsedMilliseconds}");

            var rowErrors = rowValidationResult.Errors.ToList();

            rowErrors.AddRange(overlapErrors);
            var hashedBulkUploadId = HashingService.HashValue(fileValidationResult.BulkUploadId);

            if (rowErrors.Any())
            {
                Logger.Info($"{rowErrors.Count} Upload errors", providerId, commitmentId);
                return(new BulkUploadResultViewModel
                {
                    BulkUploadId = fileValidationResult.BulkUploadId,
                    BulkUploadReference = hashedBulkUploadId,
                    HasRowLevelErrors = true,
                    RowLevelErrors = rowErrors
                });
            }

            try
            {
                await Mediator.Send(new BulkUploadApprenticeshipsCommand
                {
                    UserId           = userId,
                    ProviderId       = providerId,
                    CommitmentId     = commitmentId,
                    Apprenticeships  = await _mapper.MapFrom(commitmentId, rowValidationResult.Data),
                    UserEmailAddress = signInUser.Email,
                    UserDisplayName  = signInUser.DisplayName
                });
            }
            catch (Exception)
            {
                var overlaps = (await GetOverlapErrors(fileValidationResult.Data.ToList())).ToList();
                if (overlaps.Any())
                {
                    return(new BulkUploadResultViewModel
                    {
                        BulkUploadId = fileValidationResult.BulkUploadId,
                        HasRowLevelErrors = true,
                        RowLevelErrors = overlaps
                    });
                }

                throw;
            }

            return(new BulkUploadResultViewModel {
                BulkUploadId = fileValidationResult.BulkUploadId
            });
        }
        public async Task <BulkUploadResult> ValidateFileStructure(UploadApprenticeshipsViewModel uploadApprenticeshipsViewModel, long providerId, CommitmentView commitment)
        {
            if (uploadApprenticeshipsViewModel.Attachment == null)
            {
                return new BulkUploadResult {
                           Errors = new List <UploadError> {
                               new UploadError("No file chosen")
                           }
                }
            }
            ;

            var fileContent = new StreamReader(uploadApprenticeshipsViewModel.Attachment.InputStream).ReadToEnd();
            var fileName    = uploadApprenticeshipsViewModel?.Attachment?.FileName ?? "<- NO NAME ->";

            _logger.Trace($"Saving bulk upload file. {fileName}");
            var bulkUploadId = await _mediator.Send(
                new SaveBulkUploadFileCommand
            {
                ProviderId   = uploadApprenticeshipsViewModel.ProviderId,
                CommitmentId = commitment.Id,
                FileContent  = fileContent,
                FileName     = fileName
            });

            _logger.Info($"Saved bulk upload with Id: {bulkUploadId}");

            var fileAttributeErrors = _bulkUploadValidator.ValidateFileSize(uploadApprenticeshipsViewModel.Attachment).ToList();

            if (fileAttributeErrors.Any())
            {
                foreach (var error in fileAttributeErrors)
                {
                    _logger.Warn($"File Structure Error  -->  {error.Message}", uploadApprenticeshipsViewModel.ProviderId, commitment.Id);
                }

                _logger.Info($"Failed validation bulk upload file with {fileAttributeErrors.Count} errors", uploadApprenticeshipsViewModel.ProviderId, commitment.Id);

                return(new BulkUploadResult {
                    Errors = fileAttributeErrors
                });
            }

            var uploadResult = _fileParser.CreateViewModels(providerId, commitment, fileContent);

            if (uploadResult.HasErrors)
            {
                return(uploadResult);
            }

            var errors = _bulkUploadValidator.ValidateCohortReference(uploadResult.Data, uploadApprenticeshipsViewModel.HashedCommitmentId).ToList();

            errors.AddRange(_bulkUploadValidator.ValidateUlnUniqueness(uploadResult.Data).ToList());

            return(new BulkUploadResult
            {
                Errors = errors,
                Data = uploadResult.Data,
                BulkUploadId = bulkUploadId
            });
        }