/// <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); }
/// <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); }
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); }
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); }
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); }
/// <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); }
/// <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); }
/// <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); }
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); }
/// <inheritdoc /> public virtual OpenApiSpecVersion GetOpenApiSpecVersion(OpenApiVersionType version = OpenApiVersionType.V2) { return(version.ToOpenApiSpecVersion()); }
/// <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); }