@ti-engine/core 1.0.2 → 1.0.6
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/README.md +26 -3
- package/{settings.json → bin/settings.json} +0 -0
- package/{start-instance.js → bin/start-instance.js} +8 -7
- package/components/service-consumer.js +1 -1
- package/components/service-executor.js +9 -8
- package/components/service-provider.js +10 -15
- package/package.json +9 -3
- package/utils/exceptions.js +6 -2
package/README.md
CHANGED
|
@@ -2,12 +2,25 @@
|
|
|
2
2
|
Flexible framework for the creation of microservices with [node.js](https://nodejs.org/).
|
|
3
3
|
|
|
4
4
|
## introduction
|
|
5
|
-
The
|
|
5
|
+
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**. The general architectural concept of the framework is based on a standard messenger system that allows certain customization but also provides predictability and traceability of its behavior.
|
|
6
6
|
|
|
7
|
-
Being a messenger system, the
|
|
7
|
+
Being a messenger system, the **ti-engine** relies on a message broker for the actual exchange of messages between microservice instances. The default implementation of the framework uses [Redis](https://redis.io/) cache, however, you could create your own implementation using something like [Rabbit MQ](https://www.rabbitmq.com/). See the [advanced topics](#advanced-topics) section of this documentation for guides on how to do this.
|
|
8
8
|
|
|
9
9
|
Please be aware, that this framework is under active development and will expand in the near future. Also, this documentation is still in the process of being created and refined. Make sure to keep an eye on the changes in case you want to use it in the meantime.
|
|
10
10
|
|
|
11
|
+
## why ti-engine?
|
|
12
|
+
The framework is created based on a decade of professional experience with the utilized technologies and architectural approach. It's primary goal is to provide you with a lightweight and flexible solution that can help you build quickly a microservice ecosystem with any degree of size and complexity.
|
|
13
|
+
|
|
14
|
+
This is what you gain by using **ti-engine** in your project:
|
|
15
|
+
* Simplicity - begin productive work within minutes and get to codding you business logic
|
|
16
|
+
* Flexibility - go as complex as you need to in your implementation
|
|
17
|
+
* Reliability - message exchange between the services is constantly tracked across the entire ecosystem
|
|
18
|
+
* Security - messages are encrypted in transit and cannot be modified by external agents
|
|
19
|
+
* Scalability - serve mullions of requests by multiplying stateless service instances (hardware limitations still apply)
|
|
20
|
+
* Containerization - go with containers from the very start as the framework is designed to work in such an environment
|
|
21
|
+
|
|
22
|
+
These are just some benefits **ti-engine** offers. Get to know it better to find out more ways in which it can help you improve productivity.
|
|
23
|
+
|
|
11
24
|
## prerequisites & installation
|
|
12
25
|
In order to run the basic ti-engine framework you will need a couple of things:
|
|
13
26
|
1. A local [node.js installation](https://nodejs.org/en/download/) with a minimum version of **14.17.0**
|
|
@@ -16,7 +29,17 @@ In order to run the basic ti-engine framework you will need a couple of things:
|
|
|
16
29
|
To get the framework itself, use the command `npm install @ti-engine/core`. And to include it directly in your package.json dependencies execute `npm install @ti-engine/core --save-prod`.
|
|
17
30
|
|
|
18
31
|
## getting started
|
|
19
|
-
To start using the ti-engine
|
|
32
|
+
To start using the **ti-engine**, you will have to make sure that all prerequisites are available and operational. If you are working under Windows OS and you need to install Redis, take a look at this [guide](https://redis.com/blog/redis-on-windows-10/).
|
|
33
|
+
|
|
34
|
+
Once you have all this setup and ready, you should download the **ti-engine** tester module with the command `npm install @ti-engine/tester`. The tester module packages an example microservice that shows the basic approach for using the framework. To make sure everything is working properly, you should try and start the tester service:
|
|
35
|
+
1. Open a command prompt and navigate to the directory of the tester module; it should be something like that `<path to your project>/node_modules/@ti-engine/tester`
|
|
36
|
+
2.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
the framework is properly configured and able to run. More details and instructions will be provided in the next version of this documentation. For now please take a look at the included file `start-instance.js`. It should give you an idea of how the engine operates and what to do in order to create and run your own microservice instance.
|
|
41
|
+
|
|
42
|
+
|
|
20
43
|
|
|
21
44
|
Under development...
|
|
22
45
|
|
|
File without changes
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
-
// load any ENV variables defined in a .env file:
|
|
9
|
-
require( "dotenv" ).config();
|
|
10
|
-
|
|
11
8
|
const _ = require( "lodash" );
|
|
12
|
-
const
|
|
9
|
+
const path = require( "path" );
|
|
13
10
|
const tools = require( "#tools" );
|
|
14
11
|
const logger = require( "#logger" );
|
|
15
12
|
|
|
13
|
+
// load any ENV variables defined in a .env file:
|
|
14
|
+
require( "dotenv" ).config( { path: path.join( process.cwd(), ".env" ) } );
|
|
15
|
+
|
|
16
16
|
// configure the current instance variables before requiring any platform modules and store the necessary ones in memory cache:
|
|
17
17
|
process.env.TI_INSTANCE_ID = "ti-" + tools.getUUID();
|
|
18
18
|
process.env.TI_INSTANCE_CLASS = process.env.TI_INSTANCE_CLASS || "";
|
|
@@ -22,6 +22,7 @@ process.env.TI_INSTANCE_NAME = process.env.TI_INSTANCE_NAME || _.last( _.split(
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Will be used to gracefully shut down the instance.
|
|
25
|
+
* <br/>
|
|
25
26
|
* NOTE: Will be overridden below after the instance creation.
|
|
26
27
|
*
|
|
27
28
|
* @method
|
|
@@ -74,11 +75,11 @@ try {
|
|
|
74
75
|
logger.log( `Starting new instance of type '${ process.env.TI_INSTANCE_NAME }' with instance ID '${ process.env.TI_INSTANCE_ID }'.`, logger.logSeverity.NOTICE );
|
|
75
76
|
|
|
76
77
|
/** @type ServiceInstance */
|
|
77
|
-
const serviceConstructor = require( process.env.TI_INSTANCE_CLASS );
|
|
78
|
+
const serviceConstructor = require( path.join( process.cwd(), process.env.TI_INSTANCE_CLASS ) );
|
|
78
79
|
const serviceConfigPath = process.env.TI_INSTANCE_CONFIG;
|
|
79
80
|
let serviceConfig = {};
|
|
80
|
-
if (
|
|
81
|
-
serviceConfig = require( process.env.TI_INSTANCE_CONFIG );
|
|
81
|
+
if ( serviceConfigPath ) {
|
|
82
|
+
serviceConfig = require( path.join( process.cwd(), process.env.TI_INSTANCE_CONFIG ) );
|
|
82
83
|
}
|
|
83
84
|
const mainInstance = new serviceConstructor( process.env.TI_INSTANCE_NAME, serviceConfig );
|
|
84
85
|
|
|
@@ -20,14 +20,14 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* @callback
|
|
23
|
+
* @callback VerifyAccessMethod
|
|
24
24
|
* @param {string} authToken
|
|
25
25
|
* @param {ServiceAddress} serviceAddress
|
|
26
26
|
* @returns {Promise}
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* @callback
|
|
30
|
+
* @callback ServiceHandlerMethod
|
|
31
31
|
* @param {ServiceDefinition} serviceDefinition The service definition as provided during the service registration.
|
|
32
32
|
* @param {Object} serviceParams Set of named parameters provided to the called service.
|
|
33
33
|
* @param {ServiceExecContext} serviceExecContext The context in which the service call is being executed.
|
|
@@ -44,7 +44,7 @@ const messageDispatcher = require( "#message-dispatcher" );
|
|
|
44
44
|
class ServiceExecutor extends MessageObserver {
|
|
45
45
|
|
|
46
46
|
#serviceInterface = {};
|
|
47
|
-
/** @type
|
|
47
|
+
/** @type VerifyAccessMethod */
|
|
48
48
|
#verifyAccess;
|
|
49
49
|
|
|
50
50
|
/**
|
|
@@ -114,7 +114,7 @@ class ServiceExecutor extends MessageObserver {
|
|
|
114
114
|
* Used to setup the method for service access verification.
|
|
115
115
|
*
|
|
116
116
|
* @method
|
|
117
|
-
* @param {
|
|
117
|
+
* @param {VerifyAccessMethod} verifyAccess
|
|
118
118
|
* @public
|
|
119
119
|
*/
|
|
120
120
|
configureVerifyAccess( verifyAccess ) {
|
|
@@ -131,18 +131,19 @@ class ServiceExecutor extends MessageObserver {
|
|
|
131
131
|
* NOTE: If the same version of the service handler already exists, it will be overridden!
|
|
132
132
|
*
|
|
133
133
|
* @method
|
|
134
|
-
* @param {
|
|
134
|
+
* @param {ServiceHandlerMethod} serviceHandler
|
|
135
135
|
* @param {ServiceDefinition} serviceDefinition
|
|
136
|
+
* @param {ServiceInstance} serviceInstance This will be used as context to bind all business services.
|
|
136
137
|
* @public
|
|
137
138
|
*/
|
|
138
|
-
addServiceHandler( serviceHandler, serviceDefinition ) {
|
|
139
|
+
addServiceHandler( serviceHandler, serviceDefinition, serviceInstance ) {
|
|
139
140
|
if ( !this.#serviceInterface[ serviceDefinition.serviceAlias ] ) {
|
|
140
141
|
this.#serviceInterface[ serviceDefinition.serviceAlias ] = {};
|
|
141
142
|
}
|
|
142
143
|
if ( this.#serviceInterface[ serviceDefinition.serviceAlias ][ serviceDefinition.serviceVersion ] ) {
|
|
143
144
|
logger.log( `Service handler for '${ serviceDefinition.serviceAlias }' version '${ serviceDefinition.serviceVersion }' already existed and will be overridden.`, logger.logSeverity.WARNING );
|
|
144
145
|
}
|
|
145
|
-
this.#serviceInterface[ serviceDefinition.serviceAlias ][ serviceDefinition.serviceVersion ] = serviceHandler.bind(
|
|
146
|
+
this.#serviceInterface[ serviceDefinition.serviceAlias ][ serviceDefinition.serviceVersion ] = serviceHandler.bind( serviceInstance, _.cloneDeep( serviceDefinition ) );
|
|
146
147
|
}
|
|
147
148
|
|
|
148
149
|
/* Private interface */
|
|
@@ -203,7 +204,7 @@ class ServiceExecutor extends MessageObserver {
|
|
|
203
204
|
*
|
|
204
205
|
* @method
|
|
205
206
|
* @param {ServiceAddress} serviceAddress
|
|
206
|
-
* @returns {Promise<
|
|
207
|
+
* @returns {Promise<ServiceHandlerMethod>}
|
|
207
208
|
* @private
|
|
208
209
|
*/
|
|
209
210
|
#identifyService( serviceAddress ) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
const ServiceConsumer = require( "#service-consumer" );
|
|
7
7
|
const _ = require( "lodash" );
|
|
8
|
-
const
|
|
8
|
+
const path = require( "path" );
|
|
9
9
|
const exceptions = require( "#exceptions" );
|
|
10
10
|
const logger = require( "#logger" );
|
|
11
11
|
const messageDispatcher = require( "#message-dispatcher" );
|
|
@@ -128,25 +128,20 @@ class ServiceProvider extends ServiceConsumer {
|
|
|
128
128
|
*
|
|
129
129
|
* @method
|
|
130
130
|
* @param {ServiceDefinition} serviceDefinition Full service definition object.
|
|
131
|
-
* @param {
|
|
131
|
+
* @param {ServiceHandlerMethod} [defaultServiceHandler=undefined] A default service handler in case there is one.
|
|
132
132
|
* @return {Promise}
|
|
133
133
|
* @public
|
|
134
134
|
*/
|
|
135
135
|
registerService( serviceDefinition, defaultServiceHandler = undefined ) {
|
|
136
136
|
return new Promise( ( resolve, reject ) => {
|
|
137
|
-
/** @type {
|
|
137
|
+
/** @type {ServiceHandlerMethod} */
|
|
138
138
|
let serviceHandler = null;
|
|
139
139
|
if ( serviceDefinition.serviceFile ) {
|
|
140
|
-
let
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
} catch ( error ) {
|
|
146
|
-
logger.log( `Specified service handler file '${ serviceDefinition.serviceFile }' could not be loaded!`, logger.logSeverity.ERROR, error );
|
|
147
|
-
}
|
|
148
|
-
} else {
|
|
149
|
-
logger.log( `Specified service handler file '${ serviceDefinition.serviceFile }' was not found in the specified location!`, logger.logSeverity.WARNING );
|
|
140
|
+
let serviceFilePath = path.join( process.cwd(), serviceDefinition.serviceFile );
|
|
141
|
+
try {
|
|
142
|
+
serviceHandler = require( serviceFilePath ).service;
|
|
143
|
+
} catch ( error ) {
|
|
144
|
+
logger.log( `Specified service handler file '${ serviceFilePath }' could not be loaded!`, logger.logSeverity.ERROR, error );
|
|
150
145
|
}
|
|
151
146
|
} else {
|
|
152
147
|
if ( typeof ( defaultServiceHandler ) === "function" ) {
|
|
@@ -160,7 +155,7 @@ class ServiceProvider extends ServiceConsumer {
|
|
|
160
155
|
if ( typeof ( serviceHandler ) === "function" ) {
|
|
161
156
|
// make sure we have a version and parent service provider specified:
|
|
162
157
|
serviceDefinition.serviceVersion = serviceDefinition.serviceVersion || 1;
|
|
163
|
-
this.#serviceExecutor.addServiceHandler( serviceHandler, serviceDefinition );
|
|
158
|
+
this.#serviceExecutor.addServiceHandler( serviceHandler, serviceDefinition, this );
|
|
164
159
|
resolve();
|
|
165
160
|
} else {
|
|
166
161
|
reject( exceptions.raise( exceptions.exceptionCode.E_GEN_BAD_SERVICE_HANDLER ) );
|
|
@@ -173,7 +168,7 @@ class ServiceProvider extends ServiceConsumer {
|
|
|
173
168
|
*
|
|
174
169
|
* @method
|
|
175
170
|
* @param {ServiceDefinition[]} serviceDefinitions
|
|
176
|
-
* @param {
|
|
171
|
+
* @param {ServiceHandlerMethod} [defaultServiceHandler=undefined]
|
|
177
172
|
* @return {Promise}
|
|
178
173
|
* @public
|
|
179
174
|
*/
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ti-engine/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
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": "ISC",
|
|
7
7
|
"exports": {
|
|
8
|
+
"./exceptions": "./utils/exceptions.js",
|
|
9
|
+
"./logger": "./utils/logger.js",
|
|
8
10
|
"./service-consumer": "./components/service-consumer.js",
|
|
9
11
|
"./service-instance": "./components/service-instance.js",
|
|
10
12
|
"./service-provider": "./components/service-provider.js",
|
|
11
|
-
"./exceptions": "./utils/exceptions.js",
|
|
12
13
|
"./tools": "./utils/tools.js"
|
|
13
14
|
},
|
|
14
15
|
"imports": {
|
|
@@ -36,10 +37,11 @@
|
|
|
36
37
|
"#service-executor": "./components/service-executor.js",
|
|
37
38
|
"#service-instance": "./components/service-instance.js",
|
|
38
39
|
"#service-provider": "./components/service-provider.js",
|
|
39
|
-
"#settings": "./settings.json",
|
|
40
|
+
"#settings": "./bin/settings.json",
|
|
40
41
|
"#tools": "./utils/tools.js"
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
44
|
+
"blake2": "^4.0.2",
|
|
43
45
|
"dotenv": "^10.0.0",
|
|
44
46
|
"fs-extra": "^10.0.0",
|
|
45
47
|
"lodash": "^4.17.21",
|
|
@@ -49,6 +51,10 @@
|
|
|
49
51
|
"optionalDependencies": {
|
|
50
52
|
"@google-cloud/error-reporting": "^2.0.4"
|
|
51
53
|
},
|
|
54
|
+
"repository": {
|
|
55
|
+
"type": "git",
|
|
56
|
+
"url": "https://github.com/Belleal/ti-engine.git"
|
|
57
|
+
},
|
|
52
58
|
"engines": {
|
|
53
59
|
"node": ">=14.17.0"
|
|
54
60
|
}
|
package/utils/exceptions.js
CHANGED
|
@@ -69,8 +69,9 @@ class Exception {
|
|
|
69
69
|
* @param {string} id The unique ID to be assigned to this exception.
|
|
70
70
|
* @param {TiExceptionCode} exceptionCode An unique exception identifier. If this is not recognized, the default error code will be used instead.
|
|
71
71
|
* @param {Object} [data] Any additional data to insert into the exception.
|
|
72
|
+
* @param {string} [description] Description of the exception.
|
|
72
73
|
*/
|
|
73
|
-
constructor( id, exceptionCode, data ) {
|
|
74
|
+
constructor( id, exceptionCode, data, description ) {
|
|
74
75
|
exceptionCode = ( exceptionCodeEnum.properties[ exceptionCode ] ) ? exceptionCode : module.exports.exceptionCode.E_UNKNOWN_ERROR;
|
|
75
76
|
|
|
76
77
|
this.#id = id;
|
|
@@ -184,7 +185,8 @@ class Exception {
|
|
|
184
185
|
code: this.code,
|
|
185
186
|
httpCode: this.httpCode,
|
|
186
187
|
label: this.label,
|
|
187
|
-
description: this.description
|
|
188
|
+
description: this.description,
|
|
189
|
+
data: this.data
|
|
188
190
|
};
|
|
189
191
|
}
|
|
190
192
|
}
|
|
@@ -211,6 +213,8 @@ module.exports.raise = ( source, data, exceptionID ) => {
|
|
|
211
213
|
exception = new Exception( exceptionID || tools.getUUID(), module.exports.exceptionCode.E_GEN_JS_INTERNAL_ERROR, {
|
|
212
214
|
message: source
|
|
213
215
|
} );
|
|
216
|
+
} else if ( _.isObjectLike( source ) ) {
|
|
217
|
+
exception = new Exception( exceptionID || ( source.id || tools.getUUID() ), source.code || module.exports.exceptionCode.E_GEN_JS_INTERNAL_ERROR, source.data, source.description );
|
|
214
218
|
} else {
|
|
215
219
|
exception = new Exception( exceptionID || tools.getUUID(), ( exceptionCodeEnum.properties[ source ] ) ? source : module.exports.exceptionCode.E_UNKNOWN_ERROR );
|
|
216
220
|
}
|