Azure / Azure/azure-functions-openapi-extension

Swashbuckle MapType equivalent

Open
#537 4 comments 4 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
388
Forks
202
PR merge metrics
No merged PRs in 30d

Description

**Describe the issue**
We are looking at moving our application to Azure functions v4. We were using an App Service beforehand and swagger works correctly there (we added some configurations for a certain 3rd party library). We used Swashbuckle's MapType function to assert that the type should use the custom schema we define and the schema is then referenced from the containing object correctly. I am not sure of the equivalent functionality here if any.

**To Reproduce**
The project file:
```


net6.0
v4
Exe













PreserveNewest


PreserveNewest
Never




```

The Schema I add:
```
var quantitySchema = new OpenApiSchema
{
Properties =
{
{
"unit",
new OpenApiSchema
{
Title = "Quantity",
Type = "string",
OneOf = new[]
{
new OpenApiSchema
{ Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = nameof(EnergyUnit) } },
new OpenApiSchema
{ Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = nameof(MassUnit) } },
new OpenApiSchema
{ Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = nameof(VolumeUnit) } },
new OpenApiSchema
{ Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = nameof(LengthUnit) } },
new OpenApiSchema
{ Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = nameof(AreaUnit) } }
}
}
},
{ "value", new OpenApiSchema { Type = "number" } }
}
};

options.DocumentFilters.Add(new CustomModelDocumentFilter(quantitySchema));
```

The filter to add above schema:
```
public class CustomModelDocumentFilter : IDocumentFilter
{
private readonly OpenApiSchema openApiSchema;

public CustomModelDocumentFilter(OpenApiSchema schema)
{
this.openApiSchema = schema;
}

public void Apply(IHttpRequestDataObject req, OpenApiDocument document)
{
document.Components.Schemas.Add(typeof(T).Name, this.openApiSchema);
}
}
```

The endpoint and object:
```
[Function("Upsert")]
[OpenApiOperation(
operationId: nameof(Upsert),
Description = "Insert a new Direct Energy record or Update an existing one",
Visibility = OpenApiVisibilityType.Important)]
[OpenApiRequestBody("application/json", typeof(EnergyRequest))]
[OpenApiParameter(name: "id?", In = ParameterLocation.Path, Required = false, Type = typeof(Guid?))]
public async Task Upsert(
[HttpTrigger(AuthorizationLevel.Anonymous, "patch", Route = "energy/{id?}")] HttpRequestData requestData,
Guid? id,
FunctionContext executionContext)
{
var response = requestData.CreateResponse(HttpStatusCode.OK);
response.WriteString(id.ToString());

return response;
}

public class EnergyRequest
{

public IQuantity Quantity { get; set; }

public string Description { get; set; }

public decimal Cost { get; set; }
}
```

Sample downloadable here:
[FunctionApp4.zip](https://github.com/Azure/azure-functions-openapi-extension/files/10391311/FunctionApp4.zip)

Exception (I believe this occurs as it tries to walk through IQuantity properties instead of simply resolving custom schema that was added)
System.InvalidOperationException: 'Sequence contains no elements'
```
System.Linq.dll!System.Linq.ThrowHelper.ThrowNoElementsException() Unknown
> Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ListObjectTypeVisitor.Visit(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions.IAcceptor acceptor, System.Collections.Generic.KeyValuePair type, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy, System.Attribute[] attributes) Line 115 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.OpenApiSchemaAcceptor.Accept(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.VisitorCollection collection, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy) Line 75 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.ProcessProperties(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.IOpenApiSchemaAcceptor instance, string schemaName, System.Collections.Generic.Dictionary properties, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy) Line 189 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.Visit(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions.IAcceptor acceptor, System.Collections.Generic.KeyValuePair type, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy, System.Attribute[] attributes) Line 124 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.OpenApiSchemaAcceptor.Accept(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.VisitorCollection collection, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy) Line 75 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.ProcessProperties(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.IOpenApiSchemaAcceptor instance, string schemaName, System.Collections.Generic.Dictionary properties, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy) Line 189 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.Visit(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions.IAcceptor acceptor, System.Collections.Generic.KeyValuePair type, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy, System.Attribute[] attributes) Line 124 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.OpenApiSchemaAcceptor.Accept(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.VisitorCollection collection, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy) Line 75 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.ProcessProperties(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.IOpenApiSchemaAcceptor instance, string schemaName, System.Collections.Generic.Dictionary properties, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy) Line 189 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.ObjectTypeVisitor.Visit(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions.IAcceptor acceptor, System.Collections.Generic.KeyValuePair type, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy, System.Attribute[] attributes) Line 124 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.OpenApiSchemaAcceptor.Accept(Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.VisitorCollection collection, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy) Line 85 C#
Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.dll!Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.DocumentHelper.GetOpenApiSchemas(System.Collections.Generic.List elements, Newtonsoft.Json.Serialization.NamingStrategy namingStrategy, Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Visitors.VisitorCollection collection) Line 164 C#
Microsoft.Azure.Functions.Worker.Extensions.OpenApi.dll!Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Document.Build(System.Reflection.Assembly assembly, Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums.OpenApiVersionType version) Unknown
Microsoft.Azure.Functions.Worker.Extensions.OpenApi.dll!Microsoft.Azure.Functions.Worker.Extensions.OpenApi.Functions.OpenApiTriggerFunction.RenderSwaggerDocument(Microsoft.Azure.Functions.Worker.Http.HttpRequestData req, string extension, Microsoft.Azure.Functions.Worker.FunctionContext ctx) Unknown
Microsoft.Azure.Functions.Worker.Extensions.OpenApi.dll!Microsoft.Azure.Functions.Worker.Extensions.OpenApi.DefaultOpenApiHttpTrigger.RenderSwaggerDocument(Microsoft.Azure.Functions.Worker.Http.HttpRequestData req, string extension, Microsoft.Azure.Functions.Worker.FunctionContext ctx) Unknown
[Lightweight Function]
Microsoft.Azure.Functions.Worker.Core.dll!Microsoft.Azure.Functions.Worker.Invocation.TaskMethodInvoker.InvokeAsync(Microsoft.Azure.Functions.Worker.Extensions.OpenApi.DefaultOpenApiHttpTrigger instance, object[] arguments) Line 23 C#
Microsoft.Azure.Functions.Worker.Core.dll!Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionInvoker.InvokeAsync(object instance, object[] arguments) Line 31 C#
Microsoft.Azure.Functions.Worker.Core.dll!Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor.ExecuteAsync(Microsoft.Azure.Functions.Worker.FunctionContext context) Line 44 C#
Microsoft.Azure.Functions.Worker.Core.dll!Microsoft.Azure.Functions.Worker.Pipeline.FunctionExecutionMiddleware.Invoke(Microsoft.Azure.Functions.Worker.FunctionContext context) Line 20 C#
Microsoft.Azure.Functions.Worker.Core.dll!Microsoft.Extensions.Hosting.MiddlewareWorkerApplicationBuilderExtensions.UseFunctionExecutionMiddleware.AnonymousMethod__1_2(Microsoft.Azure.Functions.Worker.FunctionContext context) Line 57 C#
Microsoft.Azure.Functions.Worker.Core.dll!Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(Microsoft.Azure.Functions.Worker.FunctionContext context, Microsoft.Azure.Functions.Worker.Middleware.FunctionExecutionDelegate next) Line 13 C#
Microsoft.Azure.Functions.Worker.Core.dll!Microsoft.Extensions.Hosting.MiddlewareWorkerApplicationBuilderExtensions.UseOutputBindingsMiddleware.AnonymousMethod__3(Microsoft.Azure.Functions.Worker.FunctionContext context) Line 84 C#
Microsoft.Azure.Functions.Worker.Core.dll!Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(Microsoft.Azure.Functions.Worker.FunctionContext context) Line 69 C#
Microsoft.Azure.Functions.Worker.Grpc.dll!Microsoft.Azure.Functions.Worker.Handlers.InvocationHandler.InvokeAsync(Microsoft.Azure.Functions.Worker.Grpc.Messages.InvocationRequest request) Line 82 C#
Microsoft.Azure.Functions.Worker.Grpc.dll!Microsoft.Azure.Functions.Worker.GrpcWorker.InvocationRequestHandlerAsync(Microsoft.Azure.Functions.Worker.Grpc.Messages.InvocationRequest request) Line 183 C#
Microsoft.Azure.Functions.Worker.Grpc.dll!Microsoft.Azure.Functions.Worker.GrpcWorker.ProcessRequestCoreAsync(Microsoft.Azure.Functions.Worker.Grpc.Messages.StreamingMessage request) Line 138 C#
Microsoft.Azure.Functions.Worker.Grpc.dll!Microsoft.Azure.Functions.Worker.GrpcWorker.ProcessRequestAsync.AnonymousMethod__0() Line 124 C#
System.Private.CoreLib.dll!System.Threading.Tasks.Task.InnerInvoke() Unknown
System.Private.CoreLib.dll!System.Threading.Tasks.Task..cctor.AnonymousMethod__272_0(object obj) Unknown
System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(System.Threading.Thread threadPoolThread, System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
System.Private.CoreLib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot, System.Threading.Thread threadPoolThread) Unknown
System.Private.CoreLib.dll!System.Threading.Tasks.Task.ExecuteEntryUnsafe(System.Threading.Thread threadPoolThread) Unknown
System.Private.CoreLib.dll!System.Threading.Tasks.Task.ExecuteFromThreadPool(System.Threading.Thread threadPoolThread) Unknown
System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown
System.Private.CoreLib.dll!System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart() Unknown
System.Private.CoreLib.dll!System.Threading.Thread.StartCallback() Unknown
```

**Expected behavior**
Expect document generation to resolve the custom schema when looking at object type of `IQuantity` or a way to tell the generation to do this - in swagger this was done using SwaggerGenOptions:
```
options.MapType(() => new OpenApiSchema
{
Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = typeof(UnitsNet.IQuantity).Name }
});
```

Is there something comparable here?

**Screenshots**
N/A

**Environment (please complete the following information, if applicable):**
- OS: Windows

**Additional context**
Think that's everything...

Contributor guide

Open the contributing guide

Research direction

Start with the supplied project file and FunctionApp4.zip, then reproduce Swagger generation for the IQuantity property. Inspect the ListObjectTypeVisitor and ObjectTypeVisitor paths named in the stack trace. Done means the custom IQuantity schema is honored without the Sequence contains no elements exception.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, csharp, openapi
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.