core/server/data/importer/importers/data/data-importer.js
// Allow importing with an options object that is passed through the importer
Allow importing with an options object that is passed through the importer
Allow importing with an options object that is passed through the importer
function doImport(importData, importOptions) {
        importOptions = importOptions || {};
        const ops = [];
        let errors = [];
        let results = [];
        const modelOptions = {
            importing: true,
            context: {
                internal: true
            }
        };
        if (!Object.prototype.hasOwnProperty.call(importOptions, 'returnImportedData')) {
            importOptions.returnImportedData = false;
        }
        if (importOptions.importPersistUser) {
            modelOptions.importPersistUser = importOptions.importPersistUser;
        }
        if (!importData.meta) {
            return Promise.reject(new IncorrectUsageError({
                message: 'Wrong importer structure. `meta` is missing.',
                help: 'https://ghost.org/docs/migration/custom/'
            }));
        }
        if (!importData.meta.version) {
            return Promise.reject(new IncorrectUsageError({
                message: 'Wrong importer structure. `meta.version` is missing.',
                help: 'https://ghost.org/docs/migration/custom/'
            }));
        }
        if (!semver.valid(importData.meta.version)) {
            return Promise.reject(new IncorrectUsageError({
                message: 'Detected unsupported file structure.',
                help: 'Please install Ghost 1.0, import the file and then update your blog to the latest Ghost version.\nVisit https://ghost.org/docs/update/ or ask for help in our https://forum.ghost.org.'
            }));
        }
        this.init(importData);
        return models.Base.transaction(function (transacting) {
            modelOptions.transacting = transacting;
            _.each(importers, function (importer) {
                ops.push(function doModelImport() {
                    return importer.fetchExisting(modelOptions, importOptions)
                        .then(function () {
                            return importer.beforeImport(modelOptions, importOptions);
                        })
                        .then(function () {
                            if (importer.options.requiredImportedData.length) {
                                _.each(importer.options.requiredImportedData, (key) => {
                                    importer.requiredImportedData[key] = importers[key].importedData;
                                });
                            }
                            if (importer.options.requiredExistingData.length) {
                                _.each(importer.options.requiredExistingData, (key) => {
                                    importer.requiredExistingData[key] = importers[key].existingData;
                                });
                            }
                            return importer.replaceIdentifiers(modelOptions, importOptions);
                        })
                        .then(function () {
                            return importer.doImport(modelOptions, importOptions)
                                .then(function (_results) {
                                    results = results.concat(_results);
                                });
                        });
                });
            });
            ops.push(() => {
                const importedStripePrices = importers.stripe_prices.importedData;
                const importedProducts = importers.products.importedData;
                const productOps = [];
                _.forEach(importedProducts, (importedProduct) => {
                    return _.forEach(['monthly_price_id', 'yearly_price_id'], (field) => {
                        const mappedPrice = _.find(importedStripePrices, {originalId: importedProduct[field]});
                        if (mappedPrice) {
                            productOps.push(() => {
                                return models.Product.edit({[field]: mappedPrice.id}, {id: importedProduct.id, transacting});
                            });
                        }
                    });
                });
                return sequence(productOps);
            });
            return sequence(ops)
                .then(function () {
                    results.forEach(function (promise) {
                        if (!promise.isFulfilled()) {
                            errors = errors.concat(promise.reason());
                        }
                    });
                    if (errors.length === 0) {
                        return;
                    } else {
                        throw errors;
                    }
                });
        }).then(function () {
            const toReturn = {
                data: {},
                originalData: importData.data,
                problems: []
            };
            _.each(importers, function (importer) {
                toReturn.problems = toReturn.problems.concat(importer.problems);
                if (importOptions.returnImportedData) {
                    toReturn.data[importer.dataKeyToImport] = importer.importedDataToReturn;
                }
            });
            return toReturn;
        }).catch(function (err) {
            debug(err);
            return Promise.reject(err);
        }).finally(() => {
            importers = {};
            results = null;
            importData = null;
        });
    }
