@salesforce/core
Version:
Core libraries to interact with SFDX projects, orgs, and APIs.
282 lines • 10 kB
JavaScript
"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.ConfigAggregator = void 0;
const kit_1 = require("@salesforce/kit");
const ts_types_1 = require("@salesforce/ts-types");
const sfdxError_1 = require("../sfdxError");
const config_1 = require("./config");
const propertyToEnvName = (property) => `SFDX_${kit_1.snakeCase(property).toUpperCase()}`;
/**
* Aggregate global and local project config files, as well as environment variables for
* `sfdx-config.json`. The resolution happens in the following bottom-up order:
*
* 1. Environment variables (`SFDX_LOG_LEVEL`)
* 1. Workspace settings (`<workspace-root>/.sfdx/sfdx-config.json`)
* 1. Global settings (`$HOME/.sfdx/sfdx-config.json`)
*
* Use {@link ConfigAggregator.create} to instantiate the aggregator.
*
* ```
* const aggregator = await ConfigAggregator.create();
* console.log(aggregator.getPropertyValue('defaultusername'));
* ```
*/
class ConfigAggregator extends kit_1.AsyncOptionalCreatable {
/**
* **Do not directly construct instances of this class -- use {@link ConfigAggregator.create} instead.**
*
* @ignore
*/
constructor(options) {
super(options || {});
// Don't throw an project error with the aggregator, since it should resolve to global if
// there is no project.
try {
this.localConfig = new config_1.Config(config_1.Config.getDefaultOptions(false));
}
catch (err) {
if (err.name !== 'InvalidProjectWorkspace') {
throw err;
}
}
this.globalConfig = new config_1.Config(config_1.Config.getDefaultOptions(true));
this.setAllowedProperties(config_1.Config.getAllowedProperties());
}
get config() {
return this.resolveProperties(this.globalConfig.getContents(), this.localConfig && this.localConfig.getContents());
}
/**
* Get the static ConfigAggregator instance. If one doesn't exist, one will be created with
* the **encrypted** config values. Encrypted config values need to be resolved
* asynchronously by calling {@link ConfigAggregator.reload}
*/
// Use typing from AsyncOptionalCreatable to support extending ConfigAggregator.
// We really don't want ConfigAggregator extended but typescript doesn't support a final.
static getInstance() {
if (!ConfigAggregator.instance) {
ConfigAggregator.instance = new this();
ConfigAggregator.instance.loadPropertiesSync();
}
return ConfigAggregator.instance;
}
// Use typing from AsyncOptionalCreatable to support extending ConfigAggregator.
// We really don't want ConfigAggregator extended but typescript doesn't support a final.
static async create(options) {
let config = ConfigAggregator.instance;
if (!config) {
config = ConfigAggregator.instance = new this(options);
await config.init();
}
if (ConfigAggregator.encrypted) {
await config.loadProperties();
}
return ConfigAggregator.instance;
}
/**
* Get the info for a given key. If the ConfigAggregator was not asynchronously created OR
* the {@link ConfigAggregator.reload} was not called, the config value may be encrypted.
*
* @param key The config key.
*/
static getValue(key) {
return this.getInstance().getInfo(key);
}
/**
* Initialize this instances async dependencies.
*/
async init() {
await this.loadProperties();
}
/**
* Get a resolved config property.
*
* **Throws** *{@link SfdxError}{ name: 'UnknownConfigKey' }* An attempt to get a property that's not supported.
*
* @param key The key of the property.
*/
getPropertyValue(key) {
if (this.getAllowedProperties().some((element) => key === element.key)) {
return this.getConfig()[key];
}
else {
throw new sfdxError_1.SfdxError(`Unknown config key: ${key}`, 'UnknownConfigKey');
}
}
/**
* Get a resolved config property.
*
* @param key The key of the property.
*/
getInfo(key) {
const location = this.getLocation(key);
return {
key,
location,
value: this.getPropertyValue(key),
path: this.getPath(key),
isLocal: () => location === "Local" /* LOCAL */,
isGlobal: () => location === "Global" /* GLOBAL */,
isEnvVar: () => location === "Environment" /* ENVIRONMENT */,
};
}
/**
* Gets a resolved config property location.
*
* For example, `getLocation('logLevel')` will return:
* 1. `Location.GLOBAL` if resolved to an environment variable.
* 1. `Location.LOCAL` if resolved to local project config.
* 1. `Location.ENVIRONMENT` if resolved to the global config.
*
* @param key The key of the property.
*/
getLocation(key) {
if (this.getEnvVars().get(key) != null) {
return "Environment" /* ENVIRONMENT */;
}
if (this.localConfig && this.localConfig.get(key)) {
return "Local" /* LOCAL */;
}
if (this.globalConfig && this.globalConfig.get(key)) {
return "Global" /* GLOBAL */;
}
}
/**
* Get a resolved file path or environment variable name of the property.
*
* For example, `getPath('logLevel')` will return:
* 1. `$SFDX_LOG_LEVEL` if resolved to an environment variable.
* 1. `./.sfdx/sfdx-config.json` if resolved to the local config.
* 1. `~/.sfdx/sfdx-config.json` if resolved to the global config.
* 1. `undefined`, if not resolved.
*
* **Note:** that the path returned may be the absolute path instead of
* relative paths such as `./` and `~/`.
*
* @param key The key of the property.
*/
getPath(key) {
if (this.envVars[key] != null) {
return `$${propertyToEnvName(key)}`;
}
if (this.localConfig && this.localConfig.getContents()[key] != null) {
return this.localConfig.getPath();
}
if (this.globalConfig.getContents()[key] != null) {
return this.globalConfig.getPath();
}
}
/**
* Get all resolved config property keys, values, locations, and paths.
*
* ```
* > console.log(aggregator.getConfigInfo());
* [
* { key: 'logLevel', val: 'INFO', location: 'Environment', path: '$SFDX_LOG_LEVEL'}
* { key: 'defaultusername', val: '<username>', location: 'Local', path: './.sfdx/sfdx-config.json'}
* ]
* ```
*/
getConfigInfo() {
const infos = Object.keys(this.getConfig())
.filter((key) => this.getAllowedProperties().some((element) => key === element.key))
.map((key) => this.getInfo(key))
.filter((info) => !!info);
return kit_1.sortBy(infos, 'key');
}
/**
* Get the local project config instance.
*/
getLocalConfig() {
return this.localConfig;
}
/**
* Get the global config instance.
*/
getGlobalConfig() {
return this.globalConfig;
}
/**
* Get the resolved config object from the local, global and environment config instances.
*/
getConfig() {
return this.config;
}
/**
* Get the config properties that are environment variables.
*/
getEnvVars() {
return new Map(ts_types_1.definiteEntriesOf(this.envVars));
}
/**
* Re-read all property configurations from disk.
*/
async reload() {
await this.loadProperties();
return this;
}
/**
* Loads all the properties and aggregates them according to location.
*/
async loadProperties() {
this.resolveProperties(await this.globalConfig.read(), this.localConfig && (await this.localConfig.read()));
ConfigAggregator.encrypted = false;
}
/**
* Loads all the properties and aggregates them according to location.
*/
loadPropertiesSync() {
this.resolveProperties(this.globalConfig.readSync(), this.localConfig && this.localConfig.readSync());
}
resolveProperties(globalConfig, localConfig) {
const accumulator = {};
this.setEnvVars(this.getAllowedProperties().reduce((obj, property) => {
const val = process.env[propertyToEnvName(property.key)];
if (val != null) {
obj[property.key] = val;
}
return obj;
}, accumulator));
// Global config must be read first so it is on the left hand of the
// object assign and is overwritten by the local config.
const configs = [globalConfig];
// We might not be in a project workspace
if (localConfig) {
configs.push(localConfig);
}
configs.push(this.envVars);
const json = {};
const reduced = configs.filter(ts_types_1.isJsonMap).reduce((acc, el) => kit_1.merge(acc, el), json);
return reduced;
}
/**
* Get the allowed properties.
*/
getAllowedProperties() {
return this.allowedProperties;
}
/**
* Set the allowed properties.
*
* @param properties The properties to set.
*/
setAllowedProperties(properties) {
this.allowedProperties = properties;
}
/**
* Sets the env variables.
*
* @param envVars The env variables to set.
*/
setEnvVars(envVars) {
this.envVars = envVars;
}
}
exports.ConfigAggregator = ConfigAggregator;
ConfigAggregator.encrypted = true;
//# sourceMappingURL=configAggregator.js.map