Skip to content
Merged
Show file tree
Hide file tree
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
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
"@oclif/multi-stage-output": "^0.7.5",
"@salesforce/core": "^8.6.1",
"@salesforce/kit": "^3.2.2",
"@salesforce/sf-plugins-core": "^11.3.10",
"@salesforce/sf-plugins-core": "^12.0.10",
"@salesforce/ts-types": "^2.0.11",
"ansis": "^3.2.0",
"change-case": "^5.4.4",
Expand All @@ -124,7 +124,8 @@
},
"devDependencies": {
"@oclif/core": "^4.0.19",
"@oclif/plugin-command-snapshot": "^5.2.19",
"@oclif/plugin-command-snapshot": "^5.2.15",
"@oclif/test": "^4.1.0",
"@salesforce/cli-plugins-testkit": "^5.3.34",
"@salesforce/dev-scripts": "^10.2.10",
"@salesforce/plugin-command-reference": "^3.1.27",
Expand Down
17 changes: 6 additions & 11 deletions src/bulkOperationBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,17 +200,12 @@ const executeBulkV2DataRequest = async <J extends Schema>(
};

const printBulkErrors = (failedResults: IngestJobV2FailedResults<Schema>): void => {
const columns = {
id: { header: 'Id' },
sfId: { header: 'Sf_Id' },
error: { header: 'Error' },
};
const options = { title: `Bulk Failures [${failedResults.length}]` };
const ux = new Ux();
ux.log();
ux.table(
failedResults.map((f) => ({ id: 'Id' in f ? f.Id : '', sfId: f.sf__Id, error: f.sf__Error })),
columns,
options
);
ux.table({
// eslint-disable-next-line camelcase
data: failedResults.map((f) => ({ id: 'Id' in f ? f.Id : '', sfId: f.sf__Id, error: f.sf__Error })),
columns: ['id', { key: 'sfId', name: 'Sf_Id' }, 'error'],
title: `Bulk Failures [${failedResults.length}]`,
});
};
9 changes: 4 additions & 5 deletions src/commands/data/import/legacy/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,10 @@ export default class Import extends SfCommand<ImportResult[] | JsonMap> {
return { refId: ref.referenceId, type, id: ref.id };
});

