@ti-engine/core 1.3.10 → 1.3.12

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.
package/CHANGELOG.md CHANGED
@@ -2,26 +2,46 @@
2
2
 
3
3
  This document will contain the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
4
4
 
5
+ ## Version 1.3.12
6
+
7
+ * feat(exceptions): add new exception code `E_COM_SERVICE_EXEC_FAILED`
8
+ * feat(localization): add new public method `getAllLabels`
9
+ * feat(localization): use new `deepFreeze` method to freeze all label trees
10
+ * feat(tools): add new public method `deepFreeze` for freezing an object and all its sub-objects recursively
11
+ * feat(config): use new `deepFreeze` method to freeze all configuration settings
12
+ * docs: add more information about the `localization` module in `README.md` file
13
+ * build(npm): update npm dependencies to their latest versions
14
+
15
+ ## Version 1.3.11
16
+
17
+ * feat(tools): add `arrayUniques` method to the tools module that helps extract only the unique array values
18
+ * fix(start instance): fix `undefined` instance ID in the log message about starting the instance
19
+
5
20
  ## Version 1.3.10
21
+
6
22
  * chore: fix various minor issues reported by the linter
7
23
  * build(npm): add more information and options to the `package.json` file
8
24
  * docs: add some JSDoc descriptions to classes and methods
9
25
 
10
26
  ## Version 1.3.9
27
+
11
28
  * feat(exceptions): add new exception code `E_GEN_NOT_INITIALIZED`
12
29
  * docs: fix image links in the `README.md` file
13
30
 
14
31
  ## Version 1.3.8
32
+
15
33
  * fix(tester): fix the relative paths of various files in the tester service. The configuration now assumes that the working directory is the root of the tester package instead of the repository itself
16
34
  * build(npm): update npm dependencies to their latest versions
17
35
  * docs: fix some outdated information in the `README.md` file and add some more details
18
36
 
19
37
  ## Version 1.3.7
38
+
20
39
  * feat(exceptions): add new exception code `E_SEC_UNRECOGNIZED_AUTH_METHOD`
21
40
  * fix(config): fix potential issue with `TI_GCLOUD_ENABLED` parsing as boolean
22
41
  * docs: adjust some of the JSDoc descriptions and definitions
23
42
 
24
43
  ## Version 1.3.6
44
+
25
45
  * feat(exceptions): add new exception code `E_GEN_INVALID_ARGUMENT_TYPE`
26
46
  * feat(exceptions): add public enum with all HTTP codes exported as `httpCode` and typedef `TiHttpCode`. Use it as the type for the `httpCode` exception property
27
47
  * feat(tools): add `description` property to enum objects
@@ -37,19 +57,23 @@ This document will contain the list of changes made to the framework. The format
37
57
  * build(npm)!: bump the minimum supported Node.js version to 18.0.0
38
58
 
39
59
  ## Version 1.3.5
60
+
40
61
  * fix(redis integration): fix a duplicated log entry on connection ready event if multiple observers are registered
41
62
  * feat(service caller)!: change log level of error result in `process` method from `ERROR` to `DEBUG`. Implementers are expected to handle this and decide if the error should be propagated further or not
42
63
  * docs: update and fix various issues with the `README.md` file
43
64
 
44
65
  ## Version 1.3.4
66
+
45
67
  * fix(service instance): fix the way `ServiceConfiguration` is propagated via child classes and remove unnecessary defaults. Also update the relevant JSDoc
46
68
 
47
69
  ## Version 1.3.3
70
+
48
71
  * feat(exceptions): add new parameter `includeData` to `Exception.asJSON` method which allows the exclusion of the data parameter from the returned JSON
49
72
  * feat(exceptions): remove several excessive exception codes that were unlikely to be used
50
73
  * fix(localization): add several missing exception labels
51
74
 
52
75
  ## Version 1.3.2
76
+
53
77
  * feat(exceptions): add a set of new exception codes for the needs of any wrapping `web-server` standard communication
54
78
  * feat(exception)!: change the default prefix path for exception labels to `system.exceptions.`
