nodejs-structure-cli 1.0.0

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.
Files changed (243) hide show
  1. package/README.md +32 -0
  2. package/bin/index.js +143 -0
  3. package/lib/generator.js +145 -0
  4. package/lib/modules/app-setup.js +479 -0
  5. package/lib/modules/caching-setup.js +76 -0
  6. package/lib/modules/config-files.js +151 -0
  7. package/lib/modules/database-setup.js +116 -0
  8. package/lib/modules/kafka-setup.js +249 -0
  9. package/lib/modules/project-setup.js +32 -0
  10. package/lib/prompts.js +128 -0
  11. package/package.json +66 -0
  12. package/templates/clean-architecture/js/src/domain/models/User.js.ejs +11 -0
  13. package/templates/clean-architecture/js/src/errors/ApiError.js +14 -0
  14. package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -0
  15. package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -0
  16. package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -0
  17. package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -0
  18. package/templates/clean-architecture/js/src/index.js.ejs +56 -0
  19. package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -0
  20. package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -0
  21. package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
  22. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +88 -0
  23. package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -0
  24. package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -0
  25. package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +93 -0
  26. package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -0
  27. package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +190 -0
  28. package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -0
  29. package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -0
  30. package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
  31. package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -0
  32. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -0
  33. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -0
  34. package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -0
  35. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -0
  36. package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -0
  37. package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +17 -0
  38. package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
  39. package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +14 -0
  40. package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
  41. package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
  42. package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
  43. package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -0
  44. package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
  45. package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +11 -0
  46. package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
  47. package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
  48. package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -0
  49. package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -0
  50. package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -0
  51. package/templates/clean-architecture/ts/src/domain/user.ts.ejs +9 -0
  52. package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -0
  53. package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
  54. package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -0
  55. package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -0
  56. package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -0
  57. package/templates/clean-architecture/ts/src/index.ts.ejs +144 -0
  58. package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -0
  59. package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -0
  60. package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -0
  61. package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +125 -0
  62. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -0
  63. package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +208 -0
  64. package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
  65. package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -0
  66. package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -0
  67. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -0
  68. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
  69. package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -0
  70. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -0
  71. package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -0
  72. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
  73. package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +18 -0
  74. package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
  75. package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +11 -0
  76. package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
  77. package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
  78. package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
  79. package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -0
  80. package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
  81. package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +10 -0
  82. package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
  83. package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -0
  84. package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -0
  85. package/templates/common/.cursorrules.ejs +60 -0
  86. package/templates/common/.dockerignore +12 -0
  87. package/templates/common/.env.example.ejs +60 -0
  88. package/templates/common/.gitattributes +46 -0
  89. package/templates/common/.gitlab-ci.yml.ejs +86 -0
  90. package/templates/common/.lintstagedrc +6 -0
  91. package/templates/common/.prettierrc +7 -0
  92. package/templates/common/.snyk.ejs +45 -0
  93. package/templates/common/Dockerfile +73 -0
  94. package/templates/common/Jenkinsfile.ejs +87 -0
  95. package/templates/common/README.md.ejs +148 -0
  96. package/templates/common/_github/workflows/ci.yml.ejs +46 -0
  97. package/templates/common/_github/workflows/security.yml.ejs +36 -0
  98. package/templates/common/_gitignore +5 -0
  99. package/templates/common/_husky/pre-commit +4 -0
  100. package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -0
  101. package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
  102. package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -0
  103. package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
  104. package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -0
  105. package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
  106. package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -0
  107. package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
  108. package/templates/common/caching/js/memoryCache.js.ejs +60 -0
  109. package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
  110. package/templates/common/caching/js/redisClient.js.ejs +75 -0
  111. package/templates/common/caching/js/redisClient.spec.js.ejs +147 -0
  112. package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
  113. package/templates/common/caching/ts/memoryCache.ts.ejs +73 -0
  114. package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
  115. package/templates/common/caching/ts/redisClient.ts.ejs +89 -0
  116. package/templates/common/database/js/database.js.ejs +19 -0
  117. package/templates/common/database/js/database.spec.js.ejs +56 -0
  118. package/templates/common/database/js/models/User.js.ejs +91 -0
  119. package/templates/common/database/js/models/User.js.mongoose.ejs +35 -0
  120. package/templates/common/database/js/models/User.spec.js.ejs +94 -0
  121. package/templates/common/database/js/mongoose.js.ejs +33 -0
  122. package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
  123. package/templates/common/database/ts/database.spec.ts.ejs +56 -0
  124. package/templates/common/database/ts/database.ts.ejs +21 -0
  125. package/templates/common/database/ts/models/User.spec.ts.ejs +100 -0
  126. package/templates/common/database/ts/models/User.ts.ejs +102 -0
  127. package/templates/common/database/ts/models/User.ts.mongoose.ejs +34 -0
  128. package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
  129. package/templates/common/database/ts/mongoose.ts.ejs +28 -0
  130. package/templates/common/docker-compose.yml.ejs +159 -0
  131. package/templates/common/ecosystem.config.js.ejs +40 -0
  132. package/templates/common/eslint.config.mjs.ejs +77 -0
  133. package/templates/common/health/js/healthRoute.js.ejs +50 -0
  134. package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
  135. package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
  136. package/templates/common/health/ts/healthRoute.ts.ejs +49 -0
  137. package/templates/common/jest.config.js.ejs +32 -0
  138. package/templates/common/jest.e2e.config.js.ejs +8 -0
  139. package/templates/common/kafka/js/config/kafka.js +9 -0
  140. package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -0
  141. package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -0
  142. package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -0
  143. package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -0
  144. package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -0
  145. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -0
  146. package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -0
  147. package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -0
  148. package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -0
  149. package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
  150. package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -0
  151. package/templates/common/kafka/ts/config/kafka.ts +7 -0
  152. package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -0
  153. package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -0
  154. package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -0
  155. package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -0
  156. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -0
  157. package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -0
  158. package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -0
  159. package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -0
  160. package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
  161. package/templates/common/migrate-mongo-config.js.ejs +31 -0
  162. package/templates/common/migrations/init.js.ejs +23 -0
  163. package/templates/common/package.json.ejs +137 -0
  164. package/templates/common/prompts/add-feature.md.ejs +26 -0
  165. package/templates/common/prompts/project-context.md.ejs +43 -0
  166. package/templates/common/prompts/troubleshoot.md.ejs +28 -0
  167. package/templates/common/public/css/style.css +147 -0
  168. package/templates/common/scripts/run-e2e.js.ejs +63 -0
  169. package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -0
  170. package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -0
  171. package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -0
  172. package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -0
  173. package/templates/common/sonar-project.properties.ejs +27 -0
  174. package/templates/common/src/config/auth.js.ejs +19 -0
  175. package/templates/common/src/config/auth.ts.ejs +19 -0
  176. package/templates/common/src/controllers/authController.js.ejs +101 -0
  177. package/templates/common/src/controllers/authController.ts.ejs +101 -0
  178. package/templates/common/src/middleware/auth.js.ejs +20 -0
  179. package/templates/common/src/middleware/auth.ts.ejs +25 -0
  180. package/templates/common/src/middleware/upload.js.ejs +31 -0
  181. package/templates/common/src/middleware/upload.ts.ejs +32 -0
  182. package/templates/common/src/routes/authRoutes.js.ejs +20 -0
  183. package/templates/common/src/routes/authRoutes.ts.ejs +20 -0
  184. package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -0
  185. package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -0
  186. package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
  187. package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
  188. package/templates/common/swagger.yml.ejs +118 -0
  189. package/templates/common/tsconfig.json +23 -0
  190. package/templates/common/views/ejs/index.ejs +55 -0
  191. package/templates/common/views/pug/index.pug +40 -0
  192. package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -0
  193. package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -0
  194. package/templates/mvc/js/src/config/env.js.ejs +46 -0
  195. package/templates/mvc/js/src/config/swagger.js.ejs +6 -0
  196. package/templates/mvc/js/src/controllers/userController.js.ejs +288 -0
  197. package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -0
  198. package/templates/mvc/js/src/errors/ApiError.js +14 -0
  199. package/templates/mvc/js/src/errors/BadRequestError.js +11 -0
  200. package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -0
  201. package/templates/mvc/js/src/errors/NotFoundError.js +11 -0
  202. package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -0
  203. package/templates/mvc/js/src/graphql/context.js.ejs +7 -0
  204. package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
  205. package/templates/mvc/js/src/graphql/index.js.ejs +5 -0
  206. package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -0
  207. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -0
  208. package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -0
  209. package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -0
  210. package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -0
  211. package/templates/mvc/js/src/index.js.ejs +141 -0
  212. package/templates/mvc/js/src/routes/api.js.ejs +15 -0
  213. package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -0
  214. package/templates/mvc/js/src/utils/errorMessages.js +14 -0
  215. package/templates/mvc/js/src/utils/errorMiddleware.js +29 -0
  216. package/templates/mvc/js/src/utils/httpCodes.js +9 -0
  217. package/templates/mvc/js/src/utils/logger.js +40 -0
  218. package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
  219. package/templates/mvc/ts/src/config/env.ts.ejs +45 -0
  220. package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -0
  221. package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -0
  222. package/templates/mvc/ts/src/controllers/userController.ts.ejs +292 -0
  223. package/templates/mvc/ts/src/errors/ApiError.ts +15 -0
  224. package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
  225. package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -0
  226. package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -0
  227. package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -0
  228. package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
  229. package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -0
  230. package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -0
  231. package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -0
  232. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
  233. package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -0
  234. package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -0
  235. package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -0
  236. package/templates/mvc/ts/src/index.ts.ejs +157 -0
  237. package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -0
  238. package/templates/mvc/ts/src/routes/api.ts.ejs +17 -0
  239. package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
  240. package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -0
  241. package/templates/mvc/ts/src/utils/httpCodes.ts +7 -0
  242. package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -0
  243. package/templates/mvc/ts/src/utils/logger.ts +36 -0
