@ti-engine/core 1.2.4 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,7 @@ let cacheCommandsEnum = tools.enum( {
28
28
  HASH_GET: [ "hget", "hash get", "https://redis.io/docs/latest/commands/hget/" ],
29
29
  HASH_GET_ALL: [ "hgetall", "hash get all", "https://redis.io/docs/latest/commands/hgetall/" ],
30
30
  HASH_REMOVE: [ "hdel", "hash remove", "https://redis.io/docs/latest/commands/hdel/" ],
31
+ HASH_EXPIRE: [ "hexpire", "hash expire", "https://redis.io/docs/latest/commands/hexpire/" ],
31
32
  HASH_SET: [ "hset", "", "https://redis.io/docs/latest/commands/hset/" ],
32
33
  HASH_SET_MANY: [ "hmset", "(deprecated) use HSET with multiple fields", "https://redis.io/docs/latest/commands/hmset/" ],
33
34
  IS_SET_MEMBER: [ "sismember", "", "https://redis.io/docs/latest/commands/sismember/" ],
@@ -43,6 +44,21 @@ let cacheCommandsEnum = tools.enum( {
43
44
  UNION_OF_SETS: [ "sunion", "union of sets", "https://redis.io/docs/latest/commands/sunion/" ]
44
45
  } );
45
46
 
47
+ /**
48
+ * Enum for listing all client statuses.
49
+ *
50
+ * @readonly
51
+ * @enum {number}
52
+ */
53
+ let clientStatusEnum = tools.enum( {
54
+ UNINITIALIZED: [ 0, "uninitialized", "Redis client is offline and not yet initialized." ],
55
+ CONNECTED: [ 1, "connected", "Redis client is connected and online." ],
56
+ CONNECTING: [ 2, "connecting", "Redis client is connecting to server." ],
57
+ DISRUPTED: [ 3, "disrupted", "Redis client is temporarily disconnected from server due to a disruption." ],
58
+ SHUTTING_DOWN: [ 4, "shutting down", "Redis client is shutting down." ],
59
+ DISCONNECTED: [ 5, "disconnected", "Redis client is permanently disconnected from server." ]
60
+ } );
61
+
46
62
  /**
47
63
  * Enum for listing the Redis key override modes.
48
64
  *
@@ -63,6 +79,10 @@ module.exports.cacheCommands = cacheCommandsEnum;
63
79
  * @typedef {string} TiRedisOverrideMode
64
80
  */
65
81
  module.exports.cacheOverrideMode = cacheOverrideModeEnum;
82
+ /**
83
+ * @typedef {number} TiRedisClientStatus
84
+ */
85
+ module.exports.clientStatus = clientStatusEnum;
66
86
 
67
87
  /**
68
88
  * Used to create a Redis Cache client.
@@ -76,6 +96,7 @@ module.exports.cacheOverrideMode = cacheOverrideModeEnum;
76
96
  class RedisClient {
77
97
 
78
98
  #clientIdentifier;
99
+ #clientStatus = clientStatusEnum.UNINITIALIZED;
79
100
  #retryMaxInterval = 1000;
80
101
  #retryMaxAttempts = undefined;
81
102
  #redisConnection = undefined;
@@ -118,6 +139,17 @@ class RedisClient {
118
139
  return this.#redisClientID;
119
140
  }
120
141
 
142
+ /**
143
+ * Used to return the Redis client status.
144
+ *
145
+ * @property
146
+ * @returns {number}
147
+ * @public
148
+ */
149
+ get clientStatus() {
150
+ return this.#clientStatus;
151
+ }
152
+
121
153
  /**
122
154
  * Used to return the Redis server version.
123
155
  *
@@ -167,12 +199,6 @@ class RedisClient {
167
199
  } ).then( ( clientID ) => {
168
200
  // Store the client ID:
169
201
  this.#redisClientID = clientID;
170
-
171
- // Notify all connection observers about the initialization success:
172
- _.forEach( this.#connectionObservers, ( connectionObservers ) => {
173
- connectionObservers.onConnectionRecovered( this.#clientIdentifier );
174
- } );
175
-
176
202
  resolve();
177
203
  } ).catch( ( error ) => {
178
204
  reject( exceptions.raise( error ) );
@@ -359,6 +385,7 @@ class RedisClient {
359
385
  shutDown( timeoutMs = 1000 ) {
360
386
  return new Promise( ( resolve ) => {
361
387
  let finished = false;
388
+ this.#clientStatus = clientStatusEnum.SHUTTING_DOWN;
362
389
  const done = () => {
363
390
  if ( !finished ) {
364
391
  finished = true;
@@ -403,7 +430,8 @@ class RedisClient {
403
430
  * @param {number} defaultDB
404
431
  * @param {number} [retryMaxIntervalMs=1000] Optional max backoff interval.
405
432
  * @param {number|undefined} [retryMaxAttempts=undefined] Optional max (re)connection attempts before abort.
406
- * @public
433
+ * @returns {Promise}
434
+ * @private
407
435
  */
408
436
  #setupClient( host, port, authKey, user, defaultDB, retryMaxIntervalMs, retryMaxAttempts ) {
409
437
  return new Promise( ( resolve, reject ) => {
@@ -412,19 +440,22 @@ class RedisClient {
412
440
  this.#retryMaxAttempts = retryMaxAttempts;
413
441
 
414
442
  let retryStrategy = ( attempt ) => {
415
- let result = Math.min( attempt * 50, this.#retryMaxInterval );
416
-
443
+ let retryInterval = Math.min( attempt * 50, this.#retryMaxInterval );
417
444
  if ( this.#retryMaxAttempts != null && attempt > this.#retryMaxAttempts ) {
418
445
  logger.log( "In Redis retry strategy: reached max attempts for command retry. Aborting...", logger.logSeverity.WARNING, { attempts: attempt } );
419
- result = exceptions.raise( exceptions.exceptionCode.E_COM_RETRY_ATTEMPTS_EXCEEDED );
446
+ retryInterval = exceptions.raise( exceptions.exceptionCode.E_COM_RETRY_ATTEMPTS_EXCEEDED );
420
447
  }
421
-
422
- return result;
448
+ return retryInterval;
423
449
  };
424
450
 
425
451
  let reconnectOnError = ( error ) => {
426
452
  logger.log( `In Redis reconnect on error strategy: ${ error.message }`, logger.logSeverity.ERROR, error );
427
- return !!error.message.includes( "READONLY" );
453
+ if ( error.message.includes( "READONLY" ) ) {
454
+ // Returning 2 will also resubmit the failed command:
455
+ return 2;
456
+ } else {
457
+ return 0;
458
+ }
428
459
  };
429
460
 
430
461
  let options = {
@@ -441,22 +472,33 @@ class RedisClient {
441
472
 
442
473
  /** @type Redis */
443
474
  this.#redisConnection = new Redis( options );
475
+ this.#clientStatus = clientStatusEnum.CONNECTING;
476
+
477
+ this.#redisConnection.once( "ready", () => {
478
+ this.#clientStatus = clientStatusEnum.CONNECTED;
479
+ this.#notifyConnectionObservers();
480
+
481
+ this.#redisConnection.on( "ready", () => {
482
+ this.#clientStatus = clientStatusEnum.CONNECTED;
483
+ this.#notifyConnectionObservers();
484
+ } );
444
485
 
445
- this.#redisConnection.on( "ready", () => {
446
- logger.log( `Connection to Redis server ${ host }:${ port } (re)established by client '${ this.identifier }' and is ready to be used.`, logger.logSeverity.INFO );
447
486
  resolve();
448
487
  } );
449
488
  this.#redisConnection.on( "error", ( error ) => {
489
+ this.#clientStatus = clientStatusEnum.DISRUPTED;
450
490
  logger.log( `Error received in Redis client '${ this.identifier }'.`, logger.logSeverity.ERROR, error );
451
-
452
- // Notify all connection observers about this event:
453
- _.forEach( this.#connectionObservers, ( connectionObservers ) => {
454
- connectionObservers.onConnectionDisrupted( this.#clientIdentifier );
455
- } );
491
+ this.#notifyConnectionObservers();
492
+ } );
493
+ this.#redisConnection.on( "reconnecting", ( retryInterval ) => {
494
+ this.#clientStatus = clientStatusEnum.CONNECTING;
495
+ logger.log( `Client '${ this.identifier }' reconnecting to Redis server after ${ retryInterval } ms.`, logger.logSeverity.DEBUG );
456
496
  } );
457
- this.#redisConnection.on( "reconnecting", ( info ) => {
458
- if ( info.attempt > 1 ) {
459
- logger.log( `Client '${ this.identifier }' reconnecting to Redis server after ${ info.delay } ms. This is attempt ${ info.attempt }.`, logger.logSeverity.DEBUG );
497
+ this.#redisConnection.on( "end", () => {
498
+ if ( this.#clientStatus !== clientStatusEnum.SHUTTING_DOWN ) {
499
+ this.#clientStatus = clientStatusEnum.DISCONNECTED;
500
+ logger.log( `Client '${ this.identifier }' cannot reconnect to Redis server and has been shut down.`, logger.logSeverity.WARNING );
501
+ this.#notifyConnectionObservers();
460
502
  }
461
503
  } );
