-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Stop forcing people to register IConfigurationRoot in DI services
This adds an overload of the AddExternalServices method that let you pass an IConfiguration in directly, and marks the old one where it assumes it can dig IConfigurationRoot out of the dependencies as Obsolete. Also added tests for whole external services functionality, since we didn't have any before.
- Loading branch information
Showing
8 changed files
with
210 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// <copyright file="ExternalOpenApiService.cs" company="Endjin Limited"> | ||
// Copyright (c) Endjin Limited. All rights reserved. | ||
// </copyright> | ||
|
||
namespace Menes.Specs.Fakes; | ||
|
||
/// <summary> | ||
/// A class representing an OpenApi service for use by the IOpenApiExternalServices tests. | ||
/// </summary> | ||
/// <remarks> | ||
/// The tests never hit any of the endpoints for this service. The class's only purpose in the | ||
/// test is to enable the code under test to locate the OpenAPI YAML. So the only thing we need | ||
/// here is the <see cref="EmbeddedOpenApiDefinitionAttribute"/>. | ||
/// </remarks> | ||
[EmbeddedOpenApiDefinition("Menes.Specs.Fakes.ExternalOpenApiService.yaml")] | ||
public class ExternalOpenApiService : IOpenApiService | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
openapi: "3.0.0" | ||
info: | ||
version: 1.0.0 | ||
title: Service definition for testing external service resolution | ||
paths: | ||
'/test/{pathElement1}/path/{pathElement2}': | ||
get: | ||
operationId: getWithTwoPathParameters | ||
parameters: | ||
- name: pathElement1 | ||
in: path | ||
required: true | ||
schema: | ||
type: string | ||
- name: pathElement2 | ||
in: path | ||
required: true | ||
schema: | ||
type: int64 | ||
responses: | ||
'200': | ||
description: Accepted | ||
content: | ||
'application/json': | ||
schema: | ||
type: string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Feature: ExternalServices | ||
|
||
As a developer writing a web service | ||
I need to be able to generate URLs for other Menes-based services | ||
So that my service can provide client applications with links to relevant entities | ||
|
||
|
||
Scenario: Pass IConfiguration during registration | ||
Given my configuration contains the setting 'MyExternalService' with the value 'http://example.com:1234' | ||
And I have registered an external service with an embedded definition, passing an IConfiguration directly, and a base URL configuration key of 'MyExternalService' | ||
When I resolve an external service URL for the operation 'getWithTwoPathParameters' with these parameters | ||
| Name | Type | Value | | ||
| pathElement1 | System.String | p1 | | ||
| pathElement2 | System.Int64 | 1234 | | ||
Then the resolved external service URL is 'http://example.com:1234/test/p1/path/1234' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// <copyright file="ConfigurationSteps.cs" company="Endjin Limited"> | ||
// Copyright (c) Endjin Limited. All rights reserved. | ||
// </copyright> | ||
|
||
namespace Menes.Specs.Steps; | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
|
||
using Microsoft.Extensions.Configuration; | ||
|
||
using TechTalk.SpecFlow; | ||
|
||
/// <summary> | ||
/// Steps for working with <see cref="IConfiguration"/>. | ||
/// </summary> | ||
[Binding] | ||
public class ConfigurationSteps | ||
{ | ||
private readonly Dictionary<string, string?> settings = new(); | ||
private IConfigurationRoot? builtConfiguration = null; | ||
|
||
public IConfigurationRoot ConfigurationRoot => this.builtConfiguration ??= | ||
new ConfigurationBuilder().AddInMemoryCollection(this.settings).Build(); | ||
|
||
[Given("my configuration contains the setting '([^']*)' with the value '([^']*)'")] | ||
public void GivenMyConfigurationContainsTheSettingWithTheValue(string settingName, string settingValue) | ||
{ | ||
if (this.builtConfiguration is not null) | ||
{ | ||
throw new InvalidOperationException("The IConfigurationRoot has already been created (because some other test step asked for it) so it is too late to add more configuration settings"); | ||
} | ||
|
||
this.settings.Add(settingName, settingValue); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// <copyright file="ExternalServicesSteps.cs" company="Endjin Limited"> | ||
// Copyright (c) Endjin Limited. All rights reserved. | ||
// </copyright> | ||
|
||
namespace Menes.Specs.Steps; | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
|
||
using Menes.Specs.Fakes; | ||
|
||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
using NUnit.Framework; | ||
|
||
using TechTalk.SpecFlow; | ||
using TechTalk.SpecFlow.Assist; | ||
|
||
/// <summary> | ||
/// Steps for testing <see cref="IOpenApiExternalServices"/>. | ||
/// </summary> | ||
[Binding] | ||
public class ExternalServicesSteps | ||
{ | ||
private readonly ConfigurationSteps configuration; | ||
private readonly ServiceCollection services = new(); | ||
private IOpenApiExternalServices? externalServices; | ||
private Uri? resolvedUrl; | ||
|
||
public ExternalServicesSteps(ConfigurationSteps configuration) | ||
{ | ||
this.configuration = configuration; | ||
|
||
this.services.AddLogging(); | ||
} | ||
|
||
[Given("I have registered an external service with an embedded definition, passing an IConfiguration directly, and a base URL configuration key of '([^']*)'")] | ||
public void GivenIHaveRegisteredAnExternalServiceWithAnEmbeddedDefinitionPassingAnIConfigurationAndAConfigurationKey( | ||
string configurationKey) | ||
{ | ||
if (this.externalServices is not null) | ||
{ | ||
throw new InvalidOperationException("External service registration is already complete"); | ||
} | ||
|
||
this.services.AddExternalServices( | ||
this.configuration.ConfigurationRoot, | ||
externalServices => externalServices.AddExternalServiceWithEmbeddedDefinition<ExternalOpenApiService>(configurationKey)); | ||
} | ||
|
||
[When("I resolve an external service URL for the operation '([^']*)' with these parameters")] | ||
public void WhenIResolveAnExternalServiceUrlForTheOperationWithTheseParameters( | ||
string operationId, Table table) | ||
{ | ||
List<(string Name, object? Value)> parameters = new(); | ||
foreach ((string name, string type, string valueText) in table.CreateSet<(string Name, string Type, string Value)>()) | ||
{ | ||
object value = Convert.ChangeType(valueText, Type.GetType(type) ?? throw new InvalidOperationException($"Unrecognized type {type}")); | ||
parameters.Add((name, value)); | ||
} | ||
|
||
this.externalServices ??= this.services.BuildServiceProvider().GetRequiredService<IOpenApiExternalServices>(); | ||
|
||
this.resolvedUrl = this.externalServices.ResolveUrl<ExternalOpenApiService>(operationId, parameters.ToArray()); | ||
} | ||
|
||
[Then("the resolved external service URL is '([^']*)'")] | ||
public void ThenTheResolvedExternalServiceURLIs(Uri expectedUrl) | ||
{ | ||
Assert.AreEqual(expectedUrl, this.resolvedUrl); | ||
} | ||
} |