Extension template: Use backoffice JSON options and other fixes#22885
Conversation
…er response with WhoAmIResponseModel Sets the extension template's backoffice API to use the BackOffice named JsonOptions so the extension's serializer is insulated from consumer-level overrides. The sample whoAmI endpoint previously returned IUser directly. IUser is a Umbraco.Core domain interface, not an API contract - it has no JSON polymorphism configuration and its nested interface properties (e.g. IReadOnlyUserGroup) are not designed to be serialized as part of an HTTP response. Once the BackOffice JsonOptions activated UmbracoJsonTypeInfoResolver for the extension's OpenAPI document, schema generation produced incomplete output (no type information on the Groups property). Replaces the return type with a flat WhoAmIResponseModel exposing only the fields the dashboard UI consumes (name, email, groups). Domain interfaces should not be exposed directly on a controller - always project into a dedicated response model.
…o.Extensions import The composer and controller base referenced `Cms.Core.Constants...` in short form, which relied on namespace fallback from `Umbraco.Extension.Controllers` finding `Umbraco.Cms.Core`. When consumers instantiate the template with a non-Umbraco root namespace, that fallback breaks. References are now fully qualified as `Umbraco.Cms.Core.Constants...`. Additionally, the `whoAmI` controller's `using Umbraco.Extensions;` was getting mangled by the template engine's token substitution of `Umbraco.Extension` into the consumer's name. Replaces `WhereNotNull()` with the BCL-only `OfType<string>()` so the controller no longer depends on the `Umbraco.Extensions` namespace.
|
Claude encountered an error after 3s —— View job I'll analyze this and get back to you. |
There was a problem hiding this comment.
Pull request overview
Updates the Umbraco extension template’s backoffice API to consistently use the BackOffice named JSON options for both runtime serialization and OpenAPI schema generation, and adjusts the example “WhoAmI” endpoint to return an API-friendly response model (plus regenerated client/dashboard updates to match).
Changes:
- Apply BackOffice named JSON options via
[JsonOptionsName]on the controller base and.WithJsonOptions(...)on the template’s BackOffice OpenAPI document. - Replace
whoAmIreturningIUserwith a flattenedWhoAmIResponseModel(name/email/groups) to avoid serializing domain interfaces. - Regenerate/update the template’s TypeScript client + dashboard to align with the new OpenAPI IDs and response model; exclude
ViewModels/**when the example isn’t included.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| templates/UmbracoExtension/ViewModels/WhoAmIResponseModel.cs | Introduces a dedicated response contract for the whoAmI example endpoint. |
| templates/UmbracoExtension/Controllers/UmbracoExtensionApiControllerBase.cs | Applies BackOffice named JSON options to all derived controllers via [JsonOptionsName]. |
| templates/UmbracoExtension/Controllers/UmbracoExtensionApiController.cs | Updates whoAmI to return WhoAmIResponseModel and explicitly supports 204 responses. |
| templates/UmbracoExtension/Composers/UmbracoExtensionApiComposer.cs | Ensures OpenAPI schema generation uses BackOffice named JSON options. |
| templates/UmbracoExtension/Client/src/dashboards/dashboard.element.ts | Updates dashboard to call the regenerated SDK functions and handle the new response shape. |
| templates/UmbracoExtension/Client/src/api/types.gen.ts | Regenerated OpenAPI TS types (new response model + renamed operation types). |
| templates/UmbracoExtension/Client/src/api/sdk.gen.ts | Regenerated SDK with updated operation names and response typing. |
| templates/UmbracoExtension/Client/src/api/index.ts | Re-exports updated SDK functions/types. |
| templates/UmbracoExtension/Client/src/api/client/utils.gen.ts | Regenerated auth helper signature/implementation used by the client. |
| templates/UmbracoExtension/Client/src/api/client/client.gen.ts | Adjusts generated client to match updated setAuthParams call shape. |
| templates/UmbracoExtension/.template.config/template.json | Excludes ViewModels/** when IncludeExample is false. |
The generated client returns a truthy empty data object (or null body) for a 204 response, so the previous `if (data)` check could pass and render `undefined` values in the notification. Checks `data?.email` instead - it's a required field on a real 200 response and absent in the 204 fallback.
…dashboard handling When `BackOfficeSecurity.CurrentUser` is null, the sample `whoAmI` endpoint now returns `Unauthorized()` instead of `NoContent()`, matching the `GetCurrentUserController` pattern in the Management API. Drops the 204 ProducesResponseType so the OpenAPI spec only advertises 200 plus the framework-emitted 401. The dashboard collapses its empty-data check into a single `error || !data` guard, moves the notification into the success branch, and regenerates the client to drop the now-unused 204 response.
AndyButland
left a comment
There was a problem hiding this comment.
Just one tiny suggestion @lauraneto - up to you if you think it's useful to update or not. But otherwise all looks good, and I've tested it out. Have successfully created extension with and without the example, and they all build and work as expected. Feel free to merge this when you are ready.
Summary
JsonOptions(both at the controller level via[JsonOptionsName]and at the OpenAPI document via.WithJsonOptions(...)), so the extension's serializer is insulated from consumer-level overrides to the default global JSON options.whoAmIendpoint no longer returnsIUserdirectly.IUseris aUmbraco.Coredomain interface, not an API contract — it has no JSON polymorphism configuration and its nested interface properties (e.g.IReadOnlyUserGroup) are not designed to be serialized as part of an HTTP response. Once the BackOffice JSON options activatedUmbracoJsonTypeInfoResolverfor the extension's OpenAPI document, schema generation produced incomplete output (no type information on theGroupsproperty). The endpoint now projects into a flatWhoAmIResponseModelexposing only the fields the dashboard UI consumes (name, email, groups).AddBackOfficeOpenApiDocumentcauses the Umbraco schema-ID and operation-ID transformers to run for the extension's document. The previously committed generated client referenced the pre-transform names, so the dashboard would break at type-check / runtime until the SDK was regenerated. The generated client files (Client/src/api/*.gen.ts,index.ts) and the dashboard element are updated accordingly in the same commit.Testing
Create a new project from the template with the example included (
dotnet new umbraco-extension --include-example). Build it against an Umbraco host on v18 and confirm the project compiles and that theOpenApi.jsonfor theUmbraco.Extensiondocument at/umbraco/openapi/umbracoextension.jsongenerates without errors. Launch the backoffice, open the example dashboard, and press the Who am I? button — the notification should display the current user's name and email, and the user's group names should appear in the list.Then add the following to the consumer site's
Program.csto override the default global JSON options:Re-run the Who am I? button and confirm the response and OpenAPI schema still use BackOffice conventions (camelCase property names) rather than the override — proving the extension's serializer is now insulated from consumer-side global JSON option changes.
Then create a second project from the template without the example (
dotnet new umbraco-extensionwithIncludeExample=false). Confirm theViewModels/folder andWhoAmIResponseModel.csare NOT emitted, the controller compiles without thewhoAmIaction, andClient/src/dashboards/is also excluded.