// validation.js
const Butter = require('buttercms');
const BUTTERCMS_READ_TOKEN = process.env.BUTTERCMS_READ_TOKEN;
const butter = Butter(BUTTERCMS_READ_TOKEN);
class MigrationValidator {
constructor() {
this.issues = [];
this.stats = {
pagesChecked: 0,
collectionsChecked: 0,
mediaChecked: 0,
issuesFound: 0
};
}
async validatePages(pageType, expectedCount) {
console.log(`Validating ${pageType} pages...`);
const response = await butter.page.list(pageType, { page_size: 100 });
const pages = response.data.data;
// Check count
if (pages.length !== expectedCount) {
this.addIssue('count', `Expected ${expectedCount} ${pageType} pages, found ${pages.length}`);
}
// Validate each page
for (const page of pages) {
await this.validatePage(page);
this.stats.pagesChecked++;
}
}
async validatePage(page) {
const issues = [];
// Check required fields
if (!page.fields.title) {
issues.push('Missing title');
}
// Check for empty body
if (page.fields.body && page.fields.body.trim() === '') {
issues.push('Empty body content');
}
// Check for broken images
if (page.fields.featured_image) {
const imageValid = await this.checkImageUrl(page.fields.featured_image);
if (!imageValid) {
issues.push(`Broken image: ${page.fields.featured_image}`);
}
}
// Check references
if (page.fields.author && !page.fields.author.slug) {
issues.push('Invalid author reference');
}
// Check for encoding issues
const content = JSON.stringify(page.fields);
if (content.includes('�') || content.includes('&')) {
issues.push('Possible encoding issues');
}
if (issues.length > 0) {
this.addIssue('page', {
slug: page.slug,
title: page.fields.title,
issues
});
}
}
async checkImageUrl(url) {
try {
const response = await fetch(url, { method: 'HEAD' });
return response.ok;
} catch {
return false;
}
}
async validateCollection(collectionKey, expectedCount) {
console.log(`Validating ${collectionKey} collection...`);
const response = await butter.content.retrieve([collectionKey]);
const items = response.data.data[collectionKey] || [];
if (items.length !== expectedCount) {
this.addIssue('count', `Expected ${expectedCount} ${collectionKey} items, found ${items.length}`);
}
this.stats.collectionsChecked++;
}
addIssue(type, details) {
this.issues.push({ type, details, timestamp: new Date().toISOString() });
this.stats.issuesFound++;
}
getReport() {
return {
stats: this.stats,
issues: this.issues,
passed: this.issues.length === 0
};
}
}
// Usage
async function runValidation() {
const validator = new MigrationValidator();
// Validate pages
await validator.validatePages('blog_post', 150); // Expected 150 posts
await validator.validatePages('landing_page', 10); // Expected 10 landing pages
// Validate collections
await validator.validateCollection('blog_authors', 5);
await validator.validateCollection('blog_categories', 12);
// Get report
const report = validator.getReport();
console.log('\n=== Validation Report ===');
console.log(`Pages checked: ${report.stats.pagesChecked}`);
console.log(`Collections checked: ${report.stats.collectionsChecked}`);
console.log(`Issues found: ${report.stats.issuesFound}`);
if (!report.passed) {
console.log('\nIssues:');
for (const issue of report.issues) {
console.log(`- ${issue.type}: ${JSON.stringify(issue.details)}`);
}
} else {
console.log('\n✅ All validations passed!');
}
return report;
}
runValidation().catch(console.error);