Skip to content

Commit

Permalink
fix: finish pvl, add NUT
Browse files Browse the repository at this point in the history
  • Loading branch information
WillieRuemmele committed Aug 23, 2022
1 parent f987af0 commit a4fe3f0
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 64 deletions.
8 changes: 8 additions & 0 deletions messages/package_version_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,11 @@ Build Duration in Seconds
# hasMetadataRemoved

Managed Metadata Removed

# isOrgDependent

Org-Dependent Unlocked Package

# createdBy

Created By
92 changes: 55 additions & 37 deletions src/commands/force/package/beta/version/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import * as os from 'os';
import { flags, FlagsConfig, SfdxCommand } from '@salesforce/command';
import { Messages } from '@salesforce/core';
import { Messages, SfProject } from '@salesforce/core';
import { CliUx } from '@oclif/core';
import {
getContainerOptions,
Expand All @@ -18,18 +18,37 @@ import {
PackageVersion,
} from '@salesforce/packaging';
import { PackageVersionListResult } from '@salesforce/packaging/src/interfaces';
import { Optional } from '@salesforce/ts-types';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/plugin-packaging', 'package_version_list');
const packaging = Messages.loadMessages('@salesforce/plugin-packaging', 'packaging');

type PackageVersionListCommandResult = Omit<
PackageVersionListResult,
'HasMetadataRemoved' | 'IsReleased' | 'IsPasswordProtected'
| 'HasMetadataRemoved'
| 'IsReleased'
| 'IsPasswordProtected'
| 'HasPassedCodeCoverageCheck'
| 'CreatedById'
| 'BuildDurationInSeconds'
| 'CodeCoverage'
| 'Package2'
> & {
HasMetadataRemoved: string;
IsPasswordProtected: string | boolean;
IsReleased: string | boolean;
HasPassedCodeCoverageCheck: string | boolean;
BuildDurationInSeconds: string;
CodeCoverage: string;
NamespacePrefix: string;
Package2Name: string;
Version: string;
InstallUrl: string;
AncestorVersion: string;
Alias: string;
IsOrgDependent: 'N/A' | 'Yes' | 'No';
CreatedBy: string;
};

export class PackageVersionListCommand extends SfdxCommand {
Expand Down Expand Up @@ -75,12 +94,12 @@ export class PackageVersionListCommand extends SfdxCommand {
};

public async run(): Promise<PackageVersionListCommandResult[]> {
const project = SfProject.getInstance();
const pv = new PackageVersion({ connection: this.hubOrg.getConnection(), project: undefined });
const packageIdOrAliases = (this.flags.packages as string[]) || [];
// resolve any passed in values from aliases or IDs
const packages = packageIdOrAliases.map((p) => getPackageIdFromAlias(p, this.project) ?? p);
const packages = packageIdOrAliases.map((p) => getPackageIdFromAlias(p, project) ?? p);
const records = await pv.list({
connection: this.hubOrg.getConnection(),
createdLastDays: this.flags.createdlastdays as number,
concise: this.flags.concise as boolean,
modifiedLastDays: this.flags.modifiedlastdays as number,
Expand All @@ -93,8 +112,7 @@ export class PackageVersionListCommand extends SfdxCommand {
const results: PackageVersionListCommandResult[] = [];

if (records?.length > 0) {
let ancestorVersionsMap;
let containerOptionsMap;
let ancestorVersionsMap: Optional<Map<string, string>>;
// lookup ancestorVersions if ancestorIds are present
const ancestorIds = records.filter((record) => record.AncestorId).map((record) => record.AncestorId);
if (ancestorIds?.length > 0) {
Expand All @@ -103,21 +121,21 @@ export class PackageVersionListCommand extends SfdxCommand {

// Get the container options for each package version. We need this for determining if the version is OrgDependent
const recordIds = [...new Set(records.map((record) => record.Package2Id))];
containerOptionsMap = await getContainerOptions(recordIds, this.hubOrg.getConnection());
const containerOptionsMap = await getContainerOptions(recordIds, this.hubOrg.getConnection());

records.forEach((record) => {
const ids = [record.Id, record.SubscriberPackageVersionId];
const aliases = [];
ids.forEach((id) => {
const matches = getPackageAliasesFromId(id, this.project);
const matches = getPackageAliasesFromId(id, project);
if (matches.length > 0) {
aliases.push(matches);
}
});
const AliasStr = aliases.length > 0 ? aliases.join() : '';

// set Ancestor display values
let ancestorVersion = null;
let ancestorVersion: Optional<string> = null;
if (record.AncestorId) {
ancestorVersion = ancestorVersionsMap.get(record.AncestorId);
} else if (containerOptionsMap.get(record.Package2Id) !== 'Managed') {
Expand All @@ -129,7 +147,7 @@ export class PackageVersionListCommand extends SfdxCommand {
const codeCoverage =
record.CodeCoverage != null
? `${record.CodeCoverage.ApexCodeCoveragePercentage}%`
: record.Package2.IsOrgDependent === true || record.ValidationSkipped === true
: record.Package2.IsOrgDependent || record.ValidationSkipped
? 'N/A'
: '';

Expand All @@ -141,16 +159,12 @@ export class PackageVersionListCommand extends SfdxCommand {
const isOrgDependent =
containerOptionsMap.get(record.Package2Id) === 'Managed'
? 'N/A'
: record.Package2.IsOrgDependent === true
: record.Package2.IsOrgDependent
? 'Yes'
: 'No';

const hasMetadataRemoved =
containerOptionsMap.get(record.Package2Id) !== 'Managed'
? 'N/A'
: record.HasMetadataRemoved === true
? 'Yes'
: 'No';
containerOptionsMap.get(record.Package2Id) !== 'Managed' ? 'N/A' : record.HasMetadataRemoved ? 'Yes' : 'No';

results.push({
Package2Id: record.Package2Id,
Expand All @@ -175,7 +189,7 @@ export class PackageVersionListCommand extends SfdxCommand {
LastModifiedDate: record.LastModifiedDate, // moment(record.LastModifiedDate).format('YYYY-MM-DD HH:mm'),
// CreatedDate: moment(record.CreatedDate).format('YYYY-MM-DD HH:mm'),
// LastModifiedDate: moment(record.LastModifiedDate).format('YYYY-MM-DD HH:mm'),
InstallUrl: INSTALL_URL_BASE + record.SubscriberPackageVersionId,
InstallUrl: INSTALL_URL_BASE.toString() + record.SubscriberPackageVersionId,
CodeCoverage: codeCoverage,
HasPassedCodeCoverageCheck: hasPassedCodeCoverageCheck,
ValidationSkipped: record.ValidationSkipped,
Expand All @@ -184,16 +198,17 @@ export class PackageVersionListCommand extends SfdxCommand {
Alias: AliasStr,
IsOrgDependent: isOrgDependent,
ReleaseVersion: record.ReleaseVersion == null ? '' : Number.parseFloat(record.ReleaseVersion).toFixed(1),
BuildDurationInSeconds: record.BuildDurationInSeconds == null ? '' : record.BuildDurationInSeconds,
BuildDurationInSeconds: record.BuildDurationInSeconds == null ? '' : record.BuildDurationInSeconds.toString(),
HasMetadataRemoved: hasMetadataRemoved,
CreatedBy: record.CreatedById,
});
});
this.ux.styledHeader(`Package Versions [${results.length}]`);
this.ux.table(results, this.getColumnData(), { 'no-truncate': true });
} else {
this.ux.log('No results found');
}

this.ux.styledHeader(`Package Versions [${results.length}]`);
this.ux.table(results, this.getColumnData());

return results;
}

Expand All @@ -208,26 +223,29 @@ export class PackageVersionListCommand extends SfdxCommand {
IsReleased: { header: 'Released' },
};
}
const defaultCols = {
Package2Name: { header: 'Package Name' },
NamespacePrefix: { header: 'Namespace' },
Name: { header: 'Version Name' },
Version: { header: messages.getMessage('version') },
SubscriberPackageVersionId: {
header: messages.getMessage('subscriberPackageVersionId'),
},
Alias: { header: messages.getMessage('alias') },
IsPasswordProtected: { header: messages.getMessage('installKey') },
IsReleased: { header: 'Released' },
ValidationSkipped: { header: messages.getMessage('validationSkipped') },
AncestorId: { header: 'Ancestor' },
AncestorVersion: { header: 'Ancestor Version' },
Branch: { header: messages.getMessage('packageBranch') },
};

if (!this.flags.verbose) {
return {
Package2Name: { header: 'Package Name' },
NamespacePrefix: { header: 'Namespace' },
Name: { header: 'Version Name' },
Version: { header: messages.getMessage('version') },
SubscriberPackageVersionId: {
header: messages.getMessage('subscriberPackageVersionId'),
},
Alias: { header: messages.getMessage('alias') },
IsPasswordProtected: { header: messages.getMessage('installKey') },
IsReleased: { header: 'Released' },
ValidationSkipped: { header: messages.getMessage('validationSkipped') },
AncestorId: { header: 'Ancestor' },
AncestorVersion: { header: 'Ancestor Version' },
Branch: { header: messages.getMessage('packageBranch') },
};
return defaultCols;
} else {
// add additional columns for verbose output
return {
...defaultCols,
Package2Id: { header: messages.getMessage('packageId') },
InstallUrl: { header: messages.getMessage('installUrl') },
Id: { header: messages.getMessage('id') },
Expand Down
119 changes: 119 additions & 0 deletions test/commands/force/package/packageVersionList.nut.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2022, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import * as path from 'path';
import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit';
import { OrgConfigProperties } from '@salesforce/core';
import { expect } from 'chai';

describe('package:version:list', () => {
let session: TestSession;
let usernameOrAlias: string;
before(async () => {
const executablePath = path.join(process.cwd(), 'bin', 'dev');
session = await TestSession.create({
setupCommands: [`${executablePath} config:get ${OrgConfigProperties.TARGET_DEV_HUB} --json`],
project: { name: 'packageVersionList' },
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
usernameOrAlias = (session.setup[0] as { result: [{ value: string }] }).result[0].value;

if (!usernameOrAlias) throw Error('no default username set');
});

after(async () => {
await session?.clean();
});
it('should list package versions in dev hub - human readable results', () => {
const command = `force:package:beta:version:list -v ${usernameOrAlias}`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const output = execCmd(command, { ensureExitCode: 0 }).shellOutput.stdout as string;
expect(output).to.contain('=== Package Versions [');
expect(output).to.match(
/Package Name\s+Namespace\s+Version Name\s+Version\s+Subscriber Package Version Id\sAlias\s+Installation Key\s+Released\s+Validation Skipped\s+Ancestor\s+Ancestor Version\s+Branch/
);
});

it('should list package versions in dev hub - concise output', () => {
const command = `force:package:beta:version:list -v ${usernameOrAlias} --concise`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const output = execCmd(command, { ensureExitCode: 0 }).shellOutput.stdout as string;
expect(output).to.contain('=== Package Versions [');
expect(output).to.match(/Package Id\s+Version\s+Subscriber Package Version Id\s+Released/);
});

it('should list package versions modified in the last 5 days', () => {
const command = `force:package:beta:version:list -v ${usernameOrAlias} --modifiedlastdays 5`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const output = execCmd(command, { ensureExitCode: 0 }).shellOutput.stdout as string;
expect(output).to.contain('=== Package Versions [');
expect(output).to.match(
/Package Name\s+Namespace\s+Version Name\s+Version\s+Subscriber Package Version Id\sAlias\s+Installation Key\s+Released\s+Validation Skipped\s+Ancestor\s+Ancestor Version\s+Branch/
);
});
it('should list package versions created in the last 5 days', () => {
const command = `force:package:beta:version:list -v ${usernameOrAlias} --createdlastdays 5`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const output = execCmd(command, { ensureExitCode: 0 }).shellOutput.stdout as string;
expect(output).to.contain('=== Package Versions [');
expect(output).to.match(
/Package Name\s+Namespace\s+Version Name\s+Version\s+Subscriber Package Version Id\sAlias\s+Installation Key\s+Released\s+Validation Skipped\s+Ancestor\s+Ancestor Version\s+Branch/
);
});
it('should list installed packages in dev hub - verbose human readable results', () => {
const command = `force:package:beta:version:list -v ${usernameOrAlias} --verbose`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const output = execCmd(command, { ensureExitCode: 0 }).shellOutput.stdout as string;
expect(output).to.contain('=== Package Versions [');
expect(output).to.match(
/Package Name\s+Namespace\s+Version Name\s+Version\s+Subscriber Package Version Id\sAlias\s+Installation Key\s+Released\s+Validation Skipped\s+Ancestor\s+Ancestor Version\s+Branch\s+Package Id\s+Installation URL\s+Package Version Id\s+Created Date\s+Last Modified Date\s+Tag\s+Description\s+Code Coverage\s+Code Coverage Met\s+Converted From Version Id\s+Org-Dependent\s+Unlocked Package\s+Release\s+Version\s+Build Duration in Seconds\s+Managed Metadata Removed\s+Created By/
);
});
it('should list package versions in dev hub - json results', () => {
const command = `force:package:beta:version:list -v ${usernameOrAlias} --json`;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const output = execCmd(command, { ensureExitCode: 0 }).jsonOutput as {
status: number;
result: { [key: string]: unknown };
};
const keys = [
'Package2Id',
'Branch',
'Tag',
'MajorVersion',
'MinorVersion',
'PatchVersion',
'BuildNumber',
'Id',
'SubscriberPackageVersionId',
'Name',
'NamespacePrefix',
'Package2Name',
'Description',
'Version',
'IsPasswordProtected',
'IsReleased',
'CreatedDate',
'LastModifiedDate',
'InstallUrl',
'CodeCoverage',
'ValidationSkipped',
'AncestorId',
'AncestorVersion',
'Alias',
'IsOrgDependent',
'ReleaseVersion',
'BuildDurationInSeconds',
'HasMetadataRemoved',
'CreatedBy',
];
expect(output).to.be.ok;
expect(output.status).to.equal(0);
expect(output.result).to.have.length.greaterThan(0);
expect(output.result[0]).to.have.keys(keys);
});
});
Loading

0 comments on commit a4fe3f0

Please sign in to comment.