diff --git a/website/package-lock.json b/website/package-lock.json index 07c67f34..cb7c020f 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -21,9 +21,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": { @@ -6383,9 +6385,9 @@ } }, "node_modules/bson": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.3.tgz", - "integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==", + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", "license": "Apache-2.0", "engines": { "node": ">=16.20.1" @@ -6701,7 +6703,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -6716,14 +6717,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6738,7 +6737,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -9884,7 +9882,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -12874,13 +12871,13 @@ "optional": true }, "node_modules/mongodb": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.16.0.tgz", - "integrity": "sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==", + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.17.0.tgz", + "integrity": "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.3", + "bson": "^6.10.4", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -14980,7 +14977,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -18107,7 +18103,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -18124,7 +18119,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -18143,7 +18137,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -18153,14 +18146,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", diff --git a/website/package.json b/website/package.json index e1888f1d..e25bf9c9 100644 --- a/website/package.json +++ b/website/package.json @@ -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": { diff --git a/website/scripts/migrate-field-type.js b/website/scripts/migrate-field-type.js new file mode 100644 index 00000000..458f8a09 --- /dev/null +++ b/website/scripts/migrate-field-type.js @@ -0,0 +1,122 @@ +const { MongoClient } = require('mongodb'); +const { + stripHtml, + areaToString, + updateTestimonialFeedback, + updateTableRowsDescriptions, +} = require('./migrate-field-type.utils'); + +const getCollection = async ( + mongoUri = 'mongodb://localhost:27017', + dbName = 'test', +) => { + const client = new MongoClient(mongoUri); + await client.connect(); + const db = client.db(dbName); + 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; +}; + +const migrateTestimonialFeedbackToString = async (mongoUri, dbName) => { + const { client, collection } = await getCollection(mongoUri, dbName); + 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 (mongoUri, dbName) => { + const { client, collection } = await getCollection(mongoUri, dbName); + 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) { + const yargs = require('yargs/yargs'); + const { hideBin } = require('yargs/helpers'); + 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; + + (async () => { + try { + const testimonials = await migrateTestimonialFeedbackToString( + MONGODB_URI, + DB_NAME, + ); + process.stdout.write(`Updated testimonials: ${testimonials}\n`); + const tables = await migrateTableDescriptions(MONGODB_URI, DB_NAME); + 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, + getCollection, + processBatches, + migrateTestimonialFeedbackToString, + migrateTableDescriptions, +}; diff --git a/website/scripts/migrate-field-type.test.js b/website/scripts/migrate-field-type.test.js new file mode 100644 index 00000000..71c0580c --- /dev/null +++ b/website/scripts/migrate-field-type.test.js @@ -0,0 +1,210 @@ +const { + stripHtml, + areaToString, + updateTestimonialFeedback, + updateTableRowsDescriptions, +} = require('./migrate-field-type.utils'); + +const { + getCollection, + processBatches, + migrateTestimonialFeedbackToString, + migrateTableDescriptions, +} = require('./migrate-field-type'); + +jest.mock('mongodb', () => { + const mCollection = { + find: jest.fn(), + toArray: jest.fn(), + updateOne: jest.fn(), + }; + const mDb = { collection: jest.fn(() => mCollection), close: jest.fn() }; + const mClient = { + connect: jest.fn().mockResolvedValue(), + db: jest.fn(() => mDb), + close: jest.fn().mockResolvedValue(), + }; + return { MongoClient: jest.fn(() => mClient) }; +}); + +describe('stripHtml', () => { + test('removes
and tags and their closing tags', () => {
+ expect(stripHtml(' Text Nested abc foo abc foo abc foo ', start);
+ if (end === -1) break;
+ temp = temp.slice(0, start) + temp.slice(end + 1);
+ }
+ temp = temp.replaceAll('