public void GenerateSchemaCustomSettingsShouldSucceed(
            Type type,
            OpenApiDocumentGenerationSettings generationSettings,
            OpenApiSchema expectedSchema,
            IDictionary <string, OpenApiSchema> expectedReferences)
        {
            _output.WriteLine(type.ToString());

            // Arrange
            var referenceRegistryManager =
                new ReferenceRegistryManager(generationSettings);

            // Act
            var returnedSchema =
                referenceRegistryManager.FindOrAddSchemaReference(type);

            // Assert
            var actualSchema     = returnedSchema;
            var actualReferences = referenceRegistryManager.SchemaReferenceRegistry.References;

            _output.WriteLine(actualSchema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0));
            foreach (var reference in actualReferences)
            {
                _output.WriteLine(reference.Key);
                _output.WriteLine(reference.Value.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0));
            }

            actualSchema.Should().BeEquivalentTo(expectedSchema);
            actualReferences.Should().BeEquivalentTo(expectedReferences);
        }
Esempio n. 2
0
        /// <summary>
        /// Fetches the value of "url" tag from xml documentation and use it to populate
        /// Server object of Open Api V3 specification document.
        /// </summary>
        /// <param name="specificationDocument">The Open Api V3 specification document to be updated.</param>
        /// <param name="xmlDocuments">The documents representing the annotation xmls.</param>
        /// <param name="settings">Settings for document filters.</param>
        /// <param name="openApiDocumentGenerationSettings"><see cref="OpenApiDocumentGenerationSettings"/></param>
        public void Apply(
            OpenApiDocument specificationDocument,
            IList <XDocument> xmlDocuments,
            DocumentFilterSettings settings,
            OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            var basePaths   = new List <string>();
            var urlElements = new List <XElement>();

            foreach (var xmlDocument in xmlDocuments)
            {
                urlElements.AddRange(xmlDocument.XPathSelectElements("//doc/members/member/url"));
            }

            foreach (var urlElement in urlElements)
            {
                var url          = urlElement.Value;
                var sanitizedUrl = SanitizeUrl(url);

                if (sanitizedUrl != null)
                {
                    basePaths.Add(sanitizedUrl.GetLeftPart(UriPartial.Authority));
                }
            }

            foreach (var basePath in basePaths.Distinct())
            {
                specificationDocument.Servers.Add(new OpenApiServer {
                    Url = basePath
                });
            }
        }
        /// <summary>
        /// Parses the values from the the security tags for the type member
        /// to populate <see cref="OpenApiSecurityRequirement"/> in <see cref="OpenApiDocument"/>.
        /// </summary>
        /// <param name="openApiDocument">The Open API specification document to be updated.</param>
        /// <param name="xmlDocuments">The list of documents representing the annotation xmls.</param>
        /// <param name="settings">Settings for document filters.</param>
        /// <param name="openApiDocumentGenerationSettings"><see cref="OpenApiDocumentGenerationSettings"/></param>
        public void Apply(
            OpenApiDocument openApiDocument,
            IList <XDocument> xmlDocuments,
            DocumentFilterSettings settings,
            OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            if (openApiDocument == null)
            {
                throw new ArgumentNullException(nameof(openApiDocument));
            }

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

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

            var securityElements = new List <XElement>();

            foreach (var xmlDocument in xmlDocuments)
            {
                securityElements.AddRange(xmlDocument.XPathSelectElements("//doc/members/member")
                                          .Where(
                                              m => m.Attribute(KnownXmlStrings.Name) != null &&
                                              m.Attribute(KnownXmlStrings.Name).Value.StartsWith("T:"))
                                          .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);
            }

            openApiDocument.SecurityRequirements.Add(openApiSecurityRequirement);
        }
