Skip to content

Commit 38a638b

Browse files
authored
feat: use new table (#1100)
* feat: use new table * test: update nuts * test: update nuts * fix: wrap tables by default * chore: bump sf-plugins-core
1 parent 73673bf commit 38a638b

File tree

12 files changed

+237
-198
lines changed

12 files changed

+237
-198
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113
"@oclif/multi-stage-output": "^0.7.5",
114114
"@salesforce/core": "^8.6.1",
115115
"@salesforce/kit": "^3.2.2",
116-
"@salesforce/sf-plugins-core": "^11.3.10",
116+
"@salesforce/sf-plugins-core": "^12.0.10",
117117
"@salesforce/ts-types": "^2.0.11",
118118
"ansis": "^3.2.0",
119119
"change-case": "^5.4.4",
@@ -124,7 +124,8 @@
124124
},
125125
"devDependencies": {
126126
"@oclif/core": "^4.0.19",
127-
"@oclif/plugin-command-snapshot": "^5.2.19",
127+
"@oclif/plugin-command-snapshot": "^5.2.15",
128+
"@oclif/test": "^4.1.0",
128129
"@salesforce/cli-plugins-testkit": "^5.3.34",
129130
"@salesforce/dev-scripts": "^10.2.10",
130131
"@salesforce/plugin-command-reference": "^3.1.27",

src/bulkOperationBase.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -200,17 +200,12 @@ const executeBulkV2DataRequest = async <J extends Schema>(
200200
};
201201

202202
const printBulkErrors = (failedResults: IngestJobV2FailedResults<Schema>): void => {
203-
const columns = {
204-
id: { header: 'Id' },
205-
sfId: { header: 'Sf_Id' },
206-
error: { header: 'Error' },
207-
};
208-
const options = { title: `Bulk Failures [${failedResults.length}]` };
209203
const ux = new Ux();
210204
ux.log();
211-
ux.table(
212-
failedResults.map((f) => ({ id: 'Id' in f ? f.Id : '', sfId: f.sf__Id, error: f.sf__Error })),
213-
columns,
214-
options
215-
);
205+
ux.table({
206+
// eslint-disable-next-line camelcase
207+
data: failedResults.map((f) => ({ id: 'Id' in f ? f.Id : '', sfId: f.sf__Id, error: f.sf__Error })),
208+
columns: ['id', { key: 'sfId', name: 'Sf_Id' }, 'error'],
209+
title: `Bulk Failures [${failedResults.length}]`,
210+
});
216211
};

src/commands/data/import/legacy/tree.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,10 @@ export default class Import extends SfCommand<ImportResult[] | JsonMap> {
8585
return { refId: ref.referenceId, type, id: ref.id };
8686
});
8787

