| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- import fs from 'node:fs'
- import minimist from 'minimist'
- import { parse } from 'csv'
- import Stream from 'node:stream/promises'
- import { ObjectId } from '../app/src/infrastructure/mongodb.mjs'
- import { Subscription } from '../app/src/models/Subscription.mjs'
- import { scriptRunner } from './lib/ScriptRunner.mjs'
- function usage() {
- console.log(
- 'Usage: node add_salesforce_data_to_subscriptions.mjs -f <filename> [options]'
- )
- console.log(
- 'Updates the subscriptions collection with external IDs for determining the Salesforce account that goes with the subscription. The file should be a CSV and have columns account_id, v1_id and subscription_id. The account_id column is the Salesforce account ID, the v1_id column is the V1 account ID, and the subscription_id column is the subscription ID.'
- )
- console.log('Options:')
- console.log(
- ' --commit, -c Commit changes to the database'
- )
- console.log(
- ' --emptyFieldValue <value> The value to treat as an empty field (default: NA)'
- )
- console.log(
- ' -f, --filename <filename> The path to the file to read data from'
- )
- console.log(' -h, --help Show this help message')
- console.log(' -v, --verbose Produces more detailed logs')
- process.exit(0)
- }
- const { commit, emptyFieldValue, filename, help, verbose } = minimist(
- process.argv.slice(2),
- {
- string: ['emptyFieldValue', 'filename'],
- boolean: ['commit', 'help', 'verbose'],
- alias: {
- commit: 'c',
- filename: 'f',
- help: 'h',
- verbose: 'v',
- },
- default: {
- commit: false,
- emptyFieldValue: 'NA',
- help: false,
- verbose: false,
- },
- }
- )
- const SUBSCRIPTION_ID_FIELD = 'subscription_id'
- const SALESFORCE_ID_FIELD = 'account_id'
- const V1_ID_FIELD = 'v1_id'
- if (help) {
- usage()
- process.exit(0)
- }
- if (!filename) {
- console.error('No filename provided')
- usage()
- process.exit(1)
- }
- const stats = {
- totalRows: 0,
- subscriptionIDMissing: 0,
- usedV1ID: 0,
- usedSalesforceID: 0,
- processedRows: 0,
- db: {
- errors: 0,
- matched: 0,
- updateAttempted: 0,
- updated: 0,
- },
- }
- function generateStats() {
- return `Stats:
- Total rows: ${stats.totalRows}
- Processed rows: ${stats.processedRows}
- Skipped (no subscription ID): ${stats.subscriptionIDMissing}
- Used V1 ID: ${stats.usedV1ID}
- Used Salesforce ID: ${stats.usedSalesforceID}${
- commit
- ? `
- Database operations:
- Errors: ${stats.db.errors}
- Matched: ${stats.db.matched}
- Updated: ${stats.db.updated}
- Update attempted: ${stats.db.updateAttempted}`
- : ''
- }`
- }
- function pickRelevantColumns(row) {
- const newRow = {
- salesforceId: row[SALESFORCE_ID_FIELD],
- }
- if (row[V1_ID_FIELD] && row[V1_ID_FIELD] !== emptyFieldValue) {
- newRow.v1Id = row[V1_ID_FIELD]
- }
- if (
- row[SUBSCRIPTION_ID_FIELD] &&
- row[SUBSCRIPTION_ID_FIELD] !== emptyFieldValue
- ) {
- newRow.subscriptionId = row[SUBSCRIPTION_ID_FIELD]
- }
- return newRow
- }
- async function processRows(rows) {
- for await (const row of rows) {
- const { v1Id, salesforceId, subscriptionId } = row
- const update = {}
- if (v1Id) {
- stats.usedV1ID++
- update.v1_id = v1Id
- } else {
- stats.usedSalesforceID++
- update.salesforce_id = salesforceId
- }
- // Useful for logging later.
- const updateString = Object.entries(update).flatMap(([k, v]) => `${k}=${v}`)
- if (commit) {
- try {
- const result = await Subscription.updateOne(
- { _id: new ObjectId(subscriptionId) },
- update,
- { upsert: false }
- )
- if (result.matchedCount) {
- stats.db.matched++
- }
- if (result.modifiedCount) {
- stats.db.updated++
- if (verbose) {
- console.log(
- `Updated subscription ${subscriptionId} to set ${updateString}`
- )
- }
- }
- } catch (error) {
- stats.db.errors++
- if (verbose) {
- console.error(
- `Error updating subscription ${subscriptionId}: ${error}`
- )
- }
- } finally {
- stats.db.updateAttempted++
- }
- } else if (verbose) {
- console.log(`Would set ${updateString} on subscription ${subscriptionId}`)
- }
- }
- }
- async function main(trackProgress) {
- await Stream.pipeline(
- fs.createReadStream(filename),
- parse({
- columns: true,
- cast: function (value, context) {
- if (context.column === V1_ID_FIELD && value !== emptyFieldValue) {
- return parseInt(value)
- }
- return value
- },
- on_record: function (record, context) {
- stats.totalRows++
- const row = pickRelevantColumns(record)
- // Cannot process records without a Subscription ID
- if (!row.subscriptionId) {
- if (verbose) {
- console.log(
- `No subscription id found for ${row.salesforceId}, skipping...`
- )
- }
- stats.subscriptionIDMissing++
- return null
- }
- stats.processedRows++
- return row
- },
- }),
- processRows
- )
- await trackProgress(generateStats())
- }
- if (!commit) {
- console.log('Dry run')
- } else {
- console.log('Committing changes to the database')
- }
- await scriptRunner(main)
- process.exit()
|