/// <summary>
        /// Fetches the value of "summary" tag from xml documentation and populates operation's summary.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public IList <GenerationError> Apply(
            OpenApiOperation operation,
            XElement element,
            OperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                var summaryElement = element.Descendants().FirstOrDefault(i => i.Name == KnownXmlStrings.Summary);

                if (summaryElement == null)
                {
                    return(generationErrors);
                }

                if (string.IsNullOrWhiteSpace(operation.Summary))
                {
                    operation.Summary = summaryElement.GetDescriptionText();
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
        /// <summary>
        /// Fetches the value of "security" tags from xml documentation and populates operation's security requirement
        /// values.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        public IList <GenerationError> Apply(
            OpenApiOperation operation,
            XElement element,
            OperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                if (settings == null)
                {
                    throw new ArgumentNullException(nameof(settings));
                }

                if (element == null)
                {
                    throw new ArgumentNullException(nameof(element));
                }

                if (operation == null)
                {
                    throw new ArgumentNullException(nameof(operation));
                }

                var securityElements = element.Elements()
                                       .Where(
                    p => p.Name == KnownXmlStrings.Security);

                if (!securityElements.Any())
                {
                    return(generationErrors);
                }

                var openApiSecurityRequirement = new OpenApiSecurityRequirement();
                var securitySchemeRegistry     = settings.ReferenceRegistryManager.SecuritySchemeReferenceRegistry;

                foreach (var securityElement in securityElements)
                {
                    var securityScheme = securitySchemeRegistry.FindOrAddReference(securityElement);

                    openApiSecurityRequirement.Add(securityScheme, securitySchemeRegistry.Scopes);
                }

                operation.Security.Add(openApiSecurityRequirement);
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Fetches the value of "summary" tag from xml documentation and populates operation's summary.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            var summaryElement = element.Descendants().FirstOrDefault(i => i.Name == KnownXmlStrings.Summary);

            if (summaryElement == null)
            {
                return;
            }

            if (string.IsNullOrWhiteSpace(operation.Summary))
            {
                operation.Summary = summaryElement.GetDescriptionText();
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Fetches the value of "remarks" tag from xml documentation and populates operation's description.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            string description    = null;
            var    remarksElement = element.Descendants().FirstOrDefault(i => i.Name == KnownXmlStrings.Remarks);

            if (remarksElement != null)
            {
                description = remarksElement.GetDescriptionText();
            }

            if (string.IsNullOrWhiteSpace(operation.Description))
            {
                operation.Description = description;
            }
        }
        /// <summary>
        /// Fetches the value of "group" tag from xml documentation and populates operation's tag.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            var groupElement = element.Descendants().FirstOrDefault(i => i.Name == KnownXmlStrings.Group);

            var groupValue = groupElement?.Value.Trim();

            if (string.IsNullOrWhiteSpace(groupValue))
            {
                return;
            }

            if (!operation.Tags.Select(t => t.Name).Contains(groupValue))
            {
                operation.Tags.Add(
                    new OpenApiTag
                {
                    Name = groupValue
                }
                    );
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Fetches the value of "group" tag from xml documentation and populates operation's tag.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        public IList <GenerationError> Apply(
            OpenApiOperation operation,
            XElement element,
            OperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                var groupElement = element.Descendants().FirstOrDefault(i => i.Name == KnownXmlStrings.Group);

                var groupValue = groupElement?.Value.Trim();

                if (string.IsNullOrWhiteSpace(groupValue))
                {
                    return(generationErrors);
                }

                if (!operation.Tags.Select(t => t.Name).Contains(groupValue))
                {
                    operation.Tags.Add(
                        new OpenApiTag
                    {
                        Name = groupValue
                    }
                        );
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Fetches the value of "security" tags from xml documentation and populates operation's security requirement
        /// values.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings));
            }

            if (element == null)
            {
                throw new ArgumentNullException(nameof(element));
            }

            if (operation == null)
            {
                throw new ArgumentNullException(nameof(operation));
            }

            var securityElements = element.Elements()
                                   .Where(
                p => p.Name == KnownXmlStrings.Security);

            if (!securityElements.Any())
            {
                return;
            }

            var openApiSecurityRequirement = new OpenApiSecurityRequirement();
            var securitySchemeRegistry     = settings.ReferenceRegistryManager.SecuritySchemeReferenceRegistry;

            foreach (var securityElement in securityElements)
            {
                var securityScheme = securitySchemeRegistry.FindOrAddReference(securityElement);

                openApiSecurityRequirement.Add(securityScheme, securitySchemeRegistry.Scopes);
            }

            operation.Security.Add(openApiSecurityRequirement);
        }
        /// <summary>
        /// Fetches the value of "param" tags from xml documentation with in valus of "body"
        /// and populates operation's request body.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public IList <GenerationError> Apply(
            OpenApiOperation operation,
            XElement element,
            OperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                Dictionary <string, OpenApiSchema> requestSchemas = new Dictionary <string, OpenApiSchema>();

                var bodyParams = element.Elements()
                                 .Where(
                    p => p.Name == KnownXmlStrings.Param &&
                    p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Body)
                                 .ToList();

                var generationContext = settings.GenerationContext;
                foreach (var bodyParam in bodyParams)
                {
                    var name      = bodyParam.Attribute(KnownXmlStrings.Name)?.Value.Trim();
                    var mediaType = bodyParam.Attribute(KnownXmlStrings.Type)?.Value ?? "application/json";

                    var allListedTypes = bodyParam.GetListedTypes();

                    if (!allListedTypes.Any())
                    {
                        throw new InvalidRequestBodyException(
                                  string.Format(SpecificationGenerationMessages.MissingSeeCrefTag, name));
                    }

                    var crefKey = allListedTypes.ToCrefKey();

                    OpenApiSchema schema = new OpenApiSchema();
                    if (generationContext.CrefToSchemaMap.ContainsKey(crefKey))
                    {
                        var schemaInfo = generationContext.CrefToSchemaMap[crefKey];

                        if (schemaInfo.Error != null)
                        {
                            generationErrors.Add(schemaInfo.Error);

                            return(generationErrors);
                        }

                        schemaInfo.Schema.CopyInto(schema);
                    }

                    if (!requestSchemas.ContainsKey(mediaType))
                    {
                        requestSchemas[mediaType] = new OpenApiSchema {
                            Type = "object"
                        };
                    }

                    bool nullable     = false;
                    bool required     = false;
                    var  defaultValue = bodyParam.Attribute(KnownXmlStrings.Default);
                    if (bodyParam.Attribute(KnownXmlStrings.Nullable) != null)
                    {
                        nullable = (element.Attribute(KnownXmlStrings.Nullable) != null &&
                                    element.Attribute(KnownXmlStrings.Nullable).Value == "true");
                        required = false;
                    }
                    else if (bodyParam.Attribute(KnownXmlStrings.Required) != null)
                    {
                        nullable = false;
                        required = true;
                    }
                    else if (bodyParam.Attribute(KnownXmlStrings.Default) != null)
                    {
                        nullable = true;
                        required = false;

                        if (defaultValue != null)
                        {
                            if (bodyParam.Attribute(KnownXmlStrings.Cref).Value == $"T:{typeof(int).FullName}")
                            {
                                schema.Default = new OpenApiInteger(int.Parse(defaultValue.Value));
                            }
                            else if (bodyParam.Attribute(KnownXmlStrings.Cref).Value == $"T:{typeof(string).FullName}")
                            {
                                schema.Default = new OpenApiString(defaultValue.Value);
                            }
                            else if (bodyParam.Attribute(KnownXmlStrings.Cref).Value == $"T:{typeof(bool).FullName}")
                            {
                                schema.Default = new OpenApiBoolean(bool.Parse(defaultValue.Value));
                            }
                        }
                    }
                    else
                    {
                        required = true;
                    }


                    requestSchemas[mediaType].Properties.Add(name, schema);
                    if (required && !nullable)
                    {
                        if (requestSchemas[mediaType].Required == null)
                        {
                            requestSchemas[mediaType].Required = new HashSet <string>();
                        }
                        requestSchemas[mediaType].Required.Add(name);
                    }
                }

                if (bodyParams.Count > 0)
                {
                    foreach (var requestSchemaMediaType in requestSchemas.Keys)
                    {
                        var requestSchema = requestSchemas[requestSchemaMediaType];
                        if (requestSchema.Properties.Count >= 1)
                        {
                            var schemaReferenceDefaultVariant = generationContext.VariantSchemaReferenceMap[DocumentVariantInfo.Default];

                            string requestbodySchemaName      = operation.OperationId + "Request";
                            var    requestbodySchemaNameChars = requestbodySchemaName.ToCharArray();
                            requestbodySchemaNameChars[0] = requestbodySchemaName[0].ToString().ToUpper()[0];
                            requestbodySchemaName         = new string(requestbodySchemaNameChars);

                            schemaReferenceDefaultVariant[requestbodySchemaName] = requestSchema;

                            OpenApiSchema schema;
                            var           bodyParam = bodyParams.First();
                            if (bodyParams.First().Attribute("requestmodel") != null && bodyParam.Attribute("requestmodel").Value == "true")
                            {
                                var refSchemaName       = requestSchema.Properties.Values.ToList()[0].Reference.Id;
                                var refSchemaProperties = schemaReferenceDefaultVariant[refSchemaName].Properties;
                                schema = new OpenApiSchema {
                                    Reference = new OpenApiReference {
                                        Id = refSchemaName, Type = ReferenceType.Schema
                                    }
                                };
                            }
                            else
                            {
                                schema = new OpenApiSchema {
                                    Reference = new OpenApiReference {
                                        Id = requestbodySchemaName, Type = ReferenceType.Schema
                                    }
                                };
                            }

                            operation.RequestBody = new OpenApiRequestBody
                            {
                                Content = { [requestSchemaMediaType] = new OpenApiMediaType {
                                                Schema = schema
                                            } },
                                Required = true
                            };
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Fetches the value of "param" tags from xml documentation and populates operation's parameters values.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <returns>The list of generation errors(if any) produced when processing the filter.</returns>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public IList <GenerationError> Apply(
            OpenApiOperation operation,
            XElement element,
            OperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                var paramElements = element.Elements()
                                    .Where(
                    p => p.Name == KnownXmlStrings.Param)
                                    .ToList();

                // Query paramElements again to get all the parameter elements that have "in" attribute.
                // This will include those whose "in" attribute were just populated in PopulateInAttributeFilter, but exclude
                // those that have "in" attribute being "body" since they will be handled as a request body.
                var paramElementsWithIn = paramElements.Where(
                    p =>
                    KnownXmlStrings.InValuesTranslatableToParameter.Contains(
                        p.Attribute(KnownXmlStrings.In)?.Value))
                                          .ToList();

                var generationContext = settings.GenerationContext;

                foreach (var paramElement in paramElementsWithIn)
                {
                    var inValue = paramElement.Attribute(KnownXmlStrings.In)?.Value.Trim();
                    var name    = paramElement.Attribute(KnownXmlStrings.Name)?.Value.Trim();

                    if (settings.RemoveRoslynDuplicateStringFromParamName)
                    {
                        name = name.RemoveRoslynDuplicateString();
                    }

                    if (inValue == KnownXmlStrings.Path &&
                        !settings.Path.Contains($"{{{name}}}", StringComparison.InvariantCultureIgnoreCase))
                    {
                        continue;
                    }

                    var isRequired = paramElement.Attribute(KnownXmlStrings.Required)?.Value.Trim();
                    var cref       = paramElement.Attribute(KnownXmlStrings.Cref)?.Value.Trim();

                    var description = paramElement.GetDescriptionTextFromLastTextNode();

                    var allListedTypes = paramElement.GetListedTypes();

                    OpenApiSchema schema = new OpenApiSchema();

                    if (!allListedTypes.Any())
                    {
                        // Set default schema as string.
                        schema = new OpenApiSchema()
                        {
                            Type = "string"
                        };
                    }

                    var crefKey = allListedTypes.ToCrefKey();

                    if (generationContext.CrefToSchemaMap.ContainsKey(crefKey))
                    {
                        var schemaInfo = generationContext.CrefToSchemaMap[crefKey];

                        if (schemaInfo.Error != null)
                        {
                            generationErrors.Add(schemaInfo.Error);

                            return(generationErrors);
                        }

                        schemaInfo.Schema.CopyInto(schema);
                    }

                    var parameterLocation = GetParameterKind(inValue);

                    var examples = paramElement.ToOpenApiExamples(
                        generationContext.CrefToFieldValueMap,
                        generationErrors);

                    var schemaReferenceDefaultVariant = generationContext
                                                        .VariantSchemaReferenceMap[DocumentVariantInfo.Default];

                    if (examples.Any())
                    {
                        var firstExample = examples.First().Value?.Value;

                        if (firstExample != null)
                        {
                            if (schema.Reference != null)
                            {
                                if (schemaReferenceDefaultVariant.ContainsKey(schema.Reference.Id))
                                {
                                    schemaReferenceDefaultVariant[schema.Reference.Id].Example = firstExample;
                                }
                            }
                            else
                            {
                                schema.Example = firstExample;
                            }
                        }
                    }

                    var openApiParameter = new OpenApiParameter
                    {
                        Name        = name,
                        In          = parameterLocation,
                        Description = description,
                        Required    = parameterLocation == ParameterLocation.Path || Convert.ToBoolean(isRequired),
                        Schema      = schema,
                        Examples    = examples.Any() ? examples : null
                    };

                    operation.Parameters.Add(openApiParameter);
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Fetches the value of "response" tags from xml documentation and populates operation's response.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            var responseElements = element.Elements()
                                   .Where(
                p => p.Name == KnownXmlStrings.Response ||
                p.Name == KnownXmlStrings.ResponseType);

            SchemaReferenceRegistry schemaReferenceRegistry = settings.ReferenceRegistryManager.SchemaReferenceRegistry;

            foreach (var responseElement in responseElements)
            {
                var code = responseElement.Attribute(KnownXmlStrings.Code)?.Value;

                if (string.IsNullOrWhiteSpace(code))
                {
                    // Most APIs only document responses for a successful operation, so if code is not specified,
                    // we will assume it is for a successful operation. This also allows us to comply with OpenAPI spec:
                    //     The Responses Object MUST contain at least one response code,
                    //     and it SHOULD be the response for a successful operation call.
                    code = "200";
                }

                var mediaType = responseElement.Attribute(KnownXmlStrings.Type)?.Value ?? "application/json";

                var description = responseElement.GetDescriptionTextFromLastTextNode();

                var type           = typeof(string);
                var allListedTypes = responseElement.GetListedTypes();

                var responseContractType = settings.TypeFetcher.LoadTypeFromCrefValues(allListedTypes);

                OpenApiSchema schema = null;
                if (responseContractType != null)
                {
                    schema = schemaReferenceRegistry.FindOrAddReference(responseContractType);
                }

                var examples = responseElement.ToOpenApiExamples(settings.TypeFetcher);
                var headers  = responseElement.ToOpenApiHeaders(
                    settings.TypeFetcher,
                    settings.ReferenceRegistryManager.SchemaReferenceRegistry);

                if (schema != null)
                {
                    if (examples.Count > 0)
                    {
                        var firstExample = examples.First().Value?.Value;

                        if (firstExample != null)
                        {
                            if (schema.Reference != null)
                            {
                                var key = schemaReferenceRegistry.GetKey(responseContractType);

                                if (schemaReferenceRegistry.References.ContainsKey(key))
                                {
                                    settings.ReferenceRegistryManager.SchemaReferenceRegistry.References[key].Example
                                        = firstExample;
                                }
                            }
                            else
                            {
                                schema.Example = firstExample;
                            }
                        }
                    }
                }

                if (operation.Responses.ContainsKey(code))
                {
                    if (string.IsNullOrWhiteSpace(operation.Responses[code].Description))
                    {
                        operation.Responses[code].Description = description.RemoveBlankLines();
                    }

                    if (schema != null)
                    {
                        if (!operation.Responses[code].Content.ContainsKey(mediaType))
                        {
                            operation.Responses[code].Content[mediaType] = new OpenApiMediaType
                            {
                                Schema = schema
                            };
                        }
                        else
                        {
                            // If the existing schema is just a single schema (not a list of AnyOf), then
                            // we create a new schema and add that schema to AnyOf to allow us to add
                            // more schemas to it later.
                            if (!operation.Responses[code].Content[mediaType].Schema.AnyOf.Any())
                            {
                                var existingSchema = operation.Responses[code].Content[mediaType].Schema;
                                var newSchema      = new OpenApiSchema();

                                newSchema.AnyOf.Add(existingSchema);

                                operation.Responses[code].Content[mediaType].Schema = newSchema;
                            }

                            operation.Responses[code].Content[mediaType].Schema.AnyOf.Add(schema);
                        }
                    }
                }
                else
                {
                    var response = new OpenApiResponse
                    {
                        Description = description.RemoveBlankLines(),
                    };

                    if (schema != null)
                    {
                        response.Content[mediaType] = new OpenApiMediaType {
                            Schema = schema
                        };
                    }

                    if (headers.Any())
                    {
                        response.Headers = headers;
                    }

                    operation.Responses.Add(code, response);
                }

                if (examples.Count > 0)
                {
                    if (operation.Responses[code].Content[mediaType].Examples.Any())
                    {
                        examples.CopyInto(operation.Responses[code].Content[mediaType].Examples);
                    }
                    else
                    {
                        operation.Responses[code].Content[mediaType].Examples = examples;
                    }
                }
            }

            if (!operation.Responses.Any())
            {
                operation.Responses.Add(
                    "default",
                    new OpenApiResponse {
                    Description = "Responses cannot be located for this operation."
                });
            }
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Fetches the value of "param" tags from xml documentation and populates operation's parameters values.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            var paramElements = element.Elements()
                                .Where(
                p => p.Name == KnownXmlStrings.Param)
                                .ToList();

            // Query paramElements again to get all the parameter elements that have "in" attribute.
            // This will include those whose "in" attribute were just populated in PopulateInAttributeFilter, but exclude
            // those that have "in" attribute being "body" since they will be handled as a request body.
            var paramElementsWithIn = paramElements.Where(
                p =>
                KnownXmlStrings.InValuesTranslatableToParameter.Contains(
                    p.Attribute(KnownXmlStrings.In)?.Value))
                                      .ToList();

            foreach (var paramElement in paramElementsWithIn)
            {
                var inValue = paramElement.Attribute(KnownXmlStrings.In)?.Value.Trim();
                var name    = paramElement.Attribute(KnownXmlStrings.Name)?.Value.Trim();

                if (inValue == KnownXmlStrings.Path &&
                    !settings.Path.Contains($"{{{name}}}", StringComparison.InvariantCultureIgnoreCase))
                {
                    continue;
                }

                var isRequired = paramElement.Attribute(KnownXmlStrings.Required)?.Value.Trim();
                var cref       = paramElement.Attribute(KnownXmlStrings.Cref)?.Value.Trim();

                var childNodes  = paramElement.DescendantNodes().ToList();
                var description = string.Empty;

                var lastNode = childNodes.LastOrDefault();

                if (lastNode != null && lastNode.NodeType == XmlNodeType.Text)
                {
                    description = lastNode.ToString().Trim().RemoveBlankLines();
                }

                // Fetch if any see tags are present, if present populate listed types with it.
                var seeNodes = paramElement.Descendants(KnownXmlStrings.See);

                var allListedTypes = seeNodes
                                     .Select(node => node.Attribute(KnownXmlStrings.Cref)?.Value)
                                     .Where(crefValue => crefValue != null).ToList();

                // If no see tags are present, add the value from cref tag.
                if (!allListedTypes.Any() && !string.IsNullOrWhiteSpace(cref))
                {
                    allListedTypes.Add(cref);
                }

                var schema            = GenerateSchemaFromCref(allListedTypes, settings);
                var parameterLocation = GetParameterKind(inValue);

                operation.Parameters.Add(
                    new OpenApiParameter
                {
                    Name        = name,
                    In          = parameterLocation,
                    Description = description,
                    Required    = parameterLocation == ParameterLocation.Path || Convert.ToBoolean(isRequired),
                    Schema      = schema
                });
            }
        }
        /// <summary>
        /// Fetches the value of "response" tags from xml documentation and populates operation's response.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            var responseElements = element.Elements()
                                   .Where(
                p => p.Name == KnownXmlStrings.Response ||
                p.Name == KnownXmlStrings.ResponseType);

            foreach (var responseElement in responseElements)
            {
                var code = responseElement.Attribute(KnownXmlStrings.Code)?.Value;

                if (string.IsNullOrWhiteSpace(code))
                {
                    // Most APIs only document responses for a successful operation, so if code is not specified,
                    // we will assume it is for a successful operation. This also allows us to comply with OpenAPI spec:
                    //     The Responses Object MUST contain at least one response code,
                    //     and it SHOULD be the response for a successful operation call.
                    code = "200";
                }

                var mediaType = responseElement.Attribute(KnownXmlStrings.Type)?.Value ?? "application/json";

                var childNodes  = responseElement.DescendantNodes().ToList();
                var description = string.Empty;

                var lastNode = childNodes.LastOrDefault();

                if (lastNode != null && lastNode.NodeType == XmlNodeType.Text)
                {
                    description = lastNode.ToString();
                }

                var seeNodes = responseElement.Descendants(KnownXmlStrings.See);

                var allListedTypes = seeNodes
                                     .Select(node => node.Attribute(KnownXmlStrings.Cref)?.Value)
                                     .Where(crefValue => crefValue != null).ToList();

                OpenApiSchema schema = null;

                if (allListedTypes.Any())
                {
                    var responseContractType = settings.TypeFetcher.LoadTypeFromCrefValues(allListedTypes);
                    schema = settings.ReferenceRegistryManager.SchemaReferenceRegistry.FindOrAddReference(
                        responseContractType);
                }

                if (operation.Responses.ContainsKey(code))
                {
                    if (string.IsNullOrWhiteSpace(operation.Responses[code].Description))
                    {
                        operation.Responses[code].Description = description.RemoveBlankLines();
                    }

                    if (schema != null)
                    {
                        if (!operation.Responses[code].Content.ContainsKey(mediaType))
                        {
                            operation.Responses[code].Content[mediaType] = new OpenApiMediaType
                            {
                                Schema = schema
                            };
                        }
                        else
                        {
                            // If the existing schema is just a single schema (not a list of AnyOf), then
                            // we create a new schema and add that schema to AnyOf to allow us to add
                            // more schemas to it later.
                            if (!operation.Responses[code].Content[mediaType].Schema.AnyOf.Any())
                            {
                                var existingSchema = operation.Responses[code].Content[mediaType].Schema;
                                var newSchema      = new OpenApiSchema();

                                newSchema.AnyOf.Add(existingSchema);

                                operation.Responses[code].Content[mediaType].Schema = newSchema;
                            }

                            operation.Responses[code].Content[mediaType].Schema.AnyOf.Add(schema);
                        }
                    }
                }
                else
                {
                    var response = new OpenApiResponse
                    {
                        Description = description.RemoveBlankLines(),
                    };

                    if (schema != null)
                    {
                        response.Content[mediaType] = new OpenApiMediaType {
                            Schema = schema
                        };
                    }

                    operation.Responses.Add(code, response);
                }
            }

            if (!operation.Responses.Any())
            {
                operation.Responses.Add(
                    "default",
                    new OpenApiResponse {
                    Description = "Responses cannot be located for this operation."
                });
            }
        }
        /// <summary>
        /// Fetches the value of "response" tags from xml documentation and populates operation's response.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public IList <GenerationError> Apply(
            OpenApiOperation operation,
            XElement element,
            OperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                var responseElements = element.Elements()
                                       .Where(
                    p => p.Name == KnownXmlStrings.Response ||
                    p.Name == KnownXmlStrings.ResponseType);

                var generationContext = settings.GenerationContext;

                foreach (var responseElement in responseElements)
                {
                    var code = responseElement.Attribute(KnownXmlStrings.Code)?.Value;

                    if (string.IsNullOrWhiteSpace(code))
                    {
                        // Most APIs only document responses for a successful operation, so if code is not specified,
                        // we will assume it is for a successful operation. This also allows us to comply with OpenAPI spec:
                        //     The Responses Object MUST contain at least one response code,
                        //     and it SHOULD be the response for a successful operation call.
                        code = "200";
                    }

                    var mediaType = responseElement.Attribute(KnownXmlStrings.Type)?.Value ?? "application/json";

                    var description = responseElement.GetDescriptionTextFromLastTextNode();

                    var allListedTypes = responseElement.GetListedTypes();

                    var crefKey = allListedTypes.ToCrefKey();

                    OpenApiSchema schema = null;
                    if (generationContext.CrefToSchemaMap.ContainsKey(crefKey))
                    {
                        var schemaInfo = generationContext.CrefToSchemaMap[crefKey];

                        if (schemaInfo.Error != null)
                        {
                            generationErrors.Add(schemaInfo.Error);

                            return(generationErrors);
                        }

                        schema = new OpenApiSchema();
                        schemaInfo.Schema.CopyInto(schema);
                    }

                    var examples = responseElement.ToOpenApiExamples(
                        generationContext.CrefToFieldValueMap,
                        generationErrors);

                    var headers = responseElement.ToOpenApiHeaders(generationContext.CrefToSchemaMap, generationErrors);

                    var schemaReferenceDefaultVariant = generationContext
                                                        .VariantSchemaReferenceMap[DocumentVariantInfo.Default];

                    if (schema != null)
                    {
                        if (examples.Count > 0)
                        {
                            var firstExample = examples.First().Value?.Value;

                            if (firstExample != null)
                            {
                                if (schema.Reference != null)
                                {
                                    if (schemaReferenceDefaultVariant.ContainsKey(schema.Reference.Id))
                                    {
                                        schemaReferenceDefaultVariant[schema.Reference.Id].Example = firstExample;
                                    }
                                }
                                else
                                {
                                    schema.Example = firstExample;
                                }
                            }
                        }
                    }

                    if (operation.Responses.ContainsKey(code))
                    {
                        if (string.IsNullOrWhiteSpace(operation.Responses[code].Description))
                        {
                            operation.Responses[code].Description = description.RemoveBlankLines();
                        }

                        if (schema != null)
                        {
                            if (!operation.Responses[code].Content.ContainsKey(mediaType))
                            {
                                operation.Responses[code].Content[mediaType] = new OpenApiMediaType
                                {
                                    Schema = schema
                                };
                            }
                            else
                            {
                                // If the existing schema is just a single schema (not a list of AnyOf), then
                                // we create a new schema and add that schema to AnyOf to allow us to add
                                // more schemas to it later.
                                if (!operation.Responses[code].Content[mediaType].Schema.AnyOf.Any())
                                {
                                    var existingSchema = operation.Responses[code].Content[mediaType].Schema;
                                    var newSchema      = new OpenApiSchema();

                                    newSchema.AnyOf.Add(existingSchema);

                                    operation.Responses[code].Content[mediaType].Schema = newSchema;
                                }

                                operation.Responses[code].Content[mediaType].Schema.AnyOf.Add(schema);
                            }
                        }
                    }
                    else
                    {
                        var response = new OpenApiResponse
                        {
                            Description = description.RemoveBlankLines(),
                        };

                        if (schema != null)
                        {
                            response.Content[mediaType] = new OpenApiMediaType {
                                Schema = schema
                            };
                        }

                        if (headers != null && headers.Any())
                        {
                            response.Headers = headers;
                        }

                        operation.Responses.Add(code, response);
                    }

                    if (examples.Count > 0)
                    {
                        if (operation.Responses[code].Content[mediaType].Examples.Any())
                        {
                            examples.CopyInto(operation.Responses[code].Content[mediaType].Examples);
                        }
                        else
                        {
                            operation.Responses[code].Content[mediaType].Examples = examples;
                        }
                    }
                }

                if (!operation.Responses.Any())
                {
                    operation.Responses.Add(
                        "default",
                        new OpenApiResponse {
                        Description = "Responses cannot be located for this operation."
                    });
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
        /// <summary>
        /// Fetches the value of "param" tags from xml documentation with in valus of "body"
        /// and populates operation's request body.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public IList <GenerationError> Apply(
            OpenApiOperation operation,
            XElement element,
            OperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                var bodyElements = element.Elements()
                                   .Where(
                    p => p.Name == KnownXmlStrings.Param &&
                    p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Body)
                                   .ToList();

                var generationContext = settings.GenerationContext;

                foreach (var bodyElement in bodyElements)
                {
                    var name      = bodyElement.Attribute(KnownXmlStrings.Name)?.Value.Trim();
                    var mediaType = bodyElement.Attribute(KnownXmlStrings.Type)?.Value ?? "application/json";

                    var description = bodyElement.GetDescriptionTextFromLastTextNode();

                    var allListedTypes = bodyElement.GetListedTypes();

                    if (!allListedTypes.Any())
                    {
                        throw new InvalidRequestBodyException(
                                  string.Format(SpecificationGenerationMessages.MissingSeeCrefTag, name));
                    }

                    var crefKey = allListedTypes.ToCrefKey();

                    OpenApiSchema schema = new OpenApiSchema();
                    if (generationContext.CrefToSchemaMap.ContainsKey(crefKey))
                    {
                        var schemaInfo = generationContext.CrefToSchemaMap[crefKey];

                        if (schemaInfo.Error != null)
                        {
                            generationErrors.Add(schemaInfo.Error);

                            return(generationErrors);
                        }

                        schemaInfo.Schema.CopyInto(schema);
                    }

                    var examples = bodyElement.ToOpenApiExamples(
                        generationContext.CrefToFieldValueMap,
                        generationErrors);

                    var schemaReferenceDefaultVariant = generationContext
                                                        .VariantSchemaReferenceMap[DocumentVariantInfo.Default];

                    if (examples.Count > 0)
                    {
                        var firstExample = examples.First().Value?.Value;

                        if (firstExample != null)
                        {
                            // In case a schema is a reference, find that schema object in schema registry
                            // and update the example.
                            if (schema.Reference != null)
                            {
                                if (schemaReferenceDefaultVariant.ContainsKey(schema.Reference.Id))
                                {
                                    schemaReferenceDefaultVariant[schema.Reference.Id].Example = firstExample;
                                }
                            }
                            else
                            {
                                schema.Example = firstExample;
                            }
                        }
                    }

                    if (operation.RequestBody == null)
                    {
                        operation.RequestBody = new OpenApiRequestBody
                        {
                            Description = description.RemoveBlankLines(),
                            Content     =
                            {
                                [mediaType] = new OpenApiMediaType {
                                    Schema = schema
                                }
                            },
                            Required = true
                        };
                    }
                    else
                    {
                        if (string.IsNullOrWhiteSpace(operation.RequestBody.Description))
                        {
                            operation.RequestBody.Description = description.RemoveBlankLines();
                        }

                        if (!operation.RequestBody.Content.ContainsKey(mediaType))
                        {
                            operation.RequestBody.Content[mediaType] = new OpenApiMediaType
                            {
                                Schema = schema
                            };
                        }
                        else
                        {
                            if (!operation.RequestBody.Content[mediaType].Schema.AnyOf.Any())
                            {
                                var existingSchema = operation.RequestBody.Content[mediaType].Schema;
                                var newSchema      = new OpenApiSchema();
                                newSchema.AnyOf.Add(existingSchema);

                                operation.RequestBody.Content[mediaType].Schema = newSchema;
                            }

                            operation.RequestBody.Content[mediaType].Schema.AnyOf.Add(schema);
                        }
                    }

                    if (examples.Count > 0)
                    {
                        if (operation.RequestBody.Content[mediaType].Examples.Any())
                        {
                            examples.CopyInto(operation.RequestBody.Content[mediaType].Examples);
                        }
                        else
                        {
                            operation.RequestBody.Content[mediaType].Examples = examples;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
        /// <summary>
        /// Fetches the value of "param" tags from xml documentation with in valus of "body"
        /// and populates operation's request body.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            var bodyElements = element.Elements()
                               .Where(
                p => p.Name == KnownXmlStrings.Param &&
                p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Body)
                               .ToList();

            SchemaReferenceRegistry schemaReferenceRegistry = settings.ReferenceRegistryManager.SchemaReferenceRegistry;

            foreach (var bodyElement in bodyElements)
            {
                var name      = bodyElement.Attribute(KnownXmlStrings.Name)?.Value.Trim();
                var mediaType = bodyElement.Attribute(KnownXmlStrings.Type)?.Value ?? "application/json";

                var description = bodyElement.GetDescriptionTextFromLastTextNode();

                var allListedTypes = bodyElement.GetListedTypes();

                if (!allListedTypes.Any())
                {
                    throw new InvalidRequestBodyException(
                              string.Format(SpecificationGenerationMessages.MissingSeeCrefTag, name));
                }

                var type   = settings.TypeFetcher.LoadTypeFromCrefValues(allListedTypes);
                var schema = schemaReferenceRegistry.FindOrAddReference(type);

                var examples = bodyElement.ToOpenApiExamples(settings.TypeFetcher);

                if (examples.Count > 0)
                {
                    var firstExample = examples.First().Value?.Value;

                    if (firstExample != null)
                    {
                        // In case a schema is a reference, find that schmea object in schema registry
                        // and update the example.
                        if (schema.Reference != null)
                        {
                            var key = schemaReferenceRegistry.GetKey(type);

                            if (schemaReferenceRegistry.References.ContainsKey(key))
                            {
                                settings.ReferenceRegistryManager.SchemaReferenceRegistry.References[key].Example
                                    = firstExample;
                            }
                        }
                        else
                        {
                            schema.Example = firstExample;
                        }
                    }
                }

                if (operation.RequestBody == null)
                {
                    operation.RequestBody = new OpenApiRequestBody
                    {
                        Description = description.RemoveBlankLines(),
                        Content     =
                        {
                            [mediaType] = new OpenApiMediaType {
                                Schema = schema
                            }
                        },
                        Required = true
                    };
                }
                else
                {
                    if (string.IsNullOrWhiteSpace(operation.RequestBody.Description))
                    {
                        operation.RequestBody.Description = description.RemoveBlankLines();
                    }

                    if (!operation.RequestBody.Content.ContainsKey(mediaType))
                    {
                        operation.RequestBody.Content[mediaType] = new OpenApiMediaType
                        {
                            Schema = schema
                        };
                    }
                    else
                    {
                        if (!operation.RequestBody.Content[mediaType].Schema.AnyOf.Any())
                        {
                            var existingSchema = operation.RequestBody.Content[mediaType].Schema;
                            var newSchema      = new OpenApiSchema();
                            newSchema.AnyOf.Add(existingSchema);

                            operation.RequestBody.Content[mediaType].Schema = newSchema;
                        }

                        operation.RequestBody.Content[mediaType].Schema.AnyOf.Add(schema);
                    }
                }

                if (examples.Count > 0)
                {
                    if (operation.RequestBody.Content[mediaType].Examples.Any())
                    {
                        examples.CopyInto(operation.RequestBody.Content[mediaType].Examples);
                    }
                    else
                    {
                        operation.RequestBody.Content[mediaType].Examples = examples;
                    }
                }
            }
        }
        /// <summary>
        /// Fetches the value of "param" tags from xml documentation and populates operation's parameters values.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            var paramElements = element.Elements()
                                .Where(
                p => p.Name == KnownXmlStrings.Param)
                                .ToList();

            // Query paramElements again to get all the parameter elements that have "in" attribute.
            // This will include those whose "in" attribute were just populated in PopulateInAttributeFilter, but exclude
            // those that have "in" attribute being "body" since they will be handled as a request body.
            var paramElementsWithIn = paramElements.Where(
                p =>
                KnownXmlStrings.InValuesTranslatableToParameter.Contains(
                    p.Attribute(KnownXmlStrings.In)?.Value))
                                      .ToList();

            SchemaReferenceRegistry schemaReferenceRegistry
                = settings.ReferenceRegistryManager.SchemaReferenceRegistry;

            foreach (var paramElement in paramElementsWithIn)
            {
                var inValue = paramElement.Attribute(KnownXmlStrings.In)?.Value.Trim();
                var name    = paramElement.Attribute(KnownXmlStrings.Name)?.Value.Trim();

                if (inValue == KnownXmlStrings.Path &&
                    !settings.Path.Contains($"{{{name}}}", StringComparison.InvariantCultureIgnoreCase))
                {
                    continue;
                }

                var isRequired = paramElement.Attribute(KnownXmlStrings.Required)?.Value.Trim();
                var cref       = paramElement.Attribute(KnownXmlStrings.Cref)?.Value.Trim();

                var description = paramElement.GetDescriptionTextFromLastTextNode();

                var type           = typeof(string);
                var allListedTypes = paramElement.GetListedTypes();

                if (allListedTypes.Any())
                {
                    type = settings.TypeFetcher.LoadTypeFromCrefValues(allListedTypes);
                }

                var schema            = schemaReferenceRegistry.FindOrAddReference(type);
                var parameterLocation = GetParameterKind(inValue);

                var examples = paramElement.ToOpenApiExamples(settings.TypeFetcher);

                var openApiParameter = new OpenApiParameter
                {
                    Name        = name,
                    In          = parameterLocation,
                    Description = description,
                    Required    = parameterLocation == ParameterLocation.Path || Convert.ToBoolean(isRequired),
                    Schema      = schema
                };

                if (examples.Count > 0)
                {
                    var firstExample = examples.First().Value?.Value;

                    if (firstExample != null)
                    {
                        if (openApiParameter.Schema.Reference != null)
                        {
                            var key = schemaReferenceRegistry.GetKey(type);

                            if (schemaReferenceRegistry.References.ContainsKey(key))
                            {
                                schemaReferenceRegistry.References[key].Example = firstExample;
                            }
                        }
                        else
                        {
                            openApiParameter.Schema.Example = firstExample;
                        }
                    }

                    openApiParameter.Examples = examples;
                }

                operation.Parameters.Add(openApiParameter);
            }
        }
        /// <summary>
        /// Fetches the value of "param" tags from xml documentation with in valus of "body"
        /// and populates operation's request body.
        /// </summary>
        /// <param name="operation">The operation to be updated.</param>
        /// <param name="element">The xml element representing an operation in the annotation xml.</param>
        /// <param name="settings">The operation filter settings.</param>
        /// <remarks>
        /// Care should be taken to not overwrite the existing value in Operation if already present.
        /// This guarantees the predictable behavior that the first tag in the XML will be respected.
        /// It also guarantees that common annotations in the config file do not overwrite the
        /// annotations in the main documentation.
        /// </remarks>
        public void Apply(OpenApiOperation operation, XElement element, OperationFilterSettings settings)
        {
            var bodyElements = element.Elements()
                               .Where(
                p => p.Name == KnownXmlStrings.Param &&
                p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Body)
                               .ToList();

            foreach (var bodyElement in bodyElements)
            {
                var name      = bodyElement.Attribute(KnownXmlStrings.Name)?.Value.Trim();
                var mediaType = bodyElement.Attribute(KnownXmlStrings.Type)?.Value ?? "application/json";

                var childNodes  = bodyElement.DescendantNodes().ToList();
                var description = string.Empty;

                var lastNode = childNodes.LastOrDefault();

                if (lastNode != null && lastNode.NodeType == XmlNodeType.Text)
                {
                    description = lastNode.ToString();
                }

                var seeNodes = bodyElement.Descendants(KnownXmlStrings.See);

                var allListedTypes = seeNodes
                                     .Select(node => node.Attribute(KnownXmlStrings.Cref)?.Value)
                                     .Where(crefValue => crefValue != null).ToList();

                if (!allListedTypes.Any())
                {
                    throw new InvalidRequestBodyException(
                              string.Format(SpecificationGenerationMessages.MissingSeeCrefTag, name));
                }

                var type   = settings.TypeFetcher.LoadTypeFromCrefValues(allListedTypes);
                var schema = settings.ReferenceRegistryManager.SchemaReferenceRegistry.FindOrAddReference(type);

                if (operation.RequestBody == null)
                {
                    operation.RequestBody = new OpenApiRequestBody
                    {
                        Description = description.RemoveBlankLines(),
                        Content     =
                        {
                            [mediaType] = new OpenApiMediaType {
                                Schema = schema
                            }
                        },
                        Required = true
                    };
                }
                else
                {
                    if (string.IsNullOrWhiteSpace(operation.RequestBody.Description))
                    {
                        operation.RequestBody.Description = description.RemoveBlankLines();
                    }

                    if (!operation.RequestBody.Content.ContainsKey(mediaType))
                    {
                        operation.RequestBody.Content[mediaType] = new OpenApiMediaType
                        {
                            Schema = schema
                        };
                    }
                    else
                    {
                        if (!operation.RequestBody.Content[mediaType].Schema.AnyOf.Any())
                        {
                            var existingSchema = operation.RequestBody.Content[mediaType].Schema;
                            var newSchema      = new OpenApiSchema();
                            newSchema.AnyOf.Add(existingSchema);

                            operation.RequestBody.Content[mediaType].Schema = newSchema;
                        }

                        operation.RequestBody.Content[mediaType].Schema.AnyOf.Add(schema);
                    }
                }
            }
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Generates schema from type names in cref.
        /// </summary>
        /// <returns>
        /// Schema from type in cref if the type is resolvable.
        /// Otherwise, default to schema for string type.
        /// </returns>
        private static OpenApiSchema GenerateSchemaFromCref(IList <string> crefValues, OperationFilterSettings settings)
        {
            var type = typeof(string);

            if (crefValues.Any())
            {
                type = settings.TypeFetcher.LoadTypeFromCrefValues(crefValues);
            }

            return(settings.ReferenceRegistryManager.SchemaReferenceRegistry.FindOrAddReference(type));
        }