Esempio n. 4
0
        /// <summary>
        /// Creates an instance of <see cref="ReferenceRegistryManager"/> class.
        /// <param name="openApiDocumentGenerationSettings">The Open API document generation settings.</param>
        /// </summary>
        public ReferenceRegistryManager(OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            if (openApiDocumentGenerationSettings == null)
            {
                throw new ArgumentNullException(nameof(openApiDocumentGenerationSettings));
            }

            SchemaReferenceRegistry = new SchemaReferenceRegistry(
                openApiDocumentGenerationSettings.SchemaGenerationSettings);
            ExampleReferenceRegistry   = new ExampleReferenceRegistry();
            ParameterReferenceRegistry = new ParameterReferenceRegistry(
                SchemaReferenceRegistry,
                ExampleReferenceRegistry);
        }
        /// <summary>
        /// Fetches the value of "url" tag from xml documentation and use it to populate
        /// Server object of Open Api V3 specification document.
        /// </summary>
        /// <param name="specificationDocument">The Open Api V3 specification document to be updated.</param>
        /// <param name="xmlDocuments">The documents representing the annotation xmls.</param>
        /// <param name="settings">Settings for document filters.</param>
        /// <param name="openApiDocumentGenerationSettings"><see cref="OpenApiDocumentGenerationSettings"/></param>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        public IList <GenerationError> Apply(
            OpenApiDocument specificationDocument,
            IList <XDocument> xmlDocuments,
            DocumentFilterSettings settings,
            OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                var basePaths   = new List <string>();
                var urlElements = new List <XElement>();

                foreach (var xmlDocument in xmlDocuments)
                {
                    urlElements.AddRange(xmlDocument.XPathSelectElements("//doc/members/member/url"));
                }

                foreach (var urlElement in urlElements)
                {
                    var url          = urlElement.Value;
                    var sanitizedUrl = SanitizeUrl(url);

                    if (sanitizedUrl != null)
                    {
                        basePaths.Add(sanitizedUrl.GetLeftPart(UriPartial.Authority));
                    }
                }

                foreach (var basePath in basePaths.Distinct())
                {
                    specificationDocument.Servers.Add(new OpenApiServer {
                        Url = basePath
                    });
                }
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
Esempio n. 6
0
        /// <summary>
        /// Fetches the value of "assembly" tag from xml documentation and use it to populate
        /// Info object of Open Api V3 specification document.
        /// </summary>
        /// <param name="specificationDocument">The Open Api V3 specification document to be updated.</param>
        /// <param name="xmlDocuments">The list of documents representing the annotation xmls.</param>
        /// <param name="settings">Settings for document filters.</param>
        /// <param name="openApiDocumentGenerationSettings"><see cref="OpenApiDocumentGenerationSettings"/></param>
        public void Apply(
            OpenApiDocument specificationDocument,
            IList <XDocument> xmlDocuments,
            DocumentFilterSettings settings,
            OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            // Find the xml document that contains member tag with url and verb,
            // as that should be the service api documenation xml.
            var xmlDocument = xmlDocuments
                              .FirstOrDefault(i => i.XPathSelectElement("//doc/members/member[url and verb]") != null);

            if (xmlDocument == null)
            {
                return;
            }

            specificationDocument.Info = new OpenApiInfo
            {
                Title   = xmlDocument.XPathSelectElement("//doc/assembly/name")?.Value,
                Version = settings.OpenApiDocumentVersion
            };
        }
        /// <summary>
        /// Fetches the value of "assembly" tag from xml documentation and use it to populate
        /// Info object of Open Api V3 specification document.
        /// </summary>
        /// <param name="specificationDocument">The Open Api V3 specification document to be updated.</param>
        /// <param name="xmlDocuments">The list of documents representing the annotation xmls.</param>
        /// <param name="settings">Settings for document filters.</param>
        /// <param name="openApiDocumentGenerationSettings"><see cref="OpenApiDocumentGenerationSettings"/></param>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        public IList <GenerationError> Apply(
            OpenApiDocument specificationDocument,
            IList <XDocument> xmlDocuments,
            DocumentFilterSettings settings,
            OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            var generationErrors = new List <GenerationError>();

            try
            {
                // Find the xml document that contains member tag with url and verb,
                // as that should be the service api documenation xml.
                var xmlDocument = xmlDocuments
                                  .FirstOrDefault(i => i.XPathSelectElement("//doc/members/member[url and verb]") != null);

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

                specificationDocument.Info = new OpenApiInfo
                {
                    Title       = xmlDocument.XPathSelectElement("//doc/assembly/name")?.Value,
                    Version     = settings.OpenApiDocumentVersion,
                    Description = settings.OpenApiInfoDescription
                };
            }
            catch (Exception ex)
            {
                generationErrors.Add(
                    new GenerationError
                {
                    Message       = ex.Message,
                    ExceptionType = ex.GetType().Name
                });
            }

            return(generationErrors);
        }
        /// <summary>
        /// Parses the values from the summary for all the properties to populate descriptions in the schema.
        /// </summary>
        /// <param name="openApiDocument">The Open API specification document to be updated.</param>
        /// <param name="xmlDocuments">The list of documents representing the annotation xmls.</param>
        /// <param name="settings">Settings for document filters.</param>
        /// <param name="openApiDocumentGenerationSettings"><see cref="OpenApiDocumentGenerationSettings"/></param>
        public void Apply(
            OpenApiDocument openApiDocument,
            IList <XDocument> xmlDocuments,
            DocumentFilterSettings settings,
            OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            if (openApiDocument == null)
            {
                throw new ArgumentNullException(nameof(openApiDocument));
            }

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

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

            var propertyMembers = new List <XElement>();

            foreach (var xmlDocument in xmlDocuments)
            {
                propertyMembers.AddRange(xmlDocument.XPathSelectElements("//doc/members/member")
                                         .Where(
                                             m => m.Attribute(KnownXmlStrings.Name) != null &&
                                             m.Attribute(KnownXmlStrings.Name).Value.StartsWith("P:")));
            }

            foreach (var propertyMember in propertyMembers)
            {
                var fullPropertyName = propertyMember.Attribute(KnownXmlStrings.Name).Value;

                var splitPropertyName = fullPropertyName.Split('.');

                // Take everything before the last period and remove the "P:" prefix.
                var className =
                    string.Join(".", splitPropertyName.Take(splitPropertyName.Length - 1))
                    .Substring(startIndex: 2);

                // We need to sanitize class name to match the format in the schema reference registry.
                // Note that this class may also match several classes in the registry given that generics
                // with different types are treated as different schemas.
                // For example, summary information for properties in class name A
                // should apply to those properties in schema A, A_B_, and A_B_C__ as well.
                var sanitizedClassName = className.SanitizeClassName();

                var schemas = openApiDocument.Components.Schemas.Where(
                    s => s.Key == sanitizedClassName ||
                    s.Key.StartsWith(sanitizedClassName + "_"))
                              .ToList();

                if (!schemas.Any())
                {
                    continue;
                }

                var propertyName =
                    splitPropertyName[splitPropertyName.Length - 1];

                var propertyInfo = settings.TypeFetcher.LoadType(className)
                                   ?.GetProperties()
                                   .FirstOrDefault(p => p.Name == propertyName);

                if (propertyInfo != null)
                {
                    propertyName = openApiDocumentGenerationSettings
                                   .SchemaGenerationSettings
                                   .PropertyNameResolver
                                   .ResolvePropertyName(propertyInfo);
                }

                foreach (var schema in schemas)
                {
                    if (schema.Value.Properties.ContainsKey(propertyName))
                    {
                        schema.Value.Properties[propertyName].Description =
                            propertyMember.Element(KnownXmlStrings.Summary)?.Value.RemoveBlankLines();
                    }
                }
            }
        }
        /// <summary>
        /// Parses the values from the summary for all the properties to populate descriptions in the schema.
        /// </summary>
        /// <param name="openApiDocument">The Open API specification document to be updated.</param>
        /// <param name="xmlDocuments">The list of documents representing the annotation xmls.</param>
        /// <param name="settings">Settings for document filters.</param>
        /// <param name="openApiDocumentGenerationSettings"><see cref="OpenApiDocumentGenerationSettings"/></param>
        public void Apply(
            OpenApiDocument openApiDocument,
            IList <XDocument> xmlDocuments,
            DocumentFilterSettings settings,
            OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            if (openApiDocument == null)
            {
                throw new ArgumentNullException(nameof(openApiDocument));
            }

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

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

            var propertyMembers = new List <XElement>();

            foreach (var xmlDocument in xmlDocuments)
            {
                propertyMembers.AddRange(xmlDocument.XPathSelectElements("//doc/members/member")
                                         .Where(
                                             m => m.Attribute(KnownXmlStrings.Name) != null &&
                                             m.Attribute(KnownXmlStrings.Name).Value.StartsWith("P:")));
            }

            // Fetches distinct type names like
            // Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.Contracts.SampleBaseObject
            // From property memeber names like
            // P:Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.Contracts.SampleBaseObject.SampleBaseProprety
            var typeNames = propertyMembers
                            .Attributes(KnownXmlStrings.Name)
                            .Select(i =>
                                    string.Join(".", i.Value.Split('.').Take(i.Value.Split('.').Length - 1))
                                    .Substring(2))
                            .Distinct()
                            .ToList();

            foreach (var typeName in typeNames)
            {
                // We need to sanitize class name to match the format in the schema reference registry.
                // Note that this class may also match several classes in the registry given that generics
                // with different types are treated as different schemas.
                // For example, summary information for properties in class name A
                // should apply to those properties in schema A, A_B_, and A_B_C__ as well.
                var sanitizedClassName = typeName.SanitizeClassName();

                var schemas = openApiDocument.Components.Schemas.Where(
                    s => s.Key == sanitizedClassName ||
                    s.Key.StartsWith(sanitizedClassName + "_"))
                              .ToList();

                if (!schemas.Any())
                {
                    continue;
                }

                var typeInfo = settings.TypeFetcher.LoadType(typeName);

                var typesToFetchPropertiesFor = settings.TypeFetcher.GetBaseTypes(typeInfo);
                typesToFetchPropertiesFor.Add(typeInfo);

                var propertiesMap = new Dictionary <string, PropertyInfo>();

                foreach (var type in typesToFetchPropertiesFor)
                {
                    foreach (var property in type.GetProperties())
                    {
                        if (!propertiesMap.ContainsKey(property.Name))
                        {
                            propertiesMap.Add(property.Name, property);
                        }
                    }
                }

                foreach (var property in propertiesMap.Keys)
                {
                    var propertyName = openApiDocumentGenerationSettings
                                       .SchemaGenerationSettings
                                       .PropertyNameResolver
                                       .ResolvePropertyName(propertiesMap[property]);

                    foreach (var schema in schemas)
                    {
                        var propertyMember = propertyMembers.FirstOrDefault(
                            i => i.Attribute(KnownXmlStrings.Name)?.Value ==
                            $"P:{propertiesMap[property].DeclaringType}.{property}");

                        if (propertyMember == null)
                        {
                            continue;
                        }

                        if (schema.Value.Properties.ContainsKey(propertyName))
                        {
                            schema.Value.Properties[propertyName].Description =
                                propertyMember.Element(KnownXmlStrings.Summary)?.Value.RemoveBlankLines();
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Parses the values from the the security tags for the type member
        /// to populate <see cref="OpenApiSecurityRequirement"/> in <see cref="OpenApiDocument"/>.
        /// </summary>
        /// <param name="openApiDocument">The Open API specification document to be updated.</param>
        /// <param name="xmlDocuments">The list of documents representing the annotation xmls.</param>
        /// <param name="settings">Settings for document filters.</param>
        /// <param name="openApiDocumentGenerationSettings"><see cref="OpenApiDocumentGenerationSettings"/></param>
        /// <returns>The list of generation errors, if any produced when processing the filter.</returns>
        public IList <GenerationError> Apply(
            OpenApiDocument openApiDocument,
            IList <XDocument> xmlDocuments,
            DocumentFilterSettings settings,
            OpenApiDocumentGenerationSettings openApiDocumentGenerationSettings)
        {
            var generationErrors = new List <GenerationError>();

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

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

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

                var securityElements = new List <XElement>();

                foreach (var xmlDocument in xmlDocuments)
                {
                    securityElements.AddRange(xmlDocument.XPathSelectElements("//doc/members/member")
                                              .Where(m => m.Attribute(KnownXmlStrings.Name) != null && m.Attribute(KnownXmlStrings.Name).Value.Contains("WebApiConfig.Register(System.Web.Http.HttpConfiguration)"))
                                              .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);
                }

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

            return(generationErrors);
        }