示例#1
0
        /// <inheritdoc />
        public IDocument Build(Assembly assembly, OpenApiVersionType version = OpenApiVersionType.V2)
        {
            if (GenericExtensions.IsNullOrDefault(this._strategy))
            {
                this._strategy = new DefaultNamingStrategy();
            }

            var paths = new OpenApiPaths();

            var tags    = StringExtensions.ToArray(this._req.Query["tag"], ",");
            var methods = this._helper.GetHttpTriggerMethods(assembly, tags);

            foreach (var method in methods)
            {
                var trigger = this._helper.GetHttpTriggerAttribute(method);
                if (GenericExtensions.IsNullOrDefault(trigger))
                {
                    continue;
                }

                var function = this._helper.GetFunctionNameAttribute(method);
                if (GenericExtensions.IsNullOrDefault(function))
                {
                    continue;
                }

                var path = this._helper.GetHttpEndpoint(function, trigger);
                if (StringExtensions.IsNullOrWhiteSpace(path))
                {
                    continue;
                }

                var verb = this._helper.GetHttpVerb(trigger);

                var item       = this._helper.GetOpenApiPath(path, paths);
                var operations = item.Operations;

                var operation = this._helper.GetOpenApiOperation(method, function, verb);
                if (GenericExtensions.IsNullOrDefault(operation))
                {
                    continue;
                }

                operation.Security    = this._helper.GetOpenApiSecurityRequirement(method, this._strategy);
                operation.Parameters  = this._helper.GetOpenApiParameters(method, trigger, this._strategy, this._collection, version);
                operation.RequestBody = this._helper.GetOpenApiRequestBody(method, this._strategy, this._collection, version);
                operation.Responses   = this._helper.GetOpenApiResponses(method, this._strategy, this._collection, version);

                operations[verb] = operation;
                item.Operations  = operations;

                paths[path] = item;
            }

            this.OpenApiDocument.Paths = paths;
            this.OpenApiDocument.Components.Schemas         = this._helper.GetOpenApiSchemas(methods, this._strategy, this._collection);
            this.OpenApiDocument.Components.SecuritySchemes = this._helper.GetOpenApiSecuritySchemes(methods, this._strategy);
            // this.OpenApiDocument.SecurityRequirements = this.OpenApiDocument
            //                                                 .Paths
            //                                                 .SelectMany(p => p.Value.Operations.SelectMany(q => q.Value.Security))
            //                                                 .Where(p => !p.IsNullOrDefault())
            //                                                 .Distinct(new OpenApiSecurityRequirementComparer())
            //                                                 .ToList();

            return(this);
        }
        /// <summary>
        /// Converts <see cref="OpenApiResponseWithBodyAttribute"/> to <see cref="OpenApiResponse"/>.
        /// </summary>
        /// <param name="attribute"><see cref="OpenApiResponseWithBodyAttribute"/> instance.</param>
        /// <param name="namingStrategy"><see cref="NamingStrategy"/> instance to create the JSON schema from .NET Types.</param>
        /// <param name="collection"><see cref="VisitorCollection"/> instance.</param>
        /// <param name="version">OpenAPI spec version.</param>
        /// <returns><see cref="OpenApiResponse"/> instance.</returns>
        public static OpenApiResponse ToOpenApiResponse(this OpenApiResponseWithBodyAttribute attribute, NamingStrategy namingStrategy = null, VisitorCollection collection = null, OpenApiVersionType version = OpenApiVersionType.V2)
        {
            attribute.ThrowIfNullOrDefault();

            var description = string.IsNullOrWhiteSpace(attribute.Description)
                                  ? $"Payload of {attribute.BodyType.GetOpenApiDescription()}"
                                  : attribute.Description;
            var mediaType = attribute.ToOpenApiMediaType <OpenApiResponseWithBodyAttribute>(namingStrategy, collection, version);
            var content   = new Dictionary <string, OpenApiMediaType>()
            {
                { attribute.ContentType, mediaType }
            };
            var response = new OpenApiResponse()
            {
                Description = description,
                Content     = content,
            };

            if (attribute.CustomHeaderType.HasInterface <IOpenApiCustomResponseHeader>())
            {
                var header = Activator.CreateInstance(attribute.CustomHeaderType) as IOpenApiCustomResponseHeader;

                response.Headers = header.Headers;
            }

            if (!string.IsNullOrWhiteSpace(attribute.Summary))
            {
                var summary = new OpenApiString(attribute.Summary);

                response.Extensions.Add("x-ms-summary", summary);
            }

            return(response);
        }
        /// <summary>
        /// Converts the <see cref="OpenApiVersionType"/> to <see cref="OpenApiSpecVersion"/>.
        /// </summary>
        /// <param name="version"><see cref="OpenApiVersionType"/> value.</param>
        /// <returns>Returns <see cref="OpenApiSpecVersion"/> value.</returns>
        public static OpenApiSpecVersion ToOpenApiSpecVersion(this OpenApiVersionType version)
        {
            var casted = (OpenApiSpecVersion)(int)version;

            return(casted);
        }
示例#4
0
        /// <inheritdoc />
        public IDocument Build(string assemblyPath, OpenApiVersionType version = OpenApiVersionType.V2)
        {
            var assembly = Assembly.LoadFrom(assemblyPath);

            return(this.Build(assembly, version));
        }
        public void Given_OpenApiVersionType_When_ToOpenApiSpecVersion_Invoked_Then_It_Should_Return_Result(OpenApiVersionType version, OpenApiSpecVersion expected)
        {
            var result = EnumExtensions.ToOpenApiSpecVersion(version);

            result.Should().Be(expected);
        }
        public void Given_OpenApiResponseWithBodyAttribute_With_Example_When_ToOpenApiMediaType_Invoked_Then_It_Should_Return_Result(Type example, int count, OpenApiVersionType version)
        {
            var statusCode  = HttpStatusCode.OK;
            var contentType = "application/json";
            var bodyType    = typeof(object);
            var attribute   = new OpenApiResponseWithBodyAttribute(statusCode, contentType, bodyType)
            {
                Example = example,
            };
            var namingStrategy = new CamelCaseNamingStrategy();

            var result = OpenApiPayloadAttributeExtensions.ToOpenApiMediaType(attribute, namingStrategy, version: version);

            result.Examples.Should().NotBeNull();
            result.Examples.Should().HaveCount(count);

            if (count == 0)
            {
                return;
            }

            if (version != OpenApiVersionType.V2)
            {
                result.Example.Should().BeNull();

                return;
            }

            var instance = (dynamic)Activator.CreateInstance(example);
            var examples = (IDictionary <string, OpenApiExample>)instance.Build(namingStrategy).Examples;
            var first    = examples.First().Value;

            result.Example.Should().NotBeNull();
            (result.Example as OpenApiString).Value.Should().Be((first.Value as OpenApiString).Value);
        }
