Files
overleaf-cep/services/web/scripts/analytics/backfill_stripe_to_subscription_mapping.mjs
Liangjun Song 3adf77994b Merge pull request #28071 from overleaf/ls-add-logic-to-sync-ol-to-stripe-subscription-mapping
Add logic to sync INT_account_mapping and backfill script

GitOrigin-RevId: 585a3945e6f238ca345c639218ef8bd3d588a31f
2025-08-28 08:06:37 +00:00

129 lines
3.7 KiB
JavaScript

/**
* 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.js'
import AccountMappingHelper from '../../app/src/Features/Analytics/AccountMappingHelper.js'
import { registerAccountMapping } from '../../app/src/Features/Analytics/AnalyticsManager.js'
import { triggerGracefulShutdown } from '../../app/src/infrastructure/GracefulShutdown.js'
import Validation from '../../app/src/infrastructure/Validation.js'
import { scriptRunner } from '../lib/ScriptRunner.mjs'
const paramsSchema = Validation.Joi.object({
endDate: Validation.Joi.string().isoDate(),
commit: Validation.Joi.boolean().default(false),
verbose: Validation.Joi.boolean().default(false),
}).unknown(true)
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,
value: { commit, endDate, verbose },
} = paramsSchema.validate(
minimist(process.argv.slice(2), {
boolean: ['commit', 'verbose'],
string: ['endDate'],
})
)
logger.logger.level(verbose ? 'debug' : 'info')
if (error) {
logger.error({ error }, 'error with parameters')
triggerGracefulShutdown({
close(done) {
logger.info({}, 'shutting down')
done(1)
},
})
} else {
logger.info({ verbose, commit, endDate }, commit ? 'COMMITTING' : 'DRY RUN')
await scriptRunner(main)
triggerGracefulShutdown({
close(done) {
logger.info({}, 'shutting down')
done()
},
})
}