Web Automation Framework (WAF) - Best Practices Guide
Bu dokuman, WAF kullanarak karmaşık web automation senaryolarının geliştirilmesi için öğrenilen best practice'leri ve yaygın sorunların çözümlerini içerir.
İçindekiler
- POST Request Interception ve Modifikasyon
- Plugin Geliştirme Best Practices
- Network Management
- Browser Automation Patterns
- Error Handling ve Logging
- Payload Management
- Download ve File Management
- Yaygın Hatalar ve Çözümleri
POST Request Interception ve Modifikasyon
✅ Doğru Yaklaşım: CDP Seviyesinde Interception
// DOĞRU: CDP seviyesinde request modifikasyonu
this.framework.network.interceptRequest(postEndpointPattern, async (requestWrapper) => {
if (requestWrapper.method.toUpperCase() === 'POST' && requestWrapper.url.includes(postEndpointPattern)) {
this.logger.info('POST isteği modifiye ediliyor...');
// Request body'yi modifiye et
requestWrapper.setBody(newPayload);
// Modified request wrapper'ı return et
return requestWrapper;
}
return requestWrapper;
});
❌ Yanlış Yaklaşım: Fetch ile Yeni İstek
// YANLIŞ: Kendi fetch isteği gönderme (loop problemi yaratır)
this.framework.network.interceptRequest(pattern, async (request) => {
// Bu yaklaşım sonsuz loop'a neden olur
const response = await this.framework.browser.page.evaluate(async (payload, url) => {
return fetch(url, {
method: 'POST',
body: JSON.stringify(payload)
});
}, newPayload, request.url);
return null; // Orijinal isteği iptal et
});
Neden CDP Yaklaşımı Daha İyi?
- Session Korunması: Auth token'lar ve cookie'ler korunur
- Loop Problemi Yok: Kendi isteğimiz interceptor'a takılmaz
- Performans: Gerçek request modifikasyonu, yeni istek değil
- Güvenilirlik: Browser'ın native request pipeline'ını kullanır
Plugin Geliştirme Best Practices
Plugin Sınıf Yapısı
class CustomPlugin {
constructor(framework) {
this.framework = framework;
this.downloadAttempts = new Map(); // State tracking
this.name = 'CustomPlugin';
this.version = '1.0.0';
this.description = 'Plugin açıklaması';
this.logger = framework.logger.child({ module: this.name });
this.sitePatterns = ['example.com']; // Desteklenen siteler
this.actions = {
'custom:action': this.customAction.bind(this)
};
}
async onInit() {
this.logger.info(`${this.name} başlatıldı.`);
// Initialization logic
}
async customAction(params) {
// Parametre validasyonu
const { prompt, option = "default" } = params;
if (!prompt) {
throw new Error('Prompt parametresi zorunludur.');
}
try {
// Ana işlem mantığı
return { success: true, result: "..." };
} catch (error) {
this.logger.error('Aksiyon hatası:', error);
return { success: false, error: error.message };
}
}
}
module.exports = CustomPlugin;
Plugin Loading ve Usage
Eklentiler, PluginManager
tarafından yapılandırılan pluginDir
(varsayılan olarak ./plugins
) içinden sadece adlarıyla yüklenebilir. Bu, farklı senaryo veya API sunucusu dosyalarından eklentilere erişimi basitleştirir.
// Senaryo dosyasında plugin kullanımı (adıyla yükleme)
// PluginManager, './plugins/CustomPlugin' veya './plugins/CustomPlugin.js' dosyasını arayacaktır.
await waf.plugins.loadPlugin('CustomPlugin');
const result = await waf.execute('custom:action', {
prompt: "test input",
option: "custom"
});
// Alternatif olarak, tam dosya yoluyla da yüklenebilir:
// const pluginPath = path.resolve(__dirname, '../plugins', 'CustomPlugin');
// await waf.plugins.loadPlugin(pluginPath);
Network Management
Interceptor Kurulumu
// Request interception başlatma
await waf.network.startInterception();
// Pattern-based interceptor ekleme
this.framework.network.interceptRequest('api.example.com/endpoint', async (requestWrapper) => {
// Request modifikasyon mantığı
return requestWrapper;
});
// Response monitoring
this.framework.network.onResponse('api.example.com/endpoint', async (response) => {
const data = response.json();
// Response processing
});
Flag-based Interceptor Control
// Tek seferlik interceptor için flag kullanımı
let interceptorTriggered = false;
this.framework.network.interceptRequest(pattern, async (requestWrapper) => {
if (!interceptorTriggered && /* condition */) {
interceptorTriggered = true;
// İşlem sonrası interceptor'ı temizle
this.framework.network.interceptors.request.delete(pattern);
// Modifikasyon
return requestWrapper;
}
return requestWrapper;
});
Browser Automation Patterns
UI Etkileşimi
// Input alanına text girme
const promptInputSelector = 'textarea[placeholder="Describe your video..."]';
await this.framework.browser.page.evaluate((selector, text) => {
const textarea = document.querySelector(selector);
if (!textarea) throw new Error(`Element bulunamadı: ${selector}`);
const nativeSetter = Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype, "value"
).set;
nativeSetter.call(textarea, text);
textarea.dispatchEvent(new Event('input', { bubbles: true }));
}, promptInputSelector, prompt);
// Button click
await this.framework.browser.page.evaluate(() => {
const span = Array.from(document.querySelectorAll('span'))
.find(el => el.textContent === 'Create video');
if (!span) throw new Error('Button span bulunamadı');
const button = span.closest('button');
if (!button) throw new Error('Button bulunamadı');
if (button.disabled || button.getAttribute('data-disabled') === 'true') {
throw new Error('Button disabled durumda');
}
button.click();
}, promptInputSelector, prompt);
Yeni Sayfa İşlemleri
// Yeni sayfa açma ve ayarlama
const newPage = await this.framework.browser.page.browser().newPage();
await newPage.bringToFront();
await newPage.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
// CDP session kurulumu
const client = await newPage.target().createCDPSession();
await client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: downloadDir
});
// Cleanup
try {
// İşlemler...
} finally {
if (newPage && !newPage.isClosed()) {
await newPage.close();
}
}
Error Handling ve Logging
Structured Logging
// Logger kullanımı
this.logger.info('İşlem başlatılıyor...', { params });
this.logger.debug('Debug bilgisi:', { detail: value });
this.logger.warn('Uyarı:', { warning: message });
this.logger.error('Hata oluştu:', error);
// Console output için
console.log(`✅ İşlem Başarılı: ${result}`);
console.error(`❌ İşlem Başarısız: ${error.message}`);
Error Handling Patterns
async function robustOperation() {
try {
// Ana işlem
const result = await mainOperation();
return { success: true, result };
} catch (error) {
this.logger.error('Operasyon hatası:', error);
// State cleanup
this.downloadAttempts.set(key, 'failed');
return {
success: false,
error: error.message,
stack: error.stack
};
} finally {
// Resource cleanup
await cleanupResources();
}
}
Payload Management
Dinamik Payload Oluşturma
// Aspect ratio'ya göre dinamik boyutlar
function calculateDimensions(aspectRatio) {
const dimensions = {
"1:1": { height: 480, width: 480 },
"2:3": { height: 720, width: 480 },
"3:2": { height: 480, width: 720 },
"16:9": { height: 720, width: 1280 }
};
return dimensions[aspectRatio] || dimensions["1:1"];
}
// Payload oluşturma
function createPayload(params) {
const { prompt, aspectRatio = "1:1", variants = 4 } = params;
const { height, width } = calculateDimensions(aspectRatio);
return {
height,
width,
inpaint_items: [],
n_frames: 1,
n_variants: Math.min(parseInt(variants), 4), // Max limit
operation: "simple_compose",
prompt: prompt.trim(),
type: "image_gen"
};
}
Validation Patterns
function validatePayload(payload) {
const required = ['prompt', 'type', 'height', 'width'];
const missing = required.filter(field => !payload[field]);
if (missing.length > 0) {
throw new Error(`Eksik alanlar: ${missing.join(', ')}`);
}
if (payload.n_variants > 4) {
throw new Error('n_variants maksimum 4 olabilir');
}
return true;
}
Download ve File Management
Download Handling
async function handleDownload(generationId, taskDownloadsDir) {
// Download klasörü oluşturma
await fs.ensureDir(taskDownloadsDir);
// CDP download behavior ayarlama
const client = await newPage.target().createCDPSession();
await client.send('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: taskDownloadsDir
});
// Download tetikleme
await downloadButtonHandle.asElement().click();
await this.framework.browser.wait(8); // Download bekleme
// Dosya kontrolü
const files = await fs.readdir(taskDownloadsDir);
const latestFile = findLatestFile(files, taskDownloadsDir);
return processDownloadedFile(latestFile, taskDownloadsDir);
}
function findLatestFile(files, directory) {
let latestFile = null;
let latestMtime = 0;
for (const file of files) {
const filePath = path.join(directory, file);
const stat = await fs.stat(filePath);
if (stat.isFile() && stat.mtimeMs > latestMtime) {
latestMtime = stat.mtimeMs;
latestFile = file;
}
}
return latestFile;
}
File Extension Handling
function ensureCorrectExtension(fileName, expectedExtensions) {
const extensions = Array.isArray(expectedExtensions)
? expectedExtensions
: [expectedExtensions];
const hasCorrectExtension = extensions.some(ext =>
fileName.toLowerCase().endsWith(ext.toLowerCase())
);
if (!hasCorrectExtension) {
fileName += extensions[0]; // İlk extension'ı ekle
}
return fileName;
}
// Kullanım
const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.gif'];
finalFileName = ensureCorrectExtension(originalFileName, imageExtensions);
Yaygın Hatalar ve Çözümleri
1. Interceptor Loop Problemi
Problem: Kendi gönderdiğimiz fetch isteği interceptor tarafından yakalanıyor.
Çözüm: CDP seviyesinde request modifikasyonu kullan.
// YANLIŞ
const response = await fetch(url, newData); // Bu da yakalanır
// DOĞRU
requestWrapper.setBody(newData); // CDP seviyesinde modifikasyon
return requestWrapper;
2. Session ve Auth Token Kaybı
Problem: Yeni fetch isteği auth bilgilerini içermiyor.
Çözüm: Orijinal request'i modifiye et, yeni istek gönderme.
3. Element Bulunamama
Problem: UI element'leri bulunamıyor.
Çözüm: Robust selector stratejisi kullan.
// Multiple fallback selectors
const selectors = [
'textarea[placeholder="Describe your video..."]',
'textarea[data-testid="prompt-input"]',
'textarea.prompt-input'
];
for (const selector of selectors) {
const element = document.querySelector(selector);
if (element) return element;
}
throw new Error('Prompt input bulunamadı');
4. Asenkron İşlem Timing
Problem: Network istekleri henüz tamamlanmadan işlem devam ediyor.
Çözüm: Promise-based bekleme mekanizmaları.
// Promise race pattern
const result = await Promise.race([
actualOperation(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), 45000)
)
]);
5. Memory Leak'ler
Problem: Event listener'lar ve interceptor'lar temizlenmiyor.
Çözüm: Proper cleanup pattern.
try {
// İşlemler
} finally {
// Cleanup
this.framework.network.interceptors.request.delete(pattern);
this.framework.network.interceptors.response.delete(pattern);
if (newPage && !newPage.isClosed()) {
await newPage.close();
}
}
Örnek Kullanım Senaryoları
1. API Payload Modifikasyonu
- POST request'lerin body'sini farklı parametrelerle değiştirme
- Authentication header'ları ekleme/düzenleme
- API endpoint'lerini redirect etme
2. Multi-step Automation
- Form doldurma + API çağrısı + sonuç bekleme + download
- Pagination ile çok sayfalı veri toplama
- Sequential işlemler zinciri
3. File Processing
- Bulk download işlemleri
- Dosya formatı dönüştürme
- Batch processing workflows
4. Monitoring ve Analytics
- Network traffic analizi
- Performance metrikleri toplama
- Error rate tracking
Framework Konfigürasyon Örnekleri
Basic Configuration
const waf = WAF.create({
browser: {
debugPort: 7222,
headless: false,
timeout: 60000,
userAgent: null
},
network: {
enabled: true,
enableResponseCapture: true,
interceptAll: true
},
logging: {
level: 'debug',
console: true
},
plugins: {
enabled: true,
autoLoad: false
},
storage: {
enabled: true,
dataDir: './scenario_output'
}
});
Production Configuration
const waf = WAF.create({
browser: {
headless: true,
timeout: 120000,
args: ['--no-sandbox', '--disable-dev-shm-usage']
},
network: {
enabled: true,
timeout: 60000,
maxRetries: 3
},
logging: {
level: 'info',
console: false,
file: './logs/automation.log'
},
storage: {
enabled: true,
dataDir: process.env.DATA_DIR || './data'
}
});
Sonuç
Bu best practice'ler, karmaşık web automation senaryolarının geliştirilmesinde yaşanan gerçek problemlere dayalı çözümlerdir. Her yeni senaryo için bu rehberi referans alarak:
- Doğru teknik yaklaşımı seçin (CDP vs fetch)
- Robust error handling uygulayın
- Memory management'e dikkat edin
- Logging ve monitoring ekleyin
- Test ve validation süreçlerini ihmal etmeyin
Bu yaklaşımlar ile WAF kullanarak güvenilir, maintainable ve scalable automation çözümleri geliştirebilirsiniz.