mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-30 21:40:39 +03:00
Updated mapping for stripe_products when product import is skipped (#14965)
refs d63e9256ea
- Following the ref'd commit, when migrating a site the default and free tiers would be skipped because they exist by default in the new site
- As the product is skipped, we don't have the ID available in the imported data to map the stripe_product to
- If the stripe_product isn't mapped, imported members won't be mapped to the correct tier
- This commit adds a lookup for the product by name and slug to restore the correct stripe_product mapping
Co-authored-by: Simon Backx <simon@ghost.org>
This commit is contained in:
parent
2ecb4acc85
commit
859d49626c
@ -8,6 +8,7 @@ class StripeProductsImporter extends BaseImporter {
|
||||
super(allDataFromFile, {
|
||||
modelName: 'StripeProduct',
|
||||
dataKeyToImport: 'stripe_products',
|
||||
requiredFromFile: ['products'],
|
||||
requiredImportedData: ['products'],
|
||||
requiredExistingData: ['products']
|
||||
});
|
||||
@ -41,11 +42,32 @@ class StripeProductsImporter extends BaseImporter {
|
||||
objectInFile.product_id = importedObject.id;
|
||||
return;
|
||||
}
|
||||
const existingObject = _.find(this.requiredExistingData.products, {id: objectInFile.product_id});
|
||||
|
||||
const existingObjectById = _.find(this.requiredExistingData.products, {id: objectInFile.product_id});
|
||||
// CASE: the product exists in the db already
|
||||
if (existingObject) {
|
||||
if (existingObjectById) {
|
||||
return;
|
||||
}
|
||||
|
||||
// CASE: we skipped product import because a product with the same name and slug exists in the DB
|
||||
debug('lookup product by name and slug');
|
||||
const productFromFile = _.find(
|
||||
this.requiredFromFile.products,
|
||||
{id: objectInFile.product_id}
|
||||
);
|
||||
if (productFromFile) {
|
||||
// look for the existing product with the same name and slug
|
||||
const existingObjectByNameAndSlug = _.find(
|
||||
this.requiredExistingData.products,
|
||||
{name: productFromFile.name, slug: productFromFile.slug}
|
||||
);
|
||||
if (existingObjectByNameAndSlug) {
|
||||
debug(`resolved ${objectInFile.product_id} to ${existingObjectByNameAndSlug.name}`);
|
||||
objectInFile.product_id = existingObjectByNameAndSlug.id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: we don't know what product this is for
|
||||
debug(`ignoring stripe product ${objectInFile.stripe_product_id}`);
|
||||
invalidProducts.push(objectInFile.id);
|
||||
|
@ -378,3 +378,101 @@ describe('DB API (canary)', function () {
|
||||
yearlyPrice.get('stripe_product_id').should.equal('prod_d2c1708c21');
|
||||
});
|
||||
});
|
||||
|
||||
// The following tests will create a new clean database for every test
|
||||
describe('DB API (cleaned)', function () {
|
||||
let backupKey;
|
||||
let schedulerKey;
|
||||
|
||||
beforeEach(async function () {
|
||||
await testUtils.stopGhost();
|
||||
await localUtils.startGhost();
|
||||
request = supertest.agent(config.get('url'));
|
||||
await localUtils.doAuth(request);
|
||||
backupKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-backup'}});
|
||||
schedulerKey = _.find(testUtils.getExistingData().apiKeys, {integration: {slug: 'ghost-scheduler'}});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('Can import a JSON database with products for an existing product', async function () {
|
||||
// Create a product with existing slug
|
||||
const existingProduct = await models.Product.forge({
|
||||
slug: 'ghost-inc',
|
||||
name: 'Ghost Inc.',
|
||||
description: 'Our daily newsletter',
|
||||
type: 'paid',
|
||||
active: 1,
|
||||
visibility: 'public'
|
||||
}).save();
|
||||
|
||||
const res = await request.post(localUtils.API.getApiQuery('db/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.set('Accept', 'application/json')
|
||||
.expect('Content-Type', /json/)
|
||||
.attach('importfile', path.join(__dirname, '/../../../utils/fixtures/export/products_export.json'))
|
||||
.expect(200);
|
||||
|
||||
// Check if we ignored the import of the product
|
||||
const productDuplicate = await models.Product.findOne({slug: 'ghost-inc-2'});
|
||||
should.not.exist(productDuplicate);
|
||||
|
||||
// Check if we have a product
|
||||
const product = await models.Product.findOne({slug: 'ghost-inc'});
|
||||
should.exist(product);
|
||||
product.id.should.equal(existingProduct.id);
|
||||
product.get('slug').should.equal('ghost-inc');
|
||||
product.get('name').should.equal('Ghost Inc.');
|
||||
product.get('description').should.equal('Our daily newsletter');
|
||||
|
||||
// Check settings
|
||||
const portalProducts = await models.Settings.findOne({key: 'portal_products'});
|
||||
should.exist(portalProducts);
|
||||
JSON.parse(portalProducts.get('value')).should.deepEqual([]);
|
||||
|
||||
// Check stripe products
|
||||
const stripeProduct = await models.StripeProduct.findOne({product_id: product.id});
|
||||
should.exist(stripeProduct);
|
||||
stripeProduct.get('stripe_product_id').should.equal('prod_d2c1708c21');
|
||||
stripeProduct.id.should.not.equal('60be1fc9bd3af33564cfb337');
|
||||
|
||||
// Check newsletters
|
||||
const newsletter = await models.Newsletter.findOne({slug: 'test'});
|
||||
should.exist(newsletter);
|
||||
newsletter.get('name').should.equal('Ghost Inc.');
|
||||
// Make sure sender_email is not set
|
||||
should(newsletter.get('sender_email')).equal(null);
|
||||
|
||||
// Check posts
|
||||
const post = await models.Post.findOne({slug: 'test-newsletter'}, {withRelated: ['tiers']});
|
||||
should.exist(post);
|
||||
|
||||
post.get('newsletter_id').should.equal(newsletter.id);
|
||||
post.get('visibility').should.equal('public');
|
||||
post.get('email_recipient_filter').should.equal('status:-free');
|
||||
|
||||
// Check this post is connected to the imported product
|
||||
post.relations.tiers.models.map(m => m.id).should.match([product.id]);
|
||||
|
||||
// Check stripe prices
|
||||
const monthlyPrice = await models.StripePrice.findOne({stripe_price_id: 'price_a425520db0'});
|
||||
should.exist(monthlyPrice);
|
||||
|
||||
const yearlyPrice = await models.StripePrice.findOne({stripe_price_id: 'price_d04baebb73'});
|
||||
should.exist(yearlyPrice);
|
||||
|
||||
monthlyPrice.get('amount').should.equal(500);
|
||||
monthlyPrice.get('currency').should.equal('usd');
|
||||
monthlyPrice.get('interval').should.equal('month');
|
||||
monthlyPrice.get('stripe_price_id').should.equal('price_a425520db0');
|
||||
monthlyPrice.get('stripe_product_id').should.equal('prod_d2c1708c21');
|
||||
|
||||
yearlyPrice.get('amount').should.equal(4800);
|
||||
yearlyPrice.get('currency').should.equal('usd');
|
||||
yearlyPrice.get('interval').should.equal('year');
|
||||
yearlyPrice.get('stripe_price_id').should.equal('price_d04baebb73');
|
||||
yearlyPrice.get('stripe_product_id').should.equal('prod_d2c1708c21');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user