public void MatchCandidate_WithExistingCandidateRequest_MatchesOnNewestCandidateWithEmail( string email, string firstName, string lastName, string expectedFirstName ) { var request = new ExistingCandidateRequest { Email = email, FirstName = firstName, LastName = lastName }; _mockService.Setup(mock => mock.CreateQuery("contact", _context)).Returns(MockCandidates()); _mockService.Setup(mock => mock.LoadProperty(It.IsAny <Entity>(), new Relationship("dfe_contact_dfe_candidatequalification_ContactId"), _context)); _mockService.Setup(mock => mock.LoadProperty(It.IsAny <Entity>(), new Relationship("dfe_contact_dfe_candidatepastteachingposition_ContactId"), _context)); _mockService.Setup(mock => mock.LoadProperty(It.IsAny <Entity>(), new Relationship("dfe_contact_dfe_servicesubscription_contact"), _context)); _mockService.Setup(mock => mock.LoadProperty(It.IsAny <Entity>(), new Relationship("msevtmgt_contact_msevtmgt_eventregistration_Contact"), _context)); var result = _crm.MatchCandidate(request); result?.FirstName.Should().Be(expectedFirstName); }
public IActionResult CreateAccessToken([FromBody, SwaggerRequestBody("Candidate access token request (must match an existing candidate).", Required = true)] ExistingCandidateRequest request) { if (!ModelState.IsValid) { return(BadRequest(this.ModelState)); } if (_appSettings.IsCrmIntegrationPaused) { return(NotFound()); } var candidate = _crm.MatchCandidate(request); if (candidate == null) { return(NotFound()); } var token = _accessTokenService.GenerateToken(request, (Guid)candidate.Id); var personalisation = new Dictionary <string, dynamic> { { "pin_code", token } }; // We respond immediately/assume this will be successful. _notifyService.SendEmailAsync(request.Email, NotifyService.NewPinCodeEmailTemplateId, personalisation); return(NoContent()); }
public void Validate_SpecifyNoAdditionalRequiredAttributes_HasError() { var request = new ExistingCandidateRequest(); var result = _validator.TestValidate(request); result.ShouldHaveValidationErrorFor(request => request).WithErrorMessage("You must specify values for 2 additional attributes (from birthdate, firstname and lastname)."); }
public string GenerateToken(ExistingCandidateRequest request, Guid candidateId) { var totp = CreateTotp(request).ComputeTotp(); _metrics.GeneratedTotps.Inc(); return(totp); }
public void Slugify_WithMixedCasing_ReturnsLowerCase() { var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "FIrst" }; request.Slugify().Should().Be("[email protected]"); }
public void Slugify_WithNullAdditionalAttribute_OmitsNull() { var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "first" }; request.Slugify().Should().Be("[email protected]"); }
public void Validate_SpecifyTwoAdditionalRequiredAttributes_HasNoError() { var request = new ExistingCandidateRequest { FirstName = "first", LastName = "last" }; var result = _validator.TestValidate(request); result.ShouldNotHaveValidationErrorFor(request => request); }
public void Validate_WhenValid_HasNoErrors() { var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "first", DateOfBirth = DateTime.UtcNow.AddDays(-18) }; var result = _validator.TestValidate(request); result.IsValid.Should().BeTrue(); }
public void Validate_SpecifyThreeAdditionalRequiredAttributes_HasNoError() { var request = new ExistingCandidateRequest { FirstName = "first", LastName = "last", DateOfBirth = DateTime.UtcNow.AddDays(-18) }; var result = _validator.TestValidate(request); result.ShouldNotHaveValidationErrorFor(request => request); }
public ExistingCandidateRequestTests() { _request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "first", LastName = "last", DateOfBirth = new DateTime(2000, 1, 1) }; }
public void GenerateToken_SameRequestInSameStep_ReturnSameToken() { var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; var token1 = _service.GenerateToken(request, _candidateId); var token2 = _service.GenerateToken(request, _candidateId); token1.Should().Be(token2); }
public CandidatesControllerTests() { _mockTokenService = new Mock <ICandidateAccessTokenService>(); _mockCrm = new Mock <ICrmService>(); _mockJobClient = new Mock <IBackgroundJobClient>(); _request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; _controller = new CandidatesController(_mockTokenService.Object, _mockCrm.Object, _mockJobClient.Object); }
public void GenerateToken_ReturnsAValidToken(string email, string firstName, string lastName) { var generatedTotpsCount = _metrics.GeneratedTotps.Value; var verifiedTotpsCount = _metrics.VerifiedTotps.WithLabels(new[] { true.ToString() }).Value; var request = new ExistingCandidateRequest { Email = email, FirstName = firstName, LastName = lastName }; var token = _service.GenerateToken(request, _candidateId); _service.IsValid(token, request, _candidateId).Should().BeTrue(); _metrics.GeneratedTotps.Value.Should().Be(generatedTotpsCount + 1); _metrics.VerifiedTotps.WithLabels(new[] { true.ToString() }).Value.Should().Be(verifiedTotpsCount + 1); }
public void GenerateToken_DifferentFirstNameInSameStep_ReturnDifferentTokens() { var request1 = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John1", LastName = "Doe" }; var request2 = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John2", LastName = "Doe" }; var token1 = _service.GenerateToken(request1, _candidateId); var token2 = _service.GenerateToken(request2, _candidateId); token1.Should().NotBe(token2); }
public IActionResult ExchangeAccessTokenForAttendee( [FromRoute, SwaggerParameter("Access token (PIN code).", Required = true)] string accessToken, [FromBody, SwaggerRequestBody("Candidate access token request (must match an existing candidate).", Required = true)] ExistingCandidateRequest request) { var candidate = _crm.MatchCandidate(request); if (candidate == null || !_tokenService.IsValid(accessToken, request, (Guid)candidate.Id)) { return(Unauthorized()); } return(Ok(new TeachingEventAddAttendee(candidate))); }
public void IsValid_WithExpiredToken_ReturnsFalse() { var verifiedTotpsCount = _metrics.VerifiedTotps.WithLabels(new[] { false.ToString() }).Value; var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; var secondsToOutsideOfWindow = (CandidateAccessTokenService.StepInSeconds * CandidateAccessTokenService.VerificationWindow) + 1; var dateTimeOutsideOfWindow = DateTime.UtcNow.AddSeconds(-secondsToOutsideOfWindow); var token = _service.GenerateToken(request, _candidateId); _service.IsValid(token, request, _candidateId, dateTimeOutsideOfWindow).Should().BeFalse(); _metrics.VerifiedTotps.WithLabels(new[] { false.ToString() }).Value.Should().Be(verifiedTotpsCount + 1); }
public CandidatesControllerTests() { _mockTokenService = new Mock <ICandidateAccessTokenService>(); _mockCrm = new Mock <ICrmService>(); _mockJobClient = new Mock <IBackgroundJobClient>(); _mockDateTime = new Mock <IDateTimeProvider>(); _request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; _controller = new CandidatesController(_mockTokenService.Object, _mockCrm.Object, _mockJobClient.Object, _mockDateTime.Object); // Freeze time. _mockDateTime.Setup(m => m.UtcNow).Returns(DateTime.UtcNow); }
public TeachingEventsControllerTests() { _request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; _mockTokenService = new Mock <ICandidateAccessTokenService>(); _mockCrm = new Mock <ICrmService>(); _mockStore = new Mock <IStore>(); _mockJobClient = new Mock <IBackgroundJobClient>(); _mockLogger = new Mock <ILogger <TeachingEventsController> >(); _metrics = new MetricService(); _controller = new TeachingEventsController(_mockStore.Object, _mockJobClient.Object, _mockTokenService.Object, _mockCrm.Object, _mockLogger.Object, _metrics); }
public void CreateAccessToken_InvalidRequest_RespondsWithValidationErrors() { var request = new ExistingCandidateRequest { Email = "invalid-email@" }; _controller.ModelState.AddModelError("Email", "Email is invalid."); var response = _controller.CreateAccessToken(request); var badRequest = response.Should().BeOfType <BadRequestObjectResult>().Subject; var errors = badRequest.Value.Should().BeOfType <SerializableError>().Subject; errors.Should().ContainKey("Email").WhichValue.Should().BeOfType <string[]>().Which.Should().Contain("Email is invalid."); }
public void IsValid_WithInvalidToken_ReturnsFalse(string invalidToken) { var verifiedTotpsCount = _metrics.VerifiedTotps.WithLabels(new[] { false.ToString() }).Value; var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; _service.IsValid(invalidToken, request, _candidateId).Should().BeFalse(); if (!string.IsNullOrWhiteSpace(invalidToken)) { _metrics.VerifiedTotps.WithLabels(new[] { false.ToString() }).Value.Should().Be(verifiedTotpsCount + 1); } }
public void CreateAccessToken_MismatchedCandidate_ReturnsNotFound() { var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; _mockCrm.Setup(mock => mock.MatchCandidate(request)).Returns <Candidate>(null); var response = _controller.CreateAccessToken(request); response.Should().BeOfType <NotFoundResult>(); _mockNotifyService.Verify(mock => mock.SendEmailAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <Dictionary <string, dynamic> >()), Times.Never() ); }
public void CreateAccessToken_CrmIntegrationIsPaused_ReturnsNotFound() { var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; _mockAppSettings.Setup(m => m.IsCrmIntegrationPaused).Returns(true); var response = _controller.CreateAccessToken(request); response.Should().BeOfType <NotFoundResult>(); _mockNotifyService.Verify(mock => mock.SendEmailAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <Dictionary <string, dynamic> >()), Times.Never() ); }
public bool IsValid(string token, ExistingCandidateRequest request, Guid candidateId, DateTime timestamp) { if (string.IsNullOrWhiteSpace(token)) { return(false); } var totp = CreateTotp(request); var valid = totp.VerifyTotp( timestamp, token, out _, new VerificationWindow(previous: VerificationWindow, future: 0)); _metrics.VerifiedTotps.WithLabels(valid.ToString()).Inc(); return(valid); }
public void CreateAccessToken_ValidRequest_SendsPINCodeEmail() { var request = new ExistingCandidateRequest { Email = "*****@*****.**", FirstName = "John", LastName = "Doe" }; var candidate = new Candidate { Id = Guid.NewGuid(), Email = request.Email, FirstName = request.FirstName, LastName = request.LastName }; _mockAccessTokenService.Setup(mock => mock.GenerateToken(request, (Guid)candidate.Id)).Returns("123456"); _mockCrm.Setup(mock => mock.MatchCandidate(request)).Returns(candidate); var response = _controller.CreateAccessToken(request); response.Should().BeOfType <NoContentResult>(); _mockNotifyService.Verify( mock => mock.SendEmailAsync( "*****@*****.**", NotifyService.NewPinCodeEmailTemplateId, It.Is <Dictionary <string, dynamic> >(personalisation => personalisation["pin_code"] as string == "123456") ) ); }
public void MatchCandidate_WithMasterChildRecords_ReturnsMasterRecord() { var request = new ExistingCandidateRequest { Email = "*****@*****.**", LastName = "Record", DateOfBirth = new DateTime(2000, 1, 1) }; _mockService.Setup(mock => mock.CreateQuery("contact", _context)).Returns(MockCandidates()); _mockService.Setup(mock => mock.LoadProperty(It.IsAny <Entity>(), new Relationship("dfe_contact_dfe_candidatequalification_ContactId"), _context)); _mockService.Setup(mock => mock.LoadProperty(It.IsAny <Entity>(), new Relationship("dfe_contact_dfe_candidatepastteachingposition_ContactId"), _context)); _mockService.Setup(mock => mock.LoadProperty(It.IsAny <Entity>(), new Relationship("dfe_contact_dfe_servicesubscription_contact"), _context)); _mockService.Setup(mock => mock.LoadProperty(It.IsAny <Entity>(), new Relationship("msevtmgt_contact_msevtmgt_eventregistration_Contact"), _context)); var result = _crm.MatchCandidate(request); result?.FirstName.Should().Be("Master"); }
public Candidate MatchCandidate(ExistingCandidateRequest request) { var context = Context(); var entity = _service.CreateQuery("contact", context) .Where(e => e.GetAttributeValue <int>("statecode") == (int)Candidate.Status.Active && // Will perform a case-insensitive comparison. // Contains is used to ensure we match emails with white space (request.Match does an exact match in-memory). e.GetAttributeValue <string>("emailaddress1").Contains(request.Email)) .OrderByDescending(e => e.GetAttributeValue <double>("dfe_duplicatescorecalculated")) .ThenByDescending(e => e.GetAttributeValue <DateTime>("modifiedon")) .Take(MaximumNumberOfCandidatesToMatch) .FirstOrDefault(request.Match); if (entity == null) { return(null); } LoadCandidateRelationships(entity, context); return(new Candidate(entity, this, _validatorFactory)); }
private Totp CreateTotp(ExistingCandidateRequest request) { return(new Totp(CompoundSharedSecretBytes(request.Slugify()), totpSize: Length, step: StepInSeconds)); }
/// <summary> /// Retrieves an existing candidate for the Teacher Training Adviser service. Retrieves an existing candidate for the Teacher Training Adviser service. The `accessToken` is obtained from a `POST /candidates/access_tokens` request (you must also ensure the `ExistingCandidateRequest` payload you exchanged for your token matches the request payload here). /// </summary> /// <exception cref="GetIntoTeachingApi.Client.Client.ApiException">Thrown when fails to make API call</exception> /// <param name="accessToken">Access token (PIN code).</param> /// <param name="body">Candidate access token request (must match an existing candidate).</param> /// <returns>Task of ApiResponse (Candidate)</returns> public async System.Threading.Tasks.Task <ApiResponse <Candidate> > GetExistingTeacherTrainingAdviserCandidateAsyncWithHttpInfo(string accessToken, ExistingCandidateRequest body) { // verify the required parameter 'accessToken' is set if (accessToken == null) { throw new ApiException(400, "Missing required parameter 'accessToken' when calling TeacherTrainingAdviserApi->GetExistingTeacherTrainingAdviserCandidate"); } // verify the required parameter 'body' is set if (body == null) { throw new ApiException(400, "Missing required parameter 'body' when calling TeacherTrainingAdviserApi->GetExistingTeacherTrainingAdviserCandidate"); } var localVarPath = "/api/teacher_training_adviser/candidates/{accessToken}"; var localVarPathParams = new Dictionary <String, String>(); var localVarQueryParams = new List <KeyValuePair <String, String> >(); var localVarHeaderParams = new Dictionary <String, String>(this.Configuration.DefaultHeader); var localVarFormParams = new Dictionary <String, String>(); var localVarFileParams = new Dictionary <String, FileParameter>(); Object localVarPostBody = null; // to determine the Content-Type header String[] localVarHttpContentTypes = new String[] { "application/json", "text/json", "application/_*+json" }; String localVarHttpContentType = this.Configuration.ApiClient.SelectHeaderContentType(localVarHttpContentTypes); // to determine the Accept header String[] localVarHttpHeaderAccepts = new String[] { "text/plain", "application/json", "text/json" }; String localVarHttpHeaderAccept = this.Configuration.ApiClient.SelectHeaderAccept(localVarHttpHeaderAccepts); if (localVarHttpHeaderAccept != null) { localVarHeaderParams.Add("Accept", localVarHttpHeaderAccept); } if (accessToken != null) { localVarPathParams.Add("accessToken", this.Configuration.ApiClient.ParameterToString(accessToken)); // path parameter } if (body != null && body.GetType() != typeof(byte[])) { localVarPostBody = this.Configuration.ApiClient.Serialize(body); // http body (model) parameter } else { localVarPostBody = body; // byte array } // authentication (apiKey) required if (!String.IsNullOrEmpty(this.Configuration.GetApiKeyWithPrefix("Authorization"))) { localVarHeaderParams["Authorization"] = this.Configuration.GetApiKeyWithPrefix("Authorization"); } // make the HTTP request IRestResponse localVarResponse = (IRestResponse)await this.Configuration.ApiClient.CallApiAsync(localVarPath, Method.POST, localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarFormParams, localVarFileParams, localVarPathParams, localVarHttpContentType); int localVarStatusCode = (int)localVarResponse.StatusCode; if (ExceptionFactory != null) { Exception exception = ExceptionFactory("GetExistingTeacherTrainingAdviserCandidate", localVarResponse); if (exception != null) { throw exception; } } return(new ApiResponse <Candidate>(localVarStatusCode, localVarResponse.Headers.ToDictionary(x => x.Name, x => x.Value.ToString()), (Candidate)this.Configuration.ApiClient.Deserialize(localVarResponse, typeof(Candidate)))); }
/// <summary> /// Retrieves an existing candidate for the Teacher Training Adviser service. Retrieves an existing candidate for the Teacher Training Adviser service. The `accessToken` is obtained from a `POST /candidates/access_tokens` request (you must also ensure the `ExistingCandidateRequest` payload you exchanged for your token matches the request payload here). /// </summary> /// <exception cref="GetIntoTeachingApi.Client.Client.ApiException">Thrown when fails to make API call</exception> /// <param name="accessToken">Access token (PIN code).</param> /// <param name="body">Candidate access token request (must match an existing candidate).</param> /// <returns>Task of Candidate</returns> public async System.Threading.Tasks.Task <Candidate> GetExistingTeacherTrainingAdviserCandidateAsync(string accessToken, ExistingCandidateRequest body) { ApiResponse <Candidate> localVarResponse = await GetExistingTeacherTrainingAdviserCandidateAsyncWithHttpInfo(accessToken, body); return(localVarResponse.Data); }
/// <summary> /// Retrieves an existing candidate for the Teacher Training Adviser service. Retrieves an existing candidate for the Teacher Training Adviser service. The `accessToken` is obtained from a `POST /candidates/access_tokens` request (you must also ensure the `ExistingCandidateRequest` payload you exchanged for your token matches the request payload here). /// </summary> /// <exception cref="GetIntoTeachingApi.Client.Client.ApiException">Thrown when fails to make API call</exception> /// <param name="accessToken">Access token (PIN code).</param> /// <param name="body">Candidate access token request (must match an existing candidate).</param> /// <returns>Candidate</returns> public Candidate GetExistingTeacherTrainingAdviserCandidate(string accessToken, ExistingCandidateRequest body) { ApiResponse <Candidate> localVarResponse = GetExistingTeacherTrainingAdviserCandidateWithHttpInfo(accessToken, body); return(localVarResponse.Data); }