From 4c1330dd0cea3a32eafc0f9c9d49e4c0f941d44d Mon Sep 17 00:00:00 2001 From: killij <51908793+killij@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:52:35 +0000 Subject: [PATCH] fix: Azure throws an Exception when the index doesn't exist Why do companies do this? Just imagine if SQL Server threw an exception every time a query didn't return any results. Not finding something IS NOT EXCEPTIONAL. --- ...ndexerTest.cs => ResourcesIndexerTests.cs} | 41 +++++++++++++++---- .../Core/ResourcesIndexer.cs | 29 ++++++++++--- 2 files changed, 58 insertions(+), 12 deletions(-) rename src/Childrens-Social-Care-CPD-Indexer.Tests/Core/{ResourcesIndexerTest.cs => ResourcesIndexerTests.cs} (79%) diff --git a/src/Childrens-Social-Care-CPD-Indexer.Tests/Core/ResourcesIndexerTest.cs b/src/Childrens-Social-Care-CPD-Indexer.Tests/Core/ResourcesIndexerTests.cs similarity index 79% rename from src/Childrens-Social-Care-CPD-Indexer.Tests/Core/ResourcesIndexerTest.cs rename to src/Childrens-Social-Care-CPD-Indexer.Tests/Core/ResourcesIndexerTests.cs index fd27f3e..199e241 100644 --- a/src/Childrens-Social-Care-CPD-Indexer.Tests/Core/ResourcesIndexerTest.cs +++ b/src/Childrens-Social-Care-CPD-Indexer.Tests/Core/ResourcesIndexerTests.cs @@ -1,13 +1,42 @@ using Azure; +using Azure.Core; using Azure.Search.Documents; using Azure.Search.Documents.Indexes; using Azure.Search.Documents.Indexes.Models; using Childrens_Social_Care_CPD_Indexer.Core; using Microsoft.Extensions.Logging; +using NSubstitute.ExceptionExtensions; +using System.Diagnostics.CodeAnalysis; namespace Childrens_Social_Care_CPD_Indexer.Tests.Core; -public class ResourcesIndexerTest +internal sealed class MockResponse : Response +{ + public override int Status => 404; + public override string ReasonPhrase => string.Empty; + + public override Stream? ContentStream + { + get => new MemoryStream(); + set => throw new NotImplementedException(); + } + public override string ClientRequestId + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public override void Dispose() => throw new NotImplementedException(); + protected override bool ContainsHeader(string name) => false; + protected override IEnumerable EnumerateHeaders() => Array.Empty(); + protected override bool TryGetHeader(string name, [NotNullWhen(true)] out string? value) { + value = null; + return false; + } + protected override bool TryGetHeaderValues(string name, [NotNullWhen(true)] out IEnumerable? values) => throw new NotImplementedException(); +} + +public class ResourcesIndexerTests { private ILogger _logger; private SearchIndexClient _client; @@ -28,9 +57,8 @@ public void Setup() public async Task DeleteIndexAsync_Skips_Deletion_If_Index_Does_Not_Exist() { // arrange - var response = Substitute.For>(); - response.HasValue.Returns(false); - _client.GetIndexAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(response)); + var exception = new RequestFailedException(new MockResponse()); + _client.GetIndexAsync(Arg.Any(), Arg.Any()).Throws(exception); // act await _sut.DeleteIndexAsync("foo"); @@ -77,9 +105,8 @@ public async Task DeleteIndexAsync_Logs_Failure_To_Delete_Index() public async Task CreateIndexAsync_Creates_The_Index() { // arrange - var getIndexResult = Substitute.For>(); - getIndexResult.HasValue.Returns(false); - _client.GetIndexAsync(Arg.Any(), Arg.Any()).Returns(Task.FromResult(getIndexResult)); + var exception = new RequestFailedException(new MockResponse()); + _client.GetIndexAsync(Arg.Any(), Arg.Any()).Throws(exception); SearchIndex? searchIndex = null; await _client.CreateIndexAsync(Arg.Do(x => searchIndex = x), Arg.Any()); diff --git a/src/Childrens-Social-Care-CPD-Indexer/Core/ResourcesIndexer.cs b/src/Childrens-Social-Care-CPD-Indexer/Core/ResourcesIndexer.cs index 1c3124d..d8e6841 100644 --- a/src/Childrens-Social-Care-CPD-Indexer/Core/ResourcesIndexer.cs +++ b/src/Childrens-Social-Care-CPD-Indexer/Core/ResourcesIndexer.cs @@ -1,14 +1,33 @@ -using Azure.Search.Documents.Indexes; +using Azure; +using Azure.Search.Documents.Indexes; using Azure.Search.Documents.Indexes.Models; namespace Childrens_Social_Care_CPD_Indexer.Core; internal class ResourcesIndexer(SearchIndexClient searchIndexClient, IDocumentFetcher documentFetcher, ILogger logger): IResourcesIndexer { + private async Task IndexExistsAsync(string indexName, CancellationToken cancellationToken) + { + try + { + var index = await searchIndexClient.GetIndexAsync(indexName, cancellationToken); + return index.HasValue; + } + catch (RequestFailedException rf) + { + if (rf.Status == 404) + { + return false; + } + + throw; + } + } + public async Task CreateIndexAsync(string indexName, CancellationToken cancellationToken = default) { - var index = await searchIndexClient.GetIndexAsync(indexName, cancellationToken); - if (index.HasValue) + var indexExists = await IndexExistsAsync(indexName, cancellationToken); + if (indexExists) { logger.LogInformation("Index already exists, skipping creation."); return; @@ -25,8 +44,8 @@ public async Task CreateIndexAsync(string indexName, CancellationToken cancellat public async Task DeleteIndexAsync(string indexName, CancellationToken cancellationToken = default) { logger.LogInformation("Deleting index..."); - var index = await searchIndexClient.GetIndexAsync(indexName, cancellationToken); - if (index.HasValue) + var indexExists = await IndexExistsAsync(indexName, cancellationToken); + if (indexExists) { var deleteResponse = await searchIndexClient.DeleteIndexAsync(indexName, cancellationToken); if (deleteResponse.IsError)