@salesforce/packaging
Version:
Packaging library for the Salesforce packaging platform
278 lines • 12.4 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackagePushUpgrade = void 0;
/*
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const promises_1 = __importDefault(require("node:fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const node_util_1 = __importDefault(require("node:util"));
const core_1 = require("@salesforce/core");
const packageUtils_1 = require("../utils/packageUtils");
class PackagePushUpgrade {
constructor() { }
static async list(connection, options) {
try {
const whereClause = constructWhereList(options);
return await queryList(node_util_1.default.format(getListQuery(), whereClause), connection);
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
throw err;
}
}
static async report(connection, options) {
try {
const whereClause = constructWhereReport(options);
return (await queryReport(node_util_1.default.format(getReportQuery(), whereClause), connection)).records;
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
throw err;
}
}
static async getFailedJobs(connection, options) {
try {
const whereClause = constructWhereJobCountByStatus(options, 'Failed');
return (await queryJobCountByStatus(node_util_1.default.format(getJobCountByStatusQuery(), whereClause), connection)).records[0]
?.expr0;
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
throw err;
}
}
static async getSucceededJobs(connection, options) {
try {
const whereClause = constructWhereJobCountByStatus(options, 'Succeeded');
return (await queryJobCountByStatus(node_util_1.default.format(getJobCountByStatusQuery(), whereClause), connection)).records[0]
?.expr0;
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
throw err;
}
}
static async getTotalJobs(connection, options) {
try {
const whereClause = constructWhereJobCountByStatus(options);
return (await queryJobCountByStatus(node_util_1.default.format(getJobCountByStatusQuery(), whereClause), connection)).records[0]
?.expr0;
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
throw err;
}
}
static async getJobFailureReasons(connection, options) {
try {
const whereClause = constructWhereJobFailureReasons(options);
return (await queryJobFailureReasons(node_util_1.default.format(getJobFailureReasonsQuery(), whereClause), connection)).records;
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
throw err;
}
}
static async schedule(connection, packageVersionId, scheduleTime, orgList, isMigration = false) {
let job;
try {
const packagePushRequestBody = {
PackageVersionId: packageVersionId,
};
if (scheduleTime) {
packagePushRequestBody.ScheduledStartTime = scheduleTime;
}
if (isMigration) {
packagePushRequestBody.IsMigration = true;
}
const pushRequestResult = await connection.request({
method: 'POST',
url: `/services/data/v${connection.getApiVersion()}/sobjects/packagepushrequest/`,
body: JSON.stringify(packagePushRequestBody),
});
const pushJobs = orgList.map((orgId) => ({
PackagePushRequestId: pushRequestResult.id,
SubscriberOrganizationKey: orgId,
}));
// Create PackagePushJob for each org using Bulk API v2
job = connection.bulk2.createJob({ operation: 'insert', object: 'PackagePushJob' });
await job.open();
await job.uploadData(pushJobs);
await job.close();
await job.poll(1000, 600_000);
// If there are any errors for a job, write all specific job errors to an output file
const jobErrors = await job.getFailedResults();
if (jobErrors.length > 0) {
const filePath = await this.writeJobErrorsToFile(pushRequestResult?.id, jobErrors);
throw new core_1.SfError(`Push upgrade failed, job errors have been written to file: ${filePath}`);
}
await connection.request({
method: 'PATCH',
url: `/services/data/v${connection.getApiVersion()}/sobjects/packagepushrequest/` + pushRequestResult?.id,
body: JSON.stringify({ Status: 'Pending' }),
});
return {
PushRequestId: pushRequestResult.id,
ScheduledStartTime: scheduleTime,
Status: 'Pending',
};
}
catch (err) {
if (job && err.name !== 'JobPollingTimeoutError') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
job.delete().catch((ignored) => ignored);
}
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
}
static async abort(connection, options) {
try {
// Fetch the current status of the PackagePushRequest
const abortQuery = node_util_1.default.format(getPushRequestStatusQuery(), getPushRequestStatusWhereClause(options));
const queryResult = await queryReport(abortQuery, connection);
if (!queryResult.records || queryResult.records.length === 0) {
throw new Error('Can’t abort package push upgrade request. The specified push upgrade ID isn’t valid. Check the ID (starts with 0DV) and retry the command.');
}
const pushRequest = queryResult.records[0];
// Validate the current status
if (!['Created', 'Pending'].includes(pushRequest.Status)) {
throw new Error(`Can’t abort package push upgrade request with status '${pushRequest.Status}'. Only push upgrade requests with a status of 'Created' or 'Pending' can be cancelled.`);
}
// Abort the push request by setting its status to "Canceled"
await connection.request({
method: 'PATCH',
url: `/services/data/v${connection.getApiVersion()}/sobjects/packagepushrequest/` + options.packagePushRequestId,
body: JSON.stringify({ Status: 'Canceled' }),
});
// Return the updated PackagePushRequest details
return true;
}
catch (err) {
if (err instanceof Error) {
throw (0, packageUtils_1.applyErrorAction)((0, packageUtils_1.massageErrorMessage)(err));
}
return false;
}
}
static async writeJobErrorsToFile(pushRequestId, jobErrors) {
const outputDir = node_path_1.default.join(process.cwd(), 'job_errors');
const outputFile = node_path_1.default.join(outputDir, `push_request_${pushRequestId}_errors.log`);
try {
await promises_1.default.mkdir(outputDir, { recursive: true });
const errorContent = jobErrors
.map((job, index) => `Job ${index + 1} Error:${JSON.stringify(job?.sf__Error, null, 2)}`)
.join('\n');
await promises_1.default.writeFile(outputFile, errorContent, 'utf-8');
return outputFile;
}
catch (error) {
throw new core_1.SfError('Error when saving job errors to file. ' + error.message);
}
}
}
exports.PackagePushUpgrade = PackagePushUpgrade;
async function queryList(query, connection) {
const queryResult = await connection.autoFetchQuery(query, {});
return queryResult.records;
}
function constructWhereList(options) {
const where = [];
if (options?.packageId) {
where.push(`PackageVersion.MetadataPackageId = '${options.packageId}'`);
}
if (options?.status) {
where.push(`Status = '${options.status}'`);
}
if (options?.scheduledLastDays) {
const daysAgo = new Date();
daysAgo.setDate(daysAgo.getDate() - options.scheduledLastDays);
where.push(`ScheduledStartTime >= ${daysAgo.toISOString()}`);
}
if (options?.isMigration) {
where.push(`IsMigration = ${options.isMigration}`);
}
return where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
}
function getListQuery() {
// WHERE, if applicable
return ('SELECT Id, PackageVersionId, PackageVersion.Name, PackageVersion.MajorVersion, PackageVersion.MinorVersion, Status, ScheduledStartTime, StartTime, EndTime, IsMigration FROM PackagePushRequest ' +
'%s');
}
async function queryReport(query, connection) {
return connection.autoFetchQuery(query, {});
}
async function queryJobCountByStatus(query, connection) {
return connection.autoFetchQuery(query, {});
}
function constructWhereReport(options) {
const where = [];
where.push(`Id = '${options.packagePushRequestId}'`);
return `WHERE ${where.join(' AND ')}`;
}
function getReportQuery() {
const QUERY = 'SELECT PackageVersion.MetadataPackage.Name, PackageVersion.MajorVersion, PackageVersion.MinorVersion, PackageVersion.MetadataPackage.NamespacePrefix, PackageVersion.MetadataPackageId, PackageVersionId, PackageVersion.Name, Id, Status, ScheduledStartTime, StartTime, EndTime, DurationSeconds FROM PackagePushRequest ' +
'%s'; // WHERE, if applicable
return QUERY;
}
function constructWhereJobCountByStatus(options, status) {
const where = [];
where.push(`PackagePushRequestId = '${options.packagePushRequestId}'`);
if (status) {
where.push(`Status = '${status}'`);
}
return `WHERE ${where.join(' AND ')}`;
}
function getJobCountByStatusQuery() {
const QUERY = 'SELECT Count(Id) FROM PackagePushJob ' + '%s '; // WHERE, if applicable
return QUERY;
}
function constructWhereJobFailureReasons(options) {
const where = [];
where.push(`PackagePushJob.PackagePushRequestId = '${options.packagePushRequestId}'`);
return `WHERE ${where.join(' AND ')}`;
}
function getJobFailureReasonsQuery() {
const QUERY = 'SELECT ErrorMessage, ErrorDetails, ErrorTitle, ErrorSeverity, ErrorType from PackagePushError ' + '%s '; // WHERE, if applicable
return QUERY;
}
async function queryJobFailureReasons(query, connection) {
return connection.autoFetchQuery(query, {});
}
function getPushRequestStatusWhereClause(options) {
const where = [];
where.push(`Id = '${options.packagePushRequestId}'`);
return `WHERE ${where.join(' AND ')}`;
}
function getPushRequestStatusQuery() {
const QUERY = 'SELECT Id, Status FROM PackagePushRequest ' + '%s';
return QUERY;
}
//# sourceMappingURL=packagePushUpgrade.js.map