this.styledHeader('Import Results');
this.table(processedResult, {
refId: { header: 'Reference ID' },
type: { header: 'Type' },
id: { header: 'ID' },
this.table({
data: processedResult,
columns: [{ key: 'refId', name: 'Reference ID' }, 'type', { key: 'id', name: 'ID' }],
title: 'Import Results',
});

return processedResult;
Expand Down
13 changes: 8 additions & 5 deletions src/commands/data/import/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@ export default class Import extends SfCommand<ImportResult[]> {
? await importFromPlan(conn, flags.plan)
: await importFromFiles(conn, flags.files ?? []);

this.styledHeader('Import Results');
this.table(results, {
refId: { header: 'Reference ID' },
type: { header: 'Type' },
id: { header: 'ID' },
this.table({
data: results,
columns: [
{ key: 'refId', name: 'Reference ID' },
{ key: 'type', name: 'Type' },
{ key: 'id', name: 'ID' },
],
title: 'Import Results',
});
return results;
}
Expand Down
49 changes: 27 additions & 22 deletions src/reporters/query/humanReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,26 +55,6 @@ export const parseFields = (fields: Field[]): ParsedFields => ({
aggregates: fields.filter(isAggregate),
});

export const prepColumns = (columns: Array<Optional<string>>): Ux.Table.Columns<GenericObject> => {
const formattedColumns: Ux.Table.Columns<GenericObject> = {};
columns.filter(isString).map(
(field) =>
(formattedColumns[field] = {
header: field.toUpperCase(),
get: (row): string => {
// first test if key exists, if so, return value
if (Reflect.has(row, field)) {
return (Reflect.get(row, field) as string) ?? '';
} else {
// if not, try to find it query
return (get(row, field) as string) ?? '';
}
},
})
);
return formattedColumns;
};

/** find null/undefined and replace it with a styled string */
export const prepNullValues = <T>(record: T): T =>
isPlainObject(record)
Expand All @@ -86,9 +66,34 @@ export const prepNullValues = <T>(record: T): T =>
const maybeReplaceNulls = <T>(value: T): T | string => value ?? nullString;
const maybeRecurseNestedObjects = <T>(value: T): T => (isPlainObject(value) ? prepNullValues(value) : value);

const printTable = (records: GenericObject[], columns: Array<Optional<string>>, totalCount: number): void => {
function prepData(
records: GenericObject[],
columns: Array<Optional<string>>
): { data: Array<Record<string, unknown>>; columns: Array<{ key: string; name: string }> } {
const fields = columns.filter(isString);
const data = records.map(prepNullValues).map((record) => {
const row: Record<string, unknown> = {};
fields.forEach((field) => {
if (field in record) {
row[field] = (record[field] as string) ?? '';
} else {
// if not, try to find it query
row[field] = (get(record, field) as string) ?? '';
}
});
return row;
});
return { data, columns: fields.map((field) => ({ key: field, name: field.toUpperCase() })) };
}

const printTable = (records: GenericObject[], cols: Array<Optional<string>>, totalCount: number): void => {
const ux = new Ux();
ux.table(records.map(prepNullValues), prepColumns(columns));
const { data, columns } = prepData(records, cols);
ux.table({
data,
columns,
overflow: 'wrap',
});
ux.log(ansis.bold(messages.getMessage('displayQueryRecordsRetrieved', [totalCount])));
};

Expand Down
6 changes: 3 additions & 3 deletions src/reporters/search/humanSearchReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export class HumanSearchReporter extends SearchReporter {
this.ux.log('No Records Found');
}
this.typeRecordsMap.forEach((records, type) => {
// to find the columns of the query, parse the keys of the first record
this.ux.table(records, Object.fromEntries(Object.keys(records[0]).map((k) => [k, { header: k }])), {
'no-truncate': true,
this.ux.table({
data: records,
overflow: 'wrap',
title: type,
});
this.ux.log();
Expand Down
9 changes: 5 additions & 4 deletions test/commands/data/dataBulk.nut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,11 @@ describe('data:bulk commands', () => {
ensureExitCode: 1,
}).shellOutput;
// Bulk Failures [1]
// ==========================================================================
// | Id Sf_Id Error
// | ────────────────── ───── ───────────────────────────────────────────────
// | 001000000000000AAA MALFORMED_ID:malformed id 001000000000000AAA:--
// ┌────────────────────┬────────────────────┬───────────────────────────────────────────────────────────┐
// │ Id │ Sf_Id │ Error │
// ├────────────────────┼────────────────────┼───────────────────────────────────────────────────────────┤
// │ 001000000000000AAA │ 001000000000000AAA │ INVALID_CROSS_REFERENCE_KEY:invalid cross reference id:-- │
// └────────────────────┴────────────────────┴───────────────────────────────────────────────────────────┘

expect(result).to.include('Bulk Failures [1]');
expect(result).to.include('Id');
Expand Down
27 changes: 16 additions & 11 deletions test/commands/data/dataSoqlQuery.nut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ describe('data:query command', () => {

const queryResult = runQuery(query, { ensureExitCode: 0, json: false }) as string;

expect(queryResult).to.match(/ID\s+?NAME\s+?PHONE\s+?WEBSITE\s+?NUMBEROFEMPLOYEES\s+?INDUSTRY/g);
expect(queryResult).to.match(/ID.*?NAME.*?PHONE.*?WEBSITE.*?NUMBEROFEMPLOYEES.*?INDUSTRY/g);
expect(queryResult).to.match(/Total number of records retrieved: 1\./g);
});

Expand All @@ -226,7 +226,7 @@ describe('data:query command', () => {

const queryResult = execCmd(`data:query --file ${filepath}`, { ensureExitCode: 0 }).shellOutput.stdout;

expect(queryResult).to.match(/ID\s+?NAME\s+?PHONE\s+?WEBSITE\s+?NUMBEROFEMPLOYEES\s+?INDUSTRY/g);
expect(queryResult).to.match(/ID.*?NAME.*?PHONE.*?WEBSITE.*?NUMBEROFEMPLOYEES.*?INDUSTRY/g);
expect(queryResult).to.match(/Total number of records retrieved: 1\./g);
});

Expand All @@ -237,7 +237,7 @@ describe('data:query command', () => {
const queryResult = runQuery(query, { ensureExitCode: 0, json: false }) as string;

expect(queryResult).to.match(
/ID\s+?NAME\s+?PHONE\s+?WEBSITE\s+?NUMBEROFEMPLOYEES\s+?INDUSTRY\s+?CONTACTS.LASTNAME\s+?CONTACTS.TITLE\s+?CONTACTS.EMAIL/g
/ID.*?NAME.*?PHONE.*?WEBSITE.*?NUMBEROFEMPLOYEES.*?INDUSTRY.*?CONTACTS.LASTNAME.*?CONTACTS.TITLE.*?CONTACTS.EMAIL/g
);
expect(queryResult).to.match(/\sSmith/g);
expect(queryResult).to.match(/Total number of records retrieved: 2\./g);
Expand All @@ -260,17 +260,22 @@ describe('data:query command', () => {
it('should print JSON output correctly', () => {
const result = runQuery('select id, isActive, Metadata from RemoteProxy', {
ensureExitCode: 0,
json: false,
json: true,
toolingApi: true,
});

expect(result).to.not.include('[object Object]');
expect(result).to.be.ok;
expect(result).to.be.an('object');

// the Metadata object parsed correctly
expect(result).to.include('disableProtocolSecurity');
expect(result).to.include('isActive');
expect(result).to.include('url');
expect(result).to.include('urls');
expect(result).to.include('description');
// @ts-expect-error typescript doesn't know the shape of the Metadata object
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const metadataObject = result?.records[0].Metadata;
expect(metadataObject).to.have.property('disableProtocolSecurity');
expect(metadataObject).to.have.property('isActive');
expect(metadataObject).to.have.property('url');
expect(metadataObject).to.have.property('urls');
expect(metadataObject).to.have.property('description');
});
});
describe('data:query --bulk', () => {
Expand Down Expand Up @@ -300,7 +305,7 @@ describe('data:query command', () => {

const queryResult = runQuery(query, { ensureExitCode: 0, json: false, bulk: true });

expect(queryResult).to.match(/ID\s+?NAME\s+?PHONE\s+?WEBSITE\s+?NUMBEROFEMPLOYEES\s+?INDUSTRY/g);
expect(queryResult).to.match(/ID.*?NAME.*?PHONE.*?WEBSITE.*?NUMBEROFEMPLOYEES.*?INDUSTRY/g);
expect(queryResult).to.match(/Total number of records retrieved: 1\./g);
});

Expand Down
Loading