Skip to content
Merged
27 changes: 9 additions & 18 deletions website/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@
"jest-environment-jsdom": "^30.0.0-beta.3",
"lodash": "^4.17.21",
"lozad": "^1.16.0",
"mongodb": "^6.17.0",
"normalize.css": "^8.0.1",
"postmark": "^4.0.5",
"swiper": "^11.2.6",
"yargs": "^17.7.2",
"yup": "^1.6.1"
},
"devDependencies": {
Expand Down
112 changes: 112 additions & 0 deletions website/scripts/migrate-field-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
const { MongoClient } = require('mongodb');
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const {
stripHtml,
areaToString,
updateTestimonialFeedback,
updateTableRowsDescriptions,
} = require('./migrate-field-type.utils');

const { argv } = yargs(hideBin(process.argv))
.option('mongoUri', {
describe: 'MongoDB connection URI',
type: 'string',
demandOption: true,
})
.option('dbName', {
describe: 'MongoDB database name',
type: 'string',
demandOption: true,
})
.help()
.alias('help', 'h');

const MONGODB_URI = argv.mongoUri;
const DB_NAME = argv.dbName;

const getCollection = async () => {
const client = new MongoClient(MONGODB_URI);
await client.connect();
const db = client.db(DB_NAME);
const collection = db.collection('aposDocs');
return { client, collection };
};

const processBatches = async (
batches,
idKey,
collection,
updateFn = updateTestimonialFeedback,
) => {
let updatedCount = 0;
const allPromises = [];
for (const batch of batches) {
allPromises.push(...batch.map((doc) => updateFn(collection, doc, idKey)));
}
const results = await Promise.all(allPromises);
updatedCount = results.reduce((sum, value) => sum + value, 0);
return updatedCount;
};
Comment thread
VitalyyP marked this conversation as resolved.

const migrateTestimonialFeedbackToString = async () => {
const { client, collection } = await getCollection();
try {
const documents = await collection.find({ type: 'testimonials' }).toArray();
const idKey = '_id';
const batchSize = 10;
const batches = [];
for (let i = 0; i < documents.length; i += batchSize) {
batches.push(documents.slice(i, i + batchSize));
}
const updatedCount = await processBatches(batches, idKey, collection);
return updatedCount;
} finally {
await client.close();
}
};

const migrateTableDescriptions = async () => {
const { client, collection } = await getCollection();
try {
const docs = await collection
.find({ 'main.items.type': 'table' })
.toArray();
const idKey = '_id';
const batchSize = 10;
const batches = [];
for (let i = 0; i < docs.length; i += batchSize) {
batches.push(docs.slice(i, i + batchSize));
}
const updatedCount = await processBatches(
batches,
idKey,
collection,
updateTableRowsDescriptions,
);
return updatedCount;
} finally {
await client.close();
}
};

if (require.main === module) {
(async () => {
try {
const testimonials = await migrateTestimonialFeedbackToString();
process.stdout.write(`Updated testimonials: ${testimonials}\n`);
const tables = await migrateTableDescriptions();
process.stdout.write(`Updated table rows: ${tables}\n`);
} catch (error) {
process.stdout.write(`Migration error: ${error}\n`);
throw error;
}
})();
}

module.exports = {
stripHtml,
areaToString,
updateTestimonialFeedback,
updateTableRowsDescriptions,
};
107 changes: 107 additions & 0 deletions website/scripts/migrate-field-type.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
const {
stripHtml,
areaToString,
updateTestimonialFeedback,
updateTableRowsDescriptions,
} = require('./migrate-field-type.utils');

describe('stripHtml', () => {
test('removes <p> and <span> tags and their closing tags', () => {
expect(stripHtml('<p>Text</p>')).toBe('Text');
expect(stripHtml('<span>Text</span>')).toBe('Text');
expect(stripHtml('<p><span>Nested</span></p>')).toBe('Nested');
expect(stripHtml('No tags')).toBe('No tags');
});

test('returns empty string for non-string input', () => {
expect(stripHtml(null)).toBe('');
expect(stripHtml(undefined)).toBe('');
expect(stripHtml(123)).toBe('');
});
});

describe('areaToString', () => {
test('returns string as is (after stripHtml)', () => {
expect(areaToString('<p>abc</p>')).toBe('abc');
});

test('joins items content and strips HTML', () => {
const area = {
items: [{ content: '<p>foo</p>' }, { content: '<span>bar</span>' }],
};
expect(areaToString(area)).toBe('foo bar');
});

test('returns empty string for empty or invalid area', () => {
expect(areaToString({ items: [] })).toBe('');
expect(areaToString({})).toBe('');
expect(areaToString(null)).toBe('');
});
});

describe('updateTestimonialFeedback', () => {
test('updates feedback if it is an area object', async () => {
const collection = { updateOne: jest.fn().mockResolvedValue({}) };
const doc = { _id: 1, feedback: { items: [{ content: '<p>abc</p>' }] } };
const result = await updateTestimonialFeedback(collection, doc, '_id');
expect(result).toBe(1);
expect(collection.updateOne).toHaveBeenCalledWith(
{ _id: 1 },
{ $set: { feedback: 'abc' } },
);
});

test('does not update if feedback is already string', async () => {
const collection = { updateOne: jest.fn() };
const doc = { _id: 2, feedback: 'already string' };
const result = await updateTestimonialFeedback(collection, doc, '_id');
expect(result).toBe(0);
expect(collection.updateOne).not.toHaveBeenCalled();
});
});

describe('updateTableRowsDescriptions', () => {
test('updates table row descriptions if they are area objects', async () => {
const collection = { updateOne: jest.fn().mockResolvedValue({}) };
const doc = {
_id: 1,
main: {
items: [
{
type: 'table',
rows: [
{ description: { items: [{ content: '<p>foo</p>' }] } },
{ description: 'bar' },
],
},
],
},
};
const result = await updateTableRowsDescriptions(collection, doc, '_id');
expect(result).toBe(1);
expect(collection.updateOne).toHaveBeenCalledWith(
{ _id: 1 },
{
$set: {
'main.items': [
{
type: 'table',
rows: [{ description: 'foo' }, { description: 'bar' }],
},
],
},
},
);
});

test('does not update if no area descriptions found', async () => {
const collection = { updateOne: jest.fn() };
const doc = {
_id: 2,
main: { items: [{ type: 'table', rows: [{ description: 'plain' }] }] },
};
const result = await updateTableRowsDescriptions(collection, doc, '_id');
expect(result).toBe(0);
expect(collection.updateOne).not.toHaveBeenCalled();
});
});
Loading