| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- /**
- * This script backfills the account mapping for subscriptions that are active and backed by Stripe.
- *
- * The mapping joins a Stripe subscription ID to a subscription _id in BigQuery.
- *
- * This script has an assumption that it is being run in a clean slate condition, it will create some
- * duplicate mappings if run multiple times. The Analytics team will have the expectation
- * that this table may need to be deduplicated as it is an event sourcing record.
- *
- * Call it with `--commit` to actually register the mappings.
- * Call it with `--verbose` to see debug logs.
- * Call it with `--endDate=<EndDate>` to stop processing at a certain date, for example `--endDate=2024-01-01`
- */
- import logger from '@overleaf/logger'
- import minimist from 'minimist'
- import { batchedUpdate } from '@overleaf/mongo-utils/batchedUpdate.js'
- import { db } from '../../app/src/infrastructure/mongodb.mjs'
- import AccountMappingHelper from '../../app/src/Features/Analytics/AccountMappingHelper.mjs'
- import AnalyticsManager from '../../app/src/Features/Analytics/AnalyticsManager.mjs'
- import { gracefulShutdown } from '../../app/src/infrastructure/GracefulShutdown.mjs'
- import Validation from '../../app/src/infrastructure/Validation.mjs'
- import { scriptRunner } from '../lib/ScriptRunner.mjs'
- const { registerAccountMapping } = AnalyticsManager
- const paramsSchema = Validation.z.object({
- endDate: Validation.z.iso
- .date()
- .transform(v => new Date(v).toISOString())
- .optional(),
- commit: Validation.z.boolean().default(false).optional(),
- verbose: Validation.z.boolean().default(false).optional(),
- })
- let mapped = 0
- let subscriptionCount = 0
- const now = new Date().toISOString() // use the same timestamp for all mappings
- const seenSubscriptions = new Set()
- function registerMapping(subscription) {
- if (seenSubscriptions.has(subscription._id)) {
- logger.warn({ subscription }, 'duplicate subscription found, skipping')
- return
- }
- seenSubscriptions.add(subscription._id)
- subscriptionCount++
- const mapping = AccountMappingHelper.generateSubscriptionToStripeMapping(
- subscription._id,
- subscription.paymentProvider.subscriptionId,
- subscription.paymentProvider.service,
- now
- )
- logger.debug(
- {
- stripe: subscription.paymentProvider.subscriptionId,
- stripeService: subscription.paymentProvider.service,
- mapping,
- },
- `processing subscription ${subscription._id}`
- )
- if (commit) {
- registerAccountMapping(mapping)
- mapped++
- }
- }
- async function main(trackProgress) {
- const additionalBatchedUpdateOptions = {}
- if (endDate) {
- additionalBatchedUpdateOptions.BATCH_RANGE_END = endDate
- }
- await batchedUpdate(
- db.subscriptions,
- {
- 'paymentProvider.service': { $in: ['stripe-us', 'stripe-uk'] },
- },
- subscriptions => subscriptions.forEach(registerMapping),
- {
- _id: 1,
- 'paymentProvider.subscriptionId': 1,
- 'paymentProvider.service': 1,
- },
- {
- readPreference: 'secondaryPreferred',
- },
- {
- verboseLogging: verbose,
- ...additionalBatchedUpdateOptions,
- trackProgress,
- }
- )
- logger.debug({}, `${subscriptionCount} subscriptions processed`)
- if (commit) {
- logger.debug({}, `${mapped} mappings registered`)
- }
- }
- const { error, data } = paramsSchema.safeParse(
- minimist(process.argv.slice(2), {
- boolean: ['commit', 'verbose'],
- string: ['endDate'],
- })
- )
- if (error) {
- logger.error({ error }, 'error with parameters')
- await gracefulShutdown({
- close(done) {
- logger.info({}, 'shutting down')
- done()
- },
- })
- process.exit(1)
- }
- const { commit, endDate, verbose } = data
- logger.logger.level(verbose ? 'debug' : 'info')
- logger.info({ verbose, commit, endDate }, commit ? 'COMMITTING' : 'DRY RUN')
- await scriptRunner(main)
- await gracefulShutdown({
- close(done) {
- logger.info({}, 'shutting down')
- done()
- },
- })
- process.exit()
|