diff --git a/Solutions/Menes.Abstractions/Menes/Exceptions/OpenApiAccessControlPolicyEvaluationFailedException.cs b/Solutions/Menes.Abstractions/Menes/Exceptions/OpenApiAccessControlPolicyEvaluationFailedException.cs
index 68763dbc0..83499f51a 100644
--- a/Solutions/Menes.Abstractions/Menes/Exceptions/OpenApiAccessControlPolicyEvaluationFailedException.cs
+++ b/Solutions/Menes.Abstractions/Menes/Exceptions/OpenApiAccessControlPolicyEvaluationFailedException.cs
@@ -100,7 +100,7 @@ private void AddProblemDetails()
this.AddProblemDetailsExtension("Policy Name", this.PolicyName);
}
- if (this.Requests != null && this.Requests.Length > 0)
+ if (this.Requests?.Length > 0)
{
this.AddProblemDetailsExtension("Requests", string.Join(Environment.NewLine, this.Requests.Select(r => $"{r.OperationId}: {r.Method} {r.Path}")));
}
diff --git a/Solutions/Menes.Abstractions/Menes/Hal/HalDocument.cs b/Solutions/Menes.Abstractions/Menes/Hal/HalDocument.cs
index 30022b09b..1039045d2 100644
--- a/Solutions/Menes.Abstractions/Menes/Hal/HalDocument.cs
+++ b/Solutions/Menes.Abstractions/Menes/Hal/HalDocument.cs
@@ -34,7 +34,7 @@ public HalDocument(IJsonSerializerSettingsProvider serializerSettingsProvider)
///
/// Gets the serializer settings for the HAL document.
///
- public JsonSerializerSettings SerializerSettings { get; private set; }
+ public JsonSerializerSettings SerializerSettings { get; }
///
/// Gets the properites for the HalDocument.
diff --git a/Solutions/Menes.Abstractions/Menes/Hal/IHalDocumentMapper.cs b/Solutions/Menes.Abstractions/Menes/Hal/IHalDocumentMapper.cs
index bf59d8c9c..b3afa7927 100644
--- a/Solutions/Menes.Abstractions/Menes/Hal/IHalDocumentMapper.cs
+++ b/Solutions/Menes.Abstractions/Menes/Hal/IHalDocumentMapper.cs
@@ -29,4 +29,20 @@ public interface IHalDocumentMapper : IHalDocumentMapper
/// The for the resource.
HalDocument Map(T resource);
}
+
+ ///
+ /// Implemented by types which can map a resource to a HAL document and require additional context for the mapping.
+ ///
+ /// The type of the resource to map.
+ /// The type of the object that provides additional context to the mapping.
+ public interface IHalDocumentMapper : IHalDocumentMapper
+ {
+ ///
+ /// Map a resource to a HAL document.
+ ///
+ /// The resource to map.
+ /// The additional context information.
+ /// The for the resource.
+ HalDocument Map(TResource resource, TContext context);
+ }
}
diff --git a/Solutions/Menes.Abstractions/Menes/Links/OpenApiAccessCheckerExtensions.cs b/Solutions/Menes.Abstractions/Menes/Links/OpenApiAccessCheckerExtensions.cs
index c06189ff1..9f8af5d4f 100644
--- a/Solutions/Menes.Abstractions/Menes/Links/OpenApiAccessCheckerExtensions.cs
+++ b/Solutions/Menes.Abstractions/Menes/Links/OpenApiAccessCheckerExtensions.cs
@@ -42,8 +42,8 @@ public static async Task RemoveForbiddenLinksAsync(this IOpenApiAccessChecker th
AddHalDocumentLinksToMap(
target,
linkMap,
- !options.HasFlag(HalDocumentLinkRemovalOptions.NonRecursive),
- options.HasFlag(HalDocumentLinkRemovalOptions.Unsafe));
+ (options & HalDocumentLinkRemovalOptions.NonRecursive) == 0,
+ (options & HalDocumentLinkRemovalOptions.Unsafe) != 0);
// Build a second map of operation descriptors (needed to invoke the access policy check) to our OpenApiWebLinks.
var operationDescriptorMap = linkMap
diff --git a/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/HttpRequestParameterBuilder.cs b/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/HttpRequestParameterBuilder.cs
index 2b66c3494..d1cf3e87b 100644
--- a/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/HttpRequestParameterBuilder.cs
+++ b/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/HttpRequestParameterBuilder.cs
@@ -519,7 +519,8 @@ private object ConvertValue(OpenApiSchema schema, string value)
this.logger.LogError(
"Failed to convert value with [{schema}]",
schema.GetLoggingInformation());
- throw new NotImplementedException();
+
+ throw new OpenApiServiceMismatchException($"Unable to convert value to match [{schema.GetLoggingInformation()}]");
}
}
}
diff --git a/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/OpenApiActionResult.cs b/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/OpenApiActionResult.cs
index 035378cb0..c10d092da 100644
--- a/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/OpenApiActionResult.cs
+++ b/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/OpenApiActionResult.cs
@@ -263,7 +263,8 @@ private string ConvertValue(OpenApiSchema schema, object value)
this.logger.LogError(
"Failed to convert value with [{schema}]",
schema.GetLoggingInformation());
- throw new NotImplementedException();
+
+ throw new OpenApiServiceMismatchException($"Failed to convert value to match [{schema.GetLoggingInformation()}]");
}
private void BuildHeaders(HttpResponse httpResponse, OpenApiResponse response)
diff --git a/Solutions/Menes.Hosting.AspNetCore/Menes/Internal/OpenApiHttpRequestHostServiceCollectionExtensions.cs b/Solutions/Menes.Hosting.AspNetCore/Microsoft/Extensions/DependencyInjection/OpenApiHttpRequestHostServiceCollectionExtensions.cs
similarity index 100%
rename from Solutions/Menes.Hosting.AspNetCore/Menes/Internal/OpenApiHttpRequestHostServiceCollectionExtensions.cs
rename to Solutions/Menes.Hosting.AspNetCore/Microsoft/Extensions/DependencyInjection/OpenApiHttpRequestHostServiceCollectionExtensions.cs
diff --git a/Solutions/Menes.Hosting/Microsoft/Extensions/DependencyInjection/OpenApiHostingServiceCollectionExtensions.cs b/Solutions/Menes.Hosting/Microsoft/Extensions/DependencyInjection/OpenApiHostingServiceCollectionExtensions.cs
index f3fd4cf09..dd449e064 100644
--- a/Solutions/Menes.Hosting/Microsoft/Extensions/DependencyInjection/OpenApiHostingServiceCollectionExtensions.cs
+++ b/Solutions/Menes.Hosting/Microsoft/Extensions/DependencyInjection/OpenApiHostingServiceCollectionExtensions.cs
@@ -153,7 +153,7 @@ public static IServiceCollection AddOpenApiHosting(this ISe
/// Add an to the service collection.
///
/// The type of the resource mapped by the HAL document mapper.
- /// The type fo the mapper.
+ /// The type of the mapper.
/// The service collection to which to add the mapper.
/// The service collection, configured with the HAL document mapper.
public static IServiceCollection AddHalDocumentMapper(this IServiceCollection services)
@@ -165,6 +165,23 @@ public static IServiceCollection AddHalDocumentMapper(this I
return services;
}
+ ///
+ /// Add an to the service collection.
+ ///
+ /// The type of the resource mapped by the HAL document mapper.
+ /// The type of the additional context required by the HAL document mapper.
+ /// The type of the mapper.
+ /// The service collection to which to add the mapper.
+ /// The service collection, configured with the HAL document mapper.
+ public static IServiceCollection AddHalDocumentMapper(this IServiceCollection services)
+ where TMapper : class, IHalDocumentMapper
+ {
+ services.AddSingleton();
+ services.AddSingleton(s => s.GetRequiredService());
+ services.AddSingleton>(s => s.GetRequiredService());
+ return services;
+ }
+
///
/// Adds the /swagger endpoint to your host.
///
diff --git a/Solutions/Menes.PetStore.Hosting/Menes/PetStore/Hosting/Startup.cs b/Solutions/Menes.PetStore.Hosting/Menes/PetStore/Hosting/Startup.cs
index 5630039a0..58ec6c62e 100644
--- a/Solutions/Menes.PetStore.Hosting/Menes/PetStore/Hosting/Startup.cs
+++ b/Solutions/Menes.PetStore.Hosting/Menes/PetStore/Hosting/Startup.cs
@@ -31,10 +31,7 @@ public void Configure(IWebJobsBuilder builder)
services.AddHalDocumentMapper();
services.AddHalDocumentMapper();
- services.AddOpenApiHttpRequestHosting(hostConfig =>
- {
- LoadDocuments(hostConfig);
- });
+ _ = services.AddOpenApiHttpRequestHosting(LoadDocuments);
// We can add all the services here
// We will only actually *provide* services that are in the YAML file(s) we load below
diff --git a/Solutions/Menes.Specs/Features/OpenApiHostingInitialisation.feature b/Solutions/Menes.Specs/Features/OpenApiHostingInitialisation.feature
new file mode 100644
index 000000000..9f01c4ba4
--- /dev/null
+++ b/Solutions/Menes.Specs/Features/OpenApiHostingInitialisation.feature
@@ -0,0 +1,56 @@
+Feature: OpenApi Hosting Initialisation
+ In order to use Menes in my application
+ As a developer
+ I want to be able to add Menes services and related components to my service collection
+
+Background:
+ Given I have created a service collection to register my services against
+
+Scenario: Adding AspNetCore OpenApi hosting adds the IOpenApiHost for HttpRequest and IActionResult
+ When I add AspNetCore OpenApi hosting to the service collection
+ And I build the service provider from the service collection
+ Then a service is available as a Singleton for type IOpenApiHost{HttpRequest, IActionResult}
+
+Scenario: Adding OpenApi hosting enables auditing to console by default
+ Given I have added AspNetCore OpenApi hosting to the service collection
+ And I have built the service provider from the service collection
+ Then an audit log builder service is available for auditing operations which return OpenApiResults
+ And an audit log builder service is available for auditing operations which return a POCO
+ And an audit log sink service is available for console logging
+ And auditing is enabled
+
+Scenario Outline: OpenApi host initialisation maps standard Menes exception types to their corresponding HTTP status codes
+ Given I have added AspNetCore OpenApi hosting to the service collection
+ And I have built the service provider from the service collection
+ When I request an instance of the OpenApi host
+ Then the exception of type '' is mapped to response code ''
+
+ Examples:
+ | Exception Type | Mapped Response Code |
+ | Menes.Exceptions.OpenApiBadRequestException, Menes.Abstractions | 400 |
+ | Menes.Exceptions.OpenApiUnauthorizedException, Menes.Abstractions | 401 |
+ | Menes.Exceptions.OpenApiForbiddenException, Menes.Abstractions | 403 |
+ | Menes.Exceptions.OpenApiNotFoundException, Menes.Abstractions | 404 |
+
+Scenario: OpenApi host initialisation adds link maps from registered IHalDocumentMapper types
+ Given I have added AspNetCore OpenApi hosting to the service collection
+ And I have registered a HalDocumentMapper for a resource type to the service collection
+ And I have registered a HalDocumentMapper for a resource and context type to the service collection
+ And I have built the service provider from the service collection
+ When I request an instance of the OpenApi host
+ Then the HalDocumentMapper for resource type has configured its links
+ And the HalDocumentMapper for resource and context types has configured its links
+
+Scenario: Registering HAL document mappers with resource type parameters adds them to the container with the concrete type, the IHalDocumentMapper interface and the generic IHalDocumentMapper interface
+ When I register a HalDocumentMapper for a resource type to the service collection
+ And I build the service provider from the service collection
+ Then it should be available as a Singleton with the service type matching the concrete type of the mapper
+ And It should be available as a Singleton with a service type of IHalDocumentMapper
+ And it should be available as a Singleton with a service type of IHalDocumentMapper{TResource}
+
+Scenario: Registering HAL document mappers with resource and context type parameters adds them to the container with the concrete type, the IHalDocumentMapper interface and the generic IHalDocumentMapper interface
+ When I register a HalDocumentMapper for a resource and context type to the service collection
+ And I build the service provider from the service collection
+ Then it should be available as a Singleton with the service type matching the concrete type of the mapper with context
+ And It should be available as a Singleton with a service type of IHalDocumentMapper
+ And it should be available as a Singleton with a service type of IHalDocumentMapper{TResource, TContext}
diff --git a/Solutions/Menes.Specs/Features/OpenApiHostingInitialisation.feature.cs b/Solutions/Menes.Specs/Features/OpenApiHostingInitialisation.feature.cs
new file mode 100644
index 000000000..2dd59cf54
--- /dev/null
+++ b/Solutions/Menes.Specs/Features/OpenApiHostingInitialisation.feature.cs
@@ -0,0 +1,255 @@
+// ------------------------------------------------------------------------------
+//
+// This code was generated by SpecFlow (http://www.specflow.org/).
+// SpecFlow Version:3.0.0.0
+// SpecFlow Generator Version:3.0.0.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+#region Designer generated code
+#pragma warning disable
+namespace Menes.Specs.Features
+{
+ using TechTalk.SpecFlow;
+
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.0.0.0")]
+ [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [NUnit.Framework.TestFixtureAttribute()]
+ [NUnit.Framework.DescriptionAttribute("OpenApi Hosting Initialisation")]
+ public partial class OpenApiHostingInitialisationFeature
+ {
+
+ private TechTalk.SpecFlow.ITestRunner testRunner;
+
+#line 1 "OpenApiHostingInitialisation.feature"
+#line hidden
+
+ [NUnit.Framework.OneTimeSetUpAttribute()]
+ public virtual void FeatureSetup()
+ {
+ testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
+ TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "OpenApi Hosting Initialisation", "\tIn order to use Menes in my application\r\n\tAs a developer\r\n\tI want to be able to " +
+ "add Menes services and related components to my service collection", ProgrammingLanguage.CSharp, ((string[])(null)));
+ testRunner.OnFeatureStart(featureInfo);
+ }
+
+ [NUnit.Framework.OneTimeTearDownAttribute()]
+ public virtual void FeatureTearDown()
+ {
+ testRunner.OnFeatureEnd();
+ testRunner = null;
+ }
+
+ [NUnit.Framework.SetUpAttribute()]
+ public virtual void TestInitialize()
+ {
+ }
+
+ [NUnit.Framework.TearDownAttribute()]
+ public virtual void ScenarioTearDown()
+ {
+ testRunner.OnScenarioEnd();
+ }
+
+ public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
+ {
+ testRunner.OnScenarioInitialize(scenarioInfo);
+ testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext);
+ }
+
+ public virtual void ScenarioStart()
+ {
+ testRunner.OnScenarioStart();
+ }
+
+ public virtual void ScenarioCleanup()
+ {
+ testRunner.CollectScenarioErrors();
+ }
+
+ public virtual void FeatureBackground()
+ {
+#line 6
+#line 7
+ testRunner.Given("I have created a service collection to register my services against", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line hidden
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Adding AspNetCore OpenApi hosting adds the IOpenApiHost for HttpRequest and IActi" +
+ "onResult")]
+ public virtual void AddingAspNetCoreOpenApiHostingAddsTheIOpenApiHostForHttpRequestAndIActionResult()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Adding AspNetCore OpenApi hosting adds the IOpenApiHost for HttpRequest and IActi" +
+ "onResult", null, ((string[])(null)));
+#line 9
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 6
+this.FeatureBackground();
+#line 10
+ testRunner.When("I add AspNetCore OpenApi hosting to the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 11
+ testRunner.And("I build the service provider from the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 12
+ testRunner.Then("a service is available as a Singleton for type IOpenApiHost{HttpRequest, IActionR" +
+ "esult}", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Adding OpenApi hosting enables auditing to console by default")]
+ public virtual void AddingOpenApiHostingEnablesAuditingToConsoleByDefault()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Adding OpenApi hosting enables auditing to console by default", null, ((string[])(null)));
+#line 14
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 6
+this.FeatureBackground();
+#line 15
+ testRunner.Given("I have added AspNetCore OpenApi hosting to the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 16
+ testRunner.And("I have built the service provider from the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 17
+ testRunner.Then("an audit log builder service is available for auditing operations which return Op" +
+ "enApiResults", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 18
+ testRunner.And("an audit log builder service is available for auditing operations which return a " +
+ "POCO", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 19
+ testRunner.And("an audit log sink service is available for console logging", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 20
+ testRunner.And("auditing is enabled", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("OpenApi host initialisation maps standard Menes exception types to their correspo" +
+ "nding HTTP status codes")]
+ [NUnit.Framework.TestCaseAttribute("Menes.Exceptions.OpenApiBadRequestException, Menes.Abstractions", "400", null)]
+ [NUnit.Framework.TestCaseAttribute("Menes.Exceptions.OpenApiUnauthorizedException, Menes.Abstractions", "401", null)]
+ [NUnit.Framework.TestCaseAttribute("Menes.Exceptions.OpenApiForbiddenException, Menes.Abstractions", "403", null)]
+ [NUnit.Framework.TestCaseAttribute("Menes.Exceptions.OpenApiNotFoundException, Menes.Abstractions", "404", null)]
+ public virtual void OpenApiHostInitialisationMapsStandardMenesExceptionTypesToTheirCorrespondingHTTPStatusCodes(string exceptionType, string mappedResponseCode, string[] exampleTags)
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("OpenApi host initialisation maps standard Menes exception types to their correspo" +
+ "nding HTTP status codes", null, exampleTags);
+#line 22
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 6
+this.FeatureBackground();
+#line 23
+ testRunner.Given("I have added AspNetCore OpenApi hosting to the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 24
+ testRunner.And("I have built the service provider from the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 25
+ testRunner.When("I request an instance of the OpenApi host", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 26
+ testRunner.Then(string.Format("the exception of type \'{0}\' is mapped to response code \'{1}\'", exceptionType, mappedResponseCode), ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("OpenApi host initialisation adds link maps from registered IHalDocumentMapper typ" +
+ "es")]
+ public virtual void OpenApiHostInitialisationAddsLinkMapsFromRegisteredIHalDocumentMapperTypes()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("OpenApi host initialisation adds link maps from registered IHalDocumentMapper typ" +
+ "es", null, ((string[])(null)));
+#line 35
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 6
+this.FeatureBackground();
+#line 36
+ testRunner.Given("I have added AspNetCore OpenApi hosting to the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line 37
+ testRunner.And("I have registered a HalDocumentMapper for a resource type to the service collecti" +
+ "on", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 38
+ testRunner.And("I have registered a HalDocumentMapper for a resource and context type to the serv" +
+ "ice collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 39
+ testRunner.And("I have built the service provider from the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 40
+ testRunner.When("I request an instance of the OpenApi host", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 41
+ testRunner.Then("the HalDocumentMapper for resource type has configured its links", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 42
+ testRunner.And("the HalDocumentMapper for resource and context types has configured its links", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Registering HAL document mappers with resource type parameters adds them to the c" +
+ "ontainer with the concrete type, the IHalDocumentMapper interface and the generi" +
+ "c IHalDocumentMapper interface")]
+ public virtual void RegisteringHALDocumentMappersWithResourceTypeParametersAddsThemToTheContainerWithTheConcreteTypeTheIHalDocumentMapperInterfaceAndTheGenericIHalDocumentMapperInterface()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Registering HAL document mappers with resource type parameters adds them to the c" +
+ "ontainer with the concrete type, the IHalDocumentMapper interface and the generi" +
+ "c IHalDocumentMapper interface", null, ((string[])(null)));
+#line 44
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 6
+this.FeatureBackground();
+#line 45
+ testRunner.When("I register a HalDocumentMapper for a resource type to the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 46
+ testRunner.And("I build the service provider from the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 47
+ testRunner.Then("it should be available as a Singleton with the service type matching the concrete" +
+ " type of the mapper", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 48
+ testRunner.And("It should be available as a Singleton with a service type of IHalDocumentMapper", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 49
+ testRunner.And("it should be available as a Singleton with a service type of IHalDocumentMapper{T" +
+ "Resource}", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Registering HAL document mappers with resource and context type parameters adds t" +
+ "hem to the container with the concrete type, the IHalDocumentMapper interface an" +
+ "d the generic IHalDocumentMapper interface")]
+ public virtual void RegisteringHALDocumentMappersWithResourceAndContextTypeParametersAddsThemToTheContainerWithTheConcreteTypeTheIHalDocumentMapperInterfaceAndTheGenericIHalDocumentMapperInterface()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Registering HAL document mappers with resource and context type parameters adds t" +
+ "hem to the container with the concrete type, the IHalDocumentMapper interface an" +
+ "d the generic IHalDocumentMapper interface", null, ((string[])(null)));
+#line 51
+this.ScenarioInitialize(scenarioInfo);
+ this.ScenarioStart();
+#line 6
+this.FeatureBackground();
+#line 52
+ testRunner.When("I register a HalDocumentMapper for a resource and context type to the service col" +
+ "lection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line 53
+ testRunner.And("I build the service provider from the service collection", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 54
+ testRunner.Then("it should be available as a Singleton with the service type matching the concrete" +
+ " type of the mapper with context", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line 55
+ testRunner.And("It should be available as a Singleton with a service type of IHalDocumentMapper", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line 56
+ testRunner.And("it should be available as a Singleton with a service type of IHalDocumentMapper{T" +
+ "Resource, TContext}", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+ this.ScenarioCleanup();
+ }
+ }
+}
+#pragma warning restore
+#endregion
diff --git a/Solutions/Menes.Specs/Steps/AccessControlPolicySteps.cs b/Solutions/Menes.Specs/Steps/AccessControlPolicySteps.cs
index 0a7c8f2c2..31b2932ac 100644
--- a/Solutions/Menes.Specs/Steps/AccessControlPolicySteps.cs
+++ b/Solutions/Menes.Specs/Steps/AccessControlPolicySteps.cs
@@ -102,7 +102,7 @@ public void ThenEachPolicyShouldReceiveAPathOf(string path)
{
foreach ((Mock policy, CompletionSourceWithArgs> completion) in this.policies)
{
- Assert.AreEqual(path, completion.Arguments[0].Requests.First().Path);
+ Assert.AreEqual(path, completion.Arguments[0].Requests[0].Path);
}
}
@@ -111,7 +111,7 @@ public void ThenEachPolicyShouldReceiveAnOperationIdOf(string operationId)
{
foreach ((Mock policy, CompletionSourceWithArgs> completion) in this.policies)
{
- Assert.AreEqual(operationId, completion.Arguments[0].Requests.First().OperationId);
+ Assert.AreEqual(operationId, completion.Arguments[0].Requests[0].OperationId);
}
}
@@ -120,7 +120,7 @@ public void ThenEachPolicyShouldReceiveAnHttpMethodOf(string method)
{
foreach ((Mock policy, CompletionSourceWithArgs> completion) in this.policies)
{
- Assert.AreEqual(method, completion.Arguments[0].Requests.First().Method);
+ Assert.AreEqual(method, completion.Arguments[0].Requests[0].Method);
}
}
diff --git a/Solutions/Menes.Specs/Steps/HalDocumentSteps.cs b/Solutions/Menes.Specs/Steps/HalDocumentSteps.cs
index 974d19d43..892c4e26b 100644
--- a/Solutions/Menes.Specs/Steps/HalDocumentSteps.cs
+++ b/Solutions/Menes.Specs/Steps/HalDocumentSteps.cs
@@ -79,7 +79,6 @@ public void GivenIAddAnEmbeddedResourceToTheHalDocumentT()
document.AddEmbeddedResource("somerel", halDocumentFactory.CreateHalDocument());
}
-
[Then("the properties of the domain class should be serialized as top level properties in the JSON")]
public void ThenThePropertiesOfTheDomainClassShouldBeSerializedAsTopLevelPropertiesInTheJSON()
{
diff --git a/Solutions/Menes.Specs/Steps/OpenApiAccessCheckerExtensionsSteps.cs b/Solutions/Menes.Specs/Steps/OpenApiAccessCheckerExtensionsSteps.cs
index 045a167e9..deeaf821a 100644
--- a/Solutions/Menes.Specs/Steps/OpenApiAccessCheckerExtensionsSteps.cs
+++ b/Solutions/Menes.Specs/Steps/OpenApiAccessCheckerExtensionsSteps.cs
@@ -47,18 +47,23 @@ public void GivenTheHalDocumentCalledHasInternalLinks(string halDocumentName, Ta
{
HalDocument doc = this.scenarioContext.Get(halDocumentName);
- openApiWebLinkTable.Rows.ForEach(row =>
- {
- doc.AddLink(row["Rel"], new OpenApiWebLink(row["OperationId"], row["Href"], Enum.Parse(row["OperationType"], true)));
- });
+ openApiWebLinkTable.Rows.ForEach(
+ row => doc.AddLink(
+ row["Rel"],
+ new OpenApiWebLink(
+ row["OperationId"],
+ row["Href"],
+ Enum.Parse(row["OperationType"], true))));
}
[Given("the HalDocument called '(.*)' has embedded resources")]
public void GivenTheHalDocumentCalledHasEmbeddedResources(string halDocumentName, Table embeddedResourceTable)
{
- IHalDocumentFactory halDocumentFactory = ContainerBindings.GetServiceProvider(this.featureContext).GetService();
+ IHalDocumentFactory halDocumentFactory =
+ ContainerBindings.GetServiceProvider(this.featureContext).GetService();
- IEnumerable<(string Rel, object Object)> embeddedResources = embeddedResourceTable.CreateSet<(string Rel, object Object)>();
+ IEnumerable<(string Rel, object Object)> embeddedResources =
+ embeddedResourceTable.CreateSet<(string Rel, object Object)>();
HalDocument doc = this.scenarioContext.Get(halDocumentName);
embeddedResources.ForEach(x => doc.AddEmbeddedResource(x.Rel, CreateHalDocument(x, halDocumentFactory)));
}
@@ -86,12 +91,14 @@ public Task WhenIRequestAnAccessCheckOnTheHalDocumentCalledWithTheFollowingOptio
{
HalDocument doc = this.scenarioContext.Get(halDocumentName);
var mock = new Mock();
- mock.Setup(x => x.CheckAccessPoliciesAsync(It.IsAny(), It.IsAny())).Returns((IOpenApiContext context, AccessCheckOperationDescriptor[] descriptors) => this.MockCheckAccessPoliciesAsync(descriptors));
+ mock.Setup(x => x.CheckAccessPoliciesAsync(It.IsAny(), It.IsAny())).Returns((IOpenApiContext _, AccessCheckOperationDescriptor[] descriptors) => this.MockCheckAccessPoliciesAsync(descriptors));
// Normally this would be invoked as an extension method on mock.Object,
// but it's invoked as a static method here as otherwise it looks like we're
// running our test against a mock, which is misleading.
+#pragma warning disable RCS1196 // Call extension method as instance method.
return OpenApiAccessCheckerExtensions.RemoveForbiddenLinksAsync(mock.Object, doc, new SimpleOpenApiContext(), Enum.Parse(optionsTable.Rows[0][0]));
+#pragma warning restore RCS1196 // Call extension method as instance method.
}
[Then("the HalDocument called '(.*)' should contain only the following link relations")]
diff --git a/Solutions/Menes.Specs/Steps/OpenApiHostingServiceCollectionExtensionsSteps.cs b/Solutions/Menes.Specs/Steps/OpenApiHostingServiceCollectionExtensionsSteps.cs
new file mode 100644
index 000000000..ee7a8e1f2
--- /dev/null
+++ b/Solutions/Menes.Specs/Steps/OpenApiHostingServiceCollectionExtensionsSteps.cs
@@ -0,0 +1,220 @@
+//
+// Copyright (c) Endjin. All rights reserved.
+//
+
+namespace Menes.Specs.Steps
+{
+ using System;
+ using System.Linq;
+ using System.Reflection;
+ using Menes.Auditing;
+ using Menes.Auditing.AuditLogSinks.Development;
+ using Menes.Auditing.Internal;
+ using Menes.Hal;
+ using Menes.Specs.Steps.TestClasses;
+ using Microsoft.AspNetCore.Http;
+ using Microsoft.AspNetCore.Mvc;
+ using Microsoft.Extensions.DependencyInjection;
+ using NUnit.Framework;
+ using TechTalk.SpecFlow;
+
+ [Binding]
+ public class OpenApiHostingServiceCollectionExtensionsSteps
+ {
+ private readonly ScenarioContext scenarioContext;
+
+ public OpenApiHostingServiceCollectionExtensionsSteps(ScenarioContext scenarioContext)
+ {
+ this.scenarioContext = scenarioContext;
+ }
+
+ [Given("I have created a service collection to register my services against")]
+ public void GivenIHaveCreatedAServiceCollectionToRegisterMyServicesAgainst()
+ {
+ this.scenarioContext.Set(new ServiceCollection());
+ }
+
+ [Given("I have added AspNetCore OpenApi hosting to the service collection")]
+ [When("I add AspNetCore OpenApi hosting to the service collection")]
+ public void WhenIAddOpenApiHostingToTheServiceCollection()
+ {
+ ServiceCollection collection = this.scenarioContext.Get();
+
+ collection.AddLogging();
+ collection.AddOpenApiHttpRequestHosting(_ => { }, null);
+ }
+
+ [Given("I have built the service provider from the service collection")]
+ [When("I build the service provider from the service collection")]
+ public void GivenIHaveBuiltTheServiceCollection()
+ {
+ ServiceProvider provider = this.scenarioContext.Get().BuildServiceProvider();
+ this.scenarioContext.Set(provider);
+ }
+
+ [Given("I have registered a HalDocumentMapper for a resource type to the service collection")]
+ [When("I register a HalDocumentMapper for a resource type to the service collection")]
+ public void WhenIRegisterAHalDocumentMapperForAResourceTypeToTheServiceCollection()
+ {
+ this.scenarioContext.Get().AddHalDocumentMapper();
+ }
+
+ [Given("I have registered a HalDocumentMapper for a resource and context type to the service collection")]
+ [When("I register a HalDocumentMapper for a resource and context type to the service collection")]
+ public void WhenIAddAHalDocumentMapperForAResourceAndContextTypeToTheServiceCollection()
+ {
+ this.scenarioContext.Get()
+ .AddHalDocumentMapper();
+ }
+
+ [Then("a service is available as a Singleton for type IOpenApiHost{HttpRequest, IActionResult}")]
+ public void ThenAServiceIsAddedAsASingletonForTypeIOpenApiHostHttpRequestIActionResult()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider>();
+ this.AssertServiceIsASingleton>();
+ }
+
+ [Then("it should be available as a Singleton with the service type matching the concrete type of the mapper")]
+ public void ThenItShouldBeAvailableAsASingletonWithTheServiceTypeMatchingTheMapperType()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider();
+ this.AssertServiceIsASingleton();
+ }
+
+ [Then("it should be available as a Singleton with the service type matching the concrete type of the mapper with context")]
+ public void ThenItShouldBeAvailableAsASingletonWithTheServiceTypeMatchingTheMapperTypeWithContext()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider();
+ this.AssertServiceIsASingleton();
+ }
+
+ [Then("It should be available as a Singleton with a service type of IHalDocumentMapper")]
+ public void ThenItShouldBeAvailableAsASingletonWithAServiceTypeOfIHalDocumentMapper()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider();
+ this.AssertServiceIsASingleton();
+ }
+
+ [Then("it should be available as a Singleton with a service type of IHalDocumentMapper{TResource}")]
+ public void ThenItShouldBeAvailableAsASingletonWithAServiceTypeOfIHalDocumentMapperTResource()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider>();
+ this.AssertServiceIsASingleton>();
+ }
+
+ [Then("it should be available as a Singleton with a service type of IHalDocumentMapper{TResource, TContext}")]
+ public void ThenItShouldBeAvailableAsASingletonWithAServiceTypeOfIHalDocumentMapperWithResourceAndContext()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider>();
+ this.AssertServiceIsASingleton>();
+ }
+
+ [When("I request an instance of the OpenApi host")]
+ public void WhenIRequestAnInstanceOfTheOpenApiHost()
+ {
+ ServiceProvider provider = this.scenarioContext.Get();
+ IOpenApiHost host =
+ provider.GetRequiredService>();
+
+ this.scenarioContext.Set(host);
+ }
+
+ [Then("the HalDocumentMapper for resource type has configured its links")]
+ public void ThenTheHalDocumentMapperForResourceTypeHasConfiguredItsLinks()
+ {
+ ServiceProvider provider = this.scenarioContext.Get();
+ PetHalDocumentMapper mapper = provider.GetRequiredService();
+ Assert.IsTrue(mapper.LinkMapConfigured);
+ }
+
+ [Then("the HalDocumentMapper for resource and context types has configured its links")]
+ public void ThenTheHalDocumentMapperForResourceAndContextTypesHasConfiguredItsLinks()
+ {
+ ServiceProvider provider = this.scenarioContext.Get();
+ PetHalDocumentMapperWithContext mapper = provider.GetRequiredService();
+ Assert.IsTrue(mapper.LinkMapConfigured);
+ }
+
+ [Then("the exception of type '(.*)' is mapped to response code '(.*)'")]
+ public void ThenTheExceptionOfTypeIsMappedToResponseCode(string exceptionType, int statusCode)
+ {
+ IOpenApiExceptionMapper exceptionMapper = this.scenarioContext.Get()
+ .GetRequiredService();
+
+ // The only way to tell if it's been mapped without reflecting into the guts of the mapper is to try and register
+ // a new mapper for the given type/status code... even with this we need a little bit of reflection to invoke the
+ // method.
+ var type = Type.GetType(exceptionType);
+ MethodInfo mapMethod = exceptionMapper
+ .GetType()
+ .GetMethods()
+ .First(x => x.Name == "Map" && x.GetGenericArguments().Length == 1);
+
+ MethodInfo createdMapMethod = mapMethod.MakeGenericMethod(type);
+
+ try
+ {
+ createdMapMethod.Invoke(exceptionMapper, new object[] { statusCode, null });
+
+ Assert.Fail($"Exception of type '{exceptionType}' was not registered");
+ }
+ catch (TargetInvocationException ex) when (ex.InnerException is ArgumentException)
+ {
+ // This is the expected exception result, so swallow to let the test pass. Anything
+ }
+ }
+
+ [Then("an audit log builder service is available for auditing operations which return OpenApiResults")]
+ public void ThenAnAuditLogBuilderServiceIsAddedForAuditingOperationsWhichReturnOpenApiResults()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider();
+ }
+
+ [Then("an audit log builder service is available for auditing operations which return a POCO")]
+ public void ThenAnAuditLogBuilderServiceIsAddedForAuditingOperationsWhichReturnAPoco()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider();
+ }
+
+ [Then("an audit log sink service is available for console logging")]
+ public void ThenAnAuditLogSinkServiceIsAddedForConsoleLogging()
+ {
+ this.AssertServiceIsAvailableFromServiceProvider();
+ }
+
+ [Then("auditing is enabled")]
+ public void ThenAuditingIsEnabled()
+ {
+ IAuditContext auditContext = this.scenarioContext.Get().GetRequiredService();
+ Assert.IsTrue(auditContext.IsAuditingEnabled);
+ }
+
+ private void AssertServiceIsAvailableFromServiceProvider()
+ {
+ TService service = this.scenarioContext.Get().GetService();
+
+ Assert.IsNotNull(service);
+ }
+
+ private void AssertServiceIsAvailableFromServiceProvider()
+ {
+ TService[] services = this.scenarioContext.Get().GetServices().ToArray();
+
+ Assert.IsNotEmpty(services);
+ Assert.IsTrue(services.Any(x => typeof(TExpectedConcreteType) == x.GetType()));
+ }
+
+ private void AssertServiceIsASingleton()
+ {
+ ServiceProvider provider = this.scenarioContext.Get();
+
+ using IServiceScope scope1 = provider.CreateScope();
+ using IServiceScope scope2 = provider.CreateScope();
+
+ TService serviceInScope1 = scope1.ServiceProvider.GetRequiredService();
+ TService serviceInScope2 = scope2.ServiceProvider.GetRequiredService();
+
+ Assert.AreSame(serviceInScope1, serviceInScope2);
+ }
+ }
+}
diff --git a/Solutions/Menes.Specs/Steps/OpenApiOperationInvokerSteps.cs b/Solutions/Menes.Specs/Steps/OpenApiOperationInvokerSteps.cs
index 00f02aeff..f2fa6af8a 100644
--- a/Solutions/Menes.Specs/Steps/OpenApiOperationInvokerSteps.cs
+++ b/Solutions/Menes.Specs/Steps/OpenApiOperationInvokerSteps.cs
@@ -232,14 +232,14 @@ public void ThenTheInvokerShouldPassTheResultFromTheExceptionMapperToTheResultBu
[Then("the invoker should return the result from the result builder")]
public async Task ThenTheInvokerShouldReturnTheResultFromTheResultBuilder()
{
- object result = await this.invokerResultTask;
+ object result = await this.invokerResultTask.ConfigureAwait(false);
Assert.AreSame(this.resultBuilderResult, result);
}
[Then("invoker should return a (.*) error result")]
public async Task ThenInvokerShouldReturnAErrorResult(int statusCode)
{
- object result = await this.invokerResultTask;
+ object result = await this.invokerResultTask.ConfigureAwait(false);
Assert.AreSame(this.resultBuilderErrorResult, result);
this.resultBuilder.Verify(m => m.BuildErrorResult(statusCode));
}
diff --git a/Solutions/Menes.Specs/Steps/ShortCircuitingAccessControlPolicyAdapterSteps.cs b/Solutions/Menes.Specs/Steps/ShortCircuitingAccessControlPolicyAdapterSteps.cs
index 12273cb23..278ff61f3 100644
--- a/Solutions/Menes.Specs/Steps/ShortCircuitingAccessControlPolicyAdapterSteps.cs
+++ b/Solutions/Menes.Specs/Steps/ShortCircuitingAccessControlPolicyAdapterSteps.cs
@@ -176,7 +176,7 @@ public void ThenTheOtherPoliciesShouldReceiveAPathOf(string path)
{
foreach ((_, CompletionSourceWithArgs> completion) in this.otherPolicies)
{
- Assert.AreEqual(path, completion.Arguments[0].Requests.First().Path);
+ Assert.AreEqual(path, completion.Arguments[0].Requests[0].Path);
}
}
@@ -185,7 +185,7 @@ public void ThenTheOtherPoliciesShouldReceiveAnOperationIdOf(string operationId)
{
foreach ((_, CompletionSourceWithArgs> completion) in this.otherPolicies)
{
- Assert.AreEqual(operationId, completion.Arguments[0].Requests.First().OperationId);
+ Assert.AreEqual(operationId, completion.Arguments[0].Requests[0].OperationId);
}
}
@@ -194,7 +194,7 @@ public void ThenTheOtherPoliciesShouldReceiveAnHttpMethodOf(string method)
{
foreach ((_, CompletionSourceWithArgs> completion) in this.otherPolicies)
{
- Assert.AreEqual(method, completion.Arguments[0].Requests.First().Method);
+ Assert.AreEqual(method, completion.Arguments[0].Requests[0].Method);
}
}
@@ -230,7 +230,7 @@ public async Task ThenTheAdapterResultShouldHaveAnExplanationOfAsync(string expl
Assert.AreEqual(explanation, result.Values.First().Explanation);
}
- [When(@"the first policy denies access")]
+ [When("the first policy denies access")]
public void WhenTheFirstPolicyDeniesAccess()
{
var result = new Dictionary();
@@ -239,7 +239,7 @@ public void WhenTheFirstPolicyDeniesAccess()
this.firstPolicyCompletion.SupplyResult(result);
}
- [Then(@"the adapter result type should be '(.*)'")]
+ [Then("the adapter result type should be '(.*)'")]
public async Task ThenTheAdapterResultTypeShouldBeAsync(string resultTypeString)
{
AccessControlPolicyResultType resultType = Enum.Parse(resultTypeString);
@@ -247,7 +247,7 @@ public async Task ThenTheAdapterResultTypeShouldBeAsync(string resultTypeString)
Assert.AreEqual(resultType, result.Values.First().ResultType);
}
- [When(@"the other policy (.*) denies access with result '(.*)' and explanation '(.*)'")]
+ [When("the other policy (.*) denies access with result '(.*)' and explanation '(.*)'")]
public void WhenTheOtherPolicyDeniesAccessWithResultAndExplanation(int policyIndex, string resultTypeString, string explanation)
{
AccessControlPolicyResultType resultType = Enum.Parse(resultTypeString);
@@ -263,7 +263,6 @@ private class ShouldAllowArgs
public AccessCheckOperationDescriptor[] Requests { get; set; }
public IOpenApiContext Context { get; set; }
-
}
}
}
diff --git a/Solutions/Menes.Specs/Steps/TestClasses/MappingContext.cs b/Solutions/Menes.Specs/Steps/TestClasses/MappingContext.cs
new file mode 100644
index 000000000..183b27a16
--- /dev/null
+++ b/Solutions/Menes.Specs/Steps/TestClasses/MappingContext.cs
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Endjin. All rights reserved.
+//
+
+namespace Menes.Specs.Steps.TestClasses
+{
+ public class MappingContext
+ {
+ public string ContextProperty1 { get; set; }
+
+ public int ContextProperty2 { get; set; }
+ }
+}
diff --git a/Solutions/Menes.Specs/Steps/TestClasses/PetHalDocumentMapper.cs b/Solutions/Menes.Specs/Steps/TestClasses/PetHalDocumentMapper.cs
new file mode 100644
index 000000000..d97b11da5
--- /dev/null
+++ b/Solutions/Menes.Specs/Steps/TestClasses/PetHalDocumentMapper.cs
@@ -0,0 +1,23 @@
+//
+// Copyright (c) Endjin. All rights reserved.
+//
+
+namespace Menes.Specs.Steps.TestClasses
+{
+ using Menes.Hal;
+
+ public class PetHalDocumentMapper : IHalDocumentMapper
+ {
+ public bool LinkMapConfigured { get; private set; }
+
+ public void ConfigureLinkMap(IOpenApiLinkOperationMap links)
+ {
+ this.LinkMapConfigured = true;
+ }
+
+ public HalDocument Map(Pet resource)
+ {
+ return null;
+ }
+ }
+}
diff --git a/Solutions/Menes.Specs/Steps/TestClasses/PetHalDocumentMapperWithContext.cs b/Solutions/Menes.Specs/Steps/TestClasses/PetHalDocumentMapperWithContext.cs
new file mode 100644
index 000000000..db2f04aba
--- /dev/null
+++ b/Solutions/Menes.Specs/Steps/TestClasses/PetHalDocumentMapperWithContext.cs
@@ -0,0 +1,23 @@
+//
+// Copyright (c) Endjin. All rights reserved.
+//
+
+namespace Menes.Specs.Steps.TestClasses
+{
+ using Menes.Hal;
+
+ public class PetHalDocumentMapperWithContext : IHalDocumentMapper
+ {
+ public bool LinkMapConfigured { get; private set; }
+
+ public void ConfigureLinkMap(IOpenApiLinkOperationMap links)
+ {
+ this.LinkMapConfigured = true;
+ }
+
+ public HalDocument Map(Pet resource, MappingContext context)
+ {
+ return null;
+ }
+ }
+}