Skip to content

Commit

Permalink
feat: use new table (#1100)
Browse files Browse the repository at this point in the history
* feat: use new table

* test: update nuts

* test: update nuts

* fix: wrap tables by default

* chore: bump sf-plugins-core
  • Loading branch information
mdonnalley authored Oct 29, 2024
1 parent 73673bf commit 38a638b
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 198 deletions.
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

0 comments on commit 38a638b

Please sign in to comment.