ACommonNode
Overview #
ACommonNode is the base class for creating custom processing nodes. Custom nodes are similar to connectors but can be used for general-purpose data processing, transformation, or utility functions that don't fit the connector pattern.
Purpose:
- Create custom data processing logic
- Transform data between steps
- Implement utility functions
- Build specialized processing nodes
Location: orchesty-nodejs-sdk/lib/Commons/ACommonNode.ts
When to Use #
Use ACommonNode for:
- Data transformation (format conversion, filtering, mapping)
- Utility functions (validation, enrichment, calculation)
- Custom logic that doesn't fit connector or batch patterns
- Processing nodes without external API calls
Difference from AConnector:
ACommonNode: General-purpose processing, typically no external API callsAConnector: Specifically for integrating with external services/APIs
Class Hierarchy #
ANode
↓
ACommonNode (You extend this)
↓
YourCustomNode
Abstract Methods You Must Implement #
processAction() #
public abstract processAction(dto: ProcessDto): ProcessDto | Promise<ProcessDto>
Main method where you implement your custom processing logic.
Parameters:
| Parameter | Type | Description |
|---|---|---|
dto | ProcessDto | Process data transfer object |
Returns: ProcessDto | Promise<ProcessDto> - Modified process DTO
Pattern:
public processAction(dto: ProcessDto): ProcessDto {
// 1. Get input data
const input = dto.getJsonData();
// 2. Process/transform data
const output = this.transformData(input);
// 3. Set output data
dto.setJsonData(output);
// 4. Return modified DTO
return dto;
}
getName() #
public abstract getName(): string
Returns the unique identifier for your custom node.
Example:
public getName(): string {
return 'data-transformer';
}
Inherited Methods Available #
getApplication() #
protected getApplication<T extends IApplication>(): T
Returns the associated application instance (if set).
getDbClient() #
protected getDbClient(): DatabaseClient
Returns the database client for accessing repositories.
getApplicationInstallFromProcess() #
protected async getApplicationInstallFromProcess(dto: AProcessDto): Promise<ApplicationInstall>
Gets application credentials for the current user.
Usage Examples #
Basic Custom Node #
From: orchesty-nodejs-sdk/test/CustomNode/TestCustomNode.ts
import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';
export default class TestCustomNode extends ACommonNode {
public getName(): string {
return 'testcustom';
}
public processAction(dto: ProcessDto): ProcessDto {
// Transform data
dto.setJsonData({
test: 'custom',
inner: {
date: Date.now().toString(),
one: 2
}
});
return dto;
}
}
Data Transformation Node #
import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';
export default class DataTransformerNode extends ACommonNode {
public getName(): string {
return 'data-transformer';
}
public processAction(dto: ProcessDto): ProcessDto {
const input = dto.getJsonData();
// Transform data structure
const output = {
transformedAt: new Date().toISOString(),
originalData: input,
processed: {
// Map fields
fullName: `${input.firstName} ${input.lastName}`,
email: input.email.toLowerCase(),
age: this.calculateAge(input.birthDate),
tags: this.extractTags(input)
}
};
dto.setJsonData(output);
return dto;
}
private calculateAge(birthDate: string): number {
const birth = new Date(birthDate);
const today = new Date();
let age = today.getFullYear() - birth.getFullYear();
const monthDiff = today.getMonth() - birth.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--;
}
return age;
}
private extractTags(data: any): string[] {
const tags: string[] = [];
if (data.isPremium) tags.push('premium');
if (data.newsletter) tags.push('newsletter');
return tags;
}
}
Data Validation Node #
import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';
import ResultCode from '../../lib/Utils/ResultCode';
export default class DataValidatorNode extends ACommonNode {
public getName(): string {
return 'data-validator';
}
public processAction(dto: ProcessDto): ProcessDto {
const input = dto.getJsonData();
// Validate required fields
const requiredFields = ['email', 'name', 'phone'];
const missingFields: string[] = [];
requiredFields.forEach(field => {
if (!input[field]) {
missingFields.push(field);
}
});
if (missingFields.length > 0) {
dto.setStopProcess(
ResultCode.STOP_AND_FAILED,
`Missing required fields: ${missingFields.join(', ')}`
);
return dto;
}
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(input.email)) {
dto.setStopProcess(
ResultCode.STOP_AND_FAILED,
'Invalid email format'
);
return dto;
}
// Validation passed
dto.setJsonData({
...input,
validated: true,
validatedAt: new Date().toISOString()
});
return dto;
}
}
Data Filter Node #
import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';
export default class DataFilterNode extends ACommonNode {
public getName(): string {
return 'data-filter';
}
public processAction(dto: ProcessDto): ProcessDto {
const input = dto.getJsonData();
// Filter out items based on criteria
const filtered = input.items.filter((item: any) => {
// Keep only active items with positive value
return item.status === 'active' && item.value > 0;
});
dto.setJsonData({
filteredItems: filtered,
originalCount: input.items.length,
filteredCount: filtered.length,
removedCount: input.items.length - filtered.length
});
return dto;
}
}
Data Enrichment Node #
import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';
export default class DataEnrichmentNode extends ACommonNode {
public getName(): string {
return 'data-enrichment';
}
public processAction(dto: ProcessDto): ProcessDto {
const input = dto.getJsonData();
// Enrich data with additional information
const enriched = {
...input,
metadata: {
processedAt: new Date().toISOString(),
processorVersion: '1.0.0',
environment: process.env.NODE_ENV || 'development'
},
computed: {
totalValue: this.calculateTotal(input.items),
averageValue: this.calculateAverage(input.items),
itemCount: input.items.length
}
};
dto.setJsonData(enriched);
return dto;
}
private calculateTotal(items: any[]): number {
return items.reduce((sum, item) => sum + (item.value || 0), 0);
}
private calculateAverage(items: any[]): number {
if (items.length === 0) return 0;
return this.calculateTotal(items) / items.length;
}
}
Conditional Router Node #
import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';
export default class ConditionalRouterNode extends ACommonNode {
public getName(): string {
return 'conditional-router';
}
public processAction(dto: ProcessDto): ProcessDto {
const input = dto.getJsonData();
// Route based on conditions
if (input.priority === 'high') {
dto.setForceFollowers('high-priority-handler');
} else if (input.value > 1000) {
dto.setForceFollowers('large-value-handler');
} else {
dto.setForceFollowers('default-handler');
}
// Add routing information
dto.setJsonData({
...input,
routedBy: 'conditional-router',
routedAt: new Date().toISOString()
});
return dto;
}
}
Common Patterns #
Pattern 1: Simple Transformation #
public processAction(dto: ProcessDto): ProcessDto {
const input = dto.getJsonData();
const output = this.transform(input);
dto.setJsonData(output);
return dto;
}
Pattern 2: Validation with Error Handling #
public processAction(dto: ProcessDto): ProcessDto {
const input = dto.getJsonData();
const errors = this.validate(input);
if (errors.length > 0) {
dto.setStopProcess(
ResultCode.STOP_AND_FAILED,
`Validation failed: ${errors.join(', ')}`
);
return dto;
}
dto.setJsonData({ ...input, validated: true });
return dto;
}
Pattern 3: Enrichment #
public processAction(dto: ProcessDto): ProcessDto {
const input = dto.getJsonData();
const enrichedData = {
...input,
enrichments: this.addEnrichments(input)
};
dto.setJsonData(enrichedData);
return dto;
}
Registration #
import DIContainer from './DIContainer/Container';
import DataTransformerNode from './nodes/DataTransformerNode';
import DataValidatorNode from './nodes/DataValidatorNode';
const container = new DIContainer();
// Register custom nodes
const transformerNode = new DataTransformerNode();
const validatorNode = new DataValidatorNode();
container.setCustomNode(transformerNode);
container.setCustomNode(validatorNode);
Next Steps #
- ProcessDto - Work with data in nodes
- AConnector - Create connectors for APIs
- ABatchNode - Create batch processing nodes
- DIContainer - Register custom nodes