Skip to main content

Advanced CMS-Workflow Integration: Quick Reference

🎯 What's New?​

Three major improvements:

  1. No More JSON Editing - Beautiful UI replaces raw config JSON
  2. Advanced Template Rendering - Handlebars/Mustache with filters, loops, conditionals
  3. CMS Integration - Load templates from ContentData with smart merging

πŸš€ Quick Start​

Backend: Register Module Schema​

@Component
public class EmailModuleSchemaSetup {
@PostConstruct
public void setup(ModuleConfigSchemaRegistry registry) {
ModuleConfigSchema schema = new ModuleConfigSchema(
"EMAIL", "Email Module", "Send emails with CMS templates"
);

schema.addFields(
new ConfigField("integrationAccountId", "Email Account", FieldType.LOOKUP)
.required(true)
.apiLookup(new ApiLookup("/api/v1/integrationaccounts", "id", "name")
.filterParam("type", "EMAIL")),

new ConfigField("templateId", "Template", FieldType.LOOKUP)
.apiLookup(new ApiLookup("/api/v1/contentdata", "id", "title")
.filterParam("contentType", "template"))
);

registry.registerSchema("EMAIL", schema);
}
}

Frontend: Render Configuration UI​

const { schema, loading } = useModuleConfigSchema(execModule.moduleType);

<AdvancedModuleDesigner
schema={schema}
currentConfig={JSON.parse(execModule.moduleData || "{}")}
onConfigChange={(newConfig) => {
// Save to backend
updateExecModule(newConfig);
}}
/>;

Database: Use Type-Safe Config Access​

@Service
public class MyWorkflowService {
@Autowired
private ExecModuleConfigService configService;

public void executeModule(ExecModule module) {
String templateId = configService.getStringConfig(module, "templateId", "");
boolean escapeHtml = configService.getBooleanConfig(module, "escapeHtml", true);

// Do work...
}
}

🎨 Available Field Types​

TypeUse Case
TEXTSimple text input
EMAILEmail validation
NUMBERNumeric input
TEXTAREAMulti-line text
CHECKBOXBoolean toggle
SELECTDropdown (single)
MULTISELECTDropdown (multiple)
LOOKUPAPI-powered autocomplete
JSONJSON editor (complex configs)
CODECode editor (lambda/expressions)
TEMPLATE_EDITORHandlebars template editor
RECIPIENT_LISTEmail recipient list builder
DATEDate picker
DATETIMEDate+time picker
COLORColor picker

πŸ”₯ Template Filters (Handlebars/Mustache)​

{{value | uppercase}}
{{value | lowercase}}
{{value | capitalize}}
{{value | trim}}
{{value | truncate}}
{{value | html_encode}}
{{value | url_encode}}
{{price | currency}}
{{percentage | percent}}
{{number | round}}

Chain multiple filters:

{{email | lowercase | trim}}
{{text | uppercase | html_encode}}

🎯 Handlebars Control Flow​

Conditionals​

