/// <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);
            }
        }
Exemple #2
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."
                });
            }
        }
        /// <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;
                    }
                }
            }
        }