<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/VacancyRules/VacancyTitlePopularityCheckRule.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Esfa.QA.Core.Extensions;
using Esfa.Recruit.Vacancies.Client.Application.Configuration;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Esfa.Recruit.Vacancies.Client.Application.Rules.BaseRules;
using Esfa.Recruit.Vacancies.Client.Application.Rules.Engine;
using Esfa.Recruit.Vacancies.Client.Application.Services;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Newtonsoft.Json;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.VacancyRules
{
    public sealed class VacancyTitlePopularityCheckRule : Rule, IRule<Vacancy>
    {
        private readonly IApprenticeshipProgrammeProvider _apprenticeshipProgrammeProvider;
        private readonly IGetTitlePopularity _popularityService;
        private readonly QaRulesConfiguration _qaRulesConfig;

        public VacancyTitlePopularityCheckRule(IApprenticeshipProgrammeProvider apprenticeshipProgrammeProvider, IGetTitlePopularity popularityService, QaRulesConfiguration qaRulesConfig)
                : base(RuleId.TitlePopularity)
        {
            _apprenticeshipProgrammeProvider = apprenticeshipProgrammeProvider;
            _popularityService = popularityService;
            _qaRulesConfig = qaRulesConfig;
        }

        public async Task<RuleOutcome> EvaluateAsync(Vacancy subject)
        {
            RuleOutcome outcomeResult;

            var outcomeBuilder = RuleOutcomeDetailsBuilder.Create(RuleId.TitlePopularity);
            var popularityScore = await _popularityService.GetTitlePopularityAsync(subject.ProgrammeId, subject.Title);

            var trainingProgramme = await _apprenticeshipProgrammeProvider.GetApprenticeshipProgrammeAsync(subject.ProgrammeId);
            var data = JsonConvert.SerializeObject(new TitlePopularityData
            {
                TrainingCode = trainingProgramme.Id,
                TrainingTitle = trainingProgramme.Title,
                TrainingType = trainingProgramme.ApprenticeshipType.ToString().ToLower()
            });
            
            if (popularityScore < _qaRulesConfig.TitlePopularityPercentageThreshold)
                outcomeResult = new RuleOutcome(RuleId.TitlePopularity, 100, $"Title is not common for the training specified.", nameof(Vacancy.Title), null, data);
            else
                outcomeResult =  new RuleOutcome(RuleId.TitlePopularity, 0, $"Title is common for the training specified.", nameof(Vacancy.Title), null, data);

            var outcome = outcomeBuilder.Add(new RuleOutcome[]{outcomeResult})
                .ComputeSum();

            return outcome;
        }
    }
}

