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.