55
79
  * feat(localization): add an Enum list of all language codes based on ISO 639-1 standard
@@ -63,6 +87,7 @@ This document will contain the list of changes made to the framework. The format
63
87
  * fix(auditing): fix a potential problem with setting the log entry reporter from an ENV variable; instead, the system will now use the `ServiceInstance.instanceID` property
64
88
 
65
89
  ## Version 1.3.1
90
+
66
91
  * feat(service caller): refactor the entire service call execution flow for clarity and better performance
67
92
  * feat(service caller): create a new private class `ServiceCallProcessor` to handle individual service calls in a contained scope
68
93
  * feat(message observer): add new property `priority` to the message observer class. It is used to determine the order in which the observers are notified about the messages
@@ -73,6 +98,7 @@ This document will contain the list of changes made to the framework. The format
73
98
  * fix(redis integration): fix an issue which was setting the client status to `DISCONNECTED` during a normal shut down procedure
74
99
 
75
100
  ## Version 1.3.0
101
+
76
102
  * feat(redis integration)!: change `reconnectOnError` behavior to also resubmit the failed command in case the error was of type `READONLY`
77
103
  * feat(redis integration): improve the reliability and usage of the event notification mechanism for connection observers
78
104
  * feat(redis integration): implement listener to the `end` event on Redis connection to capture when connection can no longer be recovered
@@ -86,25 +112,31 @@ This document will contain the list of changes made to the framework. The format
86
112
  * fix(redis integration): fix broken event propagation on `disrupted` events to some connection observers
87
113
 
88
114
  ## Version 1.2.5
115
+
89
116
  * feat(cache): extend method `expireValue` to work with has set fields as well
90
117
 
91
118
  ## Version 1.2.4
119
+
92
120
  * feat(cache): expose the cache module as export in `package.json`
93
121
  * feat(cache): add method `hashDeleteField` to remove a hash-set field. This implements the `hdel` Redis command
94
122
  * fix(tools): optimize method `stringifyJSON` not to call unnecessary decycling of the value if it's not an object
95
123
  * fix(cache): replace `hmset` with `hset` Redis command in both methods that set hash-set values. Also remove unnecessary `_.isObjectLike` call in `hashSetFields` method
96
124
 
97
125
  ## Version 1.2.3
126
+
98
127
  * feat(start instance): add support for providing a custom path to the `.env` file to be used at service startup as process argument. Accepted arguments are `--env`, `--env-file`, `--dotenv`, `--dotenv-path`, and `-e`. The path itself should be relative to the working directory and should include the file name
99
128
 
100
129
  ## Version 1.2.2
130
+
101
131
  * feat(start instance): change package `dotenv` to `@dotenvx/dotenvx` for loading of ENV variables. The new package supports encrypting the ENV variables. For more information see https://dotenvx.com/docs/
102
132
 
103
133
  ## Version 1.2.1
134
+
104
135
  * feat(localization): add support for adding custom labels to the localization system. These have to follow the same format as the system labels
105
136
  * docs: add section about localization to the `README.md` file
106
137
 
107
138
  ## Version 1.2.0
139
+
108
140
  * feat(config)!: change the setting `localization.labelsPath` to be an array of strings. It can now be used to supply any additional custom labels in one or more files to the framework
109
141
  * feat(message memory cache): implement graceful exception handling during shut down procedure in `receiveMessage` and `sendMessage` methods
110
142
  * fix(default message sender): add missing initialization of the memory cache on enable
@@ -117,21 +149,25 @@ This document will contain the list of changes made to the framework. The format
117
149
  * fix(service executor)!: change the individual service registration process to ensure service registration does actually happen before the service provider finishes its initialization
118
150
 
119
151
  ## Version 1.1.10
152
+
120
153
  * fix(cache): fix redis client creation sequence. It is now created inside the constructor as intended. It still needs to be initialized explicitly using the `initialize` method.
121
154
 
122
155
  ## Version 1.1.9
