sfdx-hardis
Version:
Swiss-army-knife Toolbox for Salesforce. Allows you to define a complete CD/CD Pipeline. Orchestrate base commands and assist users with interactive wizards
154 lines (153 loc) • 7.22 kB
JavaScript
/* jscpd:ignore-start */
import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import c from 'chalk';
import { execSfdxJson, uxLog } from '../../../../common/utils/index.js';
import { CONSTANTS, getEnvVar } from '../../../../config/index.js';
import { NotifProvider } from '../../../../common/notifProvider/index.js';
import { getNotificationButtons, getOrgMarkdown, getSeverityIcon } from '../../../../common/utils/notifUtils.js';
import { generateCsvFile, generateReportPath } from '../../../../common/utils/filesUtils.js';
import { setConnectionVariables } from '../../../../common/utils/orgUtils.js';
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('sfdx-hardis', 'org');
export default class MonitorLimits extends SfCommand {
static title = 'Check org limits';
static description = `Check limits of a SF org and send notifications about limits are superior to 50%, 75% or 100%.
This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/salesforce-monitoring-org-limits/) and can output Grafana, Slack and MsTeams Notifications.
`;
static examples = ['$ sf hardis:org:monitor:limits'];
static flags = {
outputfile: Flags.string({
char: 'f',
description: 'Force the path and name of output report file. Must end with .csv',
}),
debug: Flags.boolean({
char: 'd',
default: false,
description: messages.getMessage('debugMode'),
}),
websocket: Flags.string({
description: messages.getMessage('websocket'),
}),
skipauth: Flags.boolean({
description: 'Skip authentication check when a default username is required',
}),
'target-org': requiredOrgFlagWithDeprecations,
};
// Set this to true if your command requires a project workspace; 'requiresProject' is false by default
static requiresProject = true;
// Trigger notification(s) to MsTeams channel
static triggerNotification = true;
limitThresholdWarning = Number(getEnvVar('LIMIT_THRESHOLD_WARNING') || 50.0);
limitThresholdError = Number(getEnvVar('LIMIT_THRESHOLD_WARNING') || 75.0);
limitEntries = [];
outputFile;
outputFilesRes = {};
debugMode = false;
/* jscpd:ignore-end */
async run() {
const { flags } = await this.parse(MonitorLimits);
this.outputFile = flags.outputfile || null;
this.debugMode = flags.debug || false;
// List org limits
uxLog(this, c.cyan(`Run the org limits list command ...`));
const limitsCommandRes = await execSfdxJson(`sf org limits list`, this, {
fail: true,
output: true,
debug: this.debugMode,
});
this.limitEntries = limitsCommandRes.result
.filter((limit) => limit?.max > 0)
.map((limit) => {
limit.used = limit.max - limit.remaining;
if (limit.max && limit.max > 0) {
limit.percentUsed = parseFloat(((100 / limit.max) * limit.used).toFixed(2));
}
else {
limit.percentUsed = 0.0;
}
limit.severity =
limit.percentUsed > this.limitThresholdError
? 'error'
: limit.percentUsed > this.limitThresholdWarning
? 'warning'
: 'success';
limit.severityIcon = getSeverityIcon(limit.severity);
limit.label = limit.name.replace(/([A-Z])/g, ' $1');
return limit;
});
console.table(this.limitEntries);
this.outputFile = await generateReportPath('org-limits', this.outputFile);
this.outputFilesRes = await generateCsvFile(this.limitEntries, this.outputFile);
const limitsError = this.limitEntries.filter((limit) => limit.severity === 'error');
const numberLimitsError = limitsError.length;
const limitsWarning = this.limitEntries.filter((limit) => limit.severity === 'warning');
const numberLimitsWarning = limitsWarning.length;
// Build notifications
const orgMarkdown = await getOrgMarkdown(flags['target-org']?.getConnection()?.instanceUrl);
const notifButtons = await getNotificationButtons();
let notifSeverity = 'log';
let notifText = `No limit issues detected in ${orgMarkdown}`;
const notifAttachments = [];
// Dangerous limits has been found
if (numberLimitsError > 0) {
notifSeverity = 'error';
notifText = `Limit severe alerts have been detected in ${orgMarkdown} (error: ${numberLimitsError}, warning: ${numberLimitsWarning})`;
const errorText = `*Error Limits*\n${limitsError
.map((limit) => {
return `• ${limit.name}: *${limit.percentUsed}%* used (${limit.used}/${limit.max})`;
})
.join('\n')}`;
notifAttachments.push({
text: errorText,
});
uxLog(this, c.red(notifText + '\n' + errorText));
process.exitCode = 1;
}
// Warning limits detected
else if (numberLimitsWarning > 0) {
notifSeverity = 'warning';
notifText = `Limit warning alerts have been detected in ${orgMarkdown} (${numberLimitsWarning})`;
const warningText = `*Warning Limits*\n${limitsWarning
.map((limit) => {
return `• ${limit.name}: *${limit.percentUsed}%* used (${limit.used}/${limit.max})`;
})
.join('\n')}`;
notifAttachments.push({
text: warningText,
});
uxLog(this, c.yellow(notifText + '\n' + warningText));
}
else {
uxLog(this, c.green('No limit issue has been found'));
}
const limitEntriesMap = {};
const limitMetricsMap = {};
for (const limit of this.limitEntries) {
limitEntriesMap[limit.name] = limit;
limitMetricsMap[limit.name] = {
value: limit.used,
max: limit.max,
percent: limit.percentUsed,
};
}
// Post notifications
await setConnectionVariables(flags['target-org']?.getConnection()); // Required for some notifications providers like Email
await NotifProvider.postNotifications({
type: 'ORG_LIMITS',
text: notifText,
buttons: notifButtons,
attachments: notifAttachments,
severity: notifSeverity,
attachedFiles: this.outputFilesRes.xlsxFile ? [this.outputFilesRes.xlsxFile] : [],
logElements: this.limitEntries,
data: { metric: numberLimitsWarning + numberLimitsError, limits: limitEntriesMap },
metrics: limitMetricsMap,
});
return {
outputString: 'Limits check on org ' + flags['target-org'].getConnection().instanceUrl,
limitEntries: this.limitEntries,
};
}
}
//# sourceMappingURL=limits.js.map