462
504
  } catch ( error ) {
@@ -465,10 +507,31 @@ class RedisClient {
465
507
  } );
466
508
  }
467
509
 
510
+ /**
511
+ * Used to notify all connection observers about the current connection state.
512
+ *
513
+ * @method
514
+ * @private
515
+ */
516
+ #notifyConnectionObservers() {
517
+ // Notify all connection observers about the event:
518
+ _.forEach( this.#connectionObservers, ( connectionObservers ) => {
519
+ if ( this.#clientStatus === clientStatusEnum.CONNECTED ) {
520
+ logger.log( `Connection to Redis server ${ this.#redisConnection.options.host }:${ this.#redisConnection.options.port } (re)established by client '${ this.identifier }' and is ready to be used.`, logger.logSeverity.INFO );
521
+ connectionObservers.onConnectionRecovered( this.#clientIdentifier );
522
+ } else if ( this.#clientStatus === clientStatusEnum.DISRUPTED ) {
523
+ connectionObservers.onConnectionDisrupted( this.#clientIdentifier );
524
+ } else if ( this.#clientStatus === clientStatusEnum.DISCONNECTED ) {
525
+ connectionObservers.onConnectionLost( this.#clientIdentifier );
526
+ }
527
+ } );
528
+ }
529
+
468
530
  /**
469
531
  * Used to fetch and store Redis server information.
470
532
  *
471
533
  * @method
534
+ * @returns {Promise}
472
535
  * @private
473
536
  */
474
537
  #fetchServerInfo() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ti-engine/core",
3
- "version": "1.2.4",
3
+ "version": "1.3.3",
4
4
  "description": "The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.",
5
5
  "author": "Boris Kostadinov <kostadinov.boris@gmail.com>",
6
6
  "license": "GPL-3.0-or-later",
package/utils/cache.js CHANGED
@@ -124,6 +124,22 @@ class CommonMemoryCache extends ConnectionObserver {
124
124
  }
125
125
  }
