/// <summary>
        /// Fetches the URL value and creates multiple operations based on optional parameters.
        /// </summary>
        /// <param name="paths">The paths 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(
            OpenApiPaths paths,
            XElement element,
            PreProcessingOperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                var paramElements = element.Elements()
                                    .Where(
                    p => p.Name == KnownXmlStrings.Param)
                                    .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 operationMethod = (OperationType)Enum.Parse(
                    typeof(OperationType),
                    element.Elements().FirstOrDefault(p => p.Name == KnownXmlStrings.Verb)?.Value,
                    ignoreCase: true);

                var allGeneratedPathStrings = GeneratePossiblePaths(
                    absolutePath,
                    paramElements.Where(
                        p => p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Path)
                    .ToList());

                foreach (var pathString in allGeneratedPathStrings)
                {
                    if (!paths.ContainsKey(pathString))
                    {
                        paths[pathString] = new OpenApiPathItem();
                    }

                    paths[pathString].Operations[operationMethod] =
                        new OpenApiOperation
                    {
                        OperationId = OperationHandler.GetOperationId(pathString, operationMethod)
                    };
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
Beispiel #2
0
        /// <summary>
        /// Validates the "in" attribute in param tagsif all parameter tags.
        /// </summary>
        /// <param name="paths">The paths to be validated.</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(OpenApiPaths paths, XElement element, PreProcessingOperationFilterSettings settings)
        {
            var paramElements = element.Elements()
                                .Where(p => p.Name == KnownXmlStrings.Param)
                                .ToList();

            var paramElementsWithoutIn = paramElements.Where(p => p.Attribute(KnownXmlStrings.In)?.Value == null)
                                         .ToList();

            var paramElementsWithoutAllowedValues = paramElements.Where(
                p => !KnownXmlStrings.AllowedInValues.Contains(p.Attribute(KnownXmlStrings.In)?.Value)).ToList();

            if (paramElementsWithoutIn.Any())
            {
                throw new MissingInAttributeException(
                          paramElementsWithoutIn.Select(
                              p => p.Attribute(KnownXmlStrings.Name)?.Value));
            }

            if (paramElementsWithoutAllowedValues.Any())
            {
                throw new NotSupportedInAttributeValueException(
                          paramElementsWithoutAllowedValues.Select(
                              p => p.Attribute(KnownXmlStrings.Name)?.Value),
                          paramElementsWithoutAllowedValues.Select(
                              p => p.Attribute(KnownXmlStrings.In)?.Value));
            }

            var url = element.Elements()
                      .FirstOrDefault(p => p.Name == KnownXmlStrings.Url)
                      ?.Value;

            var pathParamElements = paramElements
                                    .Where(p => p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Path)
                                    .ToList();

            var matches = new Regex(@"\{(.*?)\}").Matches(url.Split('?')[0]);

            foreach (Match match in matches)
            {
                var pathParamNameFromUrl = match.Groups[1].Value;

                // All path params in the URL must be documented.
                if (!pathParamElements.Any(p => p.Attribute(KnownXmlStrings.Name)?.Value == pathParamNameFromUrl))
                {
                    throw new UndocumentedPathParameterException(pathParamNameFromUrl, url);
                }
            }
        }
        public void PopulateInAttributeShouldSucceed(
            string testName,
            XElement xElement,
            string expectedExceptionMessage)
        {
            var filter   = new PopulateInAttributeFilter();
            var settings = new PreProcessingOperationFilterSettings();

            var openApiPaths = new OpenApiPaths();

            _output.WriteLine(testName);

            Action action = () => filter.Apply(openApiPaths, xElement, settings);

            action.Should().Throw <DocumentationException>(expectedExceptionMessage);
        }
        public void ValidateInAttributeShouldGenerateErrors(
            string testName,
            XElement xElement,
            IList <GenerationError> expectedGenerationErrors)
        {
            var filter   = new ValidateInAttributeFilter();
            var settings = new PreProcessingOperationFilterSettings();

            var openApiPaths = new OpenApiPaths();

            _output.WriteLine(testName);

            var actualGenerationErrors = filter.Apply(openApiPaths, xElement, settings);

            actualGenerationErrors.Should().BeEquivalentTo(expectedGenerationErrors);
        }
Beispiel #5
0
        /// <summary>
        /// Parses the parameters in the URL to populate the in attribute of the param tags as path or query
        /// if not explicitly documented.
        /// </summary>
        /// <param name="paths">The paths 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(OpenApiPaths paths, XElement element, PreProcessingOperationFilterSettings settings)
        {
            var paramElementsWithoutIn = element.Elements().Where(
                p => p.Name == KnownXmlStrings.Param &&
                p.Attribute(KnownXmlStrings.In)?.Value == null)
                                         .ToList();

            var url = element.Elements()
                      .FirstOrDefault(p => p.Name == KnownXmlStrings.Url)
                      ?.Value;

            if (!string.IsNullOrWhiteSpace(url))
            {
                foreach (var paramElement in paramElementsWithoutIn)
                {
                    var paramName = paramElement.Attribute(KnownXmlStrings.Name)?.Value;

                    if (url.Contains(
                            $"/{{{paramName}}}",
                            StringComparison.InvariantCultureIgnoreCase) &&
                        url.Contains(
                            $"={{{paramName}}}",
                            StringComparison.InvariantCultureIgnoreCase))
                    {
                        // The parameter is in both path and query. We cannot determine what to put for "in" attribute.
                        throw new ConflictingPathAndQueryParametersException(paramName, url);
                    }

                    if (url.Contains(
                            $"/{{{paramName}}}",
                            StringComparison.InvariantCultureIgnoreCase))
                    {
                        paramElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Path));
                    }
                    else if (url.Contains(
                                 $"={{{paramName}}}",
                                 StringComparison.InvariantCultureIgnoreCase))
                    {
                        paramElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Query));
                    }
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Converts the alternative param tags (queryParam, pathParam, header) to standard param tags.
        /// </summary>
        /// <param name="paths">The paths 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(OpenApiPaths paths, XElement element, PreProcessingOperationFilterSettings settings)
        {
            var pathParamElements = element.Elements()
                                    .Where(p => p.Name == KnownXmlStrings.PathParam)
                                    .ToList();

            var queryParamElements = element.Elements()
                                     .Where(p => p.Name == KnownXmlStrings.QueryParam)
                                     .ToList();

            var headerParamElements = element.Elements()
                                      .Where(p => p.Name == KnownXmlStrings.Header)
                                      .ToList();

            var requestTypeElements = element.Elements()
                                      .Where(p => p.Name == KnownXmlStrings.RequestType)
                                      .ToList();

            var paramElements = element.Elements().Where(i => i.Name == KnownXmlStrings.Param);

            if (pathParamElements.Any())
            {
                foreach (var pathParamElement in pathParamElements)
                {
                    var conflictingPathParam = paramElements.Where(
                        i => i.Attribute("name")?.Value == pathParamElement.Attribute("name")?.Value);

                    // Remove param tags that have same name as pathParam tags
                    // e.g. if service is documented like below, it will remove the param tag
                    //
                    // <param name="samplePathParam">Sample path param</param>
                    // <pathParam name="samplePathParam" in="path">Sample path param</pathParam>
                    conflictingPathParam?.Remove();

                    pathParamElement.Name = KnownXmlStrings.Param;
                    pathParamElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Path));
                }
            }

            if (queryParamElements.Any())
            {
                foreach (var queryParamElement in queryParamElements)
                {
                    var conflictingQueryParam = paramElements.Where(
                        i => i.Attribute("name")?.Value == queryParamElement.Attribute("name")?.Value);

                    // Remove param tags that have same name as queryParam tags
                    // e.g. if service is documented like below, it will remove the param tag
                    //
                    // <param name="sampleQueryParam">Sample query param</param>
                    // <queryParam name="sampleQueryParam" in="path">Sample query param</queryParam>
                    conflictingQueryParam?.Remove();

                    queryParamElement.Name = KnownXmlStrings.Param;
                    queryParamElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Query));
                }
            }

            if (requestTypeElements.Any())
            {
                var paramTagToRemove = element.Elements()
                                       .Where(i => i.Name == KnownXmlStrings.Param &&
                                              string.IsNullOrWhiteSpace(i.Attribute("in")?.Value));

                // If there are still conflicting param tags remaining, then it's safe to assume that these are neither
                // path nor query params and could be documented request params which is not intended to be used with
                // C# document generator so remove the tags.
                paramTagToRemove?.Remove();

                foreach (var requestTypeElement in requestTypeElements)
                {
                    requestTypeElement.Name = KnownXmlStrings.Param;
                    requestTypeElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Body));
                }
            }

            foreach (var headerParamElement in headerParamElements)
            {
                headerParamElement.Name = KnownXmlStrings.Param;
                headerParamElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Header));
            }
        }
