TABLE OF CONTENTS
- 1. Overview
- 2. How to Use Custom Processor Extensibility
- 2.1 Create a New Custom Processor Class
- 2.2 Add Logging in a Custom Processor
- 2.3 Writing Your Processor Class
- 2.4 Processor Lifecycle
- 2.5 Available Data
- 2.6 Conditional Step Processing
- 2.7 Modifying Step Outputs
- 2.8 Using Dependency Injection
- 2.9 Deploying the Extension
- 2.10 Registering the Processor
- 2.11 Supported Mapping Types
- 2.12 Best Practices
- 2.13 Limitations
- 3. Supported Mapping Expressions
- Summary
- Summary
1. Overview
Custom Processor Extensibility allows developers to execute custom business logic during Commerce Connector transmission execution.
Custom processors are developed externally in a Custom API project by referencing the Commerce Connector SDK and implementing the IBaseProcessor interface. A processor can execute custom logic before or after a transmission step, validate data, enrich execution results, modify output data, and integrate with platform services through Dependency Injection.
Once developed, the processor assembly is deployed using the CLI deploy extension command. After deployment, the processor can be configured and used within Commerce Connector transmission workflows.
2. How to Use Custom Processor Extensibility
2.1 Create a New Custom Processor Class
Custom processors should be created in a separate class library project within the Custom API solution. This keeps custom processing logic isolated from the main application code.
A suitable project name can be used based on the implementation or client requirement.
Examples
Custom.Libraries.DataExchangeZnode.Libraries.DataExchange.Custom<ClientName>.DataExchange.Custom
The custom processor project must reference the Commerce Connector SDK package.
To create a new processor, add a new class and implement the IBaseProcessor interface.
Example
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Znode.Libraries.CommerceConnector.SDK;
using Znode.Libraries.Common.Logger;
namespace Custom.Libraries.DataExchange
{
public class DummyMultiStepProcessor : IBaseProcessor
{
protected readonly IServiceProvider _serviceProvider;
private readonly IZnodeLogging _znodeLogging;
public DummyMultiStepProcessor(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_znodeLogging = _serviceProvider.GetService<IZnodeLogging>();
}
public async Task BeforeStepAsync(
List<TransmissionStepModel> context,
TransmissionStepModel step,
List<TransmissionStepExecutionOutput> executionOutputs)
{
await Task.CompletedTask;
}
public async Task AfterStepAsync(
List<TransmissionStepModel> context,
TransmissionStepModel step,
List<TransmissionStepExecutionOutput> executionOutputs)
{
await Task.CompletedTask;
}
}
}
2.2 Add Logging in a Custom Processor
Custom processors can write logs using IZnodeLogging.
The logger can be resolved using Dependency Injection through the provided IServiceProvider.
Example
public DummyMultiStepProcessor(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_znodeLogging = _serviceProvider.GetService<IZnodeLogging>();
}
Writing logs
_znodeLogging.LogMessage(
"DummyMultiStepProcessor called",
"DummyMultiStepProcessor",
TraceLevel.Info);
Logs can be written for:
- Before step execution
- After step execution
- Validation events
- Exception handling
- External integrations
Example
public async Task BeforeStepAsync(
List<TransmissionStepModel> context,
TransmissionStepModel step,
List<TransmissionStepExecutionOutput> executionOutputs)
{
_znodeLogging.LogMessage(
"Before step execution",
"DummyMultiStepProcessor",
TraceLevel.Info);
await Task.CompletedTask;
}
2.3 Writing Your Processor Class
The following example demonstrates the minimum implementation required for a custom processor.
Example
public class DummyMultiStepProcessor : IBaseProcessor
{
public async Task BeforeStepAsync(
List<TransmissionStepModel> context,
TransmissionStepModel step,
List<TransmissionStepExecutionOutput> executionOutputs)
{
await Task.CompletedTask;
}
public async Task AfterStepAsync(
List<TransmissionStepModel> context,
TransmissionStepModel step,
List<TransmissionStepExecutionOutput> executionOutputs)
{
await Task.CompletedTask;
}
}
Rules that are non-negotiable
| Rule | Why |
|---|---|
Implement IBaseProcessor | Required for Commerce Connector to recognize the processor. |
Implement BeforeStepAsync() | Executed before a transmission step. |
Implement AfterStepAsync() | Executed after a transmission step. |
| Always return completed tasks | Ensures the execution pipeline continues correctly. |
| Handle null data safely | Prevents unexpected runtime failures. |
| Use Dependency Injection for services | Provides access to platform services. |
2.4 Processor Lifecycle
Commerce Connector executes the custom processor as part of the transmission workflow. For every configured transmission step, the processor is invoked before and after the step execution.
Execution Flow
Transmission Execution
│
▼
BeforeStepAsync()
│
▼
Transmission Step Execution
│
▼
AfterStepAsync()
│
▼
Next Step
For every transmission step:
- The processor's
BeforeStepAsync()method executes. - The configured transmission step executes.
- The processor's
AfterStepAsync()method executes. - Execution proceeds to the next configured step.
2.5 Available Data
When a processor executes, the following information is available.
Transmission Context
The context parameter contains all transmission steps involved in the current execution.
List<TransmissionStepModel> context
Example
var totalSteps = context.Count;
Current Step
The step parameter represents the currently executing transmission step.
TransmissionStepModel step
Example
var stepCode = step.StepCode;
A common use case is to execute custom logic only for a specific transmission step.
Example
if (step.StepCode == "step13")
{
// custom logic
}
Execution Outputs
The executionOutputs parameter contains the execution results generated during transmission processing.
List<TransmissionStepExecutionOutput> executionOutputs
Execution outputs can be:
- Read
- Modified
- Extended
These outputs can be used by subsequent transmission steps during the execution workflow.
2.6 Conditional Step Processing
A custom processor can execute logic for a specific transmission step by evaluating the StepCode of the currently executing step.
Example
if (step.StepCode == "step13")
{
Console.WriteLine("Dummy processor executed successfully.");
}
This approach allows step-specific behavior during workflow execution. Common scenarios include:
- Product Export
- Inventory Synchronization
- Order Import
- Catalog Update
- Customer Synchronization
2.7 Modifying Step Outputs
A custom processor can add or modify execution outputs after a transmission step completes. These outputs can then be consumed by subsequent transmission steps.
Example
public async Task AfterStepAsync(
List<TransmissionStepModel> context,
TransmissionStepModel step,
List<TransmissionStepExecutionOutput> executionOutputs)
{
if (step.StepCode == "step134")
{
executionOutputs.Add(
new TransmissionStepExecutionOutput
{
StepCode = step.StepCode,
OutputData = "Modified after execution",
DataFormat = "Text"
});
}
await Task.CompletedTask;
}
The TransmissionStepExecutionOutput object contains the following properties:
| Property | Description |
|---|---|
StepCode | Identifies the transmission step that produced the output. |
OutputData | Stores the output generated by the processor. |
DataFormat | Specifies the format of the output data. |
Example
new TransmissionStepExecutionOutput
{
StepCode = step.StepCode,
OutputData = "Modified after execution",
DataFormat = "Text"
}
2.8 Using Dependency Injection
Commerce Connector supports Dependency Injection, allowing custom processors to access platform services during execution.
The IServiceProvider instance is provided through the processor constructor and can be used to resolve services as required.
Example
protected readonly IServiceProvider _serviceProvider;
public DummyMultiStepProcessor(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
Resolve a service using the service provider:
var logger =
_serviceProvider.GetService<IZnodeLogging>();
Common platform services include:
- Logging
- Configuration access
- Database services
- External integrations
- Platform services
2.9 Deploying the Extension
After developing the custom processor, deploy the extension using the Znode CLI.
Before deployment, connect to the Znode CLI using the provided authentication token.
Connect to the CLI
znode connect <token>
Deploy a specific processor project by specifying the project (.csproj) path.
Syntax
znode deploy extension "<path-to-project-csproj>"
Example
znode deploy extension
"D:\Extensions\Custom.Libraries.DataExchange\Custom.Libraries.DataExchange.csproj"
To deploy multiple extension projects, specify the parent folder that contains the projects.
Example
znode deploy extension "D:\Extensions"
The CLI automatically builds the project and uploads the generated assemblies for execution.
2.10 Registering the Processor
After deploying the custom processor, register it in the Commerce Connector configuration before using it in a transmission workflow.
Provide the following information while registering the processor:
| Field | Description |
|---|---|
| Processor Name | Enter a descriptive name for the custom processor. |
| Class Name | Specify the fully qualified class name of the deployed processor. |
| Description | Provide a brief description of the processor. |
Example
DummyMultiStepProcessorNote: The Class Name must exactly match the deployed processor class. If the class name does not match, Commerce Connector will not be able to locate and execute the processor.
2.11 Supported Mapping Types
The Mapping Engine supports the following mapping types.
| Mapping Type | Description |
|---|---|
| Direct Mapping | Maps a source field directly to a destination field. |
| Fixed/Static Value Mapping | Assigns a constant value. |
| Default Value Mapping | Applies a value when the source field is empty. |
| Formula Mapping | Performs calculations and expressions. |
| Conditional Mapping | Applies mappings based on configured conditions. |
| Lookup Mapping | Maps values using lookup definitions. |
| Transformation Mapping | Transforms data before assignment. |
| Processor-Based Mapping | Uses custom processor logic during mapping. |
| Object Mapping | Maps complete object structures. |
| Collection Mapping | Maps arrays and collections. |
Any mapping behavior that is not supported by the standard Mapping Engine can be implemented using a custom processor.
2.12 Best Practices
Follow these best practices when developing custom processors.
Keep Processors Lightweight
Processors execute as part of the transmission workflow. Avoid unnecessary processing to minimize execution time.
Handle Exceptions Properly
Wrap custom logic in exception handling blocks and log any exceptions using IZnodeLogging.
Example
try
{
// custom logic
}
catch (Exception ex)
{
_znodeLogging.LogMessage(
ex.Message,
"DummyMultiStepProcessor",
TraceLevel.Error);
}
Use Step Codes for Specific Logic
Execute custom logic only for the required transmission steps by evaluating the StepCode.
Example
if(step.StepCode == "ProductExport")
{
// execute product export logic
}
Minimize External Calls
Avoid expensive API calls or database operations inside frequently executed processors to improve overall execution performance.
Maintain Stateless Design
Processors should not depend on static variables or shared state between executions.
2.13 Limitations
The current implementation of Custom Processor Extensibility does not support the following capabilities:
- Extension Observability Screen (custom processor administration logs are not visible)
- Extension Sync Functionality
- Execution Log Viewer
- Runtime Processor Diagnostics
- Processor Health Dashboard
- Dedicated Troubleshooting Screen
Logging and debugging should be performed using one or more of the following:
- Application logs
- Platform logs written through
IZnodeLogging - Local debugging
- Transmission execution results
This approach keeps the current Custom Processor implementation lightweight while still providing extensibility hooks before and after Commerce Connector transmission step execution.
3. Supported Mapping Expressions
The Mapping Engine supports functions, arithmetic operations, boolean expressions, ternary operators, constants, direct field mappings, and nested JSON mappings. These expressions can be used to dynamically transform and map data during transmission execution.
3.1 Supported Expression Types
The Mapping Engine supports a rich set of expressions that enable dynamic data transformations during mapping. These expressions can be used individually or combined to build complex mapping logic.
The following expression types are supported:
- Functions
- Arithmetic operations
- Boolean expressions
- Ternary expressions
- Constants
- Direct field mappings
- Nested object mappings
- Collection mappings
3.2 Functions, Arithmetic, Boolean Logic, and Ternary Expressions
The following examples demonstrate the supported expression syntax that can be used within mappings.
# Functions, arithmetic, boolean logic, and ternary expressions.
dataAreaId: usmf
CustomerNameUpper:
{{Upper($Step2.FirstName)}}
CustomerNameLower:
{{Lower($Step2.LastName)}}
FullName:
{{Concat($Step2.FirstName," ",$Step2.LastName)}}
FirstAddressCity:
{{$Step2.AlternateAddresses[0].City}}
RoundedAmount:
{{Math.Round($Step1.Amount,2)}}
DiscountedPrice:
{{Math.Max($Step1.Price,100)}}
MappedOnDate:
{{DateTime.Today}}
CorrelationId:
{{Guid.NewGuid()}}
LineTotal:
{{$Step1.Qty * $Step1.Price}}
IsHighValue:
{{$Step1.Amount > 1000}}
CustomerTier:
{{$Step1.Amount > 1000 ? "Premium" : "Normal"}}
Greeting:
Hello {{$Step2.FirstName}}, your order total is {{Math.Round($Step1.Qty * $Step1.Price,2)}}!
3.3 Nested JSON Mapping
The Mapping Engine also supports nested JSON object mappings. This allows developers to generate structured request payloads while referencing values from previous transmission steps.
Example
{
"dataAreaId": "usmf",
"salesOrder": {
"name": "{{$Step1.ClassNumber}}",
"orderDate": "{{FormatDate(DateTime.Today,"yyyy-MM-dd")}}"
}
}
3.4 Constants and Direct Field Mapping
Constants and direct field mappings can be combined to construct request payloads without requiring additional transformations.
Example
dataAreaId: usmf
SalesOrderName:
{{$Step1.ClassNumber}}
CustomerFirstName:
{{$Step2.FirstName}}
CustomerCity:
{{$Step2.Address.City}}
Summary
Custom Processor Extensibility enables developers to extend Commerce Connector by executing custom business logic before and after transmission step execution. By implementing the IBaseProcessor interface, developers can validate data, modify execution outputs, integrate with external systems, and leverage platform services through Dependency Injection.
The Mapping Engine complements custom processors by supporting direct mappings, transformation functions, arithmetic expressions, conditional logic, nested JSON mappings, and processor-based extensibility, enabling the implementation of complex integration scenarios with minimal customization.
3.1 Supported Expression Types
The Mapping Engine supports a rich set of expressions that enable dynamic data transformations during mapping. These expressions can be used individually or combined to build complex mapping logic.
The following expression types are supported:
- Functions
- Arithmetic operations
- Boolean expressions
- Ternary expressions
- Constants
- Direct field mappings
- Nested object mappings
- Collection mappings
3.2 Functions, Arithmetic, Boolean Logic, and Ternary Expressions
The following examples demonstrate the supported expression syntax that can be used within mappings.
# Functions, arithmetic, boolean logic, and ternary expressions.
dataAreaId: usmf
CustomerNameUpper:
{{Upper($Step2.FirstName)}}
CustomerNameLower:
{{Lower($Step2.LastName)}}
FullName:
{{Concat($Step2.FirstName," ",$Step2.LastName)}}
FirstAddressCity:
{{$Step2.AlternateAddresses[0].City}}
RoundedAmount:
{{Math.Round($Step1.Amount,2)}}
DiscountedPrice:
{{Math.Max($Step1.Price,100)}}
MappedOnDate:
{{DateTime.Today}}
CorrelationId:
{{Guid.NewGuid()}}
LineTotal:
{{$Step1.Qty * $Step1.Price}}
IsHighValue:
{{$Step1.Amount > 1000}}
CustomerTier:
{{$Step1.Amount > 1000 ? "Premium" : "Normal"}}
Greeting:
Hello {{$Step2.FirstName}}, your order total is {{Math.Round($Step1.Qty * $Step1.Price,2)}}!
3.3 Nested JSON Mapping
The Mapping Engine also supports nested JSON object mappings. This allows developers to generate structured request payloads while referencing values from previous transmission steps.
Example
{
"dataAreaId": "usmf",
"salesOrder": {
"name": "{{$Step1.ClassNumber}}",
"orderDate": "{{FormatDate(DateTime.Today,"yyyy-MM-dd")}}"
}
}
3.4 Constants and Direct Field Mapping
Constants and direct field mappings can be combined to construct request payloads without requiring additional transformations.
Example
dataAreaId: usmf
SalesOrderName:
{{$Step1.ClassNumber}}
CustomerFirstName:
{{$Step2.FirstName}}
CustomerCity:
{{$Step2.Address.City}}
Summary
Custom Processor Extensibility enables developers to extend Commerce Connector by executing custom business logic before and after transmission step execution. By implementing the IBaseProcessor interface, developers can validate data, modify execution outputs, integrate with external systems, and leverage platform services through Dependency Injection.
The Mapping Engine complements custom processors by supporting direct mappings, transformation functions, arithmetic expressions, conditional logic, nested JSON mappings, and processor-based extensibility, enabling the implementation of complex integration scenarios with minimal customization.