public async Task <IEnumerable <TeachingEvent> > SearchTeachingEventsAsync(TeachingEventSearchRequest request) { IQueryable <TeachingEvent> teachingEvents = _dbContext.TeachingEvents .Include(te => te.Building) .OrderBy(te => te.StartAt); if (request.TypeId != null) { teachingEvents = teachingEvents.Where(te => te.TypeId == request.TypeId); } if (request.StartAfter != null) { teachingEvents = teachingEvents.Where(te => request.StartAfter < te.StartAt); } if (request.StartBefore != null) { teachingEvents = teachingEvents.Where(te => request.StartBefore > te.StartAt); } if (request.Radius == null) { return(await teachingEvents.ToListAsync()); } return(await FilterTeachingEventsByRadius(teachingEvents, request)); }
public async Task <IActionResult> SearchGroupedByType( [FromQuery, SwaggerParameter("Event search criteria.", Required = true)] TeachingEventSearchRequest request, [FromQuery, SwaggerParameter("Quantity to return (per type).")] int quantityPerType = 3) { if (!ModelState.IsValid) { return(BadRequest(this.ModelState)); } if (request.Postcode != null) { _logger.LogInformation($"SearchGroupedByType: {request.Postcode}"); } var teachingEvents = await _store.SearchTeachingEventsAsync(request); _metrics.TeachingEventSearchResults .WithLabels(request.TypeId.ToString(), request.Radius.ToString()) .Observe(teachingEvents.Count()); var inPesonTeachingEvents = teachingEvents.Where(e => e.IsInPerson); _metrics.InPersonTeachingEventResults .WithLabels(request.TypeId.ToString(), request.Radius.ToString()) .Observe(inPesonTeachingEvents.Count()); return(Ok(GroupTeachingEventsByType(teachingEvents, quantityPerType))); }
private async Task<IEnumerable<TeachingEvent>> OnlineEventsMatchingRequest(TeachingEventSearchRequest originalRequest) { var request = originalRequest.Clone(te => { te.Radius = null; }); var result = await SearchTeachingEventsAsync(request); return result.Where(te => te.IsOnline && !te.IsVirtual); }
public async void SearchGroupedByType_ValidRequest_ReturnsTeachingEventsByType() { var request = new TeachingEventSearchRequest() { Postcode = "KY12 8FG" }; var mockEvents = MockEvents(); _mockStore.Setup(mock => mock.SearchTeachingEventsAsync(request)).ReturnsAsync(mockEvents); var response = await _controller.SearchGroupedByType(request); var ok = response.Should().BeOfType <OkObjectResult>().Subject; var result = (IEnumerable <TeachingEventsByType>)ok.Value; result.First().TypeId.Should().Be(123); result.First().TeachingEvents.Count().Should().Be(3); result.Last().TypeId.Should().Be(456); result.Last().TeachingEvents.Count().Should().Be(1); _mockLogger.VerifyInformationWasCalled("SearchGroupedByType: KY12 8FG"); _metrics.TeachingEventSearchResults.WithLabels(new[] { request.TypeId.ToString(), request.Radius.ToString() }).Count.Should().Be(1); _metrics.TeachingEventSearchResults.WithLabels(new[] { request.TypeId.ToString(), request.Radius.ToString() }).Count.Should().Be(1); }
public void StatusId_DefaultValue_IsOpenAndClsoedEvents() { var request = new TeachingEventSearchRequest(); var expectedDefaults = new int[] { (int)TeachingEvent.Status.Open, (int)TeachingEvent.Status.Closed }; request.StatusIds.Should().Equal(expectedDefaults); }
public void Clone_WithBlock_ClonesAndCallsBlock() { var request = new TeachingEventSearchRequest() { Radius = 10, TypeId = 123 }; var clone = request.Clone((te) => te.Radius = 100); clone.Radius.Should().Be(100); clone.TypeId.Should().Be(request.TypeId); }
public void Validate_RadiusIsNotNullAndPostcodeIsNull_HasError() { var request = new TeachingEventSearchRequest() { Radius = 10, Postcode = null, }; var result = _validator.TestValidate(request); result.ShouldHaveValidationErrorFor(request => request.Postcode); }
public void Validate_StartBeforeAndNoStartAfter_HasNoError() { var request = new TeachingEventSearchRequest() { StartBefore = DateTime.UtcNow.AddDays(1), StartAfter = null, }; var result = _validator.TestValidate(request); result.ShouldNotHaveAnyValidationErrors(); }
public void Validate_StartAfterLaterThanStartBefore_HasError() { var request = new TeachingEventSearchRequest() { StartAfter = DateTime.UtcNow.AddDays(1), StartBefore = DateTime.UtcNow }; var result = _validator.TestValidate(request); result.ShouldHaveValidationErrorFor(request => request).WithErrorMessage("Start after must be earlier than start before."); }
public async void SearchTeachingEvents_FilteredByDefaultStatusId_ReturnsOpenAndClosedEvents() { await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest(); var result = await _store.SearchTeachingEventsAsync(request); result.Select(e => e.Name).Should() .Contain( new string[] { "Event 1", "Event 2", "Event 3", "Event 4", "Event 5", "Event 6", "Event 7" }); }
public async void SearchTeachingEvents_FilteredByStartBefore_ReturnsMatching() { await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest() { StartBefore = DateTime.UtcNow.AddDays(6) }; var result = await _store.SearchTeachingEventsAsync(request); result.Select(e => e.Name).Should().BeEquivalentTo(new string[] { "Event 7", "Event 2", "Event 4", "Event 1" }, options => options.WithStrictOrdering()); }
public async void SearchTeachingEvents_WithoutFilters_ReturnsAll() { await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest() { }; var result = await _store.SearchTeachingEventsAsync(request); result.Select(e => e.Name).Should().BeEquivalentTo( new string[] { "Event 7", "Event 2", "Event 4", "Event 1", "Event 3", "Event 5", "Event 6" }, options => options.WithStrictOrdering()); }
public async void SearchTeachingEvents_FilteredByRadiusWithFailedPostcodeGeocoding_ReturnsEmpty() { SeedMockLocations(); await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest() { Postcode = "TE7 1NG", Radius = 15 }; var result = await _store.SearchTeachingEventsAsync(request); result.Should().BeEmpty(); }
public async void SearchTeachingEvents_FilteredByRadiusWithOutwardOnlyPostcode_ReturnsMatchingAndOnlineEvents() { SeedMockLocations(); await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest() { Postcode = "KY6", Radius = 15 }; var result = await _store.SearchTeachingEventsAsync(request); result.Select(e => e.Name).Should().BeEquivalentTo(new string[] { "Event 2", "Event 3", "Event 1", "Event 5" }, options => options.WithStrictOrdering()); }
public async void SearchTeachingEvents_FilteredByType_ReturnsMatching() { SeedMockLocations(); await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest() { TypeId = (int)TeachingEvent.EventType.ApplicationWorkshop }; var result = await _store.SearchTeachingEventsAsync(request); result.Select(e => e.Name).Should().BeEquivalentTo(new string[] { "Event 2" }, options => options.WithStrictOrdering()); }
public async void SearchTeachingEvents_FilteredByMultipleStatusIds_ReturnsMatching() { await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest() { StatusIds = new int[] { (int)TeachingEvent.Status.Open, (int)TeachingEvent.Status.Pending } }; var result = await _store.SearchTeachingEventsAsync(request); result.Select(e => e.Name).Should() .Contain( new string[] { "Event 1", "Event 2", "Event 3", "Event 4", "Event 5", "Event 6", "Event 8" }); }
public async void SearchGroupedByType_InvalidRequest_RespondsWithValidationErrors() { var request = new TeachingEventSearchRequest() { Postcode = null }; _controller.ModelState.AddModelError("Postcode", "Postcode must be specified."); var response = await _controller.SearchGroupedByType(request); var badRequest = response.Should().BeOfType <BadRequestObjectResult>().Subject; var errors = badRequest.Value.Should().BeOfType <SerializableError>().Subject; errors.Should().ContainKey("Postcode").WhichValue.Should().BeOfType <string[]>().Which.Should().Contain("Postcode must be specified."); }
public async void SearchTeachingEvents_WithFilters_ExcludesEventNarrowlyOutOfRange() { SeedMockLocations(); await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest() { Postcode = "KY6 2NJ", Radius = 12, TypeId = (int)TeachingEvent.EventType.ApplicationWorkshop, StartAfter = DateTime.UtcNow, StartBefore = DateTime.UtcNow.AddDays(3) }; var result = await _store.SearchTeachingEventsAsync(request); result.Select(e => e.Name).Should().BeEmpty(); }
public async void SearchTeachingEvents_WithFilters_ReturnsEventNarrowlyInRange() { SeedMockLocations(); await SeedMockTeachingEventsAndBuildingsAsync(); var request = new TeachingEventSearchRequest() { Postcode = "KY6 2NJ", Radius = 13, TypeId = (int)TeachingEvent.EventType.ApplicationWorkshop, StartAfter = DateTime.UtcNow, StartBefore = DateTime.UtcNow.AddDays(3) }; var result = await _store.SearchTeachingEventsAsync(request); result.Select(e => e.Name).Should().BeEquivalentTo( new string[] { "Event 2" }, options => options.WithStrictOrdering()); }
public void Validate_WhenValid_HasNoErrors() { var mockPickListItem = new PickListItem { Id = 123 }; _mockStore .Setup(mock => mock.GetPickListItems("msevtmgt_event", "dfe_event_type")) .Returns(new[] { mockPickListItem }.AsQueryable()); var request = new TeachingEventSearchRequest() { Postcode = "KY11 9HF", Radius = 10, TypeId = mockPickListItem.Id, StartAfter = DateTime.UtcNow.AddDays(-1), StartBefore = DateTime.UtcNow.AddDays(1) }; var result = _validator.TestValidate(request); result.IsValid.Should().BeTrue(); }
private async Task <IEnumerable <TeachingEvent> > FilterTeachingEventsByRadius( IQueryable <TeachingEvent> teachingEvents, TeachingEventSearchRequest request) { var origin = await CoordinateForPostcode(request.Postcode); // If we can't locate them, return no results. if (origin == null) { return(new List <TeachingEvent>()); } // Exclude events we don't have a location for. teachingEvents = teachingEvents.Where(te => te.Building != null && te.Building.Coordinate != null); // Filter events by distance in database var result = teachingEvents.Where(teachingEvent => teachingEvent.Building.Coordinate.Distance(origin) < request.RadiusInKm() * 1000).AsEnumerable(); // We need to include the various 'online' event types in distance based search results. result = result.Concat(await OnlineEventsMatchingRequest(request)); return(result); }