@seidor-cloud-produtos/orbit-backend-lib 2.0.110 → 2.0.111
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/dist/clean-arch/application/cache/cache.spec.d.ts +1 -0
- package/dist/clean-arch/application/cache/cache.spec.js +178 -0
- package/dist/clean-arch/application/consistency-event-dispatcher/consistency-event-dispatcher.d.ts +1 -1
- package/dist/clean-arch/application/logger/logger.spec.d.ts +1 -0
- package/dist/clean-arch/application/logger/logger.spec.js +204 -0
- package/dist/clean-arch/domain/entities/entity.spec.d.ts +1 -0
- package/dist/clean-arch/domain/entities/entity.spec.js +130 -0
- package/dist/clean-arch/domain/events/consistency-event/consistency-event.spec.d.ts +1 -0
- package/dist/clean-arch/domain/events/consistency-event/consistency-event.spec.js +91 -0
- package/dist/clean-arch/domain/value-objects/password.spec.d.ts +1 -0
- package/dist/clean-arch/domain/value-objects/password.spec.js +90 -0
- package/dist/clean-arch/infra/adapters/http-adapters.spec.d.ts +1 -0
- package/dist/clean-arch/infra/adapters/http-adapters.spec.js +318 -0
- package/dist/clean-arch/infra/authorizations/authorizer.spec.js +43 -0
- package/dist/clean-arch/infra/cache/cache-clients.spec.d.ts +1 -0
- package/dist/clean-arch/infra/cache/cache-clients.spec.js +161 -0
- package/dist/clean-arch/infra/cache/clients/node-cache.js +3 -2
- package/dist/clean-arch/infra/http/controller.spec.js +170 -51
- package/dist/clean-arch/infra/http/helpers.spec.d.ts +1 -0
- package/dist/clean-arch/infra/http/helpers.spec.js +136 -0
- package/dist/clean-arch/infra/http/net.spec.d.ts +1 -0
- package/dist/clean-arch/infra/http/net.spec.js +46 -0
- package/dist/clean-arch/infra/logger/logger-orbit.d.ts +2 -0
- package/dist/clean-arch/infra/logger/logger-orbit.spec.d.ts +1 -0
- package/dist/clean-arch/infra/logger/logger-orbit.spec.js +122 -0
- package/dist/clean-arch/infra/orbit-http-client/orbit-axios-client/orbit-axios-client.spec.d.ts +1 -0
- package/dist/clean-arch/infra/orbit-http-client/orbit-axios-client/orbit-axios-client.spec.js +143 -0
- package/dist/clean-arch/infra/orbit-notification-client/discord-client.spec.d.ts +1 -0
- package/dist/clean-arch/infra/orbit-notification-client/discord-client.spec.js +58 -0
- package/dist/clean-arch/infra/queue/queue-utils.spec.d.ts +1 -0
- package/dist/clean-arch/infra/queue/queue-utils.spec.js +170 -0
- package/dist/clean-arch/infra/queue/rabbitmq/amqp-lib.js +4 -4
- package/dist/clean-arch/infra/queue/rabbitmq/amqp-lib.spec.js +466 -89
- package/dist/clean-arch/infra/queue/rabbitmq/types.d.ts +1 -1
- package/dist/clean-arch/infra/scaledjob/amqp/runner.d.ts +0 -3
- package/dist/clean-arch/infra/scaledjob/amqp/runner.js +5 -6
- package/dist/clean-arch/infra/scaledjob/amqp/runner.spec.js +74 -84
- package/dist/clean-arch/infra/scaledjob/sqs/runner.spec.d.ts +1 -0
- package/dist/clean-arch/infra/scaledjob/sqs/runner.spec.js +150 -0
- package/dist/clean-arch/infra/tracing/start-tracing.d.ts +10 -0
- package/dist/clean-arch/infra/tracing/start-tracing.js +78 -0
- package/dist/clean-arch/infra/validations/zod/validation-zod.spec.js +23 -0
- package/dist/clean-arch/shared/pagination/get-take-and-skip.spec.d.ts +1 -0
- package/dist/clean-arch/shared/pagination/get-take-and-skip.spec.js +16 -0
- package/dist/coverage/block-navigation.d.ts +1 -0
- package/dist/coverage/block-navigation.js +72 -0
- package/dist/coverage/prettify.d.ts +0 -0
- package/dist/coverage/prettify.js +1009 -0
- package/dist/coverage/sorter.d.ts +1 -0
- package/dist/coverage/sorter.js +178 -0
- package/dist/frameworks/express/api-gateway/mapping-model.spec.js +58 -0
- package/dist/frameworks/express/authorizations/authorization-express.spec.js +9 -0
- package/dist/frameworks/express/middleware-express.spec.d.ts +1 -0
- package/dist/frameworks/express/middleware-express.spec.js +51 -0
- package/dist/frameworks/nest/nest.spec.d.ts +1 -0
- package/dist/frameworks/nest/nest.spec.js +122 -0
- package/dist/infra/http/api-gateway/mapping-model.spec.js +42 -0
- package/package.json +5 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const cache_1 = require("./cache");
|
|
4
|
+
const client_1 = require("./client");
|
|
5
|
+
class MemoryCacheClient extends client_1.CacheClient {
|
|
6
|
+
startMock = vi.fn().mockResolvedValue(undefined);
|
|
7
|
+
closeMock = vi.fn().mockResolvedValue(undefined);
|
|
8
|
+
getMock = vi.fn();
|
|
9
|
+
setMock = vi.fn().mockResolvedValue(undefined);
|
|
10
|
+
delMock = vi.fn().mockResolvedValue(undefined);
|
|
11
|
+
delBatchMock = vi.fn().mockResolvedValue(undefined);
|
|
12
|
+
getKeysMock = vi.fn().mockResolvedValue([]);
|
|
13
|
+
flushMock = vi.fn().mockResolvedValue(undefined);
|
|
14
|
+
async start() {
|
|
15
|
+
await this.startMock();
|
|
16
|
+
}
|
|
17
|
+
async close() {
|
|
18
|
+
await this.closeMock();
|
|
19
|
+
}
|
|
20
|
+
async get(keyCache) {
|
|
21
|
+
return await this.getMock(keyCache);
|
|
22
|
+
}
|
|
23
|
+
async set(keyCache, data, expiration) {
|
|
24
|
+
await this.setMock(keyCache, data, expiration);
|
|
25
|
+
}
|
|
26
|
+
async del(keyCache) {
|
|
27
|
+
await this.delMock(keyCache);
|
|
28
|
+
}
|
|
29
|
+
async delBatch(keyCache) {
|
|
30
|
+
await this.delBatchMock(keyCache);
|
|
31
|
+
}
|
|
32
|
+
async getKeys(pattern) {
|
|
33
|
+
return await this.getKeysMock(pattern);
|
|
34
|
+
}
|
|
35
|
+
async flush() {
|
|
36
|
+
await this.flushMock();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
class TestCache extends cache_1.Cache {
|
|
40
|
+
client = new MemoryCacheClient();
|
|
41
|
+
get currentClient() {
|
|
42
|
+
return this.client;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const createLogger = () => ({
|
|
46
|
+
info: vi.fn(),
|
|
47
|
+
warn: vi.fn(),
|
|
48
|
+
error: vi.fn(),
|
|
49
|
+
debug: vi.fn(),
|
|
50
|
+
});
|
|
51
|
+
describe('Cache base', () => {
|
|
52
|
+
it('should perform the happy path operations', async () => {
|
|
53
|
+
const logger = createLogger();
|
|
54
|
+
const cache = new TestCache(undefined, logger);
|
|
55
|
+
cache.currentClient.getMock.mockResolvedValueOnce({ id: '1' });
|
|
56
|
+
cache.currentClient.getKeysMock.mockResolvedValueOnce(['cache:key']);
|
|
57
|
+
await cache.start();
|
|
58
|
+
await expect(cache.get('cache:key')).resolves.toEqual({ id: '1' });
|
|
59
|
+
await expect(cache.set('cache:key', { id: '1' }, 10)).resolves.toBeUndefined();
|
|
60
|
+
await expect(cache.del('cache:key')).resolves.toBeUndefined();
|
|
61
|
+
await expect(cache.getKeys('cache:*')).resolves.toEqual(['cache:key']);
|
|
62
|
+
await expect(cache.delBatch(['cache:key'])).resolves.toBeUndefined();
|
|
63
|
+
await expect(cache.flush()).resolves.toBeUndefined();
|
|
64
|
+
cache.currentClient.getMock.mockResolvedValueOnce({ ping: true });
|
|
65
|
+
await expect(cache.isRunning()).resolves.toBe(true);
|
|
66
|
+
await cache.close();
|
|
67
|
+
expect(cache.currentClient.startMock).toHaveBeenCalledTimes(1);
|
|
68
|
+
expect(cache.currentClient.setMock).toHaveBeenNthCalledWith(1, 'cache:key', JSON.stringify({ id: '1' }), 10);
|
|
69
|
+
expect(cache.currentClient.delMock).toHaveBeenCalledWith('cache:key');
|
|
70
|
+
expect(cache.currentClient.delBatchMock).toHaveBeenCalledWith([
|
|
71
|
+
'cache:key',
|
|
72
|
+
]);
|
|
73
|
+
expect(cache.currentClient.flushMock).toHaveBeenCalledTimes(1);
|
|
74
|
+
expect(cache.currentClient.closeMock).toHaveBeenCalledTimes(1);
|
|
75
|
+
expect(logger.info).toHaveBeenCalledWith('💾 Cache Connected');
|
|
76
|
+
expect(logger.info).toHaveBeenCalledWith('💾 Cache closed');
|
|
77
|
+
});
|
|
78
|
+
it('should fetch, cache and reuse values', async () => {
|
|
79
|
+
const cache = new TestCache();
|
|
80
|
+
const promise = vi.fn().mockResolvedValue({ id: '1' });
|
|
81
|
+
cache.currentClient.getMock
|
|
82
|
+
.mockResolvedValueOnce(null)
|
|
83
|
+
.mockResolvedValueOnce({ id: '1' });
|
|
84
|
+
await expect(cache.getOrFetchAndCache('cache:key', 15, promise)).resolves.toEqual({ id: '1' });
|
|
85
|
+
await expect(cache.getOrFetchAndCache('cache:key', 15, promise)).resolves.toEqual({ id: '1' });
|
|
86
|
+
expect(promise).toHaveBeenCalledTimes(1);
|
|
87
|
+
expect(cache.currentClient.setMock).toHaveBeenCalledWith('cache:key', JSON.stringify({ id: '1' }), 15);
|
|
88
|
+
});
|
|
89
|
+
it('should return safe fallbacks when operations fail without throwOnError', async () => {
|
|
90
|
+
const cache = new TestCache();
|
|
91
|
+
cache.currentClient.getMock.mockRejectedValue(new Error('get failed'));
|
|
92
|
+
cache.currentClient.setMock.mockRejectedValue(new Error('set failed'));
|
|
93
|
+
cache.currentClient.delMock.mockRejectedValue(new Error('del failed'));
|
|
94
|
+
cache.currentClient.getKeysMock.mockRejectedValue(new Error('keys failed'));
|
|
95
|
+
cache.currentClient.delBatchMock.mockRejectedValue(new Error('batch failed'));
|
|
96
|
+
cache.currentClient.closeMock.mockRejectedValue(new Error('close failed'));
|
|
97
|
+
cache.currentClient.getMock.mockResolvedValueOnce(false);
|
|
98
|
+
await expect(cache.get('cache:key')).resolves.toBeNull();
|
|
99
|
+
await expect(cache.set('cache:key', { id: '1' })).resolves.toBeUndefined();
|
|
100
|
+
await expect(cache.del('cache:key')).resolves.toBeUndefined();
|
|
101
|
+
await expect(cache.getKeys('cache:*')).resolves.toBeNull();
|
|
102
|
+
await expect(cache.delBatch(['cache:key'])).resolves.toBeUndefined();
|
|
103
|
+
await expect(cache.close()).resolves.toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
it('should throw when throwOnError is enabled', async () => {
|
|
106
|
+
const logger = createLogger();
|
|
107
|
+
const cache = new TestCache(undefined, logger);
|
|
108
|
+
cache.currentClient.getMock.mockRejectedValue(new Error('get failed'));
|
|
109
|
+
await expect(cache.get('cache:key', { throwOnError: true })).rejects.toThrow('get failed');
|
|
110
|
+
expect(logger.error).toHaveBeenCalledWith(JSON.stringify(new Error('get failed')));
|
|
111
|
+
});
|
|
112
|
+
it('should switch to a running contingency client', async () => {
|
|
113
|
+
const logger = createLogger();
|
|
114
|
+
const cache = new TestCache(undefined, logger);
|
|
115
|
+
const primary = cache.currentClient;
|
|
116
|
+
const contingency = new MemoryCacheClient();
|
|
117
|
+
primary.getMock.mockResolvedValue(false);
|
|
118
|
+
contingency.getMock.mockResolvedValue({ ping: true });
|
|
119
|
+
cache.elegibleClients = [contingency, primary];
|
|
120
|
+
await expect(cache.isRunning()).resolves.toBe(true);
|
|
121
|
+
expect(primary.closeMock).toHaveBeenCalledTimes(1);
|
|
122
|
+
expect(logger.warn).toHaveBeenCalledWith('Changed cache client to MemoryCacheClient');
|
|
123
|
+
expect(cache.client).toBe(contingency);
|
|
124
|
+
});
|
|
125
|
+
it('should tolerate failed start with a single eligible client', async () => {
|
|
126
|
+
const logger = createLogger();
|
|
127
|
+
const cache = new TestCache(undefined, logger);
|
|
128
|
+
cache.options = { throwOnError: false };
|
|
129
|
+
cache.elegibleClients = [cache.currentClient];
|
|
130
|
+
cache.currentClient.startMock.mockRejectedValueOnce(new Error('start failed'));
|
|
131
|
+
await expect(cache.start()).resolves.toBeUndefined();
|
|
132
|
+
expect(logger.warn).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
it('should ignore contingency discovery errors while switching cache clients', async () => {
|
|
135
|
+
const logger = createLogger();
|
|
136
|
+
const cache = new TestCache(undefined, logger);
|
|
137
|
+
const primary = cache.currentClient;
|
|
138
|
+
const brokenContingency = new MemoryCacheClient();
|
|
139
|
+
const healthyContingency = new MemoryCacheClient();
|
|
140
|
+
cache.options = { throwOnError: false };
|
|
141
|
+
cache.elegibleClients = [
|
|
142
|
+
brokenContingency,
|
|
143
|
+
healthyContingency,
|
|
144
|
+
primary,
|
|
145
|
+
];
|
|
146
|
+
primary.startMock.mockRejectedValueOnce(new Error('primary start failed'));
|
|
147
|
+
primary.closeMock.mockRejectedValueOnce(new Error('primary close failed'));
|
|
148
|
+
brokenContingency.startMock.mockRejectedValueOnce(new Error('contingency start failed'));
|
|
149
|
+
healthyContingency.getMock.mockResolvedValue({ ping: true });
|
|
150
|
+
await expect(cache.start()).resolves.toBeUndefined();
|
|
151
|
+
expect(cache.client).toBe(healthyContingency);
|
|
152
|
+
expect(brokenContingency.startMock).toHaveBeenCalledTimes(1);
|
|
153
|
+
expect(brokenContingency.closeMock).toHaveBeenCalledTimes(1);
|
|
154
|
+
expect(primary.closeMock).toHaveBeenCalledTimes(1);
|
|
155
|
+
expect(logger.warn).toHaveBeenCalledWith('Changed cache client to MemoryCacheClient');
|
|
156
|
+
});
|
|
157
|
+
it('should return false when health checks fail without throwOnError', async () => {
|
|
158
|
+
const logger = createLogger();
|
|
159
|
+
const cache = new TestCache(undefined, logger);
|
|
160
|
+
cache.options = { throwOnError: false };
|
|
161
|
+
cache.elegibleClients = [cache.currentClient];
|
|
162
|
+
cache.currentClient.setMock.mockRejectedValue(new Error('ping failed'));
|
|
163
|
+
await expect(cache.isRunning()).resolves.toBe(false);
|
|
164
|
+
});
|
|
165
|
+
it('should throw when close fails and throwOnError is enabled', async () => {
|
|
166
|
+
const logger = createLogger();
|
|
167
|
+
const cache = new TestCache(undefined, logger);
|
|
168
|
+
cache.currentClient.closeMock.mockRejectedValue(new Error('close failed'));
|
|
169
|
+
await expect(cache.close({ throwOnError: true })).rejects.toThrowError('close failed');
|
|
170
|
+
expect(logger.error).toHaveBeenCalledWith(JSON.stringify(new Error('close failed')));
|
|
171
|
+
});
|
|
172
|
+
it('should verify if a cache client is running with a ping key', async () => {
|
|
173
|
+
const client = new MemoryCacheClient({ timeout: 5 });
|
|
174
|
+
client.getMock.mockResolvedValue({ ping: true });
|
|
175
|
+
await expect(client.isRunning()).resolves.toBe(true);
|
|
176
|
+
expect(client.setMock).toHaveBeenCalledWith(expect.any(String), JSON.stringify({ ping: true }), 5);
|
|
177
|
+
});
|
|
178
|
+
});
|
package/dist/clean-arch/application/consistency-event-dispatcher/consistency-event-dispatcher.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AuthorizerHeaders } from '../../infra/http/handle-authorizer-headers';
|
|
2
1
|
import { Entity } from '../../domain/entities/entity';
|
|
3
2
|
import ConsistencyEvent from '../../domain/events/consistency-event/consistency-event';
|
|
4
3
|
import { EventsName } from '../../domain/events/consistency-event/events-enum';
|
|
4
|
+
import { AuthorizerHeaders } from '../../infra/http/handle-authorizer-headers';
|
|
5
5
|
export declare abstract class ConsistencyEventDispatcher {
|
|
6
6
|
dispatch(entity: Entity<any>, authorizerHeaders: AuthorizerHeaders, eventName: EventsName): Promise<void>;
|
|
7
7
|
dispatchBatch(entities: Entity<any>[], authorizerHeaders: AuthorizerHeaders, eventName: EventsName): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const types_1 = require("../../infra/environment/types");
|
|
5
|
+
const logger_in_memory_1 = require("../../infra/logger/logger-in-memory");
|
|
6
|
+
const base_audit_logger_1 = require("./base-audit-logger");
|
|
7
|
+
const index_1 = tslib_1.__importStar(require("./index"));
|
|
8
|
+
class TestLogger extends index_1.default {
|
|
9
|
+
registerMock = vi.fn().mockResolvedValue(undefined);
|
|
10
|
+
register(props, dataToLog) {
|
|
11
|
+
return this.registerMock(props, dataToLog);
|
|
12
|
+
}
|
|
13
|
+
async exposeIsToLog(props, dataToLog) {
|
|
14
|
+
return await this.isToLog(props, dataToLog);
|
|
15
|
+
}
|
|
16
|
+
exposeBuildProps(props, params) {
|
|
17
|
+
return this.buildProps(props, params);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
class TestAuditLogger extends base_audit_logger_1.BaseAuditLogger {
|
|
21
|
+
exposeBuildParams(type, authorizerHeaders, trace) {
|
|
22
|
+
return this.buildParams(type, authorizerHeaders, trace);
|
|
23
|
+
}
|
|
24
|
+
async exposeInfo(payload, params) {
|
|
25
|
+
await this.info(payload, params);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
describe('Logger base', () => {
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
vi.restoreAllMocks();
|
|
31
|
+
});
|
|
32
|
+
it('should build props and split params from the last argument', async () => {
|
|
33
|
+
const logger = new TestLogger({
|
|
34
|
+
options: {
|
|
35
|
+
lastArgIsParams: true,
|
|
36
|
+
},
|
|
37
|
+
params: {
|
|
38
|
+
actor: 'default-actor',
|
|
39
|
+
type: 'default-type',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
await logger.log({ level: types_1.LOG_LEVEL.warn }, [
|
|
43
|
+
'payload',
|
|
44
|
+
{ actor: 'custom-actor', type: 'custom-type' },
|
|
45
|
+
]);
|
|
46
|
+
expect(logger.registerMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
47
|
+
level: types_1.LOG_LEVEL.warn,
|
|
48
|
+
actor: 'custom-actor',
|
|
49
|
+
type: 'custom-type',
|
|
50
|
+
dateTime: expect.any(Date),
|
|
51
|
+
}), 'payload');
|
|
52
|
+
});
|
|
53
|
+
it('should evaluate log levels using the cache filters', async () => {
|
|
54
|
+
const cache = {
|
|
55
|
+
getKeys: vi
|
|
56
|
+
.fn()
|
|
57
|
+
.mockResolvedValue(['LOG_LEVEL:test-type:*', 'LOG_LEVEL:*:*']),
|
|
58
|
+
get: vi.fn().mockResolvedValue(types_1.LOG_LEVEL.info),
|
|
59
|
+
set: vi.fn(),
|
|
60
|
+
};
|
|
61
|
+
const logger = new TestLogger({
|
|
62
|
+
cache: cache,
|
|
63
|
+
params: {
|
|
64
|
+
actor: 'test-actor',
|
|
65
|
+
type: 'test-type',
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
await expect(logger.exposeIsToLog({
|
|
69
|
+
level: types_1.LOG_LEVEL.warn,
|
|
70
|
+
actor: 'test-actor',
|
|
71
|
+
type: 'test-type',
|
|
72
|
+
dateTime: new Date(),
|
|
73
|
+
}, ['payload'])).resolves.toBe(true);
|
|
74
|
+
await expect(logger.exposeIsToLog({
|
|
75
|
+
level: types_1.LOG_LEVEL.debug,
|
|
76
|
+
actor: 'test-actor',
|
|
77
|
+
type: 'test-type',
|
|
78
|
+
dateTime: new Date(),
|
|
79
|
+
}, [])).resolves.toBe(false);
|
|
80
|
+
await logger.setLevel(types_1.LOG_LEVEL.debug, {
|
|
81
|
+
logActor: 'user',
|
|
82
|
+
logType: 'partner',
|
|
83
|
+
});
|
|
84
|
+
expect(cache.set).toHaveBeenCalledWith('LOG_LEVEL:partner:user', types_1.LOG_LEVEL.debug, 345600);
|
|
85
|
+
});
|
|
86
|
+
it('should fallback to default props and swallow logging errors', async () => {
|
|
87
|
+
const consoleError = vi
|
|
88
|
+
.spyOn(console, 'error')
|
|
89
|
+
.mockImplementation(() => undefined);
|
|
90
|
+
const consoleLog = vi
|
|
91
|
+
.spyOn(console, 'log')
|
|
92
|
+
.mockImplementation(() => undefined);
|
|
93
|
+
const logger = new TestLogger();
|
|
94
|
+
logger.registerMock.mockRejectedValue(new Error('register failed'));
|
|
95
|
+
expect(logger.exposeBuildProps(undefined, undefined)).toEqual(expect.objectContaining({
|
|
96
|
+
level: types_1.LOG_LEVEL.info,
|
|
97
|
+
actor: 'default',
|
|
98
|
+
type: 'default',
|
|
99
|
+
dateTime: expect.any(Date),
|
|
100
|
+
}));
|
|
101
|
+
await expect(logger.log({ level: types_1.LOG_LEVEL.info }, 'payload')).resolves.toBeUndefined();
|
|
102
|
+
expect(consoleError).toHaveBeenCalledWith('Error in log', expect.any(Error));
|
|
103
|
+
expect(consoleLog).toHaveBeenCalledWith('Input', { level: types_1.LOG_LEVEL.info }, 'payload');
|
|
104
|
+
});
|
|
105
|
+
it('should prefer exact cached filters and expose the level helpers', async () => {
|
|
106
|
+
const cache = {
|
|
107
|
+
getKeys: vi
|
|
108
|
+
.fn()
|
|
109
|
+
.mockResolvedValue(['LOG_LEVEL:*:*', 'LOG_LEVEL:test-type:test-actor']),
|
|
110
|
+
get: vi.fn().mockImplementation(async (key) => {
|
|
111
|
+
return key === 'LOG_LEVEL:test-type:test-actor'
|
|
112
|
+
? types_1.LOG_LEVEL.debug
|
|
113
|
+
: types_1.LOG_LEVEL.error;
|
|
114
|
+
}),
|
|
115
|
+
set: vi.fn(),
|
|
116
|
+
};
|
|
117
|
+
const logger = new TestLogger({
|
|
118
|
+
cache: cache,
|
|
119
|
+
params: {
|
|
120
|
+
actor: 'test-actor',
|
|
121
|
+
type: 'test-type',
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
await logger.info('info-payload');
|
|
125
|
+
await logger.warn('warn-payload');
|
|
126
|
+
await logger.error('error-payload');
|
|
127
|
+
await logger.debug('debug-payload');
|
|
128
|
+
expect(cache.get).toHaveBeenCalledWith('LOG_LEVEL:test-type:test-actor');
|
|
129
|
+
expect(logger.registerMock).toHaveBeenCalledTimes(4);
|
|
130
|
+
expect(logger.registerMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ level: types_1.LOG_LEVEL.info }), ['info-payload']);
|
|
131
|
+
expect(logger.registerMock).toHaveBeenNthCalledWith(4, expect.objectContaining({ level: types_1.LOG_LEVEL.debug }), ['debug-payload']);
|
|
132
|
+
});
|
|
133
|
+
it('should skip setLevel when no cache is configured', async () => {
|
|
134
|
+
const logger = new TestLogger();
|
|
135
|
+
await expect(logger.setLevel(types_1.LOG_LEVEL.warn, {
|
|
136
|
+
logActor: 'user',
|
|
137
|
+
logType: 'partner',
|
|
138
|
+
})).resolves.toBeUndefined();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
describe('LoggerInMemory', () => {
|
|
142
|
+
afterEach(() => {
|
|
143
|
+
vi.restoreAllMocks();
|
|
144
|
+
});
|
|
145
|
+
it('should dispatch console calls according to the log level', () => {
|
|
146
|
+
const consoleInfo = vi
|
|
147
|
+
.spyOn(console, 'info')
|
|
148
|
+
.mockImplementation(() => undefined);
|
|
149
|
+
const consoleWarn = vi
|
|
150
|
+
.spyOn(console, 'warn')
|
|
151
|
+
.mockImplementation(() => undefined);
|
|
152
|
+
const consoleError = vi
|
|
153
|
+
.spyOn(console, 'error')
|
|
154
|
+
.mockImplementation(() => undefined);
|
|
155
|
+
const consoleDebug = vi
|
|
156
|
+
.spyOn(console, 'debug')
|
|
157
|
+
.mockImplementation(() => undefined);
|
|
158
|
+
const logger = new logger_in_memory_1.LoggerInMemory({
|
|
159
|
+
options: {
|
|
160
|
+
showProps: true,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
logger['register']({ level: types_1.LOG_LEVEL.info }, 'payload');
|
|
164
|
+
logger['register']({ level: types_1.LOG_LEVEL.warn }, ['payload']);
|
|
165
|
+
logger['register']({ level: types_1.LOG_LEVEL.error }, 'payload');
|
|
166
|
+
logger['register']({ level: types_1.LOG_LEVEL.debug }, 'payload');
|
|
167
|
+
expect(consoleInfo).toHaveBeenCalledWith('payload', {
|
|
168
|
+
level: types_1.LOG_LEVEL.info,
|
|
169
|
+
});
|
|
170
|
+
expect(consoleWarn).toHaveBeenCalled();
|
|
171
|
+
expect(consoleError).toHaveBeenCalled();
|
|
172
|
+
expect(consoleDebug).toHaveBeenCalled();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe('Audit loggers', () => {
|
|
176
|
+
afterEach(() => {
|
|
177
|
+
vi.restoreAllMocks();
|
|
178
|
+
});
|
|
179
|
+
it('should build audit params and delegate info logs', async () => {
|
|
180
|
+
const gateway = {
|
|
181
|
+
info: vi.fn().mockResolvedValue(undefined),
|
|
182
|
+
};
|
|
183
|
+
const logger = new TestAuditLogger(gateway);
|
|
184
|
+
const trace = { ids: [] };
|
|
185
|
+
const params = logger.exposeBuildParams('partner-created', {
|
|
186
|
+
entityId: 'user-id',
|
|
187
|
+
tenantId: 'tenant-id',
|
|
188
|
+
traceId: 'trace-id',
|
|
189
|
+
}, trace);
|
|
190
|
+
expect(params).toEqual({
|
|
191
|
+
type: 'partner-created',
|
|
192
|
+
trace: {
|
|
193
|
+
ids: ['trace-id'],
|
|
194
|
+
},
|
|
195
|
+
actor: 'user-id',
|
|
196
|
+
tenantId: 'tenant-id',
|
|
197
|
+
actorType: index_1.ActorEnum.USER,
|
|
198
|
+
category: index_1.CategoryEnum.BUSINESS,
|
|
199
|
+
expirationHours: 43800,
|
|
200
|
+
});
|
|
201
|
+
await logger.exposeInfo({ id: '1' }, params);
|
|
202
|
+
expect(gateway.info).toHaveBeenCalledWith({ payload: { id: '1' } }, params);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const aggregate_root_1 = require("./aggregate-root");
|
|
4
|
+
const entity_1 = require("./entity");
|
|
5
|
+
const unique_entity_id_1 = require("./unique-entity-id");
|
|
6
|
+
const value_object_1 = require("./value-object");
|
|
7
|
+
const watched_list_1 = require("./watched-list");
|
|
8
|
+
class StringWatchedList extends watched_list_1.WatchedList {
|
|
9
|
+
compareItems(a, b) {
|
|
10
|
+
return a === b;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
class TestEntity extends entity_1.Entity {
|
|
14
|
+
constructor(props, id) {
|
|
15
|
+
super(props, id);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class TestAggregate extends aggregate_root_1.AggregateRoot {
|
|
19
|
+
constructor(props, id) {
|
|
20
|
+
super(props, id);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
class TestValueObject extends value_object_1.ValueObject {
|
|
24
|
+
constructor(value) {
|
|
25
|
+
super({ value });
|
|
26
|
+
}
|
|
27
|
+
get value() {
|
|
28
|
+
return this.props.value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
describe('Entity', () => {
|
|
32
|
+
it('should compare entities by reference or shared id instance', () => {
|
|
33
|
+
const sharedId = new unique_entity_id_1.UniqueEntityId('shared-id');
|
|
34
|
+
const entity = new TestEntity({ name: 'primary' }, sharedId);
|
|
35
|
+
const sameReference = entity;
|
|
36
|
+
const sameId = new TestEntity({ name: 'secondary' }, sharedId);
|
|
37
|
+
const different = new TestEntity({ name: 'other' }, new unique_entity_id_1.UniqueEntityId('different-id'));
|
|
38
|
+
expect(entity.equals(sameReference)).toBe(true);
|
|
39
|
+
expect(entity.equals(sameId)).toBe(true);
|
|
40
|
+
expect(entity.equals(different)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
it('should serialize nested values to plain objects', () => {
|
|
43
|
+
const createdAt = new Date('2024-03-01T10:20:30.000Z');
|
|
44
|
+
const ownerId = new unique_entity_id_1.UniqueEntityId('owner-id');
|
|
45
|
+
const tags = new StringWatchedList(['finance', 'operations']);
|
|
46
|
+
const child = new TestEntity({
|
|
47
|
+
name: 'child',
|
|
48
|
+
createdAt,
|
|
49
|
+
ownerId,
|
|
50
|
+
tags,
|
|
51
|
+
}, new unique_entity_id_1.UniqueEntityId('child-id'));
|
|
52
|
+
const entity = new TestEntity({
|
|
53
|
+
name: 'parent',
|
|
54
|
+
createdAt,
|
|
55
|
+
ownerId,
|
|
56
|
+
child,
|
|
57
|
+
tags,
|
|
58
|
+
values: [ownerId, createdAt, { active: true }],
|
|
59
|
+
metadata: {
|
|
60
|
+
nestedDate: createdAt,
|
|
61
|
+
nestedId: ownerId,
|
|
62
|
+
nestedEntity: child,
|
|
63
|
+
},
|
|
64
|
+
}, new unique_entity_id_1.UniqueEntityId('parent-id'));
|
|
65
|
+
expect(entity.toObject()).toEqual({
|
|
66
|
+
name: 'parent',
|
|
67
|
+
createdAt: createdAt.toISOString(),
|
|
68
|
+
ownerId: 'owner-id',
|
|
69
|
+
child: {
|
|
70
|
+
name: 'child',
|
|
71
|
+
createdAt: createdAt.toISOString(),
|
|
72
|
+
ownerId: 'owner-id',
|
|
73
|
+
tags: ['finance', 'operations'],
|
|
74
|
+
id: 'child-id',
|
|
75
|
+
},
|
|
76
|
+
tags: ['finance', 'operations'],
|
|
77
|
+
values: ['owner-id', createdAt.toISOString(), { active: true }],
|
|
78
|
+
metadata: {
|
|
79
|
+
nestedDate: createdAt.toISOString(),
|
|
80
|
+
nestedId: 'owner-id',
|
|
81
|
+
nestedEntity: {
|
|
82
|
+
name: 'child',
|
|
83
|
+
createdAt: createdAt.toISOString(),
|
|
84
|
+
ownerId: 'owner-id',
|
|
85
|
+
tags: ['finance', 'operations'],
|
|
86
|
+
id: 'child-id',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
id: 'parent-id',
|
|
90
|
+
});
|
|
91
|
+
expect(entity.toEventDispatchObject()).toEqual(entity.toObject());
|
|
92
|
+
});
|
|
93
|
+
it('should allow replacing the id through the setter', () => {
|
|
94
|
+
const entity = new TestEntity({ name: 'test' });
|
|
95
|
+
const nextId = new unique_entity_id_1.UniqueEntityId('next-id');
|
|
96
|
+
entity.id = nextId;
|
|
97
|
+
expect(entity.id).toBe(nextId);
|
|
98
|
+
expect(entity.toObject()).toEqual({
|
|
99
|
+
name: 'test',
|
|
100
|
+
id: 'next-id',
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
describe('UniqueEntityId', () => {
|
|
105
|
+
it('should generate ids and compare them by value', () => {
|
|
106
|
+
const generatedId = new unique_entity_id_1.UniqueEntityId();
|
|
107
|
+
const copiedId = new unique_entity_id_1.UniqueEntityId(generatedId.toValue());
|
|
108
|
+
expect(generatedId.toString()).toBeTruthy();
|
|
109
|
+
expect(generatedId.equals(copiedId)).toBe(true);
|
|
110
|
+
expect(generatedId.toValue()).toBe(generatedId.toString());
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('AggregateRoot and ValueObject', () => {
|
|
114
|
+
it('should manage aggregate timestamps and expose value object props', () => {
|
|
115
|
+
const createdAt = new Date('2024-03-01T10:20:30.000Z');
|
|
116
|
+
const nextCreatedAt = new Date('2024-03-02T10:20:30.000Z');
|
|
117
|
+
const aggregate = new TestAggregate({
|
|
118
|
+
name: 'aggregate',
|
|
119
|
+
createdAt,
|
|
120
|
+
});
|
|
121
|
+
const valueObject = new TestValueObject('payload');
|
|
122
|
+
expect(aggregate.createdAt).toBe(createdAt);
|
|
123
|
+
expect(aggregate.updatedAt).toBeUndefined();
|
|
124
|
+
aggregate.touch();
|
|
125
|
+
aggregate.createdAt = nextCreatedAt;
|
|
126
|
+
expect(aggregate.createdAt).toBe(nextCreatedAt);
|
|
127
|
+
expect(aggregate.updatedAt).toBeInstanceOf(Date);
|
|
128
|
+
expect(valueObject.value).toBe('payload');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const entity_1 = require("../../entities/entity");
|
|
5
|
+
const unique_entity_id_1 = require("../../entities/unique-entity-id");
|
|
6
|
+
const events_enum_1 = require("./events-enum");
|
|
7
|
+
const consistency_event_1 = tslib_1.__importDefault(require("./consistency-event"));
|
|
8
|
+
const consistency_event_dispatcher_1 = require("../../../application/consistency-event-dispatcher/consistency-event-dispatcher");
|
|
9
|
+
const in_memory_consistency_event_dispatcher_1 = require("../../../infra/consistency-event-dispatcher/in-memory-consistency-event-dispatcher");
|
|
10
|
+
const amqp_consistency_event_dispatcher_1 = require("../../../infra/consistency-event-dispatcher/amqp-consistency-event-dispatcher");
|
|
11
|
+
const exchanges_enum_1 = require("../../../infra/consistency-event-dispatcher/exchanges-enum");
|
|
12
|
+
class TestEntity extends entity_1.Entity {
|
|
13
|
+
constructor(name, id) {
|
|
14
|
+
super({ name }, id);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
class TestDispatcher extends consistency_event_dispatcher_1.ConsistencyEventDispatcher {
|
|
18
|
+
publishMock = vi.fn().mockResolvedValue(undefined);
|
|
19
|
+
async publish(event) {
|
|
20
|
+
await this.publishMock(event);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
describe('Consistency events', () => {
|
|
24
|
+
const authorizerHeaders = {
|
|
25
|
+
userName: 'John',
|
|
26
|
+
userEmail: 'john@example.com',
|
|
27
|
+
appName: 'Portal',
|
|
28
|
+
entityId: 'user-id',
|
|
29
|
+
tenantId: 'tenant-id',
|
|
30
|
+
traceId: 'trace-id',
|
|
31
|
+
};
|
|
32
|
+
it('should create consistency events from entities', () => {
|
|
33
|
+
const entity = new TestEntity('entity-name', new unique_entity_id_1.UniqueEntityId('entity-id'));
|
|
34
|
+
const event = new consistency_event_1.default([entity], authorizerHeaders, events_enum_1.EventsName.PARTNER_CREATED);
|
|
35
|
+
expect(event.name).toBe('');
|
|
36
|
+
expect(event.eventDate).toBeInstanceOf(Date);
|
|
37
|
+
expect(event.input).toEqual({
|
|
38
|
+
eventName: events_enum_1.EventsName.PARTNER_CREATED,
|
|
39
|
+
origin: authorizerHeaders,
|
|
40
|
+
payload: [{ id: 'entity-id', name: 'entity-name' }],
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
it('should dispatch single and batch consistency events', async () => {
|
|
44
|
+
const dispatcher = new TestDispatcher();
|
|
45
|
+
const firstEntity = new TestEntity('first', new unique_entity_id_1.UniqueEntityId('first-id'));
|
|
46
|
+
const secondEntity = new TestEntity('second', new unique_entity_id_1.UniqueEntityId('second-id'));
|
|
47
|
+
await dispatcher.dispatch(firstEntity, authorizerHeaders, events_enum_1.EventsName.PARTNER_CREATED);
|
|
48
|
+
await dispatcher.dispatchBatch([firstEntity, secondEntity], authorizerHeaders, events_enum_1.EventsName.PARTNER_UPDATED);
|
|
49
|
+
expect(dispatcher.publishMock).toHaveBeenCalledTimes(2);
|
|
50
|
+
expect(dispatcher.publishMock.mock.calls[0][0].input).toEqual({
|
|
51
|
+
eventName: events_enum_1.EventsName.PARTNER_CREATED,
|
|
52
|
+
origin: authorizerHeaders,
|
|
53
|
+
payload: [{ id: 'first-id', name: 'first' }],
|
|
54
|
+
});
|
|
55
|
+
expect(dispatcher.publishMock.mock.calls[1][0].input).toEqual({
|
|
56
|
+
eventName: events_enum_1.EventsName.PARTNER_UPDATED,
|
|
57
|
+
origin: authorizerHeaders,
|
|
58
|
+
payload: [
|
|
59
|
+
{ id: 'first-id', name: 'first' },
|
|
60
|
+
{ id: 'second-id', name: 'second' },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it('should swallow dispatch errors and log them', async () => {
|
|
65
|
+
const consoleError = vi
|
|
66
|
+
.spyOn(console, 'error')
|
|
67
|
+
.mockImplementation(() => undefined);
|
|
68
|
+
const dispatcher = new TestDispatcher();
|
|
69
|
+
const entity = new TestEntity('entity', new unique_entity_id_1.UniqueEntityId('entity-id'));
|
|
70
|
+
dispatcher.publishMock.mockRejectedValue(new Error('publish failed'));
|
|
71
|
+
await dispatcher.dispatch(entity, authorizerHeaders, events_enum_1.EventsName.PARTNER_CREATED);
|
|
72
|
+
await dispatcher.dispatchBatch([entity], authorizerHeaders, events_enum_1.EventsName.PARTNER_CREATED);
|
|
73
|
+
expect(consoleError).toHaveBeenCalledTimes(2);
|
|
74
|
+
});
|
|
75
|
+
it('should publish consistency events in memory', async () => {
|
|
76
|
+
const dispatcher = new in_memory_consistency_event_dispatcher_1.InMemoryConsistencyEventDispatcher();
|
|
77
|
+
const event = new consistency_event_1.default([new TestEntity('entity', new unique_entity_id_1.UniqueEntityId('entity-id'))], authorizerHeaders, events_enum_1.EventsName.PARTNER_CREATED);
|
|
78
|
+
await dispatcher.publish(event);
|
|
79
|
+
expect(dispatcher.events).toEqual([event]);
|
|
80
|
+
});
|
|
81
|
+
it('should publish consistency events through AMQP', async () => {
|
|
82
|
+
const amqp = {
|
|
83
|
+
publish: vi.fn().mockResolvedValue(undefined),
|
|
84
|
+
};
|
|
85
|
+
const dispatcher = new amqp_consistency_event_dispatcher_1.AmqpConsistencyEventDispatcher(amqp);
|
|
86
|
+
const event = new consistency_event_1.default([new TestEntity('entity', new unique_entity_id_1.UniqueEntityId('entity-id'))], authorizerHeaders, events_enum_1.EventsName.PARTNER_CREATED);
|
|
87
|
+
await dispatcher.publish(event);
|
|
88
|
+
expect(amqp.publish).toHaveBeenCalledWith(`${exchanges_enum_1.PREFIX}.${events_enum_1.EventsName.PARTNER_CREATED}.${exchanges_enum_1.TYPE}`, event, { storeMessageOnError: false });
|
|
89
|
+
expect(exchanges_enum_1.ExchangesEnum.CONSISTENCY_PARTNER_CREATED).toBe(`${exchanges_enum_1.PREFIX}.${events_enum_1.EventsName.PARTNER_CREATED}.${exchanges_enum_1.TYPE}`);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|