UNPKG

@salesforce/core

Version:

Core libraries to interact with SFDX projects, orgs, and APIs.

210 lines 9.92 kB
"use strict"; /* * Copyright (c) 2021, 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.RequestStatus = void 0; const path = require("path"); const kit_1 = require("@salesforce/kit"); const ts_types_1 = require("@salesforce/ts-types"); const js2xmlparser = require("js2xmlparser"); const logger_1 = require("./logger"); const sfdxError_1 = require("./sfdxError"); const jsonXmlTools_1 = require("./util/jsonXmlTools"); const zipWriter_1 = require("./util/zipWriter"); const pollingClient_1 = require("./status/pollingClient"); var RequestStatus; (function (RequestStatus) { RequestStatus["Pending"] = "Pending"; RequestStatus["InProgress"] = "InProgress"; RequestStatus["Succeeded"] = "Succeeded"; RequestStatus["SucceededPartial"] = "SucceededPartial"; RequestStatus["Failed"] = "Failed"; RequestStatus["Canceling"] = "Canceling"; RequestStatus["Canceled"] = "Canceled"; })(RequestStatus = exports.RequestStatus || (exports.RequestStatus = {})); const breakPolling = ['Succeeded', 'SucceededPartial', 'Failed', 'Canceled']; /** * Helper class for dealing with the settings that are defined in a scratch definition file. This class knows how to extract the * settings from the definition, how to expand them into a MD directory and how to generate a package.xml. */ class SettingsGenerator { constructor() { this.shapeDirName = `shape_${Date.now()}`; this.logger = logger_1.Logger.childFromRoot('SettingsGenerator'); // If SFDX_MDAPI_TEMP_DIR is set, copy settings to that dir for people to inspect. const mdapiTmpDir = kit_1.env.getString('SFDX_MDAPI_TEMP_DIR'); this.writer = new zipWriter_1.ZipWriter(mdapiTmpDir); } /** extract the settings from the scratch def file, if they are present. */ async extract(scratchDef) { this.logger.debug('extracting settings from scratch definition file'); this.settingData = scratchDef.settings; this.objectSettingsData = scratchDef.objectSettings; this.logger.debug('settings are', this.settingData); } /** True if we are currently tracking setting or object setting data. */ hasSettings() { return !kit_1.isEmpty(this.settingData) || !kit_1.isEmpty(this.objectSettingsData); } /** Create temporary deploy directory used to upload the scratch org shape. * This will create the dir, all of the .setting files and minimal object files needed for objectSettings */ async createDeploy() { const settingsDir = path.join(this.shapeDirName, 'settings'); const objectsDir = path.join(this.shapeDirName, 'objects'); await Promise.all([this.writeSettingsIfNeeded(settingsDir), this.writeObjectSettingsIfNeeded(objectsDir)]); } /** * Deploys the settings to the org. */ async deploySettingsViaFolder(scratchOrg, apiVersion) { const username = scratchOrg.getUsername(); const logger = await logger_1.Logger.child('deploySettingsViaFolder'); this.createPackageXml(apiVersion); await this.writer.finalize(); const connection = scratchOrg.getConnection(); logger.debug(`deploying to apiVersion: ${apiVersion}`); connection.setApiVersion(apiVersion); const { id } = await connection.deploy(this.writer.buffer, {}); logger.debug(`deploying settings id ${id}`); let result = await connection.metadata.checkDeployStatus(id); const pollingOptions = { async poll() { try { result = await connection.metadata.checkDeployStatus(id, true); logger.debug(`Deploy id: ${id} status: ${result.status}`); if (breakPolling.includes(result.status)) { return { completed: true, payload: result.status, }; } return { completed: false, }; } catch (error) { logger.debug(`An error occurred trying to check deploy id: ${id}`); logger.debug(`Error: ${error.message}`); logger.debug('Re-trying deploy check again....'); return { completed: false, }; } }, timeout: kit_1.Duration.minutes(10), frequency: kit_1.Duration.seconds(1), timeoutErrorName: 'DeployingSettingsTimeoutError', }; const client = await pollingClient_1.PollingClient.create(pollingOptions); const status = await client.subscribe(); if (status !== RequestStatus.Succeeded) { const componentFailures = ts_types_1.ensureObject(result.details).componentFailures; const failures = (Array.isArray(componentFailures) ? componentFailures : [componentFailures]) .map((failure) => failure.problem) .join('\n'); const error = new sfdxError_1.SfdxError(`A scratch org was created with username ${username}, but the settings failed to deploy due to: \n${failures}`, 'ProblemDeployingSettings'); error.setData(result); throw error; } } async writeObjectSettingsIfNeeded(objectsDir) { if (!this.objectSettingsData || !Object.keys(this.objectSettingsData).length) { return; } for (const objectName of Object.keys(this.objectSettingsData)) { const value = this.objectSettingsData[objectName]; // writes the object file in source format const objectDir = path.join(objectsDir, kit_1.upperFirst(objectName)); const customObjectDir = path.join(objectDir, `${kit_1.upperFirst(objectName)}.object`); const customObjectXml = jsonXmlTools_1.JsonAsXml({ json: this.createObjectFileContent(value), type: 'RecordType', }); this.writer.addToZip(customObjectXml, customObjectDir); if (value.defaultRecordType) { const recordTypesDir = path.join(objectDir, 'recordTypes', `${kit_1.upperFirst(value.defaultRecordType)}.recordType`); const recordTypesFileContent = this.createRecordTypeFileContent(objectName, value); const recordTypesXml = jsonXmlTools_1.JsonAsXml({ json: recordTypesFileContent, type: 'RecordType', }); this.writer.addToZip(recordTypesXml, recordTypesDir); // for things that required a businessProcess if (recordTypesFileContent.businessProcess) { const businessProcessesDir = path.join(objectDir, 'businessProcesses', `${recordTypesFileContent.businessProcess}.businessProcess`); const businessProcessesXml = jsonXmlTools_1.JsonAsXml({ json: this.createBusinessProcessFileContent(objectName, recordTypesFileContent.businessProcess), type: 'BusinessProcess', }); this.writer.addToZip(businessProcessesXml, businessProcessesDir); } } } } async writeSettingsIfNeeded(settingsDir) { if (this.settingData) { for (const item of Object.keys(this.settingData)) { const value = ts_types_1.getObject(this.settingData, item); const typeName = kit_1.upperFirst(item); const fname = typeName.replace('Settings', ''); const fileContent = js2xmlparser.parse(typeName, value); this.writer.addToZip(fileContent, path.join(settingsDir, fname + '.settings')); } } } createPackageXml(apiVersion) { const pkg = `<?xml version="1.0" encoding="UTF-8"?> <Package xmlns="http://soap.sforce.com/2006/04/metadata"> <types> <members>*</members> <name>Settings</name> </types> <types> <members>*</members> <name>CustomObject</name> </types> <version>${apiVersion}</version> </Package>`; this.writer.addToZip(pkg, path.join(this.shapeDirName, 'package.xml')); } createObjectFileContent(json) { const output = {}; if (json.sharingModel) { output.sharingModel = kit_1.upperFirst(json.sharingModel); } return output; } createRecordTypeFileContent(objectName, setting) { const defaultRecordType = kit_1.upperFirst(setting.defaultRecordType); const output = { fullName: defaultRecordType, label: defaultRecordType, active: true, }; // all the edge cases if (['Case', 'Lead', 'Opportunity', 'Solution'].includes(kit_1.upperFirst(objectName))) { return { ...output, businessProcess: `${defaultRecordType}Process` }; } return output; } createBusinessProcessFileContent(objectName, businessProcessName) { const objectToBusinessProcessPicklist = { Opportunity: { fullName: 'Prospecting' }, Case: { fullName: 'New', default: true }, Lead: { fullName: 'New - Not Contacted', default: true }, Solution: { fullName: 'Draft', default: true }, }; return { fullName: businessProcessName, isActive: true, values: [objectToBusinessProcessPicklist[kit_1.upperFirst(objectName)]], }; } } exports.default = SettingsGenerator; //# sourceMappingURL=scratchOrgSettingsGenerator.js.map