Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: throw when csv produces non-unique cmdt record names #655

Merged
merged 1 commit into from
Nov 20, 2023
Merged
Changes from all commits
Commits
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
71 changes: 47 additions & 24 deletions src/commands/cmdt/generate/records.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,30 +77,32 @@ export default class Insert extends SfCommand<CreateConfigs> {
}) as Record[];

// Transforms on the recordname are to match the behavior of adding a new Custom Metadata Type record in the UI
const recordConfigs: CreateConfig[] = parsedRecords.map((record) => ({
typename: flags['type-name'],
recordname: (record[flags['name-column']] as string)
.replace(/[^a-zA-Z0-9]/g, '_') // replace all non-alphanumeric characters with _
.replace(/^(\d)/, 'X$1') // prepend an X if the first character is a number
.replace(/_{2,}/g, '_') // replace multiple underscores with single underscore
.replace(/_$/, ''), // remove trailing underscore (if any)
label: record[flags['name-column']] as string,
inputdir: flags['input-directory'],
outputdir: flags['output-directory'],
protected: false,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
varargs: Object.fromEntries(
// TODO: throw an error if any of the fields in the csvDataAry do not exist in the fileData
fileData.map((file) => {
if (file.fullName) {
return record[file.fullName] ? [file.fullName, record[file.fullName]] : [];
} else {
throw new SfError('No fullName found in fileData');
}
})
),
fileData,
}));
const recordConfigs: CreateConfig[] = validateUniqueNames(
parsedRecords.map((record) => ({
typename: flags['type-name'],
recordname: (record[flags['name-column']] as string)
.replace(/[^a-zA-Z0-9]/g, '_') // replace all non-alphanumeric characters with _
.replace(/^(\d)/, 'X$1') // prepend an X if the first character is a number
.replace(/_{2,}/g, '_') // replace multiple underscores with single underscore
.replace(/_$/, ''), // remove trailing underscore (if any)
label: record[flags['name-column']] as string,
inputdir: flags['input-directory'],
outputdir: flags['output-directory'],
protected: false,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
varargs: Object.fromEntries(
// TODO: throw an error if any of the fields in the csvDataAry do not exist in the fileData
fileData.map((file) => {
if (file.fullName) {
return record[file.fullName] ? [file.fullName, record[file.fullName]] : [];
} else {
throw new SfError('No fullName found in fileData');
}
})
),
fileData,
}))
);

// find the cmdt in the inputdir.
// loop through files and create records that match fields
Expand All @@ -112,6 +114,27 @@ export default class Insert extends SfCommand<CreateConfigs> {
}
}

/** validate name fields are unique, otherwise they'll be trying to write to the same file */
const validateUniqueNames = (recordConfigs: CreateConfig[]): CreateConfig[] => {
const recordNameSet = new Set<string>();
const dupes = recordConfigs
.map((rc) => {
if (recordNameSet.has(rc.recordname)) {
return rc.recordname;
} else {
recordNameSet.add(rc.recordname);
return undefined;
}
})
.filter((rc): rc is string => rc !== undefined);
if (dupes.length > 0) {
throw new SfError(
`Your CSV has duplicate values: ${[...new Set(dupes)].join(', ')}. CMDT require unique names in the name field.`
);
}
return recordConfigs;
};

/** Validate that every column in the CSV has known metadata */
const columnValidation = (requiredFields: string[], columnList: string[], typeNameFlag: string): string[] => {
columnList.forEach((column) => {
Expand Down