function doImport ( importData , importOptions ) { importOptions = importOptions || { } ; const ops = [ ] ; let errors = [ ] ; let results = [ ] ; const modelOptions = { importing : true , context : { internal : true } } ; if ( ! Object . prototype . hasOwnProperty . call ( importOptions , 'returnImportedData' ) ) { importOptions . returnImportedData = false ; } if ( importOptions . importPersistUser ) { modelOptions . importPersistUser = importOptions . importPersistUser ; } if ( ! importData . meta ) { return Promise . reject ( new IncorrectUsageError ( { message : 'Wrong importer structure. `meta` is missing.' , help : 'https://ghost.org/docs/migration/custom/' } ) ) ; } if ( ! importData . meta . version ) { return Promise . reject ( new IncorrectUsageError ( { message : 'Wrong importer structure. `meta.version` is missing.' , help : 'https://ghost.org/docs/migration/custom/' } ) ) ; } if ( ! semver . valid ( importData . meta . version ) ) { return Promise . reject ( new IncorrectUsageError ( { message : 'Detected unsupported file structure.' , help : 'Please install Ghost 1.0, import the file and then update your blog to the latest Ghost version.\nVisit https://ghost.org/docs/update/ or ask for help in our https://forum.ghost.org.' } ) ) ; } this . init ( importData ) ; return models . Base . transaction ( function ( transacting ) { modelOptions . transacting = transacting ; _ . each ( importers , function ( importer ) { ops . push ( function doModelImport ( ) { return importer . fetchExisting ( modelOptions , importOptions ) . then ( function ( ) { return importer . beforeImport ( modelOptions , importOptions ) ; } ) . then ( function ( ) { if ( importer . options . requiredImportedData . length ) { _ . each ( importer . options . requiredImportedData , ( key ) => { importer . requiredImportedData [ key ] = importers [ key ] . importedData ; } ) ; } if ( importer . options . requiredExistingData . length ) { _ . each ( importer . options . requiredExistingData , ( key ) => { importer . requiredExistingData [ key ] = importers [ key ] . existingData ; } ) ; } return importer . replaceIdentifiers ( modelOptions , importOptions ) ; } ) . then ( function ( ) { return importer . doImport ( modelOptions , importOptions ) . then ( function ( _results ) { results = results . concat ( _results ) ; } ) ; } ) ; } ) ; } ) ; ops . push ( ( ) => { const importedStripePrices = importers . stripe_prices . importedData ; const importedProducts = importers . products . importedData ; const productOps = [ ] ; _ . forEach ( importedProducts , ( importedProduct ) => { return _ . forEach ( [ 'monthly_price_id' , 'yearly_price_id' ] , ( field ) => { const mappedPrice = _ . find ( importedStripePrices , { originalId : importedProduct [ field ] } ) ; if ( mappedPrice ) { productOps . push ( ( ) => { return models . Product . edit ( { [ field ] : mappedPrice . id } , { id : importedProduct . id , transacting } ) ; } ) ; } } ) ; } ) ; return sequence ( productOps ) ; } ) ; return sequence ( ops ) . then ( function ( ) { results . forEach ( function ( promise ) { if ( ! promise . isFulfilled ( ) ) { errors = errors . concat ( promise . reason ( ) ) ; } } ) ; if ( errors . length === 0 ) { return ; } else { throw errors ; } } ) ; } ) . then ( function ( ) { const toReturn = { data : { } , originalData : importData . data , problems : [ ] } ; _ . each ( importers , function ( importer ) { toReturn . problems = toReturn . problems . concat ( importer . problems ) ; if ( importOptions . returnImportedData ) { toReturn . data [ importer . dataKeyToImport ] = importer . importedDataToReturn ; } } ) ; return toReturn ; } ) . catch ( function ( err ) { debug ( err ) ; return Promise . reject ( err ) ; } ) . finally ( ( ) => { importers = { } ; results = null ; importData = null ; } ) ; }
Allow importing with an options object that is passed through the importer
Allow importing with an options object that is passed through the importer
// CASE: We deny LTS imports, because these are major version jumps. Only imports from v1 until the latest are supported. //       We can detect a wrong structure by checking the meta version field. Ghost v0 doesn't use semver compliant versions. /**
             * @TODO: figure out how to fix this properly
             * fixup the circular reference from
             * stripe_prices -> stripe_products -> products -> stripe_prices
             *
             * Note: the product importer validates that all values are either
             *   - being imported, or
             *   - already exist in the db
             * so we only need to map imported products
             */ /**
             * data: imported data
             * originalData: data from the json file
             * problems: warnings
             */ // release memory