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.
- package/README.md +32 -0
- package/bin/index.js +143 -0
- package/lib/generator.js +145 -0
- package/lib/modules/app-setup.js +479 -0
- package/lib/modules/caching-setup.js +76 -0
- package/lib/modules/config-files.js +151 -0
- package/lib/modules/database-setup.js +116 -0
- package/lib/modules/kafka-setup.js +249 -0
- package/lib/modules/project-setup.js +32 -0
- package/lib/prompts.js +128 -0
- package/package.json +66 -0
- package/templates/clean-architecture/js/src/domain/models/User.js.ejs +11 -0
- package/templates/clean-architecture/js/src/errors/ApiError.js +14 -0
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -0
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -0
- package/templates/clean-architecture/js/src/index.js.ejs +56 -0
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +88 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +93 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +190 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +17 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +14 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
- 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 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +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 -0
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -0
- package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -0
- package/templates/clean-architecture/ts/src/domain/user.ts.ejs +9 -0
- package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -0
- package/templates/clean-architecture/ts/src/index.ts.ejs +144 -0
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -0
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +125 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +208 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +18 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +11 -0
- 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 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +10 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
- package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -0
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -0
- package/templates/common/.cursorrules.ejs +60 -0
- package/templates/common/.dockerignore +12 -0
- package/templates/common/.env.example.ejs +60 -0
- package/templates/common/.gitattributes +46 -0
- package/templates/common/.gitlab-ci.yml.ejs +86 -0
- package/templates/common/.lintstagedrc +6 -0
- package/templates/common/.prettierrc +7 -0
- package/templates/common/.snyk.ejs +45 -0
- package/templates/common/Dockerfile +73 -0
- package/templates/common/Jenkinsfile.ejs +87 -0
- package/templates/common/README.md.ejs +148 -0
- package/templates/common/_github/workflows/ci.yml.ejs +46 -0
- package/templates/common/_github/workflows/security.yml.ejs +36 -0
- package/templates/common/_gitignore +5 -0
- package/templates/common/_husky/pre-commit +4 -0
- package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -0
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
- package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
- package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -0
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
- package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
- package/templates/common/caching/js/memoryCache.js.ejs +60 -0
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
- package/templates/common/caching/js/redisClient.js.ejs +75 -0
- package/templates/common/caching/js/redisClient.spec.js.ejs +147 -0
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
- package/templates/common/caching/ts/memoryCache.ts.ejs +73 -0
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
- package/templates/common/caching/ts/redisClient.ts.ejs +89 -0
- package/templates/common/database/js/database.js.ejs +19 -0
- package/templates/common/database/js/database.spec.js.ejs +56 -0
- package/templates/common/database/js/models/User.js.ejs +91 -0
- package/templates/common/database/js/models/User.js.mongoose.ejs +35 -0
- package/templates/common/database/js/models/User.spec.js.ejs +94 -0
- package/templates/common/database/js/mongoose.js.ejs +33 -0
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
- package/templates/common/database/ts/database.spec.ts.ejs +56 -0
- package/templates/common/database/ts/database.ts.ejs +21 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +100 -0
- package/templates/common/database/ts/models/User.ts.ejs +102 -0
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +34 -0
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
- package/templates/common/database/ts/mongoose.ts.ejs +28 -0
- package/templates/common/docker-compose.yml.ejs +159 -0
- package/templates/common/ecosystem.config.js.ejs +40 -0
- package/templates/common/eslint.config.mjs.ejs +77 -0
- package/templates/common/health/js/healthRoute.js.ejs +50 -0
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
- package/templates/common/health/ts/healthRoute.ts.ejs +49 -0
- package/templates/common/jest.config.js.ejs +32 -0
- package/templates/common/jest.e2e.config.js.ejs +8 -0
- package/templates/common/kafka/js/config/kafka.js +9 -0
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -0
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -0
- package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -0
- package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -0
- package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -0
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -0
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -0
- package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -0
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -0
- package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -0
- package/templates/common/kafka/ts/config/kafka.ts +7 -0
- package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -0
- package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -0
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -0
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -0
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -0
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -0
- package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
- package/templates/common/migrate-mongo-config.js.ejs +31 -0
- package/templates/common/migrations/init.js.ejs +23 -0
- package/templates/common/package.json.ejs +137 -0
- package/templates/common/prompts/add-feature.md.ejs +26 -0
- package/templates/common/prompts/project-context.md.ejs +43 -0
- package/templates/common/prompts/troubleshoot.md.ejs +28 -0
- package/templates/common/public/css/style.css +147 -0
- package/templates/common/scripts/run-e2e.js.ejs +63 -0
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -0
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -0
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -0
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -0
- package/templates/common/sonar-project.properties.ejs +27 -0
- package/templates/common/src/config/auth.js.ejs +19 -0
- package/templates/common/src/config/auth.ts.ejs +19 -0
- package/templates/common/src/controllers/authController.js.ejs +101 -0
- package/templates/common/src/controllers/authController.ts.ejs +101 -0
- package/templates/common/src/middleware/auth.js.ejs +20 -0
- package/templates/common/src/middleware/auth.ts.ejs +25 -0
- package/templates/common/src/middleware/upload.js.ejs +31 -0
- package/templates/common/src/middleware/upload.ts.ejs +32 -0
- package/templates/common/src/routes/authRoutes.js.ejs +20 -0
- package/templates/common/src/routes/authRoutes.ts.ejs +20 -0
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -0
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -0
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
- package/templates/common/swagger.yml.ejs +118 -0
- package/templates/common/tsconfig.json +23 -0
- package/templates/common/views/ejs/index.ejs +55 -0
- package/templates/common/views/pug/index.pug +40 -0
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -0
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -0
- package/templates/mvc/js/src/config/env.js.ejs +46 -0
- package/templates/mvc/js/src/config/swagger.js.ejs +6 -0
- package/templates/mvc/js/src/controllers/userController.js.ejs +288 -0
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -0
- package/templates/mvc/js/src/errors/ApiError.js +14 -0
- package/templates/mvc/js/src/errors/BadRequestError.js +11 -0
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -0
- package/templates/mvc/js/src/errors/NotFoundError.js +11 -0
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -0
- package/templates/mvc/js/src/graphql/context.js.ejs +7 -0
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
- package/templates/mvc/js/src/graphql/index.js.ejs +5 -0
- package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -0
- package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -0
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -0
- package/templates/mvc/js/src/index.js.ejs +141 -0
- package/templates/mvc/js/src/routes/api.js.ejs +15 -0
- package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -0
- package/templates/mvc/js/src/utils/errorMessages.js +14 -0
- package/templates/mvc/js/src/utils/errorMiddleware.js +29 -0
- package/templates/mvc/js/src/utils/httpCodes.js +9 -0
- package/templates/mvc/js/src/utils/logger.js +40 -0
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
- package/templates/mvc/ts/src/config/env.ts.ejs +45 -0
- package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -0
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +292 -0
- package/templates/mvc/ts/src/errors/ApiError.ts +15 -0
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
- package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -0
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -0
- package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -0
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
- package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -0
- package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -0
- package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -0
- package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -0
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -0
- package/templates/mvc/ts/src/index.ts.ejs +157 -0
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -0
- package/templates/mvc/ts/src/routes/api.ts.ejs +17 -0
- package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
- package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -0
- package/templates/mvc/ts/src/utils/httpCodes.ts +7 -0
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -0
- package/templates/mvc/ts/src/utils/logger.ts +36 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { ERROR_MESSAGES } from '@/utils/errorMessages';
|
|
2
|
+
<% if (communication !== 'GraphQL') { -%>
|
|
3
|
+
import { Request, Response, NextFunction } from 'express';
|
|
4
|
+
import { HTTP_STATUS } from '@/utils/httpCodes';
|
|
5
|
+
<% } -%>
|
|
6
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
7
|
+
import CreateUser from '@/usecases/createUser';
|
|
8
|
+
import GetAllUsers from '@/usecases/getAllUsers';
|
|
9
|
+
import UpdateUser from '@/usecases/updateUser';
|
|
10
|
+
import DeleteUser from '@/usecases/deleteUser';
|
|
11
|
+
import logger from '@/infrastructure/log/logger';
|
|
12
|
+
import { kafkaService } from '@/infrastructure/messaging/kafkaClient';
|
|
13
|
+
import { KAFKA_ACTIONS } from '@/utils/kafkaEvents';
|
|
14
|
+
<%_ } -%>
|
|
15
|
+
<%_ if (auth && auth !== 'None') { _%>
|
|
16
|
+
import * as bcrypt from 'bcryptjs';
|
|
17
|
+
<%_ } _%>
|
|
18
|
+
|
|
19
|
+
export class UserController {
|
|
20
|
+
private createUserUseCase: CreateUser;
|
|
21
|
+
private getAllUsersUseCase: GetAllUsers;
|
|
22
|
+
private updateUserUseCase: UpdateUser;
|
|
23
|
+
private deleteUserUseCase: DeleteUser;
|
|
24
|
+
|
|
25
|
+
constructor() {
|
|
26
|
+
const userRepository = new UserRepository();
|
|
27
|
+
this.createUserUseCase = new CreateUser(userRepository);
|
|
28
|
+
this.getAllUsersUseCase = new GetAllUsers(userRepository);
|
|
29
|
+
this.updateUserUseCase = new UpdateUser(userRepository);
|
|
30
|
+
this.deleteUserUseCase = new DeleteUser(userRepository);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
<% if (communication === 'GraphQL') { -%>
|
|
34
|
+
async createUser(data: { name: string, email: string, password?: string, imageUrl?: string }) {
|
|
35
|
+
try {
|
|
36
|
+
const { name, email, password, imageUrl } = data;
|
|
37
|
+
let hashedPassword = password;
|
|
38
|
+
<% if (auth && auth !== 'None') { %>
|
|
39
|
+
if (password) {
|
|
40
|
+
hashedPassword = await bcrypt.hash(password, 10);
|
|
41
|
+
}
|
|
42
|
+
<% } %>
|
|
43
|
+
|
|
44
|
+
const user = await this.createUserUseCase.execute(name, email<% if (auth && auth !== 'None') { %>, hashedPassword <% } %> <% if (includeMulter) { %>, imageUrl<% } %>);
|
|
45
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
const userId = (user as any).id || (user as any)._id;
|
|
48
|
+
await kafkaService.sendMessage('user-topic', JSON.stringify({
|
|
49
|
+
action: KAFKA_ACTIONS.USER_CREATED,
|
|
50
|
+
payload: { id: userId, email: user.email }
|
|
51
|
+
}), userId?.toString() || '');
|
|
52
|
+
<%_ } -%>
|
|
53
|
+
return user;
|
|
54
|
+
} catch (error: unknown) {
|
|
55
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
56
|
+
logger.error(`${ERROR_MESSAGES.CREATE_USER_ERROR}:`, message);
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async getUsers() {
|
|
62
|
+
try {
|
|
63
|
+
const users = await this.getAllUsersUseCase.execute();
|
|
64
|
+
return users;
|
|
65
|
+
} catch (error: unknown) {
|
|
66
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
67
|
+
logger.error(`${ERROR_MESSAGES.FETCH_USERS_ERROR}:`, message);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async updateUser(id: string, data: { name?: string, email?: string }) {
|
|
73
|
+
try {
|
|
74
|
+
const user = await this.updateUserUseCase.execute(id, data);
|
|
75
|
+
if (!user) throw new Error(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
76
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
77
|
+
await kafkaService.sendMessage('user-topic', JSON.stringify({
|
|
78
|
+
action: KAFKA_ACTIONS.USER_UPDATED,
|
|
79
|
+
payload: { id, email: user.email }
|
|
80
|
+
}), id);
|
|
81
|
+
<%_ } -%>
|
|
82
|
+
return user;
|
|
83
|
+
} catch (error: unknown) {
|
|
84
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
85
|
+
logger.error(`${ERROR_MESSAGES.UPDATE_USER_ERROR}:`, message);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async deleteUser(id: string) {
|
|
91
|
+
try {
|
|
92
|
+
const deleted = await this.deleteUserUseCase.execute(id);
|
|
93
|
+
if (!deleted) throw new Error(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
94
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
95
|
+
await kafkaService.sendMessage('user-topic', JSON.stringify({
|
|
96
|
+
action: KAFKA_ACTIONS.USER_DELETED,
|
|
97
|
+
payload: { id }
|
|
98
|
+
}), id);
|
|
99
|
+
<%_ } -%>
|
|
100
|
+
return true;
|
|
101
|
+
} catch (error: unknown) {
|
|
102
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
103
|
+
logger.error(`${ERROR_MESSAGES.DELETE_USER_ERROR}:`, message);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
<% } else { -%>
|
|
108
|
+
async createUser(req: Request, res: Response, next: NextFunction) {
|
|
109
|
+
try {
|
|
110
|
+
const { name, email, password } = req.body || {};
|
|
111
|
+
let hashedPassword = password;
|
|
112
|
+
<% if (auth && auth !== 'None') { %>
|
|
113
|
+
if (password) {
|
|
114
|
+
hashedPassword = await bcrypt.hash(password, 10);
|
|
115
|
+
}
|
|
116
|
+
<% } %>
|
|
117
|
+
let imageUrl: string | undefined = undefined;
|
|
118
|
+
<% if (includeMulter) { %>
|
|
119
|
+
if (req.file) {
|
|
120
|
+
imageUrl = `/uploads/${req.file.filename}`;
|
|
121
|
+
}
|
|
122
|
+
<% } %>
|
|
123
|
+
|
|
124
|
+
const user = await this.createUserUseCase.execute(name, email <% if (auth && auth !== 'None') { %>, hashedPassword <% } %> <% if (includeMulter) { %>, imageUrl<% } %>);
|
|
125
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
127
|
+
const userId = (user as any).id || (user as any)._id;
|
|
128
|
+
await kafkaService.sendMessage('user-topic', JSON.stringify({
|
|
129
|
+
action: KAFKA_ACTIONS.USER_CREATED,
|
|
130
|
+
payload: { id: userId, email: user.email }
|
|
131
|
+
}), userId?.toString() || '');
|
|
132
|
+
<%_ } -%>
|
|
133
|
+
res.status(HTTP_STATUS.CREATED).json(user);
|
|
134
|
+
} catch (error: unknown) {
|
|
135
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
136
|
+
logger.error(`${ERROR_MESSAGES.CREATE_USER_ERROR}:`, message);
|
|
137
|
+
next(error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async getUsers(req: Request, res: Response, next: NextFunction) {
|
|
142
|
+
try {
|
|
143
|
+
const users = await this.getAllUsersUseCase.execute();
|
|
144
|
+
res.json(users);
|
|
145
|
+
} catch (error: unknown) {
|
|
146
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
147
|
+
logger.error(`${ERROR_MESSAGES.FETCH_USERS_ERROR}:`, message);
|
|
148
|
+
next(error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async updateUser(req: Request, res: Response, next: NextFunction) {
|
|
153
|
+
try {
|
|
154
|
+
const { id } = req.params;
|
|
155
|
+
const { name, email, password } = req.body || {};
|
|
156
|
+
const updateData: any = { name, email };
|
|
157
|
+
<% if (auth && auth !== 'None') { %>
|
|
158
|
+
if (password) {
|
|
159
|
+
updateData.password = await bcrypt.hash(password, 10);
|
|
160
|
+
}
|
|
161
|
+
<% } %>
|
|
162
|
+
|
|
163
|
+
<% if (includeMulter) { %>
|
|
164
|
+
if (req.file) {
|
|
165
|
+
updateData.imageUrl = `/uploads/${req.file.filename}`;
|
|
166
|
+
}
|
|
167
|
+
<% } %>
|
|
168
|
+
|
|
169
|
+
const user = await this.updateUserUseCase.execute(id, updateData);
|
|
170
|
+
if (!user) {
|
|
171
|
+
return res.status(HTTP_STATUS.NOT_FOUND).json({ error: ERROR_MESSAGES.USER_NOT_FOUND });
|
|
172
|
+
}
|
|
173
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
174
|
+
await kafkaService.sendMessage('user-topic', JSON.stringify({
|
|
175
|
+
action: KAFKA_ACTIONS.USER_UPDATED,
|
|
176
|
+
payload: { id, email: user.email }
|
|
177
|
+
}), id);
|
|
178
|
+
<%_ } -%>
|
|
179
|
+
res.json(user);
|
|
180
|
+
} catch (error: unknown) {
|
|
181
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
182
|
+
logger.error(`${ERROR_MESSAGES.UPDATE_USER_ERROR}:`, message);
|
|
183
|
+
next(error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async deleteUser(req: Request, res: Response, next: NextFunction) {
|
|
188
|
+
try {
|
|
189
|
+
const { id } = req.params;
|
|
190
|
+
const deleted = await this.deleteUserUseCase.execute(id);
|
|
191
|
+
if (!deleted) {
|
|
192
|
+
return res.status(HTTP_STATUS.NOT_FOUND).json({ error: ERROR_MESSAGES.USER_NOT_FOUND });
|
|
193
|
+
}
|
|
194
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
195
|
+
await kafkaService.sendMessage('user-topic', JSON.stringify({
|
|
196
|
+
action: KAFKA_ACTIONS.USER_DELETED,
|
|
197
|
+
payload: { id }
|
|
198
|
+
}), id);
|
|
199
|
+
<%_ } -%>
|
|
200
|
+
res.status(HTTP_STATUS.OK).json({ message: 'User deleted successfully' });
|
|
201
|
+
} catch (error: unknown) {
|
|
202
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
203
|
+
logger.error(`${ERROR_MESSAGES.DELETE_USER_ERROR}:`, message);
|
|
204
|
+
next(error);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
<% } -%>
|
|
208
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { gqlContext } from '@/interfaces/graphql/context';
|
|
2
|
+
import { Request } from 'express';
|
|
3
|
+
import { resolvers } from '@/interfaces/graphql/resolvers';
|
|
4
|
+
import { typeDefs } from '@/interfaces/graphql/typeDefs';
|
|
5
|
+
|
|
6
|
+
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
7
|
+
|
|
8
|
+
describe('GraphQL Context', () => {
|
|
9
|
+
it('should exercise GraphQL index entry points', () => {
|
|
10
|
+
expect(resolvers).toBeDefined();
|
|
11
|
+
expect(typeDefs).toBeDefined();
|
|
12
|
+
});
|
|
13
|
+
it('should return context with token when authorization header is present', async () => {
|
|
14
|
+
const mockRequest = {
|
|
15
|
+
headers: {
|
|
16
|
+
authorization: 'Bearer token123',
|
|
17
|
+
},
|
|
18
|
+
} as Request;
|
|
19
|
+
|
|
20
|
+
const context = await gqlContext({ req: mockRequest });
|
|
21
|
+
expect(context.token).toBe('Bearer token123');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should return context with empty token when authorization header is missing', async () => {
|
|
25
|
+
const mockRequest = {
|
|
26
|
+
headers: {},
|
|
27
|
+
} as Request;
|
|
28
|
+
|
|
29
|
+
const context = await gqlContext({ req: mockRequest });
|
|
30
|
+
expect(context.token).toBe('');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Request } from 'express';
|
|
2
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
+
|
|
4
|
+
export interface MyContext {
|
|
5
|
+
token?: string;
|
|
6
|
+
userRepository: UserRepository;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const gqlContext = async ({ req }: { req: Request }): Promise<MyContext> => {
|
|
10
|
+
const token = req.headers.authorization || '';
|
|
11
|
+
const userRepository = new UserRepository();
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
token,
|
|
15
|
+
userRepository
|
|
16
|
+
};
|
|
17
|
+
};
|
package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { userResolvers } from '@/interfaces/graphql/resolvers/user.resolvers';
|
|
2
|
+
|
|
3
|
+
const mockGetUsers = jest.fn().mockResolvedValue([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
|
|
4
|
+
const mockCreateUser = jest.fn().mockResolvedValue({ id: '1', name: 'Jane', email: 'jane@example.com' });
|
|
5
|
+
|
|
6
|
+
jest.mock('@/interfaces/controllers/userController', () => {
|
|
7
|
+
return {
|
|
8
|
+
UserController: jest.fn().mockImplementation(() => ({
|
|
9
|
+
getUsers: (...args: unknown[]) => mockGetUsers(...args),
|
|
10
|
+
createUser: (...args: unknown[]) => mockCreateUser(...args),
|
|
11
|
+
updateUser: jest.fn().mockImplementation((id, data) => Promise.resolve({ id, ...data })),
|
|
12
|
+
deleteUser: jest.fn().mockImplementation(() => Promise.resolve(true))
|
|
13
|
+
}))
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('User Resolvers', () => {
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
jest.clearAllMocks();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('Query.getAllUsers', () => {
|
|
23
|
+
it('should return all users', async () => {
|
|
24
|
+
const result = await userResolvers.Query.getAllUsers();
|
|
25
|
+
expect(result).toEqual([{ id: '1', name: 'John Doe', email: 'john@example.com' }]);
|
|
26
|
+
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('Mutation.createUser', () => {
|
|
31
|
+
it('should create and return a new user', async () => {
|
|
32
|
+
const result = await userResolvers.Mutation.createUser(null, { name: 'Jane', email: 'jane@example.com' });
|
|
33
|
+
expect(result).toEqual({ id: '1', name: 'Jane', email: 'jane@example.com' });
|
|
34
|
+
expect(mockCreateUser).toHaveBeenCalledWith({ name: 'Jane', email: 'jane@example.com' });
|
|
35
|
+
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('Mutation.updateUser', () => {
|
|
40
|
+
it('should update and return the user', async () => {
|
|
41
|
+
const payload = { name: 'Updated' };
|
|
42
|
+
const result = await userResolvers.Mutation.updateUser(null, { id: '1', ...payload });
|
|
43
|
+
expect(result).toMatchObject(payload);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('Mutation.deleteUser', () => {
|
|
48
|
+
it('should delete and return true', async () => {
|
|
49
|
+
const result = await userResolvers.Mutation.deleteUser(null, { id: '1' });
|
|
50
|
+
expect(result).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
54
|
+
describe('User.id', () => {
|
|
55
|
+
it('should return parent.id if available', () => {
|
|
56
|
+
const parent = { id: '123' };
|
|
57
|
+
const result = userResolvers.User.id(parent as { id?: string; _id?: unknown });
|
|
58
|
+
expect(result).toBe('123');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should fallback to parent._id if id is not available', () => {
|
|
62
|
+
const parent = { _id: '456' };
|
|
63
|
+
const result = userResolvers.User.id(parent as { id?: string; _id?: unknown });
|
|
64
|
+
expect(result).toBe('456');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
<%_ } -%>
|
|
68
|
+
});
|
package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { UserController } from '@/interfaces/controllers/userController';
|
|
2
|
+
|
|
3
|
+
const userController = new UserController();
|
|
4
|
+
|
|
5
|
+
export const userResolvers = {
|
|
6
|
+
Query: {
|
|
7
|
+
getAllUsers: async () => {
|
|
8
|
+
const users = await userController.getUsers();
|
|
9
|
+
return users;
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
Mutation: {
|
|
13
|
+
createUser: async (_: unknown, { name, email }: { name: string, email: string }) => {
|
|
14
|
+
const user = await userController.createUser({ name, email });
|
|
15
|
+
return user;
|
|
16
|
+
},
|
|
17
|
+
updateUser: async (_: unknown, { id, name, email }: { id: string, name?: string, email?: string }) => {
|
|
18
|
+
const user = await userController.updateUser(id, { name, email });
|
|
19
|
+
return user;
|
|
20
|
+
},
|
|
21
|
+
deleteUser: async (_: unknown, { id }: { id: string }) => {
|
|
22
|
+
const result = await userController.deleteUser(id);
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
}<%_ if (database === 'MongoDB') { -%>,
|
|
26
|
+
User: {
|
|
27
|
+
id: (parent: { id?: string; _id?: unknown }) => parent.id || parent._id
|
|
28
|
+
}<%_ } %>
|
|
29
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const userTypes = `#graphql
|
|
2
|
+
type User {
|
|
3
|
+
id: ID!
|
|
4
|
+
name: String!
|
|
5
|
+
email: String!
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type Query {
|
|
9
|
+
getAllUsers: [User]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Mutation {
|
|
13
|
+
createUser(name: String!, email: String!): User
|
|
14
|
+
updateUser(id: ID!, name: String, email: String): User
|
|
15
|
+
deleteUser(id: ID!): Boolean
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import request from 'supertest';
|
|
2
|
+
import express, { Express } from 'express';
|
|
3
|
+
import router from '@/interfaces/routes/userRoutes';
|
|
4
|
+
|
|
5
|
+
const mockGetUsers = jest.fn().mockImplementation((req, res) => res.status(200).json([{ id: '1', name: 'John Doe' }]));
|
|
6
|
+
const mockCreateUser = jest.fn().mockImplementation((req, res) => res.status(201).json({ id: '1', name: 'Test' }));
|
|
7
|
+
|
|
8
|
+
jest.mock('@/interfaces/controllers/userController', () => {
|
|
9
|
+
return {
|
|
10
|
+
UserController: jest.fn().mockImplementation(() => ({
|
|
11
|
+
getUsers: (...args: unknown[]) => mockGetUsers(...args),
|
|
12
|
+
createUser: (...args: unknown[]) => mockCreateUser(...args)
|
|
13
|
+
}))
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('UserRoutes', () => {
|
|
18
|
+
let app: Express;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
app = express();
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
app.use('/users', router);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('POST /users should call controller.createUser', async () => {
|
|
27
|
+
await request(app)
|
|
28
|
+
.post('/users')
|
|
29
|
+
.send({ name: 'Test', email: 'test@example.com' });
|
|
30
|
+
|
|
31
|
+
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('GET /users should call controller.getUsers', async () => {
|
|
35
|
+
await request(app)
|
|
36
|
+
.get('/users');
|
|
37
|
+
|
|
38
|
+
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Router, Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { UserController } from '@/interfaces/controllers/userController';
|
|
3
|
+
<% if (auth && auth !== 'None') { %>import { authMiddleware } from '@/middleware/auth';<% } %>
|
|
4
|
+
<% if (includeMulter) { %>import { upload } from '@/middleware/upload';<% } %>
|
|
5
|
+
<% if (auth && auth !== 'None') { %>import authRoutes from './authRoutes';<% } %>
|
|
6
|
+
|
|
7
|
+
const router = Router();
|
|
8
|
+
const userController = new UserController();
|
|
9
|
+
|
|
10
|
+
<% if (auth && auth !== 'None') { %>router.use('/auth', authRoutes);<% } %>
|
|
11
|
+
|
|
12
|
+
router.get('/', (req: Request, res: Response, next: NextFunction) => userController.getUsers(req, res, next));
|
|
13
|
+
router.post('/', <% if (includeMulter) { %>upload.single('image'), <% } %>(req: Request, res: Response, next: NextFunction) => userController.createUser(req, res, next));
|
|
14
|
+
router.patch('/:id', <% if (includeMulter) { %>upload.single('image'), <% } %>(req: Request, res: Response, next: NextFunction) => userController.updateUser(req, res, next));
|
|
15
|
+
router.delete('/:id', (req: Request, res: Response, next: NextFunction) => userController.deleteUser(req, res, next));
|
|
16
|
+
|
|
17
|
+
export default router;
|
|
18
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import CreateUser from '@/usecases/createUser';
|
|
2
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
+
<%_ if (caching !== 'None') { -%>
|
|
4
|
+
import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
|
|
5
|
+
<%_ } -%>
|
|
6
|
+
|
|
7
|
+
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
8
|
+
<%_ if (caching !== 'None') { -%>
|
|
9
|
+
jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
|
|
10
|
+
get: jest.fn(),
|
|
11
|
+
set: jest.fn(),
|
|
12
|
+
del: jest.fn()
|
|
13
|
+
}));
|
|
14
|
+
<%_ } -%>
|
|
15
|
+
|
|
16
|
+
describe('CreateUser UseCase', () => {
|
|
17
|
+
let createUser: CreateUser;
|
|
18
|
+
let mockUserRepository: jest.Mocked<UserRepository>;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mockUserRepository = new UserRepository() as jest.Mocked<UserRepository>;
|
|
22
|
+
createUser = new CreateUser(mockUserRepository);
|
|
23
|
+
jest.clearAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should create and save a new user', async () => {
|
|
27
|
+
const name = 'Test User';
|
|
28
|
+
const email = 'test@example.com';
|
|
29
|
+
const expectedResult = { id: 1, name, email };
|
|
30
|
+
|
|
31
|
+
mockUserRepository.save.mockResolvedValue(expectedResult as any);
|
|
32
|
+
|
|
33
|
+
const result = await createUser.execute(name, email);
|
|
34
|
+
|
|
35
|
+
expect(mockUserRepository.save).toHaveBeenCalledTimes(1);
|
|
36
|
+
const savedUser = mockUserRepository.save.mock.calls[0][0];
|
|
37
|
+
expect(savedUser.name).toBe(name);
|
|
38
|
+
expect(savedUser.email).toBe(email);
|
|
39
|
+
expect(result).toEqual(expectedResult);
|
|
40
|
+
<%_ if (caching !== 'None') { -%>
|
|
41
|
+
expect(cacheService.del).toHaveBeenCalledWith('users:all');
|
|
42
|
+
<%_ } %>
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should throw an error if repository fails', async () => {
|
|
46
|
+
const error = new Error('Database error');
|
|
47
|
+
mockUserRepository.save.mockRejectedValue(error);
|
|
48
|
+
|
|
49
|
+
await expect(createUser.execute('Test', 'test@test.com')).rejects.toThrow(error);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import User from '../domain/user';
|
|
2
|
+
import { UserRepository } from '../infrastructure/repositories/UserRepository';
|
|
3
|
+
|
|
4
|
+
export default class CreateUser {
|
|
5
|
+
constructor(private userRepository: UserRepository) {}
|
|
6
|
+
|
|
7
|
+
async execute(name: string, email: string<% if (auth && auth !== 'None') { %>, password?: string<% } %><% if (includeMulter) { %>, imageUrl?: string<% } %>): Promise<User> {
|
|
8
|
+
const user = new User(null, name, email<% if (auth && auth !== 'None') { %>, password<% } %><% if (includeMulter) { %>, imageUrl<% } %>);
|
|
9
|
+
return await this.userRepository.save(user);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import DeleteUser from '@/usecases/deleteUser';
|
|
2
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
+
<%_ if (caching !== 'None') { -%>
|
|
4
|
+
import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
|
|
5
|
+
<%_ } -%>
|
|
6
|
+
|
|
7
|
+
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
8
|
+
<%_ if (caching !== 'None') { -%>
|
|
9
|
+
jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
|
|
10
|
+
get: jest.fn(),
|
|
11
|
+
set: jest.fn(),
|
|
12
|
+
del: jest.fn()
|
|
13
|
+
}));
|
|
14
|
+
<%_ } -%>
|
|
15
|
+
|
|
16
|
+
describe('DeleteUser UseCase', () => {
|
|
17
|
+
let deleteUser: DeleteUser;
|
|
18
|
+
let mockUserRepository: jest.Mocked<UserRepository>;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mockUserRepository = new UserRepository() as jest.Mocked<UserRepository>;
|
|
22
|
+
deleteUser = new DeleteUser(mockUserRepository);
|
|
23
|
+
jest.clearAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should delete and return the user', async () => {
|
|
27
|
+
const id = 1;
|
|
28
|
+
const expectedResult = { id, name: 'Deleted User', email: 'test@test.com' };
|
|
29
|
+
|
|
30
|
+
mockUserRepository.delete.mockResolvedValue(expectedResult as any);
|
|
31
|
+
|
|
32
|
+
const result = await deleteUser.execute(id);
|
|
33
|
+
|
|
34
|
+
expect(mockUserRepository.delete).toHaveBeenCalledWith(id);
|
|
35
|
+
expect(result).toEqual(expectedResult);
|
|
36
|
+
<%_ if (caching !== 'None') { -%>
|
|
37
|
+
expect(cacheService.del).toHaveBeenCalledWith('users:all');
|
|
38
|
+
<%_ } %>
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should throw an error if repository fails', async () => {
|
|
42
|
+
const error = new Error('Database error');
|
|
43
|
+
mockUserRepository.delete.mockRejectedValue(error);
|
|
44
|
+
|
|
45
|
+
await expect(deleteUser.execute(1)).rejects.toThrow(error);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import GetAllUsers from '@/usecases/getAllUsers';
|
|
2
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
+
<%_ if (caching !== 'None') { -%>
|
|
4
|
+
import cacheService from '<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>';
|
|
5
|
+
<%_ } -%>
|
|
6
|
+
|
|
7
|
+
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
8
|
+
<%_ if (caching !== 'None') { -%>
|
|
9
|
+
jest.mock('<% if (caching === "Redis") { %>@/infrastructure/caching/redisClient<% } else { %>@/infrastructure/caching/memoryCache<% } %>', () => ({
|
|
10
|
+
get: jest.fn(),
|
|
11
|
+
set: jest.fn(),
|
|
12
|
+
del: jest.fn()
|
|
13
|
+
}));
|
|
14
|
+
<%_ } -%>
|
|
15
|
+
|
|
16
|
+
describe('GetAllUsers UseCase', () => {
|
|
17
|
+
let getAllUsers: GetAllUsers;
|
|
18
|
+
let mockUserRepository: jest.Mocked<UserRepository>;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mockUserRepository = new UserRepository() as jest.Mocked<UserRepository>;
|
|
22
|
+
getAllUsers = new GetAllUsers(mockUserRepository);
|
|
23
|
+
jest.clearAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should retrieve all users', async () => {
|
|
27
|
+
const expectedUsers = [{ id: 1, name: 'Alice', email: 'alice@example.com' }];
|
|
28
|
+
mockUserRepository.getUsers.mockResolvedValue(expectedUsers as any);
|
|
29
|
+
<%_ if (caching !== 'None') { -%>
|
|
30
|
+
(cacheService.get as jest.Mock).mockResolvedValue(null);
|
|
31
|
+
<%_ } %>
|
|
32
|
+
|
|
33
|
+
const result = await getAllUsers.execute();
|
|
34
|
+
|
|
35
|
+
expect(mockUserRepository.getUsers).toHaveBeenCalledTimes(1);
|
|
36
|
+
expect(result).toEqual(expectedUsers);
|
|
37
|
+
<%_ if (caching !== 'None') { -%>
|
|
38
|
+
expect(cacheService.set).toHaveBeenCalled();
|
|
39
|
+
<%_ } %>
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
<%_ if (caching !== 'None') { -%>
|
|
43
|
+
it('should return from cache if available', async () => {
|
|
44
|
+
const cachedUsers = [{ id: 1, name: 'Cached', email: 'cached@example.com' }];
|
|
45
|
+
(cacheService.get as jest.Mock).mockResolvedValue(cachedUsers);
|
|
46
|
+
|
|
47
|
+
const result = await getAllUsers.execute();
|
|
48
|
+
|
|
49
|
+
expect(mockUserRepository.getUsers).not.toHaveBeenCalled();
|
|
50
|
+
expect(result).toEqual(cachedUsers);
|
|
51
|
+
});
|
|
52
|
+
<%_ } -%>
|
|
53
|
+
|
|
54
|
+
it('should throw an error if repository fails', async () => {
|
|
55
|
+
const error = new Error('Database error');
|
|
56
|
+
mockUserRepository.getUsers.mockRejectedValue(error);
|
|
57
|
+
<%_ if (caching !== 'None') { -%>
|
|
58
|
+
(cacheService.get as jest.Mock).mockResolvedValue(null);
|
|
59
|
+
<%_ } -%>
|
|
60
|
+
|
|
61
|
+
await expect(getAllUsers.execute()).rejects.toThrow(error);
|
|
62
|
+
});
|
|
63
|
+
});
|