diff --git a/.github/clean-headless-api.md b/.github/clean-headless-api.md index 5f87f2c2..d464b9b2 100644 --- a/.github/clean-headless-api.md +++ b/.github/clean-headless-api.md @@ -29,23 +29,7 @@ builder.CreateUmbracoBuilder() Once the Content Delivery API is enabled, the next step is to rebuild the Delivery API content index **DeliveryApiContentIndex**. This can be done using the Examine Management dashboard in the Settings section of the Umbraco Backoffice. -## Next.js Revalidation - -To enable automatic revalidation of content in Next.js applications, configure the following in your `appsettings.json`: - -```json -{ - "NextJs": { - "Revalidate": { - "Enabled": true, - "WebHookUrls": "[\"http://localhost:3000/api/revalidate\"]", - "WebHookSecret": "SOMETHING_SECRET" - } - } -} -``` -**Note**: Update the `WebHookUrls` to match your Next.js application's URL. ## Headless Frontend Example @@ -55,8 +39,27 @@ Phil Whittaker has created a complete headless Next.js frontend for this starter This implementation demonstrates how to use the Clean starter kit as a headless CMS with a modern Next.js frontend. + **Note for Umbraco 18**: DeliveryApi configuration must also include `GenerateContentTypeSchemas`: + + + ```json + { + "Umbraco": { + "CMS": { + "DeliveryApi": { + "Enabled": true, + "OpenApi": { + "GenerateContentTypeSchemas": true + } + } + } + } + } + ``` + --- + ## API Endpoints The Clean starter kit provides a suite of custom API endpoints for common functionality: @@ -69,10 +72,30 @@ The Clean starter kit provides a suite of custom API endpoints for common functi Explore and test the API endpoints using the built-in Swagger UI: -**URL**: `/umbraco/swagger/index.html?urls.primaryName=Clean%20starter%20kit` +**Umbraco 18+**: `/umbraco/openapi/` + +**Earlier versions**: `/umbraco/swagger/` This provides interactive documentation for all available API endpoints, including request/response schemas and the ability to test endpoints directly from the browser. +## Next.js Revalidation + +To enable automatic revalidation of content in Next.js applications, configure the following in your `appsettings.json`: + +```json +{ + "NextJs": { + "Revalidate": { + "Enabled": true, + "WebHookUrls": "[\"http://localhost:3000/api/revalidate\"]", + "WebHookSecret": "SOMETHING_SECRET" + } + } +} +``` + +**Note**: Update the `WebHookUrls` to match your Next.js application's URL. + --- ## Related Documentation diff --git a/template/Clean.Blog/Controllers/PackageController.cs b/template/Clean.Blog/Controllers/PackageController.cs index 674ead37..37fbbf32 100644 --- a/template/Clean.Blog/Controllers/PackageController.cs +++ b/template/Clean.Blog/Controllers/PackageController.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Api.Management.Controllers.Package.Created; [Route("api/v{version:apiVersion}/package")] [ApiVersion("1.0")] [ApiExplorerSettings(GroupName = "Package")] -[MapToApi("clean-starter")] +[MapToApi("clean-starter-admin")] [ApiController] public class PackageController : CreatedPackageControllerBase { @@ -175,4 +175,4 @@ public async Task GetPackage(CancellationToken cancellationToken, return PackageOperationStatusResult(result.Status); } } -} \ No newline at end of file +} diff --git a/template/Clean.Blog/Controllers/TestController.cs b/template/Clean.Blog/Controllers/TestController.cs deleted file mode 100644 index 5c1646b8..00000000 --- a/template/Clean.Blog/Controllers/TestController.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Asp.Versioning; -using Microsoft.AspNetCore.Mvc; -using Umbraco.Cms.Api.Common.Attributes; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services; - -namespace Umbraco.Cms.Api.Management.Controllers.Package.Created; - -[Route("api/v{version:apiVersion}/test")] -[ApiVersion("1.0")] -[MapToApi("clean-starter")] -[ApiController] -public class TestController : CreatedPackageControllerBase -{ - private readonly IDataTypeService _dataTypeService; - private readonly IEntityXmlSerializer _serializer; - - public TestController( - IDataTypeService dataTypeService, - IEntityXmlSerializer serializer) - { - _dataTypeService = dataTypeService; - _serializer = serializer; - } - - [HttpGet] - [MapToApiVersion("1.0")] - [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task GetPackage( - CancellationToken cancellationToken) - { - IDataType? dataType = await _dataTypeService.GetAsync("[BlockList] Main Content"); - if (dataType == null) - { - return NotFound("Unable to locate data type"); - } - - var output = _serializer.Serialize(dataType); - - return Ok(output); - } -} \ No newline at end of file diff --git a/template/Clean.Headless/Controllers/ContactV1Controller.cs b/template/Clean.Headless/Controllers/ContactV1Controller.cs index b96924fa..3df66b3d 100644 --- a/template/Clean.Headless/Controllers/ContactV1Controller.cs +++ b/template/Clean.Headless/Controllers/ContactV1Controller.cs @@ -15,6 +15,7 @@ namespace Clean.Headless.Controllers; [Route("api/v{version:apiVersion}/contact")] [ApiVersion("1.0")] [ApiExplorerSettings(GroupName = "Contact")] +[Tags("Contact")] [MapToApi("clean-starter")] [ApiController] public class ContactApiV1Controller : ControllerBase diff --git a/template/Clean.Headless/Controllers/DictionaryApiV1Controller.cs b/template/Clean.Headless/Controllers/DictionaryApiV1Controller.cs index 88564f2b..7617b98f 100644 --- a/template/Clean.Headless/Controllers/DictionaryApiV1Controller.cs +++ b/template/Clean.Headless/Controllers/DictionaryApiV1Controller.cs @@ -17,6 +17,7 @@ namespace Clean.Headless.Controllers; [ApiVersion("1.0")] [MapToApi("clean-starter")] [ApiExplorerSettings(GroupName = "Translation")] +[Tags("Translation")] [ApiController] public class DictionaryApiV1Controller( IDictionaryItemService dictionaryItemService, diff --git a/template/Clean.Headless/Controllers/SeachApiV1Controller.cs b/template/Clean.Headless/Controllers/SeachApiV1Controller.cs index ebb147ed..f429daa1 100644 --- a/template/Clean.Headless/Controllers/SeachApiV1Controller.cs +++ b/template/Clean.Headless/Controllers/SeachApiV1Controller.cs @@ -15,6 +15,7 @@ namespace Clean.Headless.Controllers; [Route("api/v{version:apiVersion}/search")] [ApiVersion("1.0")] [ApiExplorerSettings(GroupName = "Search")] +[Tags("Search")] [MapToApi("clean-starter")] [ApiController] public class SearchApiV1Controller : ControllerBase diff --git a/template/Clean.Headless/Startup/CleanStarterOpenApiExtensions.cs b/template/Clean.Headless/Startup/CleanStarterOpenApiExtensions.cs index a9912695..5af95b7e 100644 --- a/template/Clean.Headless/Startup/CleanStarterOpenApiExtensions.cs +++ b/template/Clean.Headless/Startup/CleanStarterOpenApiExtensions.cs @@ -1,6 +1,9 @@ +using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.OpenApi; using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Api.Common.Attributes; using Umbraco.Cms.Api.Common.DependencyInjection; namespace Clean.Headless.Startup; @@ -14,6 +17,14 @@ public static IServiceCollection AddCleanStarterOpenApi(this IServiceCollection { services.AddOpenApi(DocumentName, options => { + + // Include DictoryApiV1Controller, SearchApiV1Controller and ContactApiV1Controller in the OpenAPI document + options.ShouldInclude = apiDescription => + apiDescription.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor + && controllerActionDescriptor.EndpointMetadata + .OfType() + .Any(attribute => attribute.ApiName == DocumentName); + options.AddDocumentTransformer((document, _, _) => { document.Info.Title = DisplayName;