Ghost/ghost/data-generator/lib/data-generator.js

248 lines
9.7 KiB
JavaScript
Raw Normal View History

const {
PostsImporter,
NewslettersImporter,
UsersImporter,
PostsAuthorsImporter,
TagsImporter,
PostsTagsImporter,
ProductsImporter,
MembersImporter,
BenefitsImporter,
ProductsBenefitsImporter,
MembersProductsImporter,
PostsProductsImporter,
MembersNewslettersImporter,
MembersCreatedEventsImporter,
MembersLoginEventsImporter,
MembersStatusEventsImporter,
StripeProductsImporter,
StripePricesImporter,
SubscriptionsImporter,
MembersStripeCustomersImporter,
MembersStripeCustomersSubscriptionsImporter,
MembersPaidSubscriptionEventsImporter,
MembersSubscriptionCreatedEventsImporter,
MembersSubscribeEventsImporter
} = require('./tables');
const {faker} = require('@faker-js/faker');
/**
* @typedef {Object} DataGeneratorOptions
* @property {boolean} useBaseData
* @property {import('knex/types').Knex} knex
* @property {Object} schema
* @property {Object} logger
* @property {Object} modelQuantities
*/
const defaultQuantities = {
members: () => faker.datatype.number({
min: 7000,
max: 8000
}),
membersLoginEvents: 100,
posts: () => faker.datatype.number({
min: 80,
max: 120
})
};
class DataGenerator {
/**
*
* @param {DataGeneratorOptions} options
*/
constructor({
useBaseData = false,
knex,
schema,
logger,
modelQuantities = {}
}) {
this.useBaseData = useBaseData;
this.knex = knex;
this.schema = schema;
this.logger = logger;
this.modelQuantities = Object.assign({}, defaultQuantities, modelQuantities);
}
async importData() {
const transaction = await this.knex.transaction();
const usersImporter = new UsersImporter(transaction);
const users = await usersImporter.import({amount: 8});
let newsletters;
let posts;
let tags;
let products;
let stripeProducts;
let stripePrices;
// Use an existant set of data for a more realisitic looking site
if (this.useBaseData) {
// Must have at least 2 in base data set
newsletters = await transaction.select('id').from('newsletters');
const postsImporter = new PostsImporter(transaction, {
newsletters
});
posts = await transaction.select('id').from('posts');
postsImporter.addNewsletters({posts});
posts = await transaction.select('id', 'newsletter_id').from('posts');
tags = await transaction.select('id').from('tags');
products = await transaction.select('id', 'name', 'monthly_price', 'yearly_price').from('products');
stripeProducts = await transaction.select('id', 'product_id', 'stripe_product_id').from('stripe_products');
stripePrices = await transaction.select('id', 'stripe_price_id', 'interval', 'stripe_product_id', 'currency', 'amount', 'nickname');
} else {
const newslettersImporter = new NewslettersImporter(transaction);
// First newsletter is free, second is paid
newsletters = await newslettersImporter.import({amount: 2});
const postsImporter = new PostsImporter(transaction, {
newsletters
});
posts = await postsImporter.import({
amount: this.modelQuantities.posts,
rows: ['newsletter_id']
});
const tagsImporter = new TagsImporter(transaction, {
users
});
tags = await tagsImporter.import({amount: faker.datatype.number({
min: 16,
max: 24
})});
const productsImporter = new ProductsImporter(transaction);
products = await productsImporter.import({amount: 4, rows: ['name', 'monthly_price', 'yearly_price']});
const stripeProductsImporter = new StripeProductsImporter(transaction);
stripeProducts = await stripeProductsImporter.importForEach(products, {
amount: 1,
rows: ['product_id', 'stripe_product_id']
});
const stripePricesImporter = new StripePricesImporter(transaction, {products});
stripePrices = await stripePricesImporter.importForEach(stripeProducts, {
amount: 2,
rows: ['stripe_price_id', 'interval', 'stripe_product_id', 'currency', 'amount', 'nickname']
});
await productsImporter.addStripePrices({
products,
stripeProducts,
stripePrices
});
const benefitsImporter = new BenefitsImporter(transaction);
const benefits = await benefitsImporter.import({amount: 5});
const productsBenefitsImporter = new ProductsBenefitsImporter(transaction, {benefits});
// Up to 5 benefits for each product
await productsBenefitsImporter.importForEach(products, {amount: 5});
}
const postsTagsImporter = new PostsTagsImporter(transaction, {
tags
});
await postsTagsImporter.importForEach(posts, {
amount: () => faker.datatype.number({
min: 0,
max: 3
})
});
const membersImporter = new MembersImporter(transaction);
const members = await membersImporter.import({amount: this.modelQuantities.members, rows: ['status', 'created_at', 'name', 'email']});
const postsAuthorsImporter = new PostsAuthorsImporter(transaction, {
users
});
await postsAuthorsImporter.importForEach(posts, {amount: 1});
// TODO: Use subscriptions to generate members_products table?
const membersProductsImporter = new MembersProductsImporter(transaction, {products: products.slice(1)});
const membersProducts = await membersProductsImporter.importForEach(members.filter(member => member.status !== 'free'), {
amount: 1,
rows: ['product_id', 'member_id']
});
const membersFreeProductsImporter = new MembersProductsImporter(transaction, {products: [products[0]]});
await membersFreeProductsImporter.importForEach(members.filter(member => member.status === 'free'), {
amount: 1,
rows: ['product_id', 'member_id']
});
const postsProductsImporter = new PostsProductsImporter(transaction, {products: products.slice(1)});
// Paid newsletters
await postsProductsImporter.importForEach(posts.filter(post => newsletters.findIndex(newsletter => newsletter.id === post.newsletter_id) === 1), {
// Each post is available on all 3 premium products
amount: 3
});
const membersCreatedEventsImporter = new MembersCreatedEventsImporter(transaction);
await membersCreatedEventsImporter.importForEach(members, {amount: 1});
const membersLoginEventsImporter = new MembersLoginEventsImporter(transaction);
// Will create roughly 1 login event for every 3 days, up to a maximum of 100.
await membersLoginEventsImporter.importForEach(members, {amount: this.modelQuantities.membersLoginEvents});
const membersStatusEventsImporter = new MembersStatusEventsImporter(transaction);
// Up to 2 events per member - 1 from null -> free, 1 from free -> {paid, comped}
await membersStatusEventsImporter.importForEach(members, {amount: 2});
const subscriptionsImporter = new SubscriptionsImporter(transaction, {members, stripeProducts, stripePrices});
const subscriptions = await subscriptionsImporter.importForEach(membersProducts, {
amount: 1,
rows: ['cadence', 'tier_id', 'expires_at', 'created_at', 'member_id', 'currency']
});
const membersStripeCustomersImporter = new MembersStripeCustomersImporter(transaction);
const membersStripeCustomers = await membersStripeCustomersImporter.importForEach(members, {
amount: 1,
rows: ['customer_id', 'member_id']
});
const membersStripeCustomersSubscriptionsImporter = new MembersStripeCustomersSubscriptionsImporter(transaction, {
membersStripeCustomers,
products,
stripeProducts,
stripePrices
});
const membersStripeCustomersSubscriptions = await membersStripeCustomersSubscriptionsImporter.importForEach(subscriptions, {
amount: 1,
rows: ['mrr', 'plan_id', 'subscription_id']
});
const membersSubscribeEventsImporter = new MembersSubscribeEventsImporter(transaction, {newsletters, subscriptions});
const membersSubscribeEvents = await membersSubscribeEventsImporter.importForEach(members, {
amount: 2,
rows: ['member_id', 'newsletter_id']
});
const membersNewslettersImporter = new MembersNewslettersImporter(transaction);
await membersNewslettersImporter.importForEach(membersSubscribeEvents, {amount: 1});
const membersPaidSubscriptionEventsImporter = new MembersPaidSubscriptionEventsImporter(transaction, {
membersStripeCustomersSubscriptions
});
await membersPaidSubscriptionEventsImporter.importForEach(subscriptions, {amount: 1});
const membersSubscriptionCreatedEventsImporter = new MembersSubscriptionCreatedEventsImporter(transaction, {subscriptions});
await membersSubscriptionCreatedEventsImporter.importForEach(membersStripeCustomersSubscriptions, {amount: 1});
// TODO: Emails! (relies on posts & newsletters)
// TODO: Email clicks - redirect, members_click_events (relies on emails)
// TODO: Feedback - members_feedback (relies on members and posts)
await transaction.commit();
}
}
module.exports = DataGenerator;