Skip to content
This repository was archived by the owner on Mar 27, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
c2854e7
Add multi-identifier support for preference upload
JonnavithulaGirish Aug 7, 2025
6750f10
Add multi-identifier support for preference upload
JonnavithulaGirish Aug 7, 2025
c190974
rever lock changes
JonnavithulaGirish Aug 7, 2025
720d92c
clean up
JonnavithulaGirish Aug 7, 2025
7543ac4
add costco specific logic
JonnavithulaGirish Aug 7, 2025
2549be4
add columnsToIgnore
JonnavithulaGirish Aug 12, 2025
69bfa8e
remove transcendID prompt
JonnavithulaGirish Aug 13, 2025
9544327
try increasing concurrencey
JonnavithulaGirish Aug 14, 2025
1a5c785
revert to bluebird
JonnavithulaGirish Aug 14, 2025
2de8843
Rewrites how receipts are stored to file
michaelfarrell76 Aug 16, 2025
a57fbd1
Parrallel:
michaelfarrell76 Aug 16, 2025
fba8809
basically working
michaelfarrell76 Aug 16, 2025
b4a6a52
Working logs
michaelfarrell76 Aug 16, 2025
c0d20f1
mostly wokring
michaelfarrell76 Aug 16, 2025
41c509c
Working in parallel
michaelfarrell76 Aug 16, 2025
e8f7d2c
Working
michaelfarrell76 Aug 16, 2025
9ff89fd
better concurrency
michaelfarrell76 Aug 16, 2025
6379b4b
etter concurrency
michaelfarrell76 Aug 16, 2025
1df22f3
perfectly balanced
michaelfarrell76 Aug 16, 2025
fe40f17
working
michaelfarrell76 Aug 16, 2025
1f42f36
Mostly working
michaelfarrell76 Aug 16, 2025
20c8cdf
Merges
michaelfarrell76 Aug 16, 2025
497f2a5
Uses new function
michaelfarrell76 Aug 16, 2025
855df24
Merges
michaelfarrell76 Aug 16, 2025
1344aa9
rm
michaelfarrell76 Aug 16, 2025
ef0d0f9
adds docs to codecs
michaelfarrell76 Aug 16, 2025
41ceb8c
gitignore and bb
michaelfarrell76 Aug 16, 2025
84342d6
Refactors
michaelfarrell76 Aug 17, 2025
875fdb3
Merges
michaelfarrell76 Aug 17, 2025
ca37307
checkpoint
michaelfarrell76 Aug 17, 2025
4e11953
Exports
michaelfarrell76 Aug 17, 2025
451b1cc
Working
michaelfarrell76 Aug 17, 2025
5910ce9
refactor of pooling
michaelfarrell76 Aug 17, 2025
c2dbe77
More updates
michaelfarrell76 Aug 17, 2025
735bf8c
ud
michaelfarrell76 Aug 17, 2025
6ab7fb1
Mostly fixed
michaelfarrell76 Aug 17, 2025
d120efe
Mostly fixed
michaelfarrell76 Aug 17, 2025
e00075e
Fixes transform
michaelfarrell76 Aug 17, 2025
1fef12c
cleaned
michaelfarrell76 Aug 17, 2025
e920f7d
just nearly fully working
michaelfarrell76 Aug 17, 2025
54fdf05
Adds new commands
michaelfarrell76 Aug 17, 2025
80243c1
filter slashes
michaelfarrell76 Aug 17, 2025
0e239ba
ipdats
michaelfarrell76 Aug 17, 2025
5123089
MErges
michaelfarrell76 Aug 17, 2025
8296a3a
rm node corepack
michaelfarrell76 Aug 17, 2025
f6caaae
readme
michaelfarrell76 Aug 17, 2025
e6274f2
Delete src/lib/pooling/replayTail.ts
michaelfarrell76 Aug 17, 2025
5232589
MErges
michaelfarrell76 Aug 18, 2025
9671e92
TSC
michaelfarrell76 Aug 18, 2025
1ad7512
CSV split concurrency
michaelfarrell76 Aug 18, 2025
5bf5ac5
checkpoint on csv parse
michaelfarrell76 Aug 18, 2025
9532123
RefatoreD
michaelfarrell76 Aug 18, 2025
88ea848
Merges
michaelfarrell76 Aug 18, 2025
4de6597
Fixes
michaelfarrell76 Aug 18, 2025
50acd53
MergeS
michaelfarrell76 Aug 18, 2025
00c019e
MergeS
michaelfarrell76 Aug 19, 2025
0be1c91
merges main
michaelfarrell76 Aug 19, 2025
702709e
rm
michaelfarrell76 Aug 19, 2025
3563168
rm
michaelfarrell76 Aug 19, 2025
3a10ae2
Merges
michaelfarrell76 Aug 19, 2025
cb3c694
merg
michaelfarrell76 Aug 19, 2025
03b2659
changes eexport
michaelfarrell76 Aug 21, 2025
42be153
parquet splitter
michaelfarrell76 Aug 27, 2025
7371a2c
Merges
michaelfarrell76 Aug 27, 2025
1171cd8
fuxem
michaelfarrell76 Sep 3, 2025
65a208b
ud
michaelfarrell76 Oct 17, 2025
bc568eb
fixes tsc
michaelfarrell76 Oct 17, 2025
7ea87cf
Updates
michaelfarrell76 Oct 20, 2025
decb452
Rev
michaelfarrell76 Oct 20, 2025
25702de
Rev
michaelfarrell76 Oct 20, 2025
9f8bdb6
rev
michaelfarrell76 Oct 20, 2025
7d12a5b
ud
michaelfarrell76 Oct 30, 2025
f8a91f9
MergeS
michaelfarrell76 Nov 5, 2025
0402858
Ud
michaelfarrell76 Dec 16, 2025
6817f50
Reverts
michaelfarrell76 Dec 16, 2025
a4b1f31
Reverts
michaelfarrell76 Dec 16, 2025
5ff7810
ud
michaelfarrell76 Dec 16, 2025
f9c63b8
exact
michaelfarrell76 Dec 17, 2025
85abeeb
Delete src/trim-scripts.ts
michaelfarrell76 Jan 26, 2026
cbc1aca
bluebird
michaelfarrell76 Feb 23, 2026
7aa12b6
bluebird
michaelfarrell76 Feb 23, 2026
cf11f60
bluebird
michaelfarrell76 Feb 23, 2026
0ceb94a
merges
michaelfarrell76 Feb 24, 2026
f499a9c
UD
michaelfarrell76 Feb 24, 2026
db14c39
Add configure-preference-upload command and refactor upload pipeline
michaelfarrell76 Feb 25, 2026
dbc3c4f
Move find-exact into a proper admin CLI command
michaelfarrell76 Feb 25, 2026
5a717f0
chore: merge main and resolve conflicts
michaelfarrell76 Feb 25, 2026
150a342
chore: merge main into feature branch
michaelfarrell76 Feb 25, 2026
b53d5b3
removes find-exact
michaelfarrell76 Feb 25, 2026
7c0f8a3
rev
michaelfarrell76 Feb 25, 2026
bfea65b
feat: add multi-identifier support and improve preference upload UI
michaelfarrell76 Mar 2, 2026
f3cc8a8
MERgeS
michaelfarrell76 Mar 3, 2026
5d9d2d3
Merges
michaelfarrell76 Mar 3, 2026
0a91174
Merge branch 'main' into jonnavithulaGirish/multiIdentifier
bencmbrook Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/pm-test/cli-upload-preferences-example.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
email_id,person_id,member_id,timestamp,Sales,source_system
test-acme10@gmail.com,p1,m1,2025-08-07T05:09:40.317Z,true,
test-acme11@gmail.com,p2,m2,2025-08-07T17:49:21.192Z,true,
test-acme12@gmail.com,p3,m3,2025-08-07T18:49:21.192Z,false,
test-acme13@gmail.com,p4,m4,2025-08-07T17:49:21.192Z,false,
5 changes: 5 additions & 0 deletions examples/pm-test/cli-upload-preferences-example2.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
email_id,person_id,member_id,timestamp,Sales,source_system
,p5,m5,2025-08-07T05:09:40.317Z,true,
test-acme14@gmail.com,p6,m6,2025-08-07T17:49:21.192Z,true,
test-acme15@gmail.com,p7,m7,2025-08-07T18:49:21.192Z,false,
test-acme16@gmail.com,p8,m8,2025-08-07T17:49:21.192Z,false,
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@
"query-string": "=7.0.0",
"semver": "^7.6.0",
"undici": "^5.22.1",
"yargs-parser": "^21.1.1"
"yargs-parser": "^21.1.1",
"bluebird": "^3.7.2"
},
"devDependencies": {
"@types/JSONStream": "npm:@types/jsonstream@^0.8.33",
Expand Down Expand Up @@ -165,7 +166,8 @@
"tsx": "^4.20.3",
"typescript": "^5.0.4",
"vite-tsconfig-paths": "^5.1.4",
"vitest": "^3.2.4"
"vitest": "^3.2.4",
"@types/bluebird": "^3.5.38"
},
"packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184"
}
21 changes: 20 additions & 1 deletion src/commands/consent/upload-preferences/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,26 @@ export const uploadPreferencesCommand = buildCommand({
kind: 'parsed',
parse: numberParser,
brief: 'The concurrency to use when uploading in parallel',
default: '10',
default: '1',
},
allowedIdentifierNames: {
kind: 'parsed',
parse: (value: string) => value.split(',').map((s) => s.trim()),
brief:
'Identifiers configured for the run. Comma-separated list of identifier names.',
},
identifierColumns: {
kind: 'parsed',
parse: (value: string) => value.split(',').map((s) => s.trim()),
brief:
'Columns in the CSV that should be used as identifiers. Comma-separated list of column names.',
},
columnsToIgnore: {
kind: 'parsed',
parse: (value: string) => value.split(',').map((s) => s.trim()),
brief:
'Columns in the CSV that should be ignored. Comma-separated list of column names.',
optional: true,

@bencmbrook bencmbrook Aug 15, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preferred pattern for lists is to use the built-in variadic: ','. This will parse it in the same way, but it will also (A) provide better error messages for malformed inputs, and (B) it will also self-document that it's a list which can be provided as a comma-separated argument like this:

FLAGS
      --auth                    The Transcend API key.
     [--identifierColumns]...   Identifier names configured for the run. [separator = ,]

It also allows users to pass --identifierColumns 1 --identifierColumns 2 --identifierColumns 3

And if any of these are enums (I don't think they are, but) you can also do something like this to validate (and also self-document) the expected inputs:
Image

If you

Suggested change
allowedIdentifierNames: {
kind: 'parsed',
parse: (value: string) => value.split(',').map((s) => s.trim()),
brief:
'Identifiers configured for the run. Comma-separated list of identifier names.',
},
identifierColumns: {
kind: 'parsed',
parse: (value: string) => value.split(',').map((s) => s.trim()),
brief:
'Columns in the CSV that should be used as identifiers. Comma-separated list of column names.',
},
columnsToIgnore: {
kind: 'parsed',
parse: (value: string) => value.split(',').map((s) => s.trim()),
brief:
'Columns in the CSV that should be ignored. Comma-separated list of column names.',
optional: true,
allowedIdentifierNames: {
kind: 'parsed',
parse: String,
variadic: ',',
brief: 'Identifier names configured for the run.',
},
identifierColumns: {
kind: 'parsed',
parse: String,
variadic: ',',
brief: 'Columns in the CSV that should be used as identifiers.',
},
columnsToIgnore: {
kind: 'parsed',
parse: String,
variadic: ',',
brief: 'Columns in the CSV that should be ignored.',
optional: true,

},
},
},
Expand Down
19 changes: 18 additions & 1 deletion src/commands/consent/upload-preferences/impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export interface UploadPreferencesCommandFlags {
attributes: string;
receiptFilepath: string;
concurrency: number;
allowedIdentifierNames: string[];
identifierColumns: string[];
columnsToIgnore?: string[];
}

export async function uploadPreferences(
Expand All @@ -46,6 +49,9 @@ export async function uploadPreferences(
isSilent,
attributes,
concurrency,
allowedIdentifierNames,
identifierColumns,
columnsToIgnore = [],
}: UploadPreferencesCommandFlags,
): Promise<void> {
if (!!directory && !!file) {
Expand Down Expand Up @@ -121,8 +127,15 @@ export async function uploadPreferences(

await map(
files,
async (filePath) => {
async (filePath, index) => {
const fileName = basename(filePath).replace('.csv', '');
const oldReceiptFilepath =
index > 0
? join(
receiptFileDir,
`${basename(files[0]).replace('.csv', '')}-receipts.json`,
)
: undefined;
await uploadPreferenceManagementPreferencesInteractive({
receiptFilepath: join(receiptFileDir, `${fileName}-receipts.json`),
auth,
Expand All @@ -137,6 +150,10 @@ export async function uploadPreferences(
dryRun,
attributes: splitCsvToList(attributes),
forceTriggerWorkflows,
allowedIdentifierNames,
identifierColumns,
oldReceiptFilepath,
columnsToIgnore,
});
},
{ concurrency },
Expand Down
2 changes: 2 additions & 0 deletions src/lib/graphql/fetchIdentifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export interface Identifier {
};
/** Display order */
displayOrder: number;
/** does this identifier uniquely identify a consent record */
isUniqueOnPreferenceStore: boolean;
}

const PAGE_SIZE = 20;
Expand Down
1 change: 1 addition & 0 deletions src/lib/graphql/gqls/identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const IDENTIFIERS = gql`
defaultMessage
}
displayOrder
isUniqueOnPreferenceStore
}
}
}
Expand Down
14 changes: 11 additions & 3 deletions src/lib/preference-management/codecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,20 @@ export const FileMetadataState = t.intersection([
* their preferences are already in the store
*/
skippedUpdates: t.record(t.string, t.record(t.string, t.string)),
/** The column name that maps to the identifier */
columnToIdentifier: t.record(
t.string,
t.type({
/** The identifier name */
name: t.string,
/** Is unique on preference store */
isUniqueOnPreferenceStore: t.boolean,
}),
),
}),
t.partial({
/** Determine which column name in file maps to consent record identifier to upload on */
identifierColumn: t.string,
/** Determine which column name in file maps to the timestamp */
timestampColum: t.string,
timestampColumn: t.string,
}),
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,26 @@ export async function parsePreferenceAndPurposeValuesFromCsv(
purposeSlugs,
preferenceTopics,
forceTriggerWorkflows,
columnsToIgnore,
}: {
/** The purpose slugs that are allowed to be updated */
purposeSlugs: string[];
/** The preference topics */
preferenceTopics: PreferenceTopic[];
/** Force workflow triggers */
forceTriggerWorkflows: boolean;
/** Columns to ignore in the CSV file */
columnsToIgnore: string[];
},
): Promise<FileMetadataState> {
// Determine columns to map
const columnNames = uniq(preferences.map((x) => Object.keys(x)).flat());

// Determine the columns that could potentially be used for identifier
const otherColumns = difference(columnNames, [
...(currentState.identifierColumn ? [currentState.identifierColumn] : []),
...(currentState.timestampColum ? [currentState.timestampColum] : []),
...Object.keys(currentState.columnToIdentifier),
...(currentState.timestampColumn ? [currentState.timestampColumn] : []),
...columnsToIgnore,
]);
if (otherColumns.length === 0) {
if (forceTriggerWorkflows) {
Expand Down
Loading
Loading