adpa-enterprise-framework-automation
Version:
Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe
445 lines • 18 kB
JavaScript
/**
* Adobe PDF Processor - Core Implementation
* Phase 1: PDF Services Integration with Enhanced Error Handling and Security
*/
import { getAdobeConfig, ADOBE_API_LIMITS, ADOBE_ERROR_CODES, QUALITY_PRESETS } from './config.js';
import { CircuitBreaker } from '../utils/circuit-breaker.js';
import { RateLimiter } from '../utils/rate-limiter.js';
import { Logger } from '../utils/logger.js';
/**
* Adobe PDF Services processor with enterprise-grade reliability and security
*/
export class AdobePDFProcessor {
config;
circuitBreaker;
rateLimiter;
logger;
authToken = null;
tokenExpiry = null;
constructor(config) {
this.config = { ...getAdobeConfig(), ...config };
this.logger = new Logger('AdobePDFProcessor'); // Initialize circuit breaker for resilience
this.circuitBreaker = new CircuitBreaker({
failureThreshold: ADOBE_API_LIMITS.CIRCUIT_BREAKER_THRESHOLD,
resetTimeout: ADOBE_API_LIMITS.CIRCUIT_BREAKER_RESET_TIMEOUT,
monitor: (state) => {
this.logger.info(`Circuit breaker state changed: ${state}`);
}
});
// Initialize rate limiter
this.rateLimiter = new RateLimiter({
requestsPerMinute: ADOBE_API_LIMITS.REQUESTS_PER_MINUTE,
requestsPerHour: ADOBE_API_LIMITS.REQUESTS_PER_HOUR
});
this.logger.info('Adobe PDF Processor initialized', {
environment: this.config.environment,
apiBaseUrl: this.config.apiBaseUrl
});
}
/**
* Generate professional PDF from markdown content with corporate branding
*/
async generateProfessionalPDF(markdownContent, template) {
const startTime = Date.now();
const operationId = this.generateOperationId();
this.logger.info('Starting PDF generation', {
operationId,
templateId: template.id,
contentLength: markdownContent.length
});
try {
// Rate limiting check
await this.rateLimiter.waitForAvailableSlot();
// Input validation
this.validateInputs(markdownContent, template);
// Execute with circuit breaker protection
const result = await this.circuitBreaker.execute(async () => {
return await this.executeProcessing(markdownContent, template, operationId);
});
const processingTime = Date.now() - startTime;
this.logger.info('PDF generation completed successfully', {
operationId,
processingTime,
outputSize: result.size
});
return result;
}
catch (error) {
const processingTime = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('PDF generation failed', {
operationId,
error: errorMessage,
processingTime
});
throw this.createProcessingError(ADOBE_ERROR_CODES.PROCESSING_FAILED, `PDF generation failed: ${errorMessage}`, error, { operationId, templateId: template.id });
}
}
/**
* Add interactive elements to PDF (bookmarks, forms, signatures)
*/
async addInteractiveElements(pdf, metadata) {
const operationId = this.generateOperationId();
this.logger.info('Adding interactive elements', {
operationId,
pdfId: pdf.id,
elementsCount: {
bookmarks: metadata.tableOfContents?.length || 0,
reviewFields: metadata.reviewFields?.length || 0,
approvers: metadata.approvers?.length || 0
}
});
try {
await this.rateLimiter.waitForAvailableSlot();
const enhancedPdf = await this.circuitBreaker.execute(async () => {
return await this.processInteractiveElements(pdf, metadata, operationId);
});
this.logger.info('Interactive elements added successfully', {
operationId,
pdfId: enhancedPdf.id
});
return enhancedPdf;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Failed to add interactive elements', {
operationId,
pdfId: pdf.id,
error: errorMessage
});
throw this.createProcessingError(ADOBE_ERROR_CODES.PROCESSING_FAILED, `Interactive elements processing failed: ${errorMessage}`, error, { operationId, pdfId: pdf.id });
}
}
/**
* Validate PDF document structure and content
*/
async validateDocument(pdf) {
const operationId = this.generateOperationId();
this.logger.info('Starting document validation', {
operationId,
pdfId: pdf.id,
size: pdf.size
});
try {
const validation = await this.performValidation(pdf, operationId);
this.logger.info('Document validation completed', {
operationId,
valid: validation.valid,
errorsCount: validation.errors.length,
warningsCount: validation.warnings.length
});
return validation;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Document validation failed', {
operationId,
pdfId: pdf.id,
error: errorMessage
});
throw this.createProcessingError(ADOBE_ERROR_CODES.PROCESSING_FAILED, `Document validation failed: ${errorMessage}`, error, { operationId, pdfId: pdf.id });
}
}
/**
* Optimize PDF for web delivery
*/
async optimizeForWeb(pdf) {
return this.optimizeDocument(pdf, 'web', QUALITY_PRESETS.standard);
}
/**
* Optimize PDF for print production
*/
async optimizeForPrint(pdf) {
return this.optimizeDocument(pdf, 'print', QUALITY_PRESETS.print);
}
// Private implementation methods
async executeProcessing(markdownContent, template, operationId) {
// Ensure authentication
await this.ensureAuthentication();
// Step 1: Convert markdown to structured HTML
const htmlContent = await this.markdownToHTML(markdownContent);
this.logger.debug('Markdown converted to HTML', { operationId });
// Step 2: Apply corporate branding template
const styledHTML = await this.applyBrandingTemplate(htmlContent, template);
this.logger.debug('Branding template applied', { operationId });
// Step 3: Generate PDF using Adobe PDF Services
const pdfBuffer = await this.callAdobePDFAPI({
html: styledHTML,
options: this.buildPDFOptions(template),
operationId
});
// Step 4: Create PDF document object
const pdfDocument = {
id: this.generateDocumentId(),
content: pdfBuffer,
metadata: this.extractMetadata(styledHTML, template),
format: 'pdf',
size: pdfBuffer.length,
createdAt: new Date()
};
return pdfDocument;
}
async processInteractiveElements(pdf, metadata, operationId) {
await this.ensureAuthentication();
let enhancedContent = pdf.content;
// Add bookmarks for navigation
if (metadata.tableOfContents && metadata.tableOfContents.length > 0) {
enhancedContent = await this.addBookmarks(enhancedContent, metadata.tableOfContents);
this.logger.debug('Bookmarks added', {
operationId,
bookmarksCount: metadata.tableOfContents.length
});
}
// Add form fields for stakeholder input
if (metadata.reviewFields && metadata.reviewFields.length > 0) {
enhancedContent = await this.addFormFields(enhancedContent, metadata.reviewFields);
this.logger.debug('Form fields added', {
operationId,
fieldsCount: metadata.reviewFields.length
});
}
// Add digital signature fields for approval workflow
if (metadata.approvers && metadata.approvers.length > 0) {
enhancedContent = await this.addSignatureFields(enhancedContent, metadata.approvers);
this.logger.debug('Signature fields added', {
operationId,
signatureFields: metadata.approvers.length
});
}
return {
...pdf,
content: enhancedContent,
metadata: {
...pdf.metadata,
...metadata
}
};
}
async performValidation(pdf, operationId) {
const errors = [];
const warnings = [];
// Validate file size
if (pdf.size > ADOBE_API_LIMITS.MAX_FILE_SIZE_MB * 1024 * 1024) {
errors.push({
code: ADOBE_ERROR_CODES.FILE_TOO_LARGE,
message: `File size ${pdf.size} exceeds maximum allowed size`,
severity: 'error'
});
}
// Validate PDF structure (mock implementation)
try {
// This would integrate with Adobe PDF validation APIs
const structureValid = await this.validatePDFStructure(pdf.content);
if (!structureValid) {
warnings.push({
code: 'PDF_STRUCTURE_WARNING',
message: 'PDF structure may have issues',
suggestion: 'Consider regenerating the document'
});
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
errors.push({
code: 'VALIDATION_ERROR',
message: `Structure validation failed: ${errorMessage}`,
severity: 'warning'
});
}
return {
valid: errors.length === 0,
errors,
warnings,
metadata: pdf.metadata
};
}
async optimizeDocument(pdf, target, preset) {
const operationId = this.generateOperationId();
this.logger.info('Optimizing document', {
operationId,
pdfId: pdf.id,
target,
preset: preset
});
try {
await this.ensureAuthentication();
const optimizedContent = await this.callAdobeOptimizationAPI({
content: pdf.content,
preset,
target,
operationId
});
const optimizedPdf = {
...pdf,
content: optimizedContent,
size: optimizedContent.length
};
this.logger.info('Document optimization completed', {
operationId,
originalSize: pdf.size,
optimizedSize: optimizedContent.length,
compressionRatio: ((pdf.size - optimizedContent.length) / pdf.size * 100).toFixed(2) + '%'
});
return optimizedPdf;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Document optimization failed', {
operationId,
pdfId: pdf.id,
error: errorMessage
});
throw this.createProcessingError(ADOBE_ERROR_CODES.PROCESSING_FAILED, `Document optimization failed: ${errorMessage}`, error, { operationId, pdfId: pdf.id, target });
}
}
// Adobe API integration methods
async ensureAuthentication() {
if (this.authToken && this.tokenExpiry && new Date() < this.tokenExpiry) {
return; // Token still valid
}
try {
const response = await this.authenticateWithAdobe();
this.authToken = response.access_token;
this.tokenExpiry = new Date(Date.now() + response.expires_in * 1000);
this.logger.info('Adobe authentication successful', {
expiresAt: this.tokenExpiry.toISOString()
});
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.error('Adobe authentication failed', { error: errorMessage });
throw this.createProcessingError(ADOBE_ERROR_CODES.AUTH_FAILED, `Authentication failed: ${errorMessage}`, error);
}
}
async authenticateWithAdobe() {
// Mock implementation - would integrate with actual Adobe authentication
// This would use JWT authentication with private key signing
return {
access_token: 'mock_token_' + Date.now(),
expires_in: 3600,
token_type: 'Bearer'
};
}
async callAdobePDFAPI(params) {
// Mock implementation - would make actual Adobe API calls
// This represents the HTML to PDF conversion
this.logger.debug('Calling Adobe PDF API', {
operationId: params.operationId,
htmlLength: params.html.length
});
// Simulate processing time
await new Promise(resolve => setTimeout(resolve, 1000));
// Return mock PDF buffer
return Buffer.from(`Mock PDF content for operation ${params.operationId}`);
}
async callAdobeOptimizationAPI(params) {
// Mock implementation for PDF optimization
this.logger.debug('Calling Adobe optimization API', {
operationId: params.operationId,
target: params.target
});
await new Promise(resolve => setTimeout(resolve, 500));
// Simulate compression
const compressionRatio = params.preset.compression === 'high' ? 0.6 : 0.8;
const optimizedSize = Math.floor(params.content.length * compressionRatio);
return Buffer.alloc(optimizedSize, 'Optimized PDF content');
}
// Helper methods
async markdownToHTML(markdown) {
// Mock implementation - would use a robust markdown parser
return `<html><body>${markdown.replace(/\n/g, '<br>')}</body></html>`;
}
async applyBrandingTemplate(html, template) {
// Mock implementation - would apply CSS styling based on template
const styles = this.generateCSSFromTemplate(template);
return `<html><head><style>${styles}</style></head><body>${html}</body></html>`;
}
generateCSSFromTemplate(template) {
return `
body {
font-family: ${template.corporateFonts[0]?.family || 'Arial'}, sans-serif;
color: ${template.colorScheme.text};
background: ${template.colorScheme.background};
margin: ${template.margins.top}mm ${template.margins.right}mm ${template.margins.bottom}mm ${template.margins.left}mm;
}
h1 { color: ${template.colorScheme.primary}; }
h2 { color: ${template.colorScheme.secondary}; }
`;
}
buildPDFOptions(template) {
return {
pageLayout: template.pageLayout,
margins: template.margins,
fonts: template.corporateFonts,
watermark: template.watermark,
headerFooter: template.headerFooter
};
}
extractMetadata(html, template) {
// Mock metadata extraction
return {
title: 'Generated Document',
author: 'ADPA System',
subject: 'Automated Documentation',
keywords: ['ADPA', 'Documentation', 'Adobe'],
tableOfContents: this.extractTOC(html)
};
}
extractTOC(html) {
// Mock TOC extraction from HTML headings
return [
{ title: 'Introduction', level: 1, pageNumber: 1 },
{ title: 'Overview', level: 2, pageNumber: 2 }
];
}
async addBookmarks(content, toc) {
// Mock bookmark addition
this.logger.debug('Adding bookmarks', { bookmarksCount: toc.length });
return content;
}
async addFormFields(content, fields) {
// Mock form field addition
this.logger.debug('Adding form fields', { fieldsCount: fields.length });
return content;
}
async addSignatureFields(content, approvers) {
// Mock signature field addition
this.logger.debug('Adding signature fields', { signatureFields: approvers.length });
return content;
}
async validatePDFStructure(content) {
// Mock PDF structure validation
return content.length > 0;
}
// Utility methods
validateInputs(markdownContent, template) {
if (!markdownContent || markdownContent.trim().length === 0) {
throw new Error('Markdown content cannot be empty');
}
if (!template || !template.id) {
throw new Error('Valid template is required');
}
if (markdownContent.length > 10 * 1024 * 1024) { // 10MB limit
throw new Error('Markdown content exceeds maximum size limit');
}
}
generateOperationId() {
return `adobe-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
generateDocumentId() {
return `pdf-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
createProcessingError(code, message, originalError, context) {
const error = new Error(message);
error.code = code;
error.severity = 'recoverable';
error.context = context;
error.timestamp = new Date();
error.name = 'ProcessingError';
if (originalError) {
error.stack = originalError.stack;
}
return error;
}
}
//# sourceMappingURL=pdf-processor.js.map