<|start_filename|>src/QA/QA.Web/ViewModels/ReviewEditModel.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Qa.Web.ViewModels
{
    public class ReviewEditModel
    {
        [FromRoute]
        public Guid ReviewId {get;set;}

        public List<string> SelectedFieldIdentifiers { get; set; } = new List<string>();
        public List<string> SelectedAutomatedQaResults { get; set; } = new List<string>();

        public string ReviewerComment { get; set; }

        public bool IsRefer => SelectedFieldIdentifiers.Any() || SelectedAutomatedQaResults.Any();
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Engine/RuleSet.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Rules.Extensions;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.Engine
{
    /// <summary>
    /// encapsulates a set of rules that will be applied to a particular entity.
    /// the ruleset can be evaluated to produce a single overall outcome
    /// </summary>
    /// <typeparam name="TSubject"></typeparam>
    public abstract class RuleSet<TSubject> : IRuleSet<TSubject>
    {
        private readonly RuleSetOptions _defaultOptions;
        private readonly IList<IRule<TSubject>> _rules;

        public string RuleSetId { get; }
        public int RuleCount => _rules.Count;

        protected RuleSet(string ruleSetId)
        {
            RuleSetId = ruleSetId;
            _defaultOptions = RuleSetOptions.ZeroTolerance;
            _rules = new List<IRule<TSubject>>();
        }

        public async Task<RuleSetOutcome> EvaluateAsync(TSubject subject, RuleSetOptions options = null)
        {
            if (_rules.Count == 0)
                throw new InvalidOperationException($"RuleSet '{RuleSetId}' has no rules defined");

            options = options ?? _defaultOptions;

            var outcome = new RuleSetOutcome();

            foreach (var rule in _rules)
            {
                var ruleOutcome = await rule.EvaluateAsync(subject);

                outcome.RuleOutcomes.Add(ruleOutcome);
            }

            outcome.Decision = outcome.RuleOutcomes.CalculateOutcome(options);

            return outcome;
        }

        protected void AddRule(IRule<TSubject> rule)
        {
            _rules.Add(rule);
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/RuleSetDecision.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    /// <summary>
    /// overall decision for a rule set evaluation
    /// </summary>
    public enum RuleSetDecision
    {
        Unknown = 0,
        Refer,
        Approve,
        Indeterminate
    }
}


<|start_filename|>src/Provider/Provider.Web/Configuration/Routing/RouteValues.cs<|end_filename|>
﻿namespace Esfa.Recruit.Provider.Web.Configuration.Routing
{
    public static class RouteValues
    {
        public const string Ukprn = "ukprn";
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/Part2/AboutEmployer/AboutEmployerEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Employer.Web.Views;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class AboutEmployerEditModel : VacancyRouteModel
    {
        public string EmployerDescription { get; set; }
        public string EmployerWebsiteUrl { get; set; }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/CloseVacancy/CloseEditModel.cs<|end_filename|>
using System.ComponentModel.DataAnnotations;
using Esfa.Recruit.Employer.Web.RouteModel;
using ValMsg = Esfa.Recruit.Shared.Web.ViewModels;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class CloseEditModel : VacancyRouteModel
    {
        [Required(ErrorMessage = ValMsg.ValidationMessages.CloseVacancyConfirmationMessages.SelectionRequired)]
        public bool? ConfirmClose { get; set; }
    }
}

<|start_filename|>src/Employer/Employer.Web/ViewModels/Part2/ApplicationProcess/ApplicationProcessEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class ApplicationProcessEditModel : VacancyRouteModel
    {
        public ApplicationMethod? ApplicationMethod { get; set; }
        public string ApplicationInstructions { get; set; }
        public string ApplicationUrl { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ApplicationQualification.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class ApplicationQualification
    {
        public string QualificationType { get; set; }
        public string Subject { get; set; }
        public string Grade { get; set; }
        public bool IsPredicted { get; set; }
        public int Year { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Extensions/QualificationsExtensions.cs<|end_filename|>
using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.Extensions
{
    public static class QualificationsExtensions
    {
        
        public static IOrderedEnumerable<Qualification> SortQualifications(this IEnumerable<Qualification> qualifications, IList<string> qualificationTypes)
        {
            return qualifications?.OrderBy(q => qualificationTypes.IndexOf(q.QualificationType))
                .ThenBy(q => q.Weighting, WeightingComparer)
                .ThenBy(q => q.Subject);
        }

        private static readonly Comparer<QualificationWeighting?> WeightingComparer = Comparer<QualificationWeighting?>.Create((x, y) =>
        {
            if (x == y)
            {
                return 0;
            }

            if (x == QualificationWeighting.Essential)
            {
                return -1;
            }

            return 1;
        });

        public static IEnumerable<string> AsText(this IEnumerable<Qualification> qualifications)
        {
            if (qualifications == null)
            {
                return Enumerable.Empty<string>();
            }
            
            return qualifications.Select(q => $"{q.QualificationType} {q.Subject} (Grade {q.Grade}) {q.Weighting.GetDisplayName().ToLower()}");
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/BaseRules/BaseProfanityChecksRule.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Esfa.QA.Core.Extensions;
using Esfa.Recruit.Vacancies.Client.Application.Rules.Engine;
using Esfa.Recruit.Vacancies.Client.Application.Rules.Extensions;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Newtonsoft.Json;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.BaseRules
{
    public abstract class BaseProfanityChecksRule : Rule
    {
        protected IEnumerable<string> ProfanityList { get; set; } = new List<string>();

        private readonly ConsolidationOption _consolidationOption;

        protected BaseProfanityChecksRule(RuleId ruleId, ConsolidationOption consolidationOption, decimal weighting = 1.0m) : base(ruleId, weighting)
        {
            _consolidationOption = consolidationOption;
        }

        protected IEnumerable<RuleOutcome> ProfanityCheckAsync(Expression<Func<string>> property, string relatedFieldId = null)
        {
            var fieldId = relatedFieldId ?? property.GetQualifiedFieldId();

            var foundProfanities = FindOccurrences(property);

            if (foundProfanities.Values.Sum() > 0)
            {
                switch (_consolidationOption)
                {
                    case ConsolidationOption.NoConsolidation:
                        return CreateUnconsolidatedOutcomes(foundProfanities, fieldId);

                    case ConsolidationOption.ConsolidateByField:
                        return CreateConsolidatedOutcomes(foundProfanities, fieldId);

                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            return new[] {CreateOutcome(0, $"No profanities found in '{fieldId}'", null, fieldId)};
        }

        private Dictionary<string, int> FindOccurrences(Expression<Func<string>> property)
        {
            var foundProfanities = new Dictionary<string, int>();
            var value = property.Compile()();
            if (string.IsNullOrWhiteSpace(value)) return foundProfanities;
            var checkValue = value.FormatForParsing();
            

            foreach (var profanity in ProfanityList)
            {
                var occurrences = checkValue.CountOccurrences(profanity);

                if (occurrences > 0)
                {
                    if (!foundProfanities.ContainsKey(profanity)) foundProfanities.Add(profanity, 0);

                    foundProfanities[profanity] += occurrences;
                }
            }

            return foundProfanities;
        }

        private IEnumerable<RuleOutcome> CreateUnconsolidatedOutcomes(Dictionary<string, int> foundProfanities, string fieldId)
        {
            return foundProfanities
                .Select(foundProfanity =>
                {
                    var count = foundProfanity.Value;
                    var term = foundProfanity.Key;
                    var foundMessage = count > 1 ? $"found {count} times" : "found";
                    var narrative = $"Profanity '{term}' {foundMessage} in '{fieldId}'";
                    var data = JsonConvert.SerializeObject(new ProfanityData { Profanity = term, Occurrences = count });

                    return CreateOutcome(count, narrative, data, fieldId);
                });
        }

        private IEnumerable<RuleOutcome> CreateConsolidatedOutcomes(Dictionary<string, int> foundProfanities, string fieldId)
        {
            var count = foundProfanities.Values.Sum();
            var terms = string.Join(",", foundProfanities.Keys);
            var narrative = $"{count} profanities '{terms}' found in '{fieldId}'";
            var data = JsonConvert.SerializeObject(new ProfanityData { Profanity = terms, Occurrences = count });

            return new[]
            {
                CreateOutcome(count, narrative, data, fieldId)
            };
        }
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Infrastructure/EventHandlers/UpdateFAAOnApplicationReviewedTests.cs<|end_filename|>
﻿using System;
using System.Threading;
using System.Threading.Tasks;
using Castle.Core.Logging;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Domain.Events;
using Esfa.Recruit.Vacancies.Client.Infrastructure.EventHandlers;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Services.FAA;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Infrastructure.EventHandlers
{
    public class UpdateFaaOnApplicationReviewedTests
    {
        [Theory]
        [InlineData(ApplicationReviewStatus.New, false)]
        [InlineData(ApplicationReviewStatus.Successful, true)]
        [InlineData(ApplicationReviewStatus.Unsuccessful, true)]    
        public async Task Handle_ShouldSubmitMessageToFAAMessageQueueDependingOnStatus(ApplicationReviewStatus status, bool shouldPublishFaaServiceBusMessage)
        {
            FaaApplicationStatusSummary actualFaaApplicationStatusSummary = null;

            var faaServiceMock = new Mock<IFaaService>();
            faaServiceMock.Setup(f => f.PublishApplicationStatusSummaryAsync(It.IsAny<FaaApplicationStatusSummary>()))
                .Callback<FaaApplicationStatusSummary>(f => actualFaaApplicationStatusSummary = f)
                .Returns(Task.CompletedTask);

            var sut = new UpdateFaaOnApplicationReviewed(faaServiceMock.Object, new Mock<ILogger<UpdateFaaOnApplicationReviewed>>().Object);

            var @event = new ApplicationReviewedEvent
            {
                CandidateFeedback = "candidate feedback",
                CandidateId = Guid.NewGuid(),
                Status = status,
                VacancyReference = 1234567890
            };

            await sut.Handle(@event, new CancellationToken());

            faaServiceMock.Verify(f => f.PublishApplicationStatusSummaryAsync(It.IsAny<FaaApplicationStatusSummary>()), 
                Times.Exactly(shouldPublishFaaServiceBusMessage ? 1 : 0) );

            if (actualFaaApplicationStatusSummary != null)
            {
                actualFaaApplicationStatusSummary.CandidateId.Should().Be(@event.CandidateId);
                actualFaaApplicationStatusSummary.ApplicationStatus.Should().Be(status == ApplicationReviewStatus.Successful ? FaaApplicationStatus.Successful : FaaApplicationStatus.Unsuccessful);
                actualFaaApplicationStatusSummary.IsRecruitVacancy.Should().Be(true);
                actualFaaApplicationStatusSummary.UnsuccessfulReason.Should().Be(@event.CandidateFeedback);
                actualFaaApplicationStatusSummary.VacancyReference.Should().Be((int)@event.VacancyReference);
            }
        }
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/030-vacancyReviews_Update_VacancySnapshot_QualificationTypesIncludeEquivalent.js<|end_filename|>
{
    // https://stackoverflow.com/a/44564089/5596802
    function toGUID(hex) {
        let a = hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2);
        let b = hex.substr(10, 2) + hex.substr(8, 2);
        let c = hex.substr(14, 2) + hex.substr(12, 2);
        let d = hex.substr(16, 16);
        hex = a + b + c + d;
        let uuid = hex.substr(0, 8) + "-" + hex.substr(8, 4) + "-" + hex.substr(12, 4) + "-" + hex.substr(16, 4) + "-" + hex.substr(20, 12);
        return uuid;
    }

    print("Start updating Reviews appending 'or equivalent' to snapshot qualifications.");

    let ids = db.reviews.find({"vacancySnapshot.qualifications": { $exists: true }}, {_id : 1} ).toArray();
    
    print(`Found '${ids.length}' documents`);
    
    for(var iId = 0; iId < ids.length; iId++){

        let doc = db.reviews.findOne({_id : ids[iId]._id});
        print(`Processing document '${iId + 1}' of '${ids.length}' '${toGUID(doc._id.hex())}'`);
        
        let changed = false;
            
        for(var i = 0; i < doc.vacancySnapshot.qualifications.length; i++)
        {
            let currentQualificationType = doc.vacancySnapshot.qualifications[i].qualificationType;
            let updatedQualificationType = doc.vacancySnapshot.qualifications[i].qualificationType;

            switch(currentQualificationType)
            {
            case "GCSE":
                updatedQualificationType = "GCSE or equivalent";
                break;
            case "AS Level":
                updatedQualificationType = "AS Level or equivalent";
                break;
            case "A Level":
                updatedQualificationType = "A Level or equivalent";
                break;
            case "BTEC":
                updatedQualificationType = "BTEC or equivalent";
                break;
            case "NVQ or SVQ Level 1":
                updatedQualificationType = "NVQ or SVQ Level 1 or equivalent";
                break;
            case "NVQ or SVQ Level 2":
                updatedQualificationType = "NVQ or SVQ Level 2 or equivalent";
                break;
            case "NVQ or SVQ Level 3":
                updatedQualificationType = "NVQ or SVQ Level 3 or equivalent";
                break; 
            }

            if(currentQualificationType != updatedQualificationType)
            {
                doc.vacancySnapshot.qualifications[i].qualificationType = updatedQualificationType;
                changed = true;

                print(`Changing qualificationType:'${currentQualificationType}' to '${doc.vacancySnapshot.qualifications[i].qualificationType}'`);
            }
        }

        if(changed){
            let writeResult = db.reviews.update({
                "_id": doc._id
            }, {
                $set: { "vacancySnapshot.qualifications": doc.vacancySnapshot.qualifications }
            }, {
                upsert: false
            });

            if (writeResult.hasWriteConcernError()) {
                printjson(writeResult.writeConcernError);
                quit(14);
            }

            print(`Updated document '${toGUID(doc._id.hex())}'`);
        }
        else{
            print(`Skipping document '${toGUID(doc._id.hex())}'. Qualifications already updated`);
        }
    }

    print("Finished updating Reviews appending 'or equivalent' to snapshot qualifications.");
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Exceptions/AuthorisationException.cs<|end_filename|>
﻿using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Exceptions
{
    [Serializable]
    public class AuthorisationException : RecruitException
    {
        public AuthorisationException(string message) : base(message) { }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/Interfaces/IEmployerEvent.cs<|end_filename|>
using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces
{
    public interface IEmployerEvent
    {
        string EmployerAccountId { get; }
    }
}

<|start_filename|>src/Employer/Employer.Web/Orchestrators/DeleteVacancyOrchestrator.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.ViewModels.DeleteVacancy;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Client;
using Esfa.Recruit.Vacancies.Client.Domain.Exceptions;
using Esfa.Recruit.Employer.Web.ViewModels;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Shared.Web.ViewModels;

namespace Esfa.Recruit.Employer.Web.Orchestrators
{
    public class DeleteVacancyOrchestrator
    {
        private readonly IEmployerVacancyClient _client;
        private readonly IRecruitVacancyClient _vacancyClient;

        public DeleteVacancyOrchestrator(IEmployerVacancyClient client, IRecruitVacancyClient vacancyClient)
        {
            _client = client;
            _vacancyClient = vacancyClient;
        }

        public async Task<DeleteViewModel> GetDeleteViewModelAsync(VacancyRouteModel vrm)
        {
            var vacancy = await _vacancyClient.GetVacancyAsync(vrm.VacancyId);

            Utility.CheckAuthorisedAccess(vacancy, vrm.EmployerAccountId);

            if (!vacancy.CanDelete)
                throw new InvalidStateException(string.Format(ErrorMessages.VacancyNotAvailableForEditing, vacancy.Title));

            var vm = new DeleteViewModel
            {
                Title = vacancy.Title
            };

            return vm;
        }

        public async Task DeleteVacancyAsync(DeleteEditModel m, VacancyUser user)
        {
            var vacancy = await _vacancyClient.GetVacancyAsync(m.VacancyId);

            Utility.CheckAuthorisedAccess(vacancy, m.EmployerAccountId);

            if (!vacancy.CanDelete)
                throw new InvalidStateException(string.Format(ErrorMessages.VacancyNotAvailableForEditing, vacancy.Title));

            await _client.DeleteVacancyAsync(vacancy.Id, user);
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Mappers/ReviewFieldMappingLookupsForPage.cs<|end_filename|>
using System.Collections.Generic;
using Esfa.Recruit.Shared.Web.ViewModels;

namespace Esfa.Recruit.Shared.Web.Mappers
{
    public class ReviewFieldMappingLookupsForPage
    {
        public ReviewFieldMappingLookupsForPage(IReadOnlyList<ReviewFieldIndicatorViewModel> viewModels, IDictionary<string, IEnumerable<string>> vacancyMappings)
        {
            FieldIdentifiersForPage = viewModels;
            VacancyPropertyMappingsLookup = vacancyMappings;
        }

        public IReadOnlyList<ReviewFieldIndicatorViewModel> FieldIdentifiersForPage { get; }

        public IDictionary<string, IEnumerable<string>> VacancyPropertyMappingsLookup { get; }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/ValidateAllTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation
{
    public class ValidateAllTests : VacancyValidationTestsBase
    {
        [Fact]
        public void ShouldNotCrashWhenValidatingAnEmptyVacancy()
        {
            var vacancy = new Vacancy();
            
            var result = Validator.Validate(vacancy, VacancyRuleSet.All);

            result.HasErrors.Should().BeTrue();
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/ApplicationReview/ApplicationReviewEditModel.cs<|end_filename|>
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Shared.Web.ViewModels.ApplicationReview;

namespace Esfa.Recruit.Employer.Web.ViewModels.ApplicationReview
{
    public class ApplicationReviewEditModel : ApplicationReviewRouteModel, IApplicationReviewEditModel
    {
        public ApplicationReviewStatus? Outcome { get; set; }
        public string CandidateFeedback { get; set; }
    }
}

<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/Considerations/ConsiderationsEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Provider.Web.RouteModel;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.Considerations
{
    public class ConsiderationsEditModel : VacancyRouteModel
    {
        public string ThingsToConsider { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/QueryStore/Projections/Vacancy/Qualification.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.Vacancy
{
    public class Qualification
    {
        public string QualificationType { get; set; }
        public string Subject { get; set; }
        public string Grade { get; set; }
        public string Weighting { get; set; }
    }
}


<|start_filename|>src/Shared/UnitTests/TestHelpers/TestCache.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Cache;

namespace Esfa.Recruit.UnitTests.TestHelpers
{
    public class TestCache : ICache
    {
        public Task<T> CacheAsideAsync<T>(string key, DateTime absoluteExpiration, Func<Task<T>> objectToCache)
        {
            return objectToCache();
        }
    }
}


<|start_filename|>src/Provider/Provider.Web/Configuration/ProviderRecruitClaims.cs<|end_filename|>
namespace Esfa.Recruit.Provider.Web.Configuration
{
    public class ProviderRecruitClaims
    {
        public static string IdamsUserDisplayNameClaimTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/displayname";
        public static string IdamsUserNameClaimTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";
        public static string IdamsUserUserTypeClaimTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/usertype";
        public static string IdamsUserServiceTypeClaimTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/service";
        public static string IdamsUserUkprnClaimsTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/ukprn";
        public static string IdamsUserEmailClaimTypeIdentifier = "http://schemas.portal.com/mail";
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Providers/IMinimumWageProvider.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Providers
{
    public interface IMinimumWageProvider
    {
        MinimumWage GetWagePeriod(DateTime date);
    }

}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/ApplicationReviewedEvent.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces;
using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using MediatR;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events
{
    public class ApplicationReviewedEvent : EventBase, INotification, IApplicationReviewEvent
    {
        public long VacancyReference { get; set; }
        public Guid CandidateId { get; set; }
        public ApplicationReviewStatus Status { get; set; }
        public string CandidateFeedback { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Providers/IQualificationsProvider.cs<|end_filename|>
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Providers
{
    public interface IQualificationsProvider
    {
        Task<IList<string>> GetQualificationsAsync();
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/Extensions/QualificationsMapperExtensions.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Shared.Web.ViewModels.Qualifications;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.Extensions
{
    public static class QualificationsMapperExtensions
    {
        public static IEnumerable<QualificationEditModel> ToViewModel(this IEnumerable<Qualification> qualifications)
        {
            if (qualifications == null)
            {
                return Enumerable.Empty<QualificationEditModel>();
            }

            return qualifications.Select(q => new QualificationEditModel
            {
                QualificationType = q.QualificationType,
                Subject = q.Subject,
                Grade = q.Grade,
                Weighting = q.Weighting
            });

        }
        
        public static IEnumerable<Qualification> ToEntity(this IEnumerable<QualificationEditModel> qualifications)
        {
            if (qualifications == null)
            {
                return Enumerable.Empty<Qualification>();
            }
            
            return qualifications.Select(q => new Qualification
            {
                QualificationType = q.QualificationType,
                Subject = q.Subject,
                Grade = q.Grade,
                Weighting = q.Weighting
            });
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/QueryStore/Projections/Vacancy/ClosedVacancy.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.Vacancy
{
    public class ClosedVacancy : VacancyProjectionBase
    {
        public ClosedVacancy() : base(QueryViewType.ClosedVacancy.TypeName)
        {}
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/060-referenceData_Delete_MinimumWageRanges.js<|end_filename|>
{
    print("Starting deletion of MinimumWageRanges from referenceData");

    db.referenceData.remove({"_id" : "MinimumWageRanges"});

    print("Finished deletion of MinimumWageRanges from referenceData");
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/Rules/VacancyRules/VacancyTitlePopularityCheckRuleTests.cs<|end_filename|>
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Configuration;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Esfa.Recruit.Vacancies.Client.Application.Rules;
using Esfa.Recruit.Vacancies.Client.Application.Rules.BaseRules;
using Esfa.Recruit.Vacancies.Client.Application.Rules.VacancyRules;
using Esfa.Recruit.Vacancies.Client.Application.Services;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData.ApprenticeshipProgrammes;
using FluentAssertions;
using Moq;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.Rules.VacancyRules
{
    public class VacancyTitlePopularityCheckRuleTests
    {
        private const string TrainingLarsCode = "490";
        private const string ApprenticeTitle1 = "Administration Apprentice";
        private const string ApprenticeTitle2 = "Business Apprentice";
        private const string ApprenticeTitle3 = "Business Administration Apprentice";
        private QaRulesConfiguration _qaRulesConfig = new QaRulesConfiguration
        {
            TitlePopularityPercentageThreshold = 10
        };
        private readonly Mock<IApprenticeshipProgrammeProvider> _mockApprenticeshipProgrammesProvider;
        private readonly Mock<IGetTitlePopularity> _mockGetTitlePopularityService;

        public VacancyTitlePopularityCheckRuleTests()
        {
            _mockApprenticeshipProgrammesProvider = new Mock<IApprenticeshipProgrammeProvider>();
            _mockGetTitlePopularityService = new Mock<IGetTitlePopularity>();

            _mockApprenticeshipProgrammesProvider
            .Setup(x => x.GetApprenticeshipProgrammeAsync(TrainingLarsCode))
            .ReturnsAsync(new ApprenticeshipProgramme
            {
                Id = TrainingLarsCode,
                ApprenticeshipType = TrainingType.Framework,
                Title = "Business and Administration: Business and Administration"
            });

            _mockGetTitlePopularityService
            .Setup(x => x.GetTitlePopularityAsync(TrainingLarsCode, ApprenticeTitle1))
            .ReturnsAsync(0);
            _mockGetTitlePopularityService
            .Setup(x => x.GetTitlePopularityAsync(TrainingLarsCode, ApprenticeTitle2))
            .ReturnsAsync(9);
            _mockGetTitlePopularityService
            .Setup(x => x.GetTitlePopularityAsync(TrainingLarsCode, ApprenticeTitle3))
            .ReturnsAsync(15);
        }

        [Fact]
        public void WhenCreated_ItShouldReturnBasicInformationAboutTheRule()
        {
            var rule = new VacancyTitlePopularityCheckRule(_mockApprenticeshipProgrammesProvider.Object, _mockGetTitlePopularityService.Object, _qaRulesConfig);
            rule.RuleId.Should().Be(RuleId.TitlePopularity);
        }

        [Fact]
        public async Task WhenInvoked_ItShouldSetTheCorrectDetailsDataType()
        {
            var rule = new VacancyTitlePopularityCheckRule(_mockApprenticeshipProgrammesProvider.Object, _mockGetTitlePopularityService.Object, _qaRulesConfig);

            var entity = TestVacancyBuilder
                        .Create()
                        .SetTitle(ApprenticeTitle1)
                        .SetTrainingProgrammeId(TrainingLarsCode);

            var outcome = await rule.EvaluateAsync(entity);

            outcome.Details.First().Data.Should().BeOfType(typeof(string));
        }

        [Theory]
        [InlineData(ApprenticeTitle1, 100)]
        [InlineData(ApprenticeTitle2, 100)]
        [InlineData(ApprenticeTitle3, 0)]
        public async Task WhenInvoked_ItShouldReturnTheExpectedScore(string apprenticeshipTitle, int expectedScore)
        {
            var rule = new VacancyTitlePopularityCheckRule(_mockApprenticeshipProgrammesProvider.Object, _mockGetTitlePopularityService.Object, _qaRulesConfig);

            var entity = TestVacancyBuilder
                        .Create()
                        .SetTitle(apprenticeshipTitle)
                        .SetTrainingProgrammeId(TrainingLarsCode);

            var outcome = await rule.EvaluateAsync(entity);

            outcome.Score.Should().Be(expectedScore);
        }
    }
}

<|start_filename|>src/Employer/Employer.Web/ViewModels/Part2/EmployerContactDetails/EmployerContactDetailsEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.RouteModel;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class EmployerContactDetailsEditModel : VacancyRouteModel
    {
        public string EmployerContactName { get; set; }
        public string EmployerContactEmail { get; set; }
        public string EmployerContactPhone { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ApplicationMethod.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum ApplicationMethod
    {
        ThroughFindAnApprenticeship,
        ThroughExternalApplicationSite
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Services/IReviewSummaryService.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Esfa.Recruit.Shared.Web.Mappers;
using Esfa.Recruit.Shared.Web.ViewModels;

namespace Esfa.Recruit.Shared.Web.Services
{
    public interface IReviewSummaryService
    {
        Task<ReviewSummaryViewModel> GetReviewSummaryViewModelAsync(long vacancyReference,
            ReviewFieldMappingLookupsForPage fieldMappingsLookup);

        Task<ReviewSummaryViewModel> GetReviewSummaryViewModelAsync(Guid reviewId,
            ReviewFieldMappingLookupsForPage reviewFieldIndicatorsForPage);
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/SourceType.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum SourceType
    {
        Clone,
        Extension,
        New
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/IReferenceDataItem.cs<|end_filename|>
using System;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData
{
    public interface IReferenceDataItem
    {
        string Id { get; set; }
        DateTime LastUpdatedDate { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/Configuration/GoogleAnalyticsConfiguration.cs<|end_filename|>
﻿namespace Esfa.Recruit.Shared.Web.Configuration
{
    public sealed class GoogleAnalyticsConfiguration
    {
        public string TrackingManagerCode { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/IHtmlSanitizerService.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Application.Services
{
    public interface IHtmlSanitizerService
    {
        bool IsValid(string html);
        string Sanitize(string html);
    }
}

<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/VacancyDescription/VacancyDescriptionViewModel.cs<|end_filename|>
﻿using System.Collections.Generic;
using Esfa.Recruit.Shared.Web.ViewModels;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.VacancyDescription
{
    public class VacancyDescriptionViewModel
    {
        public string Title { get; internal set; }
        public string VacancyDescription { get; internal set; }
        public string TrainingDescription { get; internal set; }
        public string OutcomeDescription { get; internal set; }
        public ReviewSummaryViewModel Review { get; set; } = new ReviewSummaryViewModel();
        public IList<string> OrderedFieldNames => new List<string>
        {
            nameof(VacancyDescriptionEditModel.VacancyDescription),
            nameof(VacancyDescriptionEditModel.TrainingDescription),
            nameof(VacancyDescriptionEditModel.OutcomeDescription)
        };
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/FAA/FaaConfiguration.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.FAA
{
    public class FaaConfiguration
    {
        public string StorageConnectionString { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/Qualifications/QualificationEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.ViewModels.Qualifications
{
    public class QualificationEditModel
    {
        public string QualificationType { get; set; }
        public string Subject { get; set; }
        public string Grade { get; set; }
        public QualificationWeighting? Weighting { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Engine/RuleSetOptions.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Application.Rules.Engine
{
    public sealed class RuleSetOptions
    {
        public int ReferralThreshold { get; }
        public int ApprovalThreshold { get; }

        public static readonly RuleSetOptions ZeroTolerance = new RuleSetOptions(0, 1);

        /// <summary>Options used to determine the refer/approve thresholds which are used when calculating the ruleset outcome.
        /// This allows for stringency or leniency to be applied.
        /// Basically, lower scores are better.
        /// <para>A low <paramref name="referralThreshold"></paramref> value means there's more chance of a refer outcome as 
        /// any score that are above this value will be a referral.</para>
        /// <para>A high <paramref name="referralThreshold"></paramref> value means there's more chance of an approve outcome.
        /// Any scores between these thresholds will result in an indeterminate outcome.</para>
        /// </summary>
        /// <param name="referralThreshold">If the score is ABOVE this value then the outcome will be to refer</param>
        /// <param name="approvalThreshold">If the score is BELOW this value then the outcome will be to approve</param>
        public RuleSetOptions(int referralThreshold, int approvalThreshold)
        {
            ReferralThreshold = referralThreshold;
            ApprovalThreshold = approvalThreshold;
        }
    }
}


<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/AboutEmployer/AboutEmployerEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Provider.Web.RouteModel;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.AboutEmployer
{
    public class AboutEmployerEditModel : VacancyRouteModel
    {
        public string EmployerDescription { get; set; }
        public string EmployerWebsiteUrl { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Exceptions/NotificationException.cs<|end_filename|>
using System;
using System.Runtime.Serialization;

namespace Esfa.Recruit.Vacancies.Client.Application.Exceptions
{
    [Serializable]
    public class NotificationException : Exception
    {
        public NotificationException() { }
        public NotificationException(string message) : base(message) { }

        public NotificationException(string message, Exception inner) : base(message, inner) { }

        protected NotificationException(SerializationInfo info, StreamingContext context) : base(info, context) { }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Engine/RuleOutcomeDetailsBuilder.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.Engine
{
    /// <summary>
    /// used to build a single consolidated outcome from multiple rule executions 
    /// (eg. when checking the content of multiple text fields).
    /// total reported score for the rule is then either the mean or sum of all the rule outcomes added
    /// </summary>
    public class RuleOutcomeDetailsBuilder
    {
        private readonly RuleId _ruleId;
        private int _totalScore;
        private int _outcomeCount;
        private readonly StringBuilder _narrative;
        private readonly List<RuleOutcome> _details;
        private readonly string _target;

        public int Count => _details.Count;

        public static RuleOutcomeDetailsBuilder Create(RuleId ruleId, string target = RuleOutcome.NoSpecificTarget)
        {
            return new RuleOutcomeDetailsBuilder(ruleId, target);
        }

        private RuleOutcomeDetailsBuilder(RuleId ruleId, string target)
        {
            _ruleId = ruleId;
            _totalScore = 0;
            _outcomeCount = 0;
            _narrative = new StringBuilder();
            _details = new List<RuleOutcome>();
            _target = target;
        }

        public RuleOutcomeDetailsBuilder Add(IEnumerable<RuleOutcome> outcomes)
        {
            foreach (var outcome in outcomes)
            {
                if (outcome.RuleId != _ruleId)
                    throw new ArgumentException($"Invalid rule ID specified '{outcome.RuleId}' (does not match existing rule ID '{_ruleId}' in the outcome)", nameof(outcome.RuleId));

                _totalScore += outcome.Score;
                _outcomeCount++;
                _narrative.AppendLine(outcome.Narrative);
                _details.Add(outcome);
            }

            return this;
        }

        public RuleOutcomeDetailsBuilder Add(RuleOutcome outcome)
        {
            return Add(new[] {outcome});
        }

        public RuleOutcome ComputeSum(string narrative = null)
        {
            EnsureThereAreOutcomes();

            return new RuleOutcome(_ruleId, _totalScore, narrative ?? _narrative.ToString(), _target, _details);
        }

        public RuleOutcome ComputeMean(string narrative = null)
        {
            EnsureThereAreOutcomes();

            var meanScore = _totalScore / _outcomeCount;

            return new RuleOutcome(_ruleId, meanScore, narrative ?? _narrative.ToString(), _target, _details);
        }

        public RuleOutcome ComputeMax(string narrative = null)
        {
            EnsureThereAreOutcomes();

            var maxScore = _details.Max(d => d.Score);

            return new RuleOutcome(_ruleId, maxScore, narrative ?? _narrative.ToString(), _target, _details);
        }

        public RuleOutcome CreateOutcome(int score, string narrative = null)
        {
            return new RuleOutcome(_ruleId, score, narrative ?? _narrative.ToString(), _target, _details);
        }

        private void EnsureThereAreOutcomes()
        {
            if (_outcomeCount == 0)
                throw new InvalidOperationException("Cannot compute score from outcome details because there are no details");
        }
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/package.json<|end_filename|>
{
  "name": "esfa_recruit_cosmosdb",
  "version": "1.0.0",
  "description": "JS project containing Mongo shell scripts for the setup and data migrations for the recruit database.",
  "main": "documentMigration.js",
  "scripts": {
    "lint": "eslint *.js",
    "test": "./test.sh"
  },
  "author": "sachin.patel",
  "license": "MIT",
  "devDependencies": {
    "eslint": "^5.0.1"
  }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ProfanityData.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class ProfanityData
    {
        public string Profanity { get; set; }
        public int Occurrences { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Configuration/QaRulesConfiguration.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Application.Configuration
{
    public class QaRulesConfiguration
    {
        public int TitlePopularityPercentageThreshold { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/QueryStore/Projections/Vacancy/TrainingProvider.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.Vacancy
{
    public class TrainingProvider
    {
        public long Ukprn { get; set; }
        public string Name { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/ApplicationReview/TrainingCoursesViewModel.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Extensions;

namespace Esfa.Recruit.Shared.Web.ViewModels.ApplicationReview
{
    public class TrainingCoursesViewModel
    {
        public string Provider { get; set; }
        public string Title { get; set; }
        public DateTime FromDate { get; set; }
        public DateTime ToDate { get; set; }

        public string FromDateAsText => FromDate.ToMonthYearString();
        public string ToDateAsText => ToDate.ToMonthYearString();
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/ReviewSummaryViewModel.cs<|end_filename|>
﻿using System.Collections.Generic;

namespace Esfa.Recruit.Shared.Web.ViewModels
{
    public class ReviewSummaryViewModel
    {
        public bool HasBeenReviewed { get; internal set; }
        public string ReviewerComments { get; internal set; }
        public IEnumerable<ReviewFieldIndicatorViewModel> FieldIndicators { get; internal set; } = new List<ReviewFieldIndicatorViewModel>();
    }
}


<|start_filename|>src/Provider/Provider.Web/ViewModels/VacancyPreview/SubmitEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Provider.Web.RouteModel;

namespace Esfa.Recruit.Provider.Web.ViewModels.VacancyPreview
{
    public class SubmitEditModel : VacancyRouteModel
    {
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/FAA/FaaApplicationStatus.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.FAA
{
    public enum FaaApplicationStatus
    {
        Successful = 80,
        Unsuccessful = 90
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Exceptions/RecruitException.cs<|end_filename|>
﻿using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Exceptions
{
    public abstract class RecruitException : Exception
    {
        protected RecruitException(string message) : base(message) { }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/RuleOutcomeIndicator.cs<|end_filename|>
﻿using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class RuleOutcomeIndicator
    {
        public Guid RuleOutcomeId { get; set; }

        public bool IsReferred { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/SourceOrigin.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum SourceOrigin
    {
        Api,
        EmployerWeb,
        ProviderWeb,
        WebComplaint
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/EmployerContactDetailContactEmailValidationTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public partial class EmployerContactDetailValidationTests : VacancyValidationTestsBase
    {
        [Theory]
        [InlineData(null)]
        [InlineData("")]
        [InlineData("<EMAIL>")]
        public void NoErrorsWhenEmployerContactEmailIsValid(string emailAddress)
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Email = emailAddress} 
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void EmployerContactEmailMustBe100CharactersOrLess()
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Email = "name@".PadRight(101, 'w') }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.EmployerContact)}.{nameof(vacancy.EmployerContact.Email)}");
            result.Errors[0].ErrorCode.Should().Be("92");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerContactDetails);
        }

        [Theory]
        [MemberData(nameof(TestData.BlacklistedCharacters), MemberType = typeof(TestData))]
        public void EmployerContactEmailMustNotContainInvalidCharacters(string invalidChar)
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Email = invalidChar }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.EmployerContact)}.{nameof(vacancy.EmployerContact.Email)}");
            result.Errors[0].ErrorCode.Should().Be("93");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerContactDetails);
        }

        [Fact]
        public void EmployerContactEmailMustBeValidEmailFormat()
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Email = "joe" }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.EmployerContact)}.{nameof(vacancy.EmployerContact.Email)}");
            result.Errors[0].ErrorCode.Should().Be("94");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerContactDetails);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/TrainingProvider.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class TrainingProvider
    {
        public long? Ukprn { get; set; }
        public string Name { get; set; }
        public Address Address { get; set; }
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/StartDateValidationTests.cs<|end_filename|>
using System;
using System.Collections.Generic;
using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class StartDateValidationTests : VacancyValidationTestsBase
    {
        public static IEnumerable<object[]> InvalidStartDates =>
            new List<object[]>
            {
                new object[] { DateTime.UtcNow.Date },
                new object[] { DateTime.UtcNow },
                new object[] { DateTime.UtcNow.AddDays(-1) }
            };

        [Fact]
        public void NoErrorsWhenStartDateIsValid()
        {
            var vacancy = new Vacancy
            {
                StartDate = DateTime.UtcNow.AddDays(5)
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.StartDate);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void StartDateMustHaveAValue()
        {
            var vacancy = new Vacancy 
            {
                StartDate = null
            };
            
            var result = Validator.Validate(vacancy, VacancyRuleSet.StartDate);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.StartDate)}");
            result.Errors[0].ErrorCode.Should().Be("20");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.StartDate);
        }

        [Theory]
        [MemberData(nameof(InvalidStartDates))]
        public void StartDateMustBeGreaterThanToday(DateTime startDate)
        {
            var vacancy = new Vacancy
            {
                StartDate = startDate
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.StartDate);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.StartDate)}");
            result.Errors[0].ErrorCode.Should().Be("22");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.StartDate);
        }
    }
}

<|start_filename|>src/QA/QA.Web/Configuration/ViewNames.cs<|end_filename|>
namespace Esfa.Recruit.Qa.Web.Configuration
{
    public static class ViewNames
    {
        public const string ErrorView = "Error";
        public const string AccessDenied = "AccessDenied";       
        public const string Review = "Review";
        public const string PageNotFound = "PageNotFound";
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/VacancyComparer/VacancyComparerResult.cs<|end_filename|>
﻿using System.Collections.Generic;

namespace Esfa.Recruit.Vacancies.Client.Application.Services.VacancyComparer
{
    public class VacancyComparerResult
    {
        public IEnumerable<VacancyComparerField> Fields;
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/Services/SlaServiceTests.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Esfa.Recruit.Vacancies.Client.Application.Services;
using FluentAssertions;
using Moq;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.Services
{
    public class SlaServiceTests
    {
        [Fact]
        public void GetSlaDeadlineAsync_ShouldHandleWorkingDays()
        {
            var bankholidaysProviderMock = new Mock<IBankHolidayProvider>();
            bankholidaysProviderMock.Setup(b => b.GetBankHolidaysAsync()).Returns(Task.FromResult(new List<DateTime>()));

            var sut = new SlaService(bankholidaysProviderMock.Object);

            var actual = sut.GetSlaDeadlineAsync(DateTime.Parse("2018-07-23 14:38")).Result;

            actual.Should().Be(DateTime.Parse("2018-07-24 14:38"));
        }

        [Fact]
        public void GetSlaDeadlineAsync_ShouldHandleWeekends()
        {
            var bankholidaysProviderMock = new Mock<IBankHolidayProvider>();
            bankholidaysProviderMock.Setup(b => b.GetBankHolidaysAsync()).Returns(Task.FromResult(new List<DateTime>()));

            var sut = new SlaService(bankholidaysProviderMock.Object);

            var actual = sut.GetSlaDeadlineAsync(DateTime.Parse("2018-07-27 14:38")).Result;

            actual.Should().Be(DateTime.Parse("2018-07-30 14:38"));
        }

        [Fact]
        public void GetSlaDeadlineAsync_ShouldHandleBankHolidays()
        {
            var bankholidaysProviderMock = new Mock<IBankHolidayProvider>();
            bankholidaysProviderMock.Setup(b => b.GetBankHolidaysAsync())
                .Returns(Task.FromResult(new List<DateTime> { DateTime.Parse("2018-08-27") }));

            var sut = new SlaService(bankholidaysProviderMock.Object);

            var actual = sut.GetSlaDeadlineAsync(DateTime.Parse("2018-08-24 14:38")).Result;

            actual.Should().Be(DateTime.Parse("2018-08-28 14:38"));
        }

        [Fact]
        public void GetSlaDeadlineAsync_ShouldHandleEaster()
        {
            var bankholidaysProviderMock = new Mock<IBankHolidayProvider>();
            bankholidaysProviderMock.Setup(b => b.GetBankHolidaysAsync())
                .Returns(Task.FromResult(new List<DateTime>
                {
                    DateTime.Parse("2019-04-19"),
                    DateTime.Parse("2019-04-22"),
                }));

            var sut = new SlaService(bankholidaysProviderMock.Object);

            var actual = sut.GetSlaDeadlineAsync(DateTime.Parse("2019-04-18 14:38")).Result;

            actual.Should().Be(DateTime.Parse("2019-04-23 14:38"));
        }

        [Fact]
        public void GetSlaDeadlineAsync_ShouldHandleSubmissionsOnNonWorkingDays()
        {
            var bankholidaysProviderMock = new Mock<IBankHolidayProvider>();
            bankholidaysProviderMock.Setup(b => b.GetBankHolidaysAsync()).Returns(Task.FromResult(new List<DateTime>()));

            var sut = new SlaService(bankholidaysProviderMock.Object);

            var actual = sut.GetSlaDeadlineAsync(DateTime.Parse("2018-07-21 14:38")).Result;

            actual.Should().Be(DateTime.Parse("2018-07-24 00:00"));
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/ApplicationReview/ApplicationReviewViewModel.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.ViewModels.ApplicationReview
{
    public class ApplicationReviewViewModel
    {
        public string AddressLine1 { get; set; }
        public string AddressLine2 { get; set; }
        public string AddressLine3 { get; set; }
        public string AddressLine4 { get; set; }
        public ApplicationReviewDisabilityStatus DisabilityStatus { get; set; }
        public int EducationFromYear { get; set; }
        public string EducationInstitution { get; set; }
        public int EducationToYear { get; set; }
        public string Email { get; set; }
        public string FriendlyId { get; set; }
        public string Improvements { get; set; }
        public string HobbiesAndInterests { get; set; }
        public string Name { get; set; }
        public string Phone { get; set; }
        public string Postcode { get; set; }
        public List<QualificationViewModel> Qualifications { get; set; }
        public List<string> Skills { get; set; }
        public ApplicationReviewStatus? Status { get; set; }
        public string Strengths { get; set; }
        public string Support { get; set; }
        public List<TrainingCoursesViewModel> TrainingCourses { get; set; }
        public List<WorkExperienceViewModel> WorkExperiences { get; set; }

        public bool HasNoQualifications => Qualifications.Any() == false;
        public bool HasNoTrainingCourses => TrainingCourses.Any() == false;
        public bool HasNoWorkExperience => WorkExperiences.Any() == false;
        public bool HasSkills => Skills.Any();
        public bool HasNoSkills => !HasSkills;
        public bool HasNoSupportRequirements => string.IsNullOrWhiteSpace(Support);
        public bool CanNotChangeOutcome => (Status == ApplicationReviewStatus.Successful || Status == ApplicationReviewStatus.Unsuccessful);
        public bool CanChangeOutcome => !CanNotChangeOutcome;
        public bool ShowDisabilityStatusAlert => DisabilityStatus == ApplicationReviewDisabilityStatus.Yes;

        public ApplicationReviewStatus? Outcome { get; set; }
        public string CandidateFeedback { get; set; }

        public IList<string> OrderedFieldNames => new List<string>
        {
            nameof(Outcome)
        };
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/documentMigration.js<|end_filename|>

// Scripts called by this script will raise Mongo exit codes as documented on the below link.
// https://docs.mongodb.com/manual/reference/exit-codes/

{
    const targetDbName = "recruit";

    if (db !== targetDbName) {
        db = db.getSiblingDB(targetDbName);
    }

    let changeScriptFileRegex = /^\.\/\d{3}.*js$/,
        changeScripts = ls().filter(scr => changeScriptFileRegex.test(scr)).sort();

    print(`Found ${changeScripts.length} change scripts to run:`);
    changeScripts.forEach(scr => print(scr));

    changeScripts.forEach(scr => load(scr));
}


<|start_filename|>src/Employer/Employer.Web/Controllers/Part2/EmployerContactDetailsController.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.Configuration.Routing;
using Esfa.Recruit.Employer.Web.Orchestrators.Part2;
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Employer.Web.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Esfa.Recruit.Employer.Web.Extensions;
using Esfa.Recruit.Shared.Web.Extensions;

namespace Esfa.Recruit.Employer.Web.Controllers.Part2
{
    [Route(RoutePaths.AccountVacancyRoutePath)]
    public class EmployerContactDetailsController : Controller
    {
        private readonly EmployerContactDetailsOrchestrator _orchestrator;

        public EmployerContactDetailsController(EmployerContactDetailsOrchestrator orchestrator)
        {
            _orchestrator = orchestrator;
        }

        [HttpGet("employer-contact-details", Name = RouteNames.EmployerContactDetails_Get)]
        public async Task<IActionResult> EmployerContactDetails(VacancyRouteModel vrm)
        {
            var vm = await _orchestrator.GetEmployerContactDetailsViewModelAsync(vrm);
            return View(vm);
        }

        [HttpPost("employer-contact-details", Name = RouteNames.EmployerContactDetails_Post)]
        public async Task<IActionResult> EmployerContactDetails(EmployerContactDetailsEditModel m)
        {            
            var response = await _orchestrator.PostEmployerContactDetailsEditModelAsync(m, User.ToVacancyUser());

            if (!response.Success)
            {
                response.AddErrorsToModelState(ModelState);
            }

            if (!ModelState.IsValid)
            {
                var vm = await _orchestrator.GetEmployerContactDetailsViewModelAsync(m);
                return View(vm);
            }

            return RedirectToRoute(RouteNames.Vacancy_Preview_Get);
        }
    }
}

<|start_filename|>src/QA/QA.Web/Extensions/ClaimsPrincipalExtensions.cs<|end_filename|>
using System.Security.Claims;
using Esfa.Recruit.Qa.Web.Security;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Qa.Web.Extensions
{
    public static class ClaimsPrincipalExtensions
    {
        public static VacancyUser GetVacancyUser(this ClaimsPrincipal user)
        {
            var userId = user.FindFirstValue(StaffIdamsClaims.IdamsUserIdClaimTypeIdentifier);
            var name = user.FindFirstValue(StaffIdamsClaims.IdamsUserNameClaimTypeIdentifier);

            return new VacancyUser 
            {
                UserId  = userId,
                Name = name,
                Email = userId
            };
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/MinimumWage.cs<|end_filename|>
using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class MinimumWage
    {
        public DateTime ValidFrom { get; set; }
        public decimal ApprenticeshipMinimumWage { get; set; }
        public decimal NationalMinimumWageLowerBound { get; set; }
        public decimal NationalMinimumWageUpperBound { get; set; }
    }
}

<|start_filename|>src/Jobs/Recruit.Vacancies.Jobs/run.cmd<|end_filename|>
@echo off
dotnet Esfa.Recruit.Vacancies.Jobs.dll


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/ValidationSummaryViewModel.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Collections.Generic;

namespace Esfa.Recruit.Shared.Web.ViewModels
{
    public class ValidationSummaryViewModel
    {
        public IList<string> OrderedFieldNames { get; set; } = new List<string>();
        public ModelStateDictionary ModelState { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Configuration/FeatureExtensions.cs<|end_filename|>
﻿using Esfa.Recruit.Shared.Web.FeatureToggle;
using Microsoft.Extensions.DependencyInjection;

namespace Esfa.Recruit.Shared.Web.Configuration
{
    public static class FeatureExtensions
    {
        public static void AddFeatureToggle(this IServiceCollection services)
        {
            services.AddSingleton<IFeature, Feature>();
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/Filters/PlannedOutageResultFilter.cs<|end_filename|>
using System.Threading.Tasks;
using Employer.Web.Configuration;
using Esfa.Recruit.Employer.Web.Configuration;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Esfa.Recruit.Employer.Web.Filters
{
    public class PlannedOutageResultFilter : ResultFilterAttribute
    {
        private readonly EmployerRecruitSystemConfiguration _systemConfig;

        public PlannedOutageResultFilter(EmployerRecruitSystemConfiguration systemConfig)
        {
            _systemConfig = systemConfig;
        }

        public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
        {
            if (context.HttpContext.Request.Cookies.ContainsKey(CookieNames.SeenOutageMessage) == false)
            {
                if (string.IsNullOrEmpty(_systemConfig.PlannedOutageMessage) == false)
                {
                    var ctrlr = (Controller)context.Controller;
                    ctrlr.ViewData.Add(ViewDataKeys.CanShowOutageMessage, true);
                    ctrlr.ViewData.Add(ViewDataKeys.PlannedOutageMessage, _systemConfig.PlannedOutageMessage);
                }
            }

            await next();
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Exceptions/InvalidStateException.cs<|end_filename|>
﻿using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Exceptions
{
    [Serializable]
    public class InvalidStateException : RecruitException
    {
        public InvalidStateException(string message) : base(message) { }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/NextVacancyReview/INextVacancyReviewService.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Services.NextVacancyReview
{
    public interface INextVacancyReviewService
    {
        Task<VacancyReview> GetNextVacancyReviewAsync(string userId);
        DateTime GetExpiredAssignationDateTime();
        bool VacancyReviewCanBeAssigned(ReviewStatus reviewStatus, DateTime? reviewedDate);
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/Interfaces/IVacancyEvent.cs<|end_filename|>
using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces
{
    public interface IVacancyEvent
    {
        Guid VacancyId { get; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/IReferenceDataWriter.cs<|end_filename|>
﻿using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData
{
    public interface IReferenceDataWriter
    {
        Task UpsertReferenceData<T>(T referenceData) where T : class, IReferenceDataItem;
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Slack/SlackMessage.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Slack
{
    internal class SlackMessage
    {
        public string Text { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/Interfaces/IProviderEvent.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces
{
    public interface IProviderEvent
    {
        long Ukprn { get; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/IGenerateVacancyNumbers.cs<|end_filename|>
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Services
{
    public interface IGenerateVacancyNumbers
    {
        Task<long> GenerateAsync();
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/SetupEmployerEvent.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces;
using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using MediatR;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events
{
    public class SetupEmployerEvent : EventBase, INotification, IEmployerEvent
    {
        public string EmployerAccountId { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/TitlePopularityService.cs<|end_filename|>
using System;
using System.Linq;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Providers;

namespace Esfa.Recruit.Vacancies.Client.Application.Services
{
    public class TitlePopularityService : IGetTitlePopularity
    {
        private readonly IGetVacancyTitlesProvider _titlesProvider;

        public TitlePopularityService(IGetVacancyTitlesProvider titlesProvider)
        {
            _titlesProvider = titlesProvider;
        }

        public async Task<int> GetTitlePopularityAsync(string larsCode, string title)
        {
            var titles = await _titlesProvider.GetVacancyTitlesAsync(larsCode);
            var totalCount = titles.Count;

            if (totalCount == 0 || !titles.Select(t => t.ToLower()).Contains(title.ToLower()))
                return 0;

            var matchedTitleGroup = titles.GroupBy(t => t)
                                            .FirstOrDefault(g => g.Key.Equals(title, StringComparison.OrdinalIgnoreCase));

            if (matchedTitleGroup != null)
            {
                var calculatedPopularityPercentage = (100 * matchedTitleGroup.Count()) / totalCount;
                return calculatedPopularityPercentage;
            }

            return 0;
        }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/Validations/TypeOfIntegerAttribute.cs<|end_filename|>
﻿namespace Esfa.Recruit.Shared.Web.ViewModels.Validations
{
    using System;
    using System.ComponentModel.DataAnnotations;

    public class TypeOfIntegerAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }

            return (int.TryParse((string)value, out var i));
        }
    }
}

<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/Program.cs<|end_filename|>
﻿using CommandLine;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using static System.Console;

namespace Esfa.Recruit.Console.RecruitSeedDataWriter
{
    class Program
    {
        public static async Task<int> Main(string[] args)
        {
            await CommandLine.Parser.Default.ParseArguments<Options>(args)
                                        .MapResult(opts => RunOptionsAndReturnExitCode(opts), ArgumentsNotParsed);
            
            if (Debugger.IsAttached) WaitPrompt();

            return 0;
        }

        private static Task ArgumentsNotParsed(IEnumerable<Error> errs)
        {
            Environment.Exit((int)SystemErrorCode.ERROR_INVALID_PARAMETER);
            return null;
        }

        private static async Task RunOptionsAndReturnExitCode(Options opts)
        {   
            string jsonContent = await GetDataContent(opts.InputFile);

            if (!string.IsNullOrWhiteSpace(jsonContent) && BsonDocument.TryParse(jsonContent, out var bsonDoc))
            {
                var writerOptions = new WriterOptions(opts.ConnectionString, opts.Collection);
                var writer = new MongoWriter(writerOptions);

                try
                {
                    var writeResult = await writer.Write(bsonDoc, opts.Overwrite);
                    WriteSuccessLine($"Loading {opts.InputFile} into {opts.ConnectionString.DatabaseName} DB collection {opts.Collection} {writeResult.ToString().ToLower()}.");
                }
                catch (Exception ex)
                {
                    WriteErrorLine(ex.Message);
                    Environment.Exit((int)CustomErrorCode.FAILED_WRITE_DATA);
                }
            }
            else
            {
                WriteErrorLine($"File '{opts.InputFile}' is empty or contains invalid formatted json.");
                Environment.Exit((int)SystemErrorCode.ERROR_BAD_FORMAT);
            }
        }

        private static async Task<string> GetDataContent(string filePath)
        {
            CheckFileExists(filePath);

            try
            {
                return await File.ReadAllTextAsync(filePath);
            }
            catch (Exception)
            {
                WriteErrorLine($"Error reading file {filePath}.");
                Environment.Exit((int)SystemErrorCode.ERROR_BAD_FORMAT);
            }

            return null;
        }

        private static void CheckFileExists(string filePath)
        {
            if (!File.Exists(filePath))
            {
                WriteErrorLine($"Could not find file {filePath}.");
                Environment.Exit((int)SystemErrorCode.ERROR_FILE_NOT_FOUND);
            }
        }

        private static void WriteSuccessLine(string message)
        {
            ForegroundColor = ConsoleColor.Green;
            WriteLine(message);
            ForegroundColor = ConsoleColor.White;
        }

        private static void WriteErrorLine(string message)
        {
            ForegroundColor = ConsoleColor.Red;
            WriteLine(message);
            ForegroundColor = ConsoleColor.White;

            if (Debugger.IsAttached) WaitPrompt();
        }

        private static void WaitPrompt()
        {
            WriteLine("Press any key to continue");
            ReadKey(true);
        }
    }

    class Options
    {
        [Option('f', "file", Required = true, HelpText = "Input file to be processed.")]
        public string InputFile { get; set; }

        [Option('c', "collection", Default = "referenceData", HelpText = "Collection to which input data would be stored in.")]
        public string Collection { get; set; }

        [Option('o', "overwrite", Default = false, HelpText = "Replace any matching data that already exists in target.")]
        public bool Overwrite { get; set; }

        [Value(0, MetaName = "cosmos connection string", Required = true, HelpText = "A Cosmos MongoDb connection string that includes the target database name.")]
        public MongoUrl ConnectionString { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/Validations/TypeOfDateAttribute.cs<|end_filename|>
﻿using System.Globalization;
using Esfa.Recruit.Shared.Web.Extensions;

namespace Esfa.Recruit.Shared.Web.ViewModels.Validations
{
    using System;
    using System.ComponentModel.DataAnnotations;

    public class TypeOfDateAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }

            return ((string) value).AsDateTimeUk() != null;
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/RuleTemplates/VacancyTitlePopularityRuleMessageTemplate.cs<|end_filename|>
﻿
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.RuleTemplates
{
    public static class VacancyTitlePopularityRuleMessageTemplate
    {
        public static string ToText(TitlePopularityData msgData, string fieldName)
        {
            return $"The {fieldName.ToLower()} is not common for apprenticeships with the training {msgData.TrainingType} {msgData.TrainingCode} - {msgData.TrainingTitle}.";
        }
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/Services/VacancyComparerServiceTests.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Vacancies.Client.Application.Services;
using Esfa.Recruit.Vacancies.Client.Application.Services.VacancyComparer;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.Services
{
    public class VacancyComparerServiceTests
    {
        public static class ComparisonDataSource
        {
            public static IEnumerable<object[]> TestData => new List<object[]>
            {
                new object[] {CreateVacancy(), CreateEmptyVacancy(), false},
                new object[] {CreateVacancy(), CreateChangedVacancy(), false},
                new object[] {CreateVacancy(), CreateVacancy(), true}
            };
        }

        [Theory]
        [MemberData(nameof(ComparisonDataSource.TestData), MemberType = typeof(ComparisonDataSource))]
        public void ShouldCompare(Vacancy a, Vacancy b, bool expectedAreEqual)
        {

            var sut = new VacancyComparerService();

            var result = sut.Compare(a, b);

            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.VacancyReference)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerAccountId)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.ApplicationInstructions)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.ApplicationMethod)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.ApplicationUrl)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.ClosingDate)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.Description)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.DisabilityConfident)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerContact.Email)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerContact.Name)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerContact.Phone)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerDescription)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerLocation.AddressLine1)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerLocation.AddressLine2)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerLocation.AddressLine3)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerLocation.AddressLine4)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerLocation.Postcode)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerName)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.EmployerWebsiteUrl)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.NumberOfPositions)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.OutcomeDescription)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.ProgrammeId)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.ShortDescription)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.StartDate)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.ThingsToConsider)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.Title)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.TrainingDescription)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.TrainingProvider.Ukprn)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.Wage.WeeklyHours)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.Wage.WorkingWeekDescription)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.Wage.WageAdditionalInformation)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.Wage.WageType)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.Wage.FixedWageYearlyAmount)).AreEqual.Should().Be(expectedAreEqual);
            result.Fields.Single(f => f.FieldName == FieldIdResolver.ToFieldId(v => v.Wage.Duration)).AreEqual.Should().Be(expectedAreEqual);
        }

        private static Vacancy CreateVacancy()
        {
            return new Vacancy
            {
                VacancyReference = 123456,
                EmployerAccountId = "employer account id",
                ApplicationInstructions = "application instructions",
                ApplicationMethod = ApplicationMethod.ThroughExternalApplicationSite,
                ApplicationUrl = "application url",
                ClosingDate = new DateTime(),
                Description = "description",
                DisabilityConfident = DisabilityConfident.No,
                EmployerContact = new ContactDetail
                {
                    Email = "employer contact email",
                    Name = "<NAME>",
                    Phone = "employer contact phone",
                },
                EmployerDescription = "employer description",
                EmployerLocation = new Address
                {
                    AddressLine1 = "address line 1",
                    AddressLine2 = "address line 2",
                    AddressLine3 = "address line 3",
                    AddressLine4 = "address line 4",
                    Postcode = "post code"
                },
                EmployerName = "<NAME>",
                EmployerWebsiteUrl = "employer website",
                NumberOfPositions = 5,
                OutcomeDescription = "outcome descriptions",
                ProgrammeId = "programme id",
                Qualifications = new List<Qualification>
                {
                    new Qualification{QualificationType = "qualification type 1", Subject = "subject 1", Grade = "grade 1", Weighting = QualificationWeighting.Desired},
                    new Qualification{QualificationType = "qualification type 2", Subject = "subject 2", Grade = "grade 2", Weighting = QualificationWeighting.Essential},
                },
                ShortDescription = "short description",
                Skills = new List<string> { "skill 1", "skill 2" },
                StartDate = new DateTime(),
                ThingsToConsider = "things to consider",
                Title = "title",
                TrainingDescription = "training description",
                TrainingProvider = new TrainingProvider { Ukprn = 1234 },
                Wage = new Wage
                {
                    WeeklyHours = 35.5m,
                    WorkingWeekDescription = "working week description",
                    WageAdditionalInformation = "wage additional information",
                    WageType = WageType.FixedWage,
                    FixedWageYearlyAmount = 1000.00m,
                    Duration = 1,
                    DurationUnit = DurationUnit.Month
                }
            };
        }

        private static Vacancy CreateChangedVacancy()
        {
            return new Vacancy
            {
                VacancyReference = 1234567,
                EmployerAccountId = "employer account id CHANGED",
                ApplicationInstructions = "application instructions CHANGED",
                ApplicationMethod = ApplicationMethod.ThroughFindAnApprenticeship,
                ApplicationUrl = "application url CHANGED",
                ClosingDate = DateTime.MaxValue,
                Description = "description CHANGED",
                DisabilityConfident = DisabilityConfident.Yes,
                EmployerContact = new ContactDetail
                {
                    Email = "employer contact email CHANGED",
                    Name = "employer contact name CHANGED",
                    Phone = "employer contact phone CHANGED",
                },
                EmployerDescription = "employer description CHANGED",
                EmployerLocation = new Address
                {
                    AddressLine1 = "address line 1 CHANGED",
                    AddressLine2 = "address line 2 CHANGED",
                    AddressLine3 = "address line 3 CHANGED",
                    AddressLine4 = "address line 4 CHANGED",
                    Postcode = "post code CHANGED"
                },
                EmployerName = "<NAME>",
                EmployerWebsiteUrl = "employer website CHANGED",
                NumberOfPositions = 6,
                OutcomeDescription = "outcome descriptions CHANGED",
                ProgrammeId = "programme id CHANGED",
                Qualifications = new List<Qualification>
                {
                    new Qualification{QualificationType = "qualification type 1", Subject = "subject 1", Grade = "grade 1", Weighting = QualificationWeighting.Desired},
                    new Qualification{QualificationType = "qualification type 2", Subject = "subject 2", Grade = "grade 2 CHANGED", Weighting = QualificationWeighting.Essential},
                },
                ShortDescription = "short description CHANGED",
                Skills = new List<string> { "skill 1", "skill 2 CHANGED" },
                StartDate = DateTime.MaxValue,
                ThingsToConsider = "things to consider CHANGED",
                Title = "title CHANGED",
                TrainingDescription = "training description CHANGED",
                TrainingProvider = new TrainingProvider { Ukprn = 12345 },
                Wage = new Wage
                {
                    WeeklyHours = 36.5m,
                    WorkingWeekDescription = "working week description CHANGED",
                    WageAdditionalInformation = "wage additional information CHANGED",
                    WageType = WageType.NationalMinimumWage,
                    FixedWageYearlyAmount = 2000.00m,
                    Duration = 2,
                    DurationUnit = DurationUnit.Year
                }
            };
        }

        private static Vacancy CreateEmptyVacancy()
        {
            return new Vacancy
            {
                VacancyReference = null,
                EmployerAccountId = null,
                ApplicationInstructions = null,
                ApplicationMethod = null,
                ApplicationUrl = null,
                ClosingDate = null,
                Description = null,
                DisabilityConfident = DisabilityConfident.Yes,
                EmployerContact = null,
                EmployerDescription = null,
                EmployerLocation = null,
                EmployerName = null,
                EmployerWebsiteUrl = null,
                NumberOfPositions = null,
                OutcomeDescription = null,
                ProgrammeId = null,
                Qualifications = null,
                ShortDescription = null,
                StartDate = null,
                ThingsToConsider = null,
                Title = null,
                TrainingDescription = null,
                TrainingProvider = null,
                Wage = null
            };
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/Controllers/CloseVacancyController.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Esfa.Recruit.Employer.Web.Configuration;
using Esfa.Recruit.Employer.Web.Configuration.Routing;
using Esfa.Recruit.Employer.Web.Extensions;
using Esfa.Recruit.Employer.Web.Orchestrators;
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Employer.Web.ViewModels;
using Esfa.Recruit.Shared.Web.ViewModels;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Employer.Web.Controllers
{
    [Route(RoutePaths.AccountVacancyRoutePath)]
    public class CloseVacancyController : Controller
    {
        private readonly CloseVacancyOrchestrator _orchestrator;

        public CloseVacancyController(CloseVacancyOrchestrator orchestrator)
        {
            _orchestrator = orchestrator;
        }

        [HttpGet("close", Name = RouteNames.CloseVacancy_Get)]
        public Task<IActionResult> Close(VacancyRouteModel vrm)
        {
            return GetCloseVacancyConfirmationView(vrm);
        }

        [HttpPost("close", Name = RouteNames.CloseVacancy_Post)]
        public async Task<IActionResult> Close(CloseEditModel m)
        {
            if (!ModelState.IsValid)
            {
                var vrm = new VacancyRouteModel { VacancyId = m.VacancyId, EmployerAccountId = m.EmployerAccountId };
                return await GetCloseVacancyConfirmationView(vrm);
            }

            if (!m.ConfirmClose.Value)
            {
                return RedirectToRoute(RouteNames.VacancyManage_Get);
            }

            var response = await _orchestrator.CloseVacancyAsync(m, User.ToVacancyUser());

            TempData.Add(TempDataKeys.VacancyClosedMessage, string.Format(InfoMessages.VacancyClosed, response.Data.VacancyReference, response.Data.Title));

            return RedirectToRoute(RouteNames.VacancyManage_Get);
        }

        private async Task<IActionResult> GetCloseVacancyConfirmationView(VacancyRouteModel vrm)
        {
            var vm = await _orchestrator.GetCloseViewModelAsync(vrm);
            return View(ViewNames.CloseVacancyView, vm);
        }
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/archived/2018-08-28_010-queryViews_LiveVacancy_Insert_DefaultField_ApplicationMethod.js<|end_filename|>
{
    print("Start adding/updating LiveVacancy queryViews with default applicationMethod.");

    const query = {
            $or: [
                { "type": "LiveVacancy" },
                { "viewType": "LiveVacancy" }
            ],
            "applicationMethod": { $exists: false }
        },
        batchUpdateLimit = 500;
    let passThrough = 1,
        maxLoops = Math.ceil(db.queryViews.find().count(query) / batchUpdateLimit);

    if (maxLoops === 0) {
        maxLoops = 1;
    }

    do {
        let matchedDocs = db.queryViews.aggregate([
            {
                $match: query
            },
            {
                $sort: { "lastUpdated": 1 }
            },
            {
                $limit: batchUpdateLimit
            }
        ]);

        print(`Found ${matchedDocs._batch.length} document(s) to operate on in pass-through ${passThrough} of ${maxLoops}.`);

        while (matchedDocs.hasNext()) {
            let doc = matchedDocs.next();

            let writeResult = db.queryViews.update({
                "_id": doc._id,
                "applicationMethod": { $exists: false }
            }, {
                $set: { "applicationMethod": "ThroughExternalApplicationSite" }
            }, {
                upsert: false
            });

            if (writeResult.hasWriteConcernError()) {
                printjson(writeResult.writeConcernError);
                quit(14);
            }

            print(`Updated document '${doc._id}' with applicationMethod: ThroughExternalApplicationSite.`);
        }

        passThrough++;
    }
    while (passThrough <= maxLoops && db.queryViews.find().count(query) > 0);

    print("Finished adding/updating LiveVacancy queryViews with default applicationMethod.");
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ApplicationReviewDisabilityStatus.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum ApplicationReviewDisabilityStatus
    {
        Unknown = 0,
        No,
        PreferNotToSay,
        Yes
    }
}


<|start_filename|>src/Shared/UnitTests/Shared.Web/RuleTemplates/RuleMessageTemplateRunnerTests.cs<|end_filename|>
﻿using Esfa.Recruit.Shared.Web.RuleTemplates;
using Esfa.Recruit.Vacancies.Client.Application.Rules;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.Vacancies.Client.UnitTests.Shared.Web.RuleTemplates
{
    public class RuleMessageTemplateRunnerTests
    {
        [Fact]
        public void ShouldCreateProfanityCheckMessage()
        {
            var sut = new RuleMessageTemplateRunner();

            var actual = sut.ToText(RuleId.ProfanityChecks, "{\"Profanity\":\"drat\",\"Occurrences\":1}", "Profanity field");
            actual.Should().Be("Profanity field contains the phrase 'drat'");

            actual = sut.ToText(RuleId.ProfanityChecks, "{\"Profanity\":\"drat\",\"Occurrences\":3}", "Profanity field");
            actual.Should().Be("Profanity field contains the phrase 'drat' 3 times");
        }

        [Fact]
        public void ShouldCreateBannedPhraseCheckMessage()
        {
            var sut = new RuleMessageTemplateRunner();

            var actual = sut.ToText(RuleId.BannedPhraseChecks, "{\"BannedPhrase\":\"driving licence\",\"Occurrences\":1}", "Banned phrase field");
            actual.Should().Be("Banned phrase field contains the phrase 'driving licence'");

            actual = sut.ToText(RuleId.BannedPhraseChecks, "{\"BannedPhrase\":\"driving licence\",\"Occurrences\":3}", "Banned phrase field");
            
            actual.Should().Be("Banned phrase field contains the phrase 'driving licence' 3 times");
        }

        [Fact]
        public void ShouldCreateTitlePopularityCheckMessage()
        {
            var sut = new RuleMessageTemplateRunner();

            var actual = sut.ToText(RuleId.TitlePopularity, "{\"TrainingCode\":\"CODE\",\"TrainingTitle\":\"TITLE\",\"TrainingType\":\"TYPE\"}", "Title field");

            actual.Should().Be("The title field is not common for apprenticeships with the training TYPE CODE - TITLE.");
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ApplicationTrainingCourse.cs<|end_filename|>
﻿using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class ApplicationTrainingCourse
    {
        public string Provider { get; set; }
        public string Title { get; set; }
        public DateTime FromDate { get; set; }
        public DateTime ToDate { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/Skills/CandidateSkillsProvider.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Cache;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData.Skills
{
    public class CandidateSkillsProvider : ICandidateSkillsProvider
    {
        private readonly IReferenceDataReader _referenceDataReader;
        private readonly ILogger<CandidateSkillsProvider> _logger;
        private readonly ICache _cache;
        private readonly ITimeProvider _timeProvider;

        public CandidateSkillsProvider(IReferenceDataReader referenceDataReader, ILogger<CandidateSkillsProvider> logger, ICache cache, ITimeProvider timeProvider)
        {
            _referenceDataReader = referenceDataReader;
            _logger = logger;
            _cache = cache;
            _timeProvider = timeProvider;
        }
        public async Task<List<string>> GetCandidateSkillsAsync()
        {
            return await _cache.CacheAsideAsync(CacheKeys.Skills,
                _timeProvider.NextDay,
                async () =>
                {
                    _logger.LogInformation("Attempting to retrieve candidate skills from reference data.");
                    var result = await _referenceDataReader.GetReferenceData<CandidateSkills>();
                    if (result != null)
                        return result.Skills;
                    _logger.LogWarning("Unable to retrieve reference data for candidate skills.");
                    return new List<string>();
                });
        }
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/WageValidationTests.cs<|end_filename|>
using System;
using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class WageValidationTests : VacancyValidationTestsBase
    {
        [Theory]
        [InlineData(WageType.FixedWage, 30000, null)]
        [InlineData(WageType.NationalMinimumWage, null, null)]
        [InlineData(WageType.NationalMinimumWageForApprentices, null, null)]
        [InlineData(WageType.Unspecified, null, "This is a valid value")]
        public void NoErrorsWhenWageFieldsAreValid(WageType wageTypeValue, int? yearlyFixedWageAmountValue, string wageAdditionalInfoValue)
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WageType = wageTypeValue,
                    FixedWageYearlyAmount = Convert.ToDecimal(yearlyFixedWageAmountValue),
                    WageAdditionalInformation = wageAdditionalInfoValue
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Wage);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void WageMustNotBeNull()
        {
            var vacancy = new Vacancy
            {
                Wage = null
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Wage);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}");
            result.Errors[0].ErrorCode.Should().Be("46");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Wage);
        }

        [Fact]
        public void WageTypeMustHaveAValue()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WageType = null
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Wage);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.WageType)}");
            result.Errors[0].ErrorCode.Should().Be("46");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Wage);
        }

        [Fact]
        public void WageTypeMustHaveAValidValue()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WageType = (WageType)1000
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Wage);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.WageType)}");
            result.Errors[0].ErrorCode.Should().Be("46");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Wage);
        }

        [Theory]
        [InlineData(null)]
        [InlineData("")]
        public void WageAdditionalInfoIsOptional(string descriptionValue)
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WageType = WageType.NationalMinimumWage,
                    WageAdditionalInformation = descriptionValue
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Wage);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Theory]
        [InlineData("<")]
        [InlineData(">")]
        public void WageAdditionalInfoMustContainValidCharacters(string invalidCharacter)
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WageType = WageType.NationalMinimumWage,
                    WageAdditionalInformation = new String('a', 50) + invalidCharacter + new String('a', 50)
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Wage);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.WageAdditionalInformation)}");
            result.Errors[0].ErrorCode.Should().Be("45");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Wage);
        }

        [Fact]
        public void WageAdditionalInfoMustBeLessThan251Characters()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WageType = WageType.NationalMinimumWage,
                    WageAdditionalInformation = new string('a', 252)
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Wage);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.WageAdditionalInformation)}");
            result.Errors[0].ErrorCode.Should().Be("44");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Wage);
        }

        [Theory]
        [InlineData(null)]
        [InlineData("")]
        public void UnspecifiedWageMustHaveAWageAddtionalInfoValue(string wageAdditionalInfoValue)
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WageType = WageType.Unspecified,
                    WageAdditionalInformation = wageAdditionalInfoValue
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Wage);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.WageAdditionalInformation)}");
            result.Errors[0].ErrorCode.Should().Be("50");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Wage);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/DurationUnit.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum DurationUnit
    {
        Month,
        Year
    }
}


<|start_filename|>src/Jobs/Recruit.Vacancies.Jobs/DomainEvents/Handlers/Candidate/DeleteCandidateHandler.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Events;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Client;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Jobs.DomainEvents.Handlers.Candidate
{
    public class DeleteCandidateHandler : DomainEventHandler, IDomainEventHandler<CandidateDeleteEvent>
    {
        private readonly ILogger<DeleteCandidateHandler> _logger;
        private readonly IJobsVacancyClient _client;

        public DeleteCandidateHandler(ILogger<DeleteCandidateHandler> logger, IJobsVacancyClient client) : base(logger)
        {
            _logger = logger;
            _client = client;
        }

        public async Task HandleAsync(string eventPayload)
        {
            var @event = DeserializeEvent<CandidateDeleteEvent>(eventPayload);

            try
            {
                _logger.LogInformation($"Processing {nameof(CandidateDeleteEvent)} for candidate: {{CandidateId}}", @event.CandidateId);

                await _client.HardDeleteApplicationReviewsForCandidate(@event.CandidateId);
                
                _logger.LogInformation($"Finished Processing {nameof(CandidateDeleteEvent)} for vacancy: candidate: {{CandidateId}}", @event.CandidateId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unable to process {eventBody}", @event);
                throw;
            }

        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/ErrorMessages.cs<|end_filename|>
﻿using System;
namespace Esfa.Recruit.Vacancies.Client.Application.Validation
{
    public static class ErrorMessages
    {
        public const string ChangedClosingDateMustBeNewer = "The closing date can't be changed to an earlier date than used previously.";
    }
}


<|start_filename|>src/QA/QA.Web/ViewModels/AutomatedQaResultViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Qa.Web.ViewModels
{
    public class AutomatedQaResultViewModel
    {
        public string OutcomeId { get; set; }
        public bool Checked { get; set; }
        public string Text { get; set; }
        public string FieldId { get; set; }
    }
}


<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/CustomErrorCode.cs<|end_filename|>
﻿namespace Esfa.Recruit.Console.RecruitSeedDataWriter
{
    public enum CustomErrorCode
    {
        FAILED_WRITE_DATA = 500
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/EventHandlers/UpdateFAAOnApplicationReviewed.cs<|end_filename|>
﻿using System.Threading;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Domain.Events;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Services.FAA;
using MediatR;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.EventHandlers
{
    public class UpdateFaaOnApplicationReviewed :
        INotificationHandler<ApplicationReviewedEvent>
    {
        private readonly IFaaService _faaService;
        private readonly ILogger<UpdateFaaOnApplicationReviewed> _logger;

        public UpdateFaaOnApplicationReviewed(IFaaService faaService, ILogger<UpdateFaaOnApplicationReviewed> logger)
        {
            _faaService = faaService;
            _logger = logger;
        }

        public Task Handle(ApplicationReviewedEvent notification, CancellationToken cancellationToken)
        {
            _logger.LogInformation("Handling {notificationType}", notification.GetType().Name);

            if (notification.Status != ApplicationReviewStatus.Successful 
                && notification.Status != ApplicationReviewStatus.Unsuccessful)
            {
                return Task.CompletedTask;
            }

            var message = new FaaApplicationStatusSummary
            {
                VacancyReference = (int)notification.VacancyReference,
                UnsuccessfulReason = notification.CandidateFeedback,
                CandidateId = notification.CandidateId,
                IsRecruitVacancy = true,
                ApplicationStatus = notification.Status == ApplicationReviewStatus.Successful 
                    ? FaaApplicationStatus.Successful 
                    : FaaApplicationStatus.Unsuccessful
            };

            return _faaService.PublishApplicationStatusSummaryAsync(message);
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/VacancyReviewSummary.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class VacancyReviewSummary
    {
        public long VacancyReference { get; set; }
        public int SubmissionCount { get; set; }
        public DateTime? SlaDeadline { get; set; }
        public DateTime? CreatedDate { get; set; }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyView/ClosedVacancyViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class ClosedVacancyViewModel : DisplayVacancyApplicationViewModel
    {
        public string ClosedDate { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/NextVacancyReview/NextVacancyReviewServiceConfiguration.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Application.Services.NextVacancyReview
{
    public class NextVacancyReviewServiceConfiguration
    {
        public int VacancyReviewAssignationTimeoutMinutes { get; set; }
    }
}


<|start_filename|>src/Provider/Provider.Web/Middleware/ProviderAccountRequirement.cs<|end_filename|>
using Microsoft.AspNetCore.Authorization;

namespace Esfa.Recruit.Provider.Web.Middleware
{
    public class ProviderAccountRequirement : IAuthorizationRequirement { }
}

<|start_filename|>src/Data/Recruit.ChangeScripts/archived/2018-08-28_030-queryViews_Unset_TypeField.js<|end_filename|>
{
    print("Start unset of document field named 'type' in queryViews collection documents.");

    const query = {
            "type": { $exists: true }
        },
        batchUpdateLimit = 500;
    let passThrough = 1,
        maxLoops = Math.ceil(db.queryViews.find().count(query) / batchUpdateLimit);

    if (maxLoops === 0) {
        maxLoops = 1;
    }

    do {
        let matchedDocs = db.queryViews.aggregate([
            {
                $match: query
            },
            {
                $sort: { "lastUpdated": 1 }
            },
            {
                $limit: batchUpdateLimit
            }
        ]);

        print(`Found ${matchedDocs._batch.length} document(s) to operate on in pass-through ${passThrough} of ${maxLoops}.`);

        while (matchedDocs.hasNext()) {
            let doc = matchedDocs.next();

            let writeResult = db.queryViews.update({
                "_id": doc._id
            }, {
                $unset: { "type": doc.type }
            });

            if (writeResult.hasWriteConcernError()) {
                printjson(writeResult.writeConcernError);
                quit(14);
            }

            print(`Updated document '${doc._id}', unset field 'type'.`);
        }

        passThrough++;
    }
    while (passThrough <= maxLoops && db.queryViews.find().count(query) > 0);

    print("Finished unset of document field named 'type' in queryViews collection documents.");
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/TitlePopularityData.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class TitlePopularityData
    {
        public string TrainingCode { get; set; }
        public string TrainingTitle { get; set; }
        public string TrainingType { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/Orchestrators/SkillsOrchestratorHelper.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Shared.Web.ViewModels.Skills;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.Orchestrators
{
    public class SkillsOrchestratorHelper
    {
        private const int ColumnOneCutOffIndex = 9;
        private const char SortPrefixSeparator = '-';

        private readonly Lazy<List<string>> _lazyCandidateSkills;
        
        private List<string> CandidateSkills => _lazyCandidateSkills.Value;
        private IEnumerable<string> Column1BuiltInSkills => CandidateSkills.Take(ColumnOneCutOffIndex);
        private IEnumerable<string> Column2BuiltInSkills => CandidateSkills.Skip(ColumnOneCutOffIndex);

        public SkillsOrchestratorHelper(Func<List<string>> getCandidateSkillsAsync)
        {
            _lazyCandidateSkills = new Lazy<List<string>>(getCandidateSkillsAsync);
        }

        public void SetViewModelSkillsFromVacancy(SkillsViewModelBase vm, Vacancy vacancy)
        {
            var orderedCustomSkills = GetCustomSkills(vacancy.Skills).ToArray();
            var baseSkills = GetBaseSkills(vacancy.Skills).ToArray();

            SetViewModelSkills(vm, baseSkills, orderedCustomSkills);
        }

        public void SetViewModelSkillsFromDraftSkills(SkillsViewModelBase vm, IList<string> draftSkills)
        {
            var orderedCustomSkills = ExtractAndSort(GetCustomSkills(draftSkills).ToArray());
            var baseSkills = GetBaseSkills(draftSkills).ToArray();

            SetViewModelSkills(vm, baseSkills, orderedCustomSkills);
        }

        public void SetVacancyFromEditModel(Vacancy vacancy, SkillsEditModelBase m)
        {
            if (m.Skills == null)
            {
                m.Skills = new List<string>();
            }

            var baseSkills = GetBaseSkills(m.Skills).ToList();
            var customSkills = GetCustomSkills(m.Skills);
            var sortedCustomSkills = ExtractAndSort(customSkills.ToArray()).ToList();

            HandleCustomSkillChange(m, baseSkills, sortedCustomSkills);

            vacancy.Skills = baseSkills.Union(sortedCustomSkills).ToList();

            // Adding in the ordering of the custom skill entries
            m.Skills = baseSkills.Union(AddOrdering(sortedCustomSkills)).ToList();
        }

        private IEnumerable<string> GetCustomSkills(IEnumerable<string> selected)
        {
            return selected == null ? new List<string>() : selected.Except(CandidateSkills);
        }

        private IEnumerable<string> GetBaseSkills(IEnumerable<string> selected)
        {
            return selected == null ? new List<string>() : selected.Intersect(CandidateSkills);
        }

        private void SetViewModelSkills(SkillsViewModelBase vm, IList<string> baseSkills, IEnumerable<string> orderedCustomSkills)
        {
            var col1Skills = GetSkillsColumnViewModel(Column1BuiltInSkills, baseSkills);
            var col2Skills = GetSkillsColumnViewModel(Column2BuiltInSkills, baseSkills);

            var allCustomSkillViewModels = GetDraftSkillsColumnViewModel(orderedCustomSkills).ToList();
            var col1CustomSkillViewModels = allCustomSkillViewModels.Where((_, index) => index % 2 == 1);
            var col2CustomSkillViewModels = allCustomSkillViewModels.Where((_, index) => index % 2 == 0);

            vm.Column1Checkboxes = col1Skills.Union(col1CustomSkillViewModels).ToList();
            vm.Column2Checkboxes = col2Skills.Union(col2CustomSkillViewModels).ToList();
        }

        private IEnumerable<SkillViewModel> GetSkillsColumnViewModel(IEnumerable<string> skills, IEnumerable<string> selectedSkills)
        {
            return skills.Select(c => new SkillViewModel
            {
                Name = c,
                Selected = selectedSkills != null && selectedSkills.Any(s => s == c),
                Value = c
            });
        }

        private IEnumerable<SkillViewModel> GetDraftSkillsColumnViewModel(IEnumerable<string> draftSkills)
        {
            return draftSkills.Select((c, index) => new SkillViewModel
            {
                Name = c,
                Selected = true, // Always selected
                Value = $"{index + 1}-{c}"
            });
        }

        //
        // Custom skills have an order value pre-fixed to their name in order to retain their order.
        // This method extracts the name part and orders the returned list by the pre-fix
        //
        private static IEnumerable<string> ExtractAndSort(string[] skills)
        {
            if (skills == null || skills.Length == 0)
            {
                return skills ?? new string[0];
            }

            var skillNames = new SortedList<int, string>();

            foreach (var skillValue in skills)
            {
                var separatorIndex = skillValue.IndexOf(SortPrefixSeparator);
                var skillIndex = int.Parse(skillValue.Substring(0, separatorIndex));
                var skillName = skillValue.Substring(separatorIndex + 1);

                skillNames.Add(skillIndex, skillName);
            }

            return skillNames.Select(x => x.Value);
        }

        private void HandleCustomSkillChange(SkillsEditModelBase m, IList<string> baseSkillList, IList<string> customSkillList)
        {
            if (m.IsAddingCustomSkill)
            {
                // If the user has tried to add a custom skill that exists in the default skills list.
                if (CandidateSkills.Contains(m.AddCustomSkillName) && !baseSkillList.Contains(m.AddCustomSkillName))
                {
                    baseSkillList.Add(m.AddCustomSkillName);
                }
                else if (!CandidateSkills.Contains(m.AddCustomSkillName) && !customSkillList.Contains(m.AddCustomSkillName))
                {
                    customSkillList.Add(m.AddCustomSkillName);
                }
            }
        }

        private IEnumerable<string> AddOrdering(List<string> extractedCustomSkills)
        {
            return extractedCustomSkills.Select((c, index) => $"{index + 1}-{c}");
        }

    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Views/Shared/_GoogleTagManagerBodyPartial.cshtml<|end_filename|>
@using Esfa.Recruit.Shared.Web.Configuration
@using Microsoft.Extensions.Options;
@inject IOptions<GoogleAnalyticsConfiguration> GaConfig
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=@GaConfig.Value.TrackingManagerCode"
height="0" width="0" class="hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Extensions/DecimalExtensions.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Domain.Extensions
{
    public static class DecimalExtensions
    {
        public static string AsMoney(this decimal dec)
        {
            return $"{dec:N2}".Replace(".00", "");
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/ApplicationWithdrawnEvent.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using MediatR;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events
{
    // Note: Doesn't implement IApplicationReviewEvent as it's an externally published event.
    public class ApplicationWithdrawnEvent : EventBase, INotification
    {
        public Guid CandidateId { get; set; }
        public long VacancyReference { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/FeatureToggle/Feature.cs<|end_filename|>
﻿using Microsoft.Extensions.Configuration;

namespace Esfa.Recruit.Shared.Web.FeatureToggle
{
    public class Feature : IFeature
    {
        private readonly IConfiguration _configuration;
        public Feature(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        public bool IsFeatureEnabled(string feature)
        {
            var featureValue = _configuration[$"Features:{feature}"];
            if (string.IsNullOrWhiteSpace(featureValue))
                return false;

            return bool.Parse(featureValue);
        }
    }
}


<|start_filename|>src/QA/QA.Web/Security/AuthorizationConfiguration.cs<|end_filename|>
namespace Esfa.Recruit.Qa.Web.Security
{
    public class AuthorizationConfiguration
    {
        public string ClaimType { get; set; }
        public string UserClaimValue { get; set; }
        public string TeamLeadClaimValue { get; set; }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/EntityValidatorTests.cs<|end_filename|>
using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Application.Validation.Fluent;
using Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation;
using FluentAssertions;
using FluentValidation;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application
{
    public class EntityValidatorTests : VacancyValidationTestsBase
    {
        [Fact]
        public void ValidateReturnsFailWhenValidationFails()
        {
            var validator = new EntityValidator<TestEntity, TestEntityRules>(new TestEntityFluentValidator());

            var testEntity = new TestEntity
            {
                TestProperty = 3
            };

            var result = validator.Validate(testEntity, TestEntityRules.All);

            result.HasErrors.Should().BeTrue();
        }

        [Fact]
        public void ValidateReturnsListOfValidationErrorWhenValidationFails()
        {
            var validator = new EntityValidator<TestEntity, TestEntityRules>(new TestEntityFluentValidator());

            var testEntity = new TestEntity
            {
                TestProperty = 3
            };

            var result = validator.Validate(testEntity, TestEntityRules.All);

            result.Errors.Should().HaveCountGreaterThan(0);
        }

        [Fact]
        public void ValidateReturnsNoErrorWhenValidationPasses()
        {
            var validator = new EntityValidator<TestEntity, TestEntityRules>(new TestEntityFluentValidator());

            var testEntity = new TestEntity
            {
                TestProperty = 6
            };

            var result = validator.Validate(testEntity, TestEntityRules.All);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }
    }

    public class TestEntity 
    {
        public int TestProperty { get; set; }
    }

    public enum TestEntityRules
    {
        All
    }

    public class TestEntityFluentValidator : AbstractValidator<TestEntity>
    {
        public TestEntityFluentValidator()
        {
            RuleFor(x => x.TestProperty)
                .GreaterThan(5)
                .WithRuleId((long)TestEntityRules.All);
        }
    }
}

<|start_filename|>src/Employer/Employer.Web/Controllers/Part2/SkillsController.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Esfa.Recruit.Employer.Web.Configuration;
using Esfa.Recruit.Employer.Web.Configuration.Routing;
using Esfa.Recruit.Employer.Web.Extensions;
using Esfa.Recruit.Employer.Web.Orchestrators.Part2;
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Employer.Web.ViewModels.Part2.Skills;
using Microsoft.AspNetCore.Mvc;
using Esfa.Recruit.Shared.Web.Extensions;

namespace Esfa.Recruit.Employer.Web.Controllers.Part2
{
    [Route(RoutePaths.AccountVacancyRoutePath)]
    public class SkillsController : Controller
    {
        private readonly SkillsOrchestrator _orchestrator;

        public SkillsController(SkillsOrchestrator orchestrator)
        {
            _orchestrator = orchestrator;
        }

        [HttpGet("skills", Name = RouteNames.Skills_Get)]
        public async Task<IActionResult> Skills(VacancyRouteModel vrm)
        {
            // If adding/removing skill, tempdata will have the current draft list.
            var vm = await _orchestrator.GetSkillsViewModelAsync(vrm, TempData[TempDataKeys.Skills] as string[]);

            return View(vm);
        }

        [HttpPost("skills", Name = RouteNames.Skills_Post)]
        public async Task<IActionResult> Skills(VacancyRouteModel vrm, SkillsEditModel m)
        {
            var response = await _orchestrator.PostSkillsEditModelAsync(vrm, m, User.ToVacancyUser());

            if (!response.Success)
            {
                response.AddErrorsToModelState(ModelState);
            }

            if (!ModelState.IsValid)
            {
                var vm = await _orchestrator.GetSkillsViewModelAsync(vrm);

                return View(vm);
            }

            if (!string.IsNullOrEmpty(m.AddCustomSkillAction))
            {
                TempData[TempDataKeys.Skills] = m.Skills;
                return RedirectToRoute(RouteNames.Skills_Get);
            }

            return RedirectToRoute(RouteNames.Vacancy_Preview_Get);
        }
    }
}

<|start_filename|>src/Employer/UnitTests/Employer.Web/Mappings/ReviewFieldIndicatorMapperTests.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Employer.Web.Mappings;
using Esfa.Recruit.Shared.Web.Mappers;
using Esfa.Recruit.Shared.Web.RuleTemplates;
using Esfa.Recruit.Vacancies.Client.Application.Rules;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.Employer.UnitTests.Employer.Web.Mappings
{
    public class ReviewFieldIndicatorMapperTests
    {
        [Fact]
        public void ShouldMap()
        {
            var shortDescriptionProfanityCheckRuleOutcome =
                new RuleOutcome(
                    RuleId.ProfanityChecks,
                    100,
                    "Profanity 'drat' found in 'ShortDescription'",
                    "ShortDescription",
                    new List<RuleOutcome>(), "{\"Profanity\" : \"drat\",\"Occurrences\" : 1}");

            var titleProfanityCheckRuleOutcome = new RuleOutcome(
                RuleId.ProfanityChecks,
                0,
                "No profanities found in 'Title'",
                "Title");

            var profanityChecksRuleOutcome = new RuleOutcome(
                RuleId.ProfanityChecks, 
                100,
                "No profanities found in 'Title', Profanity 'drat' found in 'ShortDescription', etc", 
                "",
                new List<RuleOutcome>
                    {
                        shortDescriptionProfanityCheckRuleOutcome,
                        titleProfanityCheckRuleOutcome
                    }
                );

            var sut = new ReviewFieldIndicatorMapper(new RuleMessageTemplateRunner());

            var review = new VacancyReview
            {
                ManualQaFieldIndicators = new List<ManualQaFieldIndicator>
                {
                    new ManualQaFieldIndicator
                    {
                        IsChangeRequested = false,
                        FieldIdentifier = FieldIdentifiers.Title
                    },
                    new ManualQaFieldIndicator
                    {
                        IsChangeRequested = true,
                        FieldIdentifier = FieldIdentifiers.ShortDescription
                    }
                },
                AutomatedQaOutcomeIndicators = new List<RuleOutcomeIndicator>
                {
                    new RuleOutcomeIndicator
                    {
                        IsReferred = false,
                        RuleOutcomeId = titleProfanityCheckRuleOutcome.Id
                    },
                    new RuleOutcomeIndicator
                    {
                        IsReferred = true,
                        RuleOutcomeId = shortDescriptionProfanityCheckRuleOutcome.Id
                    }
                },
                AutomatedQaOutcome = new RuleSetOutcome {RuleOutcomes = new List<RuleOutcome> {profanityChecksRuleOutcome}}
            };

            var vm = sut.MapFromFieldIndicators(ReviewFieldMappingLookups.GetPreviewReviewFieldIndicators(), review).ToList();

            vm.Count.Should().Be(1);

            var shortDescription = vm.Single(v => v.ReviewFieldIdentifier == FieldIdentifiers.ShortDescription);

            shortDescription.ManualQaText.Should().NotBeNullOrEmpty();

            shortDescription.AutoQaTexts.Count.Should().Be(1);
            shortDescription.AutoQaTexts[0].Should().Be("Brief overview contains the phrase 'drat'");
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ApplicationWorkExperience.cs<|end_filename|>
﻿using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class ApplicationWorkExperience
    {
        public string Employer { get; set; }
        public string JobTitle { get; set; }
        public string Description { get; set; }
        public DateTime FromDate { get; set; }
        public DateTime ToDate { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ModelBinders/TrimModelBinderProvider.cs<|end_filename|>
﻿using System;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Shared.Web.ModelBinders
{
    public class TrimModelBinderProvider : IModelBinderProvider
    {
        private readonly ILoggerFactory _loggerFactory;

        public TrimModelBinderProvider(ILoggerFactory loggerFactory)
        {
            _loggerFactory = loggerFactory;
        }

        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (!context.Metadata.IsComplexType && context.Metadata.ModelType == typeof(string))
            {
                return new TrimModelBinder(new SimpleTypeModelBinder(context.Metadata.ModelType, _loggerFactory));
            }
            return null;
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/RuleTemplates/IRuleMessageTemplateRunner.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Rules;

namespace Esfa.Recruit.Shared.Web.RuleTemplates
{
    public interface IRuleMessageTemplateRunner
    {
        string ToText(RuleId ruleId, string data, string fieldName);
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/QueryStore/Projections/QA/QaDashboard.cs<|end_filename|>
﻿using System.Collections.Generic;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.QA
{
    public class QaDashboard : QueryProjectionBase
    {
        public QaDashboard() : base(QueryViewType.QaDashboard.TypeName)
        {
        }

        public int TotalVacanciesForReview { get; set; }
        public int TotalVacanciesBrokenSla { get; set; }
        public int TotalVacanciesResubmitted { get; set; }
        public List<VacancyReview> SearchResults { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/Profanities/ProfanityListProvider.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Cache;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData.Profanities
{
    public class ProfanityListProvider : IProfanityListProvider
    {
        private readonly IReferenceDataReader _referenceDataReader;
        private readonly ILogger<ProfanityListProvider> _logger;
        private readonly ICache _cache;
        private readonly ITimeProvider _timeProvider;

        public ProfanityListProvider(IReferenceDataReader referenceDataReader, ILogger<ProfanityListProvider> logger, ICache cache, ITimeProvider timeProvider)
        {
            _referenceDataReader = referenceDataReader;
            _logger = logger;
            _cache = cache;
            _timeProvider = timeProvider;
        }

        public async Task<IEnumerable<string>> GetProfanityListAsync()
        {
            return await _cache.CacheAsideAsync(CacheKeys.Profanities,
                _timeProvider.NextDay,
                async () =>
                {
                    _logger.LogInformation("Attempting to retrieve profanity list from reference data.");
                    var result = await _referenceDataReader.GetReferenceData<ProfanityList>();
                    if (result != null)
                        return result.Profanities;
                    _logger.LogWarning("Unable to retrieve reference data for profanity list.");
                    return new List<string>();
                });
        }
    }
}


<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/MongoDbCollection.cs<|end_filename|>
﻿using MongoDB.Bson;
using MongoDB.Driver;
using System.Security.Authentication;
using System.Linq;
using System.Threading.Tasks;
using System;

namespace Esfa.Recruit.Console.RecruitSeedDataWriter
{
    internal class MongoDbCollection
    {
        private const string BsonCollectionDocumentNameField = "name";
        private readonly string _collectionName;
        private readonly MongoUrl _connString;

        public MongoDbCollection(MongoUrl connString, string collectionName)
        {
            _connString = connString;
            _collectionName = collectionName;
        }

        public async Task<IMongoCollection<BsonDocument>> GetCollection()
        {
            var settings = MongoClientSettings.FromUrl(_connString);
            settings.SslSettings = new SslSettings { EnabledSslProtocols = SslProtocols.Tls12 };

            var client = new MongoClient(settings);
            var database = client.GetDatabase(_connString.DatabaseName);

            if (!(await database.ListCollectionsAsync()).ToEnumerable().Any(coll => CollectionNameExists(coll, _collectionName)))
            {
                throw new Exception($"Collection {_collectionName} does not exist in database {_connString.DatabaseName}");
            }

            var collection = database.GetCollection<BsonDocument>(_collectionName);

            return collection;
        }

        private bool CollectionNameExists(BsonDocument bsonDoc, string collectionName)
            => bsonDoc.Single(field => field.Name.Equals(BsonCollectionDocumentNameField)).Value.Equals(collectionName);
    }
}


<|start_filename|>src/QA/QA.Web/Exceptions/UnassignedVacancyReviewException.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Exceptions;

namespace Esfa.Recruit.Qa.Web.Exceptions
{
    public class UnassignedVacancyReviewException : RecruitException
    {
        public UnassignedVacancyReviewException(string message) : base(message) { }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Views/Shared/_ApplicationInsightsPartial.cshtml<|end_filename|>
﻿@using Esfa.Recruit.Shared.Web.Configuration
@using Microsoft.Extensions.Options
@inject IOptions<ApplicationInsightsConfiguration> ApplicationInsights

@if(!string.IsNullOrEmpty(ApplicationInsights.Value.InstrumentationKey))
{ 
<!-- AppInsights script -->
<script type="text/javascript" nws-csp-add-nonce="true">
    var appInsights=window.appInsights||function(a){
        function b(a){c[a]=function(){var b=arguments;c.queue.push(function(){c[a].apply(c,b)})}}var c={config:a},d=document,e=window;setTimeout(function(){var b=d.createElement("script");b.src=a.url||"https://az416426.vo.msecnd.net/scripts/a/ai.0.js",d.getElementsByTagName("script")[0].parentNode.appendChild(b)});try{c.cookie=d.cookie}catch(a){}c.queue=[];for(var f=["Event","Exception","Metric","PageView","Trace","Dependency"];f.length;)b("track"+f.pop());if(b("setAuthenticatedUserContext"),b("clearAuthenticatedUserContext"),b("startTrackEvent"),b("stopTrackEvent"),b("startTrackPage"),b("stopTrackPage"),b("flush"),!a.disableExceptionTracking){f="onerror",b("_"+f);var g=e[f];e[f]=function(a,b,d,e,h){var i=g&&g(a,b,d,e,h);return!0!==i&&c["_"+f](a,b,d,e,h),i}}return c
    }({
        instrumentationKey: '@ApplicationInsights.Value.InstrumentationKey'
    });

    window.appInsights=appInsights,appInsights.queue&&0===appInsights.queue.length&&appInsights.trackPageView();
</script>
}

<|start_filename|>src/Employer/UnitTests/Employer.Web/ViewModels/Part1/PartOnePageInfoViewModelTests.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;
using Esfa.Recruit.Shared.Web.ViewModels;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.Employer.UnitTests.Employer.Web.ViewModels.Part1
{
    public class PartOnePageInfoViewModelTests
    {
        [Theory]
        [InlineData("True", false, false, true)]
        [InlineData("False", false, false, true)]
        [InlineData("non-bool value", false, false, true)]
        [InlineData(null, false, false, true)]
        [InlineData("True", true, false, true)]
        [InlineData("False", true, false, false)]
        [InlineData("non-bool value", true, false, true)]
        [InlineData(null, true, false, true)]
        [InlineData("True", true, true, false)]
        [InlineData("False", true, true, false)]
        [InlineData("non-bool value", true, true, false)]
        [InlineData(null, true, true, false)]
        public void SetWizard(string requestedWizardParam, bool hasCompletedPartOne, bool hasStartedPartTwo, bool expectedIsWizardValue)
        {
            var vm = new PartOnePageInfoViewModel
            {
                HasCompletedPartOne = hasCompletedPartOne,
                HasStartedPartTwo = hasStartedPartTwo
            };

            vm.SetWizard(requestedWizardParam);

            vm.IsWizard.Should().Be(expectedIsWizardValue);
            vm.IsNotWizard.Should().Be(!expectedIsWizardValue);
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyView/ApprovedVacancyViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class ApprovedVacancyViewModel : DisplayVacancyViewModel
    {
        public string ApprovedDate { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/ApplicationReview/QualificationViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Shared.Web.ViewModels.ApplicationReview
{
    public class QualificationViewModel
    {
        public string QualificationType { get; set; }
        public string Subject { get; set; }
        public string Grade { get; set; }
        public bool IsPredicted { get; set; }
        public int Year { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Extensions/StringExtensions.cs<|end_filename|>
﻿using System.Text.RegularExpressions;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Extensions
{
    public static class StringExtensions
    {
        public static string PascalToKebabCase(this string value)
        {
            if (string.IsNullOrEmpty(value))
                return value;

            return Regex.Replace(
                value,
                "(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])",
                "-$1",
                RegexOptions.Compiled)
                .Trim()
                .ToLower();
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyView/LiveVacancyViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class LiveVacancyViewModel : DisplayVacancyApplicationViewModel
    {
    }
}

<|start_filename|>src/Employer/Employer.Web/Models/ViewVacancy.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.ViewModels;

namespace Esfa.Recruit.Employer.Web.Models
{
    public class ViewVacancy
    {
        public DisplayVacancyViewModel ViewModel { get; internal set; }
        public string ViewName { get; internal set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/Interfaces/IVacancyReviewEvent.cs<|end_filename|>
using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces
{
    public interface IVacancyReviewEvent
    {
        long VacancyReference { get; }
        Guid ReviewId { get; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Cache/ICache.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Cache
{
    public interface ICache
    {
        Task<T> CacheAsideAsync<T>(string key, DateTime absoluteExpiration, Func<Task<T>> objectToCache);
    }
}


<|start_filename|>src/QA/QA.Web/ViewModels/Validations/ReviewEditModelValidator.cs<|end_filename|>
﻿using System.Linq;
using Esfa.Recruit.Vacancies.Client.Application.Validation.Fluent;
using FluentValidation;

namespace Esfa.Recruit.Qa.Web.ViewModels.Validations
{
    public class ReviewEditModelValidator : AbstractValidator<ReviewEditModel>
    {
        public ReviewEditModelValidator()
        {
            RuleFor(x => x.ReviewerComment)
                .MaximumLength(VacancyReviewValidator.ManualQaCommentMaxLength)
                .WithMessage(string.Format(VacancyReviewValidator.ManualQaCommentLengthMessage, VacancyReviewValidator.ManualQaCommentMaxLength))
                .ValidFreeTextCharacters()
                .WithMessage(VacancyReviewValidator.ManualQaCommentFreeTextCharactersMessage);

            When(x => x.IsRefer, () =>
            {
                RuleFor(x => x.ReviewerComment)
                    .NotEmpty()
                    .WithMessage(VacancyReviewValidator.ManualQaCommentRequired);
            });
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/Fluent/CustomValidators/FreeTextValidator.cs<|end_filename|>
using System;
using System.Text.RegularExpressions;
using FluentValidation.Validators;

namespace Esfa.Recruit.Vacancies.Client.Application.Validation.Fluent.CustomValidators
{
    internal class FreeTextValidator : PropertyValidator, IRegularExpressionValidator 
    {
		private readonly Regex _regex;

		private const string ValidCharactersExpression = @"^[a-zA-Z0-9\u0080-\uFFA7?$@#()""'!,+\-=_:;.&€£*%\s\/\[\]]*$";

		internal FreeTextValidator() : base("{PropertyName} must contain valid characters")
        {
			_regex = CreateRegEx();
		}

		protected override bool IsValid(PropertyValidatorContext context) {
			if (context.PropertyValue == null) return true;

			if (!_regex.IsMatch((string)context.PropertyValue)) {
				return false;
			}

			return true;
		}

		public string Expression => ValidCharactersExpression;

		private static Regex CreateRegEx()
		{
			try
			{
				if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) {
					return new Regex(ValidCharactersExpression, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(2.0));
				}
			}
			catch (Exception)
			{
			}

            return new Regex(ValidCharactersExpression, RegexOptions.IgnoreCase);
		}
	}
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Configuration/ApplicationInsightsConfiguration.cs<|end_filename|>
﻿namespace Esfa.Recruit.Shared.Web.Configuration
{
    public class ApplicationInsightsConfiguration
    {
        public string InstrumentationKey { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/RuleOutcome.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Vacancies.Client.Application.Rules;

namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class RuleOutcome
    {
        public const string NoSpecificTarget = "";

        public RuleOutcome(RuleId ruleId, int score, string narrative, string target = NoSpecificTarget, IEnumerable<RuleOutcome> details = null, string data = null)
        {
            Id = Guid.NewGuid();
            RuleId = ruleId;
            Score = Math.Min(score, 100);
            Narrative = narrative;
            Target = target;
            Data = data;

            if (details != null)
                Details = details;
        }

        public Guid Id { get; set; }
        public IEnumerable<RuleOutcome> Details { get; set; }
        public bool HasDetails => Details.Any();
        public RuleId RuleId { get; set; }
        public int Score { get; set;  }
        public string Narrative { get; set;  }
        public string Data { get; set; }

        /// <summary>
        /// Field or reference that this outcome relates to
        /// </summary>
        public string Target { get; set;  }

        public override string ToString()
        {
            return $"Rule {RuleId} score {Score} ({(HasDetails ? $"{Details.Count()} details" : $"'{Narrative}'")})";
        }
    }
}


<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/WriterOptions.cs<|end_filename|>
﻿using MongoDB.Driver;

namespace Esfa.Recruit.Console.RecruitSeedDataWriter
{
    internal class WriterOptions
    {
        public MongoUrl ConnectionString { get; set; }
        public string CollectionName { get; set; }

        public WriterOptions(MongoUrl connectionString, string collectionName)
        {
            ConnectionString = connectionString;
            CollectionName = collectionName;
        }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/ReviewFieldIndicatorViewModel.cs<|end_filename|>
﻿using System.Collections.Generic;

namespace Esfa.Recruit.Shared.Web.ViewModels
{
    public class ReviewFieldIndicatorViewModel
    {
        public ReviewFieldIndicatorViewModel(string reviewFieldIdentifier, string anchor)
        {
            ReviewFieldIdentifier = reviewFieldIdentifier;
            Anchor = anchor;
            AutoQaTexts = new List<string>();
        }

        public string ReviewFieldIdentifier { get; }
        public string Anchor { get; }
        public string ManualQaText { get; set; }
        public List<string> AutoQaTexts { get; }
    }
}


<|start_filename|>src/Employer/Employer.Web/Controllers/Part2/ApplicationProcessController.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.Configuration.Routing;
using Esfa.Recruit.Employer.Web.Orchestrators.Part2;
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Employer.Web.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Esfa.Recruit.Employer.Web.Extensions;
using Esfa.Recruit.Shared.Web.Extensions;

namespace Esfa.Recruit.Employer.Web.Controllers.Part2
{
    [Route(RoutePaths.AccountVacancyRoutePath)]
    public class ApplicationProcessController : Controller
    {
        private readonly ApplicationProcessOrchestrator _orchestrator;

        public ApplicationProcessController(ApplicationProcessOrchestrator orchestrator)
        {
            _orchestrator = orchestrator;
        }

        [HttpGet("application-process", Name = RouteNames.ApplicationProcess_Get)]
        public async Task<IActionResult> ApplicationProcess(VacancyRouteModel vrm)
        {
            var vm = await _orchestrator.GetApplicationProcessViewModelAsync(vrm);
            return View(vm);
        }

        [HttpPost("application-process", Name = RouteNames.ApplicationProcess_Post)]
        public async Task<IActionResult> ApplicationProcess(ApplicationProcessEditModel m)
        {            
            var response = await _orchestrator.PostApplicationProcessEditModelAsync(m, User.ToVacancyUser());

            if (!response.Success)
            {
                response.AddErrorsToModelState(ModelState);
            }

            if (!ModelState.IsValid)
            {
                var vm = await _orchestrator.GetApplicationProcessViewModelAsync(m);
                return View(vm);
            }

            return RedirectToRoute(RouteNames.Vacancy_Preview_Get);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/Orchestrators/EntityToViewModelPropertyMappings.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Esfa.Recruit.Shared.Web.Orchestrators
{
    // This class represents the mappings of the properties in an Entity to those on a particular ViewModel
    public class EntityToViewModelPropertyMappings<TEntity, TViewModel> : List<Tuple<Expression<Func<TEntity, object>>, Expression<Func<TViewModel, object>>>>
    {
        public void Add(Expression<Func<TEntity, object>> source, Expression<Func<TViewModel, object>> destination)
        {
            Add(new Tuple<Expression<Func<TEntity, object>>, Expression<Func<TViewModel, object>>>(source, destination));
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Engine/IRuleSet.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.Engine
{
    internal interface IRuleSet<in TSubject>
    {
        Task<RuleSetOutcome> EvaluateAsync(TSubject subject, RuleSetOptions options = null);
    }
}


<|start_filename|>src/Provider/Provider.Web/Views/_ViewStart.cshtml<|end_filename|>
﻿@using Esfa.Recruit.Provider.Web.Views
@{
    Layout = LayoutNames.DefaultLayout;
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/EntityValidator.cs<|end_filename|>
using System;
using Esfa.Recruit.Vacancies.Client.Application.Exceptions;
using FluentValidation;
using FluentValidation.Results;

namespace Esfa.Recruit.Vacancies.Client.Application.Validation
{
    public sealed class EntityValidator<T, TRules> : IEntityValidator<T, TRules> where TRules : struct, IComparable, IConvertible, IFormattable 
    {
        private readonly AbstractValidator<T> _validator;
    
        public EntityValidator(AbstractValidator<T> fluentValidator)
        {
            _validator = fluentValidator;
        }

        public void ValidateAndThrow(T entity, TRules rules)
        {
            var validationResult = ValidateEntity(entity, rules);

            if (validationResult.HasErrors)
            {
                throw new EntityValidationException($"Entity: {typeof(T)} has failed validation", validationResult);
            }
        }

        public EntityValidationResult Validate(T entity, TRules rules)
        {
            return ValidateEntity(entity, rules);
        }

        private EntityValidationResult ValidateEntity(T entity, TRules rules)
        {
            var context = new ValidationContext<T>(entity);

            context.RootContextData.Add(ValidationConstants.ValidationsRulesKey, rules);

            var fluentResult = _validator.Validate(context);

            if (!fluentResult.IsValid)
            {
                return CreateValidationErrors(fluentResult);
            }

            return new EntityValidationResult();
        }

        private EntityValidationResult CreateValidationErrors(ValidationResult fluentResult)
        {
            var newResult = new EntityValidationResult();

            if (fluentResult.IsValid == false && fluentResult.Errors.Count > 0)
            {
                foreach(var fluentError in fluentResult.Errors)
                {
                    newResult.Errors.Add(new EntityValidationError(ParseForRuleId(fluentError.CustomState), fluentError.PropertyName, fluentError.ErrorMessage, fluentError.ErrorCode));
                }
            }

            return newResult;
        }

        private long ParseForRuleId(object customState)
        {
            if (customState == null)
                throw new ArgumentNullException(nameof(customState), "Fluent Error should have CustomState property set to the RuleId");

            try
            {
				return Convert.ToInt64(customState);
            }
            catch (Exception ex)
            {
                throw new ArgumentException("Unexpected value for customState. Expecting a long", nameof(customState), ex);
            }
        }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/EmployerWebsiteUrlValidationTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class EmployerWebsiteUrlValidationTests : VacancyValidationTestsBase
    {
        [Theory]
        [InlineData(null)]
        [InlineData("")]
        [InlineData("company.com")]
        [InlineData("www.company.com")]
        [InlineData("http://www.company.com")]
        [InlineData("https://www.company.com")]
        public void NoErrorsWhenEmployerWebsiteUrlIsValid(string url)
        {
            var vacancy = new Vacancy
            {
                EmployerWebsiteUrl = url
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerWebsiteUrl);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }
        
        [Fact]
        public void EmployerWebsiteUrlMustBe100CharactersOrLess()
        {
            var vacancy = new Vacancy
            {
                EmployerWebsiteUrl = "http://www.company.com".PadRight(101, 'w')
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerWebsiteUrl);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be(nameof(vacancy.EmployerWebsiteUrl));
            result.Errors[0].ErrorCode.Should().Be("84");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerWebsiteUrl);
        }

        [Theory]
        [InlineData("invalid url")]
        [InlineData("company")]
        [InlineData("domain.com ?term=query")]
        [InlineData(".com")]
        [InlineData(".org.uk")]
        [InlineData(",com")]
        public void EmployerWebsiteUrlMustBeAValidWebAddress(string invalidUrl)
        {
            var vacancy = new Vacancy
            {
                EmployerWebsiteUrl = invalidUrl
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerWebsiteUrl);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be(nameof(vacancy.EmployerWebsiteUrl));
            result.Errors[0].ErrorCode.Should().Be("82");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerWebsiteUrl);
        }
    }
}

<|start_filename|>src/QA/QA.Web/ViewModels/UnassignReviewEditModel.cs<|end_filename|>
﻿using System;
using System.ComponentModel.DataAnnotations;
using Esfa.Recruit.Qa.Web.ViewModels.Validations;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Qa.Web.ViewModels
{
    public class UnassignReviewEditModel
    {
        [FromRoute]
        public Guid ReviewId { get; set; }

        [Required(ErrorMessage = ValidationMessages.UnassignReviewConfirmationMessages.SelectionRequired)]
        public bool? ConfirmUnassign { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/SequenceStore/Sequence.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Infrastructure.SequenceStore
{
    internal class Sequence
    {
        public string Id { get; internal set; }
        public long LastValue { get; internal set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/Projections/IProviderDashboardProjectionService.cs<|end_filename|>
﻿using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.Projections
{
    public interface IProviderDashboardProjectionService
    {
        Task ReBuildAllDashboardsAsync();
        Task ReBuildDashboardAsync(long ukprn);
    }
}


<|start_filename|>src/QA/QA.Web/ViewModels/UnassignReviewViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Qa.Web.ViewModels
{
    public class UnassignReviewViewModel : UnassignReviewEditModel
    {
        public string AdvisorName { get; set; }

        public string Title { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/Skills/CandidateSkills.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData.Skills
{
    public class CandidateSkills : IReferenceDataItem
    {
        public string Id { get; set; }

        public DateTime LastUpdatedDate { get; set; }

        public List<string> Skills { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/Qualifications/QualificationsProvider.cs<|end_filename|>
using System.Collections.Generic;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Cache;
using Esfa.Recruit.Vacancies.Client.Application.Providers;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData.Qualifications
{
    public class QualificationsProvider : IQualificationsProvider
    {
        private readonly IReferenceDataReader _referenceDataReader;
        private readonly ICache _cache;
        private readonly ITimeProvider _timeProvider;

        public QualificationsProvider(IReferenceDataReader referenceDataReader, ICache cache, ITimeProvider timeProvider)
        {
            _referenceDataReader = referenceDataReader;
            _cache = cache;
            _timeProvider = timeProvider;
        }

        public async Task<IList<string>> GetQualificationsAsync()
        {
            return await _cache.CacheAsideAsync(CacheKeys.Qualifications,
                _timeProvider.NextDay,
                async () =>
                {
                    var data = await _referenceDataReader.GetReferenceData<Qualifications>();
                    return data.QualificationTypes;
                });
        }
    }
}

<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/Data/QualificationTypes.json<|end_filename|>
﻿{
  "_id": "QualificationTypes",
  "lastUpdatedDate": "2018-12-18T00:00:00.000Z",
  "qualificationTypes": [
    "GCSE or equivalent",
    "AS Level or equivalent",
    "A Level or equivalent",
    "BTEC or equivalent",
    "NVQ or SVQ Level 1 or equivalent",
    "NVQ or SVQ Level 2 or equivalent",
    "NVQ or SVQ Level 3 or equivalent",
    "Other"
  ]
}


<|start_filename|>src/Employer/Employer.Web/RouteModel/VacancyRouteModel.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Mvc;
using System;

namespace Esfa.Recruit.Employer.Web.RouteModel
{
    public class VacancyRouteModel
    {
        private string _employerAccountId;

        [FromRoute]
        public string EmployerAccountId
        {
            get => _employerAccountId;
            set => _employerAccountId = value.ToUpper();
        }

        [FromRoute]
        public Guid VacancyId { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Messaging/EventBase.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Messaging
{
    public abstract class EventBase : IEvent
    {
        public override string ToString()
        {
            return GetType().Name;
        }
    }
}


<|start_filename|>src/Jobs/Recruit.Vacancies.Jobs/DomainEvents/IDomainEventHandler.cs<|end_filename|>
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Messaging;

namespace Esfa.Recruit.Vacancies.Jobs.DomainEvents
{
    public interface IDomainEventHandler<out T> where T : IEvent
    {
        Task HandleAsync(string eventPayload);
    }
}



<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/INotifyVacancyReviewUpdates.cs<|end_filename|>
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Services
{
    public interface INotifyVacancyReviewUpdates
    {
        Task VacancyReviewCreated(VacancyReview vacancyReview);
        Task VacancyReviewReferred(VacancyReview vacancyReview);
        Task VacancyReviewApproved(VacancyReview vacancyReview);
    }
}

<|start_filename|>src/Employer/Employer.Web/Models/VacancyInfo.cs<|end_filename|>
using System;

namespace Esfa.Recruit.Employer.Web.Models
{
    public class VacancyInfo
    {
        public Guid Id { get; internal set; }
        public string VacancyReference { get; internal set; }
        public string Title { get; internal set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Ioc/LoggingBuilderExtensions.cs<|end_filename|>
﻿using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Client.Ioc
{
    public static class LoggingBuilderExtensions
    {
        public static ILoggingBuilder ConfigureRecruitLogging(this ILoggingBuilder builder)
        {
            //Change LogLevel to Trace whilst Debugging to output Mongo commands
            builder.AddFilter("Mongo command", LogLevel.None);

            return builder;
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Repositories/IVacancyReviewRepository.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Domain.Repositories
{
    public interface IVacancyReviewRepository
    {
        Task CreateAsync(VacancyReview vacancy);
        Task<VacancyReview> GetAsync(Guid reviewId);
        Task UpdateAsync(VacancyReview review);
    }
}

<|start_filename|>src/Provider/Provider.Web/Configuration/AuthenticationConfiguration.cs<|end_filename|>
﻿namespace Esfa.Recruit.Provider.Web.Configuration
{
    public class AuthenticationConfiguration
    {
        public const int SessionTimeoutMinutes = 30;
        public string WtRealm { get; set; }
        public string MetaDataAddress { get; set; }
    }    
}


<|start_filename|>src/QA/QA.Web/Controllers/DashboardController.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Esfa.Recruit.Qa.Web.Configuration;
using Esfa.Recruit.Qa.Web.Configuration.Routing;
using Esfa.Recruit.Qa.Web.Extensions;
using Esfa.Recruit.Qa.Web.Orchestrators;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Qa.Web.Controllers
{
    public class DashboardController : Controller
    {
        private readonly DashboardOrchestrator _orchestrator;

        public DashboardController(DashboardOrchestrator orchestrator)
        {
            _orchestrator = orchestrator;
        }

        [HttpGet("/", Name = RouteNames.Dashboard_Index_Get)]
        public async Task<IActionResult> Index([FromQuery]string searchTerm)
        {
            var vm = await _orchestrator.GetDashboardViewModelAsync(searchTerm, User);

            vm.DashboardMessage = TempData[TempDataKeys.DashboardMessage]?.ToString();
            
            return View(vm);
        }

        [HttpPost("next-vacancy", Name = RouteNames.Dashboard_Next_Vacancy_Post)]
        public async Task<IActionResult> NextVacancy()
        {
            var vacancyReviewId = await _orchestrator.AssignNextVacancyReviewAsync(User.GetVacancyUser());

            if (vacancyReviewId == null)
                return RedirectToRoute(RouteNames.Dashboard_Index_Get);

            return RedirectToRoute(RouteNames.Vacancy_Review_Get, new {reviewId = vacancyReviewId});
        }

        [HttpGet("next-vacancy", Name = RouteNames.Dashboard_Next_Vacancy_Post)]
        public IActionResult NextVacancyCallback()
        {
            //This GET handles the authentication callback when NextVacancy is POSTed after a session timeout
            return RedirectToRoute(RouteNames.Dashboard_Index_Get);
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Extensions/OrchestratorResponseExtensions.cs<|end_filename|>
using Esfa.Recruit.Shared.Web.Orchestrators;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace Esfa.Recruit.Shared.Web.Extensions
{
    public static class OrchestratorResponseExtensions
    {
        public static void AddErrorsToModelState(this OrchestratorResponse response, ModelStateDictionary modelState)
        {
            if (!response.Success && response.Errors?.Errors !=  null)
            {
                foreach (var error in response.Errors.Errors)
                {
                    modelState.AddModelError(error.PropertyName, error.ErrorMessage);
                }
            }
        }
    }
}

<|start_filename|>src/Employer/Employer.Web/ViewModels/LegalEntityAgreement/LegalEntityAgreementHardStopViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.ViewModels.LegalEntityAgreement
{
    public class LegalEntityAgreementHardStopViewModel
    {
        public bool HasLegalEntityAgreement { get; set; }
    }
}


<|start_filename|>src/QA/QA.Web/Exceptions/NotFoundException.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Exceptions;

namespace Esfa.Recruit.Qa.Web.Exceptions
{
    [Serializable]
    public class NotFoundException : RecruitException
    {
        public NotFoundException(string message) : base(message)
        {
        }
    }
}

<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/MongoWriter.cs<|end_filename|>
﻿using MongoDB.Bson;
using MongoDB.Driver;
using System.Linq;
using System.Threading.Tasks;

namespace Esfa.Recruit.Console.RecruitSeedDataWriter
{
    internal class MongoWriter
    {
        private const string KeyFieldName = "_id";
        private readonly WriterOptions _writerOptions;

        public MongoWriter(WriterOptions writerOptions)
        {
            _writerOptions = writerOptions;
        }

        internal async Task<WriteOperationResult> Write(BsonDocument bsonDocument, bool canOverwrite)
        {
            var collection = await new MongoDbCollection(_writerOptions.ConnectionString, _writerOptions.CollectionName).GetCollection();

            var bsonDocId = bsonDocument.Single(field => field.Name.Equals(KeyFieldName)).Value.AsString;
            var filter = Builders<BsonDocument>.Filter.Eq(KeyFieldName, bsonDocId);

            if (canOverwrite)
            {
                var replaceResult = await collection.ReplaceOneAsync(filter, bsonDocument, new UpdateOptions { IsUpsert = true });
                return  replaceResult.UpsertedId != null 
                        ? WriteOperationResult.Inserted
                        : WriteOperationResult.Replaced;
            }
            else
            {
                var options = new FindOptions<BsonDocument>
                {
                    Limit = 1
                };

                var existingMatchingDocuments = await collection.FindAsync(filter, options);

                if (existingMatchingDocuments.Any() == false)
                {
                    await collection.InsertOneAsync(bsonDocument);
                    return WriteOperationResult.Inserted;
                }

                return WriteOperationResult.Skipped;
            }
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Extensions/ExpressionExtensions.cs<|end_filename|>
﻿using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Esfa.QA.Core.Extensions
{
    public static class ExpressionExtensions
    {
        public static string GetQualifiedFieldId<P>(this Expression<Func<P>> property)
        {
            return GetFieldIdForProperty(property.Body);
        }
        
        public static string GetQualifiedFieldId<T,P>(this Expression<Func<T, P>> property)
        {
            return GetFieldIdForProperty(property.Body);
        }

        private static string GetFieldIdForProperty(Expression propertyBody)
        {
            var memberExpression = propertyBody as MemberExpression;
            var fieldId = string.Empty;

            while (memberExpression != null && memberExpression.Member.MemberType == MemberTypes.Property)
            {
                fieldId = memberExpression.Member.Name + (fieldId != string.Empty ? "." : string.Empty) + fieldId;
                memberExpression = memberExpression.Expression as MemberExpression;
            }

            return fieldId;
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/IReferenceDataReader.cs<|end_filename|>
﻿using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData
{
    public interface IReferenceDataReader
    {
        Task<T> GetReferenceData<T>() where T : class, IReferenceDataItem;
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Extensions/PropertyExpressionExtensions.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Esfa.Recruit.Shared.Web.Extensions
{
    public static class PropertyExpressionExtensions
    {
        public static string GetPropertyName<T>(this Expression<Func<T, object>> propertyExpression)
        {
            var getMemberExp = new Func<Expression, MemberExpression>(toUnwrap =>
            {
                if (toUnwrap is UnaryExpression expression)
                {
                    return expression.Operand as MemberExpression;
                }

                return toUnwrap as MemberExpression;
            });

            var memberNames = new Stack<string>();

            var memberExp = getMemberExp(propertyExpression.Body);

            while (memberExp != null)
            {
                memberNames.Push(memberExp.Member.Name);
                memberExp = getMemberExp(memberExp.Expression);
            }

            return string.Join(".", memberNames);
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Extensions/ApplicationReviewExtensions.cs<|end_filename|>
using System;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.VacancyApplications;

namespace Esfa.Recruit.Shared.Web.Extensions
{
    public static class ApplicationReviewExtensions
    {
        public static string GetFriendlyId(this ApplicationReview applicationReview)
        {
            return GetFriendlyId(applicationReview.Id);
        }

        public static string GetFriendlyId(this VacancyApplication vacancyApplication)
        {
            return GetFriendlyId(vacancyApplication.ApplicationReviewId);
        }

        private static string GetFriendlyId(Guid id)
        {
            return id.ToString().Replace("-", "").Substring(0, 7).ToUpperInvariant();
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/ReferenceData/IApprenticeshipProgrammesUpdateService.cs<|end_filename|>
﻿using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Services.ReferenceData
{
    public interface IApprenticeshipProgrammesUpdateService
    {
        Task UpdateApprenticeshipProgrammesAsync();
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Orchestrators/OrchestratorResponse{T}.cs<|end_filename|>
using Esfa.Recruit.Vacancies.Client.Application.Validation;

namespace Esfa.Recruit.Shared.Web.Orchestrators
{
    public class OrchestratorResponse<T> : OrchestratorResponse
    {
        public OrchestratorResponse(T data) : base(true)
        {
            Data = data;
        }

        public OrchestratorResponse(EntityValidationResult errors) : base(false)
        {
            Errors = errors;
        }

        public T Data { get; set; }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/Rules/VacancyRules/TestVacancyBuilder.cs<|end_filename|>
using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.Rules.VacancyRules
{
    internal static class TestVacancyBuilder
    {
        internal static Vacancy Create()
        {
            return new Vacancy()
            {
                EmployerLocation = new Address(),
                EmployerContact = new ContactDetail(),
                Skills = new List<string>(),
                Qualifications = new List<Qualification>(),
                Wage = new Wage()
            };
        }

        internal static Vacancy SetVacancyReference(this Vacancy entity, long vacancyReference)
        {
            entity.VacancyReference = vacancyReference;

            return entity;
        }

        internal static Vacancy SetTitle(this Vacancy entity, string title)
        {
            entity.Title = title;
            return entity;
        }

        internal static Vacancy SetDescription(this Vacancy entity, string description)
        {
            entity.Description = description;
            return entity;
        }

        internal static Vacancy SetTrainingProgrammeId(this Vacancy entity, string programmeId)
        {
            entity.ProgrammeId = programmeId;
            return entity;
        }

        internal static Vacancy SetSkills(this Vacancy entity, IEnumerable<string> skills)
        {
            entity.Skills = skills.ToList();
            return entity;
        }

        internal static Vacancy SetDetails(this Vacancy entity, string title, string description)
        {
            entity.Title = title;
            entity.Description = description;

            return entity;
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/VacancyTitle/VacancyApiSearchResponse.cs<|end_filename|>

using System.Collections.Generic;

public class VacancyApiSearchResponse
{
    public long TotalMatched { get; set; }
    public long TotalReturned { get; set; }
    public int CurrentPage { get; set; }
    public double TotalPages { get; set; }
    public string SortBy { get; set; }
    public IEnumerable<VacancyApiApprenticeshipSearchResult> Results { get; set; }
}

public class VacancyApiApprenticeshipSearchResult
{
    public int VacancyReference { get; set; }
    public string Title { get; set; }
    public string ShortDescription { get; set; }
    public string ExpectedStartDate { get; set; }
    public string PostedDate { get; set; }
    public string ApplicationClosingDate { get; set; }
    public int NumberOfPositions { get; set; }
    public string TrainingType { get; set; }
    public string TrainingTitle { get; set; }
    public string TrainingCode { get; set; }
    public string EmployerName { get; set; }
    public string TrainingProviderName { get; set; }
    public bool IsNationwide { get; set; }
    public LatLong Location { get; set; }    

    public string ApprenticeshipLevel { get; set; }
    public string VacancyUrl { get; set; }
    public bool IsEmployerDisabilityConfident { get; set; }
    public double DistanceInMiles { get; set; }
}

public class LatLong
{
    public double Longitude { get; set; }
    public double Latitude  { get; set; }
} 



<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/IEntityValidator.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Application.Validation
{
    public interface IEntityValidator<in T, in TRules>
    {
        void ValidateAndThrow(T entity, TRules rules);

        EntityValidationResult Validate(T entity, TRules rules);
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/VacancyComparer/VacancyComparerField.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Application.Services.VacancyComparer
{
    public class VacancyComparerField
    {

        public VacancyComparerField(string fieldName, bool areEqual)
        {
            FieldName = fieldName;
            AreEqual = areEqual;
        }

        public string FieldName { get; }
        public bool AreEqual { get; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/RuleTemplates/BannedPhraseMessageTemplate.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.RuleTemplates
{
    public static class BannedPhraseMessageTemplate
    {
        public static string ToText(BannedPhrasesData msgData, string fieldName)
        {
            var baseMessage = $"{fieldName} contains the phrase '{msgData.BannedPhrase}'";
            return msgData.Occurrences > 1 
                ? $"{baseMessage} {msgData.Occurrences} times"
                : $"{baseMessage}";
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Events/IEventStore.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Events
{
    public interface IEventStore
    {
        Task Add(IEvent @event);
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/Projections/IQaDashboardProjectionService.cs<|end_filename|>
﻿using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.Projections
{
    public interface IQaDashboardProjectionService
    {
        Task RebuildQaDashboardAsync();
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/Wage.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class Wage
    {
        public int? Duration { get; set; }

        public DurationUnit? DurationUnit { get; set; }

        public string WorkingWeekDescription { get; set; }

        public decimal? WeeklyHours { get; set; }

        public WageType? WageType { get; set; }

        public decimal? FixedWageYearlyAmount { get; set; }

        public string WageAdditionalInformation { get; set; }
    }
}


<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/Considerations/ConsiderationsViewModel.cs<|end_filename|>
﻿using Esfa.Recruit.Shared.Web.ViewModels;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.Considerations
{
    public class ConsiderationsViewModel
    {
        public string Title { get; internal set; }
        public string ThingsToConsider { get; internal set; }
        public ReviewSummaryViewModel Review { get; set; } = new ReviewSummaryViewModel();
    }
}


<|start_filename|>src/Provider/Provider.Web/ViewModels/Part1/Employer/EmployerViewModel.cs<|end_filename|>
using System.Collections.Generic;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part1.Employer
{
    public class EmployerViewModel
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/ExternalApplicationUrlValidationTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class ExternalApplicationUrlValidationTests : VacancyValidationTestsBase
    {
        [Theory]
        [InlineData("applyhere.com")]
        [InlineData("www.applyhere.com")]
        [InlineData("http://www.applyhere.com")]
        [InlineData("https://www.applyhere.com")]
        [InlineData("applyhere.com#anchor")]
        [InlineData("applyhere.com?term=query")]
        public void NoErrorsWhenExternalApplicationUrlIsValid(string url)
        {
            var vacancy = new Vacancy
            {
                ApplicationMethod = ApplicationMethod.ThroughExternalApplicationSite,
                ApplicationUrl = url
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ApplicationMethod);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Theory]
        [InlineData(null)]
        [InlineData("")]
        public void ExternalApplicationUrlMustHaveAValue(string url)
        {
            var vacancy = new Vacancy
            {
                ApplicationMethod = ApplicationMethod.ThroughExternalApplicationSite,
                ApplicationUrl = url
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ApplicationMethod);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be(nameof(vacancy.ApplicationUrl));
            result.Errors[0].ErrorCode.Should().Be("85");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.ApplicationMethod);
        }

        [Fact]
        public void ExternalApplicationUrlMustBe200CharactersOrLess()
        {
            var vacancy = new Vacancy
            {
                ApplicationMethod = ApplicationMethod.ThroughExternalApplicationSite,
                ApplicationUrl = "http://www.applyhere.com".PadRight(201, 'w')
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ApplicationMethod);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be(nameof(vacancy.ApplicationUrl));
            result.Errors[0].ErrorCode.Should().Be("84");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.ApplicationMethod);
        }

        [Theory]
        [InlineData("Invalid Url")]
        [InlineData("applyhere")]
        [InlineData("domain.com ?term=query")]
        [InlineData(".com")]
        [InlineData(".org.uk")]
        [InlineData(",com")]
        public void ExternalApplicationUrlMustBeAValidWebAddress(string invalidUrl)
        {
            var vacancy = new Vacancy
            {
                ApplicationMethod = ApplicationMethod.ThroughExternalApplicationSite,
                ApplicationUrl = invalidUrl
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ApplicationMethod);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be(nameof(vacancy.ApplicationUrl));
            result.Errors[0].ErrorCode.Should().Be("86");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.ApplicationMethod);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/BannedPhrasesData.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class BannedPhrasesData
    {
        public string BannedPhrase { get; set; }
        public int Occurrences { get; set; }
    }
}

<|start_filename|>src/Jobs/Recruit.Vacancies.Jobs/DomainEvents/Handlers/Application/ApplicationWithdrawnHandler.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Events;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Client;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Jobs.DomainEvents.Handlers.Application
{
    public class ApplicationWithdrawnHandler : DomainEventHandler, IDomainEventHandler<ApplicationWithdrawnEvent>
    {
        private readonly ILogger<ApplicationWithdrawnEvent> _logger;
        private readonly IJobsVacancyClient _client;

        public ApplicationWithdrawnHandler(ILogger<ApplicationWithdrawnEvent> logger, IJobsVacancyClient client) : base(logger)
        {
            _logger = logger;
            _client = client;
        }

        public async Task HandleAsync(string eventPayload)
        {
            var @event = DeserializeEvent<ApplicationWithdrawnEvent>(eventPayload);

            try
            {
                _logger.LogInformation($"Processing {nameof(ApplicationWithdrawnEvent)} for vacancy: {{VacancyReference}} and candidate: {{CandidateId}}", @event.VacancyReference, @event.CandidateId);

                await _client.WithdrawApplicationAsync(@event.VacancyReference, @event.CandidateId);

                _logger.LogInformation($"Finished Processing {nameof(ApplicationWithdrawnEvent)} for vacancy: {{VacancyReference}} and candidate: {{CandidateId}}", @event.VacancyReference, @event.CandidateId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unable to process {eventBody}", @event);
                throw;
            }
        }
    }
}


<|start_filename|>src/Provider/Provider.Web/ViewModels/Part1/Employer/EmployersEditModel.cs<|end_filename|>
using System;
using Esfa.Recruit.Provider.Web.RouteModel;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part1.Employer
{
    public class EmployersEditModel : VacancyRouteModel
    {
        public string SelectedEmployerId { get; set; }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/CrossField/StartDateClosingDateTests.cs<|end_filename|>
using System;
using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.CrossField
{
    public class StartDateClosingDateTests : VacancyValidationTestsBase
    {
        [Fact]
        public void NoErrorsWhenFieldsAreValid()
        {
            var vacancy = new Vacancy
            {
                StartDate = DateTime.UtcNow.AddDays(5),
                ClosingDate = DateTime.UtcNow.AddDays(4)
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.StartDateEndDate);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void StartDateCantBeTheSameAsClosingDate()
        {
            var vacancy = new Vacancy
            {
                StartDate = DateTime.UtcNow,
                ClosingDate = DateTime.UtcNow
            };
            
            var result = Validator.Validate(vacancy, VacancyRuleSet.StartDateEndDate);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be(string.Empty);
            result.Errors[0].ErrorCode.Should().Be("24");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.StartDateEndDate);
        }

        [Fact]
        public void StartDateCantBeBeforeClosingDate()
        {
            var vacancy = new Vacancy
            {
                StartDate = DateTime.UtcNow,
                ClosingDate = DateTime.UtcNow.AddDays(1)
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.StartDateEndDate);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be(string.Empty);
            result.Errors[0].ErrorCode.Should().Be("24");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.StartDateEndDate);
        }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/TestData.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application
{
    public static class TestData
    {
        public static IEnumerable<object[]> BlacklistedCharacters => "<>|^".ToCharArray().Select(c => new object[] { c });
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ContactDetail.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class ContactDetail
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
    }
}

<|start_filename|>src/Employer/Employer.Web/Extensions/ClaimsPrincipalExtensions.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Employer.Web.Configuration;
using System.Security.Claims;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Newtonsoft.Json;

namespace Esfa.Recruit.Employer.Web.Extensions
{
    public static class ClaimsPrincipalExtensions
    {
        public static string GetDisplayName(this ClaimsPrincipal user)
        {
            return user.FindFirstValue(EmployerRecruitClaims.IdamsUserDisplayNameClaimTypeIdentifier);
        }

        public static string GetEmailAddress(this ClaimsPrincipal user)
        {
            return user.FindFirstValue(EmployerRecruitClaims.IdamsUserEmailClaimTypeIdentifier);
        }

        public static string GetUserId(this ClaimsPrincipal user)
        {
            return user.FindFirstValue(EmployerRecruitClaims.IdamsUserIdClaimTypeIdentifier);
        }

        public static IEnumerable<string> GetEmployerAccounts(this ClaimsPrincipal user)
        {
            var employerAccountClaim = user.FindFirst(c => c.Type.Equals(EmployerRecruitClaims.AccountsClaimsTypeIdentifier));

            if (string.IsNullOrEmpty(employerAccountClaim?.Value))
                return Enumerable.Empty<string>();
            
            var employerAccounts = JsonConvert.DeserializeObject<List<string>>(employerAccountClaim.Value);
            return employerAccounts;
        }

        public static VacancyUser ToVacancyUser(this ClaimsPrincipal user)
        {
            return new VacancyUser
            {
                UserId = user.GetUserId(),
                Name = user.GetDisplayName(),
                Email = user.GetEmailAddress()
            };
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/Part2/ApplicationProcess/ApplicationProcessViewModel.cs<|end_filename|>
﻿using System.Collections.Generic;
using Esfa.Recruit.Shared.Web.ViewModels;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class ApplicationProcessViewModel
    {
        public string Title { get; internal set; }
        public ApplicationMethod? ApplicationMethod { get; internal set; }
        public string ApplicationInstructions { get; internal set; }
        public string ApplicationUrl { get; internal set; }

        public bool HasEmptyApplicationMethod => !ApplicationMethod.HasValue;
        public ReviewSummaryViewModel Review { get; set; } = new ReviewSummaryViewModel();

        public IList<string> OrderedFieldNames => new List<string>
        {
            nameof(ApplicationProcessEditModel.ApplicationMethod),
            nameof(ApplicationProcessEditModel.ApplicationUrl),
            nameof(ApplicationProcessEditModel.ApplicationInstructions)
        };

        public string FindAnApprenticeshipUrl { get; internal set; }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/RuleTemplates/ProfanityRuleMessageTemplate.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.RuleTemplates
{
    public static class ProfanityRuleMessageTemplate
    {
        public static string ToText(ProfanityData msgData, string fieldName)
        {
            var baseMessage = $"{fieldName} contains the phrase '{msgData.Profanity}'";
            return msgData.Occurrences > 1 
                ? $"{baseMessage} {msgData.Occurrences} times"
                : $"{baseMessage}";
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/Skills/SkillsEditModelBase.cs<|end_filename|>
﻿using System.Collections.Generic;

namespace Esfa.Recruit.Shared.Web.ViewModels.Skills
{
    public class SkillsEditModelBase
    {
        public List<string> Skills { get; set; }
        public string AddCustomSkillAction { get; set; }
        public string AddCustomSkillName { get; set; }

        public bool IsAddingCustomSkill => !string.IsNullOrEmpty(AddCustomSkillAction);
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/Fluent/CustomValidators/PostCodeValidator.cs<|end_filename|>
using System;
using System.Text.RegularExpressions;
using FluentValidation.Validators;

namespace Esfa.Recruit.Vacancies.Client.Application.Validation.Fluent.CustomValidators
{
    internal class PostCodeValidator : PropertyValidator, IRegularExpressionValidator 
    {
		private readonly Regex _regex;

        internal PostCodeValidator() : base("{PropertyName} must be a valid postcode format")
        {
			_regex = CreateRegEx();
		}

		protected override bool IsValid(PropertyValidatorContext context) {
			if (context.PropertyValue == null) return true;

			if (!_regex.IsMatch((string)context.PropertyValue)) {
				return false;
			}

			return true;
		}

		public string Expression => ValidationConstants.PostCodeRegExPattern;

		private static Regex CreateRegEx()
		{
			try
			{
				if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null) {
					return new Regex(ValidationConstants.PostCodeRegExPattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(2.0));
				}
			}
            catch
            {
            }

            return new Regex(ValidationConstants.PostCodeRegExPattern, RegexOptions.IgnoreCase);
		}
	}
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyManage/EditVacancyViewModel.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Extensions;

namespace Esfa.Recruit.Employer.Web.ViewModels.VacancyManage
{
    public class EditVacancyViewModel : LiveVacancyViewModel
    {
        public string DisplayableClosingDate => ProposedClosingDate.HasValue ? ProposedClosingDate.Value.AsGdsDate() : ClosingDate;

        public string DisplayableStartDate => ProposedStartDate.HasValue ? ProposedStartDate.Value.AsGdsDate() : PossibleStartDate;

        public string ProposedClosingDateFormValue => ProposedClosingDate?.ToString("dd/MM/yyyy");
        public string ProposedStartDateFormValue => ProposedStartDate?.ToString("dd/MM/yyyy");

        public DateTime? ProposedClosingDate { get; internal set; }
        public DateTime? ProposedStartDate { get; internal set; }

        public bool ClosingDateChanged => ProposedClosingDate.HasValue;
        public bool StartDateChanged => ProposedStartDate.HasValue;

        public bool HasChanges => ClosingDateChanged || StartDateChanged;
        public bool IsUnchanged => !HasChanges;
    }
}

<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyPreview/SubmitEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.RouteModel;

namespace Esfa.Recruit.Employer.Web.ViewModels.Preview
{
    public class SubmitEditModel : VacancyRouteModel
    {
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/archived/2018-10-31_040-employerProfile_Create.js<|end_filename|>
{
    function getEmployerAccountIds(userInfos) {
        var lookup = [];
        print("Building lookup");
        for (let index = 0; index < userInfos.length; index++) {
            const element = userInfos[index];
            const employerAccountId = element._id.split("_")[1];
            lookup.push({ employerAccountId: employerAccountId,  legalEntities: element.legalEntities });
        }

        return lookup;
    }

    function getLatestAbout(legalEntityId) {

        var filter = { "legalEntityId": legalEntityId, "employerDescription": { $exists: true }};
        var result  = db.vacancies.find(filter).sort( { "lastUpdatedDate": -1 } ).limit(1);
        var vacancy = result[0];
        
        return vacancy ? vacancy.employerDescription : null;
    }

    function getLatestEmployerWebsiteUrl(legalEntityId) {
        var filter = { "legalEntityId": legalEntityId, "employerWebsiteUrl": { $exists: true }};
        var result  = db.vacancies.find(filter).sort( { "lastUpdatedDate": -1 } ).limit(1);
        var vacancy = result[0];
        
        return vacancy ? vacancy.employerWebsiteUrl : null;
    }

    print();
    print();
    print("Start creating Employer Profiles");
    print();

    let allEmployerProfilesCount = db.employerProfiles.count();

    if (allEmployerProfilesCount == 0) {
        let editUserInfos = db.queryStore.find({ "viewType": "EditVacancyInfo" }).toArray();
        print(`Found ${editUserInfos.length} EditInfo record/s`);

        var accountIds = getEmployerAccountIds(editUserInfos);

        for (let index = 0; index < accountIds.length; index++) {
            const profilesToInsert = [];
            const element = accountIds[index];

            for (let entityIndex = 0; entityIndex < element.legalEntities.length; entityIndex++) {
                const entity = element.legalEntities[entityIndex]; 

                var employerProfile = {};

                employerProfile._id = element.employerAccountId + "_" + entity.legalEntityId;
                employerProfile.employerAccountId = element.employerAccountId;
                employerProfile.legalEntityId = entity.legalEntityId;
                employerProfile.createdDate = new Date();

                const latestAbout = getLatestAbout(entity.legalEntityId);
                const latestWebsiteUrl = getLatestEmployerWebsiteUrl(entity.legalEntityId);

                if (latestAbout)
                    employerProfile.aboutOrganisation = latestAbout;
                
                if (latestWebsiteUrl)
                    employerProfile.organisationWebsiteUrl = latestWebsiteUrl;

                profilesToInsert.push(employerProfile);
            }

            var result = db.employerProfiles.insertMany(profilesToInsert);

            if (result.acknowledged)
                print(`Inserted ${result.insertedIds.length} profiles for employer ${element.employerAccountId}`);
            else
                print(`Error inserting profiles for employer ${element.employerAccountId}`);
        }

    }
    else {
        print("Employer Profiles already exist. No need to run this script");
    }

    print();
    print("Inserted Employer Profiles");
    print();
    print();
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/EventStore/EventItem.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.EventStore
{
    public class EventItem
    {
        public string EventType { get; set; }
        public string Data { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Extensions/StringExtensions.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.Extensions
{
    public static class StringExtensions
    {
        public static string FormatForParsing(this string value)
        {
            if (string.IsNullOrWhiteSpace(value)) return string.Empty;

            var rgx = new Regex("[^a-zA-Z0-9]");
            var sanitized = rgx.Replace(value, " ");

            sanitized = RemoveContiguousWhitespace(sanitized);

            return $" {sanitized} ";
        }

        public static string FormatForComparison(this string value, string[] stopWords = null)
        {
            if (string.IsNullOrWhiteSpace(value)) return string.Empty;

            var formatted = $" {value}"
                .ToLower()
                .Replace("\r", " ")
                .Replace("\n", " ")
                .Replace("\t", " ");

            var builder = new StringBuilder(formatted);

            foreach (var stopWord in stopWords ?? Enumerable.Empty<string>())
                builder.Replace($" {stopWord} ", " ");

            formatted = builder.ToString();

            formatted = formatted.FormatForParsing();

            return $" {formatted} ".Trim().ToLower();
        }

        public static string ToDelimitedString<T>(this IEnumerable<T> items, string delimiter)
        {
            return items == null ? string.Empty : string.Join(delimiter, items);
        }
        
        private static string RemoveContiguousWhitespace(string value)
        {
            return Regex.Replace(value, @"\s+", " ");
        }

        public static int CountOccurrences(this string body, string term)
        {
            var count = 0;
            var offset = 0;

            if (string.IsNullOrWhiteSpace(body) || string.IsNullOrWhiteSpace(term)) return count;

            var paddedTerm = $" {term} ";
            var checkBody = $" {body} ";

            while ((offset = checkBody.IndexOf(paddedTerm, offset, StringComparison.InvariantCultureIgnoreCase)) != -1)
            {
                offset += term.Length;
                count++;
            }

            return count;
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/Exceptions/InvalidRouteForVacancyException.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Vacancies.Client.Domain.Exceptions;

namespace Esfa.Recruit.Employer.Web.Exceptions
{
    [Serializable]
    public class InvalidRouteForVacancyException : RecruitException
    {
        public InvalidRouteForVacancyException(string message, string routeNameToRedirectTo, VacancyRouteModel routeValues) : base(message)
        {
            RouteNameToRedirectTo = routeNameToRedirectTo;
            RouteValues = routeValues;
        }

        public string RouteNameToRedirectTo { get; }

        public VacancyRouteModel RouteValues { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Providers/IProfanityListProvider.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Providers
{
    public interface IProfanityListProvider
    {
        Task<IEnumerable<string>> GetProfanityListAsync();
    }
}


<|start_filename|>src/QA/QA.Web/ViewModels/FieldIdentifierViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Qa.Web.ViewModels
{
    public class FieldIdentifierViewModel
    {
        public string FieldIdentifier { get; set; }
        public bool FieldValueHasChanged { get; set; }
        public bool Checked { get; set; }
        public string Text { get; set; }
    }
}


<|start_filename|>src/QA/QA.Web/Security/AuthorizationPolicyNames.cs<|end_filename|>
namespace Esfa.Recruit.Qa.Web.Security
{
    internal class AuthorizationPolicyNames
    {
        internal const string QaUserPolicyName = "QaUserPolicyName";
        internal const string TeamLeadUserPolicyName = "TeamLeadUserPolicyName";
    }
}

<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/ProviderContactDetails/ProviderContactDetailsEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Provider.Web.RouteModel;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.ProviderContactDetails
{
    public class ProviderContactDetailsEditModel : VacancyRouteModel
    {
        public string ProviderContactName { get; set; }
        public string ProviderContactEmail { get; set; }
        public string ProviderContactPhone { get; set; }
    }
}


<|start_filename|>src/Employer/Employer.Web/Models/SubmitVacancyResponse.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.Models
{
    public class SubmitVacancyResponse
    {
        public bool HasLegalEntityAgreement { get; set; }
        public bool IsSubmitted { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/VacancyApprovedEvent.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces;
using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using MediatR;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events
{
    public class VacancyApprovedEvent : EventBase, INotification, IVacancyEvent
    {
        public Guid VacancyId { get; set; }
        public long VacancyReference { get; set; }
    }
}


<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/SystemErrorCode.cs<|end_filename|>
﻿namespace Esfa.Recruit.Console.RecruitSeedDataWriter
{
    /// <summary>
    /// Taken from https://msdn.microsoft.com/en-us/library/ms681382.aspx
    /// </summary>
    internal enum SystemErrorCode
    {
        ERROR_FILE_NOT_FOUND = 2,
        ERROR_BAD_FORMAT = 11,
        ERROR_INVALID_PARAMETER = 87
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/Services/ReviewSummaryService.cs<|end_filename|>
﻿using System;
using System.Linq;
using System.Threading.Tasks;
using Esfa.Recruit.Shared.Web.Mappers;
using Esfa.Recruit.Shared.Web.ViewModels;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Client;

namespace Esfa.Recruit.Shared.Web.Services
{
    public class ReviewSummaryService : IReviewSummaryService
    {
        private readonly IRecruitVacancyClient _vacancyClient;
        private readonly ReviewFieldIndicatorMapper _fieldMappingsLookup;
        private readonly IQaVacancyClient _qaVacancyClient;

        public ReviewSummaryService(IRecruitVacancyClient vacancyClient, 
            ReviewFieldIndicatorMapper fieldMappingsLookup, IQaVacancyClient qaVacancyClient)
        {
            _vacancyClient = vacancyClient;
            _fieldMappingsLookup = fieldMappingsLookup;
            _qaVacancyClient = qaVacancyClient;
        }

        public async Task<ReviewSummaryViewModel> GetReviewSummaryViewModelAsync(long vacancyReference, 
            ReviewFieldMappingLookupsForPage reviewFieldIndicatorsForPage)
        {
            var review = await _vacancyClient.GetCurrentReferredVacancyReviewAsync(vacancyReference);

            return ConvertToReviewSummaryViewModel(reviewFieldIndicatorsForPage, review);
        }

        public async Task<ReviewSummaryViewModel> GetReviewSummaryViewModelAsync(Guid reviewId,
            ReviewFieldMappingLookupsForPage reviewFieldIndicatorsForPage)
        {
            var review = await _qaVacancyClient.GetVacancyReviewAsync(reviewId);

            return ConvertToReviewSummaryViewModel(reviewFieldIndicatorsForPage, review);
        }

        private ReviewSummaryViewModel ConvertToReviewSummaryViewModel(
            ReviewFieldMappingLookupsForPage reviewFieldIndicatorsForPage, VacancyReview review)
        {
            if (review != null)
            {
                var fieldIndicators =
                    _fieldMappingsLookup.MapFromFieldIndicators(reviewFieldIndicatorsForPage, review).ToList();

                return new ReviewSummaryViewModel
                {
                    HasBeenReviewed = true,
                    ReviewerComments = review.ManualQaComment,
                    FieldIndicators = fieldIndicators
                };
            }

            return new ReviewSummaryViewModel();
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/VacancyComparer/IVacancyComparerService.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Services.VacancyComparer
{
    public interface IVacancyComparerService
    {
        VacancyComparerResult Compare(Vacancy a, Vacancy b);
    }
}


<|start_filename|>src/QA/QA.Web/Security/StaffIdamsClaims.cs<|end_filename|>
namespace Esfa.Recruit.Qa.Web.Security
{
    public class StaffIdamsClaims
    {
        public static string IdamsUserIdClaimTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn";
        public static string IdamsUserNameClaimTypeIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Engine/IRule.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.Engine
{
    public interface IRule<in TSubject>
    {
        Task<RuleOutcome> EvaluateAsync(TSubject subject);
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/QueryStore/Projections/Vacancy/LiveVacancy.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.Vacancy
{
    public class LiveVacancy : VacancyProjectionBase
    {
        public LiveVacancy() : base(QueryViewType.LiveVacancy.TypeName)
        {
        }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/WeeklyHoursValidationTests.cs<|end_filename|>
using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class WeeklyHoursValidationTests : VacancyValidationTestsBase
    {
        [Fact]
        public void NoErrorsWhenWeeklyHoursIsValid()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WeeklyHours = 30
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.WeeklyHours);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void WeeklyHoursMustHaveAValue()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WeeklyHours = null
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.WeeklyHours);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.WeeklyHours)}");
            result.Errors[0].ErrorCode.Should().Be("40");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.WeeklyHours);
        }

        [Fact]
        public void WeeklyHoursMustBeMoreThan16()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WeeklyHours = 15
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.WeeklyHours);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.WeeklyHours)}");
            result.Errors[0].ErrorCode.Should().Be("42");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.WeeklyHours);
        }

        [Fact]
        public void WeeklyHoursMustBeLeeThan48()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    WeeklyHours = 49
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.WeeklyHours);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.WeeklyHours)}");
            result.Errors[0].ErrorCode.Should().Be("43");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.WeeklyHours);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/RuleSetOutcome.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;

namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class RuleSetOutcome
    {
        public List<RuleOutcome> RuleOutcomes { get; set; } = new List<RuleOutcome>();
        public RuleSetDecision Decision { get; set; }
        public int TotalScore => RuleOutcomes.Sum(o => o.Score);
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/Fluent/CustomValidators/HtmlValidator.cs<|end_filename|>
﻿using System;
using System.Text.RegularExpressions;
using Esfa.Recruit.Vacancies.Client.Application.Services;
using FluentValidation.Validators;

namespace Esfa.Recruit.Vacancies.Client.Application.Validation.Fluent.CustomValidators
{
    internal class HtmlValidator : PropertyValidator
    {
        public const string HtmlRegularExpression = @"^[a-zA-Z0-9\u0080-\uFFA7?$@#()""'!,+\-=_:;.&€£*%\s\/<>\[\]]+$";
        private readonly IHtmlSanitizerService _sanitizer;
        private readonly Regex _regex;

        internal HtmlValidator(IHtmlSanitizerService sanitizer) : base("{PropertyName} must contain allowed HTML")
        {
            _regex = CreateRegEx();
            _sanitizer = sanitizer;
        }

        protected override bool IsValid(PropertyValidatorContext context)
        {
            var value = (string) context.PropertyValue;

            var isHtmlValid = _sanitizer.IsValid(value);

            if (isHtmlValid == false)
                return false;

            if (string.IsNullOrWhiteSpace(value) == false && 
                _regex.IsMatch(value) == false)
            {
                return false;
            }

            return true;
        }

        private static Regex CreateRegEx()
        {
            try
            {
                if (AppDomain.CurrentDomain.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") == null)
                {
                    return new Regex(HtmlRegularExpression, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(2.0));
                }
            }
            catch (Exception)
            {
            }

            return new Regex(HtmlRegularExpression, RegexOptions.IgnoreCase);
        }
    }
}


<|start_filename|>src/QA/QA.Web/Configuration/ExternalLinksConfiguration.cs<|end_filename|>
namespace Esfa.Recruit.Qa.Web.Configuration
{
    public sealed class ExternalLinksConfiguration
    {
        public string StaffIdamsUrl { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/ApprenticeshipProgrammes/ApprenticeshipProgrammes.cs<|end_filename|>
using System;
using System.Collections.Generic;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData.ApprenticeshipProgrammes
{
    public class ApprenticeshipProgrammes : IReferenceDataItem
    {
        public string Id { get; set; }
        public DateTime LastUpdatedDate { get; set; } 
        public List<ApprenticeshipProgramme> Data { get; set; }
    }
}

<|start_filename|>src/Jobs/Recruit.Vacancies.Jobs/CustomQueueProcessorFactory.cs<|end_filename|>
using Microsoft.Azure.WebJobs.Host.Queues;

namespace Esfa.Recruit.Vacancies.Jobs
{
    public class CustomQueueProcessorFactory : IQueueProcessorFactory
    {
        public QueueProcessor Create(QueueProcessorFactoryContext context)
        {
            context.Queue.CreateIfNotExistsAsync().Wait();

            // return the default processor
            return new QueueProcessor(context);
        }
    }
}


<|start_filename|>src/Shared/UnitTests/Shared.Web/Orchestrators/SkillsOrchestratorHelperTests.cs<|end_filename|>
﻿using System.Collections.Generic;
using Esfa.Recruit.Shared.Web.ViewModels.Skills;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;
using System.Linq;
using Esfa.Recruit.Shared.Web.Orchestrators;

namespace Esfa.Recruit.Vacancies.Client.UnitTests.Shared.Web.Orchestrators
{
    public class SkillsOrchestratorHelperTests
    {
        
        public SkillsOrchestratorHelperTests()
        {}

        [Fact]
        public void SetViewModelSkillsFromDraftSkills_ShouldOrderVacancySkills()
        {
            var helper = new SkillsOrchestratorHelper(GetBaseSkills);

            var vm = new SkillsViewModelBase();
            var draftSkills = new List<string>
            {
                "1-Custom Skill 2",
                "Initiative",
                "Problem solving skills",
                "2-Custom Skill 1",
                "Administrative skills",
                "Communication skills"
            };

            helper.SetViewModelSkillsFromDraftSkills(vm, draftSkills);

            vm.Column1Checkboxes.Count.Should().Be(10);
            vm.Column1Checkboxes.Count(c => c.Selected == false).Should().Be(6);

            vm.Column2Checkboxes.Count.Should().Be(9);
            vm.Column2Checkboxes.Count(c => c.Selected == false).Should().Be(7);
            
            vm.Column1Checkboxes[0].Value.Should().Be("Communication skills");
            vm.Column1Checkboxes[0].Selected.Should().BeTrue();

            vm.Column1Checkboxes[5].Value.Should().Be("Problem solving skills");
            vm.Column1Checkboxes[5].Selected.Should().BeTrue();

            vm.Column1Checkboxes[7].Value.Should().Be("Administrative skills");
            vm.Column1Checkboxes[7].Selected.Should().BeTrue();

            vm.Column1Checkboxes[9].Value.Should().Be("2-Custom Skill 1");
            vm.Column1Checkboxes[9].Selected.Should().BeTrue();

            vm.Column2Checkboxes[4].Value.Should().Be("Initiative");
            vm.Column2Checkboxes[4].Selected.Should().BeTrue();

            vm.Column2Checkboxes[8].Value.Should().Be("1-Custom Skill 2");
            vm.Column2Checkboxes[8].Selected.Should().BeTrue();
        }

        [Fact]
        public void SetViewModelSkillsFromVacancy_ShouldOrderVacancySkills()
        {
            var helper = new SkillsOrchestratorHelper(GetBaseSkills);

            var vm = new SkillsViewModelBase();
            var vacancy = new Vacancy
            {
                Skills = new List<string>
                {
                    "Custom Skill 2",
                    "Initiative",
                    "Problem solving skills",
                    "Custom Skill 1",
                    "Administrative skills",
                    "Communication skills"
                }
            };
            helper.SetViewModelSkillsFromVacancy(vm, vacancy);

            vm.Column1Checkboxes.Count.Should().Be(10);
            vm.Column1Checkboxes.Count(c => c.Selected == false).Should().Be(6);

            vm.Column2Checkboxes.Count.Should().Be(9);
            vm.Column2Checkboxes.Count(c => c.Selected == false).Should().Be(7);

            vm.Column1Checkboxes[0].Value.Should().Be("Communication skills");
            vm.Column1Checkboxes[0].Selected.Should().BeTrue();

            vm.Column1Checkboxes[5].Value.Should().Be("Problem solving skills");
            vm.Column1Checkboxes[5].Selected.Should().BeTrue();

            vm.Column1Checkboxes[7].Value.Should().Be("Administrative skills");
            vm.Column1Checkboxes[7].Selected.Should().BeTrue();

            vm.Column1Checkboxes[9].Value.Should().Be("2-Custom Skill 1");
            vm.Column1Checkboxes[9].Selected.Should().BeTrue();

            vm.Column2Checkboxes[4].Value.Should().Be("Initiative");
            vm.Column2Checkboxes[4].Selected.Should().BeTrue();

            vm.Column2Checkboxes[8].Value.Should().Be("1-Custom Skill 2");
            vm.Column2Checkboxes[8].Selected.Should().BeTrue();
        }

        [Fact]
        public void SetVacancyFromEditModel_ShouldOrderVacancySkills()
        {
            var helper = new SkillsOrchestratorHelper(GetBaseSkills);

            var vacancy = new Vacancy
            {
                Skills = new List<string>
                {
                    "Team worker"
                }
            };

            var m = new SkillsEditModelBase
            {
                Skills = new List<string>
                {
                    "1-Custom Skill 2",
                    "Initiative",
                    "Problem solving skills",
                    "2-Custom Skill 1",
                    "Administrative skills",
                    "Communication skills"
                },
                AddCustomSkillAction = "add custom skill",
                AddCustomSkillName = "Added Custom Skill"
            };
        
            helper.SetVacancyFromEditModel(vacancy, m);

            m.Skills.Count.Should().Be(7);
            m.Skills[0].Should().Be("Initiative");
            m.Skills[1].Should().Be("Problem solving skills");
            m.Skills[2].Should().Be("Administrative skills");
            m.Skills[3].Should().Be("Communication skills");
            m.Skills[4].Should().Be("1-Custom Skill 2");
            m.Skills[5].Should().Be("2-Custom Skill 1");
            m.Skills[6].Should().Be("3-Added Custom Skill");

            vacancy.Skills.Count.Should().Be(7);
            vacancy.Skills[0].Should().Be("Initiative");
            vacancy.Skills[1].Should().Be("Problem solving skills");
            vacancy.Skills[2].Should().Be("Administrative skills");
            vacancy.Skills[3].Should().Be("Communication skills");
            vacancy.Skills[4].Should().Be("Custom Skill 2");
            vacancy.Skills[5].Should().Be("Custom Skill 1");
            vacancy.Skills[6].Should().Be("Added Custom Skill");
        }

        private static List<string> GetBaseSkills()
        {
            return new List<string>
            {
                "Communication skills",
                "IT skills",
                "Attention to detail",
                "Organisation skills",
                "Customer care skills",
                "Problem solving skills",
                "Presentation skills",
                "Administrative skills",
                "Number skills",
                "Analytical skills",
                "Logical",
                "Team working",
                "Creative",
                "Initiative",
                "Non judgemental",
                "Patience",
                "Physical fitness"
            };
        }
    }
}

<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyView/ReferredVacancyViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class ReferredVacancyViewModel : DisplayVacancyViewModel
    {
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyView/SubmittedVacancyViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class SubmittedVacancyViewModel : DisplayVacancyViewModel
    {
        public string SubmittedDate { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/NextVacancyReview/NextVacancyReviewService.cs<|end_filename|>
﻿using System;
using System.Linq;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Domain.Repositories;
using Microsoft.Extensions.Options;

namespace Esfa.Recruit.Vacancies.Client.Application.Services.NextVacancyReview
{
    public class NextVacancyReviewService : INextVacancyReviewService
    {
        private readonly ITimeProvider _timeProvider;
        private readonly IVacancyReviewQuery _vacancyReviewQuery;
        private readonly NextVacancyReviewServiceConfiguration _config;

        public NextVacancyReviewService(IOptions<NextVacancyReviewServiceConfiguration> config, ITimeProvider timeProvider, IVacancyReviewQuery vacancyReviewQuery)
        {
            _timeProvider = timeProvider;   
            _vacancyReviewQuery = vacancyReviewQuery;
            _config = config.Value;
        }

        public async Task<VacancyReview> GetNextVacancyReviewAsync(string userId)
        {
            var assignationExpiryTime = _timeProvider.Now.AddMinutes(_config.VacancyReviewAssignationTimeoutMinutes * -1);

            var assignedReviews = await _vacancyReviewQuery.GetByStatusAsync(ReviewStatus.UnderReview);

            //Get the oldest unexpired review assigned to the user
            var nextVacancyReview = assignedReviews.Where(r => 
                    r.ReviewedByUser.UserId == userId
                    && r.ReviewedDate >= assignationExpiryTime)
                .OrderBy(r => r.CreatedDate)
                .FirstOrDefault();

            if (nextVacancyReview != null)
                return nextVacancyReview;

            //Get the oldest expired assigned review
            nextVacancyReview = assignedReviews.Where(r =>
                    r.ReviewedDate < assignationExpiryTime)
                .OrderBy(r => r.CreatedDate)
                .FirstOrDefault();

            if (nextVacancyReview != null)
                return nextVacancyReview;

            //Get the oldest unassigned review
            var pendingReviews = await _vacancyReviewQuery.GetByStatusAsync(ReviewStatus.PendingReview);
            nextVacancyReview = pendingReviews
                .OrderBy(r => r.CreatedDate)
                .FirstOrDefault();

            return nextVacancyReview;
        }

        public DateTime GetExpiredAssignationDateTime()
        {
            return _timeProvider.Now.AddMinutes(_config.VacancyReviewAssignationTimeoutMinutes * -1);
        }

        public bool VacancyReviewCanBeAssigned(ReviewStatus reviewStatus, DateTime? reviewedDate)
        {
            if (reviewStatus == ReviewStatus.PendingReview)
                return true;

            return reviewedDate < GetExpiredAssignationDateTime();
        }
    }
}


<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/Skills/SkillsViewModel.cs<|end_filename|>
﻿using System.Collections.Generic;
using Esfa.Recruit.Shared.Web.ViewModels;
using Esfa.Recruit.Shared.Web.ViewModels.Skills;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.Skills
{
    public class SkillsViewModel : SkillsViewModelBase
    {
        public string Title { get; set; }
        public string AddCustomSkillName { get; set; }
        public List<string> Skills { get; set; }
        public ReviewSummaryViewModel Review { get; set; } = new ReviewSummaryViewModel();
    }
}


<|start_filename|>src/QA/QA.Web/Controllers/ReviewController.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Esfa.Recruit.Qa.Web.Configuration.Routing;
using Esfa.Recruit.Qa.Web.Extensions;
using Esfa.Recruit.Qa.Web.Orchestrators;
using Esfa.Recruit.Qa.Web.Security;
using Esfa.Recruit.Qa.Web.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Qa.Web.Controllers
{
    [Route(RoutePaths.VacancyReviewsRoutePath)]
    public class ReviewController : Controller
    {
        private readonly ReviewOrchestrator _orchestrator;

        public ReviewController(ReviewOrchestrator orchestrator)
        {
            _orchestrator = orchestrator;
        }

        [HttpGet(Name = RouteNames.Vacancy_Review_Get)]
        public async Task<IActionResult> Review([FromRoute] Guid reviewId) 
        {
            var vm = await _orchestrator.GetReviewViewModelAsync(reviewId, User.GetVacancyUser());

            return View(vm);
        }

        [HttpPost(Name = RouteNames.Vacancy_Review_Post)]
        public async Task<IActionResult> Submit(ReviewEditModel model) 
        {
            if (ModelState.IsValid == false)
            {
                var vm = await _orchestrator.GetReviewViewModelAsync(model, User.GetVacancyUser());
                return View("Review", vm);
            }

            var nextVacancyReviewId = await _orchestrator.SubmitReviewAsync(model, User.GetVacancyUser());

            if (nextVacancyReviewId == null)
                return RedirectToRoute(RouteNames.Dashboard_Index_Get);

            return RedirectToRoute(RouteNames.Vacancy_Review_Get, new { reviewId = nextVacancyReviewId });
        }

        [Authorize(Policy = AuthorizationPolicyNames.TeamLeadUserPolicyName)]
        [HttpGet("unassign", Name = RouteNames.Vacancy_Review_Unassign_Get)]
        public async Task<IActionResult> UnassignReview([FromRoute] Guid reviewId)
        {
            var unassignReviewVM = await _orchestrator.GetUnassignReviewViewModelAsync(reviewId);
            return View(unassignReviewVM);
        }

        [Authorize(Policy = AuthorizationPolicyNames.TeamLeadUserPolicyName)]
        [HttpPost("unassign", Name = RouteNames.Vacancy_Review_Unassign_Post)]
        public async Task<IActionResult> UnassignReview(UnassignReviewEditModel model)
        {
            var unassignReviewVM = await _orchestrator.GetUnassignReviewViewModelAsync(model.ReviewId);
            if (!ModelState.IsValid)
                return View(unassignReviewVM);
            if (model.ConfirmUnassign.GetValueOrDefault())
                await _orchestrator.UnassignVacancyReviewAsync(model.ReviewId);
            return RedirectToRoute(RouteNames.Dashboard_Index_Get);
        }

        [HttpGet("readonly", Name = RouteNames.Vacancy_Readonly_Review_Get)]
        public async Task<IActionResult> ReadonlyReview([FromRoute] Guid reviewId)
        {
            var vm = await _orchestrator.GetReadonlyReviewViewModelAsync(reviewId);

            return View(vm);
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/RouteModel/ApplicationReviewRouteModel.cs<|end_filename|>
﻿using System;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Employer.Web.RouteModel
{
    public class ApplicationReviewRouteModel : VacancyRouteModel
    {
        [FromRoute]
        public Guid ApplicationReviewId { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/IGetTitlePopularity.cs<|end_filename|>
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Services
{
    public interface IGetTitlePopularity
    {
        Task<int> GetTitlePopularityAsync(string larsCode, string title);
    }
}

<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/WriteOperationResult.cs<|end_filename|>
﻿namespace Esfa.Recruit.Console.RecruitSeedDataWriter
{
    internal enum WriteOperationResult
    {
        Inserted,
        Replaced,
        Skipped
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ApplicationReviewStatus.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum ApplicationReviewStatus
    {
        New,
        Successful,
        Unsuccessful
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/BaseRules/BaseBannedPhraseChecksRule.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Esfa.QA.Core.Extensions;
using Esfa.Recruit.Vacancies.Client.Application.Rules.Engine;
using Esfa.Recruit.Vacancies.Client.Application.Rules.Extensions;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Newtonsoft.Json;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.BaseRules
{
    public class BaseBannedPhraseChecksRule : Rule
    {
        private readonly ConsolidationOption _consolidationOption;

        protected IEnumerable<string> BannedPhrases { get; set; } = new List<string>();

        public BaseBannedPhraseChecksRule(
            RuleId ruleId, ConsolidationOption consolidationOption, decimal weighting = 1.0m) 
            : base(ruleId, weighting)
        {
            _consolidationOption = consolidationOption;
        }

        protected IEnumerable<RuleOutcome> BannedPhraseCheck(Expression<Func<string>> property, string relatedFieldId = null)
        {
            var fieldId = relatedFieldId ?? property.GetQualifiedFieldId();

            var foundBannedPhrases = FindOccurrences(property);

            if (foundBannedPhrases.Values.Sum() > 0)
                switch (_consolidationOption)
                {
                    case ConsolidationOption.NoConsolidation:
                        return CreateUnconsolidatedOutcomes(foundBannedPhrases, fieldId);

                    case ConsolidationOption.ConsolidateByField:
                        return CreateConsolidatedOutcomes(foundBannedPhrases, fieldId);

                    default:
                        throw new ArgumentOutOfRangeException();
                }

            return new[] { CreateOutcome(0, $"No banned phrases found in '{fieldId}'", null, fieldId) };
        }

        private Dictionary<string, int> FindOccurrences(Expression<Func<string>> property)
        {
            var value = property.Compile()();
            var checkValue = value.FormatForParsing();

            var foundBannedPhrases = new Dictionary<string, int>();

            foreach (var bannedPhrase in BannedPhrases)
            {
                var occurrences = checkValue.CountOccurrences(bannedPhrase);

                if (occurrences > 0)
                {
                    foundBannedPhrases.Add(bannedPhrase, occurrences);
                }
            }

            return foundBannedPhrases;
        }

        private IEnumerable<RuleOutcome> CreateUnconsolidatedOutcomes(Dictionary<string, int> foundBannedPhrases, string fieldId)
        {
            return foundBannedPhrases
                .Select(foundBannedPhrase =>
                {
                    var count = foundBannedPhrase.Value;
                    var term = foundBannedPhrase.Key;
                    var foundMessage = count > 1 ? $"found {count} times" : "found";
                    var narrative = $"Banned phrase '{term}' {foundMessage} in '{fieldId}'";

                    var data = JsonConvert.SerializeObject(new BannedPhrasesData { BannedPhrase = term, Occurrences = count });
                    return CreateOutcome(count, narrative, data, fieldId);
                });
        }

        private IEnumerable<RuleOutcome> CreateConsolidatedOutcomes(Dictionary<string, int> foundBannedPhrases, string fieldId)
        {
            var count = foundBannedPhrases.Values.Sum();
            var terms = string.Join(",", foundBannedPhrases.Keys);
            var narrative = $"{count} banned phrases '{terms}' found in '{fieldId}'";
            var data = JsonConvert.SerializeObject(new BannedPhrasesData { BannedPhrase = terms, Occurrences = count });

            return new[]
            {
                CreateOutcome(count, narrative, data, fieldId)
            };
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Providers/IApprenticeshipProgrammeProvider.cs<|end_filename|>
using System.Collections.Generic;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Providers
{
    public interface IApprenticeshipProgrammeProvider
    {
        Task<IEnumerable<IApprenticeshipProgramme>> GetApprenticeshipProgrammesAsync(bool includeExpired = false);

        Task<IApprenticeshipProgramme> GetApprenticeshipProgrammeAsync(string programmeId);
    }
}

<|start_filename|>src/Employer/Employer.Web/Controllers/EditVacancyDatesController.cs<|end_filename|>
using System.Threading.Tasks;
using Esfa.Recruit.Employer.Web.Configuration.Routing;
using Esfa.Recruit.Employer.Web.Orchestrators;
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Employer.Web.ViewModels.EditVacancyDates;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Hosting;
using System;
using Esfa.Recruit.Employer.Web.Extensions;
using Esfa.Recruit.Shared.Web.Extensions;

namespace Esfa.Recruit.Employer.Web.Controllers
{
    [Route(RoutePaths.AccountVacancyRoutePath)]
    public class EditVacancyDatesController : Controller
    {
        private readonly EditVacancyDatesOrchestrator _orchestrator;
        private readonly IHostingEnvironment _hostingEnvironment;

        public EditVacancyDatesController(EditVacancyDatesOrchestrator orchestrator, IHostingEnvironment hostingEnvironment)
        {
            _orchestrator = orchestrator;
            _hostingEnvironment = hostingEnvironment;
        }

        [HttpGet("edit-dates", Name = RouteNames.VacancyEditDates_Get)]
        public async Task<IActionResult> EditVacancyDates(VacancyRouteModel vrm)
        {
            var proposedClosingDate = Request.Cookies.GetProposedClosingDate(vrm.VacancyId);
            var proposedStartDate = Request.Cookies.GetProposedStartDate(vrm.VacancyId);

            var response = await _orchestrator.GetEditVacancyDatesViewModelAsync(vrm, proposedClosingDate, proposedStartDate);

            if (!response.Success)
            {
                response.AddErrorsToModelState(ModelState);
            }

            return View(response.Data);
        }

        [HttpPost("edit-dates", Name = RouteNames.VacancyEditDates_Post)]
        public async Task<IActionResult> EditVacancyDates(EditVacancyDatesEditModel m)
        {
            var response = await _orchestrator.PostEditVacancyDatesEditModelAsync(m);

            if (!response.Success)
            {
                response.AddErrorsToModelState(ModelState);
            }

            if (!ModelState.IsValid)
            {
                var vm = await _orchestrator.GetEditVacancyDatesViewModelAsync(m);
                return View(vm);
            }

            Response.Cookies.SetProposedClosingDate(_hostingEnvironment, m.VacancyId, DateTime.Parse(m.ClosingDate));
            Response.Cookies.SetProposedStartDate(_hostingEnvironment, m.VacancyId, DateTime.Parse(m.StartDate));

            return RedirectToRoute(RouteNames.VacancyEdit_Get);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/WageType.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum WageType
    {
        FixedWage,
        NationalMinimumWageForApprentices,
        NationalMinimumWage,        
        Unspecified
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Providers/IGetVacancyTitlesProvider.cs<|end_filename|>
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Providers
{
    public interface IGetVacancyTitlesProvider
    {
        Task<IList<string>> GetVacancyTitlesAsync(string larsCode);
    }
}

<|start_filename|>src/Shared/UnitTests/Shared.Web/Extensions/StringExtensionsTests.cs<|end_filename|>
﻿using Xunit;
using Esfa.Recruit.Shared.Web.Extensions;
using FluentAssertions;

namespace Esfa.Recruit.Shared.Web.UnitTests.Extensions
{
    public class StringExtensionsTests
    {
        [Theory]
        [InlineData("SW1A 2AA", "SW1A 2AA")]
        [InlineData(" sw 1a2aa ", "SW1A 2AA" )]
        [InlineData(null, null)]
        public void AsPostcode_ShouldFormatText(string actualPostcode, string expectedPostcode)
        {
            actualPostcode.AsPostcode().Should().Be(expectedPostcode);
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/Projections/IEmployerDashboardProjectionService.cs<|end_filename|>
﻿using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.Projections
{
    public interface IEmployerDashboardProjectionService
    {
        Task ReBuildAllDashboardsAsync();
        Task ReBuildDashboardAsync(string employerAccountId);
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Engine/Rule.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.Engine
{
    public abstract class Rule
    {
        private readonly decimal _weighting;

        protected Rule(RuleId ruleId, decimal weighting = 1.0m)
        {
            RuleId = ruleId;
            _weighting = Math.Min(weighting, 100);
        }

        public RuleId RuleId { get; }

        protected RuleOutcome CreateOutcome(int score, string narrative, string data, string target = RuleOutcome.NoSpecificTarget)
        {
            return new RuleOutcome(RuleId, (int) (score * _weighting), narrative, target, null, data);
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/Geocode/Geocode.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.Geocode
{
    public class Geocode
    {
        public GeoCodeMethod GeoCodeMethod { get; set; }
        public double Latitude { get; set; }
        public double Longitude { get; set; }

        public override string ToString()
        {
            return $"{{\"Latitude\":{Latitude}, \"Longitude\":{Longitude}}}";
        }
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/ApplicationReviewTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation.Fluent;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation
{
    public class ApplicationReviewTests
    {
        public class ShouldRequireCandiateFeedbackIfUnsuccessfulTestData : TheoryData<string, string>
        {
            public ShouldRequireCandiateFeedbackIfUnsuccessfulTestData()
            {
                var candidateFeedbackTooLong =
                    new string('a', ApplicationReviewValidator.CandidateFeedbackMaxLength + 1);

                Add(null, ApplicationReviewValidator.CandidateFeedbackRequired);
                Add("", ApplicationReviewValidator.CandidateFeedbackRequired);
                Add(">", ApplicationReviewValidator.CandidateFeedbackFreeTextCharacters);
                Add(candidateFeedbackTooLong, string.Format(ApplicationReviewValidator.CandidateFeedbackLength, ApplicationReviewValidator.CandidateFeedbackMaxLength));
            }
        }

        [Theory]
        [ClassData(typeof(ShouldRequireCandiateFeedbackIfUnsuccessfulTestData))]
        public void ShouldRequireCandiateFeedbackIfUnsuccessful(string candidateFeedback, string expectedErrorMessage)
        {
            var m = new Recruit.Vacancies.Client.Domain.Entities.ApplicationReview
            {
                Status = ApplicationReviewStatus.Unsuccessful,
                CandidateFeedback = candidateFeedback
            };

            var validator = new ApplicationReviewValidator();

            var result = validator.Validate(m);

            result.IsValid.Should().BeFalse();
            result.Errors.Count.Should().Be(1);
            result.Errors[0].ErrorMessage.Should().Be(expectedErrorMessage);
        }

        [Fact]
        public void ShouldNotRequireCandiateFeedbackIfSuccessful()
        {
            var m = new Recruit.Vacancies.Client.Domain.Entities.ApplicationReview
            {
                Status = ApplicationReviewStatus.Successful,
                CandidateFeedback = "should not specify feedback if successful"
            };

            var validator = new ApplicationReviewValidator();

            var result = validator.Validate(m);

            result.IsValid.Should().BeFalse();
            result.Errors.Count.Should().Be(1);
            result.Errors[0].ErrorMessage.Should().Be(ApplicationReviewValidator.CandidateFeedbackNull);
        }

        [Theory]
        [InlineData(ApplicationReviewStatus.Successful, null)]
        [InlineData(ApplicationReviewStatus.Unsuccessful, "Some candidate feedback")]
        public void ApplicationReviewShouldBeValid(ApplicationReviewStatus status, string feedback)
        {
            var m = new Recruit.Vacancies.Client.Domain.Entities.ApplicationReview
            {
                Status = status,
                CandidateFeedback = feedback
            };

            var validator = new ApplicationReviewValidator();

            var result = validator.Validate(m);

            result.IsValid.Should().BeTrue();
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/Validations/TypeOfMoneyAttribute.cs<|end_filename|>
﻿namespace Esfa.Recruit.Shared.Web.ViewModels.Validations
{
    using Esfa.Recruit.Shared.Web.Extensions;    
    using System.ComponentModel.DataAnnotations;

    public class TypeOfMoneyAttribute : ValidationAttribute
    {
        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }

            return (((string)value).AsMoney() != null);            
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/Skills/SkillsViewModelBase.cs<|end_filename|>
﻿using System.Collections.Generic;

namespace Esfa.Recruit.Shared.Web.ViewModels.Skills
{
    public class SkillsViewModelBase
    {
        public List<SkillViewModel> Column1Checkboxes { get; set; }
        public List<SkillViewModel> Column2Checkboxes { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/Middleware/RecruitExceptionHandlerMiddleware.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Exceptions;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;

namespace Esfa.Recruit.Shared.Web.Middleware
{
    /// <summary>
    /// Custom Exception Handler Middleware. Handles RecruitException errors without logging an error and redirects to the exceptionHandlingPath
    /// </summary>
    public class RecruitExceptionHandlerMiddleware
    {
        //This class is modified version of: https://github.com/aspnet/AspNetCore/blob/0c9eda1f4a551df52f83c7a9b378b4ae8f02e0b5/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs

        private readonly RequestDelegate _next;
        private readonly string _exceptionHandlingPath;
        private readonly Func<object, Task> _clearCacheHeadersDelegate;

        public RecruitExceptionHandlerMiddleware(RequestDelegate next, string exceptionHandlingPath)
        {
            _next = next;
            _exceptionHandlingPath = exceptionHandlingPath;
            _clearCacheHeadersDelegate = ClearCacheHeaders;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await _next(context);
            }
            catch (RecruitException ex)
            {
                if (context.Response.HasStarted)
                {
                    throw;
                }

                PathString originalPath = context.Request.Path;
                
                context.Request.Path = _exceptionHandlingPath;
                
                try
                {
                    context.Response.Clear();
                    var exceptionHandlerFeature = new ExceptionHandlerFeature()
                    {
                        Error = ex,
                        Path = originalPath.Value,
                    };
                    context.Features.Set<IExceptionHandlerFeature>(exceptionHandlerFeature);
                    context.Features.Set<IExceptionHandlerPathFeature>(exceptionHandlerFeature);
                    context.Response.StatusCode = 500;
                    context.Response.OnStarting(_clearCacheHeadersDelegate, context.Response);

                    await _next(context);
                }
                finally
                {
                    context.Request.Path = originalPath;
                }
            }
        }

        private Task ClearCacheHeaders(object state)
        {
            var response = (HttpResponse)state;
            response.Headers[HeaderNames.CacheControl] = "no-cache";
            response.Headers[HeaderNames.Pragma] = "no-cache";
            response.Headers[HeaderNames.Expires] = "-1";
            response.Headers.Remove(HeaderNames.ETag);
            return Task.CompletedTask;
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/OwnerType.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum OwnerType
    {
        Employer,
        Provider,
        Joint
    }
}

<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/ApplicationProcess/ApplicationProcessEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Provider.Web.RouteModel;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.ApplicationProcess
{
    public class ApplicationProcessEditModel : VacancyRouteModel
    {
        public ApplicationMethod? ApplicationMethod { get; set; }
        public string ApplicationInstructions { get; set; }
        public string ApplicationUrl { get; set; }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/Part2/Considerations/ConsiderationsEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.RouteModel;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class ConsiderationsEditModel : VacancyRouteModel
    {
        public string ThingsToConsider { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/EntityValidationError.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Application.Validation
{
    public class EntityValidationError
    {
        public EntityValidationError(long ruleId, string propertyName, string errorMessage, string errorCode)
        {
            RuleId = ruleId;
            PropertyName = propertyName;
            ErrorMessage = errorMessage;
            ErrorCode = errorCode;
        }

        public long RuleId { get; set; }
        public string PropertyName { get; set; }
        public string ErrorMessage { get; set; }
        public string ErrorCode { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/Views/_ViewImports.cshtml<|end_filename|>
@using Esfa.Recruit.Shared.Web.ViewModels
@using Esfa.Recruit.Shared.Web.Views
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, Recruit.Shared.Web
@addTagHelper *, NWebsec.AspNetCore.Mvc.TagHelpers

<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyView/DisplayVacancyApplicationViewModel.cs<|end_filename|>
﻿using System.Linq;
using Esfa.Recruit.Employer.Web.ViewModels.VacancyManage;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public abstract class DisplayVacancyApplicationViewModel : DisplayVacancyViewModel
    {
        public VacancyApplicationsViewModel Applications { get; internal set; }
        
        public bool HasApplications => Applications.Applications.Any();
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/QualificationsTests.cs<|end_filename|>
﻿using System.Collections.Generic;
using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Moq;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class QualificationsTests : VacancyValidationTestsBase
    {
        public QualificationsTests()
        {
            MockQualificationsProvider.Setup(q => q.GetQualificationsAsync()).ReturnsAsync(new List<string>{"type"});
        }

        [Fact]
        public void NoErrorWhenQualificationsAreValid()
        {
            var vacancy = GetVacancy();
            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        public static IEnumerable<object[]> NullOrZeroQualificationCollection =>
            new List<object[]>
            {
                new object[] {null},
                new object[] {new List<Qualification>()},
            };

        [Theory]
        [MemberData(nameof(NullOrZeroQualificationCollection))]
        public void QualificationsMustNotBeNullOrZeroCount(List<Qualification> qualifications)
        {
            var vacancy = new Vacancy
            {
                Qualifications = qualifications
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            AssertSingleError(result, nameof(vacancy.Qualifications), "52");
        }

        [Theory]
        [InlineData(null)]
        [InlineData("")]
        public void QualificationTypeMustNotBeEmpty(string emptyString)
        {
            var vacancy = GetVacancy(qualificationType: emptyString);

            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            var propertyName = $"{nameof(vacancy.Qualifications)}[0].{nameof(Qualification.QualificationType)}";
            AssertSingleError(result, propertyName, "53");
        }

        [Theory]
        [InlineData(null)]
        [InlineData("")]
        public void SubjectMustNotBeEmpty(string emptyString)
        {
            var vacancy = GetVacancy(subject: emptyString);

            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            var propertyName = $"{nameof(vacancy.Qualifications)}[0].{nameof(Qualification.Subject)}";
            AssertSingleError(result, propertyName, "54");
        }

        [Fact]
        public void SubjectMustNotBeLongerThanMaxLength()
        {
            var vacancy = GetVacancy(subject: new string('a', 51));

            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            var propertyName = $"{nameof(vacancy.Qualifications)}[0].{nameof(Qualification.Subject)}";
            AssertSingleError(result, propertyName, "7");
        }

        [Fact]
        public void SubjectMustNotContainInvalidCharacters()
        {
            var vacancy = GetVacancy(subject: "<");

            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            var propertyName = $"{nameof(vacancy.Qualifications)}[0].{nameof(Qualification.Subject)}";
            AssertSingleError(result, propertyName, "6");
        }

        [Theory]
        [InlineData(null)]
        [InlineData("")]
        public void GradeMustNotBeEmpty(string emptyString)
        {
            var vacancy = GetVacancy(grade: emptyString);

            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            var propertyName = $"{nameof(vacancy.Qualifications)}[0].{nameof(Qualification.Grade)}";
            AssertSingleError(result, propertyName, "55");
        }

        [Fact]
        public void GradeMustNotBeLongerThanMaxLength()
        {
            var vacancy = GetVacancy(grade: new string('a', 31));

            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            var propertyName = $"{nameof(vacancy.Qualifications)}[0].{nameof(Qualification.Grade)}";
            AssertSingleError(result, propertyName, "7");
        }

        [Fact]
        public void GradeMustNotContainInvalidCharacters()
        {
            var vacancy = GetVacancy(grade: "<");
            
            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            var propertyName = $"{nameof(vacancy.Qualifications)}[0].{nameof(Qualification.Grade)}";
            AssertSingleError(result, propertyName, "6");
        }

        [Fact]
        public void WeightingMustNotBeNull()
        {
            var vacancy = GetVacancy(weighting: null);

            var result = Validator.Validate(vacancy, VacancyRuleSet.Qualifications);

            var propertyName = $"{nameof(vacancy.Qualifications)}[0].{nameof(Qualification.Weighting)}";
            AssertSingleError(result, propertyName, "56");
        }

        private void AssertSingleError(EntityValidationResult result, string propertyName, string errorCode)
        {
            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be(propertyName);
            result.Errors[0].ErrorCode.Should().Be(errorCode);
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Qualifications);
        }

        private Vacancy GetVacancy(string qualificationType = "type", string subject = "subject",
            string grade = "grade", QualificationWeighting? weighting = QualificationWeighting.Desired)
        {
            var qualification = new Qualification
            {
                QualificationType = qualificationType,
                Subject = subject,
                Grade = grade,
                Weighting = weighting
            };

            return new Vacancy
            {
                Qualifications = new List<Qualification> { qualification }
            };
        }
        
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/Skills/SkillViewModel.cs<|end_filename|>
﻿namespace Esfa.Recruit.Shared.Web.ViewModels.Skills
{
    public class SkillViewModel
    {
        public string Name { get; set; }
        public bool Selected { get; set; }
        public string Value { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/HtmlSanitizerService.cs<|end_filename|>
﻿using System;
using System.Diagnostics;
using AngleSharp.Extensions;
using Ganss.XSS;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Client.Application.Services
{
    public class HtmlSanitizerService : IHtmlSanitizerService
    {
        private readonly ILogger<HtmlSanitizerService> _logger;

        public HtmlSanitizerService(ILogger<HtmlSanitizerService> logger)
        {
            _logger = logger;
        }

        public string Sanitize(string html)
        {
            return string.IsNullOrWhiteSpace(html) ? 
                html : 
                Sanitize(html, null);
        }

        public bool IsValid(string html)
        {
            if (string.IsNullOrWhiteSpace(html))
                return true;

            var isValid = true;

            var sanitized = Sanitize(html, () => isValid = false);

            if (string.IsNullOrWhiteSpace(sanitized))
                return false;

            return isValid;
        }

        private string Sanitize(string html, Action removingHtmlCallback)
        {
            var watch = Stopwatch.StartNew();

            string sanitized;
            var sanitizer = GetSanitizer(removingHtmlCallback);
            using (var dom = sanitizer.SanitizeDom(html))
            {
                sanitized = string.IsNullOrWhiteSpace(dom.Body.InnerText) ? 
                    string.Empty :
                    dom.Body.ChildNodes.ToHtml(HtmlSanitizer.DefaultOutputFormatter);
            }
            watch.Stop();

            _logger.LogInformation("Sanitized html input in {timer}ms", watch.ElapsedMilliseconds);

            return sanitized;
        }

        private HtmlSanitizer GetSanitizer(Action removingHtmlCallback = null)
        {
            var sanitizer = new HtmlSanitizer();
            sanitizer.AllowedSchemes.Clear();
            sanitizer.AllowDataAttributes = false;
            sanitizer.AllowedAtRules.Clear();
            sanitizer.AllowedCssProperties.Clear();
            sanitizer.AllowedAttributes.Clear();

            sanitizer.AllowedTags.Clear();
            sanitizer.AllowedTags.Add("p");
            sanitizer.AllowedTags.Add("br");
            sanitizer.AllowedTags.Add("ul");
            sanitizer.AllowedTags.Add("li");

            if (removingHtmlCallback != null)
            {
                sanitizer.RemovingAtRule += (s, e) => removingHtmlCallback();
                sanitizer.RemovingAttribute += (s, e) => removingHtmlCallback();
                sanitizer.RemovingComment += (s, e) => removingHtmlCallback();
                sanitizer.RemovingCssClass += (s, e) => removingHtmlCallback();
                sanitizer.RemovingStyle += (s, e) => removingHtmlCallback();
                sanitizer.RemovingTag += (s, e) => removingHtmlCallback();
            }
            
            return sanitizer;
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/Configuration/EmployerRecruitClaims.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.Configuration
{
    public static class EmployerRecruitClaims
    {
        public static string IdamsUserIdClaimTypeIdentifier = "http://das/employer/identity/claims/id";
        public static string IdamsUserDisplayNameClaimTypeIdentifier = "http://das/employer/identity/claims/display_name";
        public static string IdamsUserEmailClaimTypeIdentifier = "http://das/employer/identity/claims/email_address";
        public static string AccountsClaimsTypeIdentifier = "http://das/employer/identity/claims/associatedAccounts";
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/Qualification.cs<|end_filename|>
﻿using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public class Qualification : IEquatable<Qualification>

    {
        public string QualificationType { get; set; }
        public string Subject { get; set; }
        public string Grade { get; set; }
        public QualificationWeighting? Weighting { get; set; }
        
        public bool Equals(Qualification other)
        {
            if (other == null)
                return false;

            return QualificationType.Equals(other.QualificationType) &&
                   Subject.Equals(other.Subject) &&
                   Grade.Equals(other.Grade) &&
                   Weighting.Equals(other.Weighting);
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Configuration/IConfigurationReader.cs<|end_filename|>
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Configuration
{
    public interface IConfigurationReader
    {
        Task<T> GetAsync<T>(string id) where T : class;
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/FieldIdResolver.cs<|end_filename|>
﻿using System;
using System.Linq.Expressions;
using Esfa.QA.Core.Extensions;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Services
{
    public static class FieldIdResolver
    {
        public static string ToFieldId<P>(Expression<Func<Vacancy, P>> fieldName)
        {
            return fieldName.GetQualifiedFieldId();
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/Validations/TypeOfDecimalAttribute.cs<|end_filename|>
﻿using Esfa.Recruit.Shared.Web.Extensions;

namespace Esfa.Recruit.Shared.Web.ViewModels.Validations
{
    using System.ComponentModel.DataAnnotations;

    public class TypeOfDecimalAttribute : ValidationAttribute
    {
        private readonly int _numberOfDecimalPlaces;

        public TypeOfDecimalAttribute(int numberOfDecimalPlaces)
        {
            _numberOfDecimalPlaces = numberOfDecimalPlaces;
        }

        public override bool IsValid(object value)
        {
            if (value == null)
            {
                return true;
            }

            return (((string)value).AsDecimal(_numberOfDecimalPlaces) != null);
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyManage/ProposedChangesEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Shared.Web.ViewModels.Validations;
using Microsoft.AspNetCore.Mvc;
using ErrMsg = Esfa.Recruit.Shared.Web.ViewModels.ValidationMessages.DateValidationMessages;

namespace Esfa.Recruit.Employer.Web.ViewModels.VacancyManage
{
    public class ProposedChangesEditModel : VacancyRouteModel
    {
        [TypeOfDate(ErrorMessage = ErrMsg.TypeOfDate.ClosingDate)]
        public string ProposedClosingDate { get; set; }

        [TypeOfDate(ErrorMessage = ErrMsg.TypeOfDate.StartDate)]
        public string ProposedStartDate { get; set; }
    }
}


<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/Skills/SkillsEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Shared.Web.ViewModels.Skills;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.Skills
{
    public class SkillsEditModel : SkillsEditModelBase {}
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/BannedPhrases/BannedPhrasesProvider.cs<|end_filename|>
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Cache;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData.BannedPhrases
{
    public class BannedPhrasesProvider : IBannedPhrasesProvider
    {
        private readonly ILogger<BannedPhrasesProvider> _logger;
        private readonly IReferenceDataReader _referenceDataReader;
        private readonly ICache _cache;
        private readonly ITimeProvider _timeProvider;

        public BannedPhrasesProvider(ILogger<BannedPhrasesProvider> logger, IReferenceDataReader referenceDataReader, ICache cache, ITimeProvider timeProvider)
        {
            _logger = logger;
            _referenceDataReader = referenceDataReader;
            _cache = cache;
            _timeProvider = timeProvider;
        }
        public async Task<IEnumerable<string>> GetBannedPhrasesAsync()
        {
            return await _cache.CacheAsideAsync(CacheKeys.BannedPhrases,
                _timeProvider.NextDay,
                async () =>
                {
                    _logger.LogInformation("Attempting to retrieve banned phrases list from reference data.");
                    var result = await _referenceDataReader.GetReferenceData<BannedPhraseList>();
                    if (result != null)
                        return result.BannedPhrases;
                    _logger.LogWarning("Unable to retrieve reference data for banned phrases list.");
                    return new List<string>();
                });
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/Projections/QaDashboardProjectionService.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Domain.Repositories;
using Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore;
using Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.QA;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.Projections
{
    public class QaDashboardProjectionService : IQaDashboardProjectionService
    {
        private readonly IVacancyReviewQuery _reviewQuery;
        private readonly IQueryStoreWriter _queryStoreWriter;
        private readonly ITimeProvider _timeProvider;

        public QaDashboardProjectionService(IVacancyReviewQuery reviewQuery, IQueryStoreWriter queryStoreWriter, ITimeProvider timeProvider)
        {
            _reviewQuery = reviewQuery;
            _queryStoreWriter = queryStoreWriter;
            _timeProvider = timeProvider;
        }

        public async Task RebuildQaDashboardAsync()
        {
            var activeReviews = await _reviewQuery.GetActiveAsync<VacancyReviewSummary>();

            activeReviews = activeReviews.OrderByDescending(x => x.CreatedDate).ToList();

            var qaDashboard = new QaDashboard
            {
                TotalVacanciesForReview = activeReviews.Count,
                TotalVacanciesResubmitted = GetTotalVacanciesResubmittedCount(activeReviews),
                TotalVacanciesBrokenSla = GetTotalVacanciesBrokenSla(activeReviews)
            };

            await _queryStoreWriter.UpdateQaDashboardAsync(qaDashboard);
        }

        private int GetTotalVacanciesResubmittedCount(IEnumerable<VacancyReviewSummary> activeReviews)
        {
            return activeReviews
                .Where(r => r.SubmissionCount > 1)
                .Select(r => r.VacancyReference)
                .Distinct()
                .Count();
        }

        private int GetTotalVacanciesBrokenSla(IEnumerable<VacancyReviewSummary> activeReviews)
        {
            return activeReviews.Count(r => r.SlaDeadline.HasValue && r.SlaDeadline.Value < _timeProvider.Now);
        }
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/archived/2018-08-03_030-queryViews_Copy_TypeFieldToViewTypeField.js<|end_filename|>
{
    print("Start copy of document field named 'type' to a new field 'viewType' in queryViews collection documents.");

    const query = {
            "viewType": { $exists: false }
        },
        batchUpdateLimit = 500;
    let passThrough = 1,
        maxLoops = Math.ceil(db.queryViews.find().count(query) / batchUpdateLimit);

    if (maxLoops === 0) {
        maxLoops = 1;
    }

    do {
        let matchedDocs = db.queryViews.aggregate([
            {
                $match: query
            },
            {
                $sort: { "lastUpdated": 1 }
            },
            {
                $limit: batchUpdateLimit
            }
        ]);

        print(`Found ${matchedDocs._batch.length} document(s) to operate on in pass-through ${passThrough} of ${maxLoops}.`);

        while (matchedDocs.hasNext()) {
            let doc = matchedDocs.next();

            let writeResult = db.queryViews.update({
                "_id": doc._id
            }, {
                $set: { "viewType": doc.type }
            });

            if (writeResult.hasWriteConcernError()) {
                printjson(writeResult.writeConcernError);
                quit(14);
            }

            print(`Updated document '${doc._id}', copied field 'type' to a new field 'viewType'.`);
        }

        passThrough++;
    }
    while (passThrough <= maxLoops && db.queryViews.find().count(query) > 0);

    print("Finished copy of document field named 'type' to a new field 'viewType' in queryViews collection documents.");
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Mongo/MongoDbNames.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Mongo
{
    internal static class MongoDbNames
    {
        internal const string RecruitDb = "recruit";
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Repositories/IApplicationReviewRepository.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Domain.Repositories
{
    public interface IApplicationReviewRepository
    {
        Task CreateAsync(ApplicationReview review);
        Task<ApplicationReview> GetAsync(Guid applicationReviewId);
        Task<ApplicationReview> GetAsync(long vacancyReference, Guid candidateId);
        Task UpdateAsync(ApplicationReview applicationReview);
        Task HardDelete(Guid applicationReviewId);
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/VacancyReviewApprovedEvent.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces;
using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using MediatR;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events
{
    public class VacancyReviewApprovedEvent : EventBase, INotification, IVacancyReviewEvent
    {
        public long VacancyReference { get; set; }

        public Guid ReviewId { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/FAA/FaaApplicationStatusSummary.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.FAA
{
    public class FaaApplicationStatusSummary
    {
        public FaaApplicationStatus ApplicationStatus { get; set; }
        public string UnsuccessfulReason { get; set; }
        public bool IsRecruitVacancy { get; set; }
        public int? VacancyReference { get; set; }
        public Guid? CandidateId { get; set; }
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/EmployerContactDetailContactPhoneNumberValidationTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public partial class EmployerContactDetailValidationTests : VacancyValidationTestsBase
    {
        [Theory]
        [InlineData(null)]
        [InlineData("")]
        [InlineData("02086695847")]
        public void NoErrorsWhenEmployerContactPhoneIsValid(string phoneNo)
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Phone = phoneNo }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void EmployerContactPhoneNumberMustBe16CharactersOrLess()
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Phone = "+4402086695847".PadRight(17, 'w') }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.EmployerContact)}.{nameof(vacancy.EmployerContact.Phone)}");
            result.Errors[0].ErrorCode.Should().Be("95");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerContactDetails);
        }

        [Fact]
        public void EmployerContactPhoneNumberMustBe8CharactersOrMore()
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Phone = "0208" }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.EmployerContact)}.{nameof(vacancy.EmployerContact.Phone)}");
            result.Errors[0].ErrorCode.Should().Be("96");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerContactDetails);
        }

        [Fact]
        public void EmployerContactPhoneNumberMustBeValidEmailFormat()
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Phone = "543121***" }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.EmployerContact)}.{nameof(vacancy.EmployerContact.Phone)}");
            result.Errors[0].ErrorCode.Should().Be("97");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerContactDetails);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/ISlaService.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Services
{
    public interface ISlaService
    {
        Task<DateTime> GetSlaDeadlineAsync(DateTime date);
    }
}


<|start_filename|>src/Provider/Provider.Web/Configuration/ContextItemKeys.cs<|end_filename|>
﻿namespace Esfa.Recruit.Provider.Web.Configuration
{
    public class ContextItemKeys
    {
        public const string ProviderIdentifier = "Provider";
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/Extensions/RuleOutcomeExtensions.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Linq;
using Esfa.Recruit.Vacancies.Client.Application.Rules.Engine;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Vacancies.Client.Application.Rules.Extensions
{
    public static class RuleOutcomeExtensions
    {
        public static RuleSetDecision CalculateOutcome(this List<RuleOutcome> ruleOutcomes, RuleSetOptions options)
        {
            var totalScore = ruleOutcomes.Sum(o => o.Score);

            if (totalScore > options.ReferralThreshold)
                return RuleSetDecision.Refer;
            if (totalScore < options.ApprovalThreshold)
                return RuleSetDecision.Approve;

            return RuleSetDecision.Indeterminate;
        }

    }
}


<|start_filename|>src/Provider/Provider.Web/ViewModels/Part2/VacancyDescription/VacancyDescriptionEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Provider.Web.RouteModel;

namespace Esfa.Recruit.Provider.Web.ViewModels.Part2.VacancyDescription
{
    public class VacancyDescriptionEditModel : VacancyRouteModel
    {
        public string VacancyDescription { get; set; }
        public string TrainingDescription { get; set; }
        public string OutcomeDescription { get; set; }
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/DurationValidationTests.cs<|end_filename|>
using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class DurationValidationTests : VacancyValidationTestsBase
    {
        [Fact]
        public void NoErrorsWhenDurationFieldsAreValid()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    DurationUnit = DurationUnit.Month,
                    Duration = 12
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Duration);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }


        [Fact]
        public void DurationUnitMustHaveAValue()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    DurationUnit = null,
                    Duration = 13
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Duration);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.DurationUnit)}");
            result.Errors[0].ErrorCode.Should().Be("34");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Duration);
        }

        [Fact]
        public void DurationUnitMustHaveAValidValue()
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    DurationUnit = (DurationUnit)1000,
                    Duration = 13
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Duration);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.DurationUnit)}");
            result.Errors[0].ErrorCode.Should().Be("34");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Duration);
        }

        [Fact]
        public void DurationMustHaveAValue()
        {
            var vacancy = new Vacancy 
            {
                Wage = new Wage
                {
                    DurationUnit = DurationUnit.Month,
                    Duration = null
                }
            };
            
            var result = Validator.Validate(vacancy, VacancyRuleSet.Duration);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.Duration)}");
            result.Errors[0].ErrorCode.Should().Be("34");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Duration);
        }

        [Theory]
        [InlineData(DurationUnit.Month, 11)]
        public void DurationMustBeAtLeast12Months(DurationUnit unitValue, int durationValue)
        {
            var vacancy = new Vacancy
            {
                Wage = new Wage
                {
                    DurationUnit = unitValue,
                    Duration = durationValue
                }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.Duration);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.Wage)}.{nameof(vacancy.Wage.Duration)}");
            result.Errors[0].ErrorCode.Should().Be("36");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.Duration);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Mongo/MongoDbConnectionDetails.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Mongo
{
    public class MongoDbConnectionDetails
    {
        public string ConnectionString { get; set; }
    }
}


<|start_filename|>src/Employer/Employer.Web/Controllers/Part2/ConsiderationsController.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.Configuration.Routing;
using Esfa.Recruit.Employer.Web.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Esfa.Recruit.Employer.Web.Extensions;
using Esfa.Recruit.Employer.Web.Orchestrators.Part2;
using Esfa.Recruit.Employer.Web.RouteModel;
using Esfa.Recruit.Shared.Web.Extensions;

namespace Esfa.Recruit.Employer.Web.Controllers.Part2
{
    [Route(RoutePaths.AccountVacancyRoutePath)]
    public class ConsiderationsController : Controller
    {
        private readonly ConsiderationsOrchestrator _orchestrator;

        public ConsiderationsController(ConsiderationsOrchestrator orchestrator)
        {
            _orchestrator = orchestrator;
        }

        [HttpGet("considerations", Name = RouteNames.Considerations_Get)]
        public async Task<IActionResult> Considerations(VacancyRouteModel vrm)
        {
            var vm = await _orchestrator.GetConsiderationsViewModelAsync(vrm);
            return View(vm);
        }

        [HttpPost("considerations", Name =  RouteNames.Considerations_Post)]
        public async Task<IActionResult> Considerations(ConsiderationsEditModel m)
        {
            var response = await _orchestrator.PostConsiderationsEditModelAsync(m, User.ToVacancyUser());

            if (!response.Success)
            {
                response.AddErrorsToModelState(ModelState);
            }

            if (!ModelState.IsValid)
            {
                var vm = await _orchestrator.GetConsiderationsViewModelAsync(m);

                return View(vm);
            }

            return RedirectToRoute(RouteNames.Vacancy_Preview_Get);
        }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/ClosingDateValidationTests.cs<|end_filename|>
using System;
using System.Collections.Generic;
using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class ClosingDateValidationTests : VacancyValidationTestsBase
    {
        public static IEnumerable<object[]> InvalidClosingDates =>
            new List<object[]>
            {
                new object[] { DateTime.UtcNow.Date },
                new object[] { DateTime.UtcNow },
                new object[] { DateTime.UtcNow.AddDays(-1) }
            };


        [Fact]
        public void NoErrorsWhenClosingDateIsValid()
        {
            var vacancy = new Vacancy
            {
                ClosingDate = DateTime.UtcNow.AddDays(5)
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ClosingDate);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void ClosingDateMustHaveAValue()
        {
            var vacancy = new Vacancy 
            {
                ClosingDate = null
            };
            
            var result = Validator.Validate(vacancy, VacancyRuleSet.ClosingDate);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.ClosingDate)}");
            result.Errors[0].ErrorCode.Should().Be("16");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.ClosingDate);
        }

        [Theory]
        [MemberData(nameof(InvalidClosingDates))]
        public void ClosingDateMustBeGreaterThanToday(DateTime closingDateValue)
        {
            var vacancy = new Vacancy
            {
                ClosingDate = closingDateValue
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ClosingDate);

            result.HasErrors.Should().BeTrue();
            result.Errors.Should().HaveCount(1);
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.ClosingDate)}");
            result.Errors[0].ErrorCode.Should().Be("18");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.ClosingDate);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/Orchestrators/OrchestratorResponse.cs<|end_filename|>
using Esfa.Recruit.Vacancies.Client.Application.Validation;

namespace Esfa.Recruit.Shared.Web.Orchestrators
{
    public class OrchestratorResponse
    {
        public OrchestratorResponse(bool isSuccessful)
        {
            Success = isSuccessful;
            Errors = new EntityValidationResult();
        }

        public OrchestratorResponse(EntityValidationResult errorResult) : this(false)
        {
            Errors = errorResult;
        }

        public bool Success { get; set; }
        public EntityValidationResult Errors { get; set; }
    }
}

<|start_filename|>src/Jobs/Recruit.Vacancies.Jobs/DomainEvents/Handlers/Vacancy/VacancySubmittedHandler.cs<|end_filename|>
using System;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Events;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Client;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Jobs.DomainEvents.Handlers.Vacancy
{
    public class VacancySubmittedHandler : DomainEventHandler, IDomainEventHandler<VacancySubmittedEvent>
    {
        private readonly ILogger<VacancySubmittedHandler> _logger;
        private readonly IJobsVacancyClient _client;

        public VacancySubmittedHandler(ILogger<VacancySubmittedHandler> logger, IJobsVacancyClient client) : base(logger)
        {
            _logger = logger;
            _client = client;
        }

        public async Task HandleAsync(string eventPayload)
        {
            var @event = DeserializeEvent<VacancySubmittedEvent>(eventPayload);
            
            try
            {
                _logger.LogInformation($"Processing {nameof(VacancySubmittedEvent)} for vacancy: {{VacancyId}}", @event.VacancyId);
                
                await _client.CreateVacancyReview(@event.VacancyReference);
                
                _logger.LogInformation($"Finished Processing {nameof(VacancySubmittedEvent)} for vacancy: {{VacancyId}}", @event.VacancyId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unable to process {eventBody}", @event);
                throw;
            }
        }
    }
}



<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Providers/IBannedPhrasesProvider.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Providers
{
    public interface IBannedPhrasesProvider
    {
        Task<IEnumerable<string>> GetBannedPhrasesAsync();
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/QualificationWeighting.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum QualificationWeighting
    {
        Essential,
        Desired
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/ReviewStatus.cs<|end_filename|>
namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum ReviewStatus : byte
    {
        New,
        PendingReview,
        UnderReview,
        Closed
    }
}

<|start_filename|>src/Shared/Recruit.Shared.Web/Extensions/EntityValidationErrorExtensions.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Validation;

namespace Esfa.Recruit.Shared.Web.Extensions
{
    public static class EntityValidationErrorExtensions
    {
        private const char PositionStart = '[';
        private const char PositionEnd = ']';

        public static int? GetIndexPosition(this EntityValidationError error)
        {
            var start = error.PropertyName.IndexOf(PositionStart) + 1;

            if (start == 0)
            {
                return null;
            }

            var end = error.PropertyName.IndexOf(PositionEnd, start);

            if (end == -1)
            {
                return null;
            }

            if (int.TryParse(error.PropertyName.Substring(start, end - start), out var position))
            {
                return position;
            }

            return null;
        }
    }
}



<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Cache/Cache.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;

namespace Esfa.Recruit.Vacancies.Client.Application.Cache
{
    public class Cache : ICache
    {
        private readonly IMemoryCache _memoryCache;

        public Cache(IMemoryCache memoryCache)
        {
            _memoryCache = memoryCache;
        }

        public Task<T> CacheAsideAsync<T>(string key, DateTime absoluteExpiration, Func<Task<T>> objectToCache)
        {
            return _memoryCache.GetOrCreateAsync(key, entry =>
            {
                entry.AbsoluteExpiration = absoluteExpiration;

                return objectToCache();
            });
        }
    }
}


<|start_filename|>src/Provider/Provider.Web/RouteModel/VacancyRouteModel.cs<|end_filename|>
﻿using System;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Provider.Web.RouteModel
{
    public class VacancyRouteModel
    {
        [FromRoute]
        public long Ukprn { get; set; }

        [FromRoute]
        public Guid? VacancyId { get; set; }
    }
}

<|start_filename|>src/QA/QA.Web/Security/UserAuthorizationService.cs<|end_filename|>
﻿using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;

namespace Esfa.Recruit.Qa.Web.Security
{
    public class UserAuthorizationService 
    {
        private readonly IAuthorizationService _authorizationService;

        public UserAuthorizationService(IAuthorizationService authorizationService)
        {
            _authorizationService = authorizationService;
        }

        public async Task<bool> IsTeamLeadAsync(ClaimsPrincipal user)
        {
            var authResult =
                await _authorizationService.AuthorizeAsync(user, AuthorizationPolicyNames.TeamLeadUserPolicyName);

            return authResult.Succeeded;
        }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Infrastructure/Providers/NationalMinimumWageProviderTests.cs<|end_filename|>
using System;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Services;
using Esfa.Recruit.UnitTests.TestHelpers;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Infrastructure.Services
{
    public class NationalMinimumWageProviderTests
    {
        private readonly NationalMinimumWageProvider _provider;
        private readonly Mock<ILogger<NationalMinimumWageProvider>> _mockLogger;
        
        public NationalMinimumWageProviderTests()
        {
            _mockLogger = new Mock<ILogger<NationalMinimumWageProvider>>();
            _provider = new NationalMinimumWageProvider(_mockLogger.Object);
        }

        [Fact]
        public void ShouldPickCorrectWageRangeBasedOnDate()
        {
            var testDate = new DateTime(2017, 5, 22);
            var wagePeriod = _provider.GetWagePeriod(testDate);

            wagePeriod.ApprenticeshipMinimumWage.Should().Be(3.50m);
            wagePeriod.NationalMinimumWageLowerBound.Should().Be(4.05m);
            wagePeriod.NationalMinimumWageUpperBound.Should().Be(7.50m);
        }
        
        [Fact]
        public void StartDateShouldBeInclusive()
        {
            var testDate = new DateTime(2018, 4, 1);
            var wagePeriod = _provider.GetWagePeriod(testDate);

            wagePeriod.ApprenticeshipMinimumWage.Should().Be(3.70m);
            wagePeriod.NationalMinimumWageLowerBound.Should().Be(4.20m);
            wagePeriod.NationalMinimumWageUpperBound.Should().Be(7.83m);
        }
        
        [Fact]
        public void EndDateShouldBeInclusive()
        {
            var testDate = new DateTime(2018, 3, 31);
            var wagePeriod = _provider.GetWagePeriod(testDate);

            wagePeriod.ApprenticeshipMinimumWage.Should().Be(3.50m);
            wagePeriod.NationalMinimumWageLowerBound.Should().Be(4.05m);
            wagePeriod.NationalMinimumWageUpperBound.Should().Be(7.50m);
        }
        
        [Fact]
        public void ShouldIgnoreTimePartOfDateTime()
        {
            var testDate = new DateTime(2018, 3, 31, 13, 22, 11);
            var wagePeriod = _provider.GetWagePeriod(testDate);

            wagePeriod.ApprenticeshipMinimumWage.Should().Be(3.5m);
            wagePeriod.NationalMinimumWageLowerBound.Should().Be(4.05m);
            wagePeriod.NationalMinimumWageUpperBound.Should().Be(7.5m);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/ReferenceData/Profanities/ProfanityList.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.ReferenceData.Profanities
{
    public class ProfanityList : IReferenceDataItem
    {
        public string Id { get; set; }
        public DateTime LastUpdatedDate { get; set; }

        public List<string> Profanities { get; set; }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/Part2/Skills/SkillsEditModel.cs<|end_filename|>
﻿using Esfa.Recruit.Shared.Web.ViewModels.Skills;

namespace Esfa.Recruit.Employer.Web.ViewModels.Part2.Skills
{
    public class SkillsEditModel : SkillsEditModelBase{}
}


<|start_filename|>src/Data/Recruit.ChangeScripts/080-vacancyReviews_RenameModify_VacancySnapshotContactFields_AsSubfieldsUnderEmployerContact.js<|end_filename|>
{
    // https://stackoverflow.com/a/44564089/5596802
    function toGUID(hex) {
        let a = hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2);
        let b = hex.substr(10, 2) + hex.substr(8, 2);
        let c = hex.substr(14, 2) + hex.substr(12, 2);
        let d = hex.substr(16, 16);
        hex = a + b + c + d;
        let uuid = hex.substr(0, 8) + "-" + hex.substr(8, 4) + "-" + hex.substr(12, 4) + "-" + hex.substr(16, 4) + "-" + hex.substr(20, 12);
        return uuid;
    }

    print("Start adding/updating vacancyReviews collection and changing any employer contact field to fall under an employerContact field.");

    const query = {
            "vacancySnapshot.ownerType": "Employer",
            $or: [
                { "vacancySnapshot.employerContactName": { $exists: true } },
                { "vacancySnapshot.employerContactEmail": { $exists: true } },
                { "vacancySnapshot.employerContactPhone": { $exists: true } }
            ]
        },
        batchUpdateLimit = 5;
    let passThrough = 1,
        maxLoops = Math.ceil(db.reviews.count(query) / batchUpdateLimit);

    if (maxLoops === 0) {
        maxLoops = 1;
    }

    do {
        let matchedDocs = db.reviews.aggregate([
            {
                $match: query
            },
            {
                $sort: { "dateCreated": 1 }
            },
            {
                $limit: batchUpdateLimit
            },
            {
                $project: {
                    "vacancySnapshot.employerContactName": 1,
                    "vacancySnapshot.employerContactEmail": 1,
                    "vacancySnapshot.employerContactPhone": 1
                }
            }
        ]);

        print(`Found ${matchedDocs._batch.length} document(s) to operate on in pass-through ${passThrough} of ${maxLoops}.`);

        while (matchedDocs.hasNext()) {
            let doc = matchedDocs.next();

            let writeResult = db.reviews.update({
                "_id": doc._id
            }, {
                $rename: {
                    "vacancySnapshot.employerContactName": "vacancySnapshot.employerContact.Name",
                    "vacancySnapshot.employerContactEmail": "vacancySnapshot.employerContact.Email",
                    "vacancySnapshot.employerContactPhone": "vacancySnapshot.employerContact.Phone"
                }
            }, {
                upsert: false
            });

            if (writeResult.hasWriteConcernError()) {
                printjson(writeResult.writeConcernError);
                quit(14);
            }

            print(`Updated document '${toGUID(doc._id.hex())}' employer contact field(s) to fall under an employerContact field.`);
        }

        passThrough++;
    } while (passThrough <= maxLoops && db.reviews.count(query) > 0);

    print("Finished adding/updating vacancyReviews collection changing any employer contact field to fall under an employerContact field.");
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Exceptions/InfrastructureException.cs<|end_filename|>
using System;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Exceptions
{
    [Serializable]
    public class InfrastructureException : Exception
    {
        public InfrastructureException(string message) : base(message) { }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/SetupProviderEvent.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces;
using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using MediatR;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events
{
    public class SetupProviderEvent : EventBase, INotification, IProviderEvent
    {
        public long Ukprn { get; private set; }

        public SetupProviderEvent(long ukprn)
        {
            Ukprn = ukprn;
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Services/ReferenceData/IBankHolidayUpdateService.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Services.ReferenceData
{
    public interface IBankHolidayUpdateService
    {
        Task UpdateBankHolidaysAsync();
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/LegalEntityAgreement/LegalEntityAgreementSoftStopViewModel.cs<|end_filename|>
﻿using Esfa.Recruit.Employer.Web.ViewModels.Part1;
using Esfa.Recruit.Shared.Web.ViewModels;

namespace Esfa.Recruit.Employer.Web.ViewModels.LegalEntityAgreement
{
    public class LegalEntityAgreementSoftStopViewModel
    {
        public bool HasLegalEntityAgreement { get; set; }
        public string LegalEntityName { get; set; }

        public PartOnePageInfoViewModel PageInfo { get; set; }
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/CloseVacancy/CloseViewModel.cs<|end_filename|>
using Esfa.Recruit.Employer.Web.RouteModel;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class CloseViewModel : VacancyRouteModel
    {
        public string Title { get; internal set; }
        public string VacancyReference { get; internal set; }
        public bool? ConfirmClose { get; set; }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/Services/NextVacancyReviewServiceTests.cs<|end_filename|>
﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Esfa.Recruit.Vacancies.Client.Application.Services.NextVacancyReview;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.Vacancies.Client.Domain.Repositories;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.Services
{
    public class NextVacancyReviewServiceTests
    {
        [Theory]
        [InlineData("2018-08-20T17:00:00Z", "user A", "92a9b68b-4555-425d-92a1-5d8ea625cf5b")]
        [InlineData("2018-08-20T17:00:01Z", "user A", "7a636aad-a3a5-4293-bae6-5c5a8b199265")]
        [InlineData("2018-08-20T18:00:00Z", "user A", "c7267018-c54d-4aa4-a59d-bf4a46fa24be")]
        [InlineData("2018-08-20T16:00:00Z", "user C", "8353063d-bc67-4588-83d9-2281080f8112")]
        public void GetNextVacancyReviewAsync_ShouldReturnNextReview(string utcTimeNow, string userId, string expectedReviewId)
        {
            var timeProvider = new Mock<ITimeProvider>();
            timeProvider.Setup(t => t.Now).Returns(DateTime.Parse(utcTimeNow));
            var vacancyReviewQuery = new Mock<IVacancyReviewQuery>();
            vacancyReviewQuery.Setup(r => r.GetByStatusAsync(ReviewStatus.UnderReview)).Returns(
                Task.FromResult(new List<VacancyReview>
                {
                    CreateVacancyReview("cd87db96-fe65-4d1b-9070-e50fb74a647a", "user A", ReviewStatus.UnderReview, "2018-08-20T13:59:59Z", "2018-08-19T18:00:00Z"),
                    CreateVacancyReview("92a9b68b-4555-425d-92a1-5d8ea625cf5b", "user A", ReviewStatus.UnderReview, "2018-08-20T14:00:00Z", "2018-08-19T18:32:00Z"),
                    CreateVacancyReview("7a636aad-a3a5-4293-bae6-5c5a8b199265", "user A", ReviewStatus.UnderReview, "2018-08-20T14:00:01Z", "2018-08-19T18:45:00Z"),

                    CreateVacancyReview("c7267018-c54d-4aa4-a59d-bf4a46fa24be", "user B", ReviewStatus.UnderReview, "2018-08-20T13:59:59Z", "2018-08-18T12:00:00Z"),
                    CreateVacancyReview("46cf4c11-6129-4570-805a-b18e939f7175", "user B", ReviewStatus.UnderReview, "2018-08-20T13:59:59Z", "2018-08-18T12:00:01Z")
                }));

            vacancyReviewQuery.Setup(r => r.GetByStatusAsync(ReviewStatus.PendingReview)).Returns(
                Task.FromResult(new List<VacancyReview>
                {
                    CreateVacancyReview("956a5556-0703-4aa2-bee9-9035a7e07516", null, ReviewStatus.PendingReview, null, "2018-08-20T18:00:01Z"),
                    CreateVacancyReview("8353063d-bc67-4588-83d9-2281080f8112", null, ReviewStatus.PendingReview, null, "2018-08-20T18:00:00Z")
                }));

            var config = Options.Create(new NextVacancyReviewServiceConfiguration
            {
                VacancyReviewAssignationTimeoutMinutes = 180
            });
            
            var sut = new NextVacancyReviewService(config, timeProvider.Object, vacancyReviewQuery.Object);

            var nextVacancyReview = sut.GetNextVacancyReviewAsync(userId).Result;

            Assert.Equal(Guid.Parse(expectedReviewId), nextVacancyReview.Id);
        }

        private static VacancyReview CreateVacancyReview(string id, string userId, ReviewStatus status, string reviewedDate, string createdDate)
        {
            return new VacancyReview
            {
                Id = Guid.Parse(id),
                ReviewedDate = reviewedDate != null ? DateTime.Parse(reviewedDate) : (DateTime?)null,
                CreatedDate = DateTime.Parse(createdDate),
                Status = status,
                ReviewedByUser = userId != null ? new VacancyUser {UserId = userId} : null
            };
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/DisabilityConfident.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum DisabilityConfident
    {
        No = 0,
        Yes
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/Services/HtmlSanitizerServiceTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Services;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.Services
{
    public class HtmlSanitizerServiceTests
    {
        [Theory]
        [InlineData("<ul><li>item1</li><li>item2</li></ul>", "<ul><li>item1</li><li>item2</li></ul>")]
        [InlineData("<UL><LI>item1</LI><LI>item2</LI></UL>", "<ul><li>item1</li><li>item2</li></ul>")]
        [InlineData("<p>paragraphs are not removed</p>", "<p>paragraphs are not removed</p>")]
        [InlineData("br elements are <br> not removed", "br elements are <br> not removed")]
        [InlineData("Windows style carriage\r\nreturns are changed", "Windows style carriage\nreturns are changed")]
        [InlineData("<div>any other elements are not allowed</div>", "")]
        [InlineData("<ul><li onclick=\"alert('not allowed')\">item1</li><li>item2</li></ul>", "<ul><li>item1</li><li>item2</li></ul>")]
        [InlineData("<ul><li style=\"display: none\">item1</li><li>item2</li></ul>", "<ul><li>item1</li><li>item2</li></ul>")]
        [InlineData("<script>alert('not allowed')</script>", "")]
        [InlineData("<style>body { display:none}</style>", "")]
        public void Sanitize_ShouldSanitizeHtml(string unsanitized, string sanitized)
        {
            var htmlSanitizer = new HtmlSanitizerService(new Mock<ILogger<HtmlSanitizerService>>().Object);

            var actual = htmlSanitizer.Sanitize(unsanitized);

            Assert.Equal(sanitized, actual);
        }

        [Theory]
        [InlineData("<ul><li>item1</li><li>item2</li></ul>", true)]
        [InlineData("<UL><LI>item1</LI><LI>item2</LI></UL>", true)]
        [InlineData("<p>paragraphs are not removed</p>", true)]
        [InlineData("br elements are <br> not removed", true)]
        [InlineData("Windows style carriage\r\nreturns are ok", true)]
        [InlineData("", true)]
        [InlineData(null, true)]
        [InlineData("<div>any other elements are not allowed</div>", false)]
        [InlineData("<ul><li onclick=\"alert('not allowed')\">item1</li><li>item2</li></ul>", false)]
        [InlineData("<ul><li style=\"display: none\">item1</li><li>item2</li></ul>", false)]
        [InlineData("<script>alert('not allowed')</script>", false)]
        [InlineData("<style>body { display:none}</style>", false)]
        [InlineData("<p>\r\n&nbsp;<br>&nbsp;<p><p></p>", false)]
        public void IsValid_ShouldValidateHtml(string value, bool isValid)
        {
            var htmlSanitizer = new HtmlSanitizerService(new Mock<ILogger<HtmlSanitizerService>>().Object);

            var actual = htmlSanitizer.IsValid(value);

            Assert.Equal(isValid, actual);
        }
    }
}


<|start_filename|>src/Employer/Employer.Web/Views/_ViewStart.cshtml<|end_filename|>
﻿@{
    Layout = LayoutNames.DefaultLayout;
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/ApplicationSubmittedEvent.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using MediatR;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events
{
    // Note: Doesn't implement IApplicationReviewEvent as it's an externally published event.
    public class ApplicationSubmittedEvent : EventBase, INotification
    {
        public Entities.Application Application { get; set; }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Rules/BaseRules/ConsolidationOption.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Application.Rules.BaseRules
{
    public enum ConsolidationOption
    {
        NoConsolidation,
        ConsolidateByField
    }   
}


<|start_filename|>src/Jobs/Recruit.Vacancies.Jobs/DomainEvents/Handlers/VacancyReview/VacancyReviewCreatedHandler.cs<|end_filename|>
﻿using System;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Events;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Client;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Jobs.DomainEvents.Handlers.VacancyReview
{
    public class VacancyReviewCreatedHandler : DomainEventHandler, IDomainEventHandler<VacancyReviewCreatedEvent>
    {
        private readonly ILogger<VacancyReviewCreatedHandler> _logger;
        private readonly IJobsVacancyClient _client;

        public VacancyReviewCreatedHandler(ILogger<VacancyReviewCreatedHandler> logger, IJobsVacancyClient client) 
            : base(logger)
        {
            _logger = logger;
            _client = client;
        }

        public async Task HandleAsync(string eventPayload)
        {
            var @event = DeserializeEvent<VacancyReviewCreatedEvent>(eventPayload);

            try
            {
                _logger.LogInformation($"Begin processing rules for vacancy {@event.VacancyReference} on review {@event.ReviewId}");
                await _client.PerformRulesCheckAsync(@event.ReviewId);
                _logger.LogInformation($"Finished processing rules for vacancy {@event.VacancyReference} on review {@event.ReviewId}");
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"Error occurred trying to execute rules on vacancy {@event.VacancyReference} on review {@event.ReviewId}");
                Console.WriteLine(e);
                throw;
            }
        }
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/EmployerContactDetailContactNameValidationTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public partial class EmployerContactDetailValidationTests : VacancyValidationTestsBase
    {
        [Theory]
        [InlineData(null)]
        [InlineData("")]
        [InlineData("contact name")]
        public void NoErrorsWhenEmployerContactNameIsValid(string contactName)
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Name = contactName }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void EmployerContactNameMustBe100CharactersOrLess()
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Name = "name".PadRight(101, 'w') }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.EmployerContact)}.{nameof(vacancy.EmployerContact.Name)}");
            result.Errors[0].ErrorCode.Should().Be("90");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerContactDetails);
        }

        [Theory]
        [MemberData(nameof(TestData.BlacklistedCharacters), MemberType = typeof(TestData))]
        public void EmployerContactNameMustNotContainInvalidCharacters(string invalidChar)
        {
            var vacancy = new Vacancy
            {
                EmployerContact = new ContactDetail { Name = invalidChar }
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.EmployerContactDetails);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be($"{nameof(vacancy.EmployerContact)}.{nameof(vacancy.EmployerContact.Name)}");
            result.Errors[0].ErrorCode.Should().Be("91");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.EmployerContactDetails);
        }
    }
}

<|start_filename|>src/QA/QA.Web/ViewModels/Validations/ValidationMessages.cs<|end_filename|>
﻿namespace Esfa.Recruit.Qa.Web.ViewModels.Validations
{
    public class ValidationMessages
    {
        public static class UnassignReviewConfirmationMessages
        {
            public const string SelectionRequired = "You must select one option.";
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/QueryStore/Projections/VacancyApplications/VacancyApplications.cs<|end_filename|>
﻿using System.Collections.Generic;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.VacancyApplications
{
    public class VacancyApplications : QueryProjectionBase
    {
        public VacancyApplications() : base(QueryViewType.VacancyApplications.TypeName)
        {
        }

        public long VacancyReference { get; set; }
        public List<VacancyApplication> Applications { get; set; }
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/010-vacancies_Adjust_IllegalWageApril2019.js<|end_filename|>
{
    // https://stackoverflow.com/a/44564089/5596802
    function toGUID(hex) {
        let a = hex.substr(6, 2) + hex.substr(4, 2) + hex.substr(2, 2) + hex.substr(0, 2);
        let b = hex.substr(10, 2) + hex.substr(8, 2);
        let c = hex.substr(14, 2) + hex.substr(12, 2);
        let d = hex.substr(16, 16);
        hex = a + b + c + d;
        let uuid = hex.substr(0, 8) + "-" + hex.substr(8, 4) + "-" + hex.substr(12, 4) + "-" + hex.substr(16, 4) + "-" + hex.substr(20, 12);
        return uuid;
    }

    print("Start updating Vacancies with illegal 2019 wages.");

    const query = {
            "startDate": { $gte: ISODate("2019-04-01T00:00:00.000Z") },
            "wage.wageType" : "FixedWage",
            "isDeleted" : false,
            "status" : { $nin : ["Draft", "Closed", "Referred"]} 
        },
        apprenticeshipNationalMinimumWage2019 = 3.9;

    let matchedDocs = db.vacancies.aggregate([
        {
            $match: query
        },
        {
            $sort: { "dateCreated": 1 }
        }]);

    while (matchedDocs.hasNext()) {
        let doc = matchedDocs.next();

        var perHourWage = doc.wage.fixedWageYearlyAmount / 52 / doc.wage.weeklyHours;
        
        if(perHourWage < apprenticeshipNationalMinimumWage2019){
            print(`Found illegal 2019 wage. '${perHourWage}' per hour in document '${toGUID(doc._id.hex())}'`);

            let writeResult = db.vacancies.update({
                "_id": doc._id
            }, {
                $set: { "wage.wageType": "NationalMinimumWageForApprentices" },
                $unset : {"wage.fixedWageYearlyAmount" : "" }
            }, {
                upsert: false
            });

            if (writeResult.hasWriteConcernError()) {
                printjson(writeResult.writeConcernError);
                quit(14);
            }

            print(`Updated document '${toGUID(doc._id.hex())}' with wageType: NationalMinimumWageForApprentices.`);
        }
        else{
            print(`2019 wage OK. '${perHourWage}' per hour in document '${toGUID(doc._id.hex())}'. Not updating`);
        }
    }

    print("Finished updating Vacancies with illegal 2019 wages.");
}

<|start_filename|>src/Shared/Recruit.Shared.Web/TagHelpers/EsfaAutomationTestElementTagHelper.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Esfa.Recruit.Shared.Web.Configuration;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace Esfa.Recruit.Shared.Web.TagHelpers
{
    [HtmlTargetElement(Attributes = TagAttributeName)]
    public class EsfaAutomationTestElementTagHelper : TagHelper
    {
        private const string TagAttributeName = "esfa-automation";
        private const string DataAutomationAttributeName = "data-automation";
        private readonly IHostingEnvironment _env;

        public EsfaAutomationTestElementTagHelper(IHostingEnvironment env)
        {
            _env = env;
        }

        [HtmlAttributeName(TagAttributeName)]
        public string TargetName { get; set; }
        
        public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            if (!_env.IsEnvironment(EnvironmentNames.PROD))
            {
                output.Attributes.SetAttribute(DataAutomationAttributeName, TargetName);
            }

            return Task.CompletedTask;
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Exceptions/EntityValidationException.cs<|end_filename|>
using System;
using System.Runtime.Serialization;
using System.Security.Permissions;
using Esfa.Recruit.Vacancies.Client.Application.Validation;

namespace Esfa.Recruit.Vacancies.Client.Application.Exceptions
{
    [Serializable]
    public class EntityValidationException : Exception
    {
        public EntityValidationResult ValidationResult { get; private set; }

        public EntityValidationException() { }
        public EntityValidationException(string message) : base(message) { }
        public EntityValidationException(string message, EntityValidationResult validationResult) : base(message) 
        {
            ValidationResult = validationResult;
        }

        public EntityValidationException(string message, EntityValidationResult validationResult, Exception inner) : base(message, inner)
        {
            ValidationResult = validationResult;
        }

        public EntityValidationException(string message, Exception inner) : base(message, inner) { }

        protected EntityValidationException(SerializationInfo info, StreamingContext context) : base(info, context)
        {
            ValidationResult = (EntityValidationResult)info.GetValue(nameof(ValidationResult), typeof(EntityValidationResult));
        }

        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null) 
            {
                throw new ArgumentNullException(nameof(info));
            }

            info.AddValue(nameof(ValidationResult), ValidationResult, typeof(EntityValidationResult));

            base.GetObjectData(info, context);
        }
    }
}

<|start_filename|>src/Provider/Provider.Web/RouteModel/ApplicationReviewRouteModel.cs<|end_filename|>
﻿using System;
using Microsoft.AspNetCore.Mvc;

namespace Esfa.Recruit.Provider.Web.RouteModel
{
    public class ApplicationReviewRouteModel : VacancyRouteModel
    {
        [FromRoute]
        public Guid ApplicationReviewId { get; set; }
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/archived/2018-09-04_010-queryViews_MigrateDocuments_queryStore.js<|end_filename|>
function millisToMinutesAndSeconds(millis) {
    let minutes = Math.floor(millis / 60000);
    let seconds = ((millis % 60000) / 1000).toFixed(0);
    return (seconds === 60 ? `${(minutes+1)}:00` : `${minutes}:${(seconds < 10 ? "0" : "")}${seconds}`);
}

{
    print("Start copy of documents from queryViews collection to queryStore collection.");

    const oneSecondInMilliseconds = 1000;
    let uniqueQueryViewsViewTypes = db.queryViews.distinct("viewType");

    let uniqueViewTypes = db.queryStore.distinct("viewType");

    uniqueViewTypes.forEach(vt => {
        print(`Deleting documents of viewType ${vt} from queryStore.`);
        db.queryStore.deleteMany({ "viewType": vt });
    });

    /* Excluding dashboard documents as they have been renamed and will be re-generated */
    /* Excluding ApprenticeshipProgrammes as they have been moved to ReferenceData collection */
    let queryViewDocs = db.queryViews.find({ $and: [{ "viewType": { $ne: "Dashboard"}}, { "viewType": { $ne: "ApprenticeshipProgrammes"}}]}).toArray();

    uniqueQueryViewsViewTypes.forEach(vt => {
        print(`queryViews collection has ${db.queryViews.find({}).toArray().filter(doc => doc.viewType === vt).length} ${vt} documents.`);
    });

    let insertCount = 0,
        insertLoopCount = 0,
        startTime = new Date().getTime();

    if (queryViewDocs.length > 0) {

        for (let pos = 0, insertBatchSize = 5; pos <= queryViewDocs.length; pos += insertBatchSize) {
            print("... insert processing ...");
            insertLoopCount++;
            let docs = queryViewDocs.slice(pos, pos + insertBatchSize);

            if (docs.length > 0) {
                let insertResult = db.queryStore.insertMany(docs);
                insertCount += insertResult.insertedIds.length;
            }
            /* eslint-disable */
            sleep(oneSecondInMilliseconds);
            /* eslint-disable */
        }
    }

    let totalTime = (new Date().getTime() - startTime),
        totalTimeTaken = millisToMinutesAndSeconds(totalTime),
        totalInsertTimeTaken = millisToMinutesAndSeconds(totalTime - (insertLoopCount * oneSecondInMilliseconds));

    print(`Inserted ${insertCount} documents into queryStore. Took ${totalInsertTimeTaken}`);
    print(`Looped insert ${insertLoopCount} times. Took overall ${totalTimeTaken}`);

    uniqueQueryViewsViewTypes.forEach(vt => {
        print(`queryStore collection has ${db.queryStore.count({ "viewType": vt }) + 0} ${vt} documents.`);
    });

    print("Finished copy of documents from queryViews collection to queryStore collection.");
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Messaging/IEvent.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Messaging
{
    public interface IEvent
    {
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Services/Projections/IPublishedVacancyProjectionService.cs<|end_filename|>
﻿using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Services.Projections
{
    public interface IPublishedVacancyProjectionService
    {
        Task ReGeneratePublishedVacanciesAsync();
    }
}


<|start_filename|>src/Data/Recruit.ChangeScripts/archived/2018-10-31_030-vacancies_Add_Field_LegalEntityId.js<|end_filename|>
{
    function buildLookup(userInfos) {
        var lookup = {};
        print("Building lookup");
        for (let index = 0; index < userInfos.length; index++) {
            const element = userInfos[index];
            const employerAccountId = element._id.split("_")[1];
            lookup[employerAccountId] = element.legalEntities;
        }

        return lookup;
    }

    function matchLegalEntityName(nameToMatch, accountLegalEntities) {
        for (let index = 0; index < accountLegalEntities.length; index++) {
            const element = accountLegalEntities[index];
            if (nameToMatch === element.name)
                return element.legalEntityId;
        }

        return null;
    }

    print();
    print();
    print("Start updating Vacancies with LegalEntityId if missing and has an EmployerName");
    print();

    let allVacancies = db.vacancies.find({ "legalEntityId": { $exists: false }, "employerName": { $exists: true } }).toArray();
    var targetVacancyCount = allVacancies.length;
    print(`There are ${targetVacancyCount} vacancies with missing a LegalEntityId field.`);

    if (allVacancies.length > 0) {

        let editUserInfos = db.queryStore.find({ "viewType": "EditVacancyInfo" }).toArray();

        print(`Found ${editUserInfos.length} EditInfo record/s`);

        var lookup = buildLookup(editUserInfos);

        print("Updating vacancies");

        var updateCount = 0;
        
        for (let index = 0; index < targetVacancyCount; index++) {
            const element = allVacancies[index];
            
            var legalEntityId = matchLegalEntityName(element.employerName, lookup[element.employerAccountId]);
            
            let writeResult = db.vacancies.updateOne(
                { "_id": element._id },
                { $set: { "legalEntityId": legalEntityId }}
            );
        
            if (writeResult.matchedCount !== writeResult.modifiedCount) {
                printjson("Error occurred updating vacancy with legalEntityId field.");
                quit(14);
            }

            updateCount++;
        }

        print(`Updated ${updateCount} vacancies with LegalEntityId`);

        let postUpdateVacancies = db.vacancies.find({ "legalEntityId": { $exists: false }, "employerName": { $exists: true } }).toArray();

        if (postUpdateVacancies.length > 0)
            print(`WARNING: There is/are still ${allVacancies.length} vacancies with missing a LegalEntityId field.`);        
    }

    print();
    print("Updated Vacancies with LegalEntityId.");
    print();
    print();
}

<|start_filename|>src/Employer/Employer.Web/Configuration/Routing/RouteValues.cs<|end_filename|>
﻿namespace Esfa.Recruit.Employer.Web.Configuration.Routing
{
    public static class RouteValues
    {
        public const string EmployerAccountId = "employerAccountId";
        public const string VacancyId = "vacancyId";
    }
}


<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/Rules/VacancyRules/VacancyBannedPhraseChecksRuleTest.cs<|end_filename|>
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using AutoFixture;
using Esfa.Recruit.Vacancies.Client.Application.Providers;
using Esfa.Recruit.Vacancies.Client.Application.Rules;
using Esfa.Recruit.Vacancies.Client.Application.Rules.BaseRules;
using Esfa.Recruit.Vacancies.Client.Application.Rules.VacancyRules;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using Esfa.Recruit.UnitTests.Vacancies.Client.Application.Rules.VacancyRules;
using FluentAssertions;
using Microsoft.Azure.Amqp.Serialization;
using Moq;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.Rules.VacancyRules
{
    public class VacancyBannedPhraseChecksRuleTest
    {
        private readonly IBannedPhrasesProvider _bannedPhrasesProvider;
        private readonly IEnumerable<string> _bannedPhrases = new[] { "school", "schools", "driving license", "over 18" };
        public VacancyBannedPhraseChecksRuleTest()
        {
            var mockBannedPhraseProvider = new Mock<IBannedPhrasesProvider>();
            mockBannedPhraseProvider.Setup(b => b.GetBannedPhrasesAsync()).Returns(Task.FromResult(_bannedPhrases));
            _bannedPhrasesProvider = mockBannedPhraseProvider.Object;
        }

        [Fact]
        public void ShouldIgnoreNonAlphaNumericCharacters()
        {
             var sut = new VacancyBannedPhraseChecksRule(_bannedPhrasesProvider);

            var vacancy = new Fixture()
                .Build<Vacancy>()
                .With(v => v.Title,
                    @"Dolore school leavers �school"" leaver 5pm, shifts, may work evenings and weekends school") 
                .Create();

            var result = sut.EvaluateAsync(vacancy).Result;

            result.Details.First(d => d.Target == "Title");

            result.Score.Should().Be(100);
        }

        [Theory]
        [InlineData("a driving license is required", 1)]
        [InlineData("driving - license required", 1)]
        [InlineData("*driving*license* is required", 1)]
        [InlineData("driving license", 100, 100)]
        [InlineData("driving license, driving license, driving license", 100, 100)]
        [InlineData("driving license over 18", 100, 100)]
        [InlineData("*driving \t \n license* is required", 1)]
        [InlineData("occasionally there may be some driving. licensees are required to like beer", 0)]
        [InlineData("a great mover, 18 moves at least!", 0)]
        [InlineData("school", 1)]
        public void WhenInvoked_ItShouldReturnTheExpectedScore(string phrase, int expectedScore,
            decimal weighting = 1.0m)
        {
            var rule = new VacancyBannedPhraseChecksRule(_bannedPhrasesProvider, ConsolidationOption.ConsolidateByField, weighting);

            var entity = TestVacancyBuilder.Create().SetDetails(phrase, string.Empty);

            var outcome = rule.EvaluateAsync(entity).Result;

            outcome.Score.Should().Be(expectedScore);
        }

        [Fact]
        public void ShouldReturnAnOverallUnconsolidatedNarrative()
        {
            var rule = new VacancyBannedPhraseChecksRule(_bannedPhrasesProvider);

            var skills = new[] { "Juggling", "Running", "driving license" };

            var entity = TestVacancyBuilder.Create()
                .SetDetails("driving - license required", "must be over 18 years of age and hold a clean driving license, yes a driving license!")
                .SetSkills(skills);

            var outcome = rule.EvaluateAsync(entity).Result;

            outcome.Narrative.Should().Contain("Banned phrase 'driving license' found in 'Title'");
            outcome.Narrative.Should().Contain("Banned phrase 'driving license' found 2 times in 'Description'");
            outcome.Narrative.Should().Contain("Banned phrase 'over 18' found in 'Description'");
            outcome.Narrative.Should().Contain("Banned phrase 'driving license' found in 'Skills'");
        }

        [Fact]
        public void ShouldReturnAnOverallConsolidatedNarrative()
        {
            var rule = new VacancyBannedPhraseChecksRule(_bannedPhrasesProvider, ConsolidationOption.ConsolidateByField);

            var skills = new[] { "Juggling", "Running", "driving license" };

            var entity = TestVacancyBuilder.Create()
                .SetDetails("driving - license required", "must be over 18 years of age and hold a clean driving license, yes a driving license!")
                .SetSkills(skills);

            var outcome = rule.EvaluateAsync(entity).Result;

            outcome.Narrative.Should().Contain("1 banned phrases 'driving license' found in 'Title'");
            outcome.Narrative.Should().Contain("3 banned phrases 'driving license,over 18' found in 'Description'");
            outcome.Narrative.Should().Contain("1 banned phrases 'driving license' found in 'Skills'");
        }

        [Fact]
        public void WhenInvoked_ItShouldIncludeDetailsOfEachFieldOutcome()
        {
            var rule = new VacancyBannedPhraseChecksRule(_bannedPhrasesProvider);

            var skills = new[] { "Juggling", "Running", "driving license" };

            var entity = TestVacancyBuilder.Create()
                .SetDetails("an apprenticeship", "must be over 18 years of age and hold a clean driving license")
                .SetSkills(skills);

            var outcome = rule.EvaluateAsync(entity).Result;

            outcome.HasDetails.Should().BeTrue();

            Assert.All(outcome.Details, a =>
            {
                a.Target.Should().NotBeEmpty();
                a.Narrative.Should().NotBeEmpty();
            });
        }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/SingleField/ExternalApplicationInstructionsValidationTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation.SingleField
{
    public class ExternalApplicationInstructionsValidationTests : VacancyValidationTestsBase
    {
        [Theory]
        [InlineData(null)]
        [InlineData("")]
        [InlineData("You can apply online or through post.")]
        public void NoErrorsWhenExternalApplicationInstructionsIsValid(string instructions)
        {
            var vacancy = new Vacancy
            {
                ApplicationMethod = ApplicationMethod.ThroughExternalApplicationSite,
                ApplicationUrl = "http://www.apply.com",
                ApplicationInstructions = instructions
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ApplicationMethod);

            result.HasErrors.Should().BeFalse();
            result.Errors.Should().HaveCount(0);
        }

        [Fact]
        public void ExternalApplicationInstructionsMustBe500CharactersOrLess()
        {
            var vacancy = new Vacancy
            {
                ApplicationMethod = ApplicationMethod.ThroughExternalApplicationSite,
                ApplicationUrl = "http://www.apply.com",
                ApplicationInstructions = "instructions".PadRight(501, 'w')
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ApplicationMethod);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be(nameof(vacancy.ApplicationInstructions));
            result.Errors[0].ErrorCode.Should().Be("88");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.ApplicationMethod);
        }

        [Theory]
        [MemberData(nameof(TestData.BlacklistedCharacters), MemberType = typeof(TestData))]
        public void ExternalApplicationInstructionsMustNotContainInvalidCharacters(string invalidChar)
        {
            var vacancy = new Vacancy
            {
                ApplicationMethod = ApplicationMethod.ThroughExternalApplicationSite,
                ApplicationUrl = "http://www.apply.com",
                ApplicationInstructions = invalidChar
            };

            var result = Validator.Validate(vacancy, VacancyRuleSet.ApplicationMethod);

            result.HasErrors.Should().BeTrue();
            result.Errors[0].PropertyName.Should().Be(nameof(vacancy.ApplicationInstructions));
            result.Errors[0].ErrorCode.Should().Be("89");
            result.Errors[0].RuleId.Should().Be((long)VacancyRuleSet.ApplicationMethod);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/CandidateDeleteEvent.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Messaging;
using MediatR;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events
{
    // Note: This is an externally published event.
    public class CandidateDeleteEvent : EventBase, INotification
    {
        public Guid CandidateId { get; set; }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Providers/ICandidateSkillsProvider.cs<|end_filename|>
﻿using System.Collections.Generic;
using System.Threading.Tasks;

namespace Esfa.Recruit.Vacancies.Client.Application.Providers
{
    public interface ICandidateSkillsProvider
    {
        Task<List<string>> GetCandidateSkillsAsync();
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/FeatureToggle/IFeature.cs<|end_filename|>
﻿namespace Esfa.Recruit.Shared.Web.FeatureToggle
{
    public interface IFeature
    {
        bool IsFeatureEnabled(string feature);
    }
}


<|start_filename|>src/Employer/Employer.Web/Middleware/EmployerAccountRequirement.cs<|end_filename|>
﻿using Microsoft.AspNetCore.Authorization;

namespace Esfa.Recruit.Employer.Web.Middleware
{
    public class EmployerAccountRequirement : IAuthorizationRequirement { }
}

<|start_filename|>src/Jobs/Recruit.Vacancies.Jobs/DomainEvents/Handlers/Vacancy/DraftVacancyUpdatedHandler.cs<|end_filename|>
using System;
using System.Threading.Tasks;
using Esfa.Recruit.Vacancies.Client.Domain.Events;
using Esfa.Recruit.Vacancies.Client.Infrastructure.Client;
using Microsoft.Extensions.Logging;

namespace Esfa.Recruit.Vacancies.Jobs.DomainEvents.Handlers.Vacancy
{
    public class DraftVacancyUpdatedHandler : DomainEventHandler,  IDomainEventHandler<DraftVacancyUpdatedEvent>
    {
        private readonly ILogger<DraftVacancyUpdatedHandler> _logger;
        private readonly IJobsVacancyClient _client;

        public DraftVacancyUpdatedHandler(ILogger<DraftVacancyUpdatedHandler> logger, IJobsVacancyClient client) : base(logger)
        {
            _logger = logger;
            _client = client;
        }

        public async Task HandleAsync(string eventPayload)
        {
            var @event = DeserializeEvent<DraftVacancyUpdatedEvent>(eventPayload);

            try
            {
                _logger.LogInformation($"Processing {nameof(DraftVacancyUpdatedEvent)} for vacancy: {{VacancyId}}", @event.VacancyId);

                await _client.AssignVacancyNumber(@event.VacancyId);

                await _client.PatchTrainingProviderAsync(@event.VacancyId);

                await _client.EnsureVacancyIsGeocodedAsync(@event.VacancyId);

                _logger.LogInformation($"Finished Processing {nameof(DraftVacancyUpdatedEvent)} for vacancy: {{VacancyId}}", @event.VacancyId);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unable to process {eventBody}", @event);
                throw;
            }
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/Fluent/VacancyReviewValidator.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentValidation;

namespace Esfa.Recruit.Vacancies.Client.Application.Validation.Fluent
{
    public class VacancyReviewValidator : AbstractValidator<VacancyReview>
    {
        public const int ManualQaCommentMaxLength = 2500;

        public const string ManualQaCommentLengthMessage = "Your comment must be less than {0} characters";
        public const string ManualQaCommentFreeTextCharactersMessage = "You have entered invalid characters";
        public const string ManualQaCommentRequired = "If you are rejecting this vacancy you must include comments";

        public VacancyReviewValidator()
        {
            RuleFor(x => x.ManualQaComment)
                .MaximumLength(ManualQaCommentMaxLength)
                .WithMessage(string.Format(ManualQaCommentLengthMessage, ManualQaCommentMaxLength))
                .ValidFreeTextCharacters()
                .WithMessage(ManualQaCommentFreeTextCharactersMessage);

            When(x => x.ManualOutcome == ManualQaOutcome.Referred, () =>
            {
                RuleFor(x => x.ManualQaComment)
                    .NotEmpty()
                    .WithMessage(ManualQaCommentRequired);
            });
        }
    }
}


<|start_filename|>src/Shared/Recruit.Shared.Web/ViewModels/ApplicationReview/IApplicationReviewEditModel.cs<|end_filename|>
using Esfa.Recruit.Vacancies.Client.Domain.Entities;

namespace Esfa.Recruit.Shared.Web.ViewModels.ApplicationReview
{
    public interface IApplicationReviewEditModel
    {
        ApplicationReviewStatus? Outcome { get; set; }
        string CandidateFeedback { get; set; }
    }
}

<|start_filename|>src/Data/Recruit.ChangeScripts/archived/2018-10-31_020-vacancies_Update_StatusField_RedundantValues.js<|end_filename|>
{
    print("Start update of document field named 'status' in vacancies collection documents.");

    let writeResult = db.vacancies.updateMany(
        {
            "status":
            {
                $in: [ "PendingReview", "UnderReview" ]
            }
        },
        {
            $set: { "status": "Submitted" }
        }
    );

    if (writeResult.matchedCount !== writeResult.modifiedCount) {
        printjson("Error occurred updating vacancy documents status field.");
        quit(14);
    }

    print(`Found '${writeResult.matchedCount}' documents to update, updated ${writeResult.modifiedCount}.`);

    print("Finished update of document field named 'status' in vacancies collection documents.");
}

<|start_filename|>src/Shared/Recruit.Shared.Web/TagHelpers/EsfaValidationMessageForTagHelper.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace Esfa.Recruit.Shared.Web.TagHelpers
{
    [HtmlTargetElement("span", Attributes = ValidationForAttributeName)]
    public class EsfaValidationMessageForTagHelper : ValidationMessageTagHelper
    {
        private const string ValidationForAttributeName = "esfa-validation-message-for";

        [HtmlAttributeName(ValidationForAttributeName)]
        public ModelExpression ForField { get; set; }

        public EsfaValidationMessageForTagHelper(IHtmlGenerator generator) : base(generator) { }

        public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            base.For = ForField;
            output.Attributes.SetAttribute("id", "error-message-" + For.Name);
            return base.ProcessAsync(context, output);
        }
    }
}

<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Events/Interfaces/IApplicationReviewEvent.cs<|end_filename|>
using System;

namespace Esfa.Recruit.Vacancies.Client.Domain.Events.Interfaces
{
    public interface IApplicationReviewEvent
    {
        long VacancyReference { get; }
    }
}

<|start_filename|>src/Shared/UnitTests/Vacancies.Client/Application/VacancyValidation/VacancyReviewTests.cs<|end_filename|>
﻿using Esfa.Recruit.Vacancies.Client.Application.Validation.Fluent;
using Esfa.Recruit.Vacancies.Client.Domain.Entities;
using FluentAssertions;
using Xunit;

namespace Esfa.Recruit.UnitTests.Vacancies.Client.Application.VacancyValidation
{
    public class VacancyReviewTests
    {
        public class ManualQaCommentTestData : TheoryData<string, string>
        {
            public ManualQaCommentTestData()
            {
                var manualQaCommentTooLong =
                    new string('a',VacancyReviewValidator.ManualQaCommentMaxLength + 1);

                Add(">", VacancyReviewValidator.ManualQaCommentFreeTextCharactersMessage);
                Add(manualQaCommentTooLong, string.Format(VacancyReviewValidator.ManualQaCommentLengthMessage, VacancyReviewValidator.ManualQaCommentMaxLength));
            }
        }

        [Theory]
        [ClassData(typeof(ManualQaCommentTestData))]
        public void ShouldValidateManaulQaComment(string manualQaComment, string expectedErrorMessage)
        {
            var m = new VacancyReview
            {
                ManualQaComment = manualQaComment
            };

            var validator = new VacancyReviewValidator();

            var result = validator.Validate(m);

            result.IsValid.Should().BeFalse();
            result.Errors.Count.Should().Be(1);
            result.Errors[0].ErrorMessage.Should().Be(expectedErrorMessage);
        }

        [Fact]
        public void ShouldNotRequireCandiateFeedbackIfSuccessful()
        {
            var m = new Recruit.Vacancies.Client.Domain.Entities.ApplicationReview
            {
                Status = ApplicationReviewStatus.Successful,
                CandidateFeedback = "should not specify feedback if successful"
            };

            var validator = new ApplicationReviewValidator();

            var result = validator.Validate(m);

            result.IsValid.Should().BeFalse();
            result.Errors.Count.Should().Be(1);
            result.Errors[0].ErrorMessage.Should().Be(ApplicationReviewValidator.CandidateFeedbackNull);
        }

        [Theory]
        [InlineData(null)]
        [InlineData("")]
        public void ApplicationReviewShouldBeValid(string manualQaComment)
        {
            var m = new VacancyReview
            {
                ManualQaComment = manualQaComment
            };

            var validator = new VacancyReviewValidator();

            var result = validator.Validate(m);

            result.IsValid.Should().BeTrue();
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Domain/Entities/UserType.cs<|end_filename|>
﻿namespace Esfa.Recruit.Vacancies.Client.Domain.Entities
{
    public enum UserType
    {
        Employer,
        Provider
    }
}


<|start_filename|>src/Employer/Employer.Web/Extensions/HttpContextExtensions.cs<|end_filename|>
﻿using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication;

namespace Esfa.Recruit.Employer.Web.Extensions
{
    public static class HttpContextExtensions
    {
        public static async Task SignOutEmployerWebAsync(this HttpContext httpContext)
        {
            await httpContext.SignOutAsync("Cookies");

            await httpContext.SignOutAsync("oidc");
        }
    }
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Application/Validation/ValidationConstants.cs<|end_filename|>
﻿using System.Text.RegularExpressions;

namespace Esfa.Recruit.Vacancies.Client.Application.Validation
{
    public static class ValidationConstants
    {
        internal const string ValidationsRulesKey = "validationsToRun";

        // See http://stackoverflow.com/questions/164979/uk-postcode-regex-comprehensive
        internal const string PostCodeRegExPattern = "(([gG][iI][rR] {0,}0[aA]{2})|((([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y]?[0-9][0-9]?)|(([a-pr-uwyzA-PR-UWYZ][0-9][a-hjkstuwA-HJKSTUW])|([a-pr-uwyzA-PR-UWYZ][a-hk-yA-HK-Y][0-9][abehmnprv-yABEHMNPRV-Y]))) {0,}[0-9][abd-hjlnp-uw-zABD-HJLNP-UW-Z]{2}))";

        public static Regex PostcodeRegex => new Regex(PostCodeRegExPattern);
        public static Regex UkprnRegex => new Regex(@"^((?!(0))[0-9]{8})$");
        public static Regex EmailAddressRegex => new Regex(@"^[a-zA-Z0-9\u0080-\uFFA7?$#()""'!,+\-=_:;.&€£*%\s\/]+@[a-zA-Z0-9\u0080-\uFFA7?$#()""'!,+\-=_:;.&€£*%\s\/]+\.([a-zA-Z0-9\u0080-\uFFA7]{2,10})$");
        public static Regex PhoneNumberRegex => new Regex(@"^[0-9+\s-()]{8,16}$");
        public static Regex ContainsApprenticeOrApprenticeshipRegex = new Regex(@"(^|\s)(apprentice(ship)?)(\s|$)", RegexOptions.IgnoreCase);
    }
}


<|start_filename|>src/Employer/Employer.Web/ViewModels/VacancyView/ApplicationManageableVacancyViewModel.cs<|end_filename|>
﻿using System.Collections.Generic;
using Esfa.Recruit.Vacancies.Client.Infrastructure.QueryStore.Projections.VacancyApplications;

namespace Esfa.Recruit.Employer.Web.ViewModels
{
    public class ApplicationManageableVacancyViewModel : DisplayVacancyViewModel
    {
        public List<VacancyApplication> Applications { get; internal set; }
    }
}


<|start_filename|>src/Data/CosmosDb/Console.RecruitSeedDataWriter/Data/CandidateSkills.json<|end_filename|>
﻿{
  "_id": "CandidateSkills",
  "lastUpdatedDate": "2018-07-01T00:00:00.000Z",
  "skills": [
    "Communication skills",
    "IT skills",
    "Attention to detail",
    "Organisation skills",
    "Customer care skills",
    "Problem solving skills",
    "Presentation skills",
    "Administrative skills",
    "Number skills",
    "Analytical skills",
    "Logical",
    "Team working",
    "Creative",
    "Initiative",
    "Non judgemental",
    "Patience",
    "Physical fitness"
  ]
}


<|start_filename|>src/Shared/Recruit.Vacancies.Client/Infrastructure/Exceptions/VacancyNotFoundException.cs<|end_filename|>
﻿using System;
using Esfa.Recruit.Vacancies.Client.Domain.Exceptions;

namespace Esfa.Recruit.Vacancies.Client.Infrastructure.Exceptions
{
    [Serializable]
    public class VacancyNotFoundException : RecruitException
    {
        public VacancyNotFoundException(string message) : base(message) { }
    }
}