UNPKG

14 kBJavaScriptView Raw
1"use strict";
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 */
8Object.defineProperty(exports, "__esModule", { value: true });
9exports.SfdxConfigAggregator = exports.ConfigAggregator = void 0;
10const kit_1 = require("@salesforce/kit");
11const ts_types_1 = require("@salesforce/ts-types");
12const messages_1 = require("../messages");
13const envVars_1 = require("./envVars");
14const config_1 = require("./config");
15messages_1.Messages.importMessagesDirectory(__dirname);
16const messages = messages_1.Messages.load('@salesforce/core', 'config', ['unknownConfigKey', 'deprecatedConfigKey']);
17/**
18 * Aggregate global and local project config files, as well as environment variables for
19 * `config.json`. The resolution happens in the following bottom-up order:
20 *
21 * 1. Environment variables (`SF_LOG_LEVEL`)
22 * 1. Workspace settings (`<workspace-root>/.sf/config.json`)
23 * 1. Global settings (`$HOME/.sf/config.json`)
24 *
25 * Use {@link ConfigAggregator.create} to instantiate the aggregator.
26 *
27 * ```
28 * const aggregator = await ConfigAggregator.create();
29 * console.log(aggregator.getPropertyValue('target-org'));
30 * ```
31 */
32class ConfigAggregator extends kit_1.AsyncOptionalCreatable {
33 /**
34 * **Do not directly construct instances of this class -- use {@link ConfigAggregator.create} instead.**
35 *
36 * @ignore
37 */
38 constructor(options) {
39 super(options ?? {});
40 this.envVars = {};
41 // Don't throw an project error with the aggregator, since it should resolve to global if
42 // there is no project.
43 try {
44 this.localConfig = new config_1.Config(config_1.Config.getDefaultOptions(false));
45 }
46 catch (err) {
47 if (err.name !== 'InvalidProjectWorkspaceError') {
48 throw err;
49 }
50 }
51 this.globalConfig = new config_1.Config(config_1.Config.getDefaultOptions(true));
52 this.setAllowedProperties(config_1.Config.getAllowedProperties());
53 }
54 get config() {
55 return this.resolveProperties(this.globalConfig.getContents(), this.localConfig?.getContents());
56 }
57 // Use typing from AsyncOptionalCreatable to support extending ConfigAggregator.
58 // We really don't want ConfigAggregator extended but typescript doesn't support a final.
59 static async create(options) {
60 let config = ConfigAggregator.instance;
61 if (!config) {
62 config = ConfigAggregator.instance = new this(options);
63 await config.init();
64 }
65 if (ConfigAggregator.encrypted) {
66 await config.loadProperties();
67 }
68 if (options?.customConfigMeta) {
69 config_1.Config.addAllowedProperties(options.customConfigMeta);
70 }
71 return ConfigAggregator.instance;
72 }
73 /**
74 * Get the info for a given key. If the ConfigAggregator was not asynchronously created OR
75 * the {@link ConfigAggregator.reload} was not called, the config value may be encrypted.
76 *
77 * @param key The config key.
78 */
79 static getValue(key) {
80 return this.getInstance().getInfo(key);
81 }
82 /**
83 * Get the static ConfigAggregator instance. If one doesn't exist, one will be created with
84 * the **encrypted** config values. Encrypted config values need to be resolved
85 * asynchronously by calling {@link ConfigAggregator.reload}
86 */
87 // Use typing from AsyncOptionalCreatable to support extending ConfigAggregator.
88 // We really don't want ConfigAggregator extended but typescript doesn't support a final.
89 static getInstance() {
90 if (!ConfigAggregator.instance) {
91 ConfigAggregator.instance = new this();
92 ConfigAggregator.instance.loadPropertiesSync();
93 }
94 return ConfigAggregator.instance;
95 }
96 /**
97 * Initialize this instances async dependencies.
98 */
99 async init() {
100 await this.loadProperties();
101 }
102 /**
103 * Get a resolved config property.
104 *
105 * **Throws** *{@link SfError}{ name: 'UnknownConfigKeyError' }* An attempt to get a property that's not supported.
106 *
107 * @param key The key of the property.
108 */
109 getPropertyValue(key) {
110 if (this.getAllowedProperties().some((element) => key === element.key)) {
111 return this.getConfig()[key];
112 }
113 else {
114 throw messages.createError('unknownConfigKey', [key]);
115 }
116 }
117 /**
118 * Get a resolved config property meta.
119 *
120 * **Throws** *{@link SfError}{ name: 'UnknownConfigKeyError' }* An attempt to get a property that's not supported.
121 *
122 * @param key The key of the property.
123 */
124 getPropertyMeta(key) {
125 const match = this.getAllowedProperties().find((element) => key === element.key);
126 if (match) {
127 return match;
128 }
129 else {
130 throw messages.createError('unknownConfigKey', [key]);
131 }
132 }
133 /**
134 * Get a resolved config property.
135 *
136 * @param key The key of the property.
137 * @param throwOnDeprecation True, if you want an error throw when reading a deprecated config
138 */
139 getInfo(key, throwOnDeprecation = false) {
140 const meta = this.getPropertyMeta(key);
141 if (throwOnDeprecation && meta.deprecated && meta.newKey) {
142 throw messages.createError('deprecatedConfigKey', [key, meta.newKey]);
143 }
144 const location = this.getLocation(key);
145 return {
146 key,
147 location,
148 value: this.getPropertyValue(key),
149 path: this.getPath(key),
150 isLocal: () => location === "Local" /* ConfigAggregator.Location.LOCAL */,
151 isGlobal: () => location === "Global" /* ConfigAggregator.Location.GLOBAL */,
152 isEnvVar: () => location === "Environment" /* ConfigAggregator.Location.ENVIRONMENT */,
153 deprecated: meta.deprecated ?? false,
154 };
155 }
156 /**
157 * Gets a resolved config property location.
158 *
159 * For example, `getLocation('logLevel')` will return:
160 * 1. `Location.GLOBAL` if resolved to an environment variable.
161 * 1. `Location.LOCAL` if resolved to local project config.
162 * 1. `Location.ENVIRONMENT` if resolved to the global config.
163 *
164 * @param key The key of the property.
165 */
166 getLocation(key) {
167 if (this.envVars[key] != null) {
168 return "Environment" /* ConfigAggregator.Location.ENVIRONMENT */;
169 }
170 if (this.localConfig?.get(key)) {
171 return "Local" /* ConfigAggregator.Location.LOCAL */;
172 }
173 if (this.globalConfig?.get(key)) {
174 return "Global" /* ConfigAggregator.Location.GLOBAL */;
175 }
176 }
177 /**
178 * Get a resolved file path or environment variable name of the property.
179 *
180 * For example, `getPath('logLevel')` will return:
181 * 1. `$SF_LOG_LEVEL` if resolved to an environment variable.
182 * 1. `./.sf/config.json` if resolved to the local config.
183 * 1. `~/.sf/config.json` if resolved to the global config.
184 * 1. `undefined`, if not resolved.
185 *
186 * **Note:** that the path returned may be the absolute path instead of
187 * relative paths such as `./` and `~/`.
188 *
189 * @param key The key of the property.
190 */
191 getPath(key) {
192 if (this.envVars[key] != null) {
193 return `$${envVars_1.EnvVars.propertyToEnvName(key)}`;
194 }
195 if (this.localConfig?.getContents()[key] != null) {
196 return this.localConfig.getPath();
197 }
198 if (this.globalConfig.getContents()[key] != null) {
199 return this.globalConfig.getPath();
200 }
201 }
202 /**
203 * Get all resolved config property keys, values, locations, and paths.
204 *
205 * ```
206 * > console.log(aggregator.getConfigInfo());
207 * [
208 * { key: 'logLevel', val: 'INFO', location: 'Environment', path: '$SF_LOG_LEVEL'}
209 * { key: 'target-org', val: '<username>', location: 'Local', path: './.sf/config.json'}
210 * ]
211 * ```
212 */
213 getConfigInfo() {
214 const infos = Object.keys(this.getConfig())
215 .filter((key) => this.getAllowedProperties().some((element) => key === element.key))
216 .map((key) => this.getInfo(key))
217 .filter((info) => !!info);
218 return (0, kit_1.sortBy)(infos, 'key');
219 }
220 /**
221 * Get the local project config instance.
222 */
223 getLocalConfig() {
224 return this.localConfig;
225 }
226 /**
227 * Get the global config instance.
228 */
229 getGlobalConfig() {
230 return this.globalConfig;
231 }
232 /**
233 * Get the resolved config object from the local, global and environment config instances.
234 */
235 getConfig() {
236 return this.config;
237 }
238 /**
239 * Get the config properties that are environment variables.
240 */
241 getEnvVars() {
242 return this.envVars;
243 }
244 /**
245 * Re-read all property configurations from disk.
246 */
247 async reload() {
248 await this.loadProperties();
249 return this;
250 }
251 /**
252 * Add an allowed config property.
253 */
254 addAllowedProperties(configMetas) {
255 if ((0, ts_types_1.isArray)(configMetas)) {
256 this.allowedProperties.push(...configMetas);
257 }
258 else {
259 this.allowedProperties.push(configMetas);
260 }
261 }
262 /**
263 * Set the allowed properties.
264 *
265 * @param properties The properties to set.
266 */
267 setAllowedProperties(properties) {
268 this.allowedProperties = properties;
269 }
270 /**
271 * Get the allowed properties.
272 */
273 getAllowedProperties() {
274 return this.allowedProperties;
275 }
276 /**
277 * Loads all the properties and aggregates them according to location.
278 */
279 async loadProperties() {
280 this.resolveProperties(await this.globalConfig.read(), this.localConfig && (await this.localConfig.read()));
281 ConfigAggregator.encrypted = false;
282 }
283 /**
284 * Loads all the properties and aggregates them according to location.
285 */
286 loadPropertiesSync() {
287 this.resolveProperties(this.globalConfig.readSync(), this.localConfig?.readSync());
288 }
289 resolveProperties(globalConfig, localConfig) {
290 const envVars = new envVars_1.EnvVars();
291 for (const property of this.getAllowedProperties()) {
292 const key = property.newKey ? property.newKey : property.key;
293 const value = envVars.getPropertyFromEnv(property.key);
294 if (value)
295 this.envVars[key] = value;
296 }
297 // Global config must be read first so it is on the left hand of the
298 // object assign and is overwritten by the local config.
299 const configs = [globalConfig];
300 // We might not be in a project workspace
301 if (localConfig) {
302 configs.push(localConfig);
303 }
304 configs.push(this.envVars);
305 const json = {};
306 const reduced = configs.filter(ts_types_1.isJsonMap).reduce((acc, el) => (0, kit_1.merge)(acc, el), json);
307 return reduced;
308 }
309}
310exports.ConfigAggregator = ConfigAggregator;
311ConfigAggregator.encrypted = true;
312/**
313 * A ConfigAggregator that will work with deprecated config vars (e.g. defaultusername, apiVersion).
314 * We do NOT recommend using this class unless you absolutely have to.
315 *
316 * @deprecated
317 */
318class SfdxConfigAggregator extends ConfigAggregator {
319 static async create(options = {}) {
320 const customConfigMeta = options.customConfigMeta ?? [];
321 // org-metadata-rest-deploy has been moved to plugin-deploy-retrieve but we need to have a placeholder
322 // for it here since sfdx needs to know how to set the deprecated restDeploy config var.
323 const restDeploy = config_1.SFDX_ALLOWED_PROPERTIES.find((p) => p.key === config_1.SfdxPropertyKeys.REST_DEPLOY);
324 const orgRestDeploy = Object.assign({}, restDeploy, { key: 'org-metadata-rest-deploy', deprecated: false });
325 options.customConfigMeta = [...customConfigMeta, orgRestDeploy];
326 let config = SfdxConfigAggregator.instance;
327 if (!config) {
328 config = SfdxConfigAggregator.instance = new this(options);
329 await config.init();
330 }
331 if (SfdxConfigAggregator.encrypted) {
332 await config.loadProperties();
333 }
334 if (options?.customConfigMeta) {
335 config_1.Config.addAllowedProperties(options.customConfigMeta);
336 }
337 return SfdxConfigAggregator.instance;
338 }
339 getPropertyMeta(key) {
340 const match = this.getAllowedProperties().find((element) => key === element.key);
341 if (match?.deprecated && match?.newKey) {
342 return this.getPropertyMeta(match.newKey);
343 }
344 else if (match) {
345 return match;
346 }
347 else {
348 throw messages.createError('unknownConfigKey', [key]);
349 }
350 }
351 getPropertyValue(key) {
352 return super.getPropertyValue(this.translate(key));
353 }
354 getInfo(key) {
355 const info = super.getInfo(this.translate(key));
356 info.key = this.translate(info.key, 'toOld');
357 return info;
358 }
359 getLocation(key) {
360 return super.getLocation(this.translate(key));
361 }
362 getPath(key) {
363 return super.getPath(this.translate(key));
364 }
365 getConfigInfo() {
366 return super.getConfigInfo().map((c) => {
367 c.key = this.translate(c.key, 'toOld');
368 return c;
369 });
370 }
371 translate(key, direction = 'toNew') {
372 const propConfig = direction === 'toNew'
373 ? this.getPropertyMeta(key)
374 : config_1.Config.getAllowedProperties().find((c) => c.newKey === key) ?? {};
375 return propConfig.key || key;
376 }
377}
378exports.SfdxConfigAggregator = SfdxConfigAggregator;
379SfdxConfigAggregator.encrypted = true;
380//# sourceMappingURL=configAggregator.js.map
\No newline at end of file