156
+
123
157
  * feat(redis integration)!: change the way the `redis` client is initialized. Instead of happening automatically on class instantiation, it is now initialized on demand using the `initialize` method.
124
158
  * feat(cache): change the way the main cache instance is initialized in compliance with the new redis integration
125
159
  * feat(message memory cache)!: change the way the message memory cache is initialized in compliance with the new redis integration. The `initialize` method needs to be called explicitly to initialize the cache instance before it can be used.
126
160
  * fix(message dispatcher): fix the way the `messageExchange` is initialized in the `MessageDispatcher` class (was not returning a promise)
127
161
 
128
162
  ## Version 1.1.8
163
+
129
164
  * feat(auditing): change export of the singleton class in an `instance` variable for consistency and clarity
130
165
  * feat(message dispatcher): change export of the singleton class in an `instance` variable for consistency and clarity
131
166
  * feat(cache): change export of the singleton class in an `instance` variable for consistency and clarity
132
167
  * feat(service executor): implement service registration retry policy
133
168
 
134
169
  ## Version 1.1.7
170
+
135
171
  * feat(start instance): add support for the detection of `SIGBREAK` events and graceful shutdown on Windows
136
172
  * feat(service instance)!: prevent the initialization of multiple `ServiceInstance` within the same process
137
173
  * feat(message tracer)!: covert to singleton instance and add initialization method
@@ -141,6 +177,7 @@ This document will contain the list of changes made to the framework. The format
141
177
  * fix(redis integration): update links to official commands documentation
142
178
 
143
179
  ## Version 1.1.6
180
+
144
181
  * feat(start instance): add fail-fast mode as default behavior on promise unhandled rejections
145
182
  * feat(start instance): implement functionality to derive safe default service domain name when none is provided in the configuration
146
183
  * feat(config): add support for new ENV variable `TI_FAIL_FAST_ON_UNHANDLED_OFF` that controls the fail-fast mode
package/README.md CHANGED
@@ -544,4 +544,6 @@ The following is an example of a custom localization file. The names in brackets
544
544
  }
