Skip to content

Instantly share code, notes, and snippets.

@travist
Created May 23, 2023 02:38
Show Gist options
  • Save travist/8093d97c53b82d39d853b43a1e5e7595 to your computer and use it in GitHub Desktop.
Save travist/8093d97c53b82d39d853b43a1e5e7595 to your computer and use it in GitHub Desktop.

Clone a project from either OSS to Enterprise or from an Enterprise project to another Enterprise project.

Installation

Download the ZIP of this Gist, then install using

npm install

Usage

You can then run this command using the following command.

NODE_CONFIG='{
  "oss": true,
  "src": "mongodb://localhost:27017/formio",
  "dst": "mongodb://localhost:27017/cloned",
  "project": "61051ac21bbda812c36dcbe8"
}' node cloneproject.js

You would set "oss" to false if you are going from Enterprise to Enterprise, and the src and dst are the Database connection strings, including the database name.

The PROJECT is either the Destination Project ID (if you are using OSS), or the Source Project ID if you are going from Enterprise to Enterprise.

/**
* Clones a project in its entirety to a local install.
*
* Run this using
NODE_CONFIG='{
"oss": true,
"src": "mongodb://localhost:27017/formio",
"dst": "mongodb://localhost:27017/cloned",
"project": "61051ac21bbda812c36dcbe8"
}' node cloneproject.js
*/
const config = require('config');
const fs = require('fs');
const { MongoClient, ServerApiVersion, ObjectId } = require('mongodb');
const icons = {
projects: '|',
forms: '+',
submissions: '.',
};
// Get the cloneState and ensure we save it on exit.
let cloneState = {};
try {
cloneState = JSON.parse(fs.readFileSync('clonestate.json', 'utf8'));
}
catch (e) {
cloneState = {};
console.log('No clonestate.json file found.');
}
process.on('exit', () => {
fs.writeFileSync('clonestate.json', JSON.stringify(cloneState, null, 2));
});
async function connectDb(uri) {
const client = new MongoClient(uri, {
serverApi: {
version: ServerApiVersion.v1,
strict: true,
deprecationErrors: true,
}
});
await client.connect();
return await client.db();
};
(async () => {
const srcDb = await connectDb(config.src);
const src = {
db: srcDb,
projects: srcDb.collection('projects'),
forms: srcDb.collection('forms'),
submissions: srcDb.collection('submissions'),
submissionrevisions: srcDb.collection('submissionrevisions'),
roles: srcDb.collection('roles'),
actions: srcDb.collection('actions'),
formrevisions: srcDb.collection('formrevisions'),
tags: srcDb.collection('tags')
};
const destDb = await connectDb(config.dst);
const dest = {
db: destDb,
projects: destDb.collection('projects'),
forms: destDb.collection('forms'),
submissions: destDb.collection('submissions'),
submissionrevisions: destDb.collection('submissionrevisions'),
roles: destDb.collection('roles'),
actions: destDb.collection('actions'),
formrevisions: destDb.collection('formrevisions'),
tags: destDb.collection('tags')
};
// Load the admin user.
const admin = await dest.submissions.findOne({});
// Upsert.
const upsertAll = async function(collection, query, beforeEach, afterEach) {
if (!query._id && cloneState[collection]) {
query.created = {$gt: new Date(cloneState[collection])};
}
const ossProject = (collection === 'projects' && config.oss);
const dbCollection = ossProject ? dest[collection] : src[collection];
const cursor = dbCollection.find(query).sort({created: 1});
while (await cursor.hasNext()) {
const item = await cursor.next();
if (!ossProject) {
item.owner = admin._id;
}
if (beforeEach) {
await beforeEach(item);
}
if (icons.hasOwnProperty(collection)) {
process.stdout.write(icons[collection]);
}
if (!ossProject) {
await dest[collection].updateOne({_id: item._id}, {$set: item}, {upsert: true});
}
try {
cloneState[collection] = (new Date(item.created)).toISOString();
}
catch (err) {
// Do nothing.
}
if (afterEach) {
await afterEach(item);
}
}
delete cloneState[collection];
};
// Upsert everything.
await upsertAll('projects', {_id: new ObjectId(config.project)}, null, async (project) => {
const setProject = async (item) => {
item.project = project._id;
};
const itemQuery = (query) => {
if (!config.oss) {
query.project = project._id;
}
return query;
};
await upsertAll('forms', itemQuery({deleted: {$eq: null}}), setProject, async (form) => {
await upsertAll('submissions', {form: form._id, deleted: {$eq: null}}, setProject, async (submission) => {
await upsertAll('submissionrevisions', {_rid: submission._id});
});
await upsertAll('actions', {form: form._id, deleted: {$eq: null}}, setProject);
await upsertAll('formrevisions', {_rid: form._id, deleted: {$eq: null}}, setProject);
});
await upsertAll('roles', itemQuery({deleted: {$eq: null}}), setProject);
await upsertAll('tags', itemQuery({deleted: {$eq: null}}), setProject);
});
try {
fs.rmSync('clonestate.json');
}
catch(err) {
// Do nothing.
}
// We are done.
console.log('DONE!');
})();
{
"name": "project-clone",
"version": "0.0.1",
"private": true,
"description": "",
"main": "cloneproject.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"config": "^3.3.9",
"mongodb": "^5.4.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment