1 | ;
|
2 | /*
|
3 | * Copyright (c) 2020, salesforce.com, inc.
|
4 | * All rights reserved.
|
5 | * Licensed under the BSD 3-Clause license.
|
6 | * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
7 | */
|
8 | Object.defineProperty(exports, "__esModule", { value: true });
|
9 | exports.ConfigAggregator = void 0;
|
10 | const kit_1 = require("@salesforce/kit");
|
11 | const ts_types_1 = require("@salesforce/ts-types");
|
12 | const sfdxError_1 = require("../sfdxError");
|
13 | const config_1 = require("./config");
|
14 | const propertyToEnvName = (property) => `SFDX_${kit_1.snakeCase(property).toUpperCase()}`;
|
15 | /**
|
16 | * Aggregate global and local project config files, as well as environment variables for
|
17 | * `sfdx-config.json`. The resolution happens in the following bottom-up order:
|
18 | *
|
19 | * 1. Environment variables (`SFDX_LOG_LEVEL`)
|
20 | * 1. Workspace settings (`<workspace-root>/.sfdx/sfdx-config.json`)
|
21 | * 1. Global settings (`$HOME/.sfdx/sfdx-config.json`)
|
22 | *
|
23 | * Use {@link ConfigAggregator.create} to instantiate the aggregator.
|
24 | *
|
25 | * ```
|
26 | * const aggregator = await ConfigAggregator.create();
|
27 | * console.log(aggregator.getPropertyValue('defaultusername'));
|
28 | * ```
|
29 | */
|
30 | class ConfigAggregator extends kit_1.AsyncOptionalCreatable {
|
31 | /**
|
32 | * **Do not directly construct instances of this class -- use {@link ConfigAggregator.create} instead.**
|
33 | *
|
34 | * @ignore
|
35 | */
|
36 | constructor(options) {
|
37 | super(options || {});
|
38 | // Don't throw an project error with the aggregator, since it should resolve to global if
|
39 | // there is no project.
|
40 | try {
|
41 | this.localConfig = new config_1.Config(config_1.Config.getDefaultOptions(false));
|
42 | }
|
43 | catch (err) {
|
44 | if (err.name !== 'InvalidProjectWorkspace') {
|
45 | throw err;
|
46 | }
|
47 | }
|
48 | this.globalConfig = new config_1.Config(config_1.Config.getDefaultOptions(true));
|
49 | this.setAllowedProperties(config_1.Config.getAllowedProperties());
|
50 | }
|
51 | get config() {
|
52 | return this.resolveProperties(this.globalConfig.getContents(), this.localConfig && this.localConfig.getContents());
|
53 | }
|
54 | /**
|
55 | * Get the static ConfigAggregator instance. If one doesn't exist, one will be created with
|
56 | * the **encrypted** config values. Encrypted config values need to be resolved
|
57 | * asynchronously by calling {@link ConfigAggregator.reload}
|
58 | */
|
59 | // Use typing from AsyncOptionalCreatable to support extending ConfigAggregator.
|
60 | // We really don't want ConfigAggregator extended but typescript doesn't support a final.
|
61 | static getInstance() {
|
62 | if (!ConfigAggregator.instance) {
|
63 | ConfigAggregator.instance = new this();
|
64 | ConfigAggregator.instance.loadPropertiesSync();
|
65 | }
|
66 | return ConfigAggregator.instance;
|
67 | }
|
68 | // Use typing from AsyncOptionalCreatable to support extending ConfigAggregator.
|
69 | // We really don't want ConfigAggregator extended but typescript doesn't support a final.
|
70 | static async create(options) {
|
71 | let config = ConfigAggregator.instance;
|
72 | if (!config) {
|
73 | config = ConfigAggregator.instance = new this(options);
|
74 | await config.init();
|
75 | }
|
76 | if (ConfigAggregator.encrypted) {
|
77 | await config.loadProperties();
|
78 | }
|
79 | return ConfigAggregator.instance;
|
80 | }
|
81 | /**
|
82 | * Get the info for a given key. If the ConfigAggregator was not asynchronously created OR
|
83 | * the {@link ConfigAggregator.reload} was not called, the config value may be encrypted.
|
84 | *
|
85 | * @param key The config key.
|
86 | */
|
87 | static getValue(key) {
|
88 | return this.getInstance().getInfo(key);
|
89 | }
|
90 | /**
|
91 | * Initialize this instances async dependencies.
|
92 | */
|
93 | async init() {
|
94 | await this.loadProperties();
|
95 | }
|
96 | /**
|
97 | * Get a resolved config property.
|
98 | *
|
99 | * **Throws** *{@link SfdxError}{ name: 'UnknownConfigKey' }* An attempt to get a property that's not supported.
|
100 | *
|
101 | * @param key The key of the property.
|
102 | */
|
103 | getPropertyValue(key) {
|
104 | if (this.getAllowedProperties().some((element) => key === element.key)) {
|
105 | return this.getConfig()[key];
|
106 | }
|
107 | else {
|
108 | throw new sfdxError_1.SfdxError(`Unknown config key: ${key}`, 'UnknownConfigKey');
|
109 | }
|
110 | }
|
111 | /**
|
112 | * Get a resolved config property.
|
113 | *
|
114 | * @param key The key of the property.
|
115 | */
|
116 | getInfo(key) {
|
117 | const location = this.getLocation(key);
|
118 | return {
|
119 | key,
|
120 | location,
|
121 | value: this.getPropertyValue(key),
|
122 | path: this.getPath(key),
|
123 | isLocal: () => location === "Local" /* LOCAL */,
|
124 | isGlobal: () => location === "Global" /* GLOBAL */,
|
125 | isEnvVar: () => location === "Environment" /* ENVIRONMENT */,
|
126 | };
|
127 | }
|
128 | /**
|
129 | * Gets a resolved config property location.
|
130 | *
|
131 | * For example, `getLocation('logLevel')` will return:
|
132 | * 1. `Location.GLOBAL` if resolved to an environment variable.
|
133 | * 1. `Location.LOCAL` if resolved to local project config.
|
134 | * 1. `Location.ENVIRONMENT` if resolved to the global config.
|
135 | *
|
136 | * @param key The key of the property.
|
137 | */
|
138 | getLocation(key) {
|
139 | if (this.getEnvVars().get(key) != null) {
|
140 | return "Environment" /* ENVIRONMENT */;
|
141 | }
|
142 | if (this.localConfig && this.localConfig.get(key)) {
|
143 | return "Local" /* LOCAL */;
|
144 | }
|
145 | if (this.globalConfig && this.globalConfig.get(key)) {
|
146 | return "Global" /* GLOBAL */;
|
147 | }
|
148 | }
|
149 | /**
|
150 | * Get a resolved file path or environment variable name of the property.
|
151 | *
|
152 | * For example, `getPath('logLevel')` will return:
|
153 | * 1. `$SFDX_LOG_LEVEL` if resolved to an environment variable.
|
154 | * 1. `./.sfdx/sfdx-config.json` if resolved to the local config.
|
155 | * 1. `~/.sfdx/sfdx-config.json` if resolved to the global config.
|
156 | * 1. `undefined`, if not resolved.
|
157 | *
|
158 | * **Note:** that the path returned may be the absolute path instead of
|
159 | * relative paths such as `./` and `~/`.
|
160 | *
|
161 | * @param key The key of the property.
|
162 | */
|
163 | getPath(key) {
|
164 | if (this.envVars[key] != null) {
|
165 | return `$${propertyToEnvName(key)}`;
|
166 | }
|
167 | if (this.localConfig && this.localConfig.getContents()[key] != null) {
|
168 | return this.localConfig.getPath();
|
169 | }
|
170 | if (this.globalConfig.getContents()[key] != null) {
|
171 | return this.globalConfig.getPath();
|
172 | }
|
173 | }
|
174 | /**
|
175 | * Get all resolved config property keys, values, locations, and paths.
|
176 | *
|
177 | * ```
|
178 | * > console.log(aggregator.getConfigInfo());
|
179 | * [
|
180 | * { key: 'logLevel', val: 'INFO', location: 'Environment', path: '$SFDX_LOG_LEVEL'}
|
181 | * { key: 'defaultusername', val: '<username>', location: 'Local', path: './.sfdx/sfdx-config.json'}
|
182 | * ]
|
183 | * ```
|
184 | */
|
185 | getConfigInfo() {
|
186 | const infos = Object.keys(this.getConfig())
|
187 | .filter((key) => this.getAllowedProperties().some((element) => key === element.key))
|
188 | .map((key) => this.getInfo(key))
|
189 | .filter((info) => !!info);
|
190 | return kit_1.sortBy(infos, 'key');
|
191 | }
|
192 | /**
|
193 | * Get the local project config instance.
|
194 | */
|
195 | getLocalConfig() {
|
196 | return this.localConfig;
|
197 | }
|
198 | /**
|
199 | * Get the global config instance.
|
200 | */
|
201 | getGlobalConfig() {
|
202 | return this.globalConfig;
|
203 | }
|
204 | /**
|
205 | * Get the resolved config object from the local, global and environment config instances.
|
206 | */
|
207 | getConfig() {
|
208 | return this.config;
|
209 | }
|
210 | /**
|
211 | * Get the config properties that are environment variables.
|
212 | */
|
213 | getEnvVars() {
|
214 | return new Map(ts_types_1.definiteEntriesOf(this.envVars));
|
215 | }
|
216 | /**
|
217 | * Re-read all property configurations from disk.
|
218 | */
|
219 | async reload() {
|
220 | await this.loadProperties();
|
221 | return this;
|
222 | }
|
223 | /**
|
224 | * Loads all the properties and aggregates them according to location.
|
225 | */
|
226 | async loadProperties() {
|
227 | this.resolveProperties(await this.globalConfig.read(), this.localConfig && (await this.localConfig.read()));
|
228 | ConfigAggregator.encrypted = false;
|
229 | }
|
230 | /**
|
231 | * Loads all the properties and aggregates them according to location.
|
232 | */
|
233 | loadPropertiesSync() {
|
234 | this.resolveProperties(this.globalConfig.readSync(), this.localConfig && this.localConfig.readSync());
|
235 | }
|
236 | resolveProperties(globalConfig, localConfig) {
|
237 | const accumulator = {};
|
238 | this.setEnvVars(this.getAllowedProperties().reduce((obj, property) => {
|
239 | const val = process.env[propertyToEnvName(property.key)];
|
240 | if (val != null) {
|
241 | obj[property.key] = val;
|
242 | }
|
243 | return obj;
|
244 | }, accumulator));
|
245 | // Global config must be read first so it is on the left hand of the
|
246 | // object assign and is overwritten by the local config.
|
247 | const configs = [globalConfig];
|
248 | // We might not be in a project workspace
|
249 | if (localConfig) {
|
250 | configs.push(localConfig);
|
251 | }
|
252 | configs.push(this.envVars);
|
253 | const json = {};
|
254 | const reduced = configs.filter(ts_types_1.isJsonMap).reduce((acc, el) => kit_1.merge(acc, el), json);
|
255 | return reduced;
|
256 | }
|
257 | /**
|
258 | * Get the allowed properties.
|
259 | */
|
260 | getAllowedProperties() {
|
261 | return this.allowedProperties;
|
262 | }
|
263 | /**
|
264 | * Set the allowed properties.
|
265 | *
|
266 | * @param properties The properties to set.
|
267 | */
|
268 | setAllowedProperties(properties) {
|
269 | this.allowedProperties = properties;
|
270 | }
|
271 | /**
|
272 | * Sets the env variables.
|
273 | *
|
274 | * @param envVars The env variables to set.
|
275 | */
|
276 | setEnvVars(envVars) {
|
277 | this.envVars = envVars;
|
278 | }
|
279 | }
|
280 | exports.ConfigAggregator = ConfigAggregator;
|
281 | ConfigAggregator.encrypted = true;
|
282 | //# sourceMappingURL=configAggregator.js.map |
\ | No newline at end of file |