{{#if isPremium}}
VIP content here
{{/if}}

{{#unless isUnsubscribed}}
<a href="/subscribe">Subscribe</a>
{{/unless}}

Loops​

{{#each items}}
<li>
{{@index}}:
{{this.name}}
- ${{this.price}}
</li>
{{/each}}

Loop variables: @index, @key, @first, @last, @odd, @even


πŸ“§ Email Module Configuration​

{
"integrationAccountId": "sendgrid-account-id",
"templateMode": "cms",
"templateId": "template-uuid",
"mergeEngine": "handlebars",
"subjectTemplate": "Welcome {{firstName}}!",
"recipientMode": "dynamic",
"recipientDataPath": "recipients",
"escapeHtml": true
}

πŸ” Content Template Module Configuration​

Single Template:

{
"mode": "single",
"contentId": "template-uuid",
"mergeEngine": "handlebars"
}

Multiple Templates (Compose):

{
"mode": "multiple",
"templateIds": ["header-uuid", "body-uuid", "footer-uuid"],
"mergeEngine": "handlebars"
}

Search by Type:

{
"mode": "search",
"search": {
"type": "template",
"category": "email",
"limit": 10
}
}

🌐 REST Module Configuration​

{
"url": "https://api.example.com/endpoint",
"method": "POST",
"integrationAccountId": "auth-account",
"headers": { "Content-Type": "application/json" },
"body": "{{payload | json}}",
"responseMapping": {
"userId": "result.id",
"status": "meta.status"
}
}

πŸ”— API Lookup Configuration​

Define how dropdown options are fetched:

new ApiLookup("/api/v1/contentdata", "id", "title")
.filterParam("contentType", "template")
.filterParam("category", "email")
.searchable(true)
.searchParamName("q")
.sortBy("title", true)
.pageSize(50)

Query generated:

GET /api/v1/contentdata?contentType=template&category=email&q=welcome

✨ REST Endpoints​

GET /api/v1/module-schemas
β†’ All module configuration schemas

GET /api/v1/module-schemas/{moduleType}
β†’ Specific module schema (e.g., EMAIL, REST, CONTENT_TEMPLATE)

Example Response:
{
"moduleType": "EMAIL",
"moduleName": "Email Module",
"description": "Send emails with CMS templates",
"fields": [
{
"name": "integrationAccountId",
"label": "Email Integration",
"fieldType": "LOOKUP",
"required": true,
"section": "Account",
"order": 1,
"apiLookup": {
"endpoint": "/api/v1/integrationaccounts",
"labelField": "accountName",
"valueField": "id",
"filterParams": {"type": "EMAIL"},
"searchable": true
}
}
],
"metadata": {}
}

πŸ§ͺ Testing​

Validate Config​

ExecModuleConfigService configService = ...;
ExecModule module = new ExecModule();
module.setModuleType(ModuleTypeEnum.EMAIL);
module.setModuleData("{}");

ValidationResult result = configService.validateConfig(module);
if (!result.isValid()) {
Map<String, String> errors = result.getErrors();
errors.forEach((field, message) ->
System.out.println(field + ": " + message)
);
}

Test Template Rendering​

// Setup
ContentTemplateAdvancedModule module = new ContentTemplateAdvancedModule();
module.setModuleData(
"{\"mode\": \"single\", \"contentId\": \"...\", \"mergeEngine\": \"handlebars\"}"
);
module.setInputMap(Map.of(
"vars", Map.of("firstName", "John", "isPremium", true)
));

// Execute
module.execute();

// Verify
String rendered = (String) module.getOutputMap().get("template");
assertTrue(rendered.contains("John"));

🎯 Common Patterns​

Email with Dynamic Recipients​

{
"integrationAccountId": "sendgrid-account",
"templateId": "welcome-template",
"recipientMode": "dynamic",
"recipientDataPath": "users"
}

Input: { "users": [{"email": "john@example.com", "firstName": "John"}, ...] }

Newsletter with Multiple Templates​

{
"mode": "multiple",
"templateIds": ["header-uuid", "featured-uuid", "footer-uuid"],
"mergeEngine": "handlebars"
}

Search Templates by Category​

{
"mode": "search",
"search": {
"type": "template",
"category": "promotional"
}
}

πŸ› Troubleshooting​

IssueSolution
"No schema found"Check module type is registered in ModuleConfigSchemaRegistry
Filter not workingUse correct filter name (see Filters section)
Template variables not renderingCheck variable path and context data in input map
API lookup returns no optionsVerify endpoint, filterParams, valueField, labelField
Conditional not renderingCheck condition variable exists in context

πŸ“š Full Documentation​

See ADVANCED_CMS_WORKFLOW_INTEGRATION.md for:

  • Complete architecture overview
  • Email template walkthrough
  • Integration guide for custom modules
  • Comprehensive test examples
  • Migration guide from raw JSON

Made for developers who hate JSON editing as much as we do! πŸ”₯