示例#7
0
        public void Given_EnvironmentVariables_When_Instantiated_Then_It_Should_Return_Default_OpenApiSettings(OpenApiVersionType version, string docVersion, string docTitle)
        {
            Environment.SetEnvironmentVariable(AppSettingsKeys.OpenApiVersionKey, version.ToString().ToLowerInvariant());
            Environment.SetEnvironmentVariable(AppSettingsKeys.OpenApiDocVersionKey, docVersion);
            Environment.SetEnvironmentVariable(AppSettingsKeys.OpenApiDocTitleKey, docTitle);

            var settings = new AppSettings();

            var result = settings.OpenApi;

            result.Should().NotBeNull();
            result.Version.Should().Be(version);
            result.DocumentVersion.Should().Be(docVersion);
            result.DocumentTitle.Should().Be(docTitle);
        }
        /// <summary>
        /// Converts <see cref="OpenApiParameterAttribute"/> to <see cref="OpenApiParameter"/>.
        /// </summary>
        /// <param name="attribute"><see cref="OpenApiParameterAttribute"/> instance.</param>
        /// <param name="namingStrategy"><see cref="NamingStrategy"/> instance.</param>
        /// <param name="collection"><see cref="VisitorCollection"/> instance.</param>
        /// <param name="version"><see cref="OpenApiVersionType"/> value.</param>
        /// <returns><see cref="OpenApiParameter"/> instance.</returns>
        public static OpenApiParameter ToOpenApiParameter(this OpenApiParameterAttribute attribute, NamingStrategy namingStrategy = null, VisitorCollection collection = null, OpenApiVersionType version = OpenApiVersionType.V2)
        {
            attribute.ThrowIfNullOrDefault();

            if (namingStrategy.IsNullOrDefault())
            {
                namingStrategy = new DefaultNamingStrategy();
            }

            if (collection.IsNullOrDefault())
            {
                collection = VisitorCollection.CreateInstance();
            }

            var type = attribute.Type;

            var schema = collection.ParameterVisit(type, namingStrategy);

            var parameter = new OpenApiParameter()
            {
                Name        = attribute.Name,
                Description = attribute.Description,
                Required    = attribute.Required,
                Deprecated  = attribute.Deprecated,
                In          = attribute.In,
                Schema      = schema
            };

            if (type.IsOpenApiArray())
            {
                if (attribute.In == ParameterLocation.Path)
                {
                    parameter.Style   = ParameterStyle.Simple;
                    parameter.Explode = false;
                }

                if (attribute.In == ParameterLocation.Query)
                {
                    parameter.Style = attribute.CollectionDelimiter == OpenApiParameterCollectionDelimiterType.Comma
                                      ? ParameterStyle.Form
                                      : (attribute.CollectionDelimiter == OpenApiParameterCollectionDelimiterType.Space
                                         ? ParameterStyle.SpaceDelimited
                                         : ParameterStyle.PipeDelimited);
                    parameter.Explode = attribute.CollectionDelimiter == OpenApiParameterCollectionDelimiterType.Comma
                                        ? attribute.Explode
                                        : false;
                }
            }

            if (!string.IsNullOrWhiteSpace(attribute.Summary))
            {
                var summary = new OpenApiString(attribute.Summary);

                parameter.Extensions.Add("x-ms-summary", summary);
            }

            if (attribute.Visibility != OpenApiVisibilityType.Undefined)
            {
                var visibility = new OpenApiString(attribute.Visibility.ToDisplayName());

                parameter.Extensions.Add("x-ms-visibility", visibility);
            }

            if (attribute.Example.IsNullOrDefault())
            {
                return(parameter);
            }

            var example  = (dynamic)Activator.CreateInstance(attribute.Example);
            var examples = (IDictionary <string, OpenApiExample>)example.Build(namingStrategy).Examples;

            parameter.Examples = examples;
            if (version == OpenApiVersionType.V2)
            {
                parameter.Example = examples.First().Value.Value;
            }

            return(parameter);
        }
示例#9
0
        public void Given_EnvironmentVariable_When_GetOpenApiVersion_Invoked_Then_It_Should_Return_Result(string version, OpenApiVersionType expected)
        {
            Environment.SetEnvironmentVariable("OpenApi__Version", version);

            var options = new DefaultOpenApiConfigurationOptions();
            var method  = typeof(DefaultOpenApiConfigurationOptions).GetMethod("GetOpenApiVersion", BindingFlags.NonPublic | BindingFlags.Static);

            var result = method.Invoke(options, null);

            result.Should().BeOfType <OpenApiVersionType>();
            ((OpenApiVersionType)result).Should().Be(expected);
        }
示例#10
0
        public void Given_OpenApiVersion_When_Instantiated_Then_Property_Should_Return_Value(string version, OpenApiVersionType expected)
        {
            Environment.SetEnvironmentVariable("OpenApi__Version", version);

            var options = new DefaultOpenApiConfigurationOptions();

            options.OpenApiVersion.Should().Be(expected);
        }
