@salesforce/core
Version:
Core libraries to interact with SFDX projects, orgs, and APIs.
196 lines • 7.42 kB
JavaScript
/*
* 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 proper_lockfile_1 = require("proper-lockfile");
const kit_1 = require("@salesforce/kit");
const global_1 = require("../../global");
const sfError_1 = require("../../sfError");
const lockRetryOptions_1 = require("../../util/lockRetryOptions");
exports.DEFAULT_GROUP = 'orgs';
exports.FILENAME = 'alias.json';
class AliasAccessor extends kit_1.AsyncOptionalCreatable {
// set in init method
fileLocation;
/** orgs is the default group */
aliasStore;
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
*
* @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(es). Writes to the file
*
*/
async unsetAndSave(alias) {
await this.readFileToAliasStore(true);
this.aliasStore.delete(alias);
return this.saveAliasStoreToFile();
}
/**
* 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();
}
/**
* 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_1.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);
}
}
exports.AliasAccessor = AliasAccessor;
/**
* Returns the username of given aliasable entity
*/
const getNameOf = (entity) => {
if (typeof entity === 'string')
return entity;
const aliaseeName = entity.username;
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);
// handle when alias file exists but is missing the org property
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
;