UNPKG

@salesforce/core

Version:

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

413 lines 15.3 kB
"use strict"; /* * 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.Config = exports.SfProperty = exports.SFDX_ALLOWED_PROPERTIES = exports.SfdxPropertyKeys = void 0; const path_1 = require("path"); const kit_1 = require("@salesforce/kit"); const ts_types_1 = require("@salesforce/ts-types"); const global_1 = require("../global"); const logger_1 = require("../logger"); const messages_1 = require("../messages"); const sfdc_1 = require("../util/sfdc"); const fs_1 = require("../util/fs"); const configFile_1 = require("./configFile"); messages_1.Messages.importMessagesDirectory(__dirname); const messages = messages_1.Messages.load('@salesforce/core', 'config', [ 'invalidInstanceUrl', 'invalidApiVersion', 'invalidIsvDebuggerSid', 'invalidIsvDebuggerUrl', 'invalidBooleanConfigValue', 'invalidNumberConfigValue', 'invalidWrite', 'unknownConfigKey', 'invalidConfigValue', ]); const log = logger_1.Logger.childFromRoot('core:config'); const SFDX_CONFIG_FILE_NAME = 'sfdx-config.json'; const CONFIG_FILE_NAME = 'config.json'; var SfdxPropertyKeys; (function (SfdxPropertyKeys) { /** * Username associated with the default dev hub org. */ SfdxPropertyKeys["DEFAULT_DEV_HUB_USERNAME"] = "defaultdevhubusername"; /** * Username associate with the default org. */ SfdxPropertyKeys["DEFAULT_USERNAME"] = "defaultusername"; /** * The sid for the debugger configuration. */ SfdxPropertyKeys["ISV_DEBUGGER_SID"] = "isvDebuggerSid"; /** * The url for the debugger configuration. */ SfdxPropertyKeys["ISV_DEBUGGER_URL"] = "isvDebuggerUrl"; /** * The api version */ SfdxPropertyKeys["API_VERSION"] = "apiVersion"; /** * Disables telemetry reporting */ SfdxPropertyKeys["DISABLE_TELEMETRY"] = "disableTelemetry"; /** * allows users to override the 10,000 result query limit */ SfdxPropertyKeys["MAX_QUERY_LIMIT"] = "maxQueryLimit"; /** */ SfdxPropertyKeys["REST_DEPLOY"] = "restDeploy"; /** */ SfdxPropertyKeys["INSTANCE_URL"] = "instanceUrl"; })(SfdxPropertyKeys = exports.SfdxPropertyKeys || (exports.SfdxPropertyKeys = {})); exports.SFDX_ALLOWED_PROPERTIES = [ { key: SfdxPropertyKeys.INSTANCE_URL, description: '', input: { // If a value is provided validate it otherwise no value is unset. validator: (value) => value == null || (ts_types_1.isString(value) && sfdc_1.sfdc.isSalesforceDomain(value)), failedMessage: messages.getMessage('invalidInstanceUrl'), }, }, { key: SfdxPropertyKeys.API_VERSION, description: '', hidden: true, input: { // If a value is provided validate it otherwise no value is unset. validator: (value) => value == null || (ts_types_1.isString(value) && sfdc_1.sfdc.validateApiVersion(value)), failedMessage: messages.getMessage('invalidApiVersion'), }, }, { key: SfdxPropertyKeys.DEFAULT_DEV_HUB_USERNAME, description: '', }, { key: SfdxPropertyKeys.DEFAULT_USERNAME, description: '', }, { key: SfdxPropertyKeys.ISV_DEBUGGER_SID, description: '', encrypted: true, input: { // If a value is provided validate it otherwise no value is unset. validator: (value) => value == null || ts_types_1.isString(value), failedMessage: messages.getMessage('invalidIsvDebuggerSid'), }, }, { key: SfdxPropertyKeys.ISV_DEBUGGER_URL, description: '', input: { // If a value is provided validate it otherwise no value is unset. validator: (value) => value == null || ts_types_1.isString(value), failedMessage: messages.getMessage('invalidIsvDebuggerUrl'), }, }, { key: SfdxPropertyKeys.DISABLE_TELEMETRY, description: '', input: { validator: (value) => value == null || ['true', 'false'].includes(value.toString()), failedMessage: messages.getMessage('invalidBooleanConfigValue'), }, }, // This should be brought in by a plugin, but there isn't a way to do that right now. { key: SfdxPropertyKeys.REST_DEPLOY, description: '', hidden: true, input: { validator: (value) => value != null && ['true', 'false'].includes(value.toString()), failedMessage: messages.getMessage('invalidBooleanConfigValue'), }, }, { key: SfdxPropertyKeys.MAX_QUERY_LIMIT, description: '', input: { // the bit shift will remove the negative bit, and any decimal numbers // then the parseFloat will handle converting it to a number from a string validator: (value) => value >>> 0 === parseFloat(value) && value > 0, failedMessage: messages.getMessage('invalidNumberConfigValue'), }, }, ]; // Generic global config properties. Specific properties can be loaded like orgConfigProperties.ts. exports.SfProperty = {}; /** * The files where sfdx config values are stored for projects and the global space. * * *Note:* It is not recommended to instantiate this object directly when resolving * config values. Instead use {@link ConfigAggregator} * * ``` * const localConfig = await Config.create(); * localConfig.set('defaultusername', 'username@company.org'); * await localConfig.write(); * ``` * https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_cli_config_values.htm */ class Config extends configFile_1.ConfigFile { constructor(options) { super(Object.assign({ isGlobal: false, }, options, { // Don't let consumers of config override this. If they really really want to, // they can extend this class. isState: true, filename: Config.getFileName(), stateFolder: global_1.Global.SF_STATE_FOLDER, })); // Resolve the config path on creation. this.getPath(); } /** * Returns the default file name for a config file. * * **See** {@link CONFIG_FILE_NAME} */ static getFileName() { return CONFIG_FILE_NAME; } /** * Returns an array of objects representing the allowed config properties. */ static getAllowedProperties() { return Config.allowedProperties; } /** * Add an array of allowed config properties. * * @param metas Array of objects to set as the allowed config properties. */ static addAllowedProperties(metas) { const currentMetaKeys = Object.keys(Config.propertyConfigMap()); metas.forEach((meta) => { if (currentMetaKeys.includes(meta.key)) { log.info(`Key ${meta.key} already exists in allowedProperties, skipping.`); return; } Config.allowedProperties.push(meta); }); } /** * The value of a supported config property. * * @param isGlobal True for a global config. False for a local config. * @param propertyName The name of the property to set. * @param value The property value. */ static async update(isGlobal, propertyName, value) { const config = await Config.create({ isGlobal }); const content = await config.read(); if (value == null) { delete content[propertyName]; } else { kit_1.set(content, propertyName, value); } return config.write(content); } /** * Clear all the configured properties both local and global. */ static async clear() { const globalConfig = await Config.create({ isGlobal: true }); globalConfig.clear(); await globalConfig.write(); const localConfig = await Config.create(); localConfig.clear(); await localConfig.write(); } static propertyConfigMap() { return kit_1.keyBy(Config.allowedProperties, 'key'); } /** * Read, assign, and return the config contents. */ async read(force = true) { try { const config = await super.read(false, force); // Merge .sfdx/sfdx-config.json and .sf/config.json const sfdxConfig = global_1.Global.SFDX_INTEROPERABILITY ? this.readSfdxConfigSync() : {}; this.setContents(Object.assign(sfdxConfig, config)); await this.cryptProperties(false); return this.getContents(); } finally { await this.clearCrypto(); } } readSync(force = true) { const config = super.readSync(false, force); // Merge .sfdx/sfdx-config.json and .sf/config.json const sfdxConfig = global_1.Global.SFDX_INTEROPERABILITY ? this.readSfdxConfigSync() : {}; this.setContents(Object.assign(sfdxConfig, config)); return this.getContents(); } /** * Writes Config properties taking into account encrypted properties. * * @param newContents The new Config value to persist. */ async write(newContents) { if (newContents != null) { this.setContents(newContents); } await this.cryptProperties(true); await super.write(); await this.writeSfdxConfig(); await this.cryptProperties(false); return this.getContents(); } /** * DO NOT CALL - The config file needs to encrypt values which can only be done asynchronously. * Call {@link SfdxConfig.write} instead. * * **Throws** *{@link SfdxError}{ name: 'InvalidWriteError' }* Always. * * @param newContents Contents to write */ // eslint-disable-next-line @typescript-eslint/no-unused-vars writeSync(newContents) { throw messages.createError('invalidWrite'); } /** * Sets a value for a property. * * **Throws** *{@link SfdxError}{ name: 'UnknownConfigKeyError' }* An attempt to get a property that's not supported. * **Throws** *{@link SfdxError}{ name: 'InvalidConfigValueError' }* If the input validator fails. * * @param key The property to set. * @param value The value of the property. */ set(key, value) { const property = Config.allowedProperties.find((allowedProp) => allowedProp.key === key); if (!property) { throw messages.createError('unknownConfigKey', [key]); } if (property.input) { if (property.input && property.input.validator(value)) { super.set(property.key, value); } else { let valueError = (value === null || value === void 0 ? void 0 : value.toString()) || ''; if (property.input.failedMessage) { valueError = ts_types_1.isString(property.input.failedMessage) ? property.input.failedMessage : property.input.failedMessage(value); } throw messages.createError('invalidConfigValue', [valueError]); } } else { super.set(property.key, value); } return this.getContents(); } /** * Unsets a value for a property. * * **Throws** *{@link SfdxError}{ name: 'UnknownConfigKeyError' }* If the input validator fails. * * @param key The property to unset. */ unset(key) { const property = Config.allowedProperties.find((allowedProp) => allowedProp.key === key); if (!property) { throw messages.createError('unknownConfigKey', [key]); } return super.unset(property.key); } /** * Initializer for supported config types. */ async init() { // Super ConfigFile calls read, which has a dependency on crypto, which finally has a dependency on // Config.propertyConfigMap being set. This is why init is called after the setup. await super.init(); } readSfdxConfigSync() { try { return fs_1.fs.readJsonMapSync(this.getSfdxPath()); } catch (error) { /* Do nothing */ return {}; } } async writeSfdxConfig() { try { await fs_1.fs.mkdirp(path_1.dirname(this.getSfdxPath())); this.logger.info(`Writing to config file: ${this.getPath()}`); await fs_1.fs.writeJson(this.getSfdxPath(), this.toObject()); } catch (error) { /* Do nothing */ } } getSfdxPath() { if (!this.sfdxPath) { const stateFolder = global_1.Global.SFDX_STATE_FOLDER; const fileName = SFDX_CONFIG_FILE_NAME; const _isGlobal = ts_types_1.isBoolean(this.options.isGlobal) && this.options.isGlobal; const _isState = ts_types_1.isBoolean(this.options.isState) && this.options.isState; // Don't let users store config files in homedir without being in the state folder. let configRootFolder = this.options.rootFolder ? this.options.rootFolder : configFile_1.ConfigFile.resolveRootFolderSync(!!this.options.isGlobal); if (_isGlobal || _isState) { configRootFolder = path_1.join(configRootFolder, stateFolder); } this.sfdxPath = path_1.join(configRootFolder, fileName); } return this.sfdxPath; } /** * Get an individual property config. * * **Throws** *{@link SfdxError}{ name: 'UnknownConfigKeyError' }* An attempt to get a property that's not supported. * * @param propertyName The name of the property. */ getPropertyConfig(propertyName) { const prop = Config.propertyConfigMap()[propertyName]; if (!prop) { throw messages.createError('unknownConfigKey', [propertyName]); } return prop; } /** * Encrypts and content properties that have a encryption attribute. * * @param encrypt `true` to encrypt. */ async cryptProperties(encrypt) { const hasEncryptedProperties = this.entries().some(([key]) => { var _a; return !!((_a = Config.propertyConfigMap()[key]) === null || _a === void 0 ? void 0 : _a.encrypted); }); if (hasEncryptedProperties) { await this.initCrypto(); const crypto = ts_types_1.ensure(this.crypto); this.forEach((key, value) => { if (this.getPropertyConfig(key).encrypted && ts_types_1.isString(value)) { this.set(key, ts_types_1.ensure(encrypt ? crypto.encrypt(value) : crypto.decrypt(value))); } }); } } } exports.Config = Config; Config.allowedProperties = [...exports.SFDX_ALLOWED_PROPERTIES]; //# sourceMappingURL=config.js.map