Skip to content

Commit 14b5c5b

Browse files
authored
W-22164863: Address review comments and update backend request param (#11)
* chore: address review comments * chore: addressing PR comment * chore: addressing PR comment
1 parent 8288b93 commit 14b5c5b

8 files changed

Lines changed: 87 additions & 72 deletions

File tree

.eslintrc.cjs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
/*
2-
* Copyright (c) 2020, salesforce.com, inc.
3-
* All rights reserved.
4-
* Licensed under the BSD 3-Clause license.
5-
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6-
*/
71
module.exports = {
82
extends: ['eslint-config-salesforce-typescript', 'eslint-config-salesforce-license', 'plugin:sf-plugin/recommended'],
93
root: true,

.nycrc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
{
2-
"extends": "@salesforce/dev-config/nyc"
2+
"nyc": {
3+
"extends": "@salesforce/dev-config/nyc"
4+
}
35
}

README.md

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,13 @@
1-
**NOTE: This template for sf plugins is not yet official. Please consult with the Platform CLI team before using this template.**
2-
3-
# plugin-license-management
1+
# License Management Plugin
42

53
[![NPM](https://img.shields.io/npm/v/@salesforce/plugin-license-management.svg?label=@salesforce/plugin-license-management)](https://www.npmjs.com/package/@salesforce/plugin-license-management) [![Downloads/week](https://img.shields.io/npm/dw/@salesforce/plugin-license-management.svg)](https://npmjs.org/package/@salesforce/plugin-license-management) [![License](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/license/apache-2-0)
64

7-
## Using the template
8-
9-
This repository provides a template for creating a plugin for the Salesforce CLI. To convert this template to a working plugin:
10-
11-
1. Please get in touch with the Platform CLI team. We want to help you develop your plugin.
12-
2. Generate your plugin:
13-
14-
```
15-
sf plugins install dev
16-
sf dev generate plugin
17-
18-
git init -b main
19-
git add . && git commit -m "chore: initial commit"
20-
```
21-
22-
3. Create your plugin's repo in the salesforcecli github org
23-
4. When you're ready, replace the contents of this README with the information you want.
24-
25-
## Learn about `sf` plugins
26-
27-
Salesforce CLI plugins are based on the [oclif plugin framework](https://oclif.io/docs/introduction). Read the [plugin developer guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_plugins.meta/sfdx_cli_plugins/cli_plugins_architecture_sf_cli.htm) to learn about Salesforce CLI plugin development.
28-
29-
This repository contains a lot of additional scripts and tools to help with general Salesforce node development and enforce coding standards. You should familiarize yourself with some of the [node developer packages](#tooling) used by Salesforce. There is also a default circleci config using the [release management orb](https://github.com/forcedotcom/npm-release-management-orb) standards.
5+
Manage Permission Set Licenses (PSLs) in your scratch orgs. This plugin lets you provision PSL seats into a target scratch org — either one at a time via command line flags, or in bulk using a JSON definition file.
306

31-
Additionally, there are some additional tests that the Salesforce CLI will enforce if this plugin is ever bundled with the CLI. These test are included by default under the `posttest` script and it is required to keep these tests active in your plugin if you plan to have it bundled.
7+
## Before You Begin
328

33-
### Tooling
34-
35-
- [@salesforce/core](https://github.com/forcedotcom/sfdx-core)
36-
- [@salesforce/kit](https://github.com/forcedotcom/kit)
37-
- [@salesforce/sf-plugins-core](https://github.com/salesforcecli/sf-plugins-core)
38-
- [@salesforce/ts-types](https://github.com/forcedotcom/ts-types)
39-
- [@salesforce/ts-sinon](https://github.com/forcedotcom/ts-sinon)
40-
- [@salesforce/dev-config](https://github.com/forcedotcom/dev-config)
41-
- [@salesforce/dev-scripts](https://github.com/forcedotcom/dev-scripts)
42-
43-
# Everything past here is only a suggestion as to what should be in your specific plugin's description
44-
45-
This plugin is bundled with the [Salesforce CLI](https://developer.salesforce.com/tools/sfdxcli). For more information on the CLI, read the [getting started guide](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_intro.htm).
46-
47-
We always recommend using the latest version of these commands bundled with the CLI, however, you can install a specific version or tag if needed.
9+
- Install and authenticate the [Salesforce CLI](https://developer.salesforce.com/tools/sfdxcli).
10+
- Ensure you have the appropriate permissions to manage Permission Set Licenses in the target org.
4811

4912
## Install
5013

@@ -170,12 +133,53 @@ _See code: [src/commands/license/provision.ts](https://github.com/salesforcecli/
170133

171134
# Local Testing
172135

136+
### 1. Log in to your Dev Hub
137+
138+
```bash
139+
sf org login web --set-default-dev-hub --instance-url <dev-hub-url>
140+
```
141+
142+
### 2. Create a scratch org
143+
144+
Before creating the scratch org, update `test/config/scratch-org-def.json` with your values:
145+
146+
| Field | Description |
147+
| ----------- | ------------------------------------ |
148+
| `orgName` | Display name for the scratch org |
149+
| `namespace` | Your package namespace (e.g. `myNS`) |
150+
151+
```bash
152+
sf org create scratch --definition-file test/config/scratch-org-def.json --alias <scratch-org-alias>
153+
```
154+
155+
### 3. Install the package into the scratch org
156+
157+
Make the package available in the scratch org. Some ways to do this include:
158+
159+
**Install a released package version**
160+
161+
```bash
162+
sf package install --package <package-version-id> --target-org <scratch-org-alias>
163+
```
164+
165+
**Push source directly**
166+
173167
```bash
174-
sf org create scratch --target-dev-hub <devhub-alias> --definition-file test/config/scratch-org-def.json
168+
sf project deploy start --target-org <scratch-org-alias>
169+
```
175170

176-
sf package install --package <package-id> --target-org <scratch-org-username>
171+
### 4. Open the scratch org (optional)
177172

178-
sf package install report -i <install-request-id> -o <scratch-org-username>
173+
```bash
174+
sf org open --target-org <scratch-org-alias>
175+
```
176+
177+
### 5. Provision licenses
178+
179+
```bash
180+
# Provision a single PSL
181+
sf license provision --target-org <scratch-org-alias> --namespace <namespace> --license <license-name> --quantity <number>
179182

180-
sf license provision -o <scratch-org-username> --license premium --namespace demo --quantity 10
183+
# Provision multiple PSLs using a definition file
184+
sf license provision --target-org <scratch-org-alias> --definition-file <path-to-definition-file>
181185
```

messages/license.provision.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Provision Permission Set Licenses (PSL) into the target org. Successful executio
88

99
There are two ways to run this command. You can provide the information to identify a single PSL via command line flags, or provision multiple PSLs in a single call by supplying a JSON formatted file.
1010

11-
See <Add URL Here> for the format and options contained within the JSON file.
11+
See https://github.com/salesforcecli/plugin-license-management#sf-license-provision for the format and options contained within the JSON file.
1212

1313
# flags.namespace.summary
1414

@@ -50,7 +50,11 @@ The definition file must contain at least one license entry.
5050

5151
# error.unsupportedDefinitionFileFields
5252

53-
Nonexistent fields: %s
53+
Unknown fields in definition file: %s
54+
55+
# error.invalidDefinitionFileJson
56+
57+
Definition file contains invalid JSON: %s
5458

5559
# error.missingRequiredDefinitionFileFields
5660

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
{
22
"name": "@salesforce/plugin-license-management",
3-
"description": "Manage Permission Set Licenses ",
3+
"description": "Manage Permission Set Licenses",
44
"version": "1.0.0",
55
"author": "Salesforce",
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {
88
"@oclif/core": "^4",
99
"@salesforce/core": "^8.28.1",
10-
"@salesforce/kit": "^3.2.6",
1110
"@salesforce/sf-plugins-core": "^12"
1211
},
1312
"devDependencies": {

src/commands/license/provision.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type ProvisionLicenseSpec = {
2828
};
2929

3030
type ApiLicenseSpec = {
31-
namespacePrefix?: string;
31+
namespace?: string;
3232
permissionSetLicense?: string;
3333
quantity?: number;
3434
};
@@ -60,7 +60,7 @@ function getLicenseDefinitionName(spec: ProvisionLicenseSpec): string {
6060

6161
function toApiSpec(spec: ProvisionLicenseSpec): ApiLicenseSpec {
6262
return {
63-
namespacePrefix: spec.namespace,
63+
namespace: spec.namespace,
6464
permissionSetLicense: spec.license,
6565
quantity: spec.quantity,
6666
};
@@ -77,8 +77,8 @@ export default class LicenseProvision extends SfCommand<LicenseProvisionResult>
7777
namespace: Flags.string({
7878
char: 'n',
7979
summary: messages.getMessage('flags.namespace.summary'),
80-
dependsOn: ['license'],
8180
exclusive: ['definition-file'],
81+
relationships: [{ type: 'all', flags: ['license', 'quantity'] }],
8282
}),
8383
license: Flags.string({
8484
char: 'l',
@@ -91,20 +91,26 @@ export default class LicenseProvision extends SfCommand<LicenseProvisionResult>
9191
summary: messages.getMessage('flags.quantity.summary'),
9292
min: 0,
9393
max: Number.MAX_SAFE_INTEGER,
94-
dependsOn: ['license'],
9594
exclusive: ['definition-file'],
95+
relationships: [{ type: 'all', flags: ['namespace', 'license'] }],
9696
}),
97-
'definition-file': Flags.string({
97+
'definition-file': Flags.file({
9898
char: 'f',
9999
summary: messages.getMessage('flags.definition-file.summary'),
100100
exclusive: ['license', 'namespace', 'quantity'],
101+
exists: true,
101102
}),
102103
};
103104

104105
// Protected to allow stubbing in tests
105106
protected static async loadSpecsFromFile(filePath: string): Promise<ProvisionLicenseSpec[]> {
106107
const fileContent = await readFile(filePath, 'utf-8');
107-
const definition = JSON.parse(fileContent) as DefinitionFile;
108+
let definition: DefinitionFile;
109+
try {
110+
definition = JSON.parse(fileContent) as DefinitionFile;
111+
} catch (e) {
112+
throw messages.createError('error.invalidDefinitionFileJson', [(e as Error).message]);
113+
}
108114

109115
if (!Array.isArray(definition.licenses) || definition.licenses.length === 0) {
110116
throw messages.createError('error.emptyDefinitionFile');

test/.eslintrc.cjs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
/*
2-
* Copyright (c) 2020, salesforce.com, inc.
3-
* All rights reserved.
4-
* Licensed under the BSD 3-Clause license.
5-
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6-
*/
7-
81
module.exports = {
92
extends: '../.eslintrc.cjs',
103
// Allow describe and it

test/commands/license/provision.test.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ describe('license provision', () => {
107107
const body = JSON.parse(callArgs.body) as { licenses: unknown[] };
108108
expect(body.licenses).to.have.length(1);
109109
expect(body.licenses[0]).to.deep.include({
110-
namespacePrefix: 'demo',
110+
namespace: 'demo',
111111
permissionSetLicense: 'newLicense',
112112
quantity: 5,
113113
});
@@ -285,7 +285,20 @@ describe('license provision', () => {
285285
await LicenseProvision.run(['--target-org', testOrg.username, '--definition-file', tmpFilePath]);
286286
expect.fail('Expected an error to be thrown');
287287
} catch (error: unknown) {
288-
expect((error as Error).message).to.include('Nonexistent fields: startDate, endDate');
288+
expect((error as Error).message).to.include('Unknown fields in definition file: startDate, endDate');
289+
} finally {
290+
await unlink(tmpFilePath).catch(() => {});
291+
}
292+
});
293+
294+
it('throws a user-friendly error when definition file contains invalid JSON', async () => {
295+
const tmpFilePath = join(tmpdir(), `provision-malformed-${Date.now()}.json`);
296+
await writeFile(tmpFilePath, '{ this is not valid json }');
297+
try {
298+
await LicenseProvision.run(['--target-org', testOrg.username, '--definition-file', tmpFilePath]);
299+
expect.fail('Expected an error to be thrown');
300+
} catch (error: unknown) {
301+
expect((error as Error).message).to.include('invalid JSON');
289302
} finally {
290303
await unlink(tmpFilePath).catch(() => {});
291304
}

0 commit comments

Comments
 (0)