Ga naar hoofdinhoud

Headless maatwerk

Algemeen

De algemene uitgangspunten voor de ontwikkeling van maatwerk op iprox.producten worden ook gevolgd bij headless plugins. Met name:

  • Plugins voegen nieuwe functionaliteit toe en wijzigen niet het gedrag van bestaande functionaliteit.

Deze pagina begint met een sectie over headless maatwerk binnen het CMS en bevat daarna een aantal secties over maatwerk in de API.

CMS

Maatwerk in een headless implementatie in iprox.cms is in beginsel niet anders dan enig ander maatwerk in iprox.cms. Er zijn 2 aandachtsgebieden waar headless-specifieke aspecten gelden: preview en plugin-gegevenstypen.

Tonen (live/preview)

Het tonen van een headless pagina zal altijd door een externe applicatie verzorgd moeten worden. Deze applicatie zal voldoende moeten snappen van de ID's in iprox.cms om daartoe in staat te zijn.

NB: in de huidige situatie is er geen koppeling van preview of tonen live met hetgeen is ingesteld onder Bibliotheek > Internet Adressen.

Methode 1: setting itemview_url

Deze setting bepaald naar welke URL de preview wordt geredirect. In deze settings kunnen dmv {0} t/m {6} placeholders worden opgenomen om ID's door te krijgen. Hierbij geldt:

  • {0} = mapnaam van de stijlset
  • {1} = VarIdt
  • {2} = SitIdt
  • {3} = ItmIdt
  • {4} = PosIdt
  • {5} = Vrs
  • {6} = Extra

Methode 2: geplugde ItemViewProcessor

Als je een eigen implementatie maak van IItemViewProcessor dan heb je volledig de vrije hand om met previewen te doen wat je maar wilt.

Plugin gegevenstypen

Het inpluggen van gegevenstypen verloopt zoals standaard in iprox.cms. Specifiek voor iprox.headless moet de statificatie van de waarde naar de static page geïmplementeerd worden. Zie ook de sectie Zoeken hieronder.

Solution

Als een omgeving in het geheel geen endpoints heeft, dan zal deze ook geen eigen solution hebben en kan gewoon de API gebouwd worden met het standaard OpenApi project.

Als een omgeving wel een eigen project heeft, dan zal de Program class enkele partial methods moeten hebben om aan te sluiten bij de generator. Bijvoorbeeld:

// ReSharper disable once PartialTypeWithSinglePart
internal static partial class Program {
public static IIproxIdService IproxIdService {
get {
Dictionary<string, int> iproxIdMap = new();
InitIproxIdMap(iproxIdMap);
List<EnumerationInfo> selections = new();
InitSelectionMap(selections);
return new StaticIproxIdService(iproxIdMap, selections);
}
}

/// <summary>
/// Main method
/// </summary>
public static void Main(string[] args) {
Startup.Initialize<Implementation>(args);
}

// ReSharper disable once PartialMethodWithSinglePart
static partial void InitIproxIdMap(IDictionary<string, int> iproxIdMap);

// ReSharper disable once PartialMethodWithSinglePart
static partial void InitSelectionMap(List<EnumerationInfo> selections);
}

In de Main method wordt de class Implementation meegegeven waarin de specifieke services voor de implementatie worden geregistreerd. Bijvoorbeeld:

internal sealed class Implementation : ImplementationBase {
public override void AddIproxIdService() {
Program.RegisterIproxIdService(this.ServiceCollection);
}

public override void AddCustomServices() {
this.ServiceCollection.AddSingleton<IMjmlService, MjmlService>();
}
}

Endpoints

In het geval van een eigen solution kunnen eigen endpoints daar gewoon aan worden toegevoegd. De benodigde services moeten zijn geregistreerd in de Implementation class zoals beschreven onder Algemeen.

Er zijn in feite geen specifieke randvoorwaarden aan de realisatie van eigen plugins, zodat de mogelijkheden eindeloos zijn en de beschrijvingen alhier beperkt.

Gegevenstypen

Geplugde gegevenstypen zijn in het CMS niet anders voor headless dan voor andere CMS uitingen.

Generator

Voor de OpenAPI geldt dat een eigen generator moet worden gemaakt op basis van Iprox.OpenApi.Generator.Core. Hierbij is de minimale code die nodig is ook inderdaad minimaal:

using Iprox.OpenApi.Generator;

using Microsoft.Extensions.DependencyInjection;

var configuration = ProgramUtil.GetConfiguration(args);
var serviceProvider = new ServiceCollection().ConfigureGenerator(configuration).BuildServiceProvider();
await ProgramUtil.Run(serviceProvider);

Om het genereren van het gegevenstype in de API goed te laten verlopen, moet in de eigen generator worden aangehaakt op het event FieldDefinition.CustomPropertyTypeRequested. Dit moet uiteraard gebeuren voordat ProgramUtil.Run wordt aangeroepen.

Bijvoorbeeld, als gegevenstype 9942 een array van integers moet opleveren:

FieldDefinition.CustomPropertyTypeRequested += (_, args) => (int)args.DataType switch {
9942 => "int[]",
_ => null
};

Als je een 'eigen' type hebt, specificeer dit dan inclusief de volledige namespace:

FieldDefinition.CustomPropertyTypeRequested += (_, args) => (int)args.DataType switch {
1972 => "Chevrolet.Blazer.Custom",
_ => null
};

API runtime

Om de runtime van het gegevenstype in de API goed te laten verlopen, moet uiteraard een eigen ApiServer project bestaan waarin het maatwerk zich bevindt. Het aanhaken van het gegevenstype gebeurt via het event Field.CustomValueRequested.

Code voor de casus van hierboven zou kunnen zijn:

Field.CustomValueRequested += (_, args) => (int)args.DataType switch {
9942 when int.TryParse(args.Veld["Wrd"].AsString, out var wrd) => new int[wrd],
_ => null
};

NB: bij de realisatie van IPCMS-1456 wordt nader uitgewerkt wat dit betekent voor het schrijven van data.

Externe identity provider

Om een identity provider aan te sluiten, moet in de AddCustomServices method van de implementatie een implementatie van IExternalIdentityProvider met de juiste waarde van ExternalIdentityProviderType als key worden geïnjecteerd, bv:

services.AddKeyedTransient<IExternalIdentityProvider, MyCustomIdentityProvider>(
ExternalIdentityProviderType.Custom);

Zoeken

Eigen tekst-extractie ten behoeve van zoeken is mogelijk door in de Implementation class de method AddTextScanner te overriden en daarin de gewenste implementatie van ITextScanner toe te voegen. Dit is met name relevant voor geplugde gegevenstypen.

Facetten

Eigen facetten voor de zoekfunctie zijn te implementeren door in de Implementation class de methods AddFacetScanner en/of AddFacetService te overriden en daarin de gewenste implementatie van IFacetScanner c.q. IFacetService toe te voegen.