示例#11
0
        /// <inheritdoc />
        public OpenApiResponses GetOpenApiResponses(MethodInfo element, NamingStrategy namingStrategy, VisitorCollection collection, OpenApiVersionType version = OpenApiVersionType.V2)
        {
            var responsesWithBody = element.GetCustomAttributes <OpenApiResponseWithBodyAttribute>(inherit: false)
                                    .Where(p => p.Deprecated == false)
                                    .Select(p => new { StatusCode = p.StatusCode, Response = p.ToOpenApiResponse(namingStrategy, version: version) });

            var responsesWithoutBody = element.GetCustomAttributes <OpenApiResponseWithoutBodyAttribute>(inherit: false)
                                       .Select(p => new { StatusCode = p.StatusCode, Response = p.ToOpenApiResponse(namingStrategy) });

            var responses = responsesWithBody.Concat(responsesWithoutBody)
                            .ToDictionary(p => ((int)p.StatusCode).ToString(), p => p.Response)
                            .ToOpenApiResponses();

            return(responses);
        }
示例#12
0
        /// <inheritdoc />
        public OpenApiRequestBody GetOpenApiRequestBody(MethodInfo element, NamingStrategy namingStrategy, VisitorCollection collection, OpenApiVersionType version = OpenApiVersionType.V2)
        {
            var attributes = element.GetCustomAttributes <OpenApiRequestBodyAttribute>(inherit: false);

            if (!attributes.Any())
            {
                return(null);
            }

            var contents = attributes.Where(p => p.Deprecated == false)
                           .ToDictionary(p => p.ContentType, p => p.ToOpenApiMediaType(namingStrategy, collection, version));

            if (contents.Any())
            {
                return(new OpenApiRequestBody()
                {
                    Content = contents,
                    Required = attributes.First().Required
                });
            }

            return(null);
        }
        /// <summary>
        /// Gets the list of <see cref="OpenApiParameter"/> instances.
        /// </summary>
        /// <param name="helper"><see cref="IDocumentHelper"/> instance.</param>
        /// <param name="element"><see cref="MethodInfo"/> instance.</param>
        /// <param name="trigger"><see cref="HttpTriggerAttribute"/> instance.</param>
        /// <param name="namingStrategy"><see cref="NamingStrategy"/> instance to create the JSON schema from .NET Types.</param>
        /// <param name="collection"><see cref="VisitorCollection"/> instance to process parameters.</param>
        /// <param name="version"><see cref="OpenApiVersionType"/> value.</param>
        /// <returns>List of <see cref="OpenApiParameter"/> instance.</returns>
        public static List <OpenApiParameter> GetOpenApiParameters(this IDocumentHelper helper, MethodInfo element, HttpTriggerAttribute trigger, NamingStrategy namingStrategy, VisitorCollection collection, OpenApiVersionType version)
        {
            var parameters = element.GetCustomAttributes <OpenApiParameterAttribute>(inherit: false)
                             .Where(p => p.Deprecated == false)
                             .Select(p => p.ToOpenApiParameter(namingStrategy, collection))
                             .ToList();

            // This is the interim solution to resolve:
            // https://github.com/Azure/azure-functions-openapi-extension/issues/365
            //
            // It will be removed when the following issue is resolved:
            // https://github.com/microsoft/OpenAPI.NET/issues/747
            if (version == OpenApiVersionType.V3)
            {
                return(parameters);
            }

            var attributes = element.GetCustomAttributes <OpenApiRequestBodyAttribute>(inherit: false);

            if (!attributes.Any())
            {
                return(parameters);
            }

            var contents = attributes.Where(p => p.Deprecated == false)
                           .Where(p => p.ContentType == "application/x-www-form-urlencoded" || p.ContentType == "multipart/form-data")
                           .Select(p => p.ToOpenApiMediaType(namingStrategy, collection, version));

            if (!contents.Any())
            {
                return(parameters);
            }

            var @ref    = contents.First().Schema.Reference;
            var schemas = helper.GetOpenApiSchemas(new[] { element }.ToList(), namingStrategy, collection);
            var schema  = schemas.SingleOrDefault(p => p.Key == @ref.Id);

            if (schema.IsNullOrDefault())
            {
                return(parameters);
            }

            var properties = schema.Value.Properties;

            foreach (var property in properties)
            {
                var value = property.Value;
                if ((value.Type == "string" && value.Format == "binary") || (value.Type == "string" && value.Format == "base64"))
                {
                    value.Type   = "file";
                    value.Format = null;
                }

                var parameter = new OpenApiParameter()
                {
                    Name        = property.Key,
                    Description = $"[formData]{value.Description}",
                    Required    = bool.TryParse($"{value.Required}", out var result) ? result : false,
                    Deprecated  = value.Deprecated,
                    Schema      = value,
                };

                parameters.Add(parameter);
            }

            // // TODO: Should this be forcibly provided?
            // // This needs to be provided separately.
            // if (trigger.AuthLevel != AuthorizationLevel.Anonymous)
            // {
            //     parameters.AddOpenApiParameter<string>("code", @in: ParameterLocation.Query, required: false);
            // }

            return(parameters);
        }
示例#14
0
        /// <summary>
        /// Generates the OpenAPI document.
        /// </summary>
        /// <param name="project">Project path.</param>
        /// <param name="configuration">Copile configuration.</param>
        /// <param name="version">OpenAPI version.</param>
        /// <param name="format">OpenAPI output format.</param>
        /// <param name="output">Output path.</param>
        /// <param name="console">Value indicating whether to render the document on console or not.</param>
        public void Generate(
            [Option('p', Description = "Project path. Default is current directory")] string project  = ".",
            [Option('c', Description = "Configuration. Default is 'Debug'")] string configuration     = "Debug",
            [Option('t', Description = "Target framework. Default is 'netcoreapp2.1'")] string target = "netcoreapp2.1",
            [Option('v', Description = "OpenAPI spec version. Value can be either 'v2' or 'v3'. Default is 'v2'")] OpenApiVersionType version      = OpenApiVersionType.V2,
            [Option('f', Description = "OpenAPI output format. Value can be either 'json' or 'yaml'. Default is 'yaml'")] OpenApiFormatType format = OpenApiFormatType.Json,
            [Option('o', Description = "Generated OpenAPI output location. Default is 'output'")] string output = "output",
            bool console = false)
        {
            var pi = default(ProjectInfo);

            try
            {
                pi = new ProjectInfo(project, configuration, target);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);

                return;
            }

            var req = new Mock <HttpRequest>();

            req.SetupGet(p => p.Scheme).Returns("http");
            req.SetupGet(p => p.Host).Returns(new HostString("localhost", 7071));

            var filter         = new RouteConstraintFilter();
            var acceptor       = new OpenApiSchemaAcceptor();
            var namingStrategy = new CamelCaseNamingStrategy();
            var collection     = VisitorCollection.CreateInstance();
            var helper         = new DocumentHelper(filter, acceptor);
            var document       = new Document(helper);

            var swagger = default(string);

            try
            {
                swagger = document.InitialiseDocument()
                          .AddMetadata(pi.OpenApiInfo)
                          .AddServer(req.Object, pi.HostJsonHttpSettings.RoutePrefix)
                          .AddNamingStrategy(namingStrategy)
                          .AddVisitors(collection)
                          .Build(pi.CompiledDllPath)
                          .RenderAsync(version.ToOpenApiSpecVersion(), format.ToOpenApiFormat())
                          .Result;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            if (console)
            {
                Console.WriteLine(swagger);
            }

            var outputpath = Path.IsPathFullyQualified(output)
                                 ? output
                                 : $"{pi.CompiledPath}{directorySeparator}{output}";

            if (!Directory.Exists(outputpath))
            {
                Directory.CreateDirectory(outputpath);
            }

            File.WriteAllText($"{outputpath}{directorySeparator}swagger.{format.ToDisplayName()}", swagger, Encoding.UTF8);
        }
