Skip to content

Commit

Permalink
Merge pull request #32 from salesforcecli/sm/flags
Browse files Browse the repository at this point in the history
Sm/flags
  • Loading branch information
mdonnalley committed Mar 2, 2022
2 parents 7bf8b96 + c1f95fc commit c831f36
Show file tree
Hide file tree
Showing 16 changed files with 1,025 additions and 63 deletions.
3 changes: 2 additions & 1 deletion .mocharc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"require": "test/init.js, ts-node/register, source-map-support/register",
"watch-extensions": "ts",
"watch-extensions": ["ts", "md"],
"watch-files": ["src", "test", "messages"],
"recursive": true,
"reporter": "spec",
"timeout": 5000
Expand Down
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Attach",
"port": 9229,
"skipFiles": ["<node_internals>/**"]
}
]
}
34 changes: 33 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Description

The @salesforce/sf-pluins-core provides utilities for writing [sf](https://github.com/salesforcecli/cli) plugins.
The @salesforce/sf-plugins-core provides utilities for writing [sf](https://github.com/salesforcecli/cli) plugins.

## SfCommand Abstract Class

Expand Down Expand Up @@ -43,3 +43,35 @@ A general purpose class that prompts a user for information. See [inquirer NPM M
## Flags

Flags is a convenience reference to [@oclif/core#Flags](https://github.com/oclif/core/blob/main/src/flags.ts)

### Specialty Flags

These flags can be imported into a command and used like any other flag. See code examples in the links

- [orgApiVersionFlag](src/flags/apiVersion.ts)
- specifies a Salesforce API version.
- reads from Config (if available)
- validates version is still active
- warns if version if deprecated
- [requiredOrgFlag](src/flags/orgFlags.ts)
- accepts a username or alias
- aware of configuration defaults
- throws if org or default doesn't exist or can't be found
- [optionalOrgFlag](src/flags/orgFlags.ts)
- accepts a username or alias
- aware of configuration defaults
- might be undefined if an org isn't found
- [requiredHubFlag](src/flags/orgFlags.ts)
- accepts a username or alias
- aware of configuration defaults
- throws if org or default doesn't exist or can't be found
- throws if an org is found but is not a dev hub
- [durationFlag](src/flags/duration.ts)
- specify a unit
- optionally specify a min, max, and defaultValue
- returns a [Duration](https://github.com/forcedotcom/kit/blob/main/src/duration.ts)
- can be undefined if you don't set the default
- [salesforceIdFlag](src/flags/salesforceId.ts)
- validates that IDs are valid salesforce ID
- optionally restrict to 15/18 char
- optionally require it to be begin with a certain prefix
54 changes: 53 additions & 1 deletion messages/messages.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,59 @@
# warning.security

This command will expose sensitive information that allows for subsequent activity using your current authenticated session. Sharing this information is equivalent to logging someone in under the current credential, resulting in unintended access and escalation of privilege. For additional information, please review the authorization section of the https://developer.salesforce.com/docs/atlas.en-us.234.0.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_web_flow.htm
This command will expose sensitive information that allows for subsequent activity using your current authenticated session. Sharing this information is equivalent to logging someone in under the current credential, resulting in unintended access and escalation of privilege. For additional information, please review the authorization section of the <https://developer.salesforce.com/docs/atlas.en-us.234.0.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_web_flow.htm>

# errors.RequiresProject

This command is required to run from within a Salesforce project directory.

# errors.InvalidIdLength

The id must be %s characters.

# errors.InvalidId

The id is invalid.

# errors.InvalidPrefix

The id must begin with %s.

# errors.NoDefaultEnv

No default environment found. Use -e or --target-org to specify an environment.

# errors.NoDefaultDevHub

No default dev hub found. Use -v or --target-dev-hub to specify an environment.

# errors.NotADevHub

The specified org %s is not a Dev Hub.

# flags.apiVersion.description

Override the api version used for api requests made by this command

# flags.apiVersion.overrideWarning

apiVersion configuration overridden at %s

# flags.apiVersion.warning.deprecated

API versions up to %s are deprecated. See %s for more information.

# errors.InvalidApiVersion

%s is not a valid API version.

# errors.RetiredApiVersion

The API version must be greater than %s.

# errors.InvalidDuration

The value must be an integer.

# errors.DurationBounds

The value must be between %s and %s (inclusive).
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"/messages"
],
"dependencies": {
"@oclif/core": "^1.2.0",
"@salesforce/core": "3.7.3",
"@oclif/core": "^1.5.0",
"@salesforce/core": "^3.7.6",
"@salesforce/kit": "^1.5.17",
"@salesforce/ts-types": "^1.5.20",
"inquirer": "^8.2.0"
Expand Down
24 changes: 23 additions & 1 deletion src/exported.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,33 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { Flags as OclifFlags } from '@oclif/core';

export { toHelpSection } from './util';
export { Deployable, Deployer } from './deployer';
export { Deauthorizer } from './deauthorizer';
export { Progress, Prompter, generateTableChoices } from './ux';
export { SfHook } from './hooks';
export * from './types';
export { SfCommand, SfCommandInterface } from './sfCommand';
export { Flags } from '@oclif/core';

// custom flags
import { requiredOrgFlag, requiredHubFlag, optionalOrgFlag } from './flags/orgFlags';
import { salesforceIdFlag } from './flags/salesforceId';
import { orgApiVersionFlag } from './flags/orgApiVersion';
import { durationFlag } from './flags/duration';

export const Flags = {
boolean: OclifFlags.boolean,
directory: OclifFlags.directory,
file: OclifFlags.file,
integer: OclifFlags.integer,
string: OclifFlags.string,
url: OclifFlags.url,
duration: durationFlag,
salesforceId: salesforceIdFlag,
orgApiVersion: orgApiVersionFlag,
requiredOrg: requiredOrgFlag,
requiredHub: requiredHubFlag,
optionalOrg: optionalOrgFlag,
};
73 changes: 73 additions & 0 deletions src/flags/duration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2020, 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 { Flags, Interfaces } from '@oclif/core';
import { Messages } from '@salesforce/core';
import { Duration } from '@salesforce/kit';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/sf-plugins-core', 'messages');

type DurationUnit = Lowercase<keyof typeof Duration.Unit>;

export interface DurationFlagConfig extends Partial<Interfaces.OptionFlagProps> {
unit: Required<DurationUnit>;
defaultValue?: number;
min?: number;
max?: number;
}

/**
* Duration flag with built-in default and min/max validation
* You must specify a unit
* Defaults to undefined if you don't specify a default
*
* @example
* import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
* public static flags = {
* 'wait': duration({
* min: 1,
* unit: 'minutes'
* defaultValue: 33,
* char: 'w',
* description: 'Wait time in minutes'
* }),
* }
*/
export const durationFlag = (durationConfig: DurationFlagConfig): Interfaces.OptionFlag<Duration | undefined> => {
const { defaultValue, min, max, unit, ...baseProps } = durationConfig;
return Flags.build<Duration>({
...baseProps,
parse: async (input: string) => validate(input, { min, max, unit }),
default: defaultValue ? async () => toDuration(defaultValue, unit) : undefined,
})();
};

const validate = (input: string, config: DurationFlagConfig): Duration => {
const { min, max, unit } = config || {};
let parsedInput: number;

try {
parsedInput = parseInt(input, 10);
if (typeof parsedInput !== 'number' || isNaN(parsedInput)) {
throw messages.createError('errors.InvalidDuration');
}
} catch (e) {
throw messages.createError('errors.InvalidDuration');
}

if (min && parsedInput < min) {
throw messages.createError('errors.DurationBounds', [min, max]);
}
if (max && parsedInput > max) {
throw messages.createError('errors.DurationBounds', [min, max]);
}
return toDuration(parsedInput, unit);
};

const toDuration = (parsedInput: number, unit: DurationUnit): Duration => {
return Duration[unit](parsedInput);
};
61 changes: 61 additions & 0 deletions src/flags/orgApiVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2020, 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 { Flags } from '@oclif/core';
import { Messages, sfdc, Lifecycle } from '@salesforce/core';

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/sf-plugins-core', 'messages');

// versions below this are retired
export const minValidApiVersion = 21;
// this and all un-retired versions below it are deprecated
export const maxDeprecated = 30;
export const maxDeprecatedUrl = 'https://help.salesforce.com/s/articleView?id=000354473&type=1;';

/**
* apiVersion for a salesforce org's rest api.
* Will validate format and that the api version is still supported.
* Will default to the version specified in Config, if it exists (and will provide an override warning)
*
* CAVEAT: unlike the apiversion flag on sfdxCommand, this does not set the version on the org/connection
* We leave this up to the plugins to implement
*/
export const orgApiVersionFlag = Flags.build<string>({
parse: async (input: string) => validate(input),
default: async () => await getDefaultFromConfig(),
description: messages.getMessage('flags.apiVersion.description'),
});

const getDefaultFromConfig = async (): Promise<string | undefined> => {
// (perf) only import ConfigAggregator if necessary
const { ConfigAggregator } = await import('@salesforce/core');
const config = await ConfigAggregator.create();
const apiVersionFromConfig = config.getInfo('apiVersion')?.value as string;
if (apiVersionFromConfig) {
await Lifecycle.getInstance().emitWarning(
messages.getMessage('flags.apiVersion.overrideWarning', [apiVersionFromConfig])
);
return validate(apiVersionFromConfig);
}
};

const validate = async (input: string): Promise<string> => {
// basic format check
if (!sfdc.validateApiVersion(input)) {
throw messages.createError('errors.InvalidApiVersion', [input]);
}
const requestedVersion = parseInt(input, 10);
if (requestedVersion < minValidApiVersion) {
throw messages.createError('errors.RetiredApiVersion', [minValidApiVersion]);
}
if (requestedVersion <= maxDeprecated) {
await Lifecycle.getInstance().emitWarning(
messages.getMessage('flags.apiVersion.warning.deprecated', [maxDeprecated, maxDeprecatedUrl])
);
}
return input;
};
Loading

0 comments on commit c831f36

Please sign in to comment.