545
545
  ```
546
546
 
547
- The individual languages are specified with a two-letter code according to ISO 639-1. The default language is `en` (English). If you want to use a different language, you can set the `LOCALIZATION_LANGUAGE` setting to the desired language code.
547
+ The individual languages are specified with a two-letter code according to ISO 639-1. The default language is `en` (English). If you want to use a different language, you can set the `LOCALIZATION_LANGUAGE` setting to the desired language code.
548
+
549
+ If you want to localize the system labels of the framework, you can do so by providing a custom localization file with the same structure as the default one but containing only the languages you want to add. For example, the default English label for `E_UNKNOWN_ERROR` is found in JSON path `system.exceptions.0.en`. To add an entry for German, your file needs to contain a `system.exceptions.0.de` property. The localization module will handle the rest. You can use the same approach to modify the existing English labels as well. The full list of system labels is found in the `bin/localization/labels.json` file. Avoid modifying the default file since it might get overwritten by future updates.
@@ -70,6 +70,9 @@
70
70
  "3007": {
71
71
  "en": "The message exchange is irrevocably broken and cannot be used any longer."
72
72
  },
73
+ "3008": {
74
+ "en": "The execution of a service reported a failure. This might be a business logic error or a system error."
75
+ },
73
76
  "3010": {
74
77
  "en": "Connection retry attempts exceeded the configured limit."
75
78
  },
@@ -121,8 +121,6 @@ process.on( "uncaughtException", ( error ) => {
121
121
 
122
122
  // Start the instance:
123
123
  try {
124
- logger.log( `Starting new instance of type '${ process.env.TI_INSTANCE_NAME }' with instance ID '${ process.env.TI_INSTANCE_ID }'.`, logger.logSeverity.NOTICE );
125
-
126
124
  const serviceConstructor = require( path.join( process.cwd(), process.env.TI_INSTANCE_CLASS ) );
127
125
  const serviceConfigPath = process.env.TI_INSTANCE_CONFIG;
128
126
  /** @type ServiceConfiguration */
@@ -144,6 +142,7 @@ try {
144
142
  };
145
143
 
146
144
  if ( mainInstance.isServiceInstance ) {
145
+ logger.log( `Starting new instance of type '${ process.env.TI_INSTANCE_NAME }' with instance ID '${ process.env.TI_INSTANCE_ID }'.`, logger.logSeverity.NOTICE );
147
146
  mainInstance.start().catch( ( error ) => {
148
147
  logger.log( `Error detected during instance '${ process.env.TI_INSTANCE_ID }' startup!`, logger.logSeverity.ALERT, error );
149
148
  setImmediate( () => process.exit( 1 ) );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ti-engine/core",
3
- "version": "1.3.10",
3
+ "version": "1.3.12",
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
  "keywords": [
6
6
  "microservices",
@@ -59,7 +59,7 @@
59
59
  "dependencies": {
60
60
  "@dotenvx/dotenvx": "^1.51.0",
61
61
  "blake2": "^5.0.0",
62
- "ioredis": "^5.8.0",
62
+ "ioredis": "^5.8.2",
63
63
  "lodash": "^4.17.21",
64
64
  "node-schedule": "^2.1.1"
65
65
  },
package/utils/config.js CHANGED
@@ -185,7 +185,7 @@ if ( tools.toBool( process.env.TI_GCLOUD_ENABLED ) === true && settings.gcloudIn
185
185
  settings.operationMode = process.env.NODE_ENV || settings.operationMode;
186
186
 
187
187
  // Prevent further modifications to the settings object:
188
- Object.freeze( settings );
188
+ tools.deepFreeze( settings );
189
189
 
190
190
  /**
191
191
  * A standard getter method for fetching a setting.
@@ -43,6 +43,7 @@ const exceptionCodeEnum = tools.enum( {
43
43
  E_COM_SERVICE_HANDLER_NOT_FOUND: [ 3005, "service handler not found", "No handler found in the interface for the specified service or service version." ],
44
44
  E_COM_MESSAGE_RECEIVER_UNAVAILABLE: [ 3006, "message receiver unavailable", "The message receiver instance is currently unavailable." ],
45
45
  E_COM_MESSAGE_EXCHANGE_BROKEN: [ 3007, "message exchange broken", "The message exchange is irrevocably broken and cannot be used any longer." ],
46
+ E_COM_SERVICE_EXEC_FAILED: [ 3008, "service exec failed", "The execution of a service reported a failure. This might be a business logic error or a system error." ],
46
47
  E_COM_RETRY_ATTEMPTS_EXCEEDED: [ 3010, "retry attempts exceeded", "Connection retry attempts exceeded the configured limit." ],
47
48
  /** Web server exceptions - codes under 4xxx */
48
49
  E_WEB_INVALID_REQUEST_METHOD: [ 4000, "invalid request method", "The request method is not recognized or not supported." ],
@@ -245,12 +245,7 @@ module.exports.localizationLanguage = localizationLanguageEnum;
245
245
  * @typedef {Object<string, TiLocalizedLabel | TiLabelsTree>} TiLabelsTree
246
246
  */
247
247
 
248
- /**
249
- * @typedef {Object} TiLabels
250
- * @property {TiLabelsTree} labels
251
- */
252
-
253
- /** @type {TiLabels} */
248
+ /** @type {TiLabelsTree} */
254
249
  const labels = require( "#labels" );
255
250
 
256
251
  // Load any custom labels defined in the configuration:
@@ -264,7 +259,7 @@ if ( labelsPaths && _.isArray( labelsPaths ) && labelsPaths.length > 0 ) {
264
259
  }
265
260
 
266
261
  // Prevent further modifications to the TiLabels object:
267
- Object.freeze( labels );
262
+ tools.deepFreeze( labels );
268
263
 
269
264
  /**
270
265
  * Used to return the textual value for a label based on the current system language by default or the specified language code if provided.
@@ -277,4 +272,43 @@ Object.freeze( labels );
277
272
  */
278
273
  module.exports.getLabel = ( label, language ) => {
279
274
  return _.get( labels, label + "." + ( ( language ) ? language : config.getSetting( config.setting.LOCALIZATION_LANGUAGE ) ), defaultEmptyLabel );
275
+ };
276
+
277
+ const labelsCacheByLanguage = new Map();
278
+
279
+ /**
280
+ * Used to return the entire labels tree.
281
+ * <br/>
282
+ * NOTE: The result will be a modified copy of the label tree that has no "language" end-nodes and keeps only the appropriate labels for the requested language.
283
+ * For example, if the original JSON path is "path.to.label.language", the returned path will be just "path.to.label" corresponding to the text label.
284
+ *
285
+ * @method
286
+ * @param {TiLocalizationLanguage} [language] The language code to use for the lookup. If not provided, the current system language will be used.
287
+ * @returns {TiLabelsTree}
288
+ * @public
289
+ */
290
+ module.exports.getAllLabels = ( language ) => {
291
+ const usedLanguage = language || config.getSetting( config.setting.LOCALIZATION_LANGUAGE );
292
+
293
+ if ( labelsCacheByLanguage.has( usedLanguage ) ) {
294
+ return labelsCacheByLanguage.get( usedLanguage );
295
+ } else {
296
+ const labelsByLanguage = _.cloneDeepWith( labels, ( value ) => {
297
+ if ( _.isPlainObject( value ) ) {
298
+ const values = Object.values( value );
299
+ const isLeaf = values.length > 0 && values.every( v => _.isString( v ) || _.isNil( v ) );
300
+ if ( isLeaf ) {
301
+ // Return only the desired language or default placeholder if missing:
302
+ return _.get( value, usedLanguage, defaultEmptyLabel );
303
+ }
304
+ } else {
305
+ // Return undefined to let lodash handle default deep cloning for non-leaves:
306
+ return undefined;
307
+ }
308
+ } );
309
+
310
+ tools.deepFreeze( labelsByLanguage );
311
+ labelsCacheByLanguage.set( usedLanguage, labelsByLanguage );
312
+ return labelsByLanguage;
313
+ }
280
314
  };
package/utils/tools.js CHANGED
@@ -35,6 +35,27 @@ module.exports.getUUID = () => {
35
35
  return crypto.randomUUID( { disableEntropyCache: true } );
36
36
  };
37
37
 
38
+ /**
39
+ * Used to deep-freeze an object.
40
+ *
41
+ * @method
42
+ * @param {Object} object
43
+ * @param {WeakSet} [seen]
44
+ * @return {Object}
45
+ * @public
46
+ */
47
+ module.exports.deepFreeze = ( object, seen = new WeakSet() ) => {
48
+ if ( object === null || typeof object !== "object" || seen.has( object ) ) {
49
+ return object;
50
+ } else {
51
+ seen.add( object );
52
+ _.forOwn( object, ( value ) => {
53
+ module.exports.deepFreeze( value, seen );
54
+ } );
55
+ return Object.freeze( object );
56
+ }
57
+ };
58
+
38
59
  /**
39
60
  * Used to create a custom Enum list.
40
61
  *
@@ -199,6 +220,24 @@ module.exports.toBool = ( value ) => {
199
220
  return result;
200
221
  };
201
222
 
223
+ /**
224
+ * Used to fetch only the unique values from the provided array.
225
+ * <br/>
226
+ * Note: For objects and arrays, uniqueness is determined by reference equality. Primitives are compared by their value.
227
+ *
228
+ * @method
229
+ * @param {Array} array
230
+ * @returns {Array}
231
+ * @throws {TypeError} If the provided parameter is not an array
232
+ * @public
233
+ */
234
+ module.exports.arrayUniques = ( array ) => {
235
+ if ( !Array.isArray( array ) ) {
236
+ throw new TypeError( "Expected an array" );
237
+ }
238
+ return [ ...new Set( array ) ];
239
+ };
240
+
202
241
  /**
203
242
  * Will return a UTC date string in format YYYY-MM-DD from the provided date.
204
243
  *