@flusys/nestjs-shared 7.0.2 → 8.0.0-beta.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/classes/api-controller.class.d.ts +4 -4
- package/classes/api-service.class.d.ts +7 -7
- package/classes/disabled-event-bus.class.d.ts +1 -1
- package/classes/domain-event.publisher.d.ts +1 -1
- package/classes/event-bus.registry.d.ts +1 -1
- package/classes/event-transports/event-transport.registry.d.ts +1 -1
- package/classes/event-transports/index.d.ts +2 -2
- package/classes/event-transports/kafka-transport.optional.d.ts +1 -1
- package/classes/event-transports/memory-transport.d.ts +1 -1
- package/classes/event-transports/rabbitmq-transport.optional.d.ts +1 -1
- package/classes/hybrid-event-bus.class.d.ts +1 -1
- package/classes/index.d.ts +11 -11
- package/constants/index.d.ts +2 -2
- package/constants/permissions.d.ts +12 -0
- package/decorators/api-response.decorator.d.ts +1 -1
- package/decorators/current-user.decorator.d.ts +1 -1
- package/decorators/index.d.ts +6 -6
- package/decorators/require-permission.decorator.d.ts +1 -1
- package/dtos/filter-and-pagination.dto.d.ts +1 -1
- package/dtos/index.d.ts +6 -6
- package/entities/index.d.ts +3 -3
- package/enums/index.d.ts +3 -3
- package/exceptions/index.d.ts +2 -2
- package/fesm/470.js +2713 -0
- package/fesm/509.js +793 -0
- package/fesm/667.js +606 -0
- package/fesm/67.js +808 -0
- package/fesm/720.js +914 -0
- package/{cjs/classes/event-transports/rabbitmq-transport.optional.js → fesm/chunks/123.js} +78 -55
- package/fesm/{classes/event-transports/kafka-transport.optional.js → chunks/37.js} +74 -41
- package/fesm/classes/index.js +62 -12
- package/fesm/constants/index.js +445 -17
- package/fesm/decorators/index.js +341 -6
- package/fesm/dtos/index.js +23 -6
- package/fesm/entities/index.js +336 -3
- package/fesm/enums/index.js +57 -3
- package/fesm/exceptions/index.js +280 -2
- package/fesm/guards/index.js +1003 -3
- package/fesm/index.js +1524 -14
- package/fesm/interceptors/index.js +924 -5
- package/fesm/interfaces/index.js +57 -11
- package/fesm/middlewares/index.js +33 -1
- package/fesm/modules/index.js +432 -9
- package/fesm/runtime.js +134 -0
- package/fesm/utils/index.js +1045 -8
- package/filters/global-exception.filter.d.ts +1 -1
- package/filters/index.d.ts +1 -1
- package/guards/index.d.ts +3 -3
- package/guards/jwt-auth.guard.d.ts +1 -1
- package/guards/optional-jwt-auth.guard.d.ts +1 -1
- package/guards/permission.guard.d.ts +2 -2
- package/index.d.ts +14 -14
- package/interceptors/delete-empty-id-from-body.interceptor.d.ts +1 -1
- package/interceptors/idempotency.interceptor.d.ts +2 -2
- package/interceptors/index.d.ts +5 -5
- package/interceptors/response-meta.interceptor.d.ts +1 -1
- package/interceptors/set-user-field-on-body.interceptor.d.ts +1 -1
- package/interceptors/slug.interceptor.d.ts +2 -2
- package/interfaces/ai-assistant-adapter.interface.d.ts +1 -1
- package/interfaces/api.interface.d.ts +2 -2
- package/interfaces/datasource.interface.d.ts +1 -1
- package/interfaces/event-manager-adapter.interface.d.ts +2 -2
- package/interfaces/index.d.ts +11 -11
- package/interfaces/module-config.interface.d.ts +1 -1
- package/interfaces/notification-adapter.interface.d.ts +1 -1
- package/middlewares/index.d.ts +1 -1
- package/middlewares/logger.middleware.d.ts +2 -2
- package/modules/cache/cache.module.d.ts +1 -1
- package/modules/datasource/index.d.ts +1 -1
- package/modules/datasource/multi-tenant-datasource.service.d.ts +4 -4
- package/modules/event-bus/event-bus.explorer.d.ts +2 -2
- package/modules/event-bus/event-bus.module.d.ts +2 -2
- package/modules/event-bus/module-events.provider.d.ts +1 -1
- package/modules/index.d.ts +9 -9
- package/modules/shared-permission-cache/shared-permission-cache.service.d.ts +3 -3
- package/modules/utils/utils.service.d.ts +1 -1
- package/package.json +30 -44
- package/utils/index.d.ts +8 -8
- package/utils/permission-cache-key.util.d.ts +1 -1
- package/utils/query-helpers.util.d.ts +2 -2
- package/utils/request.util.d.ts +1 -1
- package/cjs/classes/api-controller.class.js +0 -728
- package/cjs/classes/api-controller.class.spec.js +0 -684
- package/cjs/classes/api-service.class.js +0 -631
- package/cjs/classes/api-service.class.spec.js +0 -1137
- package/cjs/classes/disabled-event-bus.class.js +0 -15
- package/cjs/classes/disabled-event-bus.class.spec.js +0 -40
- package/cjs/classes/domain-event.publisher.js +0 -76
- package/cjs/classes/domain-event.publisher.spec.js +0 -234
- package/cjs/classes/event-bus.registry.js +0 -68
- package/cjs/classes/event-bus.registry.spec.js +0 -141
- package/cjs/classes/event-transports/event-transport.registry.js +0 -41
- package/cjs/classes/event-transports/event-transport.registry.spec.js +0 -31
- package/cjs/classes/event-transports/index.js +0 -23
- package/cjs/classes/event-transports/kafka-transport.optional.js +0 -151
- package/cjs/classes/event-transports/kafka-transport.optional.spec.js +0 -198
- package/cjs/classes/event-transports/memory-transport.js +0 -53
- package/cjs/classes/event-transports/memory-transport.spec.js +0 -57
- package/cjs/classes/event-transports/rabbitmq-transport.optional.spec.js +0 -255
- package/cjs/classes/handle-custome-db.js +0 -37
- package/cjs/classes/handle-custome-db.spec.js +0 -82
- package/cjs/classes/hybrid-cache.class.js +0 -96
- package/cjs/classes/hybrid-cache.class.spec.js +0 -168
- package/cjs/classes/hybrid-event-bus.class.js +0 -281
- package/cjs/classes/hybrid-event-bus.class.spec.js +0 -218
- package/cjs/classes/index.js +0 -28
- package/cjs/classes/winston.logger.class.js +0 -274
- package/cjs/classes/winston.logger.class.spec.js +0 -330
- package/cjs/constants/index.js +0 -88
- package/cjs/constants/message-keys.js +0 -68
- package/cjs/constants/permissions.js +0 -297
- package/cjs/decorators/api-response.decorator.js +0 -52
- package/cjs/decorators/api-response.decorator.spec.js +0 -126
- package/cjs/decorators/current-user.decorator.js +0 -16
- package/cjs/decorators/current-user.decorator.spec.js +0 -74
- package/cjs/decorators/index.js +0 -23
- package/cjs/decorators/log-action.decorator.js +0 -149
- package/cjs/decorators/log-action.decorator.spec.js +0 -353
- package/cjs/decorators/on-domain-event.decorator.js +0 -13
- package/cjs/decorators/on-domain-event.decorator.spec.js +0 -50
- package/cjs/decorators/public.decorator.js +0 -13
- package/cjs/decorators/public.decorator.spec.js +0 -50
- package/cjs/decorators/require-permission.decorator.js +0 -32
- package/cjs/decorators/require-permission.decorator.spec.js +0 -147
- package/cjs/dtos/delete.dto.js +0 -92
- package/cjs/dtos/filter-and-pagination.dto.js +0 -164
- package/cjs/dtos/get-by-ids.dto.js +0 -65
- package/cjs/dtos/identity-response.dto.js +0 -108
- package/cjs/dtos/index.js +0 -23
- package/cjs/dtos/pagination.dto.js +0 -67
- package/cjs/dtos/response-payload.dto.js +0 -369
- package/cjs/entities/identity.js +0 -100
- package/cjs/entities/index.js +0 -20
- package/cjs/entities/raw-type.js +0 -4
- package/cjs/entities/user-root.js +0 -189
- package/cjs/enums/index.js +0 -20
- package/cjs/enums/notification-type.enum.js +0 -17
- package/cjs/enums/participant-status.enum.js +0 -17
- package/cjs/enums/recurrence-type.enum.js +0 -18
- package/cjs/exceptions/base-app.exception.js +0 -151
- package/cjs/exceptions/base-app.exception.spec.js +0 -157
- package/cjs/exceptions/index.js +0 -19
- package/cjs/exceptions/permission.exception.js +0 -56
- package/cjs/exceptions/permission.exception.spec.js +0 -82
- package/cjs/filters/global-exception.filter.js +0 -279
- package/cjs/filters/global-exception.filter.spec.js +0 -440
- package/cjs/filters/index.js +0 -18
- package/cjs/guards/index.js +0 -20
- package/cjs/guards/jwt-auth.guard.js +0 -75
- package/cjs/guards/jwt-auth.guard.spec.js +0 -75
- package/cjs/guards/optional-jwt-auth.guard.js +0 -67
- package/cjs/guards/optional-jwt-auth.guard.spec.js +0 -55
- package/cjs/guards/permission.guard.js +0 -179
- package/cjs/guards/permission.guard.spec.js +0 -373
- package/cjs/index.js +0 -31
- package/cjs/interceptors/delete-empty-id-from-body.interceptor.js +0 -40
- package/cjs/interceptors/delete-empty-id-from-body.interceptor.spec.js +0 -107
- package/cjs/interceptors/idempotency.interceptor.js +0 -96
- package/cjs/interceptors/idempotency.interceptor.spec.js +0 -116
- package/cjs/interceptors/index.js +0 -22
- package/cjs/interceptors/response-meta.interceptor.js +0 -47
- package/cjs/interceptors/response-meta.interceptor.spec.js +0 -89
- package/cjs/interceptors/set-user-field-on-body.interceptor.js +0 -60
- package/cjs/interceptors/set-user-field-on-body.interceptor.spec.js +0 -128
- package/cjs/interceptors/slug.interceptor.js +0 -75
- package/cjs/interceptors/slug.interceptor.spec.js +0 -110
- package/cjs/interfaces/ai-assistant-adapter.interface.js +0 -24
- package/cjs/interfaces/api.interface.js +0 -4
- package/cjs/interfaces/datasource.interface.js +0 -4
- package/cjs/interfaces/event-bus.interface.js +0 -4
- package/cjs/interfaces/event-manager-adapter.interface.js +0 -11
- package/cjs/interfaces/identity.interface.js +0 -4
- package/cjs/interfaces/index.js +0 -28
- package/cjs/interfaces/logged-user-info.interface.js +0 -6
- package/cjs/interfaces/module-config.interface.js +0 -4
- package/cjs/interfaces/notification-adapter.interface.js +0 -11
- package/cjs/interfaces/permission-sync-adapter.interface.js +0 -11
- package/cjs/interfaces/permission.interface.js +0 -4
- package/cjs/middlewares/index.js +0 -18
- package/cjs/middlewares/logger.middleware.js +0 -264
- package/cjs/middlewares/logger.middleware.spec.js +0 -462
- package/cjs/modules/cache/cache.module.js +0 -39
- package/cjs/modules/cache/cache.module.spec.js +0 -48
- package/cjs/modules/datasource/index.js +0 -18
- package/cjs/modules/datasource/multi-tenant-datasource.service.js +0 -222
- package/cjs/modules/datasource/multi-tenant-datasource.service.spec.js +0 -381
- package/cjs/modules/event-bus/event-bus.explorer.js +0 -124
- package/cjs/modules/event-bus/event-bus.explorer.spec.js +0 -189
- package/cjs/modules/event-bus/event-bus.integration.spec.js +0 -253
- package/cjs/modules/event-bus/event-bus.module.js +0 -94
- package/cjs/modules/event-bus/event-bus.module.spec.js +0 -149
- package/cjs/modules/event-bus/module-events.provider.js +0 -39
- package/cjs/modules/event-bus/module-events.provider.spec.js +0 -56
- package/cjs/modules/index.js +0 -26
- package/cjs/modules/shared-permission-cache/shared-permission-cache.module.js +0 -32
- package/cjs/modules/shared-permission-cache/shared-permission-cache.service.js +0 -101
- package/cjs/modules/shared-permission-cache/shared-permission-cache.service.spec.js +0 -165
- package/cjs/modules/utils/utils.module.js +0 -32
- package/cjs/modules/utils/utils.service.js +0 -76
- package/cjs/modules/utils/utils.service.spec.js +0 -169
- package/cjs/utils/date-time.util.js +0 -94
- package/cjs/utils/date-time.util.spec.js +0 -84
- package/cjs/utils/event-pattern.util.js +0 -103
- package/cjs/utils/event-pattern.util.spec.js +0 -251
- package/cjs/utils/html-sanitizer.util.js +0 -64
- package/cjs/utils/html-sanitizer.util.spec.js +0 -76
- package/cjs/utils/index.js +0 -25
- package/cjs/utils/optional-module.util.js +0 -35
- package/cjs/utils/optional-module.util.spec.js +0 -14
- package/cjs/utils/permission-cache-key.util.js +0 -31
- package/cjs/utils/permission-cache-key.util.spec.js +0 -88
- package/cjs/utils/query-helpers.util.js +0 -60
- package/cjs/utils/query-helpers.util.spec.js +0 -209
- package/cjs/utils/request.util.js +0 -96
- package/cjs/utils/request.util.spec.js +0 -249
- package/cjs/utils/string.util.js +0 -63
- package/cjs/utils/string.util.spec.js +0 -63
- package/fesm/classes/api-controller.class.js +0 -718
- package/fesm/classes/api-controller.class.spec.js +0 -680
- package/fesm/classes/api-service.class.js +0 -621
- package/fesm/classes/api-service.class.spec.js +0 -1133
- package/fesm/classes/disabled-event-bus.class.js +0 -11
- package/fesm/classes/disabled-event-bus.class.spec.js +0 -36
- package/fesm/classes/domain-event.publisher.js +0 -63
- package/fesm/classes/domain-event.publisher.spec.js +0 -230
- package/fesm/classes/event-bus.registry.js +0 -64
- package/fesm/classes/event-bus.registry.spec.js +0 -137
- package/fesm/classes/event-transports/event-transport.registry.js +0 -35
- package/fesm/classes/event-transports/event-transport.registry.spec.js +0 -27
- package/fesm/classes/event-transports/index.js +0 -6
- package/fesm/classes/event-transports/kafka-transport.optional.spec.js +0 -194
- package/fesm/classes/event-transports/memory-transport.js +0 -49
- package/fesm/classes/event-transports/memory-transport.spec.js +0 -53
- package/fesm/classes/event-transports/rabbitmq-transport.optional.js +0 -174
- package/fesm/classes/event-transports/rabbitmq-transport.optional.spec.js +0 -251
- package/fesm/classes/handle-custome-db.js +0 -19
- package/fesm/classes/handle-custome-db.spec.js +0 -78
- package/fesm/classes/hybrid-cache.class.js +0 -81
- package/fesm/classes/hybrid-cache.class.spec.js +0 -164
- package/fesm/classes/hybrid-event-bus.class.js +0 -237
- package/fesm/classes/hybrid-event-bus.class.spec.js +0 -214
- package/fesm/classes/winston.logger.class.js +0 -228
- package/fesm/classes/winston.logger.class.spec.js +0 -327
- package/fesm/constants/message-keys.js +0 -49
- package/fesm/constants/permissions.js +0 -208
- package/fesm/decorators/api-response.decorator.js +0 -42
- package/fesm/decorators/api-response.decorator.spec.js +0 -122
- package/fesm/decorators/current-user.decorator.js +0 -18
- package/fesm/decorators/current-user.decorator.spec.js +0 -73
- package/fesm/decorators/log-action.decorator.js +0 -139
- package/fesm/decorators/log-action.decorator.spec.js +0 -349
- package/fesm/decorators/on-domain-event.decorator.js +0 -10
- package/fesm/decorators/on-domain-event.decorator.spec.js +0 -46
- package/fesm/decorators/public.decorator.js +0 -20
- package/fesm/decorators/public.decorator.spec.js +0 -46
- package/fesm/decorators/require-permission.decorator.js +0 -27
- package/fesm/decorators/require-permission.decorator.spec.js +0 -143
- package/fesm/dtos/delete.dto.js +0 -82
- package/fesm/dtos/filter-and-pagination.dto.js +0 -148
- package/fesm/dtos/get-by-ids.dto.js +0 -55
- package/fesm/dtos/identity-response.dto.js +0 -98
- package/fesm/dtos/pagination.dto.js +0 -57
- package/fesm/dtos/response-payload.dto.js +0 -336
- package/fesm/entities/identity.js +0 -90
- package/fesm/entities/raw-type.js +0 -1
- package/fesm/entities/user-root.js +0 -179
- package/fesm/enums/notification-type.enum.js +0 -7
- package/fesm/enums/participant-status.enum.js +0 -7
- package/fesm/enums/recurrence-type.enum.js +0 -8
- package/fesm/exceptions/base-app.exception.js +0 -115
- package/fesm/exceptions/base-app.exception.spec.js +0 -153
- package/fesm/exceptions/permission.exception.js +0 -35
- package/fesm/exceptions/permission.exception.spec.js +0 -78
- package/fesm/filters/global-exception.filter.js +0 -269
- package/fesm/filters/global-exception.filter.spec.js +0 -436
- package/fesm/filters/index.js +0 -1
- package/fesm/guards/jwt-auth.guard.js +0 -65
- package/fesm/guards/jwt-auth.guard.spec.js +0 -71
- package/fesm/guards/optional-jwt-auth.guard.js +0 -57
- package/fesm/guards/optional-jwt-auth.guard.spec.js +0 -51
- package/fesm/guards/permission.guard.js +0 -169
- package/fesm/guards/permission.guard.spec.js +0 -369
- package/fesm/interceptors/delete-empty-id-from-body.interceptor.js +0 -30
- package/fesm/interceptors/delete-empty-id-from-body.interceptor.spec.js +0 -103
- package/fesm/interceptors/idempotency.interceptor.js +0 -86
- package/fesm/interceptors/idempotency.interceptor.spec.js +0 -112
- package/fesm/interceptors/response-meta.interceptor.js +0 -37
- package/fesm/interceptors/response-meta.interceptor.spec.js +0 -85
- package/fesm/interceptors/set-user-field-on-body.interceptor.js +0 -39
- package/fesm/interceptors/set-user-field-on-body.interceptor.spec.js +0 -124
- package/fesm/interceptors/slug.interceptor.js +0 -65
- package/fesm/interceptors/slug.interceptor.spec.js +0 -106
- package/fesm/interfaces/ai-assistant-adapter.interface.js +0 -3
- package/fesm/interfaces/api.interface.js +0 -1
- package/fesm/interfaces/datasource.interface.js +0 -1
- package/fesm/interfaces/event-bus.interface.js +0 -1
- package/fesm/interfaces/event-manager-adapter.interface.js +0 -1
- package/fesm/interfaces/identity.interface.js +0 -1
- package/fesm/interfaces/logged-user-info.interface.js +0 -3
- package/fesm/interfaces/module-config.interface.js +0 -5
- package/fesm/interfaces/notification-adapter.interface.js +0 -1
- package/fesm/interfaces/permission-sync-adapter.interface.js +0 -1
- package/fesm/interfaces/permission.interface.js +0 -1
- package/fesm/middlewares/logger.middleware.js +0 -235
- package/fesm/middlewares/logger.middleware.spec.js +0 -459
- package/fesm/modules/cache/cache.module.js +0 -29
- package/fesm/modules/cache/cache.module.spec.js +0 -44
- package/fesm/modules/datasource/index.js +0 -1
- package/fesm/modules/datasource/multi-tenant-datasource.service.js +0 -212
- package/fesm/modules/datasource/multi-tenant-datasource.service.spec.js +0 -377
- package/fesm/modules/event-bus/event-bus.explorer.js +0 -114
- package/fesm/modules/event-bus/event-bus.explorer.spec.js +0 -185
- package/fesm/modules/event-bus/event-bus.integration.spec.js +0 -249
- package/fesm/modules/event-bus/event-bus.module.js +0 -84
- package/fesm/modules/event-bus/event-bus.module.spec.js +0 -145
- package/fesm/modules/event-bus/module-events.provider.js +0 -26
- package/fesm/modules/event-bus/module-events.provider.spec.js +0 -52
- package/fesm/modules/shared-permission-cache/shared-permission-cache.module.js +0 -22
- package/fesm/modules/shared-permission-cache/shared-permission-cache.service.js +0 -91
- package/fesm/modules/shared-permission-cache/shared-permission-cache.service.spec.js +0 -161
- package/fesm/modules/utils/utils.module.js +0 -22
- package/fesm/modules/utils/utils.service.js +0 -66
- package/fesm/modules/utils/utils.service.spec.js +0 -165
- package/fesm/utils/date-time.util.js +0 -70
- package/fesm/utils/date-time.util.spec.js +0 -80
- package/fesm/utils/event-pattern.util.js +0 -91
- package/fesm/utils/event-pattern.util.spec.js +0 -247
- package/fesm/utils/html-sanitizer.util.js +0 -69
- package/fesm/utils/html-sanitizer.util.spec.js +0 -72
- package/fesm/utils/optional-module.util.js +0 -25
- package/fesm/utils/optional-module.util.spec.js +0 -10
- package/fesm/utils/permission-cache-key.util.js +0 -10
- package/fesm/utils/permission-cache-key.util.spec.js +0 -84
- package/fesm/utils/query-helpers.util.js +0 -85
- package/fesm/utils/query-helpers.util.spec.js +0 -205
- package/fesm/utils/request.util.js +0 -81
- package/fesm/utils/request.util.spec.js +0 -245
- package/fesm/utils/string.util.js +0 -71
- package/fesm/utils/string.util.spec.js +0 -59
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
const mockLoadOptionalModule = jest.fn();
|
|
2
|
-
jest.mock('../../utils/optional-module.util', ()=>({
|
|
3
|
-
loadOptionalModule: (...args)=>mockLoadOptionalModule(...args)
|
|
4
|
-
}));
|
|
5
|
-
import { InternalServerErrorException, Logger } from '@nestjs/common';
|
|
6
|
-
import { KafkaTransport } from './kafka-transport.optional';
|
|
7
|
-
function buildEvent() {
|
|
8
|
-
return {
|
|
9
|
-
id: 'event-1',
|
|
10
|
-
name: 'storage.folder.created',
|
|
11
|
-
module: 'storage',
|
|
12
|
-
entity: 'folder',
|
|
13
|
-
action: 'created',
|
|
14
|
-
payload: {
|
|
15
|
-
ids: [
|
|
16
|
-
'1'
|
|
17
|
-
]
|
|
18
|
-
},
|
|
19
|
-
actor: {},
|
|
20
|
-
occurredAt: '2026-01-01T00:00:00.000Z',
|
|
21
|
-
version: 1
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
function createKafkaMock() {
|
|
25
|
-
const producer = {
|
|
26
|
-
connect: jest.fn().mockResolvedValue(undefined),
|
|
27
|
-
send: jest.fn().mockResolvedValue(undefined),
|
|
28
|
-
disconnect: jest.fn().mockResolvedValue(undefined)
|
|
29
|
-
};
|
|
30
|
-
const consumer = {
|
|
31
|
-
connect: jest.fn().mockResolvedValue(undefined),
|
|
32
|
-
subscribe: jest.fn().mockResolvedValue(undefined),
|
|
33
|
-
run: jest.fn().mockResolvedValue(undefined),
|
|
34
|
-
disconnect: jest.fn().mockResolvedValue(undefined)
|
|
35
|
-
};
|
|
36
|
-
const Kafka = jest.fn().mockImplementation(()=>({
|
|
37
|
-
producer: ()=>producer,
|
|
38
|
-
consumer: ()=>consumer
|
|
39
|
-
}));
|
|
40
|
-
return {
|
|
41
|
-
producer,
|
|
42
|
-
consumer,
|
|
43
|
-
Kafka
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
describe('KafkaTransport', ()=>{
|
|
47
|
-
let kafka;
|
|
48
|
-
let errorSpy;
|
|
49
|
-
beforeEach(()=>{
|
|
50
|
-
kafka = createKafkaMock();
|
|
51
|
-
mockLoadOptionalModule.mockResolvedValue({
|
|
52
|
-
Kafka: kafka.Kafka
|
|
53
|
-
});
|
|
54
|
-
errorSpy = jest.spyOn(Logger.prototype, 'error').mockImplementation(()=>undefined);
|
|
55
|
-
});
|
|
56
|
-
afterEach(()=>{
|
|
57
|
-
errorSpy.mockRestore();
|
|
58
|
-
jest.clearAllMocks();
|
|
59
|
-
});
|
|
60
|
-
it('should throw a descriptive error when kafkajs is not installed', async ()=>{
|
|
61
|
-
mockLoadOptionalModule.mockRejectedValue(new Error('Cannot find module'));
|
|
62
|
-
await expect(new KafkaTransport().connect()).rejects.toThrow(InternalServerErrorException);
|
|
63
|
-
});
|
|
64
|
-
it('should connect the producer and leave the consumer alone until a bind', async ()=>{
|
|
65
|
-
const transport = new KafkaTransport({
|
|
66
|
-
topic: 'events',
|
|
67
|
-
groupId: 'svc'
|
|
68
|
-
});
|
|
69
|
-
await transport.connect();
|
|
70
|
-
expect(kafka.producer.connect).toHaveBeenCalled();
|
|
71
|
-
expect(kafka.consumer.connect).not.toHaveBeenCalled();
|
|
72
|
-
expect(transport.isReady()).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
it('should connect and subscribe the consumer on the first bind', async ()=>{
|
|
75
|
-
const transport = new KafkaTransport({
|
|
76
|
-
topic: 'events',
|
|
77
|
-
groupId: 'svc'
|
|
78
|
-
});
|
|
79
|
-
await transport.connect();
|
|
80
|
-
await transport.bind('storage.#');
|
|
81
|
-
expect(kafka.consumer.connect).toHaveBeenCalled();
|
|
82
|
-
expect(kafka.consumer.subscribe).toHaveBeenCalledWith({
|
|
83
|
-
topic: 'events',
|
|
84
|
-
fromBeginning: false
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
it('should pass the broker list and client id through to kafkajs', async ()=>{
|
|
88
|
-
await new KafkaTransport({
|
|
89
|
-
brokers: [
|
|
90
|
-
'b1:9092'
|
|
91
|
-
],
|
|
92
|
-
clientId: 'svc'
|
|
93
|
-
}).connect();
|
|
94
|
-
expect(kafka.Kafka).toHaveBeenCalledWith(expect.objectContaining({
|
|
95
|
-
brokers: [
|
|
96
|
-
'b1:9092'
|
|
97
|
-
],
|
|
98
|
-
clientId: 'svc'
|
|
99
|
-
}));
|
|
100
|
-
});
|
|
101
|
-
it('should send the event keyed by its name', async ()=>{
|
|
102
|
-
const transport = new KafkaTransport({
|
|
103
|
-
topic: 'events'
|
|
104
|
-
});
|
|
105
|
-
await transport.connect();
|
|
106
|
-
await transport.publish(buildEvent());
|
|
107
|
-
expect(kafka.producer.send).toHaveBeenCalledWith({
|
|
108
|
-
topic: 'events',
|
|
109
|
-
messages: [
|
|
110
|
-
expect.objectContaining({
|
|
111
|
-
key: 'storage.folder.created',
|
|
112
|
-
headers: expect.objectContaining({
|
|
113
|
-
eventId: 'event-1',
|
|
114
|
-
module: 'storage'
|
|
115
|
-
})
|
|
116
|
-
})
|
|
117
|
-
]
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
it('should ignore publish before connect instead of throwing', async ()=>{
|
|
121
|
-
await expect(new KafkaTransport().publish(buildEvent())).resolves.toBeUndefined();
|
|
122
|
-
});
|
|
123
|
-
it('should start the consumer only once across several binds', async ()=>{
|
|
124
|
-
const transport = new KafkaTransport();
|
|
125
|
-
await transport.connect();
|
|
126
|
-
await transport.bind('storage.#');
|
|
127
|
-
await transport.bind('auth.#');
|
|
128
|
-
expect(kafka.consumer.run).toHaveBeenCalledTimes(1);
|
|
129
|
-
});
|
|
130
|
-
it('should hand a consumed message to its handlers', async ()=>{
|
|
131
|
-
const transport = new KafkaTransport();
|
|
132
|
-
const handler = jest.fn();
|
|
133
|
-
transport.onMessage(handler);
|
|
134
|
-
await transport.connect();
|
|
135
|
-
await transport.bind('storage.#');
|
|
136
|
-
const event = buildEvent();
|
|
137
|
-
await kafka.consumer.run.mock.calls[0][0].eachMessage({
|
|
138
|
-
message: {
|
|
139
|
-
value: Buffer.from(JSON.stringify(event))
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
expect(handler).toHaveBeenCalledWith(event);
|
|
143
|
-
});
|
|
144
|
-
it('should swallow a message it cannot parse', async ()=>{
|
|
145
|
-
const transport = new KafkaTransport();
|
|
146
|
-
await transport.connect();
|
|
147
|
-
await transport.bind('storage.#');
|
|
148
|
-
await expect(kafka.consumer.run.mock.calls[0][0].eachMessage({
|
|
149
|
-
message: {
|
|
150
|
-
value: Buffer.from('not json')
|
|
151
|
-
}
|
|
152
|
-
})).resolves.toBeUndefined();
|
|
153
|
-
});
|
|
154
|
-
it('should disconnect both clients on close', async ()=>{
|
|
155
|
-
const transport = new KafkaTransport();
|
|
156
|
-
await transport.connect();
|
|
157
|
-
await transport.bind('storage.#');
|
|
158
|
-
await transport.close();
|
|
159
|
-
expect(kafka.producer.disconnect).toHaveBeenCalled();
|
|
160
|
-
expect(kafka.consumer.disconnect).toHaveBeenCalled();
|
|
161
|
-
expect(transport.isReady()).toBe(false);
|
|
162
|
-
});
|
|
163
|
-
it('should keep publishing when the consumer cannot subscribe', async ()=>{
|
|
164
|
-
kafka.consumer.subscribe.mockRejectedValueOnce(new Error('UNKNOWN_TOPIC_OR_PARTITION'));
|
|
165
|
-
const transport = new KafkaTransport();
|
|
166
|
-
await transport.connect();
|
|
167
|
-
await expect(transport.bind('storage.#')).rejects.toThrow('UNKNOWN_TOPIC_OR_PARTITION');
|
|
168
|
-
expect(transport.isReady()).toBe(true);
|
|
169
|
-
await transport.publish(buildEvent());
|
|
170
|
-
expect(kafka.producer.send).toHaveBeenCalled();
|
|
171
|
-
});
|
|
172
|
-
it('should let a later bind retry after the consumer failed to start', async ()=>{
|
|
173
|
-
kafka.consumer.subscribe.mockRejectedValueOnce(new Error('COORDINATOR_NOT_AVAILABLE'));
|
|
174
|
-
const transport = new KafkaTransport();
|
|
175
|
-
await transport.connect();
|
|
176
|
-
await expect(transport.bind('storage.#')).rejects.toThrow('COORDINATOR_NOT_AVAILABLE');
|
|
177
|
-
await transport.bind('auth.#');
|
|
178
|
-
expect(kafka.consumer.run).toHaveBeenCalledTimes(1);
|
|
179
|
-
});
|
|
180
|
-
it('should skip a message that carries no value', async ()=>{
|
|
181
|
-
const transport = new KafkaTransport();
|
|
182
|
-
const handler = jest.fn();
|
|
183
|
-
transport.onMessage(handler);
|
|
184
|
-
await transport.connect();
|
|
185
|
-
await transport.bind('storage.#');
|
|
186
|
-
await kafka.consumer.run.mock.calls[0][0].eachMessage({
|
|
187
|
-
message: {
|
|
188
|
-
value: null
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
expect(handler).not.toHaveBeenCalled();
|
|
192
|
-
expect(errorSpy).not.toHaveBeenCalled();
|
|
193
|
-
});
|
|
194
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
import { EventEmitter } from 'events';
|
|
15
|
-
const CHANNEL = 'domain-event';
|
|
16
|
-
const MAX_LISTENERS = 100;
|
|
17
|
-
/**
|
|
18
|
-
* In-process transport. Always available, needs no broker, and is the default.
|
|
19
|
-
*
|
|
20
|
-
* HybridEventBus constructs it directly rather than through
|
|
21
|
-
* EventTransportRegistry, which exists for the broker transports it must not
|
|
22
|
-
* depend on at build time.
|
|
23
|
-
*/ export class MemoryTransport {
|
|
24
|
-
async connect() {
|
|
25
|
-
this.ready = true;
|
|
26
|
-
}
|
|
27
|
-
async publish(event) {
|
|
28
|
-
this.emitter.emit(CHANNEL, event);
|
|
29
|
-
}
|
|
30
|
-
async bind(_pattern) {}
|
|
31
|
-
onMessage(handler) {
|
|
32
|
-
this.emitter.on(CHANNEL, (event)=>{
|
|
33
|
-
void handler(event);
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
async close() {
|
|
37
|
-
this.emitter.removeAllListeners();
|
|
38
|
-
this.ready = false;
|
|
39
|
-
}
|
|
40
|
-
isReady() {
|
|
41
|
-
return this.ready;
|
|
42
|
-
}
|
|
43
|
-
constructor(){
|
|
44
|
-
_define_property(this, "name", 'memory');
|
|
45
|
-
_define_property(this, "emitter", new EventEmitter());
|
|
46
|
-
_define_property(this, "ready", false);
|
|
47
|
-
this.emitter.setMaxListeners(MAX_LISTENERS);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { MemoryTransport } from './memory-transport';
|
|
2
|
-
function buildEvent(name = 'storage.folder.created') {
|
|
3
|
-
return {
|
|
4
|
-
id: 'event-1',
|
|
5
|
-
name,
|
|
6
|
-
module: 'storage',
|
|
7
|
-
entity: 'folder',
|
|
8
|
-
action: 'created',
|
|
9
|
-
payload: {
|
|
10
|
-
ids: [
|
|
11
|
-
'1'
|
|
12
|
-
]
|
|
13
|
-
},
|
|
14
|
-
actor: {},
|
|
15
|
-
occurredAt: new Date().toISOString(),
|
|
16
|
-
version: 1
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
describe('MemoryTransport', ()=>{
|
|
20
|
-
let transport;
|
|
21
|
-
beforeEach(()=>{
|
|
22
|
-
transport = new MemoryTransport();
|
|
23
|
-
});
|
|
24
|
-
afterEach(async ()=>{
|
|
25
|
-
await transport.close();
|
|
26
|
-
});
|
|
27
|
-
it('should report ready only after connect', async ()=>{
|
|
28
|
-
expect(transport.isReady()).toBe(false);
|
|
29
|
-
await transport.connect();
|
|
30
|
-
expect(transport.isReady()).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
it('should deliver a published event to every registered handler', async ()=>{
|
|
33
|
-
const first = jest.fn();
|
|
34
|
-
const second = jest.fn();
|
|
35
|
-
transport.onMessage(first);
|
|
36
|
-
transport.onMessage(second);
|
|
37
|
-
const event = buildEvent();
|
|
38
|
-
await transport.publish(event);
|
|
39
|
-
expect(first).toHaveBeenCalledWith(event);
|
|
40
|
-
expect(second).toHaveBeenCalledWith(event);
|
|
41
|
-
});
|
|
42
|
-
it('should not deliver anything once closed', async ()=>{
|
|
43
|
-
const handler = jest.fn();
|
|
44
|
-
transport.onMessage(handler);
|
|
45
|
-
await transport.close();
|
|
46
|
-
await transport.publish(buildEvent());
|
|
47
|
-
expect(handler).not.toHaveBeenCalled();
|
|
48
|
-
expect(transport.isReady()).toBe(false);
|
|
49
|
-
});
|
|
50
|
-
it('should accept bind calls without a broker', async ()=>{
|
|
51
|
-
await expect(transport.bind('storage.#')).resolves.toBeUndefined();
|
|
52
|
-
});
|
|
53
|
-
});
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Optional RabbitMQ event transport
|
|
16
|
-
* Requires: npm install amqplib
|
|
17
|
-
*/ import { InternalServerErrorException, Logger } from '@nestjs/common';
|
|
18
|
-
import { DEFAULT_EVENT_EXCHANGE, DEFAULT_EVENT_SERVICE_NAME, SYSTEM_MESSAGES } from '../../constants';
|
|
19
|
-
import { isMultiWildcard } from '../../utils/event-pattern.util';
|
|
20
|
-
import { loadOptionalModule } from '../../utils/optional-module.util';
|
|
21
|
-
import { EventTransportRegistry } from './event-transport.registry';
|
|
22
|
-
const DEFAULT_URL = 'amqp://localhost:5672';
|
|
23
|
-
const DEFAULT_RECONNECT_DELAY = 5000;
|
|
24
|
-
export class RabbitMqTransport {
|
|
25
|
-
async connect() {
|
|
26
|
-
let amqp;
|
|
27
|
-
try {
|
|
28
|
-
amqp = await loadOptionalModule('amqplib');
|
|
29
|
-
} catch {
|
|
30
|
-
throw new InternalServerErrorException({
|
|
31
|
-
message: 'amqplib not found',
|
|
32
|
-
messageKey: SYSTEM_MESSAGES.SDK_NOT_INSTALLED,
|
|
33
|
-
messageVariables: {
|
|
34
|
-
sdk: 'amqplib'
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
this.connection = await (amqp.connect ?? amqp.default.connect)(this.config.url ?? DEFAULT_URL);
|
|
39
|
-
this.channel = await this.connection.createChannel();
|
|
40
|
-
await this.channel.assertExchange(this.exchange, 'topic', {
|
|
41
|
-
durable: this.durable
|
|
42
|
-
});
|
|
43
|
-
await this.channel.assertQueue(this.queue, {
|
|
44
|
-
durable: this.durable
|
|
45
|
-
});
|
|
46
|
-
await this.channel.prefetch(this.config.prefetch ?? 10);
|
|
47
|
-
// A channel dies on its own for any protocol error - a mismatched assert, a
|
|
48
|
-
// deleted exchange, an unknown delivery tag - while the connection stays up.
|
|
49
|
-
// Watching only the connection would leave the transport holding a dead
|
|
50
|
-
// channel: publishing throws into the void and nothing ever reconnects.
|
|
51
|
-
this.connection.on('close', ()=>this.scheduleReconnect());
|
|
52
|
-
this.connection.on('error', (error)=>this.logger.warn(error.message));
|
|
53
|
-
this.channel.on('close', ()=>this.scheduleReconnect());
|
|
54
|
-
this.channel.on('error', (error)=>this.logger.warn(error.message));
|
|
55
|
-
for (const pattern of this.boundPatterns){
|
|
56
|
-
await this.channel.bindQueue(this.queue, this.exchange, this.toRoutingKey(pattern));
|
|
57
|
-
}
|
|
58
|
-
if (this.boundPatterns.size) await this.startConsuming();
|
|
59
|
-
}
|
|
60
|
-
async publish(event) {
|
|
61
|
-
if (!this.channel) return;
|
|
62
|
-
this.channel.publish(this.exchange, event.name, Buffer.from(JSON.stringify(event)), {
|
|
63
|
-
persistent: this.durable,
|
|
64
|
-
messageId: event.id,
|
|
65
|
-
appId: this.config.queue ?? DEFAULT_EVENT_SERVICE_NAME,
|
|
66
|
-
contentType: 'application/json'
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
async bind(pattern) {
|
|
70
|
-
if (this.boundPatterns.has(pattern)) return;
|
|
71
|
-
this.boundPatterns.add(pattern);
|
|
72
|
-
if (!this.channel) return;
|
|
73
|
-
await this.channel.bindQueue(this.queue, this.exchange, this.toRoutingKey(pattern));
|
|
74
|
-
await this.startConsuming();
|
|
75
|
-
}
|
|
76
|
-
onMessage(handler) {
|
|
77
|
-
this.handlers.push(handler);
|
|
78
|
-
}
|
|
79
|
-
async close() {
|
|
80
|
-
this.closed = true;
|
|
81
|
-
if (this.reconnectTimer) {
|
|
82
|
-
clearTimeout(this.reconnectTimer);
|
|
83
|
-
this.reconnectTimer = undefined;
|
|
84
|
-
}
|
|
85
|
-
await this.teardown();
|
|
86
|
-
}
|
|
87
|
-
isReady() {
|
|
88
|
-
return Boolean(this.channel);
|
|
89
|
-
}
|
|
90
|
-
get exchange() {
|
|
91
|
-
return this.config.exchange ?? DEFAULT_EVENT_EXCHANGE;
|
|
92
|
-
}
|
|
93
|
-
get queue() {
|
|
94
|
-
return this.config.queue ?? `${DEFAULT_EVENT_SERVICE_NAME}.events`;
|
|
95
|
-
}
|
|
96
|
-
get durable() {
|
|
97
|
-
return this.config.durable ?? true;
|
|
98
|
-
}
|
|
99
|
-
/** `**` is the FLUSYS alias for the AMQP multi word wildcard */ toRoutingKey(pattern) {
|
|
100
|
-
return pattern.split('.').map((segment)=>isMultiWildcard(segment) ? '#' : segment).join('.');
|
|
101
|
-
}
|
|
102
|
-
async startConsuming() {
|
|
103
|
-
// Captured, because a reconnect swaps `this.channel` while deliveries are in
|
|
104
|
-
// flight - a delivery tag means nothing on the new channel, and acking it
|
|
105
|
-
// there is a protocol error that kills that channel too.
|
|
106
|
-
const channel = this.channel;
|
|
107
|
-
if (this.consuming || !channel) return;
|
|
108
|
-
this.consuming = true;
|
|
109
|
-
try {
|
|
110
|
-
await channel.consume(this.queue, async (message)=>{
|
|
111
|
-
if (!message) return;
|
|
112
|
-
try {
|
|
113
|
-
const event = JSON.parse(message.content.toString());
|
|
114
|
-
await Promise.all(this.handlers.map((handler)=>handler(event)));
|
|
115
|
-
channel.ack(message);
|
|
116
|
-
} catch (error) {
|
|
117
|
-
this.logger.error(error instanceof Error ? error.message : 'Failed to handle event');
|
|
118
|
-
channel.nack(message, false, false);
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
} catch (error) {
|
|
122
|
-
// A latch left set would keep the queue unconsumed for the process lifetime
|
|
123
|
-
this.consuming = false;
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
/** A dropped connection reports itself twice - once per channel, once per connection */ scheduleReconnect() {
|
|
128
|
-
if (this.closed || this.reconnectTimer) return;
|
|
129
|
-
// Not awaited: the fields are cleared before its first await, so publishing
|
|
130
|
-
// stops using the dead channel now rather than after the sockets close.
|
|
131
|
-
void this.teardown();
|
|
132
|
-
this.reconnectTimer = setTimeout(()=>{
|
|
133
|
-
this.reconnectTimer = undefined;
|
|
134
|
-
this.connect().catch((error)=>{
|
|
135
|
-
this.logger.warn(`RabbitMQ reconnect failed: ${error.message}`);
|
|
136
|
-
this.scheduleReconnect();
|
|
137
|
-
});
|
|
138
|
-
}, this.config.reconnectDelay ?? DEFAULT_RECONNECT_DELAY);
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Drops the current channel and connection. The connection is closed even when
|
|
142
|
-
* it was only the channel that died, otherwise every channel level failure
|
|
143
|
-
* would leave one more live connection behind on the broker.
|
|
144
|
-
*/ async teardown() {
|
|
145
|
-
const { channel, connection } = this;
|
|
146
|
-
this.channel = undefined;
|
|
147
|
-
this.connection = undefined;
|
|
148
|
-
this.consuming = false;
|
|
149
|
-
channel?.removeAllListeners?.();
|
|
150
|
-
connection?.removeAllListeners?.();
|
|
151
|
-
await channel?.close().catch(()=>{});
|
|
152
|
-
await connection?.close().catch(()=>{});
|
|
153
|
-
}
|
|
154
|
-
constructor(config = {}){
|
|
155
|
-
_define_property(this, "config", void 0);
|
|
156
|
-
_define_property(this, "name", void 0);
|
|
157
|
-
_define_property(this, "logger", void 0);
|
|
158
|
-
_define_property(this, "handlers", void 0);
|
|
159
|
-
_define_property(this, "boundPatterns", void 0);
|
|
160
|
-
_define_property(this, "connection", void 0);
|
|
161
|
-
_define_property(this, "channel", void 0);
|
|
162
|
-
_define_property(this, "consuming", void 0);
|
|
163
|
-
_define_property(this, "closed", void 0);
|
|
164
|
-
_define_property(this, "reconnectTimer", void 0);
|
|
165
|
-
this.config = config;
|
|
166
|
-
this.name = 'rabbitmq';
|
|
167
|
-
this.logger = new Logger(RabbitMqTransport.name);
|
|
168
|
-
this.handlers = [];
|
|
169
|
-
this.boundPatterns = new Set();
|
|
170
|
-
this.consuming = false;
|
|
171
|
-
this.closed = false;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
EventTransportRegistry.register('rabbitmq', (config)=>new RabbitMqTransport(config));
|