@salesforce/core
Version:
Core libraries to interact with SFDX projects, orgs, and APIs.
263 lines • 9.83 kB
JavaScript
"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