@salesforce/core 3.21.4 → 3.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [3.22.0](https://github.com/forcedotcom/sfdx-core/compare/v3.21.6...v3.22.0) (2022-06-23)
6
+
7
+ ### Features
8
+
9
+ - modify uniqid to accept length and template ([#604](https://github.com/forcedotcom/sfdx-core/issues/604)) ([1fd1b5c](https://github.com/forcedotcom/sfdx-core/commit/1fd1b5c40ab7f9b4d66eada94f3ecfa149b263b7))
10
+
11
+ ### [3.21.6](https://github.com/forcedotcom/sfdx-core/compare/v3.21.5...v3.21.6) (2022-06-23)
12
+
13
+ ### [3.21.5](https://github.com/forcedotcom/sfdx-core/compare/v3.21.4...v3.21.5) (2022-06-23)
14
+
15
+ ### Bug Fixes
16
+
17
+ - bump jsforce ([b1e8604](https://github.com/forcedotcom/sfdx-core/commit/b1e8604203b09df7b252fd6520fb73405e287aa4))
18
+
5
19
  ### [3.21.4](https://github.com/forcedotcom/sfdx-core/compare/v3.21.3...v3.21.4) (2022-06-22)
6
20
 
7
21
  ### Bug Fixes
package/README.md CHANGED
@@ -1,9 +1,19 @@
1
1
  [![NPM](https://img.shields.io/npm/v/@salesforce/core.svg)](https://www.npmjs.com/package/@salesforce/core)
2
2
  [![CircleCI](https://circleci.com/gh/forcedotcom/sfdx-core.svg?style=svg&circle-token=2377ca31221869e9d13448313620486da80e595f)](https://circleci.com/gh/forcedotcom/sfdx-core)
3
3
 
4
+ - [Description](#description)
5
+ - [Usage](#usage)
6
+ - [Contributing](#contributing)
7
+ - [Using TestSetup](#using-testsetup)
8
+ - [Mocking Authorizations](#mocking-authorizations)
9
+ - [Mocking Config Files](#mocking-config-files)
10
+ - [Using the Built-in Sinon Sandboxes](#using-the-built-in-sinon-sandboxes)
11
+ - [Testing Expected Failures](#testing-expected-failures)
12
+ - [Testing Log Lines](#testing-log-lines)
13
+
4
14
  # Description
5
15
 
6
- The @salesforce/core library provides client-side management of Salesforce DX projects, org authentication, connections to Salesforce APIs, and other utilities. Much of the core functionality that powers the Salesforcedx plug-ins comes from this library. You can use this functionality in your plug-ins too.
16
+ The @salesforce/core library provides client-side management of Salesforce DX projects, org authentication, connections to Salesforce APIs, and other utilities. Much of the core functionality that powers the Salesforce CLI plugins comes from this library. You can use this functionality in your plugins too.
7
17
 
8
18
  # Usage
9
19
 
@@ -13,16 +23,11 @@ See the [API documentation](https://forcedotcom.github.io/sfdx-core/).
13
23
 
14
24
  If you are interested in contributing, please take a look at the [CONTRIBUTING](CONTRIBUTING.md) guide.
15
25
 
16
- # Related Docs and Repositories
17
-
18
- - [@salesforce/command](https://github.com/forcedotcom/cli-packages/tree/main/packages/command) - Contains base Salesforce CLI command, `SfdxCommand`.
19
- - [@salesforce/plugin-generator](https://github.com/forcedotcom/sfdx-plugin-generate) - The generator plug-in for building plug-ins for Salesforce CLI.
20
-
21
26
  # Using TestSetup
22
27
 
23
28
  The Salesforce DX Core Library provides a unit testing utility to help with mocking and sand-boxing core components. This feature allows unit tests to execute without needing to make API calls to salesforce.com.
24
29
 
25
- ## Mocking AuthInfo
30
+ ## Mocking Authorizations
26
31
 
27
32
  Here you can mock authorization for a Salesforce scratch org.
28
33
 
@@ -77,6 +82,43 @@ describe('Mocking a force server call', () => {
77
82
  });
78
83
  ```
79
84
 
85
+ ## Mocking Config Files
86
+
87
+ You can mock the contents of various config files
88
+
89
+ ```typescript
90
+ import { strictEqual } from 'assert';
91
+ import { MockTestOrgData, testSetup } from '@salesforce/core/lib/testSetup';
92
+ import { StateAggregator, OrgConfigProperties } from '@salesforce/core';
93
+
94
+ const $$ = testSetup();
95
+
96
+ describe('Mocking Aliases', () => {
97
+ it('example', async () => {
98
+ const testData = new MockTestOrgData();
99
+ await $$.stubAliases({ myAlias: testData.username });
100
+ const alias = (await StateAggregator.getInstance()).aliases.get(testData.username);
101
+ strictEqual(alias, 'myAlais');
102
+ });
103
+ });
104
+
105
+ describe('Mocking Config', () => {
106
+ it('example', async () => {
107
+ const testData = new MockTestOrgData();
108
+ await $$.stubConfig({ [OrgConfigProperties.TARGET_ORG]: testData.username });
109
+ const {value} = (await ConfigAggregator.create()).getInfo(OrgConfigProperties.TARGET_ORG);
110
+ strictEqual(value, testData.username);
111
+ });
112
+ });
113
+
114
+ describe('Mocking Arbitrary Config Files', () => {
115
+ it('example', async () => {
116
+ // MyConfigFile must extend the ConfigFile class in order for this to work properly.
117
+ $$.setConfigStubContents('MyConfigFile', { contents: { foo: 'bar' } });
118
+ });
119
+ });
120
+ ```
121
+
80
122
  ## Using the Built-in Sinon Sandboxes
81
123
 
82
124
  sfdx-core uses Sinon as its underlying mocking system. If you're unfamiliar with Sinon and it's sandboxing concept you can find more information here:
@@ -126,6 +168,30 @@ describe('Testing for expected errors', () => {
126
168
  });
127
169
  ```
128
170
 
171
+ You can also use `shouldThrowSync` for syncrhonous functions you expect to fail
172
+
173
+ ```typescript
174
+ import { SfError } from '@salesforce/core';
175
+ import { shouldThrowSync } from '@salesforce/core/lib/testSetup';
176
+ import { strictEqual } from 'assert';
177
+
178
+ class TestObject {
179
+ public static method() {
180
+ throw new SfError('Error', 'ExpectedError');
181
+ }
182
+ }
183
+
184
+ describe('Testing for expected errors', () => {
185
+ it('example', async () => {
186
+ try {
187
+ shouldThrowSync(() => TestObject.method());
188
+ } catch (e) {
189
+ strictEqual(e.name, 'ExpectedError');
190
+ }
191
+ });
192
+ });
193
+ ```
194
+
129
195
  ## Testing Log Lines
130
196
 
131
197
  It's also useful to check expected values and content from log lines. TestSetup configures the sfdx-core logger to use an in memory LogLine storage structure. These can be easily accessed from tests.
@@ -151,9 +217,9 @@ class TestObject {
151
217
 
152
218
  describe('Testing log lines', () => {
153
219
  it('example', async () => {
154
- const obj: TestObject = new TestObject($$.TEST_LOGGER);
220
+ const obj = new TestObject($$.TEST_LOGGER);
155
221
  obj.method();
156
- const records: LogLine[] = $$.TEST_LOGGER.getBufferedRecords();
222
+ const records = $$.TEST_LOGGER.getBufferedRecords();
157
223
  strictEqual(records.length, 1);
158
224
  strictEqual(records[0].msg, TEST_STRING);
159
225
  });
package/lib/exported.d.ts CHANGED
@@ -2,8 +2,8 @@ export { OAuth2Config } from 'jsforce';
2
2
  export { ConfigFile } from './config/configFile';
3
3
  export { TTLConfig } from './config/ttlConfig';
4
4
  export { envVars, EnvironmentVariable, SUPPORTED_ENV_VARS, EnvVars } from './config/envVars';
5
- export { BaseConfigStore, ConfigContents, ConfigEntry, ConfigStore, ConfigValue } from './config/configStore';
6
- export { GlobalInfo, SfEntry, SfInfo, SfInfoKeys, SfOrg, SfOrgs, SfToken, SfTokens, StateAggregator, } from './stateAggregator';
5
+ export { ConfigContents, ConfigEntry, ConfigStore, ConfigValue } from './config/configStore';
6
+ export { GlobalInfo, SfInfo, SfInfoKeys, SfOrg, SfOrgs, SfToken, SfTokens, StateAggregator } from './stateAggregator';
7
7
  export { DeviceOauthService, DeviceCodeResponse, DeviceCodePollingResponse } from './deviceOauthService';
8
8
  export { OrgUsersConfig } from './config/orgUsersConfig';
9
9
  export { ConfigPropertyMeta, ConfigPropertyMetaInput, Config, SfdxPropertyKeys, SfConfigProperties, SFDX_ALLOWED_PROPERTIES, SF_ALLOWED_PROPERTIES, } from './config/config';
package/lib/exported.js CHANGED
@@ -16,8 +16,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
17
17
  };
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.MyDomainResolver = exports.StreamingClient = exports.CometClient = exports.PollingClient = exports.SfdxError = exports.SfError = exports.SchemaValidator = exports.SchemaPrinter = exports.SfdxProjectJson = exports.SfdxProject = exports.SfProjectJson = exports.SfProject = exports.ORG_CONFIG_ALLOWED_PROPERTIES = exports.OrgConfigProperties = exports.OrgTypes = exports.SandboxEvents = exports.Org = exports.Messages = exports.Logger = exports.LoggerLevel = exports.getJwtAudienceUrl = exports.SfdcUrl = exports.WebOAuthServer = exports.Lifecycle = exports.Global = exports.Mode = exports.SFDX_HTTP_HEADERS = exports.Connection = exports.AuthRemover = exports.AuthInfo = exports.SfdxConfigAggregator = exports.ConfigAggregator = exports.SandboxRequestCache = exports.SF_ALLOWED_PROPERTIES = exports.SFDX_ALLOWED_PROPERTIES = exports.SfConfigProperties = exports.SfdxPropertyKeys = exports.Config = exports.OrgUsersConfig = exports.DeviceOauthService = exports.StateAggregator = exports.SfInfoKeys = exports.GlobalInfo = exports.BaseConfigStore = exports.EnvVars = exports.SUPPORTED_ENV_VARS = exports.EnvironmentVariable = exports.envVars = exports.TTLConfig = exports.ConfigFile = void 0;
20
- exports.ScratchOrgCache = exports.scratchOrgLifecycleStages = exports.scratchOrgLifecycleEventName = exports.scratchOrgResume = exports.scratchOrgCreate = exports.PermissionSetAssignment = exports.User = exports.REQUIRED_FIELDS = exports.DefaultUserFields = void 0;
19
+ exports.DefaultUserFields = exports.MyDomainResolver = exports.StreamingClient = exports.CometClient = exports.PollingClient = exports.SfdxError = exports.SfError = exports.SchemaValidator = exports.SchemaPrinter = exports.SfdxProjectJson = exports.SfdxProject = exports.SfProjectJson = exports.SfProject = exports.ORG_CONFIG_ALLOWED_PROPERTIES = exports.OrgConfigProperties = exports.OrgTypes = exports.SandboxEvents = exports.Org = exports.Messages = exports.Logger = exports.LoggerLevel = exports.getJwtAudienceUrl = exports.SfdcUrl = exports.WebOAuthServer = exports.Lifecycle = exports.Global = exports.Mode = exports.SFDX_HTTP_HEADERS = exports.Connection = exports.AuthRemover = exports.AuthInfo = exports.SfdxConfigAggregator = exports.ConfigAggregator = exports.SandboxRequestCache = exports.SF_ALLOWED_PROPERTIES = exports.SFDX_ALLOWED_PROPERTIES = exports.SfConfigProperties = exports.SfdxPropertyKeys = exports.Config = exports.OrgUsersConfig = exports.DeviceOauthService = exports.StateAggregator = exports.SfInfoKeys = exports.GlobalInfo = exports.EnvVars = exports.SUPPORTED_ENV_VARS = exports.EnvironmentVariable = exports.envVars = exports.TTLConfig = exports.ConfigFile = void 0;
20
+ exports.ScratchOrgCache = exports.scratchOrgLifecycleStages = exports.scratchOrgLifecycleEventName = exports.scratchOrgResume = exports.scratchOrgCreate = exports.PermissionSetAssignment = exports.User = exports.REQUIRED_FIELDS = void 0;
21
21
  const messages_1 = require("./messages");
22
22
  messages_1.Messages.importMessagesDirectory(__dirname);
23
23
  var configFile_1 = require("./config/configFile");
@@ -29,8 +29,6 @@ Object.defineProperty(exports, "envVars", { enumerable: true, get: function () {
29
29
  Object.defineProperty(exports, "EnvironmentVariable", { enumerable: true, get: function () { return envVars_1.EnvironmentVariable; } });
30
30
  Object.defineProperty(exports, "SUPPORTED_ENV_VARS", { enumerable: true, get: function () { return envVars_1.SUPPORTED_ENV_VARS; } });
31
31
  Object.defineProperty(exports, "EnvVars", { enumerable: true, get: function () { return envVars_1.EnvVars; } });
32
- var configStore_1 = require("./config/configStore");
33
- Object.defineProperty(exports, "BaseConfigStore", { enumerable: true, get: function () { return configStore_1.BaseConfigStore; } });
34
32
  var stateAggregator_1 = require("./stateAggregator");
35
33
  Object.defineProperty(exports, "GlobalInfo", { enumerable: true, get: function () { return stateAggregator_1.GlobalInfo; } });
36
34
  Object.defineProperty(exports, "SfInfoKeys", { enumerable: true, get: function () { return stateAggregator_1.SfInfoKeys; } });
@@ -8,6 +8,7 @@ export declare type OAuth2Config = JsforceOAuth2Config & {
8
8
  authCode?: string;
9
9
  refreshToken?: string;
10
10
  loginUrl?: string;
11
+ username?: string;
11
12
  };
12
13
  /**
13
14
  * Fields for authorization, org, and local information.
@@ -289,6 +290,7 @@ export declare class AuthInfo extends AsyncOptionalCreatable<AuthInfo.Options> {
289
290
  private loadDecryptedAuthFromConfig;
290
291
  private isTokenOptions;
291
292
  private refreshFn;
293
+ private readJwtKey;
292
294
  private authJwt;
293
295
  private tryJwtAuth;
294
296
  private buildRefreshTokenConfig;
@@ -522,7 +522,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
522
522
  async init() {
523
523
  this.stateAggregator = await stateAggregator_1.StateAggregator.getInstance();
524
524
  const username = this.options.username;
525
- const authOptions = this.options.oauth2Options || this.options.accessTokenOptions;
525
+ const authOptions = (this.options.oauth2Options || this.options.accessTokenOptions);
526
526
  // Must specify either username and/or options
527
527
  if (!username && !authOptions) {
528
528
  throw messages.createError('authInfoCreationError');
@@ -534,7 +534,7 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
534
534
  throw messages.createError('authInfoOverwriteError');
535
535
  }
536
536
  }
537
- const oauthUsername = username || (0, ts_types_1.getString)(authOptions, 'username');
537
+ const oauthUsername = username || (authOptions === null || authOptions === void 0 ? void 0 : authOptions.username);
538
538
  if (oauthUsername) {
539
539
  this.username = oauthUsername;
540
540
  await this.stateAggregator.orgs.read(oauthUsername, false, false);
@@ -562,9 +562,9 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
562
562
  }
563
563
  }
564
564
  getInstanceUrl(options, aggregator) {
565
- const instanceUrl = (0, ts_types_1.getString)(options, 'instanceUrl') ||
566
- aggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL);
567
- return instanceUrl || sfdcUrl_1.SfdcUrl.PRODUCTION;
565
+ var _a;
566
+ const instanceUrl = (_a = options === null || options === void 0 ? void 0 : options.instanceUrl) !== null && _a !== void 0 ? _a : aggregator.getPropertyValue(orgConfigProperties_1.OrgConfigProperties.ORG_INSTANCE_URL);
567
+ return instanceUrl !== null && instanceUrl !== void 0 ? instanceUrl : sfdcUrl_1.SfdcUrl.PRODUCTION;
568
568
  }
569
569
  /**
570
570
  * Initialize this AuthInfo instance with the specified options. If options are not provided, initialize it from cache
@@ -668,15 +668,19 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
668
668
  return await callback(error);
669
669
  }
670
670
  }
671
+ async readJwtKey(keyFile) {
672
+ return fs.promises.readFile(keyFile, 'utf8');
673
+ }
671
674
  // Build OAuth config for a JWT auth flow
672
675
  async authJwt(options) {
676
+ var _a;
673
677
  if (!options.clientId) {
674
678
  throw messages.createError('missingClientId');
675
679
  }
676
- const privateKeyContents = await fs.promises.readFile((0, ts_types_1.ensure)(options.privateKey), 'utf8');
680
+ const privateKeyContents = await this.readJwtKey((0, ts_types_1.ensureString)(options.privateKey));
677
681
  const { loginUrl = sfdcUrl_1.SfdcUrl.PRODUCTION } = options;
678
682
  const url = new sfdcUrl_1.SfdcUrl(loginUrl);
679
- const createdOrgInstance = (0, ts_types_1.getString)(options, 'createdOrgInstance', '').trim().toLowerCase();
683
+ const createdOrgInstance = ((_a = this.getFields().createdOrgInstance) !== null && _a !== void 0 ? _a : '').trim().toLowerCase();
680
684
  const audienceUrl = await url.getJwtAudienceUrl(createdOrgInstance);
681
685
  let authFieldsBuilder;
682
686
  const authErrors = [];
@@ -751,7 +755,6 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
751
755
  const { orgId } = parseIdUrl(authFieldsBuilder.id);
752
756
  let username = this.getUsername();
753
757
  if (!username) {
754
- // @ts-ignore
755
758
  const userInfo = await this.retrieveUserInfo(authFieldsBuilder.instance_url, authFieldsBuilder.access_token);
756
759
  username = (0, ts_types_1.ensureString)(userInfo === null || userInfo === void 0 ? void 0 : userInfo.username);
757
760
  }
@@ -854,18 +857,16 @@ class AuthInfo extends kit_1.AsyncOptionalCreatable {
854
857
  * @private
855
858
  */
856
859
  throwUserGetException(response) {
857
- var _a;
860
+ var _a, _b, _c;
858
861
  let errorMsg = '';
859
- const bodyAsString = (0, ts_types_1.getString)(response, 'body', JSON.stringify({ message: 'UNKNOWN', errorCode: 'UNKNOWN' }));
862
+ const bodyAsString = (_a = response.body) !== null && _a !== void 0 ? _a : JSON.stringify({ message: 'UNKNOWN', errorCode: 'UNKNOWN' });
860
863
  try {
861
864
  const body = (0, kit_1.parseJson)(bodyAsString);
862
865
  if ((0, ts_types_1.isArray)(body)) {
863
- errorMsg = body
864
- .map((line) => { var _a; return (_a = (0, ts_types_1.getString)(line, 'message')) !== null && _a !== void 0 ? _a : (0, ts_types_1.getString)(line, 'errorCode', 'UNKNOWN'); })
865
- .join(os.EOL);
866
+ errorMsg = body.map((line) => { var _a, _b; return (_b = (_a = line.message) !== null && _a !== void 0 ? _a : line.errorCode) !== null && _b !== void 0 ? _b : 'UNKNOWN'; }).join(os.EOL);
866
867
  }
867
868
  else {
868
- errorMsg = (_a = (0, ts_types_1.getString)(body, 'message')) !== null && _a !== void 0 ? _a : (0, ts_types_1.getString)(body, 'errorCode', 'UNKNOWN');
869
+ errorMsg = (_c = (_b = body.message) !== null && _b !== void 0 ? _b : body.errorCode) !== null && _c !== void 0 ? _c : 'UNKNOWN';
869
870
  }
870
871
  }
871
872
  catch (err) {
@@ -380,7 +380,7 @@ class Connection extends jsforce_1.Connection {
380
380
  async loadInstanceApiVersion() {
381
381
  const authFileFields = this.options.authInfo.getFields();
382
382
  const lastCheckedDateString = authFileFields.instanceApiVersionLastRetrieved;
383
- let version = (0, ts_types_1.getString)(authFileFields, 'instanceApiVersion');
383
+ let version = authFileFields.instanceApiVersion;
384
384
  let lastChecked;
385
385
  try {
386
386
  if (lastCheckedDateString && (0, ts_types_1.isString)(lastCheckedDateString)) {
package/lib/org/org.js CHANGED
@@ -292,7 +292,7 @@ class Org extends kit_1.AsyncOptionalCreatable {
292
292
  const DEV_HUB_SOQL = `SELECT CreatedDate,Edition,ExpirationDate FROM ActiveScratchOrg WHERE ScratchOrg='${trimmedId}'`;
293
293
  try {
294
294
  const results = await devHubConnection.query(DEV_HUB_SOQL);
295
- if ((0, ts_types_1.getNumber)(results, 'records.length') !== 1) {
295
+ if (results.records.length !== 1) {
296
296
  throw new sfError_1.SfError('No results', 'NoResultsError');
297
297
  }
298
298
  }
@@ -49,6 +49,7 @@ class PermissionSetAssignment {
49
49
  * @param permSetString An array of permission set names.
50
50
  */
51
51
  async create(id, permSetString) {
52
+ var _a;
52
53
  if (!id) {
53
54
  throw messages.createError('userIdRequired');
54
55
  }
@@ -61,7 +62,7 @@ class PermissionSetAssignment {
61
62
  query += ` AND NamespacePrefix='${nsPrefix}'`;
62
63
  }
63
64
  const result = await this.org.getConnection().query(query);
64
- const permissionSetId = (0, ts_types_1.getString)(result, 'records[0].Id');
65
+ const permissionSetId = (_a = result === null || result === void 0 ? void 0 : result.records[0]) === null || _a === void 0 ? void 0 : _a.Id;
65
66
  if (!permissionSetId) {
66
67
  if (nsPrefix) {
67
68
  throw messages.createError('assignCommandPermissionSetNotFoundForNSError', [permSetName, nsPrefix]);
@@ -142,7 +142,7 @@ const scratchOrgCreate = async (options) => {
142
142
  (0, scratchOrgInfoApi_1.requestScratchOrgCreation)(hubOrg, scratchOrgInfo, settingsGenerator),
143
143
  getSignupTargetLoginUrl(),
144
144
  ]);
145
- const scratchOrgInfoId = (0, ts_types_1.ensureString)((0, ts_types_1.getString)(scratchOrgInfoRequestResult, 'id'));
145
+ const scratchOrgInfoId = (0, ts_types_1.ensureString)(scratchOrgInfoRequestResult.id);
146
146
  const cache = await scratchOrgCache_1.ScratchOrgCache.create();
147
147
  cache.set(scratchOrgInfoId, {
148
148
  hubUsername: hubOrg.getUsername(),
@@ -8,16 +8,15 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.updateRevisionCounterToZero = exports.resolveUrl = exports.deploySettings = exports.pollForScratchOrgInfo = exports.requestScratchOrgCreation = exports.authorizeScratchOrg = exports.queryScratchOrgInfo = void 0;
10
10
  const kit_1 = require("@salesforce/kit");
11
- const ts_types_1 = require("@salesforce/ts-types");
12
11
  const ts_retry_promise_1 = require("ts-retry-promise");
13
12
  const logger_1 = require("../logger");
14
- const mapKeys_1 = require("../util/mapKeys");
15
13
  const messages_1 = require("../messages");
16
14
  const sfError_1 = require("../sfError");
17
15
  const sfdcUrl_1 = require("../util/sfdcUrl");
18
16
  const pollingClient_1 = require("../status/pollingClient");
19
17
  const myDomainResolver_1 = require("../status/myDomainResolver");
20
18
  const lifecycleEvents_1 = require("../lifecycleEvents");
19
+ const mapKeys_1 = require("../util/mapKeys");
21
20
  const authInfo_1 = require("./authInfo");
22
21
  const org_1 = require("./org");
23
22
  const scratchOrgErrorCodes_1 = require("./scratchOrgErrorCodes");
@@ -195,7 +194,7 @@ const checkOrgDoesntExist = async (scratchOrgInfo) => {
195
194
  if (!usernameKey) {
196
195
  return;
197
196
  }
198
- const username = (0, ts_types_1.getString)(scratchOrgInfo, usernameKey);
197
+ const username = scratchOrgInfo[usernameKey];
199
198
  if (username && username.length > 0) {
200
199
  try {
201
200
  await authInfo_1.AuthInfo.create({ username: username.toLowerCase() });
package/lib/org/user.js CHANGED
@@ -86,16 +86,16 @@ async function retrieveUserFields(logger, username) {
86
86
  if (result.totalSize === 1) {
87
87
  const results = (0, kit_1.mapKeys)(result.records[0], (value, key) => (0, kit_1.lowerFirst)(key));
88
88
  const fields = {
89
- id: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.id)),
89
+ id: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.id]),
90
90
  username,
91
- alias: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.alias)),
92
- email: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.email)),
93
- emailEncodingKey: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.emailEncodingKey)),
94
- languageLocaleKey: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.languageLocaleKey)),
95
- localeSidKey: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.localeSidKey)),
96
- profileId: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.profileId)),
97
- lastName: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.lastName)),
98
- timeZoneSidKey: (0, ts_types_1.ensure)((0, ts_types_1.getString)(results, exports.REQUIRED_FIELDS.timeZoneSidKey)),
91
+ alias: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.alias]),
92
+ email: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.email]),
93
+ emailEncodingKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.emailEncodingKey]),
94
+ languageLocaleKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.languageLocaleKey]),
95
+ localeSidKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.localeSidKey]),
96
+ profileId: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.profileId]),
97
+ lastName: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.lastName]),
98
+ timeZoneSidKey: (0, ts_types_1.ensureString)(results[exports.REQUIRED_FIELDS.timeZoneSidKey]),
99
99
  };
100
100
  return fields;
101
101
  }
package/lib/sfProject.js CHANGED
@@ -68,13 +68,17 @@ class SfProjectJson extends configFile_1.ConfigFile {
68
68
  return contents;
69
69
  }
70
70
  async write(newContents) {
71
- this.setContents(newContents);
71
+ if (newContents) {
72
+ this.setContents(newContents);
73
+ }
72
74
  this.validateKeys();
73
75
  await this.schemaValidate();
74
76
  return super.write();
75
77
  }
76
78
  writeSync(newContents) {
77
- this.setContents(newContents);
79
+ if (newContents) {
80
+ this.setContents(newContents);
81
+ }
78
82
  this.validateKeys();
79
83
  this.schemaValidateSync();
80
84
  return super.writeSync();
@@ -25,18 +25,87 @@ export declare abstract class BaseOrgAccessor<T extends ConfigFile, P extends Co
25
25
  private configs;
26
26
  private contents;
27
27
  private logger;
28
+ /**
29
+ * Read the auth file for the given useranme. Once the file has been read, it can be reaccessed with the `get` method.
30
+ *
31
+ * @param username username to read
32
+ * @param decrypt if true, decrypt encrypted values
33
+ * @param throwOnNotFound throw if file is not found for username
34
+ */
28
35
  read(username: string, decrypt?: boolean, throwOnNotFound?: boolean): Promise<Nullable<P>>;
36
+ /**
37
+ * Read all the auth files under the global state directory
38
+ *
39
+ * @param decrypt if true, decrypt encrypted values
40
+ */
29
41
  readAll(decrypt?: boolean): Promise<P[]>;
42
+ /**
43
+ * Return the contents of the username's auth file from cache. The `read` or `readAll` methods must be called first in order to populate the cache.
44
+ *
45
+ * @param username username to get
46
+ * @param decrypt if true, decrypt encrypted values
47
+ */
30
48
  get(username: string, decrypt?: boolean): Nullable<P>;
49
+ /**
50
+ * Return the contents of all the auth files from cache. The `read` or `readAll` methods must be called first in order to populate the cache.
51
+ *
52
+ * @param decrypt if true, decrypt encrypted values
53
+ * @returns
54
+ */
31
55
  getAll(decrypt?: boolean): P[];
56
+ /**
57
+ * Returns true if the username has been cached.
58
+ *
59
+ * @param username
60
+ */
32
61
  has(username: string): boolean;
62
+ /**
63
+ * Returns true if there is an auth file for the given username. The `read` or `readAll` methods must be called first in order to populate the cache.
64
+ *
65
+ * @param username
66
+ */
33
67
  exists(username: string): Promise<boolean>;
68
+ /**
69
+ * Return the file stats for a given userame's auth file.
70
+ *
71
+ * @param username
72
+ */
34
73
  stat(username: string): Promise<Nullable<fs.Stats>>;
74
+ /**
75
+ * Returns true if there is an auth file for the given username
76
+ *
77
+ * @param username
78
+ */
35
79
  hasFile(username: string): Promise<boolean>;
80
+ /**
81
+ * Return all auth files under the global state directory.
82
+ */
36
83
  list(): Promise<string[]>;
84
+ /**
85
+ * Set the contents for a given username.
86
+ *
87
+ * @param username
88
+ * @param org
89
+ */
37
90
  set(username: string, org: P): void;
91
+ /**
92
+ * Update the contents for a given username.
93
+ *
94
+ * @param username
95
+ * @param org
96
+ */
38
97
  update(username: string, org: Partial<P> & JsonMap): void;
98
+ /**
99
+ * Delete the auth file for a given username.
100
+ *
101
+ * @param username
102
+ */
39
103
  remove(username: string): Promise<void>;
104
+ /**
105
+ * Write the contents of the auth file for a given username.
106
+ *
107
+ * @param username
108
+ */
40
109
  write(username: string): Promise<Nullable<P>>;
41
110
  protected init(): Promise<void>;
42
111
  private getAllFiles;
@@ -63,6 +63,13 @@ class BaseOrgAccessor extends kit_1.AsyncOptionalCreatable {
63
63
  this.configs = new Map();
64
64
  this.contents = new Map();
65
65
  }
66
+ /**
67
+ * Read the auth file for the given useranme. Once the file has been read, it can be reaccessed with the `get` method.
68
+ *
69
+ * @param username username to read
70
+ * @param decrypt if true, decrypt encrypted values
71
+ * @param throwOnNotFound throw if file is not found for username
72
+ */
66
73
  async read(username, decrypt = false, throwOnNotFound = true) {
67
74
  try {
68
75
  const config = await this.initAuthFile(username, throwOnNotFound);
@@ -73,6 +80,11 @@ class BaseOrgAccessor extends kit_1.AsyncOptionalCreatable {
73
80
  return null;
74
81
  }
75
82
  }
83
+ /**
84
+ * Read all the auth files under the global state directory
85
+ *
86
+ * @param decrypt if true, decrypt encrypted values
87
+ */
76
88
  async readAll(decrypt = false) {
77
89
  const fileChunks = chunk(await this.getAllFiles(), 50);
78
90
  for (const fileChunk of fileChunks) {
@@ -85,6 +97,12 @@ class BaseOrgAccessor extends kit_1.AsyncOptionalCreatable {
85
97
  }
86
98
  return this.getAll(decrypt);
87
99
  }
100
+ /**
101
+ * Return the contents of the username's auth file from cache. The `read` or `readAll` methods must be called first in order to populate the cache.
102
+ *
103
+ * @param username username to get
104
+ * @param decrypt if true, decrypt encrypted values
105
+ */
88
106
  get(username, decrypt = false) {
89
107
  const config = this.configs.get(username);
90
108
  if (config) {
@@ -92,23 +110,49 @@ class BaseOrgAccessor extends kit_1.AsyncOptionalCreatable {
92
110
  }
93
111
  return this.contents.get(username);
94
112
  }
113
+ /**
114
+ * Return the contents of all the auth files from cache. The `read` or `readAll` methods must be called first in order to populate the cache.
115
+ *
116
+ * @param decrypt if true, decrypt encrypted values
117
+ * @returns
118
+ */
95
119
  getAll(decrypt = false) {
96
120
  return [...this.configs.keys()].reduce((orgs, username) => {
97
121
  const org = this.get(username, decrypt);
98
122
  return org && !(0, kit_1.isEmpty)(org) ? orgs.concat([org]) : orgs;
99
123
  }, []);
100
124
  }
125
+ /**
126
+ * Returns true if the username has been cached.
127
+ *
128
+ * @param username
129
+ */
101
130
  has(username) {
102
131
  return this.contents.has(username);
103
132
  }
133
+ /**
134
+ * Returns true if there is an auth file for the given username. The `read` or `readAll` methods must be called first in order to populate the cache.
135
+ *
136
+ * @param username
137
+ */
104
138
  async exists(username) {
105
139
  const config = this.configs.get(username);
106
140
  return config ? await config.exists() : false;
107
141
  }
142
+ /**
143
+ * Return the file stats for a given userame's auth file.
144
+ *
145
+ * @param username
146
+ */
108
147
  async stat(username) {
109
148
  const config = this.configs.get(username);
110
149
  return config ? await config.stat() : null;
111
150
  }
151
+ /**
152
+ * Returns true if there is an auth file for the given username
153
+ *
154
+ * @param username
155
+ */
112
156
  async hasFile(username) {
113
157
  try {
114
158
  await fs.promises.access(this.parseFilename(username));
@@ -119,9 +163,18 @@ class BaseOrgAccessor extends kit_1.AsyncOptionalCreatable {
119
163
  return false;
120
164
  }
121
165
  }
166
+ /**
167
+ * Return all auth files under the global state directory.
168
+ */
122
169
  async list() {
123
170
  return this.getAllFiles();
124
171
  }
172
+ /**
173
+ * Set the contents for a given username.
174
+ *
175
+ * @param username
176
+ * @param org
177
+ */
125
178
  set(username, org) {
126
179
  var _a, _b;
127
180
  const config = this.configs.get(username);
@@ -137,17 +190,33 @@ class BaseOrgAccessor extends kit_1.AsyncOptionalCreatable {
137
190
  this.contents.set(username, org);
138
191
  }
139
192
  }
193
+ /**
194
+ * Update the contents for a given username.
195
+ *
196
+ * @param username
197
+ * @param org
198
+ */
140
199
  update(username, org) {
141
200
  const existing = this.get(username) || {};
142
201
  const merged = Object.assign({}, existing, org);
143
202
  return this.set(username, merged);
144
203
  }
204
+ /**
205
+ * Delete the auth file for a given username.
206
+ *
207
+ * @param username
208
+ */
145
209
  async remove(username) {
146
210
  var _a;
147
211
  await ((_a = this.configs.get(username)) === null || _a === void 0 ? void 0 : _a.unlink());
148
212
  this.configs.delete(username);
149
213
  this.contents.delete(username);
150
214
  }
215
+ /**
216
+ * Write the contents of the auth file for a given username.
217
+ *
218
+ * @param username
219
+ */
151
220
  async write(username) {
152
221
  const config = this.configs.get(username);
153
222
  if (config) {
@@ -17,12 +17,53 @@ export declare class GlobaInfoTokenAccessor {
17
17
  }
18
18
  export declare class TokenAccessor extends AsyncOptionalCreatable {
19
19
  private config;
20
+ /**
21
+ * Return all tokens.
22
+ *
23
+ * @param decrypt
24
+ * @returns {SfTokens}
25
+ */
20
26
  getAll(decrypt?: boolean): SfTokens;
27
+ /**
28
+ * Return a token for the provided name.
29
+ *
30
+ * @param name
31
+ * @param decrypt
32
+ * @returns {Optional<SfToken & Timestamp>}
33
+ */
21
34
  get(name: string, decrypt?: boolean): Optional<SfToken & Timestamp>;
35
+ /**
36
+ * Return true if a given name has a token associated with it.
37
+ *
38
+ * @param name
39
+ * @returns {boolean}
40
+ */
22
41
  has(name: string): boolean;
42
+ /**
43
+ * Set the token for the provided name.
44
+ *
45
+ * @param name
46
+ * @param token
47
+ */
23
48
  set(name: string, token: SfToken): void;
49
+ /**
50
+ * Update the token for the provided name.
51
+ *
52
+ * @param name
53
+ * @param token
54
+ */
24
55
  update(name: string, token: Partial<SfToken>): void;
56
+ /**
57
+ * Unet the token for the provided name.
58
+ *
59
+ * @param name
60
+ */
25
61
  unset(name: string): void;
62
+ /**
63
+ * Write the contents to the token file.
64
+ *
65
+ * @returns {Promise<SfTokens>}
66
+ */
26
67
  write(): Promise<SfTokens>;
27
68
  protected init(): Promise<void>;
28
69
  }
@@ -38,24 +38,65 @@ class GlobaInfoTokenAccessor {
38
38
  }
39
39
  exports.GlobaInfoTokenAccessor = GlobaInfoTokenAccessor;
40
40
  class TokenAccessor extends kit_1.AsyncOptionalCreatable {
41
+ /**
42
+ * Return all tokens.
43
+ *
44
+ * @param decrypt
45
+ * @returns {SfTokens}
46
+ */
41
47
  getAll(decrypt = false) {
42
48
  return this.config.getContents(decrypt) || {};
43
49
  }
50
+ /**
51
+ * Return a token for the provided name.
52
+ *
53
+ * @param name
54
+ * @param decrypt
55
+ * @returns {Optional<SfToken & Timestamp>}
56
+ */
44
57
  get(name, decrypt = false) {
45
58
  return this.config.get(name, decrypt);
46
59
  }
60
+ /**
61
+ * Return true if a given name has a token associated with it.
62
+ *
63
+ * @param name
64
+ * @returns {boolean}
65
+ */
47
66
  has(name) {
48
67
  return !!this.getAll()[name];
49
68
  }
69
+ /**
70
+ * Set the token for the provided name.
71
+ *
72
+ * @param name
73
+ * @param token
74
+ */
50
75
  set(name, token) {
51
76
  this.config.set(name, token);
52
77
  }
78
+ /**
79
+ * Update the token for the provided name.
80
+ *
81
+ * @param name
82
+ * @param token
83
+ */
53
84
  update(name, token) {
54
85
  this.config.update(name, token);
55
86
  }
87
+ /**
88
+ * Unet the token for the provided name.
89
+ *
90
+ * @param name
91
+ */
56
92
  unset(name) {
57
93
  this.config.unset(name);
58
94
  }
95
+ /**
96
+ * Write the contents to the token file.
97
+ *
98
+ * @returns {Promise<SfTokens>}
99
+ */
59
100
  async write() {
60
101
  return this.config.write();
61
102
  }
@@ -88,14 +88,21 @@ export interface TestContext {
88
88
  [configName: string]: Optional<ConfigStub>;
89
89
  AliasesConfig?: ConfigStub;
90
90
  AuthInfoConfig?: ConfigStub;
91
- SfdxConfig?: ConfigStub;
91
+ Config?: ConfigStub;
92
92
  SfProjectJson?: ConfigStub;
93
93
  TokensConfig?: ConfigStub;
94
94
  };
95
95
  /**
96
96
  * An record of stubs created during instantaion.
97
97
  */
98
- stubs?: Record<string, sinonType.SinonStub>;
98
+ stubs: {
99
+ configRead?: sinonType.SinonStub;
100
+ configReadSync?: sinonType.SinonStub;
101
+ configWriteSync?: sinonType.SinonStub;
102
+ configWrite?: sinonType.SinonStub;
103
+ configExists?: sinonType.SinonStub;
104
+ configRemove?: sinonType.SinonStub;
105
+ };
99
106
  /**
100
107
  * A function used when resolving the local path. Calls localPathResolverSync by default.
101
108
  *
@@ -157,12 +164,45 @@ export interface TestContext {
157
164
  * @param value The actual stub contents. The Mock data.
158
165
  */
159
166
  setConfigStubContents(name: string, value: ConfigContents): void;
167
+ /**
168
+ * Set stubs for working in the context of a SfProject
169
+ */
160
170
  inProject(inProject: boolean): void;
171
+ /**
172
+ * Stub salesforce org authorizations.
173
+ */
161
174
  stubAuths(...orgs: MockTestOrgData[]): Promise<void>;
175
+ /**
176
+ * Stub salesforce sandbox authorizations.
177
+ */
162
178
  stubSandboxes(...orgs: MockTestSandboxData[]): Promise<void>;
179
+ /**
180
+ * Stub the aliases in the global aliases config file.
181
+ */
163
182
  stubAliases(aliases: Record<string, string>, group?: AliasGroup): void;
183
+ /**
184
+ * Stub contents in the config file.
185
+ */
186
+ stubConfig(config: Record<string, string>): void;
187
+ /**
188
+ * Stub the tokens in the global token config file.
189
+ */
190
+ stubTokens(tokens: Record<string, string>): void;
164
191
  }
165
- export declare const uniqid: () => string;
192
+ /**
193
+ * A function to generate a unique id and return it in the context of a template, if supplied.
194
+ *
195
+ * A template is a string that can contain `${%s}` to be replaced with a unique id.
196
+ * If the template contains the "%s" placeholder, it will be replaced with the unique id otherwise the id will be appended to the template.
197
+ *
198
+ * @param options an object with the following properties:
199
+ * - template: a template string.
200
+ * - length: the length of the unique id as presented in hexadecimal.
201
+ */
202
+ export declare function uniqid(options?: {
203
+ template?: string;
204
+ length?: number;
205
+ }): string;
166
206
  /**
167
207
  * Instantiate a @salesforce/core test context. This is automatically created by `const $$ = testSetup()`
168
208
  * but is useful if you don't want to have a global stub of @salesforce/core and you want to isolate it to
@@ -249,10 +289,10 @@ export declare const restoreContext: (testContext: TestContext) => void;
249
289
  * $$.SANDBOX.stub(MyClass.prototype, 'myMethod').returnsFake(() => {});
250
290
  *
251
291
  * // Set the contents that is used when aliases are read. Same for all config files.
252
- * $$.configStubs.Aliases = { contents: { 'myTestAlias': 'user@company.com' } };
292
+ * $$.stubAliases({ 'myTestAlias': 'user@company.com' });
253
293
  *
254
294
  * // Will use the contents set above.
255
- * const username = Aliases.fetch('myTestAlias');
295
+ * const username = (await StateAggregator.getInstance()).aliases.resolveUseranme('myTestAlias');
256
296
  * expect(username).to.equal('user@company.com');
257
297
  * });
258
298
  * });
@@ -286,7 +326,29 @@ export declare const unexpectedResult: SfError;
286
326
  *
287
327
  * @param f The async function that is expected to throw.
288
328
  */
289
- export declare function shouldThrow(f: Promise<unknown>): Promise<never>;
329
+ export declare function shouldThrow(f: Promise<unknown>, message?: string): Promise<never>;
330
+ /**
331
+ * Use for this testing pattern:
332
+ * ```
333
+ * try {
334
+ * call()
335
+ * assert.fail('this should never happen');
336
+ * } catch (e) {
337
+ * ...
338
+ * }
339
+ *
340
+ * Just do this
341
+ *
342
+ * try {
343
+ * shouldThrowSync(call); // If this succeeds unexpectedResultError is thrown.
344
+ * } catch(e) {
345
+ * ...
346
+ * }
347
+ * ```
348
+ *
349
+ * @param f The function that is expected to throw.
350
+ */
351
+ export declare function shouldThrowSync(f: () => unknown, message?: string): never;
290
352
  /**
291
353
  * A helper to determine if a subscription will use callback or errorback.
292
354
  * Enable errback to simulate a subscription failure.
@@ -385,7 +447,11 @@ export declare class StreamingMockCometClient extends CometClient {
385
447
  disconnect(): Promise<void>;
386
448
  }
387
449
  /**
388
- * Mock class for OrgData.
450
+ * Mock class for Salesforce Orgs.
451
+ *
452
+ * @example
453
+ * const testOrg = new MockTestOrgData();
454
+ * await $$.stubAuths(testOrg)
389
455
  */
390
456
  export declare class MockTestOrgData {
391
457
  testId: string;
@@ -410,13 +476,38 @@ export declare class MockTestOrgData {
410
476
  constructor(id?: string, options?: {
411
477
  username: string;
412
478
  });
479
+ /**
480
+ * Add devhub username to properties.
481
+ */
413
482
  createDevHubUsername(username: string): void;
483
+ /**
484
+ * Mark this org as a devhub.
485
+ */
414
486
  makeDevHub(): void;
487
+ /**
488
+ * Returns a MockTestOrgData that represents a user created in the org.
489
+ */
415
490
  createUser(user: string): MockTestOrgData;
491
+ /**
492
+ * Return mock user information based on this org.
493
+ */
416
494
  getMockUserInfo(): JsonMap;
495
+ /**
496
+ * Return the auth config file contents.
497
+ */
417
498
  getConfig(): Promise<AuthFields>;
499
+ /**
500
+ * Return the Connection for the org.
501
+ */
418
502
  getConnection(): Promise<Connection>;
419
503
  }
504
+ /**
505
+ * Mock class for Salesforce Sandboxes.
506
+ *
507
+ * @example
508
+ * const testOrg = new MockTestSandboxData();
509
+ * await $$.stubSandboxes(testOrg)
510
+ */
420
511
  export declare class MockTestSandboxData {
421
512
  id: string;
422
513
  sandboxOrgId: string;
@@ -428,5 +519,8 @@ export declare class MockTestSandboxData {
428
519
  name: string;
429
520
  username: string;
430
521
  }>);
522
+ /**
523
+ * Return the auth config file contents.
524
+ */
431
525
  getConfig(): Promise<SandboxFields>;
432
526
  }
package/lib/testSetup.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MockTestSandboxData = exports.MockTestOrgData = exports.StreamingMockCometClient = exports.StreamingMockCometSubscription = exports.StreamingMockSubscriptionCall = exports.shouldThrow = exports.unexpectedResult = exports.testSetup = exports.restoreContext = exports.stubContext = exports.instantiateContext = exports.uniqid = void 0;
3
+ exports.MockTestSandboxData = exports.MockTestOrgData = exports.StreamingMockCometClient = exports.StreamingMockCometSubscription = exports.StreamingMockSubscriptionCall = exports.shouldThrowSync = exports.shouldThrow = exports.unexpectedResult = exports.testSetup = exports.restoreContext = exports.stubContext = exports.instantiateContext = exports.uniqid = void 0;
4
4
  /*
5
5
  * Copyright (c) 2020, salesforce.com, inc.
6
6
  * All rights reserved.
@@ -12,6 +12,7 @@ const crypto_1 = require("crypto");
12
12
  const events_1 = require("events");
13
13
  const os_1 = require("os");
14
14
  const path_1 = require("path");
15
+ const util = require("util");
15
16
  const kit_1 = require("@salesforce/kit");
16
17
  const ts_sinon_1 = require("@salesforce/ts-sinon");
17
18
  const ts_types_1 = require("@salesforce/ts-types");
@@ -29,9 +30,28 @@ const org_1 = require("./org");
29
30
  const sandboxAccessor_1 = require("./stateAggregator/accessors/sandboxAccessor");
30
31
  const aliasesConfig_1 = require("./config/aliasesConfig");
31
32
  const global_1 = require("./global");
32
- const uniqid = () => {
33
- return (0, crypto_1.randomBytes)(16).toString('hex');
34
- };
33
+ /**
34
+ * A function to generate a unique id and return it in the context of a template, if supplied.
35
+ *
36
+ * A template is a string that can contain `${%s}` to be replaced with a unique id.
37
+ * If the template contains the "%s" placeholder, it will be replaced with the unique id otherwise the id will be appended to the template.
38
+ *
39
+ * @param options an object with the following properties:
40
+ * - template: a template string.
41
+ * - length: the length of the unique id as presented in hexadecimal.
42
+ */
43
+ function uniqid(options) {
44
+ var _a, _b;
45
+ const uniqueString = (0, crypto_1.randomBytes)(Math.ceil(((_a = options === null || options === void 0 ? void 0 : options.length) !== null && _a !== void 0 ? _a : 32) / 2.0))
46
+ .toString('hex')
47
+ .slice(0, (_b = options === null || options === void 0 ? void 0 : options.length) !== null && _b !== void 0 ? _b : 32);
48
+ if (!(options === null || options === void 0 ? void 0 : options.template)) {
49
+ return uniqueString;
50
+ }
51
+ return options.template.includes('%s')
52
+ ? util.format(options.template, uniqueString)
53
+ : `${options.template}${uniqueString}`;
54
+ }
35
55
  exports.uniqid = uniqid;
36
56
  function getTestLocalPath(uid) {
37
57
  return (0, path_1.join)((0, os_1.tmpdir)(), uid, 'sfdx_core', 'local');
@@ -39,11 +59,11 @@ function getTestLocalPath(uid) {
39
59
  function getTestGlobalPath(uid) {
40
60
  return (0, path_1.join)((0, os_1.tmpdir)(), uid, 'sfdx_core', 'global');
41
61
  }
42
- function retrieveRootPathSync(isGlobal, uid = (0, exports.uniqid)()) {
62
+ function retrieveRootPathSync(isGlobal, uid = uniqid()) {
43
63
  return isGlobal ? getTestGlobalPath(uid) : getTestLocalPath(uid);
44
64
  }
45
65
  // eslint-disable-next-line @typescript-eslint/require-await
46
- async function retrieveRootPath(isGlobal, uid = (0, exports.uniqid)()) {
66
+ async function retrieveRootPath(isGlobal, uid = uniqid()) {
47
67
  return retrieveRootPathSync(isGlobal, uid);
48
68
  }
49
69
  function defaultFakeConnectionRequest() {
@@ -99,9 +119,10 @@ const instantiateContext = (sinon) => {
99
119
  TEST_LOGGER: new logger_1.Logger({
100
120
  name: 'SFDX_Core_Test_Logger',
101
121
  }).useMemoryLogging(),
102
- id: (0, exports.uniqid)(),
103
- uniqid: exports.uniqid,
122
+ id: uniqid(),
123
+ uniqid,
104
124
  configStubs: {},
125
+ stubs: {},
105
126
  // eslint-disable-next-line @typescript-eslint/require-await
106
127
  localPathRetriever: async (uid) => getTestLocalPath(uid),
107
128
  localPathRetrieverSync: getTestLocalPath,
@@ -167,6 +188,12 @@ const instantiateContext = (sinon) => {
167
188
  stubAliases(aliases, group = aliasesConfig_1.AliasGroup.ORGS) {
168
189
  this.configStubs.AliasesConfig = { contents: { [group]: aliases } };
169
190
  },
191
+ stubConfig(config) {
192
+ this.configStubs.Config = { contents: config };
193
+ },
194
+ stubTokens(tokens) {
195
+ this.configStubs.TokensConfig = { contents: tokens };
196
+ },
170
197
  };
171
198
  return testContext;
172
199
  };
@@ -235,8 +262,8 @@ const stubContext = (testContext) => {
235
262
  };
236
263
  // Mock out all config file IO for all tests. They can restore individually if they need original functionality.
237
264
  // @ts-ignore
238
- testContext.SANDBOXES.CONFIG.stub(configFile_1.ConfigFile.prototype, 'readSync').callsFake(readSync);
239
- testContext.SANDBOXES.CONFIG.stub(configFile_1.ConfigFile.prototype, 'read').callsFake(read);
265
+ stubs.configRead = testContext.SANDBOXES.CONFIG.stub(configFile_1.ConfigFile.prototype, 'readSync').callsFake(readSync);
266
+ stubs.configReadSync = testContext.SANDBOXES.CONFIG.stub(configFile_1.ConfigFile.prototype, 'read').callsFake(read);
240
267
  const writeSync = function (newContents) {
241
268
  if (!testContext.configStubs[this.constructor.name]) {
242
269
  testContext.configStubs[this.constructor.name] = {};
@@ -358,10 +385,10 @@ const _testSetup = (sinon) => {
358
385
  * $$.SANDBOX.stub(MyClass.prototype, 'myMethod').returnsFake(() => {});
359
386
  *
360
387
  * // Set the contents that is used when aliases are read. Same for all config files.
361
- * $$.configStubs.Aliases = { contents: { 'myTestAlias': 'user@company.com' } };
388
+ * $$.stubAliases({ 'myTestAlias': 'user@company.com' });
362
389
  *
363
390
  * // Will use the contents set above.
364
- * const username = Aliases.fetch('myTestAlias');
391
+ * const username = (await StateAggregator.getInstance()).aliases.resolveUseranme('myTestAlias');
365
392
  * expect(username).to.equal('user@company.com');
366
393
  * });
367
394
  * });
@@ -395,11 +422,47 @@ exports.unexpectedResult = new sfError_1.SfError('This code was expected to fail
395
422
  *
396
423
  * @param f The async function that is expected to throw.
397
424
  */
398
- async function shouldThrow(f) {
425
+ async function shouldThrow(f, message) {
399
426
  await f;
400
- throw exports.unexpectedResult;
427
+ if (message) {
428
+ throw new sfError_1.SfError(message, 'UnexpectedResult');
429
+ }
430
+ else {
431
+ throw exports.unexpectedResult;
432
+ }
401
433
  }
402
434
  exports.shouldThrow = shouldThrow;
435
+ /**
436
+ * Use for this testing pattern:
437
+ * ```
438
+ * try {
439
+ * call()
440
+ * assert.fail('this should never happen');
441
+ * } catch (e) {
442
+ * ...
443
+ * }
444
+ *
445
+ * Just do this
446
+ *
447
+ * try {
448
+ * shouldThrowSync(call); // If this succeeds unexpectedResultError is thrown.
449
+ * } catch(e) {
450
+ * ...
451
+ * }
452
+ * ```
453
+ *
454
+ * @param f The function that is expected to throw.
455
+ */
456
+ function shouldThrowSync(f, message) {
457
+ f();
458
+ if (message) {
459
+ throw new sfError_1.SfError(message, 'UnexpectedResult');
460
+ }
461
+ else {
462
+ throw exports.unexpectedResult;
463
+ }
464
+ }
465
+ exports.shouldThrowSync = shouldThrowSync;
403
466
  /**
404
467
  * A helper to determine if a subscription will use callback or errorback.
405
468
  * Enable errback to simulate a subscription failure.
@@ -521,10 +584,14 @@ class StreamingMockCometClient extends streamingClient_1.CometClient {
521
584
  }
522
585
  exports.StreamingMockCometClient = StreamingMockCometClient;
523
586
  /**
524
- * Mock class for OrgData.
587
+ * Mock class for Salesforce Orgs.
588
+ *
589
+ * @example
590
+ * const testOrg = new MockTestOrgData();
591
+ * await $$.stubAuths(testOrg)
525
592
  */
526
593
  class MockTestOrgData {
527
- constructor(id = (0, exports.uniqid)(), options) {
594
+ constructor(id = uniqid(), options) {
528
595
  this.testId = id;
529
596
  this.userId = `user_id_${this.testId}`;
530
597
  this.orgId = `${this.testId}`;
@@ -536,14 +603,23 @@ class MockTestOrgData {
536
603
  this.authcode = `${this.testId}/authcode`;
537
604
  this.accessToken = `${this.testId}/accessToken`;
538
605
  this.refreshToken = `${this.testId}/refreshToken`;
539
- this.redirectUri = `http://${this.testId}/localhost:1717/OauthRedirect`;
606
+ this.redirectUri = 'http://localhost:1717/OauthRedirect';
540
607
  }
608
+ /**
609
+ * Add devhub username to properties.
610
+ */
541
611
  createDevHubUsername(username) {
542
612
  this.devHubUsername = username;
543
613
  }
614
+ /**
615
+ * Mark this org as a devhub.
616
+ */
544
617
  makeDevHub() {
545
618
  this.isDevHub = true;
546
619
  }
620
+ /**
621
+ * Returns a MockTestOrgData that represents a user created in the org.
622
+ */
547
623
  createUser(user) {
548
624
  const userMock = new MockTestOrgData();
549
625
  userMock.username = user;
@@ -561,6 +637,9 @@ class MockTestOrgData {
561
637
  userMock.isExpired = this.isExpired;
562
638
  return userMock;
563
639
  }
640
+ /**
641
+ * Return mock user information based on this org.
642
+ */
564
643
  getMockUserInfo() {
565
644
  return {
566
645
  Id: this.userId,
@@ -576,6 +655,9 @@ class MockTestOrgData {
576
655
  Email: `user_email@${this.testId}.com`,
577
656
  };
578
657
  }
658
+ /**
659
+ * Return the auth config file contents.
660
+ */
579
661
  async getConfig() {
580
662
  const crypto = await crypto_2.Crypto.create();
581
663
  const config = {};
@@ -601,19 +683,32 @@ class MockTestOrgData {
601
683
  config.isDevHub = this.isDevHub;
602
684
  return config;
603
685
  }
686
+ /**
687
+ * Return the Connection for the org.
688
+ */
604
689
  async getConnection() {
605
690
  return (await org_1.Org.create({ aliasOrUsername: this.username })).getConnection();
606
691
  }
607
692
  }
608
693
  exports.MockTestOrgData = MockTestOrgData;
694
+ /**
695
+ * Mock class for Salesforce Sandboxes.
696
+ *
697
+ * @example
698
+ * const testOrg = new MockTestSandboxData();
699
+ * await $$.stubSandboxes(testOrg)
700
+ */
609
701
  class MockTestSandboxData {
610
- constructor(id = (0, exports.uniqid)(), options) {
702
+ constructor(id = uniqid(), options) {
611
703
  this.id = id;
612
704
  this.sandboxOrgId = id;
613
705
  this.prodOrgUsername = (options === null || options === void 0 ? void 0 : options.prodOrgUsername) || `admin_${id}@gb.org`;
614
706
  this.sandboxName = (options === null || options === void 0 ? void 0 : options.name) || `sandbox_${id}`;
615
707
  this.username = (options === null || options === void 0 ? void 0 : options.username) || `${this.prodOrgUsername}.sandbox`;
616
708
  }
709
+ /**
710
+ * Return the auth config file contents.
711
+ */
617
712
  async getConfig() {
618
713
  return {
619
714
  sandboxOrgId: this.sandboxOrgId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/core",
3
- "version": "3.21.4",
3
+ "version": "3.22.0",
4
4
  "description": "Core libraries to interact with SFDX projects, orgs, and APIs.",
5
5
  "main": "lib/exported",
6
6
  "types": "lib/exported.d.ts",
@@ -49,7 +49,7 @@
49
49
  "form-data": "^4.0.0",
50
50
  "graceful-fs": "^4.2.9",
51
51
  "js2xmlparser": "^4.0.1",
52
- "jsforce": "2.0.0-beta.13",
52
+ "jsforce": "2.0.0-beta.14",
53
53
  "jsonwebtoken": "8.5.1",
54
54
  "mkdirp": "1.0.4",
55
55
  "ts-retry-promise": "^0.6.0"
@@ -63,6 +63,7 @@
63
63
  "@types/debug": "0.0.31",
64
64
  "@types/jsen": "0.0.21",
65
65
  "@types/jsonwebtoken": "8.5.7",
66
+ "@types/lodash": "^4.14.182",
66
67
  "@types/shelljs": "0.8.11",
67
68
  "@typescript-eslint/eslint-plugin": "^4.33.0",
68
69
  "@typescript-eslint/parser": "4.33.0",
@@ -78,6 +79,7 @@
78
79
  "eslint-plugin-jsdoc": "^35.1.2",
79
80
  "eslint-plugin-prettier": "^3.1.3",
80
81
  "husky": "^7.0.4",
82
+ "lodash": "^4.17.21",
81
83
  "mocha": "^9.1.3",
82
84
  "nyc": "^15.1.0",
83
85
  "prettier": "^2.5.1",
@@ -1,19 +0,0 @@
1
- import { ConfigFile } from './configFile';
2
- import { ConfigContents } from './configStore';
3
- /**
4
- * Represent a key chain config backed by a json file.
5
- */
6
- export declare class KeychainConfig extends ConfigFile<ConfigFile.Options> {
7
- static getFileName(): string;
8
- /**
9
- * Gets default options for the KeychainConfig
10
- */
11
- static getDefaultOptions(): ConfigFile.Options;
12
- /**
13
- * Write the config file with new contents. If no new contents are passed in
14
- * it will write this.contents that was set from read(). Returns the written contents.
15
- *
16
- * @param newContents the new contents of the file
17
- */
18
- write(newContents?: ConfigContents): Promise<ConfigContents>;
19
- }
@@ -1,44 +0,0 @@
1
- "use strict";
2
- /*
3
- * Copyright (c) 2020, salesforce.com, inc.
4
- * All rights reserved.
5
- * Licensed under the BSD 3-Clause license.
6
- * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.KeychainConfig = void 0;
10
- const path_1 = require("path");
11
- const fs = require("fs");
12
- const mkdirp = require("mkdirp");
13
- const configFile_1 = require("./configFile");
14
- /**
15
- * Represent a key chain config backed by a json file.
16
- */
17
- // istanbul ignore next - getPassword/setPassword is always mocked out
18
- class KeychainConfig extends configFile_1.ConfigFile {
19
- static getFileName() {
20
- return 'key.json';
21
- }
22
- /**
23
- * Gets default options for the KeychainConfig
24
- */
25
- static getDefaultOptions() {
26
- return configFile_1.ConfigFile.getDefaultOptions(true, KeychainConfig.getFileName());
27
- }
28
- /**
29
- * Write the config file with new contents. If no new contents are passed in
30
- * it will write this.contents that was set from read(). Returns the written contents.
31
- *
32
- * @param newContents the new contents of the file
33
- */
34
- async write(newContents) {
35
- if (newContents != null) {
36
- this.setContents(newContents);
37
- }
38
- await mkdirp((0, path_1.dirname)(this.getPath()));
39
- await fs.promises.writeFile(this.getPath(), JSON.stringify(this.getContents(), null, 4), { mode: '600' });
40
- return this.getContents();
41
- }
42
- }
43
- exports.KeychainConfig = KeychainConfig;
44
- //# sourceMappingURL=keychainConfig.js.map