mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-26 04:13:30 +03:00
c0ca7b16f6
refs https://github.com/TryGhost/Toolbox/issues/515 Tiers are very frequently queried and we want to reduce the number of DB calls we're making. We can store the Tiers in-memory, using the existing in-memory repository patterns, but still persisting writes the the database. We also have to update our test helpers, because they were bypassing the repository for writes, but using it for reads resulting in an invalid cache
911 lines
34 KiB
JavaScript
911 lines
34 KiB
JavaScript
// Utility Packages
|
|
const _ = require('lodash');
|
|
const path = require('path');
|
|
const fs = require('fs-extra');
|
|
const uuid = require('uuid');
|
|
const ObjectId = require('bson-objectid').default;
|
|
const KnexMigrator = require('knex-migrator');
|
|
const {sequence} = require('@tryghost/promise');
|
|
const knexMigrator = new KnexMigrator();
|
|
|
|
// Ghost Internals
|
|
const models = require('../../core/server/models');
|
|
const {fixtureManager} = require('../../core/server/data/schema/fixtures');
|
|
const emailAnalyticsService = require('../../core/server/services/email-analytics');
|
|
const permissions = require('../../core/server/services/permissions');
|
|
const settingsService = require('../../core/server/services/settings/settings-service');
|
|
const labsService = require('../../core/shared/labs');
|
|
|
|
// Other Test Utilities
|
|
const context = require('./fixtures/context');
|
|
const DataGenerator = require('./fixtures/data-generator');
|
|
const filterData = require('./fixtures/filter-param');
|
|
|
|
let postsInserted = 0;
|
|
|
|
/** TEST FIXTURES **/
|
|
const fixtures = {
|
|
insertPosts: function insertPosts(posts) {
|
|
const tasks = posts.map(post => () => {
|
|
return models.Post.add(post, context.internal);
|
|
});
|
|
|
|
return sequence(tasks);
|
|
},
|
|
|
|
insertPostsAndTags: function insertPostsAndTags() {
|
|
return Promise.all(DataGenerator.forKnex.tags.map((tag) => {
|
|
return models.Tag.add(tag, context.internal);
|
|
}))
|
|
.then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.posts).map(post => () => {
|
|
let postTagRelations = _.filter(DataGenerator.forKnex.posts_tags, {post_id: post.id});
|
|
let postAuthorsRelations = _.filter(DataGenerator.forKnex.posts_authors, {post_id: post.id});
|
|
|
|
postTagRelations = _.map(postTagRelations, function (postTagRelation) {
|
|
return _.find(DataGenerator.forKnex.tags, {id: postTagRelation.tag_id});
|
|
});
|
|
|
|
postAuthorsRelations = _.map(postAuthorsRelations, function (postAuthorsRelation) {
|
|
return _.find(DataGenerator.forKnex.users, {id: postAuthorsRelation.author_id});
|
|
});
|
|
|
|
post.tags = postTagRelations;
|
|
post.authors = postAuthorsRelations;
|
|
|
|
return models.Post.add(post, context.internal);
|
|
}));
|
|
})
|
|
.then(function () {
|
|
return Promise.all(DataGenerator.forKnex.posts_meta.map((postMeta) => {
|
|
return models.PostsMeta.add(postMeta, context.internal);
|
|
}));
|
|
});
|
|
},
|
|
|
|
insertMultiAuthorPosts: function insertMultiAuthorPosts() {
|
|
let i;
|
|
let k = 0;
|
|
let posts = [];
|
|
|
|
// NOTE: this variable should become a parameter as test logic depends on it
|
|
const count = 10;
|
|
|
|
// insert users of different roles
|
|
return Promise.resolve(fixtures.createUsersWithRoles()).then(function () {
|
|
return Promise.all(DataGenerator.forKnex.tags.map((tag) => {
|
|
return models.Tag.add(tag, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
return Promise.all([
|
|
models.User.fetchAll(_.merge({columns: ['id']}, context.internal)),
|
|
models.Tag.fetchAll(_.merge({columns: ['id']}, context.internal))
|
|
]);
|
|
}).then(function (results) {
|
|
let users = results[0];
|
|
let tags = results[1];
|
|
|
|
tags = tags.toJSON();
|
|
|
|
users = users.toJSON();
|
|
users = _.map(users, 'id');
|
|
|
|
// Let's insert posts with random authors
|
|
for (i = 0; i < count; i += 1) {
|
|
const author = users[i % users.length];
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, null, null, [{id: author}]));
|
|
k = k + 1;
|
|
}
|
|
|
|
return Promise.all(posts.map((post, index) => {
|
|
posts[index].authors = [{id: posts[index].authors[0].id}];
|
|
posts[index].tags = [tags[Math.floor(Math.random() * (tags.length - 1))]];
|
|
return models.Post.add(posts[index], context.internal);
|
|
}));
|
|
});
|
|
},
|
|
|
|
insertEmailedPosts: function insertEmailedPosts({postCount = 2} = {}) {
|
|
const posts = [];
|
|
|
|
for (let i = 0; i < postCount; i++) {
|
|
posts.push(DataGenerator.forKnex.createGenericPost);
|
|
}
|
|
},
|
|
|
|
insertExtraPosts: function insertExtraPosts(max) {
|
|
let lang;
|
|
let status;
|
|
const posts = [];
|
|
let i;
|
|
let j;
|
|
let k = postsInserted;
|
|
|
|
max = max || 50;
|
|
|
|
for (i = 0; i < 2; i += 1) {
|
|
lang = i % 2 ? 'en' : 'fr';
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, null, lang));
|
|
k = k + 1;
|
|
|
|
for (j = 0; j < max; j += 1) {
|
|
status = j % 2 ? 'draft' : 'published';
|
|
posts.push(DataGenerator.forKnex.createGenericPost(k, status, lang));
|
|
k = k + 1;
|
|
}
|
|
}
|
|
|
|
// Keep track so we can run this function again safely
|
|
postsInserted = k;
|
|
|
|
return models.User.getOwnerUser(context.internal)
|
|
.then(function (ownerUser) {
|
|
return Promise.all(posts.map((post, index) => {
|
|
posts[index].authors = [ownerUser.toJSON()];
|
|
return models.Post.add(posts[index], context.internal);
|
|
}));
|
|
});
|
|
},
|
|
|
|
insertTags: function insertTags() {
|
|
return Promise.all(DataGenerator.forKnex.tags.map((tag) => {
|
|
return models.Tag.add(tag, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertExtraTags: function insertExtraTags(max) {
|
|
max = max || 50;
|
|
const tags = [];
|
|
let tagName;
|
|
let i;
|
|
|
|
for (i = 0; i < max; i += 1) {
|
|
tagName = uuid.v4().split('-')[0];
|
|
tags.push(DataGenerator.forKnex.createBasic({name: tagName, slug: tagName}));
|
|
}
|
|
|
|
return Promise.all(tags.map((tag, index) => {
|
|
return models.Tag.add(tags[index], context.internal);
|
|
}));
|
|
},
|
|
|
|
insertExtraPostsTags: function insertExtraPostsTags(max) {
|
|
max = max || 50;
|
|
|
|
return Promise.all([
|
|
models.Post.fetchAll(_.merge({columns: ['id'], withRelated: 'tags'}, context.internal)),
|
|
models.Tag.fetchAll(_.merge({columns: ['id', 'name']}, context.internal))
|
|
]).then(function (results) {
|
|
let posts = results[0].toJSON();
|
|
let tags = results[1].toJSON();
|
|
|
|
const injectionTagId = _.chain(tags)
|
|
.filter({name: 'injection'})
|
|
.map('id')
|
|
.value()[0];
|
|
|
|
if (max > posts.length) {
|
|
throw new Error('Trying to add more posts_tags than the number of posts.');
|
|
}
|
|
|
|
return Promise.all(posts.slice(0, max).map((post) => {
|
|
post.tags = post.tags ? post.tags : [];
|
|
|
|
return models.Post.edit({
|
|
tags: post.tags.concat([_.find(DataGenerator.Content.tags, {id: injectionTagId})])
|
|
}, _.merge({id: post.id}, context.internal));
|
|
}));
|
|
});
|
|
},
|
|
|
|
insertRoles: function insertRoles() {
|
|
return Promise.all(DataGenerator.forKnex.roles.map((role) => {
|
|
return models.Role.add(role, context.internal);
|
|
}));
|
|
},
|
|
|
|
initOwnerUser: function initOwnerUser() {
|
|
let user = DataGenerator.Content.users[0];
|
|
|
|
user = DataGenerator.forKnex.createBasic(user);
|
|
user = _.extend({}, user, {status: 'inactive'});
|
|
|
|
return Promise.all(DataGenerator.forKnex.roles.map((role) => {
|
|
return models.Role.add(role, context.internal);
|
|
})).then(function () {
|
|
const userRolesRelation = _.cloneDeep(DataGenerator.forKnex.roles_users[0]);
|
|
user.roles = _.filter(DataGenerator.forKnex.roles, {id: userRolesRelation.role_id});
|
|
return models.User.add(user, context.internal);
|
|
});
|
|
},
|
|
|
|
insertOwnerUser: function insertOwnerUser() {
|
|
const user = _.cloneDeep(DataGenerator.forKnex.users[0]);
|
|
user.roles = [DataGenerator.forKnex.roles[3]];
|
|
return models.User.add(user, context.internal);
|
|
},
|
|
|
|
overrideOwnerUser: function overrideOwnerUser(slug) {
|
|
return models.User.getOwnerUser(context.internal)
|
|
.then(function (ownerUser) {
|
|
const user = DataGenerator.forKnex.createUser(DataGenerator.Content.users[0]);
|
|
|
|
if (slug) {
|
|
user.slug = slug;
|
|
}
|
|
|
|
return models.User.edit(user, _.merge({id: ownerUser.id}, context.internal));
|
|
});
|
|
},
|
|
|
|
changeOwnerUserStatus: function changeOwnerUserStatus(options) {
|
|
return models.User.getOwnerUser(context.internal)
|
|
.then(function (user) {
|
|
return models.User.edit({status: options.status}, _.merge({id: user.id}, context.internal));
|
|
});
|
|
},
|
|
|
|
createUsersWithRoles: function createUsersWithRoles() {
|
|
return Promise.all(DataGenerator.forKnex.roles.map((role) => {
|
|
return models.Role.add(role, context.internal);
|
|
})).then(function () {
|
|
return Promise.all(_.cloneDeep(DataGenerator.forKnex.users).map((user) => {
|
|
let userRolesRelations = _.filter(DataGenerator.forKnex.roles_users, {user_id: user.id});
|
|
|
|
userRolesRelations = _.map(userRolesRelations, function (userRolesRelation) {
|
|
return _.find(DataGenerator.forKnex.roles, {id: userRolesRelation.role_id});
|
|
});
|
|
|
|
user.roles = userRolesRelations;
|
|
return models.User.add(user, context.internal);
|
|
}));
|
|
});
|
|
},
|
|
|
|
ensureUserForEachRole: async function ensureUserForEachRole() {
|
|
const usersWithoutOwner = _.cloneDeep(DataGenerator.forKnex.users.slice(1));
|
|
|
|
let roles = await models.Role.fetchAll();
|
|
roles = roles.toJSON();
|
|
|
|
return Promise.all(usersWithoutOwner.map((user) => {
|
|
let userRolesRelations = _.filter(DataGenerator.forKnex.roles_users, {user_id: user.id});
|
|
|
|
userRolesRelations = _.map(userRolesRelations, function (userRolesRelation) {
|
|
return _.find(roles, {name: userRolesRelation.role_name});
|
|
});
|
|
|
|
user.roles = userRolesRelations;
|
|
|
|
return models.User.add(user, context.internal);
|
|
}));
|
|
},
|
|
|
|
createInactiveUser() {
|
|
const user = DataGenerator.forKnex.createUser({
|
|
email: 'inactive@test.org',
|
|
slug: 'inactive',
|
|
status: 'inactive'
|
|
});
|
|
|
|
return models.User.add(user, context.internal);
|
|
},
|
|
|
|
createExtraUsers: function createExtraUsers() {
|
|
// grab 3 more users
|
|
let extraUsers = _.cloneDeep(DataGenerator.Content.users.slice(2, 6));
|
|
extraUsers = _.map(extraUsers, function (user) {
|
|
return DataGenerator.forKnex.createUser(_.extend({}, user, {
|
|
id: ObjectId().toHexString(),
|
|
email: 'a' + user.email,
|
|
slug: 'a' + user.slug
|
|
}));
|
|
});
|
|
|
|
const roles = {};
|
|
roles[extraUsers[0].id] = DataGenerator.Content.roles[0];
|
|
roles[extraUsers[1].id] = DataGenerator.Content.roles[1];
|
|
roles[extraUsers[2].id] = DataGenerator.Content.roles[2];
|
|
roles[extraUsers[3].id] = DataGenerator.Content.roles[4];
|
|
|
|
// @TODO: remove when overhauling test env
|
|
// tests need access to the extra created users (especially to the created id)
|
|
// replacement for admin2, editor2 etc
|
|
DataGenerator.Content.extraUsers = extraUsers;
|
|
|
|
return Promise.all(extraUsers.map((user) => {
|
|
user.roles = roles[user.id];
|
|
return models.User.add(user, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertOneUser: function insertOneUser(options) {
|
|
options = options || {};
|
|
|
|
return models.User.add({
|
|
name: options.name,
|
|
email: options.email,
|
|
slug: options.slug,
|
|
status: options.status
|
|
}, context.internal);
|
|
},
|
|
|
|
insertOne: function insertOne(modelName, tableName, fn, index) {
|
|
const obj = DataGenerator.forKnex[fn](DataGenerator.Content[tableName][index || 0]);
|
|
return models[modelName].add(obj, context.internal);
|
|
},
|
|
|
|
getImportFixturePath: function (filename) {
|
|
return path.resolve(__dirname + '/fixtures/import/' + filename);
|
|
},
|
|
|
|
getExportFixturePath: function (filename) {
|
|
const relativePath = '/fixtures/export/';
|
|
return path.resolve(__dirname + relativePath + filename + '.json');
|
|
},
|
|
|
|
loadExportFixture: function loadExportFixture(filename) {
|
|
const filePath = this.getExportFixturePath(filename);
|
|
|
|
return fs.readFile(filePath).then(function (fileContents) {
|
|
let data;
|
|
|
|
// Parse the json data
|
|
try {
|
|
data = JSON.parse(fileContents);
|
|
} catch (e) {
|
|
return new Error('Failed to parse the file');
|
|
}
|
|
|
|
return data;
|
|
});
|
|
},
|
|
|
|
permissionsFor: function permissionsFor(obj) {
|
|
let permsToInsert = _.cloneDeep(fixtureManager.findModelFixtures('Permission', {object_type: obj}).entries);
|
|
const permsRolesToInsert = fixtureManager.findPermissionRelationsForObject(obj).entries;
|
|
const actions = [];
|
|
const permissionsRoles = {};
|
|
|
|
const roles = {
|
|
Administrator: DataGenerator.Content.roles[0].id,
|
|
Editor: DataGenerator.Content.roles[1].id,
|
|
Author: DataGenerator.Content.roles[2].id,
|
|
Owner: DataGenerator.Content.roles[3].id,
|
|
Contributor: DataGenerator.Content.roles[4].id,
|
|
'Admin Integration': DataGenerator.Content.roles[5].id
|
|
};
|
|
|
|
// CASE: if empty db will throw SQLITE_MISUSE, hard to debug
|
|
if (_.isEmpty(permsToInsert)) {
|
|
return Promise.reject(new Error('no permission found:' + obj));
|
|
}
|
|
|
|
permsToInsert = _.map(permsToInsert, function (perms) {
|
|
perms.id = ObjectId().toHexString();
|
|
|
|
actions.push({type: perms.action_type, permissionId: perms.id});
|
|
return DataGenerator.forKnex.createBasic(perms);
|
|
});
|
|
|
|
_.each(permsRolesToInsert, function (perms, role) {
|
|
if (perms[obj]) {
|
|
if (perms[obj] === 'all') {
|
|
_.each(actions, function (action) {
|
|
if (!permissionsRoles[action.permissionId]) {
|
|
permissionsRoles[action.permissionId] = [];
|
|
}
|
|
|
|
permissionsRoles[action.permissionId].push(_.find(DataGenerator.Content.roles, {id: roles[role]}));
|
|
});
|
|
} else {
|
|
_.each(perms[obj], function (action) {
|
|
if (!permissionsRoles[_.find(actions, {type: action}).permissionId]) {
|
|
permissionsRoles[_.find(actions, {type: action}).permissionId] = [];
|
|
}
|
|
|
|
permissionsRoles[_.find(actions, {type: action}).permissionId].push(_.find(DataGenerator.Content.roles, {id: roles[role]}));
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
return Promise.all(permsToInsert.map((perm) => {
|
|
if (!_.isEmpty(permissionsRoles)) {
|
|
perm.roles = permissionsRoles[perm.id];
|
|
}
|
|
|
|
return models.Permission.add(perm, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertInvites: function insertInvites() {
|
|
return Promise.all(DataGenerator.forKnex.invites.map((invite) => {
|
|
return models.Invite.add(invite, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertWebhook: function (attributes) {
|
|
const webhook = DataGenerator.forKnex.createWebhook(
|
|
Object.assign(attributes, {
|
|
integration_id: DataGenerator.forKnex.integrations[0].id
|
|
})
|
|
);
|
|
|
|
return models.Webhook.add(webhook, context.internal);
|
|
},
|
|
|
|
insertWebhooks: function insertWebhooks() {
|
|
return Promise.all(DataGenerator.forKnex.webhooks.map((webhook) => {
|
|
return models.Webhook.add(webhook, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertIntegrations: function insertIntegrations() {
|
|
return Promise.all(DataGenerator.forKnex.integrations.map((integration) => {
|
|
return models.Integration.add(integration, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertApiKeys: function insertApiKeys() {
|
|
return Promise.all(DataGenerator.forKnex.api_keys.map((api_key) => {
|
|
return models.ApiKey.add(api_key, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertLinks: function insertLinks() {
|
|
return Promise.all(DataGenerator.forKnex.links.map((link) => {
|
|
return models.Redirect.add(link, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertMentions: function insertMentions() {
|
|
return Promise.all(DataGenerator.forKnex.mentions.map((mention) => {
|
|
return models.Mention.add(mention, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertEmails: function insertEmails() {
|
|
return Promise.all(DataGenerator.forKnex.emails.map((email) => {
|
|
return models.Email.add(email, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertArchivedTiers: function insertArchivedTiers() {
|
|
let archivedProduct = DataGenerator.forKnex.createProduct({
|
|
active: false
|
|
});
|
|
|
|
return models.Product.add(archivedProduct, context.internal);
|
|
},
|
|
|
|
insertProducts: async function insertProducts() {
|
|
let coreProductFixtures = fixtureManager.findModelFixtures('Product').entries;
|
|
await Promise.all(coreProductFixtures.map(async (product) => {
|
|
const found = await models.Product.findOne(product, context.internal);
|
|
if (!found) {
|
|
await models.Product.add(product, context.internal);
|
|
}
|
|
}));
|
|
|
|
const product = await models.Product.findOne({type: 'paid'}, context.internal);
|
|
|
|
await sequence(_.cloneDeep(DataGenerator.forKnex.stripe_products).map(stripeProduct => () => {
|
|
stripeProduct.product_id = product.id;
|
|
return models.StripeProduct.add(stripeProduct, context.internal);
|
|
}));
|
|
|
|
await sequence(_.cloneDeep(DataGenerator.forKnex.stripe_prices).map(stripePrice => () => {
|
|
return models.StripePrice.add(stripePrice, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertMembersAndLabelsAndProducts: function insertMembersAndLabelsAndProducts(newsletters = false) {
|
|
return Promise.all(DataGenerator.forKnex.labels.map((label) => {
|
|
return models.Label.add(label, context.internal);
|
|
})).then(function () {
|
|
let coreProductFixtures = fixtureManager.findModelFixtures('Product').entries;
|
|
return Promise.all(coreProductFixtures.map(async (product) => {
|
|
const found = await models.Product.findOne(product, context.internal);
|
|
if (!found) {
|
|
await models.Product.add(product, context.internal);
|
|
}
|
|
}));
|
|
}).then(async function () {
|
|
let testProductFixtures = DataGenerator.forKnex.products;
|
|
for (const productFixture of testProductFixtures) {
|
|
if (productFixture.id) { // Not currently used - this is used to add new text fixtures, e.g. a Bronze/Silver/Gold Tier
|
|
await models.Product.add(productFixture, context.internal);
|
|
} else { // Used to update the core fixtures
|
|
// If it doesn't exist we have invalid fixtures, so require: true to ensure we throw
|
|
const existing = await models.Product.findOne({slug: productFixture.slug}, {...context.internal, require: true});
|
|
await models.Product.edit(productFixture, {...context.internal, id: existing.id});
|
|
}
|
|
}
|
|
}).then(function () {
|
|
return models.Product.findOne({type: 'paid'}, context.internal);
|
|
}).then(function (product) {
|
|
return Promise.all([
|
|
sequence(_.cloneDeep(DataGenerator.forKnex.stripe_products).map(stripeProduct => () => {
|
|
stripeProduct.product_id = product.id;
|
|
return models.StripeProduct.add(stripeProduct, context.internal);
|
|
})),
|
|
sequence(_.cloneDeep(DataGenerator.forKnex.members).map(member => () => {
|
|
let memberLabelRelations = _.filter(DataGenerator.forKnex.members_labels, {member_id: member.id});
|
|
|
|
memberLabelRelations = _.map(memberLabelRelations, function (memberLabelRelation) {
|
|
return _.find(DataGenerator.forKnex.labels, {id: memberLabelRelation.label_id});
|
|
});
|
|
|
|
member.labels = memberLabelRelations;
|
|
|
|
if (newsletters) {
|
|
let memberNewsletterRelations = _.filter(DataGenerator.forKnex.members_newsletters, {member_id: member.id});
|
|
memberNewsletterRelations = _.map(memberNewsletterRelations, function (memberNewsletterRelation) {
|
|
return _.find(DataGenerator.forKnex.newsletters, {id: memberNewsletterRelation.newsletter_id});
|
|
});
|
|
|
|
member.newsletters = memberNewsletterRelations;
|
|
}
|
|
|
|
// TODO: replace with full member/product associations
|
|
if (member.email === 'with-product@test.com') {
|
|
member.products = [{id: product.id}];
|
|
}
|
|
|
|
return models.Member.add(member, context.internal);
|
|
}))
|
|
]);
|
|
}).then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.members_stripe_customers).map(customer => () => {
|
|
return models.MemberStripeCustomer.add(customer, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.stripe_prices).map(stripePrice => () => {
|
|
return models.StripePrice.add(stripePrice, context.internal);
|
|
}));
|
|
}).then(async function () {
|
|
// Add monthly/yearly prices to default product for testing
|
|
const defaultProduct = await models.Product.findOne({slug: 'default-product'}, context.internal);
|
|
return models.Product.edit({
|
|
...defaultProduct.toJSON(),
|
|
monthly_price_id: DataGenerator.forKnex.stripe_prices[1].id,
|
|
yearly_price_id: DataGenerator.forKnex.stripe_prices[2].id
|
|
}, _.merge({id: defaultProduct.id}, context.internal));
|
|
}).then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.stripe_customer_subscriptions).map(subscription => () => {
|
|
return models.StripeCustomerSubscription.add(subscription, context.internal);
|
|
}));
|
|
}).then(async function () {
|
|
const members = (await models.Member.findAll({
|
|
withRelated: [
|
|
'labels',
|
|
'stripeSubscriptions',
|
|
'stripeSubscriptions.customer',
|
|
'stripeSubscriptions.stripePrice',
|
|
'stripeSubscriptions.stripePrice.stripeProduct',
|
|
'products',
|
|
'offerRedemptions'
|
|
]
|
|
})).toJSON();
|
|
|
|
for (const member of members) {
|
|
for (const subscription of member.subscriptions) {
|
|
const product = subscription.price.product.product_id;
|
|
await models.Member.edit({products: member.products.concat({
|
|
id: product
|
|
})}, {id: member.id});
|
|
}
|
|
}
|
|
}).then(async function () {
|
|
for (const event of DataGenerator.forKnex.members_paid_subscription_events) {
|
|
await models.MemberPaidSubscriptionEvent.add(event);
|
|
}
|
|
}).then(async function () {
|
|
for (const event of DataGenerator.forKnex.members_created_events) {
|
|
await models.MemberCreatedEvent.add(event);
|
|
}
|
|
}).then(async function () {
|
|
for (const event of DataGenerator.forKnex.members_subscription_created_events) {
|
|
await models.SubscriptionCreatedEvent.add(event);
|
|
}
|
|
});
|
|
},
|
|
|
|
insertEmailsAndRecipients: function insertEmailsAndRecipients(withFailed = false) {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.emails).map(email => () => {
|
|
return models.Email.add(email, context.internal);
|
|
})).then(function () {
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.email_batches).map(emailBatch => () => {
|
|
return models.EmailBatch.add(emailBatch, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
const email_recipients = withFailed ?
|
|
DataGenerator.forKnex.email_recipients
|
|
: DataGenerator.forKnex.email_recipients.filter(r => r.failed_at === null);
|
|
|
|
return sequence(_.cloneDeep(email_recipients).map(emailRecipient => () => {
|
|
return models.EmailRecipient.add(emailRecipient, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
if (!withFailed) {
|
|
return;
|
|
}
|
|
|
|
return sequence(_.cloneDeep(DataGenerator.forKnex.email_recipient_failures).map(failure => () => {
|
|
return models.EmailRecipientFailure.add(failure, context.internal);
|
|
}));
|
|
}).then(function () {
|
|
const toAggregate = {
|
|
emailIds: DataGenerator.forKnex.emails.map(email => email.id),
|
|
memberIds: DataGenerator.forKnex.members.map(member => member.id)
|
|
};
|
|
|
|
return emailAnalyticsService.service.aggregateStats(toAggregate);
|
|
});
|
|
},
|
|
|
|
insertNewsletters: async function insertNewsletters() {
|
|
return Promise.all(DataGenerator.forKnex.newsletters.map((newsletter) => {
|
|
return models.Newsletter.add(newsletter, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertComments: async function insertComments() {
|
|
// First create the parents (can happen in parallel), because the children depend on those
|
|
const parents = DataGenerator.forKnex.comments.filter(c => !c.parent_id);
|
|
const children = DataGenerator.forKnex.comments.filter(c => !!c.parent_id);
|
|
|
|
await Promise.all(parents.map((comment) => {
|
|
return models.Comment.add(comment, context.internal);
|
|
}));
|
|
|
|
await Promise.all(children.map((comment) => {
|
|
return models.Comment.add(comment, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertRedirects: async function insertClicks() {
|
|
await Promise.all(DataGenerator.forKnex.redirects.map((click) => {
|
|
return models.Redirect.add(click, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertClicks: async function insertClicks() {
|
|
await Promise.all(DataGenerator.forKnex.members_click_events.map((click) => {
|
|
return models.MemberClickEvent.add(click, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertFeedback: async function insertFeedback() {
|
|
await Promise.all(DataGenerator.forKnex.members_feedback.map((feedback) => {
|
|
return models.MemberFeedback.add(feedback, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertSnippets: function insertSnippets() {
|
|
return Promise.all(DataGenerator.forKnex.snippets.map((snippet) => {
|
|
return models.Snippet.add(snippet, context.internal);
|
|
}));
|
|
},
|
|
|
|
insertCustomThemeSettings: function insertCustomThemeSettings() {
|
|
return Promise.all(DataGenerator.forKnex.custom_theme_settings.map((setting) => {
|
|
return models.CustomThemeSetting.add(setting, context.internal);
|
|
}));
|
|
},
|
|
|
|
async enableAllLabsFeatures() {
|
|
const labsValue = Object.fromEntries(labsService.WRITABLE_KEYS_ALLOWLIST.map(key => [key, true]));
|
|
const labsSetting = DataGenerator.forKnex.createSetting({
|
|
key: 'labs',
|
|
group: 'labs',
|
|
type: 'object',
|
|
value: JSON.stringify(labsValue)
|
|
});
|
|
|
|
const existingLabsSetting = await models.Settings.findOne({key: 'labs'});
|
|
|
|
if (existingLabsSetting) {
|
|
delete labsSetting.id;
|
|
await models.Settings.edit(labsSetting);
|
|
} else {
|
|
await models.Settings.add(labsSetting);
|
|
}
|
|
|
|
await settingsService.init();
|
|
}
|
|
};
|
|
|
|
const toDoList = {
|
|
roles: function insertRoles() {
|
|
return fixtures.insertRoles();
|
|
},
|
|
tag: function insertTag() {
|
|
return fixtures.insertOne('Tag', 'tags', 'createTag');
|
|
},
|
|
member: function insertMember() {
|
|
return fixtures.insertOne('Member', 'members', 'createMember');
|
|
},
|
|
members: function insertMembersAndLabelsAndProducts() {
|
|
return fixtures.insertMembersAndLabelsAndProducts(false);
|
|
},
|
|
products: function insertProducts() {
|
|
return fixtures.insertProducts();
|
|
},
|
|
newsletters: function insertNewsletters() {
|
|
return fixtures.insertNewsletters();
|
|
},
|
|
'members:newsletters': function insertMembersAndLabelsAndProductsAndNewsletters() {
|
|
return fixtures.insertMembersAndLabelsAndProducts(true);
|
|
},
|
|
'members:emails': function insertEmailsAndRecipients() {
|
|
return fixtures.insertEmailsAndRecipients(false);
|
|
},
|
|
'members:emails:failed': function insertEmailsAndRecipients() {
|
|
return fixtures.insertEmailsAndRecipients(true);
|
|
},
|
|
posts: function insertPostsAndTags() {
|
|
return fixtures.insertPostsAndTags();
|
|
},
|
|
'posts:mu': function insertMultiAuthorPosts() {
|
|
return fixtures.insertMultiAuthorPosts();
|
|
},
|
|
tags: function insertTags() {
|
|
return fixtures.insertTags();
|
|
},
|
|
'tags:extra': function insertExtraTags() {
|
|
return fixtures.insertExtraTags();
|
|
},
|
|
settings: function populateSettings() {
|
|
return settingsService.init();
|
|
},
|
|
'users:roles': function createUsersWithRoles() {
|
|
return fixtures.createUsersWithRoles();
|
|
},
|
|
users: function ensureUserForEachRole() {
|
|
return fixtures.ensureUserForEachRole();
|
|
},
|
|
'user:inactive': function createInactiveUser() {
|
|
return fixtures.createInactiveUser();
|
|
},
|
|
'users:extra': function createExtraUsers() {
|
|
return fixtures.createExtraUsers();
|
|
},
|
|
owner: function insertOwnerUser() {
|
|
return fixtures.insertOwnerUser();
|
|
},
|
|
'owner:pre': function initOwnerUser() {
|
|
return fixtures.initOwnerUser();
|
|
},
|
|
'owner:post': function overrideOwnerUser() {
|
|
return fixtures.overrideOwnerUser();
|
|
},
|
|
'perms:init': function initPermissions() {
|
|
return permissions.init();
|
|
},
|
|
perms: function permissionsFor(obj) {
|
|
return fixtures.permissionsFor(obj);
|
|
},
|
|
filter: function createFilterParamFixtures() {
|
|
return filterData(DataGenerator);
|
|
},
|
|
invites: function insertInvites() {
|
|
return fixtures.insertInvites();
|
|
},
|
|
webhooks: function insertWebhooks() {
|
|
return fixtures.insertWebhooks();
|
|
},
|
|
integrations: function insertIntegrations() {
|
|
return fixtures.insertIntegrations();
|
|
},
|
|
api_keys: function insertApiKeys() {
|
|
return fixtures.insertApiKeys();
|
|
},
|
|
emails: function insertEmails() {
|
|
return fixtures.insertEmails();
|
|
},
|
|
snippets: function insertSnippets() {
|
|
return fixtures.insertSnippets();
|
|
},
|
|
'labs:enabled': function enableAllLabsFeatures() {
|
|
return fixtures.enableAllLabsFeatures();
|
|
},
|
|
custom_theme_settings: function insertCustomThemeSettings() {
|
|
return fixtures.insertCustomThemeSettings();
|
|
},
|
|
'tiers:archived': function insertArchivedTiers() {
|
|
return fixtures.insertArchivedTiers();
|
|
},
|
|
comments: function insertComments() {
|
|
return fixtures.insertComments();
|
|
},
|
|
redirects: function insertRedirects() {
|
|
return fixtures.insertRedirects();
|
|
},
|
|
clicks: function insertClicks() {
|
|
return fixtures.insertClicks();
|
|
},
|
|
feedback: function insertFeedback() {
|
|
return fixtures.insertFeedback();
|
|
},
|
|
links: function insertLinks() {
|
|
return fixtures.insertLinks();
|
|
},
|
|
mentions: function insertMentions() {
|
|
return fixtures.insertMentions();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ## getFixtureOps
|
|
*
|
|
* Takes the arguments from a setup function and turns them into an array of promises to fullfil
|
|
*
|
|
* This is effectively a list of instructions with regard to which fixtures should be setup for this test.
|
|
* * `default` - a special option which will cause the full suite of normal fixtures to be initialized
|
|
* * `perms:init` - initialize the permissions object after having added permissions
|
|
* * `perms:obj` - initialize permissions for a particular object type
|
|
* * `users:roles` - create a full suite of users, one per role
|
|
* @param {Object} toDos
|
|
*/
|
|
const getFixtureOps = (toDos) => {
|
|
// default = default fixtures, if it isn't present, init with tables only
|
|
const tablesOnly = !toDos.default;
|
|
|
|
const fixtureOps = [];
|
|
|
|
// Database initialization
|
|
if (toDos.init || toDos.default) {
|
|
fixtureOps.push(function initDB() {
|
|
// skip adding all fixtures!
|
|
if (tablesOnly) {
|
|
return knexMigrator.init({skip: 2});
|
|
}
|
|
|
|
return knexMigrator.init();
|
|
});
|
|
|
|
delete toDos.default;
|
|
delete toDos.init;
|
|
}
|
|
|
|
fixtureOps.push(toDoList['labs:enabled']);
|
|
|
|
// Go through our list of things to do, and add them to an array
|
|
_.each(toDos, function (value, toDo) {
|
|
let tmp;
|
|
|
|
if ((toDo !== 'perms:init' && toDo.indexOf('perms:') !== -1)) {
|
|
tmp = toDo.split(':');
|
|
|
|
fixtureOps.push(function addCustomFixture() {
|
|
return toDoList[tmp[0]](tmp[1]);
|
|
});
|
|
} else {
|
|
if (!toDoList[toDo]) {
|
|
throw new Error(`The fixture ${toDo} does not exist.`);
|
|
}
|
|
|
|
fixtureOps.push(toDoList[toDo]);
|
|
}
|
|
});
|
|
|
|
fixtureOps.push(() => {
|
|
return require('../../core/server/services/tiers').repository?.init();
|
|
});
|
|
|
|
return fixtureOps;
|
|
};
|
|
|
|
const getCurrentOwnerUser = async () => {
|
|
return await models.User.getOwnerUser(context.internal);
|
|
};
|
|
|
|
module.exports = {
|
|
fixtures,
|
|
getFixtureOps,
|
|
DataGenerator,
|
|
getCurrentOwnerUser
|
|
};
|