@@ -0,0 +1,142 @@
1
+ const UserRepository = require('@/infrastructure/repositories/UserRepository');
2
+ const UserModel = require('@/infrastructure/database/models/User');
3
+
4
+ jest.mock('@/infrastructure/database/models/User');
5
+
6
+ describe('UserRepository', () => {
7
+ let userRepository;
8
+
9
+ beforeEach(() => {
10
+ userRepository = new UserRepository();
11
+ jest.clearAllMocks();
12
+ });
13
+
14
+ describe('save', () => {
15
+ it('should save and return a newly created user (Happy Path)', async () => {
16
+ const userData = { name: 'New User', email: 'new@example.com' };
17
+ <%_ if (database === 'MongoDB') { -%>
18
+ const savedData = { _id: 'mock-id', ...userData };
19
+ UserModel.create.mockResolvedValue(savedData);
20
+ <%_ } else if (database === 'None') { -%>
21
+ UserModel.create.mockResolvedValue(userData);
22
+ <%_ } else { -%>
23
+ const savedData = { <%= database === "MongoDB" ? "_id" : "id" %>: '<%= database === "MongoDB" ? "mock-id" : "1" %>', ...userData };
24
+ UserModel.create.mockResolvedValue(savedData);
25
+ <%_ } -%>
26
+
27
+ const result = await userRepository.save(userData);
28
+
29
+ <%_ if (database === 'None') { -%>
30
+ expect(result.name).toEqual(userData.name);
31
+ <%_ } else { -%>
32
+ expect(UserModel.create).toHaveBeenCalledWith(userData);
33
+ expect(result).toEqual({ id: '<%= database === "MongoDB" ? "mock-id" : "1" %>', ...userData });
34
+ <%_ } -%>
35
+ });
36
+
37
+ it('should throw an error when DB fails explicitly (Edge Case)', async () => {
38
+ <%_ if (database === 'None') { -%>
39
+ // mock data logic doesn't throw naturally
40
+ <%_ } else { -%>
41
+ const error = new Error('DB Error');
42
+ UserModel.create.mockRejectedValue(error);
43
+
44
+ await expect(userRepository.save({})).rejects.toThrow('DB Error');
45
+ <%_ } -%>
46
+ });
47
+ });
48
+
49
+ describe('getUsers', () => {
50
+ it('should return a list of mapped UserEntities (Happy Path)', async () => {
51
+ <%_ if (database === 'MongoDB') { -%>
52
+ const mockUsers = [
53
+ { _id: '1', name: 'User 1', email: 'u1@test.com' },
54
+ { _id: '2', name: 'User 2', email: 'u2@test.com' }
55
+ ];
56
+ UserModel.find.mockResolvedValue(mockUsers);
57
+ <%_ } else if (database === 'None') { -%>
58
+ const mockUsers = [{ id: '1', name: 'User 1', email: 'u1@test.com' }];
59
+ UserModel.find.mockResolvedValue(mockUsers);
60
+ <%_ } else { -%>
61
+ const mockUsers = [
62
+ { <%= database === "MongoDB" ? "_id" : "id" %>: '1', name: 'User 1', email: 'u1@test.com' },
63
+ { <%= database === "MongoDB" ? "_id" : "id" %>: '2', name: 'User 2', email: 'u2@test.com' }
64
+ ];
65
+ UserModel.<%= database === "MongoDB" ? "find" : "findAll" %>.mockResolvedValue(mockUsers);
66
+ <%_ } -%>
67
+
68
+ const result = await userRepository.getUsers();
69
+
70
+ <%_ if (database !== 'None') { -%>
71
+ expect(UserModel.<%= database === 'MongoDB' ? 'find' : 'findAll' %>).toHaveBeenCalled();
72
+ <%_ } -%>
73
+ expect(result).toEqual([
74
+ { id: '1', name: 'User 1', email: 'u1@test.com' }
75
+ <%_ if (database !== 'None') { -%>
76
+ , { id: '2', name: 'User 2', email: 'u2@test.com' }
77
+ <%_ } -%>
78
+ ]);
79
+ });
80
+ });
81
+
82
+ describe('update', () => {
83
+ it('should successfully update a user (Happy Path)', async () => {
84
+ const id = '1';
85
+ const updateData = { name: 'Updated name' };
86
+ <%_ if (database === 'MongoDB') { -%>
87
+ const updatedUser = { _id: id, ...updateData, email: 'test@test.com' };
88
+ UserModel.findByIdAndUpdate.mockResolvedValue(updatedUser);
89
+ <%_ } else if (database === 'None') { -%>
90
+ const updatedUser = { id, ...updateData, email: 'test@test.com' };
91
+ UserModel.update.mockResolvedValue(updatedUser);
92
+ <%_ } else { -%>
93
+ const updatedUser = { id: '1', ...updateData, email: 'test@test.com', update: jest.fn().mockResolvedValue(true) };
94
+ UserModel.findByPk.mockResolvedValue(updatedUser);
95
+ <%_ } -%>
96
+
97
+ const result = await userRepository.update(id, updateData);
98
+ expect(result.name).toEqual(updateData.name);
99
+ });
100
+
101
+ it('should handle user not found during update (Error Handling)', async () => {
102
+ <%_ if (database === 'MongoDB') { -%>
103
+ UserModel.findByIdAndUpdate.mockResolvedValue(null);
104
+ <%_ } else if (database === 'None') { -%>
105
+ UserModel.update.mockResolvedValue(null);
106
+ <%_ } else { -%>
107
+ UserModel.findByPk.mockResolvedValue(null);
108
+ <%_ } -%>
109
+ const result = await userRepository.update('999', { name: 'fail' });
110
+ expect(result).toBeNull();
111
+ });
112
+ });
113
+
114
+ describe('delete', () => {
115
+ it('should successfully delete a user (Happy Path)', async () => {
116
+ const id = '1';
117
+ <%_ if (database === 'MongoDB') { -%>
118
+ UserModel.findByIdAndDelete.mockResolvedValue(true);
119
+ <%_ } else if (database === 'None') { -%>
120
+ UserModel.destroy.mockResolvedValue(true);
121
+ <%_ } else { -%>
122
+ const userMock = { id, destroy: jest.fn().mockResolvedValue(true) };
123
+ UserModel.findByPk.mockResolvedValue(userMock);
124
+ <%_ } -%>
125
+
126
+ const result = await userRepository.delete(id);
127
+ expect(result).toBe(true);
128
+ });
129
+
130
+ it('should handle user not found during deletion (Error Handling)', async () => {
131
+ <%_ if (database === 'MongoDB') { -%>
132
+ UserModel.findByIdAndDelete.mockResolvedValue(null);
133
+ <%_ } else if (database === 'None') { -%>
134
+ UserModel.destroy.mockResolvedValue(false);
135
+ <%_ } else { -%>
136
+ UserModel.findByPk.mockResolvedValue(null);
137
+ <%_ } -%>
138
+ const result = await userRepository.delete('999');
139
+ expect(result).toBe(false);
140
+ });
141
+ });
142
+ });
@@ -0,0 +1,30 @@
1
+ const logger = require('../../log/logger');
2
+ const { ApiError } = require('../../../errors/ApiError');
3
+ const HTTP_STATUS = require('../../../utils/httpCodes');
4
+
5
+ // eslint-disable-next-line no-unused-vars
6
+ const errorMiddleware = (err, req, res, next) => {
7
+ let error = err;
8
+
9
+ if (!(error instanceof ApiError)) {
10
+ const statusCode = err.statusCode || HTTP_STATUS.INTERNAL_SERVER_ERROR;
11
+ const message = error.message || 'Internal Server Error';
12
+ error = new ApiError(statusCode, message, false, err.stack);
13
+ }
14
+
15
+ const { statusCode, message } = error;
16
+
17
+ if (statusCode === HTTP_STATUS.INTERNAL_SERVER_ERROR) {
18
+ logger.error(`${statusCode} - ${message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
19
+ logger.error(error.stack || 'No stack trace');
20
+ }
21
+
22
+ res.status(statusCode).json({
23
+ statusCode,
24
+ message,
25
+ ...(process.env.NODE_ENV === 'development' && { stack: error.stack }),
26
+ });
27
+ };
28
+
29
+ module.exports = { errorMiddleware };
30
+
@@ -0,0 +1,93 @@
1
+ const express = require('express');
2
+ const cors = require('cors');
3
+ const logger = require('../log/logger');
4
+ const morgan = require('morgan');
5
+ const { errorMiddleware } = require('./middleware/errorMiddleware');
6
+ const healthRoutes = require('../../interfaces/routes/healthRoute');
7
+ const setupGracefulShutdown = require('../../utils/gracefulShutdown');
8
+ <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
9
+ const apiRoutes = require('../../interfaces/routes/api');
10
+ const swaggerUi = require('swagger-ui-express');
11
+ const swaggerSpecs = require('./swagger');
12
+ <%_ } -%>
13
+ <%_ if (communication === 'GraphQL') { -%>
14
+ const { ApolloServer } = require('@apollo/server');
15
+ const { expressMiddleware } = require('@as-integrations/express4');
16
+ const { ApolloServerPluginLandingPageLocalDefault } = require('@apollo/server/plugin/landingPage/default');
17
+ const { unwrapResolverError } = require('@apollo/server/errors');
18
+ const { ApiError } = require('../../errors/ApiError');
19
+ const { typeDefs, resolvers } = require('../../interfaces/graphql');
20
+ const { gqlContext } = require('../../interfaces/graphql/context');
21
+ <%_ } -%>
22
+
23
+ const { env } = require('../config/env');
24
+
25
+ const startServer = async () => {
26
+ // Determine port using the validated env
27
+ const port = env.PORT;
28
+ const app = express();
29
+
30
+ app.use(cors());
31
+ app.use(express.json());
32
+ <% if (includeMulter) { %>
33
+ const path = require('path');
34
+ app.use('/uploads', express.static(path.join(__dirname, '../../../../uploads')));
35
+ <% } %>
36
+ app.use(morgan('combined', { stream: { write: message => logger.info(message.trim()) } }));
37
+ <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
38
+ app.use('/api', apiRoutes);
39
+ <%_ } -%>
40
+ <%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
41
+ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
42
+ <%_ } -%>
43
+ <%_ if (communication === 'GraphQL') { -%>
44
+ // GraphQL Setup
45
+ const apolloServer = new ApolloServer({
46
+ typeDefs,
47
+ resolvers,
48
+ plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
49
+ formatError: (formattedError, error) => {
50
+ const originalError = unwrapResolverError(error);
51
+ if (originalError instanceof ApiError) {
52
+ return {
53
+ ...formattedError,
54
+ message: originalError.message,
55
+ extensions: {
56
+ ...formattedError.extensions,
57
+ code: originalError.statusCode.toString(),
58
+ }
59
+ };
60
+ }
61
+
62
+ logger.error(`GraphQL Error: ${formattedError.message}`);
63
+ if (originalError && originalError.stack && process.env.NODE_ENV === 'development') {
64
+ logger.error(originalError.stack);
65
+ }
66
+ return formattedError;
67
+ },
68
+ });
69
+ await apolloServer.start();
70
+ app.use('/graphql', expressMiddleware(apolloServer, { context: gqlContext }));
71
+ <%_ } -%>
72
+ app.use('/health', healthRoutes);
73
+
74
+ app.use(errorMiddleware);
75
+
76
+ const server = app.listen(port, () => {
77
+ logger.info(`Server running on port ${port}`);
78
+ <%_ if (communication === 'Kafka') { -%>
79
+ const { connectKafka } = require('../../infrastructure/messaging/kafkaClient');
80
+ connectKafka()
81
+ .then(async () => {
82
+ logger.info('Kafka connected');
83
+ })
84
+ .catch(err => {
85
+ logger.error('Failed to connect to Kafka after retries:', err.message);
86
+ });
87
+ <%_ } -%>
88
+ });
89
+
90
+ setupGracefulShutdown(server);
91
+ };
92
+
93
+ module.exports = startServer;
@@ -0,0 +1,6 @@
1
+ const path = require('path');
2
+ const YAML = require('yamljs');
3
+
4
+ const swaggerDocument = YAML.load(path.join(__dirname, 'swagger.yml'));
5
+
6
+ module.exports = swaggerDocument;
@@ -0,0 +1,190 @@
1
+ const CreateUser = require('../../usecases/CreateUser');
2
+ const GetAllUsers = require('../../usecases/GetAllUsers');
3
+ const UpdateUser = require('../../usecases/UpdateUser');
4
+ const DeleteUser = require('../../usecases/DeleteUser');
5
+ const UserRepository = require('../../infrastructure/repositories/UserRepository');
6
+ const ERROR_MESSAGES = require('../../utils/errorMessages');
7
+ <% if (communication !== 'GraphQL') { -%>
8
+ const HTTP_STATUS = require('../../utils/httpCodes');
9
+ <% } -%>
10
+ const logger = require('../../infrastructure/log/logger');
11
+ <%_ if (communication === 'Kafka') { -%>
12
+ const { sendMessage } = require('../../infrastructure/messaging/kafkaClient');
13
+ const { KAFKA_ACTIONS } = require('../../utils/kafkaEvents');
14
+ <%_ } -%>
15
+ <%_ if (auth && auth !== 'None') { _%>
16
+ const bcrypt = require('bcryptjs');
17
+ <%_ } _%>
18
+
19
+ class UserController {
20
+ constructor() {
21
+ this.userRepository = new UserRepository();
22
+ this.createUserUseCase = new CreateUser(this.userRepository);
23
+ this.getAllUsersUseCase = new GetAllUsers(this.userRepository);
24
+ this.updateUserUseCase = new UpdateUser(this.userRepository);
25
+ this.deleteUserUseCase = new DeleteUser(this.userRepository);
26
+ }
27
+
28
+ <% if (communication === 'GraphQL') { -%>
29
+ async getUsers() {
30
+ try {
31
+ return await this.getAllUsersUseCase.execute();
32
+ } catch (error) {
33
+ logger.error(`${ERROR_MESSAGES.FETCH_USERS_ERROR}:`, error);
34
+ throw error;
35
+ }
36
+ }
37
+
38
+ async createUser(data) {
39
+ const { name, email, password, imageUrl } = data;
40
+ try {
41
+ let hashedPassword = password;
42
+ <% if (auth && auth !== 'None') { %>
43
+ if (password) {
44
+ hashedPassword = await bcrypt.hash(password, 10);
45
+ }
46
+ <% } %>
47
+ const user = await this.createUserUseCase.execute(name, email<% if (auth && auth !== 'None') { %>, hashedPassword <% } %> <% if (includeMulter) { %>, imageUrl<% } %>);
48
+ <%_ if (communication === 'Kafka') { -%>
49
+ await sendMessage('user-topic', JSON.stringify({
50
+ action: KAFKA_ACTIONS.USER_CREATED,
51
+ payload: { id: user.id || user._id, email: user.email }
52
+ }), (user.id || user._id).toString());
53
+ <%_ } -%>
54
+ return user;
55
+ } catch (error) {
56
+ logger.error(`${ERROR_MESSAGES.CREATE_USER_ERROR}:`, error);
57
+ throw error;
58
+ }
59
+ }
60
+
61
+ async updateUser(id, data) {
62
+ try {
63
+ const user = await this.updateUserUseCase.execute(id, data);
64
+ if (!user) throw new Error(ERROR_MESSAGES.USER_NOT_FOUND);
65
+ <%_ if (communication === 'Kafka') { -%>
66
+ await sendMessage('user-topic', JSON.stringify({
67
+ action: KAFKA_ACTIONS.USER_UPDATED,
68
+ payload: { id, email: user.email }
69
+ }), id);
70
+ <%_ } -%>
71
+ return user;
72
+ } catch (error) {
73
+ logger.error(`${ERROR_MESSAGES.UPDATE_USER_ERROR}:`, error);
74
+ throw error;
75
+ }
76
+ }
77
+
78
+ async deleteUser(id) {
79
+ try {
80
+ const deleted = await this.deleteUserUseCase.execute(id);
81
+ if (!deleted) throw new Error(ERROR_MESSAGES.USER_NOT_FOUND);
82
+ <%_ if (communication === 'Kafka') { -%>
83
+ await sendMessage('user-topic', JSON.stringify({
84
+ action: KAFKA_ACTIONS.USER_DELETED,
85
+ payload: { id }
86
+ }), id);
87
+ <%_ } -%>
88
+ return true;
89
+ } catch (error) {
90
+ logger.error(`${ERROR_MESSAGES.DELETE_USER_ERROR}:`, error);
91
+ throw error;
92
+ }
93
+ }
94
+ <% } else { -%>
95
+ async getUsers(req, res, next) {
96
+ try {
97
+ const users = await this.getAllUsersUseCase.execute();
98
+ res.json(users);
99
+ } catch (error) {
100
+ logger.error(`${ERROR_MESSAGES.FETCH_USERS_ERROR}:`, error);
101
+ next(error);
102
+ }
103
+ }
104
+
105
+ async createUser(req, res, next) {
106
+ const { name, email, password } = req.body || {};
107
+ try {
108
+ let hashedPassword = password;
109
+ <% if (auth && auth !== 'None') { %>
110
+ if (password) {
111
+ hashedPassword = await bcrypt.hash(password, 10);
112
+ }
113
+ <% } %>
114
+ let imageUrl = null;
115
+ <% if (includeMulter) { %>
116
+ if (req.file) {
117
+ imageUrl = `/uploads/${req.file.filename}`;
118
+ }
119
+ <% } %>
120
+
121
+ const user = await this.createUserUseCase.execute(name, email <% if (auth && auth !== 'None') { %>, hashedPassword <% } %> <% if (includeMulter) { %>, imageUrl<% } %>);
122
+ <%_ if (communication === 'Kafka') { -%>
123
+ await sendMessage('user-topic', JSON.stringify({
124
+ action: KAFKA_ACTIONS.USER_CREATED,
125
+ payload: { id: user.id || user._id, email: user.email }
126
+ }), (user.id || user._id).toString());
127
+ <%_ } -%>
128
+ res.status(HTTP_STATUS.CREATED).json(user);
129
+ } catch (error) {
130
+ logger.error(`${ERROR_MESSAGES.CREATE_USER_ERROR}:`, error);
131
+ next(error);
132
+ }
133
+ }
134
+
135
+ async updateUser(req, res, next) {
136
+ const { id } = req.params;
137
+ const { name, email, password } = req.body || {};
138
+ try {
139
+ const updateData = { name, email };
140
+ <% if (auth && auth !== 'None') { %>
141
+ if (password) {
142
+ updateData.password = await bcrypt.hash(password, 10);
143
+ }
144
+ <% } %>
145
+ <% if (includeMulter) { %>
146
+ if (req.file) {
147
+ updateData.imageUrl = `/uploads/${req.file.filename}`;
148
+ }
149
+ <% } %>
150
+
151
+ const user = await this.updateUserUseCase.execute(id, updateData);
152
+ if (!user) {
153
+ return res.status(HTTP_STATUS.NOT_FOUND).json({ error: ERROR_MESSAGES.USER_NOT_FOUND });
154
+ }
155
+ <%_ if (communication === 'Kafka') { -%>
156
+ await sendMessage('user-topic', JSON.stringify({
157
+ action: KAFKA_ACTIONS.USER_UPDATED,
158
+ payload: { id, email: user.email }
159
+ }), id);
160
+ <%_ } -%>
161
+ res.json(user);
162
+ } catch (error) {
163
+ logger.error(`${ERROR_MESSAGES.UPDATE_USER_ERROR}:`, error);
164
+ next(error);
165
+ }
166
+ }
167
+
168
+ async deleteUser(req, res, next) {
169
+ const { id } = req.params;
170
+ try {
171
+ const deleted = await this.deleteUserUseCase.execute(id);
172
+ if (!deleted) {
173
+ return res.status(HTTP_STATUS.NOT_FOUND).json({ error: ERROR_MESSAGES.USER_NOT_FOUND });
174
+ }
175
+ <%_ if (communication === 'Kafka') { -%>
176
+ await sendMessage('user-topic', JSON.stringify({
177
+ action: KAFKA_ACTIONS.USER_DELETED,
178
+ payload: { id }
179
+ }), id);
180
+ <%_ } -%>
181
+ res.status(HTTP_STATUS.OK).json({ message: 'User deleted successfully' });
182
+ } catch (error) {
183
+ logger.error(`${ERROR_MESSAGES.DELETE_USER_ERROR}:`, error);
184
+ next(error);
185
+ }
186
+ }
187
+ <% } -%>
188
+ }
189
+
190
+ module.exports = UserController;