UNPKG

@salesforce/packaging

Version:

Packaging library for the Salesforce packaging platform

269 lines 12.1 kB
"use strict"; 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 (c) 2024, 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 */ 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