Skip to main content

Builder Validation API

The validation system ensures DDEX XML output complies with schema requirements, business rules, and partner specifications before generation.

Validation Classes

DDEXValidator

Main validation engine for DDEX Builder:

class DDEXValidator {
constructor(options?: ValidationOptions);
validate(data: any, version?: string): ValidationResult;
validateAsync(data: any, version?: string): Promise<ValidationResult>;
addCustomRule(rule: ValidationRule): void;
removeCustomRule(ruleName: string): boolean;
getAvailableRules(): ValidationRule[];
}

ValidationOptions

Configuration for the validation engine:

interface ValidationOptions {
strictMode?: boolean; // Strict schema compliance
allowPartnerExtensions?: boolean; // Allow partner-specific extensions
customRules?: ValidationRule[]; // Custom validation rules
skipOptionalFields?: boolean; // Skip validation of optional fields
maxErrors?: number; // Maximum errors before stopping
enableWarnings?: boolean; // Include warnings in results
}

Validation Rules

Built-in Rules

The validator includes comprehensive built-in rules:

enum BuiltInRules {
// Schema validation
SCHEMA_COMPLIANCE = 'schemaCompliance',
REQUIRED_FIELDS = 'requiredFields',
DATA_TYPES = 'dataTypes',

// Business logic
UNIQUE_IDENTIFIERS = 'uniqueIdentifiers',
REFERENCE_INTEGRITY = 'referenceIntegrity',
DATE_CONSISTENCY = 'dateConsistency',

// Format validation
ISRC_FORMAT = 'isrcFormat',
UPC_FORMAT = 'upcFormat',
PARTY_IDENTIFIERS = 'partyIdentifiers',

// Technical requirements
DURATION_FORMAT = 'durationFormat',
TERRITORY_CODES = 'territoryCodes',
LANGUAGE_CODES = 'languageCodes'
}

Custom Validation Rules

Create custom rules for specific business requirements:

interface ValidationRule {
name: string;
description: string;
severity: 'error' | 'warning' | 'info';
validate: (data: any, context: ValidationContext) => ValidationError[];
}

// Example custom rule
const customISRCRule: ValidationRule = {
name: 'customISRCValidation',
description: 'Validates ISRC format for our catalog',
severity: 'error',
validate: (data, context) => {
const errors: ValidationError[] = [];

data.resources?.soundRecordings?.forEach((recording, index) => {
const isrc = recording.soundRecordingId?.isrc;
if (isrc && !isrc.match(/^[A-Z]{2}[A-Z0-9]{3}\d{7}$/)) {
errors.push({
path: `resources.soundRecordings[${index}].soundRecordingId.isrc`,
message: 'ISRC must follow format: CC-XXX-YY-NNNNN',
code: 'INVALID_ISRC_FORMAT',
severity: 'error',
value: isrc
});
}
});

return errors;
}
};

Validation Results

ValidationResult

Complete validation outcome:

interface ValidationResult {
isValid: boolean;
errors: ValidationError[];
warnings: ValidationError[];
info: ValidationError[];
summary: ValidationSummary;
metadata: ValidationMetadata;
}

ValidationError

Individual validation issue:

interface ValidationError {
path: string; // JSON path to the invalid field
message: string; // Human-readable error message
code: string; // Machine-readable error code
severity: 'error' | 'warning' | 'info';
value?: any; // The invalid value
expectedType?: string; // Expected data type
suggestions?: string[]; // Possible fixes
}

ValidationSummary

High-level validation statistics:

interface ValidationSummary {
totalChecks: number;
errorCount: number;
warningCount: number;
infoCount: number;
rulesCoverage: number; // Percentage of rules that ran
validationTime: number; // Time taken in milliseconds
}

Usage Examples

Basic Validation

import { DDEXBuilder, DDEXValidator } from 'ddex-builder';

const validator = new DDEXValidator({
strictMode: true,
enableWarnings: true
});

const releaseData = {
releaseId: 'R123456789',
title: 'Example Album',
// ... other release data
};

