Skip to content

V14: Adding the ability to conditionally serialize version bound properties for the Delivery API#16731

Merged
Zeegaan merged 12 commits intov14/devfrom
v14/feature/property-level-versioning-for-delivery-api
Dec 16, 2024
Merged

V14: Adding the ability to conditionally serialize version bound properties for the Delivery API#16731
Zeegaan merged 12 commits intov14/devfrom
v14/feature/property-level-versioning-for-delivery-api

Conversation

@elit0451
Copy link
Contributor

@elit0451 elit0451 commented Jul 3, 2024

Details

  • Adding support for property-level versioning for the Delivery API;
    • Introducing IncludeInApiVersionAttribute to define API version bounds for properties. It supports both an inclusive minimum and maximum API version;
      • Properties annotated with this attribute will be included in the Delivery API response only if the API version falls within the specified bounds.
    • Adding a base converter class that you can inherit from in your custom JsonConverters. It handles the System.Text.Json serialization of properties based on the Delivery API version bounds.
      • Uses IHttpContextAccessor to access and act upon the current API version.

Example:

public class MyModel
{
    [IncludeInApiVersion(3)] // Only available from version 3 onwards
    public string Property1 { get; set; }

    [IncludeInApiVersion(2, 5)] // Available from version 2 to 5 (inclusive)
    public int Property2 { get; set; }

    [IncludeInApiVersion(maxVersion: 4)] // Available until version 4 (inclusive)
    public bool Property3 { get; set; }
}

Now, properties in MyModel will be included in the Delivery API response based on the specified version bounds.

Test

Setup

  1. Enable the Delivery API is appsettings.json:
{
  . . .
  "Umbraco": {
    "CMS": {
      "DeliveryApi": {
        "Enabled": true
      },
      . . .
}

  1. Set up some content, like:
  • Products
    • Item 1
    • Item 2

Baseline

  1. Verify that the output is the same from both:
  • GET /umbraco/delivery/api/v2/content and GET /umbraco/delivery/api/v1/content endpoints;
  • and GET /umbraco/delivery/api/v2/content/item/{id} and GET /umbraco/delivery/api/v1/content/item/{id} endpoints;
    • so we have an example from both multiple and single item serialization ❗
  1. Keep the JSON from /content and /item endpoints in a text comparison tool, like https://www.diffchecker.com/text-compare/ to compare next.

Custom converter and application of [IncludeInApiVersion] attribute

  1. Modify ApiContentResponse.cs (or use your own Delivery API custom endpoints and models):
public class ApiContentResponse : ApiContent, IApiContentResponse
{
    . . .
    [JsonPropertyOrder(100)]
    [IncludeInApiVersion(maxVersion: 1)] // <--- ADD THIS
    public IDictionary<string, IApiContentRoute> Cultures { get; }
}
  1. Modify ApiContent.cs (or use your own Delivery API custom endpoints and models):
public class ApiContent : ApiElement, IApiContent
{
    . . .

    [IncludeInApiVersion(1, 2)] // <--- ADD THIS
    public DateTime CreateDate { get; }

    [IncludeInApiVersion(2)] // <--- ADD THIS
    public DateTime UpdateDate { get; }

    public IApiContentRoute Route { get; }
}
  1. Create a custom JSON converter and configure additional JSON options for the DeliveryAPI:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Api.Delivery.Json;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Models.DeliveryApi;

namespace Umbraco.Cms.Web.UI;

public class MyComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
    {
        // Configure additional Delivery API JSON options
        builder.Services.ConfigureOptions<ConfigureDeliveryApiVersionAwareNamedOptions>();
    }
}

/// <summary>
///     Configures JSON options for the Delivery API based on API version awareness.
///     Passes on IHttpContextAccessor to access and act upon the current API version.
/// </summary>
public class ConfigureDeliveryApiVersionAwareNamedOptions : IConfigureNamedOptions<JsonOptions>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public ConfigureDeliveryApiVersionAwareNamedOptions(IHttpContextAccessor httpContextAccessor)
        => _httpContextAccessor = httpContextAccessor;

    /// <inheritdoc />
    public void Configure(string? name, JsonOptions options)
    {
        if (name is Constants.JsonOptionsNames.DeliveryApi)
        {
            Configure(options);
        }
    }

    /// <summary>
    ///     Configures JSON options by adding a custom JSON converter.
    /// </summary>
    public void Configure(JsonOptions options)
    {
        options.JsonSerializerOptions.Converters.Add(new MyJsonConverter(_httpContextAccessor));
    }
}

/// <summary>
///     Custom JSON converter for the Delivery API to apply custom logic based on the API version.
/// </summary>
public class MyJsonConverter : DeliveryApiVersionAwareJsonConverterBase<ApiContentResponse>
{
    public MyJsonConverter(IHttpContextAccessor httpContextAccessor)
        : base(httpContextAccessor)
    {
    }
}

Verification:

  1. Compare first the output from /umbraco/delivery/api/v1 and then /umbraco/delivery/api/v2 of /content and /item endpoints with the complete output from before (step: 4) and verify that the following properties are there or not based on ✅ and ❌ indicators:
v1 v2 v3
"createDate"
"updateDate"
"cultures"
  1. Bonus: You can create v3 endpoint and verify that v3 column is correct 😊

Copy link
Member

@Zeegaan Zeegaan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, tests good 🚀

@Zeegaan Zeegaan merged commit 32d0cb4 into v14/dev Dec 16, 2024
@Zeegaan Zeegaan deleted the v14/feature/property-level-versioning-for-delivery-api branch December 16, 2024 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants