public async Task QueryPublishedFundingDelegatesToQueryBuilderAndExecutesCosmosDbQueryItCreates() { IEnumerable <string> fundingStreamIds = EnumerableFor(NewRandomString(), NewRandomString()); IEnumerable <string> fundingPeriodIds = EnumerableFor(NewRandomString(), NewRandomString(), NewRandomString()); IEnumerable <string> groupingReasons = EnumerableFor(NewRandomString()); IEnumerable <string> variationReasons = EnumerableFor(NewRandomString()); int top = NewRandomNumber(); int pageRef = NewRandomNumber(); IEnumerable <PublishedFundingIndex> expectedResults = new PublishedFundingIndex[0]; CosmosDbQuery query = new CosmosDbQuery(); GivenTheCosmosDbQuery(fundingStreamIds, fundingPeriodIds, groupingReasons, variationReasons, top, pageRef, query); AndTheDynamicResultsForTheQuery(query, expectedResults); IEnumerable <PublishedFundingIndex> actualResults = await _repository.QueryPublishedFunding(fundingStreamIds, fundingPeriodIds, groupingReasons, variationReasons, top, pageRef, 0); actualResults .Should() .BeEquivalentTo(expectedResults); }
private async Task CachePublishedFundingDocument(PublishedFundingIndex publishedFundingIndex, SemaphoreSlim cacheThrottle) { try { await _retrievalService.GetFundingFeedDocument(publishedFundingIndex.DocumentPath, true); } finally { cacheThrottle.Release(); } }
public async Task GetFundingByFundingResultId_GivenResultFound_ReturnsContentResult() { //Arrange string resultId = "12345"; string documentPath = "Round the ragged rocks the ragged rascal ran"; string fundingDocument = "Now is the time for all good men to come to the aid of the party."; PublishedFundingIndex publishedFundingIndex = CreatePublishedFundingResult(); publishedFundingIndex.DocumentPath = documentPath; ISearchRepository <PublishedFundingIndex> searchRepository = CreateSearchRepository(); searchRepository .SearchById(Arg.Is(resultId)) .Returns(publishedFundingIndex); IPublishedFundingRetrievalService publishedFundingRetrievalService = Substitute.For <IPublishedFundingRetrievalService>(); publishedFundingRetrievalService .GetFundingFeedDocument(documentPath) .Returns(fundingDocument); FundingFeedItemByIdService service = CreateService(searchRepository: searchRepository, publishedFundingRetrievalService: publishedFundingRetrievalService); //Act IActionResult result = await service.GetFundingByFundingResultId(resultId); //Assert result .Should().BeOfType <ContentResult>() .Which .StatusCode .Should().Be((int)HttpStatusCode.OK); await searchRepository .Received(1) .SearchById(resultId); await publishedFundingRetrievalService .Received(1) .GetFundingFeedDocument(documentPath); ContentResult contentResult = result as ContentResult; contentResult.Content .Should().Be(fundingDocument); contentResult.ContentType .Should().Be("application/json"); }
private void AddAtomEntry(HttpRequest request, string fundingTrimmedRequestPath, PublishedFundingIndex feedIndex, ConcurrentDictionary <string, object> feedContentResults, AtomFeed <AtomEntry> atomFeed) { string link = $"{request.Scheme}://{request.Host.Value}{fundingTrimmedRequestPath}/byId/{feedIndex.Id}"; feedContentResults.TryGetValue(feedIndex.Id, out object contentsObject); atomFeed.AtomEntry.Add(new AtomEntry { Id = link, Title = feedIndex.Id, Summary = feedIndex.Id, Published = feedIndex.StatusChangedDate, Updated = feedIndex.StatusChangedDate.Value, Version = feedIndex.Version, Link = new CalculateFunding.Models.External.AtomItems.AtomLink(link, "Funding"), Content = contentsObject, }); }
public async Task <IActionResult> GetFundingByFundingResultId(string id) { Guard.IsNullOrWhiteSpace(id, nameof(id)); PublishedFundingIndex fundingIndexedDocument = await _fundingSearchRepositoryPolicy.ExecuteAsync(() => _fundingSearchRepository.SearchById(id)); if (fundingIndexedDocument == null) { return(new NotFoundResult()); } string fundingDocument = await _publishedFundingRetrievalService.GetFundingFeedDocument(fundingIndexedDocument.DocumentPath); if (string.IsNullOrWhiteSpace(fundingDocument)) { _logger.Error("Failed to find blob with id {id} and document path: {documentPath}", id, fundingIndexedDocument.DocumentPath); return(new NotFoundResult()); } return(new ContentResult() { Content = fundingDocument, ContentType = "application/json", StatusCode = (int)HttpStatusCode.OK }); }
public async Task GetFundingByFundingResultId_GivenEmptyResultFound_ReturnsNotFoundResult(string document) { //Arrange string resultId = "12345"; string documentPath = "Round the ragged rocks the ragged rascal ran"; PublishedFundingIndex publishedFundingIndex = CreatePublishedFundingResult(); publishedFundingIndex.DocumentPath = documentPath; ISearchRepository <PublishedFundingIndex> searchRepository = CreateSearchRepository(); searchRepository .SearchById(Arg.Is(resultId)) .Returns(publishedFundingIndex); IPublishedFundingRetrievalService publishedFundingRetrievalService = Substitute.For <IPublishedFundingRetrievalService>(); publishedFundingRetrievalService .GetFundingFeedDocument(documentPath) .Returns(document); FundingFeedItemByIdService service = CreateService(searchRepository: searchRepository, publishedFundingRetrievalService: publishedFundingRetrievalService); //Act IActionResult result = await service.GetFundingByFundingResultId(resultId); //Assert result .Should().BeOfType <NotFoundResult>(); await searchRepository .Received(1) .SearchById(resultId); }
public async Task GetNotifications_GivenAQueryStringForWhichThereAreResults_ReturnsAtomFeedWithCorrectLinks() { //Arrange string fundingFeedDocument = JsonConvert.SerializeObject(new { FundingStreamId = "PES" }); int pageRef = 2; int pageSize = 3; List <PublishedFundingIndex> searchFeedEntries = CreateFeedIndexes().ToList(); SearchFeedV3 <PublishedFundingIndex> feeds = new SearchFeedV3 <PublishedFundingIndex> { PageRef = pageRef, Top = 2, TotalCount = 8, Entries = searchFeedEntries }; PublishedFundingIndex firstFeedItem = feeds.Entries.ElementAt(0); IFundingFeedSearchService feedsSearchService = CreateSearchService(); feedsSearchService .GetFeedsV3(Arg.Is(pageRef), Arg.Is(pageSize)) .ReturnsForAnyArgs(feeds); IPublishedFundingRetrievalService publishedFundingRetrievalService = Substitute.For <IPublishedFundingRetrievalService>(); publishedFundingRetrievalService .GetFundingFeedDocument(Arg.Any <string>()) .Returns(fundingFeedDocument); Mock <IExternalEngineOptions> externalEngineOptions = new Mock <IExternalEngineOptions>(); externalEngineOptions .Setup(_ => _.BlobLookupConcurrencyCount) .Returns(10); FundingFeedService service = CreateService( searchService: feedsSearchService, publishedFundingRetrievalService: publishedFundingRetrievalService, externalEngineOptions.Object); IHeaderDictionary headerDictionary = new HeaderDictionary { { "Accept", new StringValues("application/json") } }; IQueryCollection queryStringValues = new QueryCollection(new Dictionary <string, StringValues> { { "pageRef", new StringValues(pageRef.ToString()) }, { "allocationStatuses", new StringValues("Published,Approved") }, { "pageSize", new StringValues(pageSize.ToString()) } }); string scheme = "https"; string path = "/api/v3/fundings/notifications"; string host = "wherever.naf:12345"; string queryString = "?pageSize=2"; HttpRequest request = Substitute.For <HttpRequest>(); request.Scheme.Returns(scheme); request.Path.Returns(new PathString(path)); request.Host.Returns(new HostString(host)); request.QueryString.Returns(new QueryString(queryString)); request.Headers.Returns(headerDictionary); request.Query.Returns(queryStringValues); //Act IActionResult result = await service.GetFunding(request, pageRef : pageRef, pageSize : pageSize); //Assert result .Should() .BeOfType <OkObjectResult>(); OkObjectResult contentResult = result as OkObjectResult; Models.External.V3.AtomItems.AtomFeed <AtomEntry> atomFeed = contentResult.Value as Models.External.V3.AtomItems.AtomFeed <AtomEntry>; atomFeed .Should() .NotBeNull(); atomFeed.Id.Should().NotBeEmpty(); atomFeed.Title.Should().Be("Calculate Funding Service Funding Feed"); atomFeed.Author.Name.Should().Be("Calculate Funding Service"); atomFeed.Link.First(m => m.Rel == "next-archive").Href.Should().Be($"{scheme}://{host}{path}/3{queryString}"); atomFeed.Link.First(m => m.Rel == "prev-archive").Href.Should().Be($"{scheme}://{host}{path}/1{queryString}"); atomFeed.Link.First(m => m.Rel == "self").Href.Should().Be($"{scheme}://{host}{path}{queryString}"); atomFeed.Link.First(m => m.Rel == "current").Href.Should().Be($"{scheme}://{host}{path}/2{queryString}"); atomFeed.AtomEntry.Count.Should().Be(3); for (int i = 0; i < 3; i++) { string text = $"id-{i + 1}"; atomFeed.AtomEntry.ElementAt(i).Id.Should().Be($"{scheme}://{host}/api/v3/fundings/byId/{text}"); atomFeed.AtomEntry.ElementAt(i).Title.Should().Be(text); atomFeed.AtomEntry.ElementAt(i).Summary.Should().Be(text); atomFeed.AtomEntry.ElementAt(i).Content.Should().NotBeNull(); } JObject content = atomFeed.AtomEntry.ElementAt(0).Content as JObject; content.TryGetValue("FundingStreamId", out JToken token); ((JValue)token).Value <string>().Should().Be("PES"); await feedsSearchService .Received(1) .GetFeedsV3(pageRef, pageSize, null, null, null); await publishedFundingRetrievalService .Received(searchFeedEntries.Count) .GetFundingFeedDocument(Arg.Any <string>()); foreach (PublishedFundingIndex index in searchFeedEntries) { await publishedFundingRetrievalService .Received(1) .GetFundingFeedDocument(index.DocumentPath); } }
public void SearchIndexTest_GivenSearchSchemasAndModels_EnsureFieldsMatch() { // Arrange IList <string> ErrorLog = new List <string>(); DatasetDefinitionIndex datasetDefinitionIndex = new DatasetDefinitionIndex(); ProvidersIndex providersIndex = new ProvidersIndex(); PublishedFundingIndex publishedfundingindex = new PublishedFundingIndex(); PublishedProviderIndex publishedProviderIndex = new PublishedProviderIndex(); SpecificationIndex specificationindex = new SpecificationIndex(); TemplateIndex templateIndex = new TemplateIndex(); IEnumerable <Type> searchIndexTypes = GetTypesWithSearchIndexAttribute(); IEnumerable <string> indexNames = Directory .GetDirectories(searchIndexDirectoryPath, "*index", SearchOption.TopDirectoryOnly) .Select(m => new DirectoryInfo(m).Name); //Act foreach (string indexName in indexNames) { try { string jsonFilePath = $@"{searchIndexDirectoryPath}\{indexName}\{indexName}.json"; string jsonText = File.ReadAllText(jsonFilePath, Encoding.UTF8); SearchIndexSchema searchIndexSchema = JsonConvert.DeserializeObject <SearchIndexSchema>(jsonText); if (searchIndexSchema?.Name == null) { ErrorLog.Add(string.IsNullOrWhiteSpace(jsonText) ? $"{indexName} json is blank" : $"{indexName} json name is not available"); } else if (searchIndexSchema.Name != indexName) { ErrorLog.Add($"Expected to find index { indexName }, but found { searchIndexSchema.Name }"); } else { Type searchIndexType = searchIndexTypes .FirstOrDefault(m => m.CustomAttributes .FirstOrDefault(p => p.NamedArguments .Any(n => n.TypedValue.Value.ToString() == searchIndexSchema.Name)) != null); IEnumerable <string> searchIndexProperties = searchIndexType.GetProperties() .Select(m => m.CustomAttributes .FirstOrDefault(a => a.AttributeType.Name == "JsonPropertyAttribute") ?.ConstructorArguments[0].Value.ToString().ToLower()) .Where(p => p != null); IEnumerable <string> searchIndexJsonProperties = searchIndexSchema.Fields .Select(m => m.Name.ToLower()) .Where(p => p != null); if (!searchIndexProperties.Any()) { ErrorLog.Add($"Index {indexName}: The model contains no properties"); } else if (!searchIndexJsonProperties.Any()) { ErrorLog.Add($"Index {indexName}: The json contains no properties"); } else { IEnumerable <string> notInJson = searchIndexProperties.Except(searchIndexJsonProperties); if (notInJson.Any()) { string properties = string.Join(",", notInJson); ErrorLog.Add($"Index {indexName}: The model contains the following properties not found in the json schema ({properties})"); } IEnumerable <string> notInModel = searchIndexJsonProperties.Except(searchIndexProperties); if (notInModel.Any()) { string properties = string.Join(",", notInModel); ErrorLog.Add($"Index {indexName}: The json schema contains the following properties not found in the model ({properties})"); } } } } catch (Exception e) { ErrorLog.Add($"Unexpected error checking '{indexName}': {e.Message}{Environment.NewLine}{ e.StackTrace }"); } } //Assert if (ErrorLog.Any()) { Assert.Fail(string.Join(Environment.NewLine, ErrorLog)); } }
public void SearchIndexTest_GivenSearchSchemasAndModels_EnsuresAttributesMatch() { //Arrange IList <string> ErrorLog = new List <string>(); DatasetDefinitionIndex datasetDefinitionIndex = new DatasetDefinitionIndex(); ProvidersIndex providersIndex = new ProvidersIndex(); PublishedFundingIndex publishedfundingindex = new PublishedFundingIndex(); PublishedProviderIndex publishedProviderIndex = new PublishedProviderIndex(); SpecificationIndex specificationindex = new SpecificationIndex(); TemplateIndex templateIndex = new TemplateIndex(); IEnumerable <Type> searchIndexTypes = GetTypesWithSearchIndexAttribute(); IEnumerable <string> indexNames = Directory .GetDirectories(searchIndexDirectoryPath, "*index", SearchOption.TopDirectoryOnly) .Select(m => new DirectoryInfo(m).Name); //Act foreach (string indexName in indexNames) { try { string jsonFilePath = $@"{searchIndexDirectoryPath}\{indexName}\{indexName}.json"; string jsonText = File.ReadAllText(jsonFilePath, Encoding.UTF8); SearchIndexSchema searchIndexSchema = JsonConvert.DeserializeObject <SearchIndexSchema>(jsonText); if (searchIndexSchema?.Name == null) { ErrorLog.Add(string.IsNullOrWhiteSpace(jsonText) ? $"{indexName} json is blank" : $"{indexName} json name is not available"); } else if (searchIndexSchema.Name != indexName) { ErrorLog.Add($"Expected to find index {indexName}, but found {searchIndexSchema.Name}"); } else { Type searchIndexType = searchIndexTypes.FirstOrDefault(m => m.CustomAttributes.FirstOrDefault(p => p.NamedArguments.Any(n => n.TypedValue.Value.ToString() == searchIndexSchema.Name)) != null); IEnumerable <PropertyInfo> searchIndexProperties = searchIndexType.GetProperties(); foreach (SearchIndexField searchIndexField in searchIndexSchema.Fields) { PropertyInfo matchedProperty = searchIndexProperties.FirstOrDefault(m => string.Equals(m.CustomAttributes .SingleOrDefault(x => x.AttributeType.Name == "JsonPropertyAttribute") ?.ConstructorArguments[0].ToString().Replace("\"", "") ?? m.Name, searchIndexField.Name, StringComparison.InvariantCultureIgnoreCase)); if (matchedProperty == null) { ErrorLog.Add($"{indexName}: {searchIndexField.Name} did not match any properties"); } else { CheckIndexFieldTypes(matchedProperty, searchIndexField, ErrorLog, indexName); CheckIndexAttributes(matchedProperty, searchIndexField, ErrorLog, indexName); } } } } catch (Exception e) { ErrorLog.Add($"Unexpected error checking '{indexName}': {e.Message}{Environment.NewLine}{e.StackTrace}"); } } //Assert if (ErrorLog.Any()) { Assert.Fail(string.Join(Environment.NewLine, ErrorLog)); } }