From f4aa7da5eab0f2bc072669de2dba30dc07fc192f Mon Sep 17 00:00:00 2001 From: tshopshire Date: Wed, 26 Aug 2026 19:03:16 -0700 Subject: [PATCH] feat: adds support for authorization list cmd @W-23970417@ --- command-snapshot.json | 8 +++ messages/package_authorize_list.md | 41 +++++++++++++++ package.json | 5 +- schemas/package-authorize-list.json | 49 ++++++++++++++++++ src/commands/package/authorize/list.ts | 70 ++++++++++++++++++++++++++ src/utils/packageAuthorization.ts | 43 ++++++++++++++++ src/utils/subscriberOrg.ts | 27 ++++++++++ 7 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 messages/package_authorize_list.md create mode 100644 schemas/package-authorize-list.json create mode 100644 src/commands/package/authorize/list.ts create mode 100644 src/utils/packageAuthorization.ts create mode 100644 src/utils/subscriberOrg.ts diff --git a/command-snapshot.json b/command-snapshot.json index 6a908640..5f421116 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -55,6 +55,14 @@ "flags": ["api-version", "flags-dir", "json", "loglevel", "package-id", "target-org"], "plugin": "@salesforce/plugin-packaging" }, + { + "alias": [], + "command": "package:authorize:list", + "flagAliases": ["apiversion", "targetusername", "u"], + "flagChars": ["o", "p"], + "flags": ["api-version", "flags-dir", "json", "loglevel", "package", "target-org"], + "plugin": "@salesforce/plugin-packaging" + }, { "alias": [], "command": "package:bundle:create", diff --git a/messages/package_authorize_list.md b/messages/package_authorize_list.md new file mode 100644 index 00000000..8076eb47 --- /dev/null +++ b/messages/package_authorize_list.md @@ -0,0 +1,41 @@ +# summary + +List package authorization records. + +# description + +Display subscriber org authorization records. Optionally specify --package to filter the results by package. + +# examples + +- List all subscriber org authorizations: + + <%= config.bin %> <%= command.id %> --target-org AuthoringOrg + +- List subscriber org authorizations for a package: + + <%= config.bin %> <%= command.id %> --package MyPackage --target-org AuthoringOrg + +# flags.package.summary + +Optional ID or alias of the package used to filter the authorization records. + +# columns.subscriber-org + +Subscriber Org + +# columns.subscriber-package + +Subscriber Package + +# columns.status + +Status + +# columns.created-date + +Authorized Date + +# columns.created-by + +Authorizing User diff --git a/package.json b/package.json index 0f217430..905c381b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@oclif/core": "^4", "@salesforce/core": "^9.1.0", "@salesforce/kit": "^4.0.0", - "@salesforce/packaging": "^5.0.4", + "@salesforce/packaging": "5.0.9-spi-dev.0", "@salesforce/sf-plugins-core": "^13.0.0", "@salesforce/ts-types": "^3.0.1", "chalk": "^5.6.2" @@ -109,6 +109,9 @@ }, "installed": { "description": "Command to list installed packages." + }, + "authorize": { + "description": "Commands to manage authorized subscriber orgs for a package." } } } diff --git a/schemas/package-authorize-list.json b/schemas/package-authorize-list.json new file mode 100644 index 00000000..a8b02d1f --- /dev/null +++ b/schemas/package-authorize-list.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/PackageAuthorizeListCommandResult", + "definitions": { + "PackageAuthorizeListCommandResult": { + "type": "array", + "items": { + "$ref": "#/definitions/PackageAuthorizationRecord" + } + }, + "PackageAuthorizationRecord": { + "type": "object", + "properties": { + "Id": { + "type": "string" + }, + "SubscriberOrg": { + "type": "string" + }, + "SubscriberPackageId": { + "type": ["string", "null"] + }, + "Status": { + "type": "string", + "enum": ["Active", "Revoked"] + }, + "CreatedDate": { + "type": "string" + }, + "CreatedById": { + "type": "string" + }, + "CreatedByUsername": { + "type": "string" + } + }, + "required": [ + "Id", + "SubscriberOrg", + "SubscriberPackageId", + "Status", + "CreatedDate", + "CreatedById", + "CreatedByUsername" + ], + "additionalProperties": false + } + } +} diff --git a/src/commands/package/authorize/list.ts b/src/commands/package/authorize/list.ts new file mode 100644 index 00000000..3f3bae93 --- /dev/null +++ b/src/commands/package/authorize/list.ts @@ -0,0 +1,70 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Messages } from '@salesforce/core/messages'; +import { PackageAuthorization, PackageAuthorizationRecord } from '@salesforce/packaging'; +import { + Flags, + loglevel, + orgApiVersionFlagWithDeprecations, + requiredOrgFlagWithDeprecations, + SfCommand, +} from '@salesforce/sf-plugins-core'; +import { maybeGetProject } from '../../../utils/getProject.js'; +import { resolveSubscriberPackageId } from '../../../utils/packageAuthorization.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('@salesforce/plugin-packaging', 'package_authorize_list'); + +export type PackageAuthorizeListCommandResult = PackageAuthorizationRecord[]; + +export class PackageAuthorizeListCommand extends SfCommand { + public static readonly hidden = true; + public static state = 'beta'; + public static readonly summary = messages.getMessage('summary'); + public static readonly description = messages.getMessage('description'); + public static readonly examples = messages.getMessages('examples'); + public static readonly flags = { + loglevel, + 'target-org': requiredOrgFlagWithDeprecations, + 'api-version': orgApiVersionFlagWithDeprecations, + package: Flags.string({ + char: 'p', + summary: messages.getMessage('flags.package.summary'), + }), + }; + + public async run(): Promise { + const { flags } = await this.parse(PackageAuthorizeListCommand); + const connection = flags['target-org'].getConnection(flags['api-version']); + const project = flags.package ? await maybeGetProject() : undefined; + const subscriberPackageId = flags.package + ? await resolveSubscriberPackageId({ packageAliasOrId: flags.package, connection, project }) + : undefined; + const records = await new PackageAuthorization({ connection, subscriberPackageId }).list(); + + this.table({ + data: records, + columns: [ + { key: 'SubscriberOrg', name: messages.getMessage('columns.subscriber-org') }, + { key: 'SubscriberPackageId', name: messages.getMessage('columns.subscriber-package') }, + { key: 'Status', name: messages.getMessage('columns.status') }, + { key: 'CreatedDate', name: messages.getMessage('columns.created-date') }, + { key: 'CreatedByUsername', name: messages.getMessage('columns.created-by') }, + ], + }); + return records; + } +} diff --git a/src/utils/packageAuthorization.ts b/src/utils/packageAuthorization.ts new file mode 100644 index 00000000..9e2a94a4 --- /dev/null +++ b/src/utils/packageAuthorization.ts @@ -0,0 +1,43 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Connection, SfError, SfProject, validateSalesforceId } from '@salesforce/core'; +import { Package } from '@salesforce/packaging'; + +export const resolveSubscriberPackageId = async ({ + packageAliasOrId, + connection, + project, +}: { + packageAliasOrId: string; + connection: Connection; + project?: SfProject; +}): Promise => { + const resolvedPackageId = project?.getPackageIdFromAlias(packageAliasOrId) ?? packageAliasOrId; + if ( + (resolvedPackageId.startsWith('033') || resolvedPackageId.startsWith('0Ho')) && + !validateSalesforceId(resolvedPackageId) + ) { + throw new SfError( + `The package ID ${resolvedPackageId} is invalid. It must be a 15- or 18-character Salesforce ID.` + ); + } + if (resolvedPackageId.startsWith('033')) { + return resolvedPackageId; + } + + const pkg = new Package({ packageAliasOrId, connection, project }); + return pkg.getSubscriberPackageId(); +}; diff --git a/src/utils/subscriberOrg.ts b/src/utils/subscriberOrg.ts new file mode 100644 index 00000000..a3a676e8 --- /dev/null +++ b/src/utils/subscriberOrg.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { readFile } from 'node:fs/promises'; + +export const parseSubscriberOrgList = (subscriberOrgs: string): string[] => + subscriberOrgs.split(',').map((subscriberOrg) => subscriberOrg.trim()); + +export const parseSubscriberOrgFile = async (filePath: string): Promise => { + const contents = await readFile(filePath, 'utf8'); + return contents + .split(/\r?\n/) + .map((line) => line.replace(/#.*/, '').trim()) + .filter(Boolean); +};