Custom OperationId generate for packages.
# package-development
k
Hi, somone has raised this valid issue with uSync rc. around how we do custom operation ids. https://github.com/KevinJump/uSync/issues/629 and in it they point to the docs. https://docs.umbraco.com/umbraco-cms/reference/api-versioning-and-openapi#adding-custom-schema-ids which in summary say. - override Umbraco's
OperationIdSelector
with something that checks for your api end point and then only applies that. - replace umbracos in a build
builder.Services.AddSingleton<IOperationIdSelector, MyOperationIdSelector>();
my convern here is that this isn't sustainable ? if i register
.AddSingleton<IOperationSelector, uSyncOperationSelector>
and then someone else comes along and reigsters
.AddSingleton<IOperationSelector, SuperCustomIdSelector>
doesn't that mean that my customId selector will never then be used ? --- I think (and i might be wrong) that what i should actually do, is only register my custom selector on the site i am using to generate my API from and not actually put it in the solution ? because as far as i can tell once the typescript files have been generated they are using the path not the id, so it doesn't matter ?
w
Interesting. Leaving a comment here to remind myself to read this
j
We are going to implement a way to share the operation id logic of the management api, I dont know if that helps you and dont have the exact details right now
w
Do you really need the custom
IOperationSelector
can you not do something like this instead @Kevin Jump ?
Copy code
csharp
builder.Services.Configure<SwaggerGenOptions>(opt =>
{
    // Configure the Swagger generation options
    // Add in a new Swagger API document solely for our own package that can be browsed via Swagger UI
    // Along with having a generated swagger JSON file that we can use to auto generate a TypeScript client
    opt.SwaggerDoc("AccessibilityReporter", new OpenApiInfo
    {
        Title = "Accessibility Reporter Package API",
        Version = "1.0"
    });

    // https://docs.umbraco.com/umbraco-cms/v/14.latest-beta/reference/custom-swagger-api
    // PR: https://github.com/umbraco/Umbraco-CMS/pull/15699
    opt.OperationFilter<MyBackOfficeSecurityRequirementsOperationFilter>();

    // Rather than very verbose names from generated TS client, we simplify them a bit
    // https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md#operation-filters
    // https://docs.umbraco.com/umbraco-cms/reference/api-versioning-and-openapi#adding-custom-operation-ids
    // https://g-mariano.medium.com/generate-readable-apis-clients-by-setting-unique-and-meaningful-operationid-in-swagger-63d404f32ff8
    opt.CustomOperationIds(apiDesc => $"{apiDesc.ActionDescriptor.RouteValues["action"]}");
});
And use the config`CustomOperationIds` to set what you need?
k
I'm not sure but isn't the issue that if you set your own operations filter this way you override the default one in Umbraco?
I actually think you only need to set this during development, it doesn't need to ship as part of any package.?
w
Yeh not sure anyone will be generating their own client from my package API
And I didn't try generating a new client using Umbraco's mgmt API to see if my change overode it
Alternative is to use
Swashbuckle.AspNetCore.Annotations
nuget package https://github.com/domaindrivendev/Swashbuckle.AspNetCore?tab=readme-ov-file#enrich-operation-metadata
Copy code
csharp
[HttpPost]
[SwaggerOperation(
    Summary = "Creates a new product",
    Description = "Requires admin privileges",
    OperationId = "CreateProduct",
    Tags = new[] { "Purchase", "Products" }
)]
public IActionResult Create([FromBody]Product product)
Or even simpler [HttpGet("{id}", Name = "GetProductById")] public IActionResult Get(int id) // operationId = "GetProductById"
Seems if you want more control or logic applied is to use IOperationFilter as it’s a pipeline in Swashbuckle and then you can run your custom code to determine and set the operation id as opposed to IOperationIdSelector which is an Umbraco thing and like you say is a singleton. https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/README.md#operation-filters Be curious to hear from CMS core gang on this
Is IOperationIdHandler the way to go? Seems overkill if I can just set a name property in the HttpGet attribute as it won’t effect other packages or CMS core. Curious on your thoughts @Kevin Jump
k
I think with IOperationIdHandler I am less likely to forget / don't have to install another package, but equally I am tempted to have a development only bit of code that isn't part of the main package that sets it while I Dev but doesn't ship
Prob look at it a bit more next week
w
Yeh my gut is to just amend the HttpGet attr with a name as opposed to the first suggestion with another package. Let me know what you roll with in the end.
26 Views