Consider returning reference type from `AddComponent` API
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.
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?
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.
Because:
- One might want to create multiple references to the same component.
- 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)