@hiliosai/sdk 0.1.0 → 0.1.1
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/INTEGRATION_SERVICE.md +394 -0
- package/package.json +5 -3
- package/src/configs/constants.ts +122 -0
- package/src/configs/index.ts +1 -0
- package/src/configs/moleculer/bulkhead.ts +8 -0
- package/src/configs/moleculer/channels.ts +102 -0
- package/src/configs/moleculer/circuit-breaker.ts +17 -0
- package/src/configs/moleculer/index.ts +85 -0
- package/src/configs/moleculer/logger.ts +17 -0
- package/src/configs/moleculer/metrics.ts +20 -0
- package/src/configs/moleculer/registry.ts +7 -0
- package/src/configs/moleculer/retry-policy.ts +17 -0
- package/src/configs/moleculer/tracing.ts +6 -0
- package/src/configs/moleculer/tracking.ts +6 -0
- package/src/datasources/base.datasource.ts +79 -0
- package/src/datasources/index.ts +1 -0
- package/src/middlewares/context-helpers.middleware.ts +4 -1
- package/src/middlewares/datasource.middleware.ts +3 -1
- package/src/middlewares/index.ts +0 -1
- package/src/mixins/datasource.mixin.ts +118 -0
- package/src/mixins/index.ts +1 -0
- package/src/service/define-integration.ts +177 -61
- package/src/service/define-service.ts +10 -30
- package/src/service/example-user/user.service.ts +2 -10
- package/src/types/context.ts +7 -4
- package/src/types/message.ts +1 -0
- package/src/types/service.ts +3 -4
- package/src/examples/cache-usage.example.ts +0 -293
- package/src/middlewares/cache.middleware.ts +0 -278
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import env from '@ltv/env';
|
|
2
|
+
import type {BrokerOptions} from 'moleculer';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
import {bulkheadConfig} from './bulkhead';
|
|
6
|
+
import {ChannelsMiddleware} from './channels';
|
|
7
|
+
import {circuitBreakerConfig} from './circuit-breaker';
|
|
8
|
+
import logger from './logger';
|
|
9
|
+
import {metricsConfig} from './metrics';
|
|
10
|
+
import {registryConfig} from './registry';
|
|
11
|
+
import {retryPolicyConfig} from './retry-policy';
|
|
12
|
+
import {tracingConfig} from './tracing';
|
|
13
|
+
import {trackingConfig} from './tracking';
|
|
14
|
+
|
|
15
|
+
const pkgNm = env.string('NAMESPACE', 'hios');
|
|
16
|
+
|
|
17
|
+
const nodeID =
|
|
18
|
+
env.string('NODE_ID') ?? `${pkgNm}-${os.hostname()}-${process.pid}`;
|
|
19
|
+
|
|
20
|
+
// Export default config for backward compatibility
|
|
21
|
+
const configs: BrokerOptions = {
|
|
22
|
+
namespace: pkgNm,
|
|
23
|
+
nodeID,
|
|
24
|
+
metadata: {},
|
|
25
|
+
logger,
|
|
26
|
+
// Default log level for built-in console logger. It can be overwritten in logger options above.
|
|
27
|
+
// Available values: trace, debug, info, warn, error, fatal
|
|
28
|
+
logLevel: 'info',
|
|
29
|
+
|
|
30
|
+
cacher: env.string('REDIS_URL', 'Memory'),
|
|
31
|
+
|
|
32
|
+
// Define a serializer.
|
|
33
|
+
// Available values: "JSON", "Avro", "ProtoBuf", "MsgPack", "Notepack", "Thrift".
|
|
34
|
+
// More info: https://moleculer.services/docs/0.14/networking.html#Serialization
|
|
35
|
+
serializer: 'JSON',
|
|
36
|
+
|
|
37
|
+
// Number of milliseconds to wait before reject a request with a RequestTimeout error. Disabled: 0
|
|
38
|
+
requestTimeout: 10 * 1000,
|
|
39
|
+
|
|
40
|
+
// Retry policy settings. More info: https://moleculer.services/docs/0.14/fault-tolerance.html#Retry
|
|
41
|
+
retryPolicy: retryPolicyConfig,
|
|
42
|
+
|
|
43
|
+
// Limit of calling level. If it reaches the limit, broker will throw an MaxCallLevelError error. (Infinite loop protection)
|
|
44
|
+
maxCallLevel: 100,
|
|
45
|
+
|
|
46
|
+
// Number of seconds to send heartbeat packet to other nodes.
|
|
47
|
+
heartbeatInterval: 10,
|
|
48
|
+
// Number of seconds to wait before setting node to unavailable status.
|
|
49
|
+
heartbeatTimeout: 30,
|
|
50
|
+
|
|
51
|
+
// Cloning the params of context if enabled. High performance impact, use it with caution!
|
|
52
|
+
contextParamsCloning: false,
|
|
53
|
+
|
|
54
|
+
// Tracking requests and waiting for running requests before shuting down. More info: https://moleculer.services/docs/0.14/context.html#Context-tracking
|
|
55
|
+
tracking: trackingConfig,
|
|
56
|
+
|
|
57
|
+
// Disable built-in request & emit balancer. (Transporter must support it, as well.). More info: https://moleculer.services/docs/0.14/networking.html#Disabled-balancer
|
|
58
|
+
disableBalancer: false,
|
|
59
|
+
|
|
60
|
+
// Settings of Service Registry. More info: https://moleculer.services/docs/0.14/registry.html
|
|
61
|
+
registry: registryConfig,
|
|
62
|
+
|
|
63
|
+
// Settings of Circuit Breaker. More info: https://moleculer.services/docs/0.14/fault-tolerance.html#Circuit-Breaker
|
|
64
|
+
circuitBreaker: circuitBreakerConfig,
|
|
65
|
+
|
|
66
|
+
// Settings of bulkhead feature. More info: https://moleculer.services/docs/0.14/fault-tolerance.html#Bulkhead
|
|
67
|
+
bulkhead: bulkheadConfig,
|
|
68
|
+
|
|
69
|
+
// Enable action & event parameter validation. More info: https://moleculer.services/docs/0.14/validating.html
|
|
70
|
+
validator: 'Fastest',
|
|
71
|
+
|
|
72
|
+
// errorHandler: null,
|
|
73
|
+
|
|
74
|
+
transporter: env.string('TRANSPORTER_URL'),
|
|
75
|
+
|
|
76
|
+
// Enable/disable built-in metrics function. More info: https://moleculer.services/docs/0.14/metrics.html
|
|
77
|
+
metrics: metricsConfig,
|
|
78
|
+
|
|
79
|
+
// Enable built-in tracing function. More info: https://moleculer.services/docs/0.14/tracing.html
|
|
80
|
+
tracing: tracingConfig,
|
|
81
|
+
|
|
82
|
+
middlewares: [ChannelsMiddleware],
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default configs;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const loggerConfig = {
|
|
2
|
+
type: 'Console',
|
|
3
|
+
options: {
|
|
4
|
+
// Using colors on the output
|
|
5
|
+
colors: true,
|
|
6
|
+
// Print module names with different colors (like docker-compose for containers)
|
|
7
|
+
moduleColors: false,
|
|
8
|
+
// Line formatter. It can be "json", "short", "simple", "full", a `Function` or a template string like "{timestamp} {level} {nodeID}/{mod}: {msg}"
|
|
9
|
+
formatter: 'full',
|
|
10
|
+
// Custom object printer. If not defined, it uses the `util.inspect` method.
|
|
11
|
+
objectPrinter: null,
|
|
12
|
+
// Auto-padding the module name in order to messages begin at the same column.
|
|
13
|
+
autoPadding: false,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default loggerConfig;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type {MetricRegistry} from 'moleculer';
|
|
2
|
+
|
|
3
|
+
export const metricsConfig = {
|
|
4
|
+
enabled: false,
|
|
5
|
+
// Available built-in reporters: "Console", "CSV", "Event", "Prometheus", "Datadog", "StatsD"
|
|
6
|
+
reporter: {
|
|
7
|
+
type: 'Prometheus',
|
|
8
|
+
options: {
|
|
9
|
+
// HTTP port
|
|
10
|
+
port: 3030,
|
|
11
|
+
// HTTP URL path
|
|
12
|
+
path: '/metrics',
|
|
13
|
+
// Default labels which are appended to all metrics labels
|
|
14
|
+
defaultLabels: (registry: MetricRegistry) => ({
|
|
15
|
+
namespace: registry.broker.namespace,
|
|
16
|
+
nodeID: registry.broker.nodeID,
|
|
17
|
+
}),
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const registryConfig = {
|
|
2
|
+
// Define balancing strategy. More info: https://moleculer.services/docs/0.14/balancing.html
|
|
3
|
+
// Available values: "RoundRobin", "Random", "CpuUsage", "Latency", "Shard"
|
|
4
|
+
strategy: 'RoundRobin',
|
|
5
|
+
// Enable local action call preferring. Always call the local action instance if available.
|
|
6
|
+
preferLocal: true,
|
|
7
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type {Errors} from 'moleculer';
|
|
2
|
+
|
|
3
|
+
export const retryPolicyConfig = {
|
|
4
|
+
// Enable feature
|
|
5
|
+
enabled: false,
|
|
6
|
+
// Count of retries
|
|
7
|
+
retries: 5,
|
|
8
|
+
// First delay in milliseconds.
|
|
9
|
+
delay: 100,
|
|
10
|
+
// Maximum delay in milliseconds.
|
|
11
|
+
maxDelay: 1000,
|
|
12
|
+
// Backoff factor for delay. 2 means exponential backoff.
|
|
13
|
+
factor: 2,
|
|
14
|
+
// A function to check failed requests.
|
|
15
|
+
check: (err: Errors.MoleculerError | Error) =>
|
|
16
|
+
!!(err as Errors.MoleculerError).retryable,
|
|
17
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base datasource interface that all datasources should implement
|
|
3
|
+
* Provides common structure and optional lifecycle methods
|
|
4
|
+
*/
|
|
5
|
+
export interface BaseDatasource {
|
|
6
|
+
/**
|
|
7
|
+
* Datasource name for identification and logging
|
|
8
|
+
*/
|
|
9
|
+
readonly name: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Optional initialization method
|
|
13
|
+
* Called when datasource is instantiated
|
|
14
|
+
*/
|
|
15
|
+
init?(): Promise<void> | void;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Optional connection method
|
|
19
|
+
* Called when service starts if implemented
|
|
20
|
+
*/
|
|
21
|
+
connect?(): Promise<void> | void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Optional disconnection method
|
|
25
|
+
* Called when service stops if implemented
|
|
26
|
+
*/
|
|
27
|
+
disconnect?(): Promise<void> | void;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Optional health check method
|
|
31
|
+
* Can be used to verify datasource is working correctly
|
|
32
|
+
*/
|
|
33
|
+
healthCheck?(): Promise<boolean> | boolean;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Optional method to clear/reset the datasource
|
|
37
|
+
*/
|
|
38
|
+
clear?(): Promise<void> | void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Abstract base class for datasources that provides default implementations
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* export class UserDatasource extends AbstractDatasource {
|
|
46
|
+
* readonly name = 'users';
|
|
47
|
+
*
|
|
48
|
+
* private users: User[] = [];
|
|
49
|
+
*
|
|
50
|
+
* async findById(id: string): Promise<User | undefined> {
|
|
51
|
+
* return this.users.find(u => u.id === id);
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* async create(user: User): Promise<User> {
|
|
55
|
+
* this.users.push(user);
|
|
56
|
+
* return user;
|
|
57
|
+
* }
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export abstract class AbstractDatasource implements BaseDatasource {
|
|
62
|
+
abstract readonly name: string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Default health check - always returns true
|
|
66
|
+
* Override for custom health check logic
|
|
67
|
+
*/
|
|
68
|
+
async healthCheck(): Promise<boolean> {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Default clear method - does nothing
|
|
74
|
+
* Override to implement clearing logic
|
|
75
|
+
*/
|
|
76
|
+
async clear(): Promise<void> {
|
|
77
|
+
// Default implementation - does nothing
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './base.datasource';
|
|
@@ -110,7 +110,10 @@ export const ContextHelpersMiddleware = {
|
|
|
110
110
|
ctx.broker.logger.info('Audit log', {
|
|
111
111
|
action,
|
|
112
112
|
resource: resource
|
|
113
|
-
? {
|
|
113
|
+
? {
|
|
114
|
+
type: typeof resource,
|
|
115
|
+
id: (resource as Record<string, unknown>).id,
|
|
116
|
+
}
|
|
114
117
|
: undefined,
|
|
115
118
|
userId: ctx.meta.user?.id,
|
|
116
119
|
tenantId: ctx.meta.tenantId,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type {Context} from 'moleculer';
|
|
2
|
+
import type {BaseDatasource} from '../datasources';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Datasource constructor registry type
|
|
6
|
+
* All datasources should implement BaseDatasource interface
|
|
5
7
|
*/
|
|
6
8
|
export interface DatasourceConstructorRegistry {
|
|
7
|
-
[key: string]: new () => object;
|
|
9
|
+
[key: string]: new () => BaseDatasource | object;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
/**
|
package/src/middlewares/index.ts
CHANGED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import type {ServiceSchema} from 'moleculer';
|
|
3
|
+
import type {BaseDatasource} from '../datasources';
|
|
4
|
+
import type {AppContext} from '../types/context';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Datasource constructor registry type
|
|
8
|
+
* All datasources should implement BaseDatasource interface
|
|
9
|
+
*/
|
|
10
|
+
export interface DatasourceConstructorRegistry {
|
|
11
|
+
[key: string]: new () => BaseDatasource | object;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Datasource instance registry type
|
|
16
|
+
*/
|
|
17
|
+
export interface DatasourceInstanceRegistry {
|
|
18
|
+
[key: string]: object;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Creates a Moleculer mixin for datasource injection
|
|
23
|
+
* Simple mixin that just instantiates datasources and injects them into context
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import {DatasourceMixin} from '@pkg/sdk';
|
|
28
|
+
*
|
|
29
|
+
* export default {
|
|
30
|
+
* name: 'users',
|
|
31
|
+
* mixins: [DatasourceMixin({
|
|
32
|
+
* userDb: UserDatasource,
|
|
33
|
+
* cache: CacheDatasource,
|
|
34
|
+
* })],
|
|
35
|
+
* actions: {
|
|
36
|
+
* get: {
|
|
37
|
+
* handler(ctx) {
|
|
38
|
+
* // Access datasources via ctx.datasources
|
|
39
|
+
* return ctx.datasources.userDb.findById(ctx.params.id);
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function DatasourceMixin(
|
|
47
|
+
datasourceConstructors: DatasourceConstructorRegistry = {}
|
|
48
|
+
): Partial<ServiceSchema> {
|
|
49
|
+
// Initialize datasources once
|
|
50
|
+
const datasourceInstances: DatasourceInstanceRegistry = {};
|
|
51
|
+
|
|
52
|
+
for (const [key, DatasourceClass] of Object.entries(datasourceConstructors)) {
|
|
53
|
+
datasourceInstances[key] = new DatasourceClass();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
/**
|
|
58
|
+
* Service created lifecycle hook
|
|
59
|
+
* Initialize datasources and store on service
|
|
60
|
+
*/
|
|
61
|
+
async created() {
|
|
62
|
+
// Call init() on datasources that have it
|
|
63
|
+
for (const [, datasource] of Object.entries(datasourceInstances)) {
|
|
64
|
+
if (typeof (datasource as any).init === 'function') {
|
|
65
|
+
await (datasource as any).init();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Store instances on the service for access in methods
|
|
70
|
+
(this as any).$datasources = datasourceInstances;
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Service started lifecycle hook
|
|
75
|
+
* Connect datasources that have connect method
|
|
76
|
+
*/
|
|
77
|
+
async started() {
|
|
78
|
+
// Call connect() on datasources that have it
|
|
79
|
+
for (const [, datasource] of Object.entries(datasourceInstances)) {
|
|
80
|
+
if (typeof (datasource as any).connect === 'function') {
|
|
81
|
+
await (datasource as any).connect();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Service stopped lifecycle hook
|
|
88
|
+
* Disconnect datasources that have disconnect method
|
|
89
|
+
*/
|
|
90
|
+
async stopped() {
|
|
91
|
+
// Call disconnect() on datasources that have it
|
|
92
|
+
for (const [, datasource] of Object.entries(datasourceInstances)) {
|
|
93
|
+
if (typeof (datasource as any).disconnect === 'function') {
|
|
94
|
+
await (datasource as any).disconnect();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Hooks to inject datasources into context
|
|
101
|
+
*/
|
|
102
|
+
hooks: {
|
|
103
|
+
before: {
|
|
104
|
+
'*': function injectDatasources(ctx) {
|
|
105
|
+
// Inject datasources into the context
|
|
106
|
+
(ctx as AppContext).datasources = (this as any).$datasources ?? {};
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Type helper for services that use the datasource mixin
|
|
115
|
+
*/
|
|
116
|
+
export type ServiceWithDatasources<T extends DatasourceInstanceRegistry> = {
|
|
117
|
+
datasources: T;
|
|
118
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './datasource.mixin';
|