88-
this.styledHeader('Import Results');
89-
this.table(processedResult, {
90-
refId: { header: 'Reference ID' },
91-
type: { header: 'Type' },
92-
id: { header: 'ID' },
88+
this.table({
89+
data: processedResult,
90+
columns: [{ key: 'refId', name: 'Reference ID' }, 'type', { key: 'id', name: 'ID' }],
91+
title: 'Import Results',
9392
});
9493

9594
return processedResult;

src/commands/data/import/tree.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,14 @@ export default class Import extends SfCommand<ImportResult[]> {
5353
? await importFromPlan(conn, flags.plan)
5454
: await importFromFiles(conn, flags.files ?? []);
5555

56-
this.styledHeader('Import Results');
57-
this.table(results, {
58-
refId: { header: 'Reference ID' },
59-
type: { header: 'Type' },
60-
id: { header: 'ID' },
56+
this.table({
57+
data: results,
58+
columns: [
59+
{ key: 'refId', name: 'Reference ID' },
60+
{ key: 'type', name: 'Type' },
61+
{ key: 'id', name: 'ID' },
62+
],
63+
title: 'Import Results',
6164
});
6265
return results;
6366
}

src/reporters/query/humanReporter.ts

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,26 +55,6 @@ export const parseFields = (fields: Field[]): ParsedFields => ({
5555
aggregates: fields.filter(isAggregate),
5656
});
5757

58-
export const prepColumns = (columns: Array<Optional<string>>): Ux.Table.Columns<GenericObject> => {
59-
const formattedColumns: Ux.Table.Columns<GenericObject> = {};
60-
columns.filter(isString).map(
61-
(field) =>
62-
(formattedColumns[field] = {
63-
header: field.toUpperCase(),
64-
get: (row): string => {
65-
// first test if key exists, if so, return value
66-
if (Reflect.has(row, field)) {
67-
return (Reflect.get(row, field) as string) ?? '';
68-
} else {
69-
// if not, try to find it query
70-
return (get(row, field) as string) ?? '';
71-
}
72-
},
73-
})
74-
);
75-
return formattedColumns;
76-
};
77-
7858
/** find null/undefined and replace it with a styled string */
7959
export const prepNullValues = <T>(record: T): T =>
8060
isPlainObject(record)
@@ -86,9 +66,34 @@ export const prepNullValues = <T>(record: T): T =>
8666
const maybeReplaceNulls = <T>(value: T): T | string => value ?? nullString;
8767
const maybeRecurseNestedObjects = <T>(value: T): T => (isPlainObject(value) ? prepNullValues(value) : value);
8868

89-
const printTable = (records: GenericObject[], columns: Array<Optional<string>>, totalCount: number): void => {
69+
function prepData(
70+
records: GenericObject[],
71+
columns: Array<Optional<string>>
72+
): { data: Array<Record<string, unknown>>; columns: Array<{ key: string; name: string }> } {
73+
const fields = columns.filter(isString);
74+
const data = records.map(prepNullValues).map((record) => {
75+
const row: Record<string, unknown> = {};
76+
fields.forEach((field) => {
77+
if (field in record) {
78+
row[field] = (record[field] as string) ?? '';
79+
} else {
80+
// if not, try to find it query
81+
row[field] = (get(record, field) as string) ?? '';
82+
}
83+
});
84+
return row;
85+
});
86+
return { data, columns: fields.map((field) => ({ key: field, name: field.toUpperCase() })) };
87+
}
88+
89+
const printTable = (records: GenericObject[], cols: Array<Optional<string>>, totalCount: number): void => {
9090
const ux = new Ux();
91-
ux.table(records.map(prepNullValues), prepColumns(columns));
91+
const { data, columns } = prepData(records, cols);
92+
ux.table({
93+
data,
94+
columns,
95+
overflow: 'wrap',
96+
});
9297
ux.log(ansis.bold(messages.getMessage('displayQueryRecordsRetrieved', [totalCount])));
9398
};
9499

src/reporters/search/humanSearchReporter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ export class HumanSearchReporter extends SearchReporter {
1717
this.ux.log('No Records Found');
1818
}
1919
this.typeRecordsMap.forEach((records, type) => {
20-
// to find the columns of the query, parse the keys of the first record
21-
this.ux.table(records, Object.fromEntries(Object.keys(records[0]).map((k) => [k, { header: k }])), {
22-
'no-truncate': true,
20+
this.ux.table({
21+
data: records,
22+
overflow: 'wrap',
2323
title: type,
2424
});
2525
this.ux.log();

test/commands/data/dataBulk.nut.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ describe('data:bulk commands', () => {
106106
ensureExitCode: 1,
107107
}).shellOutput;
108108
// Bulk Failures [1]
109-
// ==========================================================================
110-
// | Id Sf_Id Error
111-
// | ────────────────── ───── ───────────────────────────────────────────────
112-
// | 001000000000000AAA MALFORMED_ID:malformed id 001000000000000AAA:--
109+
// ┌────────────────────┬────────────────────┬───────────────────────────────────────────────────────────┐
110+
// │ Id │ Sf_Id │ Error │
111+
// ├────────────────────┼────────────────────┼───────────────────────────────────────────────────────────┤
112+
// │ 001000000000000AAA │ 001000000000000AAA │ INVALID_CROSS_REFERENCE_KEY:invalid cross reference id:-- │
113+
// └────────────────────┴────────────────────┴───────────────────────────────────────────────────────────┘
113114

114115
expect(result).to.include('Bulk Failures [1]');
115116
expect(result).to.include('Id');

test/commands/data/dataSoqlQuery.nut.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ describe('data:query command', () => {
214214

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

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

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

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

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

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

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

267-
expect(result).to.not.include('[object Object]');
267+
expect(result).to.be.ok;
268+
expect(result).to.be.an('object');
269+
268270
// the Metadata object parsed correctly
269-
expect(result).to.include('disableProtocolSecurity');
270-
expect(result).to.include('isActive');
271-
expect(result).to.include('url');
272-
expect(result).to.include('urls');
273-
expect(result).to.include('description');
271+
// @ts-expect-error typescript doesn't know the shape of the Metadata object
272+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
273+
const metadataObject = result?.records[0].Metadata;
274+
expect(metadataObject).to.have.property('disableProtocolSecurity');
275+
expect(metadataObject).to.have.property('isActive');
276+
expect(metadataObject).to.have.property('url');
277+
expect(metadataObject).to.have.property('urls');
278+
expect(metadataObject).to.have.property('description');
274279
});
275280
});
276281
describe('data:query --bulk', () => {
@@ -300,7 +305,7 @@ describe('data:query command', () => {
300305

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

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

0 commit comments

Comments
 (0)