diff --git a/docs/testing.md b/docs/testing.md index 1a2c24ec..af151c70 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -19,8 +19,10 @@ IGeoLocationApiClient ├── .GeoLookup (IVersionedGeoLookupApi) │ ├── .V1 (IGeoLookupApi) → GetGeoLocation, GetGeoLocations, DeleteMetadata │ └── .V1_1 (IGeoLookupApi) → GetCityGeoLocation, GetInsightsGeoLocation -├── .ApiInfo (IApiInfoApi) → GetApiInfo -└── .ApiHealth (IApiHealthApi) → CheckHealth +├── .ApiInfo (IVersionedApiInfoApi) +│ └── .V1 (IApiInfoApi) → GetApiInfo +└── .ApiHealth (IVersionedApiHealthApi) + └── .V1 (IApiHealthApi) → CheckHealth ``` Without the testing package, each test needs 3+ levels of nested mocks just to call a single method. Additionally, all DTO properties use `internal set`, so external consumers cannot construct DTOs with custom values. diff --git a/src/MX.GeoLocation.Abstractions.V1/Interfaces/IVersionedGeoLookupApi.cs b/src/MX.GeoLocation.Abstractions.V1/Interfaces/IVersionedGeoLookupApi.cs deleted file mode 100644 index 0a68f469..00000000 --- a/src/MX.GeoLocation.Abstractions.V1/Interfaces/IVersionedGeoLookupApi.cs +++ /dev/null @@ -1,20 +0,0 @@ -using MX.GeoLocation.Abstractions.Interfaces.V1; - -namespace MX.GeoLocation.Abstractions.Interfaces -{ - /// - /// Provides versioned access to geo lookup APIs - /// - public interface IVersionedGeoLookupApi - { - /// - /// Gets the V1 implementation of the geo lookup API - /// - V1.IGeoLookupApi V1 { get; } - - /// - /// Gets the V1.1 implementation of the geo lookup API - /// - V1_1.IGeoLookupApi V1_1 { get; } - } -} diff --git a/src/MX.GeoLocation.Abstractions.V1/README.md b/src/MX.GeoLocation.Abstractions.V1/README.md index e924addf..de9bab79 100644 --- a/src/MX.GeoLocation.Abstractions.V1/README.md +++ b/src/MX.GeoLocation.Abstractions.V1/README.md @@ -35,15 +35,7 @@ public interface IGeoLookupApi ### IVersionedGeoLookupApi -Wraps both API versions for version-aware consumers: - -```csharp -public interface IVersionedGeoLookupApi -{ - V1.IGeoLookupApi V1 { get; } - V1_1.IGeoLookupApi V1_1 { get; } -} -``` +> **Note:** Version selector interfaces (`IVersionedGeoLookupApi`, `IVersionedApiHealthApi`, `IVersionedApiInfoApi`) are defined in the `MX.GeoLocation.Api.Client.V1` package, not in this abstractions package. ## Data Models diff --git a/src/MX.GeoLocation.Api.Client.Testing.Tests/FakeGeoLocationApiClientTests.cs b/src/MX.GeoLocation.Api.Client.Testing.Tests/FakeGeoLocationApiClientTests.cs index d9927630..7512d0a2 100644 --- a/src/MX.GeoLocation.Api.Client.Testing.Tests/FakeGeoLocationApiClientTests.cs +++ b/src/MX.GeoLocation.Api.Client.Testing.Tests/FakeGeoLocationApiClientTests.cs @@ -38,7 +38,7 @@ public async Task ApiInfo_DelegatesToInfoFake() var client = new FakeGeoLocationApiClient(); client.InfoApi.WithInfo(GeoLocationDtoFactory.CreateApiInfo(buildVersion: "2.0.0.1")); - var result = await client.ApiInfo.GetApiInfo(); + var result = await client.ApiInfo.V1.GetApiInfo(); Assert.Equal("2.0.0.1", result.Result!.Data!.BuildVersion); } @@ -49,7 +49,7 @@ public async Task ApiHealth_DelegatesToHealthFake() var client = new FakeGeoLocationApiClient(); client.HealthApi.WithStatusCode(System.Net.HttpStatusCode.ServiceUnavailable); - var result = await client.ApiHealth.CheckHealth(); + var result = await client.ApiHealth.V1.CheckHealth(); Assert.Equal(System.Net.HttpStatusCode.ServiceUnavailable, result.StatusCode); } diff --git a/src/MX.GeoLocation.Api.Client.Testing.Tests/ServiceCollectionExtensionsTests.cs b/src/MX.GeoLocation.Api.Client.Testing.Tests/ServiceCollectionExtensionsTests.cs index 5244481b..78ba1d2b 100644 --- a/src/MX.GeoLocation.Api.Client.Testing.Tests/ServiceCollectionExtensionsTests.cs +++ b/src/MX.GeoLocation.Api.Client.Testing.Tests/ServiceCollectionExtensionsTests.cs @@ -26,6 +26,8 @@ public void AddFakeGeoLocationApiClient_RegistersAllServices() Assert.NotNull(provider.GetRequiredService()); Assert.NotNull(provider.GetRequiredService()); + Assert.NotNull(provider.GetRequiredService()); + Assert.NotNull(provider.GetRequiredService()); Assert.NotNull(provider.GetRequiredService()); Assert.NotNull(provider.GetRequiredService()); Assert.NotNull(provider.GetRequiredService()); diff --git a/src/MX.GeoLocation.Api.Client.Testing/FakeGeoLocationApiClient.cs b/src/MX.GeoLocation.Api.Client.Testing/FakeGeoLocationApiClient.cs index c2c4fa05..d3f4ce0a 100644 --- a/src/MX.GeoLocation.Api.Client.Testing/FakeGeoLocationApiClient.cs +++ b/src/MX.GeoLocation.Api.Client.Testing/FakeGeoLocationApiClient.cs @@ -6,6 +6,32 @@ namespace MX.GeoLocation.Api.Client.Testing; +/// +/// In-memory fake of for tests. +/// +public class FakeVersionedApiHealthApi : IVersionedApiHealthApi +{ + public FakeVersionedApiHealthApi(FakeApiHealthApi v1) + { + V1 = v1; + } + + public IApiHealthApi V1 { get; } +} + +/// +/// In-memory fake of for tests. +/// +public class FakeVersionedApiInfoApi : IVersionedApiInfoApi +{ + public FakeVersionedApiInfoApi(FakeApiInfoApi v1) + { + V1 = v1; + } + + public IApiInfoApi V1 { get; } +} + /// /// In-memory fake of composing the V1 and V1.1 fakes. /// @@ -61,15 +87,19 @@ public class FakeGeoLocationApiClient : IGeoLocationApiClient public FakeApiHealthApi HealthApi { get; } = new(); private readonly Lazy _geoLookup; + private readonly Lazy _apiHealth; + private readonly Lazy _apiInfo; public FakeGeoLocationApiClient() { _geoLookup = new Lazy(() => new FakeVersionedGeoLookupApi(V1Lookup, V1_1Lookup)); + _apiHealth = new Lazy(() => new FakeVersionedApiHealthApi(HealthApi)); + _apiInfo = new Lazy(() => new FakeVersionedApiInfoApi(InfoApi)); } public IVersionedGeoLookupApi GeoLookup => _geoLookup.Value; - public IApiInfoApi ApiInfo => InfoApi; - public IApiHealthApi ApiHealth => HealthApi; + public IVersionedApiInfoApi ApiInfo => _apiInfo.Value; + public IVersionedApiHealthApi ApiHealth => _apiHealth.Value; /// /// Resets all fakes to their initial state, clearing configured responses, diff --git a/src/MX.GeoLocation.Api.Client.Testing/ServiceCollectionExtensions.cs b/src/MX.GeoLocation.Api.Client.Testing/ServiceCollectionExtensions.cs index dc287764..e35d7095 100644 --- a/src/MX.GeoLocation.Api.Client.Testing/ServiceCollectionExtensions.cs +++ b/src/MX.GeoLocation.Api.Client.Testing/ServiceCollectionExtensions.cs @@ -38,6 +38,8 @@ public static IServiceCollection AddFakeGeoLocationApiClient( services.RemoveAll(); services.RemoveAll(); + services.RemoveAll(); + services.RemoveAll(); services.RemoveAll(); services.RemoveAll(); services.RemoveAll(); @@ -45,6 +47,8 @@ public static IServiceCollection AddFakeGeoLocationApiClient( services.AddSingleton(fakeClient); services.AddSingleton(fakeClient.GeoLookup); + services.AddSingleton(fakeClient.ApiHealth); + services.AddSingleton(fakeClient.ApiInfo); services.AddSingleton(fakeClient.V1Lookup); services.AddSingleton(fakeClient.V1_1Lookup); services.AddSingleton(fakeClient.InfoApi); diff --git a/src/MX.GeoLocation.Api.Client.V1/Api/ApiHealthApi.cs b/src/MX.GeoLocation.Api.Client.V1/Api/V1/ApiHealthApi.cs similarity index 100% rename from src/MX.GeoLocation.Api.Client.V1/Api/ApiHealthApi.cs rename to src/MX.GeoLocation.Api.Client.V1/Api/V1/ApiHealthApi.cs diff --git a/src/MX.GeoLocation.Api.Client.V1/Api/ApiInfoApi.cs b/src/MX.GeoLocation.Api.Client.V1/Api/V1/ApiInfoApi.cs similarity index 100% rename from src/MX.GeoLocation.Api.Client.V1/Api/ApiInfoApi.cs rename to src/MX.GeoLocation.Api.Client.V1/Api/V1/ApiInfoApi.cs diff --git a/src/MX.GeoLocation.Api.Client.V1/VersionedGeoLookupApi.cs b/src/MX.GeoLocation.Api.Client.V1/ApiVersionSelectorImplementations.cs similarity index 51% rename from src/MX.GeoLocation.Api.Client.V1/VersionedGeoLookupApi.cs rename to src/MX.GeoLocation.Api.Client.V1/ApiVersionSelectorImplementations.cs index 5ee2ddfa..e586e413 100644 --- a/src/MX.GeoLocation.Api.Client.V1/VersionedGeoLookupApi.cs +++ b/src/MX.GeoLocation.Api.Client.V1/ApiVersionSelectorImplementations.cs @@ -3,28 +3,36 @@ namespace MX.GeoLocation.Api.Client.V1 { - /// - /// Provides version-specific access to the GeoLookup API endpoints - /// + // Implementation classes for version selectors public class VersionedGeoLookupApi : IVersionedGeoLookupApi { - /// - /// Initializes a new instance of the class - /// public VersionedGeoLookupApi(IGeoLookupApi v1, Abstractions.Interfaces.V1_1.IGeoLookupApi v1_1) { V1 = v1; V1_1 = v1_1; } - /// - /// Gets the V1 GeoLookup API implementation - /// public IGeoLookupApi V1 { get; } - - /// - /// Gets the V1.1 GeoLookup API implementation - /// public Abstractions.Interfaces.V1_1.IGeoLookupApi V1_1 { get; } } + + public class VersionedApiHealthApi : IVersionedApiHealthApi + { + public VersionedApiHealthApi(IApiHealthApi v1) + { + V1 = v1; + } + + public IApiHealthApi V1 { get; } + } + + public class VersionedApiInfoApi : IVersionedApiInfoApi + { + public VersionedApiInfoApi(IApiInfoApi v1) + { + V1 = v1; + } + + public IApiInfoApi V1 { get; } + } } diff --git a/src/MX.GeoLocation.Api.Client.V1/ApiVersionSelectors.cs b/src/MX.GeoLocation.Api.Client.V1/ApiVersionSelectors.cs new file mode 100644 index 00000000..f5c1a9e0 --- /dev/null +++ b/src/MX.GeoLocation.Api.Client.V1/ApiVersionSelectors.cs @@ -0,0 +1,22 @@ +using MX.GeoLocation.Abstractions.Interfaces; +using MX.GeoLocation.Abstractions.Interfaces.V1; + +namespace MX.GeoLocation.Api.Client.V1 +{ + // Version selectors for GeoLookup API (V1 and V1.1) + public interface IVersionedGeoLookupApi + { + IGeoLookupApi V1 { get; } + Abstractions.Interfaces.V1_1.IGeoLookupApi V1_1 { get; } + } + + public interface IVersionedApiHealthApi + { + IApiHealthApi V1 { get; } + } + + public interface IVersionedApiInfoApi + { + IApiInfoApi V1 { get; } + } +} diff --git a/src/MX.GeoLocation.Api.Client.V1/GeoLocationApiClient.cs b/src/MX.GeoLocation.Api.Client.V1/GeoLocationApiClient.cs index df5692a7..3ad80e03 100644 --- a/src/MX.GeoLocation.Api.Client.V1/GeoLocationApiClient.cs +++ b/src/MX.GeoLocation.Api.Client.V1/GeoLocationApiClient.cs @@ -1,6 +1,4 @@ -using MX.GeoLocation.Abstractions.Interfaces; - -namespace MX.GeoLocation.Api.Client.V1 +namespace MX.GeoLocation.Api.Client.V1 { /// /// Implementation of the GeoLocation API client that provides access to versioned API endpoints @@ -11,9 +9,9 @@ public class GeoLocationApiClient : IGeoLocationApiClient /// Initializes a new instance of the class /// /// The versioned GeoLookup API - /// The API info endpoint - /// The API health endpoint - public GeoLocationApiClient(IVersionedGeoLookupApi versionedGeoLookupApi, IApiInfoApi apiInfoApi, IApiHealthApi apiHealthApi) + /// The versioned API info endpoint + /// The versioned API health endpoint + public GeoLocationApiClient(IVersionedGeoLookupApi versionedGeoLookupApi, IVersionedApiInfoApi apiInfoApi, IVersionedApiHealthApi apiHealthApi) { GeoLookup = versionedGeoLookupApi; ApiInfo = apiInfoApi; @@ -26,13 +24,13 @@ public GeoLocationApiClient(IVersionedGeoLookupApi versionedGeoLookupApi, IApiIn public IVersionedGeoLookupApi GeoLookup { get; } /// - /// Gets the API info endpoint + /// Gets the versioned API info endpoint /// - public IApiInfoApi ApiInfo { get; } + public IVersionedApiInfoApi ApiInfo { get; } /// - /// Gets the API health endpoint + /// Gets the versioned API health endpoint /// - public IApiHealthApi ApiHealth { get; } + public IVersionedApiHealthApi ApiHealth { get; } } } \ No newline at end of file diff --git a/src/MX.GeoLocation.Api.Client.V1/IGeoLocationApiClient.cs b/src/MX.GeoLocation.Api.Client.V1/IGeoLocationApiClient.cs index 910f74ab..c2293ec6 100644 --- a/src/MX.GeoLocation.Api.Client.V1/IGeoLocationApiClient.cs +++ b/src/MX.GeoLocation.Api.Client.V1/IGeoLocationApiClient.cs @@ -1,6 +1,4 @@ -using MX.GeoLocation.Abstractions.Interfaces; - -namespace MX.GeoLocation.Api.Client.V1 +namespace MX.GeoLocation.Api.Client.V1 { /// /// Interface for the GeoLocation API client @@ -13,13 +11,13 @@ public interface IGeoLocationApiClient IVersionedGeoLookupApi GeoLookup { get; } /// - /// Gets the API info endpoint + /// Gets the versioned API info endpoint /// - IApiInfoApi ApiInfo { get; } + IVersionedApiInfoApi ApiInfo { get; } /// - /// Gets the API health endpoint + /// Gets the versioned API health endpoint /// - IApiHealthApi ApiHealth { get; } + IVersionedApiHealthApi ApiHealth { get; } } } \ No newline at end of file diff --git a/src/MX.GeoLocation.Api.Client.V1/ServiceCollectionExtensions.cs b/src/MX.GeoLocation.Api.Client.V1/ServiceCollectionExtensions.cs index e088f2d6..dbda59be 100644 --- a/src/MX.GeoLocation.Api.Client.V1/ServiceCollectionExtensions.cs +++ b/src/MX.GeoLocation.Api.Client.V1/ServiceCollectionExtensions.cs @@ -1,6 +1,5 @@ using Microsoft.Extensions.DependencyInjection; -using MX.GeoLocation.Api.Client.V1; using MX.GeoLocation.Abstractions.Interfaces; using MX.GeoLocation.Abstractions.Interfaces.V1; @@ -35,10 +34,12 @@ public static IServiceCollection AddGeoLocationApiClient( // Register API health endpoint serviceCollection.AddTypedApiClient(configureOptions); - // Register versioned API wrapper + // Register version selectors as scoped serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); - // Register main client + // Register the unified client as scoped serviceCollection.AddScoped(); return serviceCollection; diff --git a/src/MX.GeoLocation.Api.V1/Program.cs b/src/MX.GeoLocation.Api.V1/Program.cs index a878633c..996cc306 100644 --- a/src/MX.GeoLocation.Api.V1/Program.cs +++ b/src/MX.GeoLocation.Api.V1/Program.cs @@ -134,6 +134,8 @@ app.MapControllers(); +app.MapGet("/", () => Results.Ok()).ExcludeFromDescription(); + app.Run(); // Make Program accessible for WebApplicationFactory in integration tests diff --git a/terraform/azurerm_api_management_product_policy.tf b/terraform/azurerm_api_management_product_policy.tf index 5556b820..f5f8394b 100644 --- a/terraform/azurerm_api_management_product_policy.tf +++ b/terraform/azurerm_api_management_product_policy.tf @@ -10,20 +10,27 @@ resource "azurerm_api_management_product_policy" "api_product_policy" { - - - - ${local.entra_api_identifier_uri} - - - https://sts.windows.net/${data.azuread_client_config.current.tenant_id}/ - - - - LookupApiUser - - - + + + + + + + + + ${local.entra_api_identifier_uri} + + + https://sts.windows.net/${data.azuread_client_config.current.tenant_id}/ + + + + LookupApiUser + + + + +