Beispiel #7
0
        /// <summary>
        /// Converts the alternative param tags (queryParam, pathParam, header) to standard param tags.
        /// </summary>
        /// <param name="paths">The paths 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(
            OpenApiPaths paths,
            XElement element,
            PreProcessingOperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

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

                var queryParamElements = element.Elements()
                                         .Where(p => p.Name == KnownXmlStrings.QueryParam)
                                         .ToList();

                var headerParamElements = element.Elements()
                                          .Where(p => p.Name == KnownXmlStrings.Header)
                                          .ToList();

                var requestTypeElements = element.Elements()
                                          .Where(p => p.Name == KnownXmlStrings.RequestType)
                                          .ToList();

                var paramElements = element.Elements().Where(i => i.Name == KnownXmlStrings.Param);
                var paramElementsWithInAttributeNotSpecified = paramElements.Where(i => i.Attribute("in") == null);

                if (pathParamElements.Any())
                {
                    foreach (var pathParamElement in pathParamElements)
                    {
                        var conflictingPathParam = paramElements.Where(
                            i => i.Attribute("name")?.Value == pathParamElement.Attribute("name")?.Value);

                        // Remove param tags that have same name as pathParam tags
                        // e.g. if service is documented like below, it will remove the param tag
                        //
                        // <param name="samplePathParam">Sample path param</param>
                        // <pathParam name="samplePathParam" in="path">Sample path param</pathParam>
                        conflictingPathParam?.Remove();

                        var nameAttribute = pathParamElement.Attribute(KnownXmlStrings.Name);
                        var name          = nameAttribute?.Value.Trim();

                        if (!string.IsNullOrWhiteSpace(name))
                        {
                            nameAttribute.Value = name;
                        }

                        pathParamElement.Name = KnownXmlStrings.Param;
                        pathParamElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Path));
                    }
                }

                if (queryParamElements.Any())
                {
                    foreach (var queryParamElement in queryParamElements)
                    {
                        var conflictingQueryParam = paramElements.Where(
                            i => i.Attribute("name")?.Value == queryParamElement.Attribute("name")?.Value);

                        // Remove param tags that have same name as queryParam tags
                        // e.g. if service is documented like below, it will remove the param tag
                        //
                        // <param name="sampleQueryParam">Sample query param</param>
                        // <queryParam name="sampleQueryParam" in="path">Sample query param</queryParam>
                        conflictingQueryParam?.Remove();

                        var nameAttribute = queryParamElement.Attribute(KnownXmlStrings.Name);
                        var name          = nameAttribute?.Value.Trim();

                        if (!string.IsNullOrWhiteSpace(name))
                        {
                            nameAttribute.Value = name;
                        }

                        queryParamElement.Name = KnownXmlStrings.Param;
                        queryParamElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Query));
                    }
                }

                if (requestTypeElements.Any())
                {
                    var paramTagToRemove = element.Elements()
                                           .Where(i => i.Name == KnownXmlStrings.Param &&
                                                  string.IsNullOrWhiteSpace(i.Attribute("in")?.Value));

                    // If there are still conflicting param tags remaining, then it's safe to assume that these are neither
                    // path nor query params and could be documented request params which is not intended to be used with
                    // C# document generator so remove the tags.
                    paramTagToRemove?.Remove();

                    foreach (var requestTypeElement in requestTypeElements)
                    {
                        requestTypeElement.Name = KnownXmlStrings.Param;
                        requestTypeElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Body));
                    }
                }

                foreach (var headerParamElement in headerParamElements)
                {
                    var nameAttribute = headerParamElement.Attribute(KnownXmlStrings.Name);
                    var name          = nameAttribute?.Value.Trim();

                    if (!string.IsNullOrWhiteSpace(name))
                    {
                        nameAttribute.Value = name;
                    }

                    headerParamElement.Name = KnownXmlStrings.Param;
                    headerParamElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Header));
                }

                // If any of the alternative tags are present then remove any param tag element that have "in" attribute
                // not specified b/c assumption is made that those params are not supposed to be processed by
                // CSharp Annotation Document Generator.
                if (pathParamElements.Any() ||
                    queryParamElements.Any() ||
                    requestTypeElements.Any() ||
                    headerParamElements.Any())
                {
                    paramElementsWithInAttributeNotSpecified?.Remove();
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
Beispiel #8
0
        /// <summary>
        /// Parses the parameters in the URL to populate the in attribute of the param tags as path or query
        /// if not explicitly documented.
        /// </summary>
        /// <param name="paths">The paths 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(OpenApiPaths paths, XElement element, PreProcessingOperationFilterSettings settings)
        {
            var paramElements = element.Elements()
                                .Where(p => p.Name == KnownXmlStrings.Param)
                                .ToList();

            var paramElementsWithoutIn = paramElements.Where(
                p => p.Attribute(KnownXmlStrings.In)?.Value == null)
                                         .ToList();

            var url = element.Elements()
                      .FirstOrDefault(p => p.Name == KnownXmlStrings.Url)
                      ?.Value;

            if (!string.IsNullOrWhiteSpace(url))
            {
                foreach (var paramElement in paramElementsWithoutIn)
                {
                    var paramName = paramElement.Attribute(KnownXmlStrings.Name)?.Value;

                    if (url.Contains(
                            $"/{{{paramName}}}",
                            StringComparison.InvariantCultureIgnoreCase) &&
                        url.Contains(
                            $"={{{paramName}}}",
                            StringComparison.InvariantCultureIgnoreCase))
                    {
                        // The parameter is in both path and query. We cannot determine what to put for "in" attribute.
                        throw new ConflictingPathAndQueryParametersException(paramName, url);
                    }

                    if (url.Contains(
                            $"/{{{paramName}}}",
                            StringComparison.InvariantCultureIgnoreCase))
                    {
                        paramElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Path));
                    }
                    else if (url.Contains(
                                 $"={{{paramName}}}",
                                 StringComparison.InvariantCultureIgnoreCase))
                    {
                        paramElement.Add(new XAttribute(KnownXmlStrings.In, KnownXmlStrings.Query));
                    }
                }

                var pathParamElements = paramElements
                                        .Where(p => p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Path)
                                        .ToList();

                var matches = new Regex(@"\{(.*?)\}").Matches(url.Split('?')[0]);

                foreach (Match match in matches)
                {
                    var pathParamNameFromUrl = match.Groups[1].Value;

                    // All path params in the URL must be documented.
                    if (!pathParamElements.Any(p => p.Attribute(KnownXmlStrings.Name)?.Value == pathParamNameFromUrl))
                    {
                        throw new UndocumentedPathParameterException(pathParamNameFromUrl, url);
                    }
                }
            }

            paramElementsWithoutIn = paramElements.Where(p => p.Attribute(KnownXmlStrings.In)?.Value == null)
                                     .ToList();

            var paramElementsWithoutAllowedValues = paramElements.Where(
                p => !KnownXmlStrings.AllowedInValues.Contains(p.Attribute(KnownXmlStrings.In)?.Value)).ToList();

            if (paramElementsWithoutIn.Any())
            {
                throw new MissingInAttributeException(
                          paramElementsWithoutIn.Select(
                              p => p.Attribute(KnownXmlStrings.Name)?.Value));
            }

            if (paramElementsWithoutAllowedValues.Any())
            {
                throw new NotSupportedInAttributeValueException(
                          paramElementsWithoutAllowedValues.Select(
                              p => p.Attribute(KnownXmlStrings.Name)?.Value),
                          paramElementsWithoutAllowedValues.Select(
                              p => p.Attribute(KnownXmlStrings.In)?.Value)
                          );
            }
        }
        /// <summary>
        /// Validates the "in" attribute in param tagsif all parameter tags.
        /// </summary>
        /// <param name="paths">The paths to be validated.</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(
            OpenApiPaths paths,
            XElement element,
            PreProcessingOperationFilterSettings settings)
        {
            var generationErrors = new List <GenerationError>();

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

                var paramWithInValues = paramElements.Where(p => p.Attribute(KnownXmlStrings.In)?.Value != null).ToList();

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

                var paramElementsWithoutAllowedValues = paramWithInValues.Where(
                    p => !KnownXmlStrings.AllowedInValues.Contains(p.Attribute(KnownXmlStrings.In)?.Value)).ToList();

                if (paramElementsWithoutAllowedValues.Any())
                {
                    throw new NotSupportedInAttributeValueException(
                              paramElementsWithoutAllowedValues.Select(
                                  p => p.Attribute(KnownXmlStrings.Name)?.Value),
                              paramElementsWithoutAllowedValues.Select(
                                  p => p.Attribute(KnownXmlStrings.In)?.Value));
                }

                var url = element.Elements()
                          .FirstOrDefault(p => p.Name == KnownXmlStrings.Url)
                          ?.Value;

                var pathParamElements = paramElements
                                        .Where(p => p.Attribute(KnownXmlStrings.In)?.Value == KnownXmlStrings.Path)
                                        .ToList();

                var matches = new Regex(@"\{(.*?)\}").Matches(url.Split('?')[0]);

                foreach (Match match in matches)
                {
                    var pathParamNameFromUrl = match.Groups[1].Value;

                    // All path params in the URL must be documented.
                    if (!pathParamElements.Any(p => p.Attribute(KnownXmlStrings.Name)?.Value == pathParamNameFromUrl))
                    {
                        throw new UndocumentedPathParameterException(pathParamNameFromUrl, url);
                    }
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }