nodejs-quickstart-structure 1.18.1 → 1.19.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/CHANGELOG.md +309 -294
- package/LICENSE +15 -15
- package/README.md +2 -1
- package/lib/generator.js +139 -139
- package/lib/modules/app-setup.js +401 -401
- package/lib/modules/caching-setup.js +76 -73
- package/lib/modules/config-files.js +151 -151
- package/lib/modules/database-setup.js +116 -116
- package/lib/modules/kafka-setup.js +249 -191
- package/lib/modules/project-setup.js +32 -31
- package/lib/prompts.js +100 -100
- package/package.json +78 -67
- package/templates/clean-architecture/js/src/domain/models/User.js +9 -9
- package/templates/clean-architecture/js/src/errors/ApiError.js +14 -14
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -10
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -21
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -10
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -21
- package/templates/clean-architecture/js/src/index.js.ejs +55 -55
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -47
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -36
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -63
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +69 -39
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -81
- package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -30
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +89 -89
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +156 -75
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -138
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -13
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -31
- package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -5
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -21
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -49
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -17
- package/templates/clean-architecture/js/src/interfaces/routes/api.js +12 -10
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -38
- package/templates/clean-architecture/js/src/usecases/CreateUser.js +14 -14
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -51
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -12
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -61
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
- package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -9
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -46
- package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -6
- package/templates/clean-architecture/ts/src/domain/user.ts +7 -7
- package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -15
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -8
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -21
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -8
- package/templates/clean-architecture/ts/src/index.ts.ejs +139 -139
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -63
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -36
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -85
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +74 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -185
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +173 -84
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -32
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -17
- package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -3
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -4
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -21
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -4
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -15
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -40
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +13 -11
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -51
- package/templates/clean-architecture/ts/src/usecases/createUser.ts +13 -13
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -63
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -10
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts +9 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
- package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -27
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -7
- package/templates/common/.cursorrules.ejs +60 -60
- package/templates/common/.dockerignore +12 -12
- package/templates/common/.env.example.ejs +41 -41
- package/templates/common/.gitattributes +46 -0
- package/templates/common/.gitlab-ci.yml.ejs +86 -86
- package/templates/common/.lintstagedrc +6 -6
- package/templates/common/.prettierrc +7 -7
- package/templates/common/Dockerfile +73 -73
- package/templates/common/Jenkinsfile.ejs +87 -87
- package/templates/common/README.md.ejs +294 -270
- package/templates/common/SECURITY.md +20 -20
- package/templates/common/_github/workflows/ci.yml.ejs +46 -46
- package/templates/common/_github/workflows/security.yml.ejs +36 -36
- package/templates/common/_gitignore +5 -5
- package/templates/common/_husky/pre-commit +4 -4
- package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -29
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
- package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -37
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
- package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -27
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
- package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -34
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
- package/templates/common/caching/js/memoryCache.js.ejs +60 -60
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -101
- package/templates/common/caching/js/redisClient.js.ejs +75 -75
- package/templates/common/caching/js/redisClient.spec.js.ejs +147 -147
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -102
- package/templates/common/caching/ts/memoryCache.ts.ejs +73 -64
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -157
- package/templates/common/caching/ts/redisClient.ts.ejs +89 -80
- package/templates/common/database/js/database.js.ejs +19 -19
- package/templates/common/database/js/database.spec.js.ejs +56 -56
- package/templates/common/database/js/models/User.js.ejs +79 -53
- package/templates/common/database/js/models/User.js.mongoose.ejs +23 -19
- package/templates/common/database/js/models/User.spec.js.ejs +94 -84
- package/templates/common/database/js/mongoose.js.ejs +33 -33
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -43
- package/templates/common/database/ts/database.spec.ts.ejs +56 -56
- package/templates/common/database/ts/database.ts.ejs +21 -21
- package/templates/common/database/ts/models/User.spec.ts.ejs +100 -84
- package/templates/common/database/ts/models/User.ts.ejs +87 -61
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +30 -25
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -42
- package/templates/common/database/ts/mongoose.ts.ejs +28 -28
- package/templates/common/docker-compose.yml.ejs +159 -159
- package/templates/common/ecosystem.config.js.ejs +40 -40
- package/templates/common/eslint.config.mjs.ejs +77 -77
- package/templates/common/health/js/healthRoute.js.ejs +50 -47
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -70
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -76
- package/templates/common/health/ts/healthRoute.ts.ejs +49 -46
- package/templates/common/jest.config.js.ejs +32 -32
- package/templates/common/jest.e2e.config.js.ejs +8 -8
- package/templates/common/kafka/js/config/kafka.js +9 -9
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -27
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -30
- package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -58
- package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -11
- package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -27
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -31
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -49
- package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -93
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -106
- package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -27
- package/templates/common/kafka/ts/config/kafka.ts +7 -7
- package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -50
- package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -27
- package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -51
- package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -11
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -49
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -25
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -81
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -95
- package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
- package/templates/common/migrate-mongo-config.js.ejs +31 -31
- package/templates/common/migrations/init.js.ejs +23 -23
- package/templates/common/package.json.ejs +119 -118
- package/templates/common/prompts/add-feature.md.ejs +26 -26
- package/templates/common/prompts/project-context.md.ejs +43 -43
- package/templates/common/prompts/troubleshoot.md.ejs +28 -28
- package/templates/common/public/css/style.css +147 -147
- package/templates/common/scripts/run-e2e.js.ejs +63 -63
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -61
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -160
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -158
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -55
- package/templates/common/sonar-project.properties.ejs +27 -27
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -49
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -49
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -79
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -94
- package/templates/common/swagger.yml.ejs +118 -66
- package/templates/common/tsconfig.json +22 -22
- package/templates/common/views/ejs/index.ejs +55 -55
- package/templates/common/views/pug/index.pug +40 -40
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -9
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -9
- package/templates/mvc/js/src/config/env.js.ejs +46 -46
- package/templates/mvc/js/src/config/swagger.js.ejs +6 -6
- package/templates/mvc/js/src/controllers/userController.js.ejs +246 -105
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -209
- package/templates/mvc/js/src/errors/ApiError.js +14 -14
- package/templates/mvc/js/src/errors/BadRequestError.js +11 -10
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -21
- package/templates/mvc/js/src/errors/NotFoundError.js +11 -10
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -21
- package/templates/mvc/js/src/graphql/context.js.ejs +7 -7
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -29
- package/templates/mvc/js/src/graphql/index.js.ejs +5 -5
- package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -6
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -19
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -47
- package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -6
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -17
- package/templates/mvc/js/src/index.js.ejs +136 -136
- package/templates/mvc/js/src/routes/api.js +10 -8
- package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -36
- package/templates/mvc/js/src/utils/errorMessages.js +14 -0
- package/templates/mvc/js/src/utils/errorMiddleware.js +29 -29
- package/templates/mvc/js/src/utils/httpCodes.js +9 -9
- package/templates/mvc/js/src/utils/logger.js +40 -40
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -63
- package/templates/mvc/ts/src/config/env.ts.ejs +45 -45
- package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -6
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -203
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +248 -107
- package/templates/mvc/ts/src/errors/ApiError.ts +15 -15
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
- package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -8
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -21
- package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -8
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -30
- package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -12
- package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -3
- package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -4
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -21
- package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -4
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -15
- package/templates/mvc/ts/src/index.ts.ejs +156 -153
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -40
- package/templates/mvc/ts/src/routes/api.ts +12 -10
- package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
- package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -27
- package/templates/mvc/ts/src/utils/httpCodes.ts +7 -7
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -63
- package/templates/mvc/ts/src/utils/logger.ts +36 -36
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +0 -37
|
@@ -1,49 +1,86 @@
|
|
|
1
|
-
import { WelcomeEmailConsumer } from '<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>@/messaging/consumers/instances/welcomeEmailConsumer<% } %>';
|
|
2
|
-
import logger from '<%= loggerPath %>';
|
|
3
|
-
|
|
4
|
-
jest.mock('<%= loggerPath %>');
|
|
5
|
-
|
|
6
|
-
describe('WelcomeEmailConsumer', () => {
|
|
7
|
-
let consumer: WelcomeEmailConsumer;
|
|
8
|
-
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
jest.clearAllMocks();
|
|
11
|
-
consumer = new WelcomeEmailConsumer();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('should log welcome email simulation for USER_CREATED action', async () => {
|
|
15
|
-
const data = {
|
|
16
|
-
action: 'USER_CREATED',
|
|
17
|
-
payload: {
|
|
18
|
-
id: 1,
|
|
19
|
-
email: 'test@example.com'
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
await consumer.handle(data);
|
|
24
|
-
|
|
25
|
-
expect(logger.info).toHaveBeenCalledWith(
|
|
26
|
-
expect.stringContaining('[Kafka] Consumer: Received USER_CREATED.')
|
|
27
|
-
);
|
|
28
|
-
expect(logger.info).toHaveBeenCalledWith(
|
|
29
|
-
expect.stringContaining('📧 Sending welcome email to \'test@example.com\'... Done!')
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should log
|
|
34
|
-
const data = {
|
|
35
|
-
action: '
|
|
36
|
-
payload: {
|
|
37
|
-
id: 1,
|
|
38
|
-
email: '
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
await consumer.handle(data);
|
|
43
|
-
|
|
44
|
-
expect(logger.
|
|
45
|
-
expect.stringContaining('[Kafka]
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
import { WelcomeEmailConsumer } from '<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>@/messaging/consumers/instances/welcomeEmailConsumer<% } %>';
|
|
2
|
+
import logger from '<%= loggerPath %>';
|
|
3
|
+
|
|
4
|
+
jest.mock('<%= loggerPath %>');
|
|
5
|
+
|
|
6
|
+
describe('WelcomeEmailConsumer', () => {
|
|
7
|
+
let consumer: WelcomeEmailConsumer;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
consumer = new WelcomeEmailConsumer();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should log welcome email simulation for USER_CREATED action', async () => {
|
|
15
|
+
const data = {
|
|
16
|
+
action: 'USER_CREATED',
|
|
17
|
+
payload: {
|
|
18
|
+
id: 1,
|
|
19
|
+
email: 'test@example.com'
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
await consumer.handle(data);
|
|
24
|
+
|
|
25
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
26
|
+
expect.stringContaining('[Kafka] Consumer: Received USER_CREATED.')
|
|
27
|
+
);
|
|
28
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
29
|
+
expect.stringContaining('📧 Sending welcome email to \'test@example.com\'... Done!')
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should log update simulation for USER_UPDATED action', async () => {
|
|
34
|
+
const data = {
|
|
35
|
+
action: 'USER_UPDATED',
|
|
36
|
+
payload: {
|
|
37
|
+
id: 1,
|
|
38
|
+
email: 'updated@example.com'
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
await consumer.handle(data);
|
|
43
|
+
|
|
44
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
45
|
+
expect.stringContaining('[Kafka] Consumer: Received USER_UPDATED.')
|
|
46
|
+
);
|
|
47
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
48
|
+
expect.stringContaining('🔄 Updating user records for \'1\' (Email: updated@example.com)... Done!')
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should log deletion simulation for USER_DELETED action', async () => {
|
|
53
|
+
const data = {
|
|
54
|
+
action: 'USER_DELETED',
|
|
55
|
+
payload: {
|
|
56
|
+
id: 1
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
await consumer.handle(data);
|
|
61
|
+
|
|
62
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
63
|
+
expect.stringContaining('[Kafka] Consumer: Received USER_DELETED.')
|
|
64
|
+
);
|
|
65
|
+
expect(logger.info).toHaveBeenCalledWith(
|
|
66
|
+
expect.stringContaining('🗑️ Cleaning up data for user \'1\'... Done!')
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should log error for invalid data', async () => {
|
|
71
|
+
const data = {
|
|
72
|
+
action: 'USER_CREATED',
|
|
73
|
+
payload: {
|
|
74
|
+
id: 1,
|
|
75
|
+
email: 'invalid-email'
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
await consumer.handle(data);
|
|
80
|
+
|
|
81
|
+
expect(logger.error).toHaveBeenCalledWith(
|
|
82
|
+
expect.stringContaining('[Kafka] Invalid user event data:'),
|
|
83
|
+
expect.anything()
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -1,25 +1,38 @@
|
|
|
1
|
-
import { BaseConsumer } from '<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/baseConsumer<% } else { %>@/messaging/baseConsumer<% } %>';
|
|
2
|
-
import logger from '<%= loggerPath %>';
|
|
3
|
-
import { UserEventSchema } from '<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/schemas/userEventSchema<% } else { %>@/messaging/schemas/userEventSchema<% } %>';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
import { BaseConsumer } from '<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/baseConsumer<% } else { %>@/messaging/baseConsumer<% } %>';
|
|
2
|
+
import logger from '<%= loggerPath %>';
|
|
3
|
+
import { UserEventSchema } from '<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/schemas/userEventSchema<% } else { %>@/messaging/schemas/userEventSchema<% } %>';
|
|
4
|
+
import { ERROR_MESSAGES } from '@/utils/errorMessages';
|
|
5
|
+
import { KAFKA_ACTIONS } from '@/utils/kafkaEvents';
|
|
6
|
+
|
|
7
|
+
export class WelcomeEmailConsumer extends BaseConsumer {
|
|
8
|
+
topic = 'user-topic';
|
|
9
|
+
groupId = 'welcome-email-group';
|
|
10
|
+
|
|
11
|
+
async handle(data: unknown) {
|
|
12
|
+
const result = UserEventSchema.safeParse(data);
|
|
13
|
+
|
|
14
|
+
if (!result.success) {
|
|
15
|
+
logger.error(`[Kafka] ${ERROR_MESSAGES.INVALID_USER_DATA}:`, result.error.format());
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { action, payload } = result.data;
|
|
20
|
+
|
|
21
|
+
switch (action) {
|
|
22
|
+
case KAFKA_ACTIONS.USER_CREATED:
|
|
23
|
+
logger.info(`[Kafka] Consumer: Received ${KAFKA_ACTIONS.USER_CREATED}.`);
|
|
24
|
+
logger.info(`[Kafka] Consumer: 📧 Sending welcome email to '${payload.email}'... Done!`);
|
|
25
|
+
break;
|
|
26
|
+
case KAFKA_ACTIONS.USER_UPDATED:
|
|
27
|
+
logger.info(`[Kafka] Consumer: Received ${KAFKA_ACTIONS.USER_UPDATED}.`);
|
|
28
|
+
logger.info(`[Kafka] Consumer: 🔄 Updating user records for '${payload.id}' (Email: ${payload.email})... Done!`);
|
|
29
|
+
break;
|
|
30
|
+
case KAFKA_ACTIONS.USER_DELETED:
|
|
31
|
+
logger.info(`[Kafka] Consumer: Received ${KAFKA_ACTIONS.USER_DELETED}.`);
|
|
32
|
+
logger.info(`[Kafka] Consumer: 🗑️ Cleaning up data for user '${payload.id}'... Done!`);
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
logger.warn(`[Kafka] Unknown action: ${action}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
import { KafkaService } from '<% if (architecture === "Clean Architecture") { %>@/infrastructure/messaging/kafkaClient<% } else { %>@/services/kafkaService<% } %>';
|
|
2
|
-
import { kafka } from '<%= configPath %>';
|
|
3
|
-
|
|
4
|
-
jest.mock('<%= configPath %>', () => ({
|
|
5
|
-
kafka: {
|
|
6
|
-
producer: jest.fn().mockReturnValue({
|
|
7
|
-
connect: jest.fn().mockResolvedValue(undefined),
|
|
8
|
-
send: jest.fn().mockResolvedValue(undefined),
|
|
9
|
-
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
10
|
-
}),
|
|
11
|
-
consumer: jest.fn().mockReturnValue({
|
|
12
|
-
connect: jest.fn().mockResolvedValue(undefined),
|
|
13
|
-
subscribe: jest.fn().mockResolvedValue(undefined),
|
|
14
|
-
run: jest.fn().mockResolvedValue(undefined),
|
|
15
|
-
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
16
|
-
}),
|
|
17
|
-
},
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
jest.mock('<%= loggerPath %>');
|
|
21
|
-
|
|
22
|
-
describe('KafkaService', () => {
|
|
23
|
-
let kafkaService: KafkaService;
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
jest.clearAllMocks();
|
|
27
|
-
kafkaService = new KafkaService();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should connect producer and consumer', async () => {
|
|
31
|
-
await kafkaService.connect();
|
|
32
|
-
const producer = (kafka.producer as jest.Mock).mock.results[0].value;
|
|
33
|
-
const consumer = (kafka.consumer as jest.Mock).mock.results[0].value;
|
|
34
|
-
|
|
35
|
-
expect(producer.connect).toHaveBeenCalled();
|
|
36
|
-
expect(consumer.connect).toHaveBeenCalled();
|
|
37
|
-
expect(consumer.subscribe).toHaveBeenCalledWith(expect.objectContaining({ topic: 'user-topic', fromBeginning: true }));
|
|
38
|
-
expect(consumer.run).toHaveBeenCalled();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should send a message', async () => {
|
|
42
|
-
await kafkaService.connect();
|
|
43
|
-
const topic = 'test-topic';
|
|
44
|
-
const message = JSON.stringify({ action: 'TEST', payload: { email: 'test@example.com' } });
|
|
45
|
-
await kafkaService.sendMessage(topic, message);
|
|
46
|
-
const producer = (kafka.producer as jest.Mock).mock.results[0].value;
|
|
47
|
-
|
|
48
|
-
expect(producer.send).toHaveBeenCalledWith({
|
|
49
|
-
topic,
|
|
50
|
-
messages: [{ value: message }],
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should retry connection on failure', async () => {
|
|
55
|
-
const producer = (kafka.producer as jest.Mock).mock.results[0].value;
|
|
56
|
-
producer.connect
|
|
57
|
-
.mockRejectedValueOnce(new Error('Connection failed'))
|
|
58
|
-
.mockResolvedValueOnce(undefined);
|
|
59
|
-
|
|
60
|
-
jest.useFakeTimers();
|
|
61
|
-
const connectPromise = kafkaService.connect(2);
|
|
62
|
-
|
|
63
|
-
await jest.advanceTimersByTimeAsync(10000);
|
|
64
|
-
await connectPromise;
|
|
65
|
-
|
|
66
|
-
expect(producer.connect).toHaveBeenCalledTimes(2);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should throw error if producer not connected', async () => {
|
|
70
|
-
await expect(kafkaService.sendMessage('topic', 'msg')).rejects.toThrow('[Kafka] Producer not connected');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should disconnect producer and consumer', async () => {
|
|
74
|
-
await kafkaService.disconnect();
|
|
75
|
-
const producer = (kafka.producer as jest.Mock).mock.results[0].value;
|
|
76
|
-
const consumer = (kafka.consumer as jest.Mock).mock.results[0].value;
|
|
77
|
-
|
|
78
|
-
expect(producer.disconnect).toHaveBeenCalled();
|
|
79
|
-
expect(consumer.disconnect).toHaveBeenCalled();
|
|
80
|
-
});
|
|
81
|
-
});
|
|
1
|
+
import { KafkaService } from '<% if (architecture === "Clean Architecture") { %>@/infrastructure/messaging/kafkaClient<% } else { %>@/services/kafkaService<% } %>';
|
|
2
|
+
import { kafka } from '<%= configPath %>';
|
|
3
|
+
|
|
4
|
+
jest.mock('<%= configPath %>', () => ({
|
|
5
|
+
kafka: {
|
|
6
|
+
producer: jest.fn().mockReturnValue({
|
|
7
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
8
|
+
send: jest.fn().mockResolvedValue(undefined),
|
|
9
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
10
|
+
}),
|
|
11
|
+
consumer: jest.fn().mockReturnValue({
|
|
12
|
+
connect: jest.fn().mockResolvedValue(undefined),
|
|
13
|
+
subscribe: jest.fn().mockResolvedValue(undefined),
|
|
14
|
+
run: jest.fn().mockResolvedValue(undefined),
|
|
15
|
+
disconnect: jest.fn().mockResolvedValue(undefined),
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
jest.mock('<%= loggerPath %>');
|
|
21
|
+
|
|
22
|
+
describe('KafkaService', () => {
|
|
23
|
+
let kafkaService: KafkaService;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
jest.clearAllMocks();
|
|
27
|
+
kafkaService = new KafkaService();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should connect producer and consumer', async () => {
|
|
31
|
+
await kafkaService.connect();
|
|
32
|
+
const producer = (kafka.producer as jest.Mock).mock.results[0].value;
|
|
33
|
+
const consumer = (kafka.consumer as jest.Mock).mock.results[0].value;
|
|
34
|
+
|
|
35
|
+
expect(producer.connect).toHaveBeenCalled();
|
|
36
|
+
expect(consumer.connect).toHaveBeenCalled();
|
|
37
|
+
expect(consumer.subscribe).toHaveBeenCalledWith(expect.objectContaining({ topic: 'user-topic', fromBeginning: true }));
|
|
38
|
+
expect(consumer.run).toHaveBeenCalled();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should send a message', async () => {
|
|
42
|
+
await kafkaService.connect();
|
|
43
|
+
const topic = 'test-topic';
|
|
44
|
+
const message = JSON.stringify({ action: 'TEST', payload: { email: 'test@example.com' } });
|
|
45
|
+
await kafkaService.sendMessage(topic, message);
|
|
46
|
+
const producer = (kafka.producer as jest.Mock).mock.results[0].value;
|
|
47
|
+
|
|
48
|
+
expect(producer.send).toHaveBeenCalledWith({
|
|
49
|
+
topic,
|
|
50
|
+
messages: [{ value: message }],
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should retry connection on failure', async () => {
|
|
55
|
+
const producer = (kafka.producer as jest.Mock).mock.results[0].value;
|
|
56
|
+
producer.connect
|
|
57
|
+
.mockRejectedValueOnce(new Error('Connection failed'))
|
|
58
|
+
.mockResolvedValueOnce(undefined);
|
|
59
|
+
|
|
60
|
+
jest.useFakeTimers();
|
|
61
|
+
const connectPromise = kafkaService.connect(2);
|
|
62
|
+
|
|
63
|
+
await jest.advanceTimersByTimeAsync(10000);
|
|
64
|
+
await connectPromise;
|
|
65
|
+
|
|
66
|
+
expect(producer.connect).toHaveBeenCalledTimes(2);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should throw error if producer not connected', async () => {
|
|
70
|
+
await expect(kafkaService.sendMessage('topic', 'msg')).rejects.toThrow('[Kafka] Producer not connected');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should disconnect producer and consumer', async () => {
|
|
74
|
+
await kafkaService.disconnect();
|
|
75
|
+
const producer = (kafka.producer as jest.Mock).mock.results[0].value;
|
|
76
|
+
const consumer = (kafka.consumer as jest.Mock).mock.results[0].value;
|
|
77
|
+
|
|
78
|
+
expect(producer.disconnect).toHaveBeenCalled();
|
|
79
|
+
expect(consumer.disconnect).toHaveBeenCalled();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
import { kafka } from '<%= configPath %>';
|
|
2
|
-
import { EachMessagePayload, Producer, Consumer } from 'kafkajs';
|
|
3
|
-
import logger from '<%= loggerPath %>';
|
|
4
|
-
|
|
5
|
-
export class KafkaService {
|
|
6
|
-
private producer: Producer;
|
|
7
|
-
private consumer: Consumer;
|
|
8
|
-
private isConnected = false;
|
|
9
|
-
private connectionPromise: Promise<void> | null = null;
|
|
10
|
-
|
|
11
|
-
constructor() {
|
|
12
|
-
this.producer = kafka.producer();
|
|
13
|
-
this.consumer = kafka.consumer({ groupId: 'test-group' });
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async connect(retries = 10) {
|
|
17
|
-
if (this.connectionPromise) return this.connectionPromise;
|
|
18
|
-
|
|
19
|
-
this.connectionPromise = (async () => {
|
|
20
|
-
let attempt = 0;
|
|
21
|
-
// Auto-register WelcomeEmailConsumer if it exists
|
|
22
|
-
// Note: Dynamic import used here for simplicity and to avoid startup crashes.
|
|
23
|
-
// In enterprise production, consider using Dependency Injection.
|
|
24
|
-
const { WelcomeEmailConsumer } = await import('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>@/messaging/consumers/instances/welcomeEmailConsumer<% } %>');
|
|
25
|
-
while (attempt < retries) {
|
|
26
|
-
try {
|
|
27
|
-
await this.producer.connect();
|
|
28
|
-
await this.consumer.connect();
|
|
29
|
-
logger.info('[Kafka] Producer connected successfully');
|
|
30
|
-
logger.info('[Kafka] Consumer connected successfully');
|
|
31
|
-
this.isConnected = true;
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const welcomeConsumer = new WelcomeEmailConsumer();
|
|
35
|
-
await this.consumer.subscribe({ topic: welcomeConsumer.topic, fromBeginning: true });
|
|
36
|
-
logger.info(`[Kafka] Registered consumer for topic: ${welcomeConsumer.topic}`);
|
|
37
|
-
|
|
38
|
-
await this.consumer.run({
|
|
39
|
-
eachMessage: async (payload) => welcomeConsumer.onMessage(payload),
|
|
40
|
-
});
|
|
41
|
-
} catch (e) {
|
|
42
|
-
// Fallback or no consumers found
|
|
43
|
-
logger.warn(`[Kafka] Could not load WelcomeEmailConsumer, using fallback: ${(e as Error).message}`);
|
|
44
|
-
await this.consumer.subscribe({ topic: 'user-topic', fromBeginning: true });
|
|
45
|
-
await this.consumer.run({
|
|
46
|
-
eachMessage: async ({ message }: EachMessagePayload) => {
|
|
47
|
-
logger.info(`[Kafka] Consumer: Received message on user-topic: ${message.value?.toString()}`);
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
return; // Success
|
|
52
|
-
} catch (error) {
|
|
53
|
-
attempt++;
|
|
54
|
-
logger.error(`[Kafka] Connection attempt ${attempt} failed:`, (error as Error).message);
|
|
55
|
-
if (attempt >= retries) {
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
await new Promise(res => setTimeout(res, 3000));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
})();
|
|
62
|
-
|
|
63
|
-
return this.connectionPromise;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async sendMessage(topic: string, message: string) {
|
|
67
|
-
if (this.connectionPromise) {
|
|
68
|
-
await this.connectionPromise;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (!this.isConnected) {
|
|
72
|
-
throw new Error('[Kafka] Producer not connected. Check logs for connection errors.');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
await this.producer.send({
|
|
76
|
-
topic,
|
|
77
|
-
messages: [
|
|
78
|
-
{ value: message },
|
|
79
|
-
],
|
|
80
|
-
});
|
|
81
|
-
try {
|
|
82
|
-
const parsed = JSON.parse(message);
|
|
83
|
-
logger.info(`[Kafka] Producer: Sent ${parsed.action} event for '${parsed.payload?.email || 'unknown'}'`);
|
|
84
|
-
} catch {
|
|
85
|
-
logger.info(`[Kafka] Producer: Sent message to ${topic}`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async disconnect() {
|
|
90
|
-
await this.producer.disconnect();
|
|
91
|
-
await this.consumer.disconnect();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export const kafkaService = new KafkaService();
|
|
1
|
+
import { kafka } from '<%= configPath %>';
|
|
2
|
+
import { EachMessagePayload, Producer, Consumer } from 'kafkajs';
|
|
3
|
+
import logger from '<%= loggerPath %>';
|
|
4
|
+
|
|
5
|
+
export class KafkaService {
|
|
6
|
+
private producer: Producer;
|
|
7
|
+
private consumer: Consumer;
|
|
8
|
+
private isConnected = false;
|
|
9
|
+
private connectionPromise: Promise<void> | null = null;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
this.producer = kafka.producer();
|
|
13
|
+
this.consumer = kafka.consumer({ groupId: 'test-group' });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async connect(retries = 10) {
|
|
17
|
+
if (this.connectionPromise) return this.connectionPromise;
|
|
18
|
+
|
|
19
|
+
this.connectionPromise = (async () => {
|
|
20
|
+
let attempt = 0;
|
|
21
|
+
// Auto-register WelcomeEmailConsumer if it exists
|
|
22
|
+
// Note: Dynamic import used here for simplicity and to avoid startup crashes.
|
|
23
|
+
// In enterprise production, consider using Dependency Injection.
|
|
24
|
+
const { WelcomeEmailConsumer } = await import('<% if (architecture === "Clean Architecture") { %>@/interfaces/messaging/consumers/instances/welcomeEmailConsumer<% } else { %>@/messaging/consumers/instances/welcomeEmailConsumer<% } %>');
|
|
25
|
+
while (attempt < retries) {
|
|
26
|
+
try {
|
|
27
|
+
await this.producer.connect();
|
|
28
|
+
await this.consumer.connect();
|
|
29
|
+
logger.info('[Kafka] Producer connected successfully');
|
|
30
|
+
logger.info('[Kafka] Consumer connected successfully');
|
|
31
|
+
this.isConnected = true;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const welcomeConsumer = new WelcomeEmailConsumer();
|
|
35
|
+
await this.consumer.subscribe({ topic: welcomeConsumer.topic, fromBeginning: true });
|
|
36
|
+
logger.info(`[Kafka] Registered consumer for topic: ${welcomeConsumer.topic}`);
|
|
37
|
+
|
|
38
|
+
await this.consumer.run({
|
|
39
|
+
eachMessage: async (payload) => welcomeConsumer.onMessage(payload),
|
|
40
|
+
});
|
|
41
|
+
} catch (e) {
|
|
42
|
+
// Fallback or no consumers found
|
|
43
|
+
logger.warn(`[Kafka] Could not load WelcomeEmailConsumer, using fallback: ${(e as Error).message}`);
|
|
44
|
+
await this.consumer.subscribe({ topic: 'user-topic', fromBeginning: true });
|
|
45
|
+
await this.consumer.run({
|
|
46
|
+
eachMessage: async ({ message }: EachMessagePayload) => {
|
|
47
|
+
logger.info(`[Kafka] Consumer: Received message on user-topic: ${message.value?.toString()}`);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return; // Success
|
|
52
|
+
} catch (error) {
|
|
53
|
+
attempt++;
|
|
54
|
+
logger.error(`[Kafka] Connection attempt ${attempt} failed:`, (error as Error).message);
|
|
55
|
+
if (attempt >= retries) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
await new Promise(res => setTimeout(res, 3000));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
|
|
63
|
+
return this.connectionPromise;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async sendMessage(topic: string, message: string, key?: string) {
|
|
67
|
+
if (this.connectionPromise) {
|
|
68
|
+
await this.connectionPromise;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!this.isConnected) {
|
|
72
|
+
throw new Error('[Kafka] Producer not connected. Check logs for connection errors.');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
await this.producer.send({
|
|
76
|
+
topic,
|
|
77
|
+
messages: [
|
|
78
|
+
{ key, value: message },
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(message);
|
|
83
|
+
logger.info(`[Kafka] Producer: Sent ${parsed.action} event for '${parsed.payload?.email || 'unknown'}'`);
|
|
84
|
+
} catch {
|
|
85
|
+
logger.info(`[Kafka] Producer: Sent message to ${topic}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async disconnect() {
|
|
90
|
+
await this.producer.disconnect();
|
|
91
|
+
await this.consumer.disconnect();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const kafkaService = new KafkaService();
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
const config = {
|
|
2
|
-
mongodb: {
|
|
3
|
-
url: process.env.MONGO_URI || `mongodb://${process.env.DB_HOST || 'localhost'}:27017`,
|
|
4
|
-
databaseName: process.env.DB_NAME || '<%= dbName %>',
|
|
5
|
-
|
|
6
|
-
options: {
|
|
7
|
-
// useNewUrlParser: true, // No longer needed in Node.js driver v4+
|
|
8
|
-
// useUnifiedTopology: true, // No longer needed in Node.js driver v4+
|
|
9
|
-
// connectTimeoutMS: 3600000, // increase connection timeout to 1 hour
|
|
10
|
-
// socketTimeoutMS: 3600000, // increase socket timeout to 1 hour
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
// The migrations dir, can be an relative or absolute path. Only edit this when really necessary.
|
|
15
|
-
migrationsDir: "migrations",
|
|
16
|
-
|
|
17
|
-
// The mongodb collection where the applied changes are stored. Only edit this when really necessary.
|
|
18
|
-
changelogCollectionName: "changelog",
|
|
19
|
-
|
|
20
|
-
// The file extension to create migrations and search for in migration dir
|
|
21
|
-
migrationFileExtension: ".js",
|
|
22
|
-
|
|
23
|
-
// Enable the algorithm to create a checksum of the file contents and use that in the comparison to determine
|
|
24
|
-
// if the file should be run. Requires that scripts are coded to be run multiple times.
|
|
25
|
-
useFileHash: false,
|
|
26
|
-
|
|
27
|
-
// Don't change this, unless you know what you are doing
|
|
28
|
-
moduleSystem: 'commonjs',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
module.exports = config;
|
|
1
|
+
const config = {
|
|
2
|
+
mongodb: {
|
|
3
|
+
url: process.env.MONGO_URI || `mongodb://${process.env.DB_HOST || 'localhost'}:27017`,
|
|
4
|
+
databaseName: process.env.DB_NAME || '<%= dbName %>',
|
|
5
|
+
|
|
6
|
+
options: {
|
|
7
|
+
// useNewUrlParser: true, // No longer needed in Node.js driver v4+
|
|
8
|
+
// useUnifiedTopology: true, // No longer needed in Node.js driver v4+
|
|
9
|
+
// connectTimeoutMS: 3600000, // increase connection timeout to 1 hour
|
|
10
|
+
// socketTimeoutMS: 3600000, // increase socket timeout to 1 hour
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
// The migrations dir, can be an relative or absolute path. Only edit this when really necessary.
|
|
15
|
+
migrationsDir: "migrations",
|
|
16
|
+
|
|
17
|
+
// The mongodb collection where the applied changes are stored. Only edit this when really necessary.
|
|
18
|
+
changelogCollectionName: "changelog",
|
|
19
|
+
|
|
20
|
+
// The file extension to create migrations and search for in migration dir
|
|
21
|
+
migrationFileExtension: ".js",
|
|
22
|
+
|
|
23
|
+
// Enable the algorithm to create a checksum of the file contents and use that in the comparison to determine
|
|
24
|
+
// if the file should be run. Requires that scripts are coded to be run multiple times.
|
|
25
|
+
useFileHash: false,
|
|
26
|
+
|
|
27
|
+
// Don't change this, unless you know what you are doing
|
|
28
|
+
moduleSystem: 'commonjs',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
module.exports = config;
|