🌐 Website Implementation
Integration Methods
📖 Docs

Integration Methods - Optimizely CMS 12

Exam Area: Area 3 – Website Implementation & Delivery (25%)
Reference: https://docs.developers.optimizely.com/content-management-system/docs/integrations


1. REST API (Headless/Delivery)

// Optimizely Content Delivery API
// NuGet: EPiServer.ContentDeliveryApi.Cms

builder.Services.AddContentDeliveryApi(options =>
{
    options.EnablePreviewMode = true;
    options.EnableAllProperties = false;  // Only decorated properties
    options.FlattenPropertyModel = false;
});

// Route: GET /api/episerver/v3.0/content/{contentLink}
// Route: GET /api/episerver/v3.0/content?contentUrl=/my-page

2. Content Delivery API - Endpoints

GET /api/episerver/v3.0/content                → List content
GET /api/episerver/v3.0/content/{ref}          → Get by reference
GET /api/episerver/v3.0/content/{ref}/children → Get children
GET /api/episerver/v3.0/search?query=...        → Search
GET /api/episerver/v3.0/site                   → Site info

3. IContentApiSerializer (Custom Expansion)

// Custom property expansion in API response
[ServiceConfiguration(typeof(IPropertyConverter))]
public class RatingPropertyConverter : IPropertyConverter
{
    public ExpandedValue ConvertToExpanded(
        IContent content, 
        PropertyData property, 
        ConverterContext context)
    {
        if (property is PropertyRating rating)
        {
            return new ExpandedValue 
            { 
                Value = rating.Value,
                Maximum = rating.Maximum
            };
        }
        return null;
    }
}

4. IContentApiModel

// Model returned from the API
// Can be customized with IContentModelMapper

[ServiceConfiguration(typeof(IContentModelMapper))]
public class CustomContentModelMapper : IContentModelMapper
{
    private readonly IContentModelMapper _defaultMapper;

    public CustomContentModelMapper(DefaultContentModelMapper defaultMapper)
    {
        _defaultMapper = defaultMapper;
    }

    public ContentApiModel TransformContent(
        IContent content, 
        bool excludePersonalizedContent,
        string expand)
    {
        var model = _defaultMapper.TransformContent(content, excludePersonalizedContent, expand);
        
        // Add custom data
        if (content is ArticlePage article)
        {
            model.Properties["customField"] = article.CustomField;
        }
        
        return model;
    }
}

5. Webhooks / Event-based Integration

// Use Content Events to trigger external integration
[InitializableModule]
public class ExternalIntegrationModule : IInitializableModule
{
    private IContentEvents _events;

    public void Initialize(InitializationEngine context)
    {
        _events = context.Locate.Advanced.GetInstance<IContentEvents>();
        _events.PublishedContent += OnPublished;
    }

    public void Uninitialize(InitializationEngine context)
    {
        _events.PublishedContent -= OnPublished;
    }

    private async void OnPublished(object sender, ContentEventArgs e)
    {
        if (e.Content is ProductPage product)
        {
            // POST to external system
            using var client = new HttpClient();
            await client.PostAsJsonAsync(
                "https://external.api/products/sync",
                new { id = product.ContentLink.ID, name = product.Name });
        }
    }
}

6. Azure Service Bus Integration

// Send events to Azure Service Bus
builder.Services.AddAzureServiceBusEventProvider(options =>
{
    options.ConnectionString = builder.Configuration["ServiceBus:ConnectionString"];
    options.TopicName = "cms-events";
    options.SubscriptionName = Environment.MachineName;
});

7. Custom Import/Export

// IDataImporter / IDataExporter for bulk operations
public class ProductImportService
{
    private readonly IContentRepository _repository;
    private readonly IContentTypeRepository _contentTypeRepo;

    public async Task ImportProductsFromCsvAsync(string csvPath)
    {
        var products = ReadCsv(csvPath);
        var parentLink = new ContentReference(123); // Products container
        
        foreach (var product in products)
        {
            var page = _repository.GetDefault<ProductPage>(parentLink);
            page.Name = product.Name;
            page.Price = product.Price;
            
            _repository.Save(page, SaveAction.Publish);
        }
    }
}

8. Scheduled Import (cron-like)

// Use Scheduled Job for periodic imports
[ScheduledPlugIn(
    DisplayName = "Import Products from ERP",
    GUID = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
    DefaultIntervalType = ScheduledIntervalType.Hours,
    DefaultInterval = 4)]
public class ProductImportJob : ScheduledJobBase
{
    private readonly ProductImportService _importService;

    public ProductImportJob(ProductImportService importService)
    {
        _importService = importService;
        IsStoppable = false;
    }

    public override string Execute()
    {
        _importService.ImportFromERP();
        return "Import completed successfully";
    }
}

Review Questions

  1. Which NuGet package provides the Content Delivery API? (EPiServer.ContentDeliveryApi.Cms)
  2. What is the base URL of the Content Delivery API? (/api/episerver/v3.0/)
  3. How do you sync content when it is published? (Subscribe to IContentEvents.PublishedContent)
  4. When should you use a Scheduled Job for integration? (Periodic imports – not real-time)
  5. What is IContentModelMapper used for? (Customize the JSON output of the Content Delivery API)