const result = validator.validate(releaseData, '4.3');

if (!result.isValid) {
console.error(`Validation failed with ${result.errors.length} errors:`);
result.errors.forEach(error => {
console.error(`- ${error.path}: ${error.message}`);
});
} else {
console.log('Validation passed!');
}

Async Validation with Custom Rules

const validator = new DDEXValidator();

// Add custom rules
validator.addCustomRule(customISRCRule);
validator.addCustomRule(labelCodeValidationRule);

// Validate asynchronously
const result = await validator.validateAsync(releaseData, '4.3');

// Process results
if (result.warnings.length > 0) {
console.warn('Validation warnings:');
result.warnings.forEach(warning => {
console.warn(`- ${warning.path}: ${warning.message}`);
});
}

Integration with Builder

const builder = new DDEXBuilder({
validation: {
enabled: true,
strictMode: false,
customRules: [customISRCRule],
maxErrors: 50
}
});

try {
// Builder automatically validates before building
const xml = await builder.build(releaseData);
console.log('Build successful!');
} catch (error) {
if (error.validationErrors) {
console.error('Validation errors prevented build:');
error.validationErrors.forEach(err => {
console.error(`- ${err.path}: ${err.message}`);
});
}
}

Partner-Specific Validation

Preset Validation Rules

import { DDEXValidator, PartnerPresets } from 'ddex-builder';

// Use Spotify-specific validation
const spotifyValidator = new DDEXValidator({
preset: PartnerPresets.SPOTIFY,
strictMode: true
});

// Custom partner validation
const customPartnerRules = {
maxTrackCount: 500,
requiredArtworkDimensions: { width: 1400, height: 1400 },
requiredPreviewClips: true,
allowedTerritories: ['US', 'CA', 'MX']
};

const partnerValidator = new DDEXValidator({
customRules: createPartnerRules(customPartnerRules)
});

Multi-Partner Validation

const multiValidator = new DDEXValidator();

// Validate against multiple partner requirements
const partners = ['spotify', 'apple', 'youtube'];
const results = {};

for (const partner of partners) {
const preset = PartnerPresets[partner.toUpperCase()];
const validator = new DDEXValidator({ preset });
results[partner] = validator.validate(releaseData);
}

// Find common issues
const commonErrors = findCommonValidationIssues(results);
console.log('Issues affecting multiple partners:', commonErrors);

Performance Optimization

Validation Caching

const validator = new DDEXValidator({
cache: {
enabled: true,
maxSize: 1000, // Cache up to 1000 validation results
ttl: 3600000 // 1 hour cache lifetime
}
});

// Subsequent validations of identical data will use cache
const result1 = validator.validate(releaseData);
const result2 = validator.validate(releaseData); // Uses cache

Selective Validation

// Only validate specific sections
const partialResult = validator.validate(releaseData, '4.3', {
sections: ['releases', 'resources'],
skipRules: ['territoryValidation', 'dealValidation']
});

// Validate only changed fields
const incrementalResult = validator.validateIncremental(
originalData,
changedData,
changedPaths
);

Error Recovery

Validation with Auto-Fix

const validator = new DDEXValidator({
autoFix: {
enabled: true,
maxFixes: 10,
allowedFixes: [
'trimWhitespace',
'formatDates',
'normalizeIdentifiers'
]
}
});

const result = validator.validate(releaseData);

if (result.autoFixed) {
console.log(`Auto-fixed ${result.autoFixed.length} issues:`);
result.autoFixed.forEach(fix => {
console.log(`- ${fix.path}: ${fix.description}`);
});
}

Graceful Degradation

const validator = new DDEXValidator({
errorHandling: {
strategy: 'continue', // Continue validation after errors
maxErrors: 100, // Stop after 100 errors
reportPartialResults: true
}
});

const result = validator.validate(problematicData);

// Even with errors, get partial validation results
if (result.partialResults) {
console.log(`Validated ${result.partialResults.coverage}% of data`);
}

See Also