@salesforce/core
Version:
Core libraries to interact with SFDX projects, orgs, and APIs.
235 lines • 9.09 kB
JavaScript
;
/*
* 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
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Connection = exports.SFDX_HTTP_HEADERS = void 0;
const kit_1 = require("@salesforce/kit");
const ts_types_1 = require("@salesforce/ts-types");
const jsforce_1 = require("jsforce");
const configAggregator_1 = require("./config/configAggregator");
const logger_1 = require("./logger");
const sfdxError_1 = require("./sfdxError");
const sfdc_1 = require("./util/sfdc");
/**
* The 'async' in our request override replaces the jsforce promise with the node promise, then returns it back to
* jsforce which expects .thenCall. Add .thenCall to the node promise to prevent breakage.
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
Promise.prototype.thenCall = jsforce_1.Promise.prototype.thenCall;
const clientId = `sfdx toolbelt:${process.env.SFDX_SET_CLIENT_IDS || ''}`;
exports.SFDX_HTTP_HEADERS = {
'content-type': 'application/json',
'user-agent': clientId,
};
/**
* Handles connections and requests to Salesforce Orgs.
*
* ```
* // Uses latest API version
* const connection = await Connection.create({
* authInfo: await AuthInfo.create({ username: 'myAdminUsername' })
* });
* connection.query('SELECT Name from Account');
*
* // Use different API version
* connection.setApiVersion("42.0");
* connection.query('SELECT Name from Account');
* ```
*/
class Connection extends jsforce_1.Connection {
/**
* Constructor
* **Do not directly construct instances of this class -- use {@link Connection.create} instead.**
*
* @param options The options for the class instance.
* @ignore
*/
constructor(options) {
super(options.connectionOptions || {});
// eslint-disable-next-line @typescript-eslint/unbound-method
this.tooling.autoFetchQuery = Connection.prototype.autoFetchQuery;
this.options = options;
}
/**
* Creates an instance of a Connection. Performs additional async initializations.
*
* @param options Constructor options.
*/
static async create(options) {
const configAggregator = options.configAggregator || (await configAggregator_1.ConfigAggregator.create());
const versionFromConfig = ts_types_1.asString(configAggregator.getInfo('apiVersion').value);
const baseOptions = {
// Set the API version obtained from the config aggregator.
// Will use jsforce default if undefined.
version: versionFromConfig,
callOptions: {
client: clientId,
},
};
// Get connection options from auth info and create a new jsForce connection
options.connectionOptions = Object.assign(baseOptions, options.authInfo.getConnectionOptions());
const conn = new this(options);
await conn.init();
if (!versionFromConfig) {
await conn.useLatestApiVersion();
}
return conn;
}
/**
* Async initializer.
*/
async init() {
// eslint-disable-next-line no-underscore-dangle
this.logger = this.tooling._logger = await logger_1.Logger.child('connection');
}
/**
* Send REST API request with given HTTP request info, with connected session information
* and SFDX headers.
*
* @param request HTTP request object or URL to GET request.
* @param options HTTP API request options.
*/
async request(request, options) {
const requestInfo = ts_types_1.isString(request) ? { method: 'GET', url: request } : request;
requestInfo.headers = Object.assign({}, exports.SFDX_HTTP_HEADERS, requestInfo.headers);
this.logger.debug(`request: ${JSON.stringify(requestInfo)}`);
// The "as" is a workaround for the jsforce typings.
return super.request(requestInfo, options);
}
/**
* Send REST API request with given HTTP request info, with connected session information
* and SFDX headers. This method returns a raw http response which includes a response body and statusCode.
*
* @param request HTTP request object or URL to GET request.
*/
async requestRaw(request) {
const headers = this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {};
kit_1.merge(headers, exports.SFDX_HTTP_HEADERS, request.headers);
return this.transport.httpRequest({
method: request.method,
url: request.url,
headers,
body: request.body,
});
}
/**
* The Force API base url for the instance.
*/
baseUrl() {
// essentially the same as pathJoin(super.instanceUrl, 'services', 'data', `v${super.version}`);
return super._baseUrl();
}
/**
* Retrieves the highest api version that is supported by the target server instance.
*/
async retrieveMaxApiVersion() {
const versions = (await this.request(`${this.instanceUrl}/services/data`));
this.logger.debug(`response for org versions: ${versions}`);
const max = ts_types_1.ensure(kit_1.maxBy(versions, (version) => version.version));
return max.version;
}
/**
* Use the latest API version available on `this.instanceUrl`.
*/
async useLatestApiVersion() {
try {
this.setApiVersion(await this.retrieveMaxApiVersion());
}
catch (err) {
// Don't fail if we can't use the latest, just use the default
this.logger.warn('Failed to set the latest API version:', err);
}
}
/**
* Get the API version used for all connection requests.
*/
getApiVersion() {
return this.version;
}
/**
* Set the API version for all connection requests.
*
* **Throws** *{@link SfdxError}{ name: 'IncorrectAPIVersion' }* Incorrect API version.
*
* @param version The API version.
*/
setApiVersion(version) {
if (!sfdc_1.sfdc.validateApiVersion(version)) {
throw new sfdxError_1.SfdxError(`Invalid API version ${version}. Expecting format "[1-9][0-9].0", i.e. 42.0`, 'IncorrectAPIVersion');
}
this.version = version;
}
/**
* Getter for the AuthInfo.
*/
getAuthInfoFields() {
return this.options.authInfo.getFields();
}
/**
* Getter for the auth fields.
*/
getConnectionOptions() {
return this.options.authInfo.getConnectionOptions();
}
/**
* Getter for the username of the Salesforce Org.
*/
getUsername() {
return this.getAuthInfoFields().username;
}
/**
* Returns true if this connection is using access token auth.
*/
isUsingAccessToken() {
return this.options.authInfo.isUsingAccessToken();
}
/**
* Normalize a Salesforce url to include a instance information.
*
* @param url Partial url.
*/
normalizeUrl(url) {
return this._normalizeUrl(url);
}
/**
* Executes a query and auto-fetches (i.e., "queryMore") all results. This is especially
* useful with large query result sizes, such as over 2000 records. The default maximum
* fetch size is 10,000 records. Modify this via the options argument.
*
* @param soql The SOQL string.
* @param executeOptions The query options. NOTE: the autoFetch option will always be true.
*/
async autoFetchQuery(soql, executeOptions = {}) {
const config = await configAggregator_1.ConfigAggregator.create();
// take the limit from the calling function, then the config, then default 10,000
const maxFetch = config.getInfo('maxQueryLimit').value || executeOptions.maxFetch || 10000;
const options = Object.assign(executeOptions, {
autoFetch: true,
maxFetch,
});
const records = [];
return new Promise((resolve, reject) => {
const query = this.query(soql, options)
.on('record', (rec) => records.push(rec))
.on('error', (err) => reject(err))
.on('end', () => {
const totalSize = ts_types_1.getNumber(query, 'totalSize') || 0;
if (totalSize > records.length) {
process.emitWarning(`The query result is missing ${totalSize - records.length} records due to a ${maxFetch} record limit. Increase the number of records returned by setting the config value "maxQueryLimit" or the environment variable "SFDX_MAX_QUERY_LIMIT" to ${totalSize} or greater than ${maxFetch}.`);
}
resolve({
done: true,
totalSize,
records,
});
});
});
}
}
exports.Connection = Connection;
//# sourceMappingURL=connection.js.map