diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Exceptions/InvalidOperationIdException.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Exceptions/InvalidOperationIdException.cs
new file mode 100644
index 0000000..c48d7f0
--- /dev/null
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Exceptions/InvalidOperationIdException.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System;
+
+namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Exceptions
+{
+ ///
+ /// The exception that is recorded when it is expected to have operationId XML tag
+ /// but it is missing or there are more than one tags.
+ ///
+ [Serializable]
+ internal class InvalidOperationIdException : DocumentationException
+ {
+ ///
+ /// Initializes a new instance of the .
+ ///
+ /// Error message.
+ public InvalidOperationIdException(string message = "")
+ : base(message)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Extensions/StringExtensions.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Extensions/StringExtensions.cs
index 6a43501..e5196de 100644
--- a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Extensions/StringExtensions.cs
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Extensions/StringExtensions.cs
@@ -245,15 +245,5 @@ public static string ToTitleCase(this string value)
return value.Substring(startIndex: 0, length: 1).ToUpperInvariant() + value.Substring(startIndex: 1);
}
-
- ///
- /// Extracts the absolute path from a full URL string.
- ///
- /// The string in URL format.
- /// The absolute path inside the URL.
- public static string UrlStringToAbsolutePath(this string value)
- {
- return WebUtility.UrlDecode(new Uri(value).AbsolutePath);
- }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/FilterSet.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/FilterSet.cs
index 968e15f..1989ce8 100644
--- a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/FilterSet.cs
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/FilterSet.cs
@@ -113,7 +113,7 @@ public static FilterSet GetDefaultFilterSet(FilterSetVersion version)
_defaultFilterSet.Add(new PopulateInAttributeFilter());
_defaultFilterSet.Add(new ConvertAlternativeParamTagsFilter());
_defaultFilterSet.Add(new ValidateInAttributeFilter());
- _defaultFilterSet.Add(new BranchOptionalPathParametersFilter());
+ _defaultFilterSet.Add(new CreateOperationMetaFilter());
//Post processing document filters
_defaultFilterSet.Add(new RemoveFailedGenerationOperationFilter());
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/InternalOpenApiGenerator.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/InternalOpenApiGenerator.cs
index da7d3d9..a9b3ed7 100644
--- a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/InternalOpenApiGenerator.cs
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/InternalOpenApiGenerator.cs
@@ -501,7 +501,7 @@ private IList GenerateSpecificationDocuments(
try
{
- operationMethod = OperationHandler.GetOperationMethod(url, operationElement);
+ operationMethod = OperationHandler.GetOperationMethod(operationElement);
}
catch (InvalidVerbException e)
{
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Models/KnownStrings/KnownXmlStrings.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Models/KnownStrings/KnownXmlStrings.cs
index 5f6d84e..903f87e 100644
--- a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Models/KnownStrings/KnownXmlStrings.cs
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/Models/KnownStrings/KnownXmlStrings.cs
@@ -64,6 +64,7 @@ public class KnownXmlStrings
public const string AuthorizationCode = "authorizationCode";
public const string ClientCredentials = "clientCredentials";
public const string Scope = "scope";
+ public const string OperationId = "operationId";
public static string[] AllowedAppKeyInValues => new[] {Header, Query, Cookie};
public static string[] AllowedFlowTypeValues => new[]
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/OperationHandler.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/OperationHandler.cs
index 329eab2..8e24f93 100644
--- a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/OperationHandler.cs
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/OperationHandler.cs
@@ -19,9 +19,40 @@ namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration
internal static class OperationHandler
{
///
- /// Gets the operation id by parsing segments out of the absolute path.
+ /// Checks whether the optional operation id tag is present in the operation element.
///
- public static string GetOperationId(string absolutePath, OperationType operationMethod)
+ ///
+ /// True if the operationId tag is in the operation element, otherwise false.
+ public static bool HasOperationId(XElement operationElement)
+ {
+ return operationElement?.Elements().Where(p => p.Name == KnownXmlStrings.OperationId).Any() ?? false;
+ }
+
+ ///
+ /// Extracts the operation id from the operation element.
+ ///
+ ///
+ /// Operation id.
+ /// Thrown if the operationId tag is missing or
+ /// there are more than one tags.
+ public static string GetOperationId(XElement operationElement)
+ {
+ var operationIdList = operationElement?.Elements().Where(p => p.Name == KnownXmlStrings.OperationId).ToList();
+ if (operationIdList?.Count == 1)
+ {
+ return operationIdList[0].Value;
+ }
+
+ string error = operationIdList.Count > 1
+ ? SpecificationGenerationMessages.MultipleOperationId
+ : SpecificationGenerationMessages.NoOperationId;
+ throw new InvalidOperationIdException(error);
+ }
+
+ ///
+ /// Generates the operation id by parsing segments out of the absolute path.
+ ///
+ public static string GenerateOperationId(string absolutePath, OperationType operationMethod)
{
var operationId = new StringBuilder(operationMethod.ToString().ToLowerInvariant());
@@ -63,10 +94,9 @@ public static string GetOperationId(string absolutePath, OperationType operation
///
/// Extracts the operation method from the operation element
///
+ /// The xml element representing an operation in the annotation xml.
/// Thrown if the verb is missing or has invalid format.
- public static OperationType GetOperationMethod(
- string url,
- XElement operationElement)
+ public static OperationType GetOperationMethod(XElement operationElement)
{
var verbElement = operationElement.Descendants().FirstOrDefault(i => i.Name == KnownXmlStrings.Verb);
@@ -85,6 +115,7 @@ public static OperationType GetOperationMethod(
///
/// Extracts the URL from the operation element
///
+ /// The xml element representing an operation in the annotation xml.
/// Thrown if the URL is missing or has invalid format.
public static string GetUrl(
XElement operationElement)
@@ -103,7 +134,7 @@ public static string GetUrl(
try
{
- url = WebUtility.UrlDecode(new Uri(url).AbsolutePath);
+ url = WebUtility.UrlDecode(new Uri(WebUtility.UrlDecode(url)).AbsolutePath);
}
catch (UriFormatException)
{
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/BranchOptionalPathParametersFilter.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/BranchOptionalPathParametersFilter.cs
index ad82087..ad40a52 100644
--- a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/BranchOptionalPathParametersFilter.cs
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/BranchOptionalPathParametersFilter.cs
@@ -19,8 +19,18 @@ namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.PreprocessingOp
/// Parses the value of the URL and creates multiple operations in the Paths object when
/// there are optional path parameters.
///
- public class BranchOptionalPathParametersFilter : IPreProcessingOperationFilter
+ public class BranchOptionalPathParametersFilter : ICreateOperationPreProcessingOperationFilter
{
+ ///
+ /// Verifies that the annotation XML element contains all data which are required to apply this filter.
+ ///
+ /// The xml element representing an operation in the annotation xml.
+ /// Always true (this filter can be always applied).
+ public bool IsApplicable(XElement element)
+ {
+ return true;
+ }
+
///
/// Fetches the URL value and creates multiple operations based on optional parameters.
///
@@ -37,30 +47,18 @@ public IList Apply(
try
{
- var paramElements = element.Elements()
+ var paramPathElements = element.Elements()
.Where(
p => p.Name == KnownXmlStrings.Param)
+ .Where(
+ p => p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Path)
.ToList();
- // We need both the full URL and the absolute paths for processing.
- // Full URL contains all path and query parameters.
- // Absolute path is needed to get OperationId parsed out correctly.
- var fullUrl = element.Elements()
- .FirstOrDefault(p => p.Name == KnownXmlStrings.Url)
- ?.Value;
-
- var absolutePath = fullUrl.UrlStringToAbsolutePath();
+ var absolutePath = OperationHandler.GetUrl(element);
- var operationMethod = (OperationType)Enum.Parse(
- typeof(OperationType),
- element.Elements().FirstOrDefault(p => p.Name == KnownXmlStrings.Verb)?.Value,
- ignoreCase: true);
+ var allGeneratedPathStrings = GeneratePossiblePaths(absolutePath, paramPathElements);
- var allGeneratedPathStrings = GeneratePossiblePaths(
- absolutePath,
- paramElements.Where(
- p => p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Path)
- .ToList());
+ var operationMethod = OperationHandler.GetOperationMethod(element);
foreach (var pathString in allGeneratedPathStrings)
{
@@ -72,7 +70,7 @@ public IList Apply(
paths[pathString].Operations[operationMethod] =
new OpenApiOperation
{
- OperationId = OperationHandler.GetOperationId(pathString, operationMethod)
+ OperationId = OperationHandler.GenerateOperationId(pathString, operationMethod)
};
}
}
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/CreateOperationMetaFilter.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/CreateOperationMetaFilter.cs
new file mode 100644
index 0000000..3ea090b
--- /dev/null
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/CreateOperationMetaFilter.cs
@@ -0,0 +1,81 @@
+// ------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
+// ------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Xml.Linq;
+using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Models;
+using Microsoft.OpenApi.Models;
+
+namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.PreprocessingOperationFilters
+{
+ ///
+ /// Filter to initialize OpenApi operation based on the annotation XML element.
+ /// It does not do the creation itself but forwards the call to the real generator filters.
+ /// The first filter which is applicable is executed in order to generate the operation.
+ ///
+ public class CreateOperationMetaFilter : IPreProcessingOperationFilter
+ {
+ private List createOperationFilters;
+
+ ///
+ /// Initializes a new instance of the .
+ ///
+ ///
+ /// Using this constructor, the following filter list is used:
+ /// If the XML element contains 'operationId' tag, it is used as unique identifier
+ /// of the operation. Otherwise, the id is generated using the path of the operation.
+ /// In the latter case, multiple operation could be generated, if the path has optional
+ /// parameters.
+ ///
+ public CreateOperationMetaFilter() :
+ this(new List {
+ new UsePredefinedOperationIdFilter(), new BranchOptionalPathParametersFilter()})
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the .
+ ///
+ /// List of generator filters.
+ internal CreateOperationMetaFilter(
+ List createOperationFilters)
+ {
+ this.createOperationFilters = createOperationFilters;
+ }
+
+ ///
+ /// Initializes the OpenApi operation based on the annotation XML element.
+ /// It applies the first applicable filter to create operations.
+ ///
+ /// The paths to be updated.
+ /// The xml element representing an operation in the annotation xml.
+ /// The operation filter settings.
+ /// The list of generation errors, if any produced when processing the filter.
+ public IList Apply(
+ OpenApiPaths paths,
+ XElement element,
+ PreProcessingOperationFilterSettings settings)
+ {
+ foreach (var filter in this.createOperationFilters)
+ {
+ if (filter.IsApplicable(element))
+ {
+ return filter.Apply(paths, element, settings);
+ }
+ }
+
+ // If none of the filters could be applied --> error
+ return new List
+ {
+ new GenerationError
+ {
+ Message = "Failed to apply any operation creation filter.",
+ ExceptionType = nameof(InvalidOperationException)
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/ICreateOperationPreProcessingOperationFilter.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/ICreateOperationPreProcessingOperationFilter.cs
new file mode 100644
index 0000000..3e84540
--- /dev/null
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/ICreateOperationPreProcessingOperationFilter.cs
@@ -0,0 +1,28 @@
+// ------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
+// ------------------------------------------------------------
+
+using System.Collections.Generic;
+using System.Xml.Linq;
+using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Models;
+using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.OperationFilters;
+using Microsoft.OpenApi.Models;
+
+namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.PreprocessingOperationFilters
+{
+ ///
+ /// The class representing the contract of a filter to preprocess the
+ /// objects in before each is processed by the
+ /// .
+ ///
+ public interface ICreateOperationPreProcessingOperationFilter : IPreProcessingOperationFilter
+ {
+ ///
+ /// Verifies that the annotation XML element contains all data which are required to apply this filter.
+ ///
+ /// The xml element representing an operation in the annotation xml.
+ /// True if the filter can be applied, otherwise false.
+ bool IsApplicable(XElement element);
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/UsePredefinedOperationIdFilter.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/UsePredefinedOperationIdFilter.cs
new file mode 100644
index 0000000..48b36ce
--- /dev/null
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/PreprocessingOperationFilters/UsePredefinedOperationIdFilter.cs
@@ -0,0 +1,111 @@
+// ------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
+// ------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Xml.Linq;
+using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Models;
+using Microsoft.OpenApi.Models;
+
+namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.PreprocessingOperationFilters
+{
+ ///
+ /// Filter to creates operation using the 'operationId' tag of the operation.
+ /// It does not consider optional parameters, it create one operation for the full path.
+ ///
+ public class UsePredefinedOperationIdFilter : ICreateOperationPreProcessingOperationFilter
+ {
+ private readonly Func GetUrlFunc;
+ private readonly Func GetOperationMethodFunc;
+ private readonly Func GetOperationIdFunc;
+ private readonly Predicate HasOperationIdFunc;
+
+ ///
+ /// Initializes a new production instance of the .
+ ///
+ public UsePredefinedOperationIdFilter()
+ : this(
+ OperationHandler.GetUrl,
+ OperationHandler.GetOperationMethod,
+ OperationHandler.GetOperationId,
+ OperationHandler.HasOperationId)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the .
+ ///
+ /// Get url function.
+ /// Get operation method function.
+ /// Get operation id function.
+ /// Has operation id function.
+ internal UsePredefinedOperationIdFilter(
+ Func GetUrlFunc,
+ Func GetOperationMethodFunc,
+ Func GetOperationIdFunc,
+ Predicate HasOperationIdFunc)
+ {
+ this.GetUrlFunc = GetUrlFunc;
+ this.GetOperationMethodFunc = GetOperationMethodFunc;
+ this.GetOperationIdFunc = GetOperationIdFunc;
+ this.HasOperationIdFunc = HasOperationIdFunc;
+ }
+
+ ///
+ /// Verifies whether the annotation XML element contains operationId XML tag.
+ ///
+ /// The xml element representing an operation in the annotation xml.
+ ///
+ public bool IsApplicable(XElement element)
+ {
+ return this.HasOperationIdFunc(element);
+ }
+
+ ///
+ /// Creates the operation using the operationId tag of the operation. It does not consider optional
+ /// parameters, it creates one operation for the full path.
+ ///
+ /// The paths to be updated.
+ /// The xml element representing an operation in the annotation xml.
+ /// The operation filter settings.
+ /// The list of generation errors, if any produced when processing the filter.
+ public IList Apply(
+ OpenApiPaths paths,
+ XElement element,
+ PreProcessingOperationFilterSettings settings)
+ {
+ var generationErrors = new List();
+
+ try
+ {
+ var absolutePath = this.GetUrlFunc(element);
+ var operationMethod = this.GetOperationMethodFunc(element);
+ string operationId = this.GetOperationIdFunc(element);
+
+ if (!paths.ContainsKey(absolutePath))
+ {
+ paths[absolutePath] = new OpenApiPathItem();
+ }
+
+ paths[absolutePath].Operations[operationMethod] =
+ new OpenApiOperation
+ {
+ OperationId = operationId
+ };
+ }
+ catch(Exception ex)
+ {
+ generationErrors.Add(
+ new GenerationError
+ {
+ Message = ex.Message,
+ ExceptionType = ex.GetType().Name
+ });
+ }
+
+ return generationErrors;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/SpecificationGenerationMessages.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/SpecificationGenerationMessages.cs
index d83b683..328ab8d 100644
--- a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/SpecificationGenerationMessages.cs
+++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/SpecificationGenerationMessages.cs
@@ -123,5 +123,9 @@ public static class SpecificationGenerationMessages
public const string NotSupportedTypeAttributeValue =
"Documented type: \"{0}\" is not supported for \"type\" attribute value in tag: \"{1}\". Supported values are: {2}.";
+
+ public const string NoOperationId = "No operationId element is present in the operation.";
+
+ public const string MultipleOperationId = "More than one operationId element is not accepted in the operation.";
}
}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/FilterTests/CreateOperationMetaFilterTest.cs b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/FilterTests/CreateOperationMetaFilterTest.cs
new file mode 100644
index 0000000..25ca7be
--- /dev/null
+++ b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/FilterTests/CreateOperationMetaFilterTest.cs
@@ -0,0 +1,239 @@
+// ------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
+// ------------------------------------------------------------
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Linq;
+using FluentAssertions;
+using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Models;
+using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.PreprocessingOperationFilters;
+using Microsoft.OpenApi.Models;
+using Moq;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.FilterTests
+{
+ [Collection("DefaultSettings")]
+ public class CreateOperationMetaFilterTest
+ {
+ private readonly ITestOutputHelper _output;
+
+ public CreateOperationMetaFilterTest(ITestOutputHelper output)
+ {
+ _output = output;
+ }
+
+ [Theory]
+ [MemberData(nameof(GetTestCasesForApplyShouldCallOnlyTheFirstFilterWhichIsApplicable))]
+ public void ApplyShouldCallOnlyTheFirstFilterWhichIsApplicable(
+ string testName,
+ List> mockFilters,
+ int firstApplicableFilterIndex)
+ {
+ _output.WriteLine(testName);
+
+ // Prepare
+ var openApiPaths = new OpenApiPaths();
+ XElement element = new XElement("elem");
+ var settings = new PreProcessingOperationFilterSettings();
+
+ List filters =
+ mockFilters.Select(mockFilter => mockFilter.Object).ToList();
+
+ var filter = new CreateOperationMetaFilter(filters);
+
+ // Action
+ filter.Apply(openApiPaths, element, settings);
+
+ // Assert
+ for(int i = 0; i < firstApplicableFilterIndex - 1; ++i)
+ {
+ mockFilters[i].Verify(mock => mock.Apply(
+ It.IsAny(), It.IsAny(), It.IsAny()),
+ Times.Never());
+ }
+
+ for (int i = firstApplicableFilterIndex + 1; i < mockFilters.Count; ++i)
+ {
+ mockFilters[i].Verify(mock => mock.Apply(
+ It.IsAny(), It.IsAny(), It.IsAny()),
+ Times.Never());
+ }
+
+ mockFilters[firstApplicableFilterIndex].Verify(mock => mock.Apply(
+ openApiPaths, element, settings), Times.Once());
+ }
+
+ [Fact]
+ public void ApplyShouldReturnErrorIfNoneOfTheFiltersCouldBeApplied()
+ {
+ // Prepare
+ var openApiPaths = new OpenApiPaths();
+ XElement element = new XElement("elem");
+ var settings = new PreProcessingOperationFilterSettings();
+
+ var mockFilters = new List>{
+ CreateMockFilter(false),
+ CreateMockFilter(false),
+ CreateMockFilter(false)};
+
+ List filters =
+ mockFilters.Select(mockFilter => mockFilter.Object).ToList();
+
+ var filter = new CreateOperationMetaFilter(filters);
+
+ // Action
+ var errorList = filter.Apply(openApiPaths, element, settings);
+
+ // Assert
+ errorList.Should().OnlyContain(error => error.ExceptionType == "InvalidOperationException");
+ }
+
+ [Fact]
+ public void ApplyShouldNotReturnErrorIfTheAppliedFilterDoesNotReturnError()
+ {
+ // Prepare
+ var openApiPaths = new OpenApiPaths();
+ XElement element = new XElement("elem");
+ var settings = new PreProcessingOperationFilterSettings();
+
+ var successfullFilter = CreateMockFilter(true);
+
+ var mockFilters = new List>{
+ successfullFilter};
+
+ List filters =
+ mockFilters.Select(mockFilter => mockFilter.Object).ToList();
+
+ var filter = new CreateOperationMetaFilter(filters);
+
+ // Action
+ var errorList = filter.Apply(openApiPaths, element, settings);
+
+ // Assert
+ errorList.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ApplyShouldReturnTheErrorListOfTheAppliedFilter()
+ {
+ // Prepare
+ var openApiPaths = new OpenApiPaths();
+ XElement element = new XElement("elem");
+ var settings = new PreProcessingOperationFilterSettings();
+
+ var mockFilterWithError = new Mock();
+
+ var expectedErrorList = new List
+ {
+ new GenerationError
+ {
+ Message = "Message_1",
+ ExceptionType = "ExceptionType_1"
+ },
+ new GenerationError
+ {
+ Message = "Message_2",
+ ExceptionType = "ExceptionType_2"
+ }
+ };
+
+ mockFilterWithError.Setup(
+ mock => mock.Apply(
+ It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns(expectedErrorList);
+
+ mockFilterWithError.Setup(mock => mock.IsApplicable(It.IsAny())).Returns(true);
+
+ var mockFilters = new List>
+ {
+ mockFilterWithError
+ };
+
+ List filters =
+ mockFilters.Select(mockFilter => mockFilter.Object).ToList();
+
+ // Action
+ var filter = new CreateOperationMetaFilter(filters);
+ var errorList = filter.Apply(openApiPaths, element, settings);
+
+ // Assert
+ errorList.Should().ContainInOrder(expectedErrorList);
+ }
+
+ public static IEnumerable