示例#15
0
        public void Given_OpenApi_Version_When_Instantiated_Then_It_Should_Return_Result(OpenApiVersionType version)
        {
            Environment.SetEnvironmentVariable(AppSettingsKeys.OpenApiVersionKey, version.ToString().ToLowerInvariant());

            var options = new OpenApiConfigurationOptions();

            var result = options.OpenApiVersion;

            result.Should().Be(version);
        }
示例#16
0
 /// <inheritdoc />
 public virtual OpenApiSpecVersion GetOpenApiSpecVersion(OpenApiVersionType version = OpenApiVersionType.V2)
 {
     return(version.ToOpenApiSpecVersion());
 }
示例#17
0
        /// <summary>
        /// Converts <see cref="OpenApiPayloadAttribute"/> to <see cref="OpenApiMediaType"/>.
        /// </summary>
        /// <typeparam name="T">Type of payload attribute inheriting <see cref="OpenApiPayloadAttribute"/>.</typeparam>
        /// <param name="attribute">OpenApi payload attribute.</param>
        /// <param name="namingStrategy"><see cref="NamingStrategy"/> instance to create the JSON schema from .NET Types.</param>
        /// <param name="collection"><see cref="VisitorCollection"/> instance.</param>
        /// <param name="version">OpenAPI spec version.</param>
        /// <returns><see cref="OpenApiMediaType"/> instance.</returns>
        public static OpenApiMediaType ToOpenApiMediaType <T>(this T attribute, NamingStrategy namingStrategy = null, VisitorCollection collection = null, OpenApiVersionType version = OpenApiVersionType.V2) where T : OpenApiPayloadAttribute
        {
            attribute.ThrowIfNullOrDefault();

            if (namingStrategy.IsNullOrDefault())
            {
                namingStrategy = new DefaultNamingStrategy();
            }

            if (collection.IsNullOrDefault())
            {
                collection = VisitorCollection.CreateInstance();
            }

            var type = attribute.BodyType;

            // Generate schema based on the type.
            var schema = collection.PayloadVisit(type, namingStrategy);

            // Add deprecated attribute.
            if (attribute is OpenApiRequestBodyAttribute)
            {
                schema.Deprecated = (attribute as OpenApiRequestBodyAttribute).Deprecated;
            }
            if (attribute is OpenApiResponseWithBodyAttribute)
            {
                schema.Deprecated = (attribute as OpenApiResponseWithBodyAttribute).Deprecated;
            }

            // For array and dictionary object, the reference has already been added by the visitor.
            if (type.IsReferentialType() && !type.IsOpenApiNullable() && !type.IsOpenApiArray() && !type.IsOpenApiDictionary())
            {
                var reference = new OpenApiReference()
                {
                    Type = ReferenceType.Schema,
                    Id   = attribute.BodyType.GetOpenApiReferenceId(isDictionary: false, isList: false, namingStrategy)
                };

                schema.Reference = reference;
            }

            var mediaType = new OpenApiMediaType()
            {
                Schema = schema
            };

            if (attribute.Example.IsNullOrDefault())
            {
                return(mediaType);
            }

            if (!attribute.Example.HasInterface("IOpenApiExample`1"))
            {
                return(mediaType);
            }

            var example  = (dynamic)Activator.CreateInstance(attribute.Example);
            var examples = (IDictionary <string, OpenApiExample>)example.Build(namingStrategy).Examples;

            mediaType.Examples = examples;
            if (version == OpenApiVersionType.V2)
            {
                mediaType.Example = examples.First().Value.Value;
            }

            return(mediaType);
        }