salesforce-alm
Version:
This package contains tools, and APIs, for an improved salesforce.com developer experience.
217 lines (215 loc) • 10.1 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
*/
/* --------------------------------------------------------------------------------------------------------------------
* WARNING: This file has been deprecated and should now be considered locked against further changes. Its contents
* have been partially or wholely superceded by functionality included in the @salesforce/core npm package, and exists
* now to service prior uses in this repository only until they can be ported to use the new @salesforce/core library.
*
* If you need or want help deciding where to add new functionality or how to migrate to the new library, please
* contact the CLI team at alm-cli@salesforce.com.
* ----------------------------------------------------------------------------------------------------------------- */
// Thirdparty
const jsforce = require("jsforce");
const optional = require("optional-js");
const requestModule = require("request");
// Local
const configApi_1 = require("./configApi");
const logger = require("./logApi");
const core_1 = require("@salesforce/core");
const almError = require("./almError");
const messages = require("../messages");
const srcDevUtil = require("./srcDevUtil");
const core_2 = require("@salesforce/core");
const jsforceRequestMethod = jsforce.Connection.prototype.request;
const callOptionsValue = {
client: srcDevUtil.getSfdxCLIClientId(),
};
// Get a @salesforce/core Connection, which extends jsforce.Connection.
const getCoreConnection = async (username, options) => {
const conn = await core_1.Connection.create({
authInfo: await core_1.AuthInfo.create({ username, oauth2Options: options }),
});
return conn;
};
const getCoreConnectionFromOrgApi = async (orgApi) => {
const orgUserName = orgApi.getName();
const options = orgUserName ? null : orgApi.authConfig;
return getCoreConnection(orgUserName, options);
};
/**
* Internally we want to override jsforce::connection so we can inject our own http headers for all requests.
* JSforce has limited support for enabling HTTP headers, only certain apis support the http header option.
* With this strategy all requests are supported. Also see status.js. StreamingApi is managed as well.
*/
jsforce.Connection.prototype.request = function (request, options, callback) {
const _request = srcDevUtil.setSfdxRequestHeaders(request, options);
return jsforceRequestMethod.apply(this, [_request, options, callback]);
};
const Force = function (config) {
this.config = optional.ofNullable(config).orElse(new configApi_1.Config());
this.logger = logger.child('force');
};
Force.prototype.debug = function (orgApi, msg) {
if (this.logger.isDebugEnabled()) {
if (orgApi && orgApi.getName) {
this.logger.setConfig('org', orgApi.getName());
}
this.logger.debug(msg);
}
};
Force.prototype.getConfig = function () {
return this.config;
};
Force.prototype.create = async function (orgApi, sobjectName, sobject) {
this.debug(orgApi, `create: ${sobjectName}`, { sobject });
const connection = await getCoreConnectionFromOrgApi(orgApi);
try {
await connection.sobject(sobjectName).describe();
return connection.sobject(sobjectName).create(sobject);
}
catch (err) {
if (err.errorCode === 'NOT_FOUND') {
err['message'] = messages().getMessage('createOrgCommandUnauthorized', sobjectName);
err['name'] = 'ACCESS_DENIED';
}
throw err;
}
};
Force.prototype.update = async function (orgApi, sobjectName, sobject) {
this.debug(orgApi, `update: ${sobjectName}, ${sobject}`);
return (await getCoreConnectionFromOrgApi(orgApi)).sobject(sobjectName).update(sobject);
};
Force.prototype.delete = async function (orgApi, sobjectName, id) {
this.debug(orgApi, `delete: ${sobjectName}, ${id}`);
return (await getCoreConnectionFromOrgApi(orgApi)).sobject(sobjectName).delete(id);
};
Force.prototype.describe = async function (orgApi, sobjectName) {
this.debug(orgApi, `describe: ${sobjectName}`);
return (await getCoreConnectionFromOrgApi(orgApi)).sobject(sobjectName).describe();
};
Force.prototype.find = async function (orgApi, sobjectName, conditions, fields) {
return (await getCoreConnectionFromOrgApi(orgApi)).sobject(sobjectName).find(conditions, fields);
};
// NOTE: All queries will auto fetch results up to 10,000 records.
Force.prototype.query = async function (orgApi, query) {
this.debug(orgApi, `query: ${query}`);
return (await getCoreConnectionFromOrgApi(orgApi)).autoFetchQuery(query);
};
Force.prototype.retrieve = async function (orgApi, sobjectName, id) {
this.debug(orgApi, `toolingRetrieve: ${sobjectName}, ${id}`);
return (await getCoreConnectionFromOrgApi(orgApi)).tooling.sobject(sobjectName).retrieve(id);
};
Force.prototype.getApiVersions = function (orgApi) {
return orgApi.getConfig().then((orgConfig) => {
const url = `${orgConfig.instanceUrl}/services/data`;
return this.request(orgApi, 'GET', url, this.config);
});
};
Force.prototype.toolingCreate = async function (orgApi, sobjectName, sobject) {
this.debug(orgApi, `toolingCreate: ${sobjectName}`, { sobject });
return (await getCoreConnectionFromOrgApi(orgApi)).tooling.sobject(sobjectName).create(sobject);
};
Force.prototype.toolingUpdate = async function (orgApi, sobjectName, sobjectRecord) {
return (await getCoreConnectionFromOrgApi(orgApi)).tooling.sobject(sobjectName).update(sobjectRecord);
};
Force.prototype.toolingFind = async function (orgApi, sobjectName, conditions, fields) {
return (await getCoreConnectionFromOrgApi(orgApi)).tooling.sobject(sobjectName).find(conditions, fields);
};
// NOTE: all tooling queries will auto fetch results up to 10,000 records.
Force.prototype.toolingQuery = async function (orgApi, query) {
this.debug(orgApi, `toolingQuery: ${query}`);
const orgUserName = orgApi.getName();
const options = orgUserName ? null : orgApi.authConfig;
const connection = await getCoreConnection(orgUserName, options);
return connection.tooling.autoFetchQuery(query);
};
Force.prototype.toolingRetrieve = async function (orgApi, sobjectName, id) {
this.debug(orgApi, `toolingRetrieve: ${sobjectName}, ${id}`);
return (await getCoreConnectionFromOrgApi(orgApi)).tooling.sobject(sobjectName).retrieve(id);
};
// generic request to given URL w/ body and headers
Force.prototype.request = async function (orgApi, method, url, headers, body) {
this.debug(orgApi, `request: ${url}`);
// ensure string body
body = typeof body === 'string' ? body : JSON.stringify(body);
if (!headers) {
headers = {};
}
const conn = await getCoreConnectionFromOrgApi(orgApi);
if (conn.accessToken) {
headers.Authorization = `Bearer ${conn.accessToken}`;
}
return conn.requestRaw({ method, url, headers, body });
};
Force.prototype.mdapiSoapDeploy = async function (orgApi, zipStream, options) {
return (await getCoreConnectionFromOrgApi(orgApi)).metadata.deploy(zipStream, options !== null && options !== void 0 ? options : {});
};
Force.prototype.getRestHeaders = function (connection) {
return {
Authorization: connection && `OAuth ${connection.accessToken}`,
clientId: connection.oauth2 && connection.oauth2.clientId,
'Sforce-Call-Options': `client=${callOptionsValue.client}`,
};
};
Force.prototype.mdapiRestDeploy = async function (orgApi, zipStream, options) {
const connection = await getCoreConnectionFromOrgApi(orgApi);
const headers = this.getRestHeaders(connection);
const url = `${connection.instanceUrl.replace(/\/$/, '')}/services/data/v${this.config.getApiVersion()}/metadata/deployRequest`;
return new Promise((resolve, reject) => {
const r = requestModule.post(url, { headers }, (err, httpResponse, body) => {
try {
body = JSON.parse(body);
}
catch (e) {
reject(core_2.SfdxError.wrap(body));
}
if (err || httpResponse.statusCode > 300) {
let error;
if (body[0].errorCode === 'API_DISABLED_FOR_ORG') {
error = almError('mdDeployCommandCliNoRestDeploy');
}
else {
error = new Error(`${body[0].errorCode}: ${body[0].message}`);
}
reject(error);
}
else {
resolve(body);
}
});
const form = r.form();
// Add the zip file
form.append('file', zipStream, {
contentType: 'application/zip',
});
// Add the deploy options
form.append('entity_content', JSON.stringify({ deployOptions: options }), {
contentType: 'application/json',
});
});
};
Force.prototype.mdapiCheckDeployStatus = async function (orgApi, jobId) {
return (await getCoreConnectionFromOrgApi(orgApi)).metadata.checkDeployStatus(jobId, true);
};
Force.prototype.mdapiCheckRetrieveStatus = async function (orgApi, jobId) {
const result = await (await getCoreConnectionFromOrgApi(orgApi)).metadata.checkRetrieveStatus(jobId);
// if you look at what @types/jsforce thinks checkRetrieveStatus returns, done and success are invalid properties.
// but if we don't set them, the NUTs will fail.
// someday this will all be moot and use SDR instead
// @ts-ignore
return { ...result, done: result.done === 'true', success: result.success === 'true' };
};
// metadata api retrieve; options contains what to retrieve
Force.prototype.mdapiRetrieve = async function (orgApi, options) {
return (await getCoreConnectionFromOrgApi(orgApi)).metadata.retrieve(options);
};
Force.prototype.setCallOptions = function (key, value) {
callOptionsValue[key] = value;
};
module.exports = Force;
//# sourceMappingURL=force.js.map