UNPKG

@salesforce/core

Version:

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

263 lines 9.83 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.getFileLocation = exports.AliasAccessor = exports.FILENAME = exports.DEFAULT_GROUP = void 0; const node_path_1 = require("node:path"); const node_os_1 = require("node:os"); const promises_1 = require("node:fs/promises"); const node_fs_1 = require("node:fs"); const proper_lockfile_1 = require("proper-lockfile"); const kit_1 = require("@salesforce/kit"); const global_1 = require("../../global"); const sfError_1 = require("../../sfError"); exports.DEFAULT_GROUP = 'orgs'; exports.FILENAME = 'alias.json'; const lockOptions = { stale: 10000 }; const lockRetryOptions = { ...lockOptions, retries: { retries: 10, maxTimeout: 1000, factor: 2 }, }; class AliasAccessor extends kit_1.AsyncOptionalCreatable { getAll(entity) { // This will only return aliases under "orgs". This will need to be modified // if/when we want to support more aliases groups. if (entity) { const nameFromEntity = getNameOf(entity); return Array.from(this.aliasStore.entries()) .filter(([, value]) => nameFromEntity === value) .map(([alias]) => alias); } else { return Object.fromEntries(this.aliasStore.entries()); } } /** * Returns the first alias found for a given entity * * @param entity the aliasable entity that you want to get the alias of */ get(entity) { return this.getAll(entity)[0] ?? null; } /** * Returns the value that corresponds to the given alias if it exists * * @param alias the alias that corresponds to a value */ getValue(alias) { return this.aliasStore.get(alias) ?? null; } /** * Returns the username that corresponds to the given alias if it exists * * @param alias the alias that corresponds to a username */ getUsername(alias) { return this.aliasStore.get(alias) ?? null; } /** * If the provided string is an alias, it returns the corresponding username. * If the provided string is not an alias, we assume that the provided string * is the username and return it. * * This method is helpful when you don't know if the string you have is a username * or an alias. * * @param usernameOrAlias a string that might be a username or might be an alias */ resolveUsername(usernameOrAlias) { return this.getUsername(usernameOrAlias) ?? usernameOrAlias; } /** * If the provided string is an alias, return it. * If the provided string is not an alias, return the username of the provided alias * * This method is helpful when you don't know if the string you have is a username * or an alias. * * @param usernameOrAlias a string that might be a username or might be an alias */ resolveAlias(usernameOrAlias) { if (this.aliasStore.has(usernameOrAlias)) return usernameOrAlias; return Array.from(this.aliasStore.entries()).find(([, value]) => value === usernameOrAlias)?.[0]; } /** * Set an alias for the given aliasable entity. Writes to the file * * @deprecated use setAndSave * @param alias the alias you want to set * @param entity the aliasable entity that's being aliased */ set(alias, entity) { // get a very fresh copy to merge with to avoid conflicts this.readFileToAliasStoreSync(); this.aliasStore.set(alias, getNameOf(entity)); this.saveAliasStoreToFileSync(); } /** * Set an alias for the given aliasable entity. Writes to the file * * @param alias the alias you want to set * @param entity the aliasable entity that's being aliased */ async setAndSave(alias, entity) { // get a very fresh copy to merge with to avoid conflicts, then lock await this.readFileToAliasStore(true); this.aliasStore.set(alias, getNameOf(entity)); return this.saveAliasStoreToFile(); } /** * Unset the given alias. Writes to the file * * @deprecated use unsetAndSave * */ unset(alias) { this.readFileToAliasStoreSync(); this.aliasStore.delete(alias); this.saveAliasStoreToFileSync(); } /** * Unset the given alias(es). Writes to the file * */ async unsetAndSave(alias) { await this.readFileToAliasStore(true); this.aliasStore.delete(alias); return this.saveAliasStoreToFile(); } /** * Unsets all the aliases for the given entity. * * @deprecated use unsetValuesAndSave * * @param entity the aliasable entity for which you want to unset all aliases */ unsetAll(entity) { this.readFileToAliasStoreSync(); const aliases = this.getAll(entity); aliases.forEach((a) => this.aliasStore.delete(a)); this.saveAliasStoreToFileSync(); } /** * Unset all the aliases for the given array of entity. * * @param entity the aliasable entity for which you want to unset all aliases */ async unsetValuesAndSave(aliasees) { await this.readFileToAliasStore(true); (0, kit_1.ensureArray)(aliasees) .flatMap((a) => this.getAll(a)) .map((a) => this.aliasStore.delete(a)); return this.saveAliasStoreToFile(); } /** * @deprecated the set/unset methods now write to the file when called. Use (un)setAndSave instead of calling (un)set and then calling write() */ async write() { return Promise.resolve(this.getAll()); } /** * Returns true if the provided alias exists * * @param alias the alias you want to check */ has(alias) { return this.aliasStore.has(alias); } async init() { this.fileLocation = (0, exports.getFileLocation)(); await this.readFileToAliasStore(); } /** * go to the fileSystem and read the file, storing a copy in the class's store * if the file doesn't exist, create it empty */ async readFileToAliasStore(useLock = false) { if (useLock) { await (0, proper_lockfile_1.lock)(this.fileLocation, lockRetryOptions); } try { this.aliasStore = fileContentsRawToAliasStore(await (0, promises_1.readFile)(this.fileLocation, 'utf-8')); } catch (e) { if (e instanceof Error && 'code' in e && e.code === 'ENOENT') { this.aliasStore = new Map(); await (0, promises_1.mkdir)((0, node_path_1.dirname)(this.fileLocation), { recursive: true }); await this.saveAliasStoreToFile(); return; } if (useLock) return unlockIfLocked(this.fileLocation); throw e; } } async saveAliasStoreToFile() { await (0, promises_1.writeFile)(this.fileLocation, aliasStoreToRawFileContents(this.aliasStore)); return unlockIfLocked(this.fileLocation); } /** * @deprecated use the async version of this method instead * provided for the legacy sync set/unset methods. */ readFileToAliasStoreSync() { // the file is guaranteed to exist because this init method ensures it // put a lock in place. This method is only used by legacy set/unset methods. (0, proper_lockfile_1.lockSync)(this.fileLocation, lockOptions); this.aliasStore = fileContentsRawToAliasStore((0, node_fs_1.readFileSync)(this.fileLocation, 'utf-8')); } /** * @deprecated use the async version of this method instead * provided for the legacy sync set/unset methods */ saveAliasStoreToFileSync() { (0, node_fs_1.writeFileSync)(this.fileLocation, aliasStoreToRawFileContents(this.aliasStore)); try { (0, proper_lockfile_1.unlockSync)(this.fileLocation); } catch (e) { // ignore the error. If it wasn't locked, that's what we wanted if (errorIsNotAcquired(e)) return; throw e; } } } exports.AliasAccessor = AliasAccessor; /** * Returns the username of given aliasable entity */ const getNameOf = (entity) => { if (typeof entity === 'string') return entity; const aliaseeName = entity.username ?? entity.user; if (!aliaseeName) { throw new sfError_1.SfError(`Invalid aliasee, it must contain a user or username property: ${JSON.stringify(entity)}`); } return aliaseeName; }; const fileContentsRawToAliasStore = (contents) => { const fileContents = JSON.parse(contents); return new Map(Object.entries(fileContents[exports.DEFAULT_GROUP])); }; const aliasStoreToRawFileContents = (aliasStore) => JSON.stringify({ [exports.DEFAULT_GROUP]: Object.fromEntries(Array.from(aliasStore.entries())) }); // exported for testSetup mocking const getFileLocation = () => (0, node_path_1.join)((0, node_os_1.homedir)(), global_1.Global.SFDX_STATE_FOLDER, exports.FILENAME); exports.getFileLocation = getFileLocation; const unlockIfLocked = async (fileLocation) => { try { await (0, proper_lockfile_1.unlock)(fileLocation); } catch (e) { // ignore the error. If it wasn't locked, that's what we wanted if (errorIsNotAcquired(e)) return; throw e; } }; const errorIsNotAcquired = (e) => e instanceof Error && 'code' in e && e.code === 'ENOTACQUIRED'; //# sourceMappingURL=aliasAccessor.js.map