126
126
 
127
+ /**
128
+ * Needs to be invoked by the connection handler when the connection is irrevocably lost.
129
+ *
130
+ * @method
131
+ * @param {string} identifier The identifier of the observed connection.
132
+ * @override
133
+ * @public
134
+ */
135
+ onConnectionLost( identifier ) {
136
+ if ( identifier === this.#connectionIdentifier ) {
137
+ this.#isOperational = false;
138
+ // TODO: implement forced shut down of the service instance instead of crashing it outright
139
+ throw exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE );
140
+ }
141
+ }
142
+
127
143
  /**
128
144
  * Used to register a new {@link ConnectionObserver} for events related to the underlying Redis connection state.
129
145
  *
@@ -317,13 +333,19 @@ class CommonMemoryCache extends ConnectionObserver {
317
333
  * @method
318
334
  * @param {string} key
319
335
  * @param {number} seconds
336
+ * @param {string} [name] If you need to expire a field in a hash set instead, provide the name of the set here.
320
337
  * @returns {Promise<number>} This will resolve with the seconds as provided initially by the caller.
321
338
  * @public
322
339
  */
323
- expireValue( key, seconds ) {
340
+ expireValue( key, seconds, name ) {
324
341
  return new Promise( ( resolve, reject ) => {
325
342
  if ( this.#isOperational === true ) {
326
- let commandExpire = [ redis.cacheCommands.EXPIRE, key, seconds ];
343
+ let commandExpire = [];
344
+ if ( name ) {
345
+ commandExpire = [ redis.cacheCommands.HASH_EXPIRE, name, seconds, "FIELDS", 1, key ];
346
+ } else {
347
+ commandExpire = [ redis.cacheCommands.EXPIRE, key, seconds ];
348
+ }
327
349
  this.#redisClient.executeCommands( [ commandExpire ] ).then( () => {
328
350
  resolve( seconds );
329
351
  } ).catch( ( error ) => {
package/utils/config.js CHANGED
@@ -73,7 +73,7 @@ const tools = require( "#tools" );
73
73
  /**
74
74
  * @typedef {Object} SettingsLocalization
75
75
  * @property {Array<string>} labelsPath
76
- * @property {string} language
76
+ * @property {TiLocalizationLanguage} language
77
77
  */
78
78
 
79
79
  /**
@@ -113,7 +113,7 @@ const tools = require( "#tools" );
113
113
  * @readonly
114
114
  * @enum {string} Keys of this ENUM are strings.
115
115
  */
116
- let settingsEnum = tools.enum( {
116
+ const settingsEnum = tools.enum( {
117
117
  AUDITING_LOG_CONSOLE_ENABLED: [ "auditing.logConsoleEnabled", "logConsoleEnabled", "" ],
118
118
  AUDITING_LOG_DETAILS: [ "auditing.logDetails", "logDetails", "" ],
119
119
  AUDITING_LOG_MIN_LEVEL: [ "auditing.logMinLevel", "logMinLevel", "" ],
@@ -152,7 +152,7 @@ module.exports.setting = settingsEnum;
152
152
  /** @type {SettingsMain} */
153
153
  const settings = require( "#settings" );
154
154
 
155
- // override remaining settings with ENV variables (if provided):
155
+ // Override the remaining settings with ENV variables (if provided):
156
156
  if ( settings.auditing ) {
157
157
  settings.auditing.logConsoleEnabled = ( process.env.TI_AUDITING_LOG_CONSOLE_ENABLED !== undefined ) ? tools.toBool( process.env.TI_AUDITING_LOG_CONSOLE_ENABLED ) : settings.auditing.logConsoleEnabled;
158
158
  settings.auditing.logDetails = ( process.env.TI_AUDITING_LOG_DETAILS !== undefined ) ? tools.toBool( process.env.TI_AUDITING_LOG_DETAILS ) : settings.auditing.logDetails;
@@ -160,7 +160,7 @@ if ( settings.auditing ) {
160
160
  settings.auditing.logUsesJSON = ( process.env.TI_AUDITING_LOG_USES_JSON !== undefined ) ? tools.toBool( process.env.TI_AUDITING_LOG_USES_JSON ) : settings.auditing.logUsesJSON;
161
161
  }
162
162
  if ( settings.localization ) {
163
- settings.localization.labelsPath = ( process.env.TI_LOCALIZATION_LABELS_PATH !== undefined ) ? process.env.TI_LOCALIZATION_LABELS_PATH : settings.localization.labelsPath;
163
+ settings.localization.labelsPath = ( process.env.TI_LOCALIZATION_LABELS_PATH !== undefined ) ? [ process.env.TI_LOCALIZATION_LABELS_PATH ] : settings.localization.labelsPath;
164
164
  settings.localization.language = ( process.env.TI_LOCALIZATION_LANGUAGE !== undefined ) ? process.env.TI_LOCALIZATION_LANGUAGE : settings.localization.language;
165
165
  }
166
166
  if ( settings.memoryCache ) {
@@ -186,7 +186,7 @@ if ( process.env.TI_GCLOUD_ENABLED === true && settings.gcloudIntegration ) {
186
186
 
187
187
  settings.operationMode = process.env.NODE_ENV || settings.operationMode;
188
188
 
189
- // prevent further modifications to the settings object:
189
+ // Prevent further modifications to the settings object:
190
190
  Object.freeze( settings );
191
191
 
192
192
  /**
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
3
- * Copyright © 2021-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
4
4
  * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5
5
  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6
6
  * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -15,7 +15,7 @@ const tools = require( "#tools" );
15
15
  * @readonly
16
16
  * @enum {number}
17
17
  */
18
- let exceptionCodeEnum = tools.enum( {
18
+ const exceptionCodeEnum = tools.enum( {
19
19
  E_UNKNOWN_ERROR: [ 0, "unknown error", "Unidentified error encountered or unrecognized exception code provided." ],
20
20
  /** General exceptions - codes under 1xxx */
21
21
  E_GEN_JS_INTERNAL_ERROR: [ 1000, "js internal error", "Error thrown by internal JS source." ],
@@ -25,7 +25,7 @@ let exceptionCodeEnum = tools.enum( {
25
25
  E_GEN_SYSTEM_CACHE_UNAVAILABLE: [ 1004, "system cache unavailable", "The system cache required for proper engine operation is unavailable." ],
26
26
  E_GEN_BAD_SERVICE_HANDLER: [ 1005, "bad service handler", "The provided service handler is not a proper function." ],
27
27
  E_GEN_FEATURE_UNSUPPORTED: [ 1006, "feature unsupported", "The requested feature is not supported by current configuration or version." ],
28
- /** Security & Administration related exceptions - codes under 2xxx */
28
+ /** Security & Administration exceptions - codes under 2xxx */
29
29
  E_SEC_INVALID_AUTH_TOKEN: [ 2000, "invalid auth token", "Invalid authorization token provided." ],
30
30
  E_SEC_INVALID_EXPIRED_SESSION: [ 2001, "invalid or expired session", "Invalid or expired session encountered." ],
31
31
  E_SEC_UNAUTHORIZED_ACCESS: [ 2002, "unauthorized access", "Attempt for unauthorized access detected." ],
@@ -38,16 +38,19 @@ let exceptionCodeEnum = tools.enum( {
38
38
  E_COM_SERVICE_NOT_FOUND: [ 3004, "service not found", "The specified service is not found in the service definition interface." ],
39
39
  E_COM_SERVICE_HANDLER_NOT_FOUND: [ 3005, "service handler not found", "No handler found in the interface for the specified service or service version." ],
40
40
  E_COM_MESSAGE_RECEIVER_UNAVAILABLE: [ 3006, "message receiver unavailable", "The message receiver instance is currently unavailable." ],
41
- // E_COM_UNRECOGNIZED_API_URL: [ 301, "unrecognized api url", "Attempt to access unrecognized or invalid API URL." ],
42
- // E_COM_MISSING_REQUIRED_ARGUMENTS: [ 302, "missing required arguments", "Attempt to execute operation without all required arguments." ],
43
- // E_COM_UNRECOGNIZED_RESPONSE_STRUCTURE: [ 303, "unrecognized response structure", "The received response has unrecognized structure and cannot be parsed or examined." ],
44
- // E_COM_RECEIVED_ERROR_RESPONSE: [ 304, "received error response", "The received response indicates error in the external system." ],
45
- // E_COM_JSON_RPC_DATA_INVALID: [ 305, "json rpc data invalid", "The JSON RPC 2.0 data being verified is not valid." ],
46
- // E_COM_NO_OPEN_CONNECTION: [ 306, "no open connection", "Attempting to do communication request while there is no open connection available." ],
47
- // E_COM_REQUEST_ERROR_RESPONSE: [ 307, "received error response", "The received response indicates error in the external system." ],
48
- // E_COM_CONNECTION_TIMEOUT: [ 308, "connection timeout", "Attempting to do communication request but request timeout." ],
49
- // E_COM_INVALID_API_MAPPING: [ 309, "invalid api mapping", "Attempt to access API URL without proper controller mapping." ],
50
- E_COM_RETRY_ATTEMPTS_EXCEEDED: [ 3010, "retry attempts exceeded", "Connection retry attempts exceeded the configured limit." ]
41
+ E_COM_MESSAGE_EXCHANGE_BROKEN: [ 3007, "message exchange broken", "The message exchange is irrevocably broken and cannot be used any longer." ],
42
+ E_COM_RETRY_ATTEMPTS_EXCEEDED: [ 3010, "retry attempts exceeded", "Connection retry attempts exceeded the configured limit." ],
43
+ /** Web server exceptions - codes under 4xxx */
44
+ E_WEB_INVALID_REQUEST_METHOD: [ 4000, "invalid request method", "The request method is not recognized or not supported." ],
45
+ E_WEB_INVALID_REQUEST_URI: [ 4001, "invalid request uri", "The request URI is not recognized or not supported." ],
46
+ E_WEB_INVALID_REQUEST_BODY: [ 4002, "invalid request body", "The request body is not recognized or not supported." ],
47
+ E_WEB_INVALID_REQUEST_QUERY: [ 4003, "invalid request query", "The request query is not recognized or not supported." ],
48
+ E_WEB_INVALID_REQUEST_HEADERS: [ 4004, "invalid request headers", "The request headers are not recognized or not supported." ],
49
+ E_WEB_INVALID_REQUEST_PARAMETERS: [ 4005, "invalid request parameters", "The request parameters are not recognized or not supported." ],
50
+ E_WEB_INVALID_REQUEST_FORMAT: [ 4006, "invalid request format", "The request format is not recognized or not supported." ],
51
+ E_WEB_INVALID_REQUEST_CONTENT_TYPE: [ 4007, "invalid request content type", "The request content type is not recognized or not supported." ],
52
+ E_WEB_INVALID_REQUEST_CONTENT_LENGTH: [ 4008, "invalid request content length", "The request content length is not recognized or not supported." ],
53
+ E_WEB_INVALID_REQUEST_CONTENT_ENCODING: [ 4009, "invalid request content encoding", "The request content encoding is not recognized or not supported." ]
51
54
  } );
52
55
 
53
56
  /**
@@ -55,6 +58,8 @@ let exceptionCodeEnum = tools.enum( {
55
58
  */
56
59
  module.exports.exceptionCode = exceptionCodeEnum;
57
60
 
61
+ const labelPath = "system.exceptions.";
62
+
58
63
  /**
59
64
  * Represents an exception.
60
65
  *
@@ -83,8 +88,8 @@ class Exception {
83
88
  this.#id = id;
84
89
  this.#code = exceptionCode;
85
90
  this.#httpCode = undefined;
86
- this.#label = "labels.general.exceptions." + exceptionCode;
87
- this.#description = exceptionCodeEnum.properties[ exceptionCode ].description;
91
+ this.#label = labelPath + exceptionCode;
92
+ this.#description = description || exceptionCodeEnum.properties[ exceptionCode ].description;
88
93
  this.#data = data || {};
89
94
  }
90
95
 
@@ -93,8 +98,8 @@ class Exception {
93
98
  /**
94
99
  * Unique identifier of the exception instance. Can be used for tracing problems with customer support cases.
95
100
  *
96
- * @method
97
- * @return {string}
101
+ * @property
102
+ * @returns {string}
98
103
  * @public
99
104
  */
100
105
  get id() {
@@ -104,8 +109,8 @@ class Exception {
104
109
  /**
105
110
  * Identifier code of the exception type.
106
111
  *
107
- * @method
108
- * @return {TiExceptionCode}
112
+ * @property
113
+ * @returns {TiExceptionCode}
109
114
  * @public
110
115
  */
111
116
  get code() {
@@ -115,8 +120,8 @@ class Exception {
115
120
  /**
116
121
  * HTTP error code if relevant.
117
122
  *
118
- * @method
119
- * @return {number}
123
+ * @property
124
+ * @returns {number}
120
125
  * @public
121
126
  */
122
127
  get httpCode() {
@@ -126,7 +131,7 @@ class Exception {
126
131
  /**
127
132
  * HTTP error code if relevant.
128
133
  *
129
- * @method
134
+ * @property
130
135
  * @param {number} httpCode
131
136
  * @public
132
137
  */
@@ -137,8 +142,8 @@ class Exception {
137
142
  /**
138
143
  * Localized label identifier.
139
144
  *
140
- * @method
141
- * @return {string}
145
+ * @property
146
+ * @returns {string}
142
147
  * @public
143
148
  */
144
149
  get label() {
@@ -148,8 +153,8 @@ class Exception {
148
153
  /**
149
154
  * Description or additional technical information that is NOT localized.
150
155
  *
151
- * @method
152
- * @return {string}
156
+ * @property
157
+ * @returns {string}
153
158
  * @public
154
159
  */
155
160
  get description() {
@@ -159,8 +164,8 @@ class Exception {
159
164
  /**
160
165
  * JSON containing any additional data that has relevance for the exception. Can be converted JavaScript {@link Error} object as well.
161
166
  *
162
- * @method
163
- * @return {Object}
167
+ * @property
168
+ * @returns {Object}
164
169
  * @public
165
170
  */
166
171
  get data() {
@@ -170,7 +175,7 @@ class Exception {
170
175
  /**
171
176
  * JSON containing any additional data that has relevance for the exception. Can be converted JavaScript {@link Error} object as well.
172
177
  *
173
- * @method
178
+ * @property
174
179
  * @param {Object} data
175
180
  * @public
176
181
  */
@@ -182,17 +187,18 @@ class Exception {
182
187
  * Extracts the essential information about the Exception and returns it as JSON.
183
188
  *
184
189
  * @method
190
+ * @param {boolean} [includeData=true] Whether to include the data property in the output.
185
191
  * @returns {Object}
186
192
  * @public
187
193
  */
188
- asJSON() {
194
+ asJSON( includeData = true ) {
189
195
  return {
190
196
  id: this.id,
191
197
  code: this.code,
192
198
  httpCode: this.httpCode,
193
199
  label: this.label,
194
200
  description: this.description,
195
- data: this.data
201
+ data: ( includeData === true ) ? this.data : undefined
196
202
  };
197
203
  }
198
204
  }
@@ -202,7 +208,7 @@ class Exception {
202
208
  *
203
209
  * @method
204
210
  * @param {Error|TiExceptionCode|Exception} source Could be a standard JS Error, an ExceptionCode, or another Exception (in which case it will be raised further).
205
- * @param {Object} [data] Additional JSON data that can accompany the exception. If more data is added on subsequent Raise calls, it will be merged with the existing one.
211
+ * @param {Object} [data] Additional JSON data that can go with the exception. If more data is added on later Raise calls, it will be merged with the existing one.
206
212
  * @param {string} [exceptionID] Should be used only in cases when we have a recognizable exception ID beforehand. Should not be entered otherwise!
207
213
  * @returns {Exception}
208
214
  * @public
@@ -225,13 +231,13 @@ module.exports.raise = ( source, data, exceptionID ) => {
225
231
  exception = new Exception( exceptionID || tools.getUUID(), ( exceptionCodeEnum.properties[ source ] ) ? source : module.exports.exceptionCode.E_UNKNOWN_ERROR );
226
232
  }
227
233
 
228
- // merge the default exception data with the additional one, if it's provided:
234
+ // Merge the default exception data with the additional one if it's provided:
229
235
  if ( data ) {
230
236
  exception.data = _.mergeWith( ( exception.data || {} ), _.cloneDeep( data ), ( objValue, srcValue ) => {
231
237
  return ( _.isArray( objValue ) ) ? objValue.concat( srcValue ) : undefined;
232
238
  } );
233
239
 
234
- // make sure to eliminate any circular dependencies inside the data object (these should never be needed for an error description):
240
+ // Make sure to eliminate any circular dependencies inside the data object (these should never be needed for an error description):
235
241
  exception.data = tools.decycle( exception.data );
236
242
  }
237
243
 
@@ -242,7 +248,7 @@ module.exports.raise = ( source, data, exceptionID ) => {
242
248
  * Verifies if the passed object is an Exception.
243
249
  *
244
250
  * @method
245
- * @param object
251
+ * @param {*} object
246
252
  * @returns {boolean}
247
253
  * @public
248
254
  */