/**
* @copyright 2023 Tridium, Inc. All Rights Reserved.
*/
/**
* API Status: **Developer**
* @module nmodule/js/rc/errors/LocalizableError
*/
define([
'baja!',
'lex!',
'underscore',
'Promise' ], function (
baja,
lex,
_,
Promise) {
'use strict';
/**
* Makes sure the params are an array and also escapes them correctly if they are a string
* @param {Array.<*>|*} params the parameters that need to be processed
* @returns {String} the parameters as a String with each entry escaped and separted by a ":"
*/
function convertParams(params) {
params = Array.isArray(params) ? params : [ params ];
return params.map((param) => (_.isString(param) ? baja.SlotPath.escape(param) : param)).join(':');
}
/**
* Provides a simple means of throwing, or rejecting with, an Error that is intended to be
* displayed to the user.
*
* (Previously, in order to both throw an error and display an error dialog to the user, you
* would have to do it in two steps: display the error dialog with `feDialogs.error` or similar,
* and then throw the actual error so the Promise itself would reject.)
*
* To use `LocalizableError`, simply create and throw an instance of it. Many places in the
* framework are capable of handling `LocalizableError` and displaying it to the user:
*
* - The `log!` plugin
* - `baja.error()`
* - `feDialogs.error()`
* - And transitively, any functionality in the UI that is configured to log/display Errors
* through these APIs, including:
* - `Command` instances that are invoked through `CommandButtons` (via the HTML5 Hx Profile,
* Manager views, etc.)
* - Hyperlinking between views
*
* It follows that if you display the error yourself, and _also_ throw it:
*
* ```
* this.validators().add((value) => {
* if (value > max) {
* const err = new LocalizableError('%lexicon(myModule:valueTooBig)%');
* return feDialogs.error(err)
* .then(() => { throw err; });
* }
* });
* ```
*
* the framework will not know that you already displayed the error, and will display it a second
* time. `LocalizableError` is intended to simply be thrown by your code, not displayed _and_
* thrown.
*
* @since Niagara 4.14
* @class
* @alias module:nmodule/js/rc/errors/LocalizableError
* @extends Error
*/
class LocalizableError extends Error {
/**
* Creates a LocalizableError
* @param {String|Object} params either an error message as a string or an object that contains
* the values for a formatted error
* @param {String} [params.module] the module name to use for looking up the lexicon entries
* @param {String} [params.lex] the lexicon key to look up the error message components
* @param {String} [params.title] the title for the error. If both lexicon information and title
* is supplied the title overrides any title found in the lexicon. This can be in the format of
* a Lexicon format. For instance, `%lexicon(moduleName:keyName)%`.
* @param {Array<*>} [params.titleArgs] an array of value to be passed to the 'lex.format' command
* to complete the title. Only works with module and lex.
* @param {String} [params.summaryMessage] the summaryMessage for the error that is prepended to
* the error message. If both lexicon information and summaryMessage is supplied the summaryMessage
* overrides any summaryMessage found in the lexicon This can be in the format of * a Lexicon
* format. For instance, `%lexicon(moduleName:keyName)%`.
* @param {Array<*>} [params.summaryArgs] an array of value to be passed to the 'lex.format'
* command to complete the summary message. Only works with module and lex.
* @param {String} [params.message] the error message for the error. If both lexicon
* information and message is supplied the message overrides any message found in the lexicon
* This can be in the format of a Lexicon format. For instance, `%lexicon(moduleName:keyName)%`.
* @param {Array<*>} [params.messageArgs] an array of value to be passed to the 'lex.format' command
* to complete the message. Only works with module and lex.
*/
constructor(params = {}) {
let { module, lex, title, titleArgs, summaryMessage, summaryArgs, message, messageArgs } = params;
if (!(params instanceof Object)) {
message = params;
}
if (module && lex) {
if (!title) {
title = '%lexicon(' + module + ':' + lex + '.title';
if (titleArgs) {
title = title + ':' + convertParams(titleArgs);
}
title = title + ')%';
}
if (!summaryMessage) {
summaryMessage = '%lexicon(' + module + ':' + lex + '.summaryMessage';
if (summaryArgs) {
summaryMessage = summaryMessage + ':' + convertParams(summaryArgs);
}
summaryMessage = summaryMessage + ')%';
}
if (!message) {
message = '%lexicon(' + module + ':' + lex + '.message';
if (messageArgs) {
message = message + ':' + convertParams(messageArgs);
}
message = message + ')%';
}
}
super(message);
this.name = "LocalizableError";
this.$title = title;
this.$summaryMessage = summaryMessage;
}
/**
* Resolves the title for the error
* @returns {Promise.<String>}
*/
toTitle() {
return this.$doFormat(this.$title);
}
/**
* Resolves the message summary for the error
* @returns {Promise.<String>}
*/
toSummaryMessage() {
return this.$doFormat(this.$summaryMessage);
}
/**
* Resolves the message for the error
* @returns {Promise.<String>}
*/
toMessage() {
return this.$doFormat(this.message);
}
/**
* Resolves to a formatted string that holds the error stack
* @returns {Promise.<string>}
*/
toStack() {
let stack = this.stack;
if (stack && typeof stack === 'string') {
return Promise.all([
this.toSummaryMessage(),
this.toMessage()
])
.then(([ summary, message ]) => {
const name = this.name;
message = name + ": " + ((summary) ? summary + ": " + message : message);
if (this.message) {
let stackArray = stack.split('\n');
if (stackArray[0].indexOf(name + ": " + this.message) !== 0) {
stackArray.unshift(message);
} else {
stackArray[0] = message;
}
stack = stackArray.join('\n');
}
return stack;
});
}
return Promise.resolve(stack);
}
/**
* Checks to see if the string to be formatted and if undefined returns undefined.
* @private
* @param {String} str the string rot formatted
* @returns {Promise<String|undefined>}
*/
$doFormat(str) {
if (str === undefined) {
return Promise.resolve(undefined);
}
return lex.format(...arguments);
}
}
return LocalizableError;
});