Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 12 commits into
base: v14/dev
Choose a base branch
from

Conversation

elit0451
Copy link
Member

@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 😊

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.

1 participant