OpenAPI.NET icon indicating copy to clipboard operation
OpenAPI.NET copied to clipboard

Consider returning reference type from `AddComponent` API

Open captainsafia opened this issue 10 months ago • 3 comments

The AddComponent API currently returns a boolean that indicates if the addition of the component to the document registry succeeded.

It would be nice if the API returned a reference to the item that was just constructed. In an M.A.OpenAPI transformer, I currently need to write:

options.AddOperationTransformer((operation, context, cancellationToken) =>
{
    var schemaService = context.ApplicationServices.GetRequiredKeyedService<IOpenApiSchemaService>(context.DocumentName);
    if (context.Description.RelativePath == "error")
    {
        var errorSchema = schemaService.GetOrCreateSchema(typeof(ProblemDetails));
        context.Document.AddComponent("Error", errorSchema);
        operation.Responses["500"] = new OpenApiResponse
        {
            Description = "Error",
            Content =
            {
                ["application/problem+json"] = new OpenApiMediaType
                {
                    Schema = new OpenApiSchemaReference("Error", context.Document),
                },
            },
        };
    }
    return Task.CompletedTask;
});

but would like to write:

options.AddOperationTransformer((operation, context, cancellationToken) =>
{
    var schemaService = context.ApplicationServices.GetRequiredKeyedService<IOpenApiSchemaService>(context.DocumentName);
    if (context.Description.RelativePath == "error")
    {
        var errorSchema = schemaService.GetOrCreateSchema(typeof(ProblemDetails));
        var insertedSchema = context.Document.AddComponent("Error", errorSchema);
        operation.Responses["500"] = new OpenApiResponse
        {
            Description = "Error",
            Content =
            {
                ["application/problem+json"] = new OpenApiMediaType
                {
                    Schema = insertedSchema,
                },
            },
        };
    }
    return Task.CompletedTask;
});

cc: @baywet I couldn't see if an API that did this existed already and figured I'd add the proposal.

captainsafia avatar Feb 24 '25 16:02 captainsafia

Hi @captainsafia We do have constructors overloads that are internal at the moment and would allow you to pass the source schema directly. Effectively allowing you to write something like this

options.AddOperationTransformer((operation, context, cancellationToken) =>
{
    var schemaService = context.ApplicationServices.GetRequiredKeyedService<IOpenApiSchemaService>(context.DocumentName);
    if (context.Description.RelativePath == "error")
    {
        var errorSchema = schemaService.GetOrCreateSchema(typeof(ProblemDetails));
        context.Document.AddComponent("Error", errorSchema);
        operation.Responses["500"] = new OpenApiResponse
        {
            Description = "Error",
            Content =
            {
                ["application/problem+json"] = new OpenApiMediaType
                {
                    Schema = new OpenApiSchemaReference("Error", insertedSchema, document),
                },
            },
        };
    }
    return Task.CompletedTask;
});

I'm not saying this would be better than your proposal, just stating it's a possibility.

I think it'd be preferable to separate the two concerns, and offer something like that instead.

options.AddOperationTransformer((operation, context, cancellationToken) =>
{
    var schemaService = context.ApplicationServices.GetRequiredKeyedService<IOpenApiSchemaService>(context.DocumentName);
    if (context.Description.RelativePath == "error")
    {
        var errorSchema = schemaService.GetOrCreateSchema(typeof(ProblemDetails));
        var schemaId = "error";
        context.Document.AddComponent(schemaId, errorSchema);
        operation.Responses["500"] = new OpenApiResponse
        {
            Description = "Error",
            Content =
            {
                ["application/problem+json"] = new OpenApiMediaType
                {
                    Schema = context.Document.GetReferenceForComponent(schemaId),
                },
            },
        };
    }
    return Task.CompletedTask;
});

(which would return a value or throw, we could also have a TryGet variant) what do you think?

baywet avatar Feb 24 '25 17:02 baywet

I think it'd be preferable to separate the two concerns, and offer something like that instead.

Why do you think its preferable to separate?

IMO, having the AddComponent API makes it a little easier to connect the fact that the object that is inserted into a Schema property is a reference to the thing that was added to components. The two concepts seem linked in that way to me.

captainsafia avatar Feb 24 '25 21:02 captainsafia

Because:

  1. One might want to create multiple references to the same component.
  2. One might not want to create the reference at the same time the component is added, in which case the allocation is wasted (depending on how we model the overloads)

baywet avatar Feb 25 '25 09:02 baywet