Added support for ON DELETE CASCADE to the schema (#12105)

no-issue

We are in the process of creating migrations to add foreign key constraints
and cascading deletes to the members_stripe_* tables to make listing members
and deleting members faster. As well as the migrations we need to update the
database schema so that new installations have the correct indexes and constraints.

Co-authored-by: Kevin Ansfield <kevin@lookingsideways.co.uk>
This commit is contained in:
Fabien 'egg' O'Carroll 2020-08-05 13:20:30 +02:00 committed by GitHub
parent 0eed0d8c88
commit d15446593a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 21 deletions

View File

@ -40,6 +40,9 @@ function addTableColumn(tableName, table, columnName, columnSpec = schema[tableN
// check if table exists?
column.references(columnSpec.references);
}
if (Object.prototype.hasOwnProperty.call(columnSpec, 'cascadeDelete') && columnSpec.cascadeDelete === true) {
column.onDelete('CASCADE');
}
if (Object.prototype.hasOwnProperty.call(columnSpec, 'defaultTo')) {
column.defaultTo(columnSpec.defaultTo);
}

View File

@ -387,15 +387,14 @@ module.exports = {
},
members_labels: {
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id'},
label_id: {type: 'string', maxlength: 24, nullable: false, references: 'labels.id'},
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', cascadeDelete: true},
label_id: {type: 'string', maxlength: 24, nullable: false, references: 'labels.id', cascadeDelete: true},
sort_order: {type: 'integer', nullable: false, unsigned: true, defaultTo: 0}
},
members_stripe_customers: {
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
member_id: {type: 'string', maxlength: 24, nullable: false, unique: false},
// customer_id is unique: false because mysql with innodb utf8mb4 cannot have unqiue columns larger than 191 chars
customer_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
member_id: {type: 'string', maxlength: 24, nullable: false, unique: false, references: 'members.id', cascadeDelete: true},
customer_id: {type: 'string', maxlength: 255, nullable: false, unique: true},
name: {type: 'string', maxlength: 191, nullable: true},
email: {type: 'string', maxlength: 191, nullable: true},
created_at: {type: 'dateTime', nullable: false},
@ -405,8 +404,8 @@ module.exports = {
},
members_stripe_customers_subscriptions: {
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
customer_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
subscription_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
customer_id: {type: 'string', maxlength: 255, nullable: false, unique: false, references: 'members_stripe_customers.customer_id', cascadeDelete: true},
subscription_id: {type: 'string', maxlength: 255, nullable: false, unique: true},
plan_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
status: {type: 'string', maxlength: 50, nullable: false},
cancel_at_period_end: {type: 'bool', nullable: false, defaultTo: false},

View File

@ -15,8 +15,13 @@ describe('MemberStripeCustomer Model', function run() {
// For some reason the initial .add of MemberStripeCustomer is **not** adding a StripeCustomerSubscription :(
it.skip('Is correctly mapped to the stripe subscriptions', async function () {
const context = testUtils.context.admin;
const member = await Member.add({
email: 'test@test.test'
});
await MemberStripeCustomer.add({
member_id: 'fake_member_id',
member_id: member.get('id'),
customer_id: 'fake_customer_id',
subscriptions: [{
subscription_id: 'fake_subscription_id1',
@ -72,13 +77,12 @@ describe('MemberStripeCustomer Model', function run() {
describe('member', function () {
it('Is correctly mapped to the member', async function () {
const context = testUtils.context.admin;
await Member.add({
id: 'fake_member_id',
const member = await Member.add({
email: 'test@test.member'
}, context);
await MemberStripeCustomer.add({
member_id: 'fake_member_id',
member_id: member.get('id'),
customer_id: 'fake_customer_id'
}, context);
@ -88,25 +92,29 @@ describe('MemberStripeCustomer Model', function run() {
withRelated: ['member']
}));
const member = customer.related('member');
const memberFromRelation = customer.related('member');
should.exist(member, 'MemberStripeCustomer should have been fetched with member');
should.exist(memberFromRelation, 'MemberStripeCustomer should have been fetched with member');
should.equal(member.get('id'), 'fake_member_id');
should.equal(member.get('email'), 'test@test.member');
should.equal(memberFromRelation.get('id'), member.get('id'));
should.equal(memberFromRelation.get('email'), 'test@test.member');
});
});
describe('destroy', function () {
it('Cascades to members_stripe_customers_subscriptions', async function () {
const context = testUtils.context.admin;
const member = await Member.add({
email: 'test@test.member'
}, context);
await MemberStripeCustomer.add({
member_id: 'fake_member_id',
member_id: member.get('id'),
customer_id: 'fake_customer_id'
}, context);
const customer = await MemberStripeCustomer.findOne({
member_id: 'fake_member_id'
customer_id: 'fake_customer_id'
}, context);
should.exist(customer, 'Customer should have been created');
@ -136,12 +144,12 @@ describe('MemberStripeCustomer Model', function run() {
}, context));
const customerAfterDestroy = await MemberStripeCustomer.findOne({
member_id: 'fake_member_id'
customer_id: 'fake_customer_id'
});
should.not.exist(customerAfterDestroy, 'MemberStripeCustomer should have been destroyed');
const subscriptionAfterDestroy = await StripeCustomerSubscription.findOne({
customer_id: customer.get('customer_id')
customer_id: 'fake_customer_id'
});
should.not.exist(subscriptionAfterDestroy, 'StripeCustomerSubscription should have been destroyed');
});

View File

@ -1,4 +1,5 @@
const should = require('should');
const {Member} = require('../../../core/server/models/member');
const {MemberStripeCustomer} = require('../../../core/server/models/member-stripe-customer');
const {StripeCustomerSubscription} = require('../../../core/server/models/stripe-customer-subscription');
@ -12,8 +13,11 @@ describe('StripeCustomerSubscription Model', function run() {
describe('customer', function () {
it('Is correctly mapped to the stripe customer', async function () {
const context = testUtils.context.admin;
const member = await Member.add({
email: 'test@test.member'
}, context);
await MemberStripeCustomer.add({
member_id: 'fake_member_id',
member_id: member.get('id'),
customer_id: 'fake_customer_id'
}, context);

View File

@ -21,7 +21,7 @@ const defaultSettings = require('../../../../core/server/data/schema/default-set
*/
describe('DB version integrity', function () {
// Only these variables should need updating
const currentSchemaHash = '134c9de4e59b31ec6b73f03638a01396';
const currentSchemaHash = '42a966364eb4b5851e807133374821da';
const currentFixturesHash = '3d942c46e8487c4aee1e9ac898ed29ca';
const currentSettingsHash = 'a4ac78d3810175428b4833645231d6d5';