nodejs-quickstart-structure 2.0.0 → 2.1.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/CHANGELOG.md +26 -0
- package/README.md +44 -40
- package/bin/index.js +6 -3
- package/lib/generator.js +10 -4
- package/lib/modules/app-setup.js +76 -6
- package/lib/modules/auth-setup.js +143 -0
- package/lib/modules/caching-setup.js +8 -1
- package/lib/modules/config-files.js +10 -0
- package/lib/modules/database-setup.js +2 -1
- package/lib/modules/project-setup.js +1 -0
- package/lib/prompts.js +40 -1
- package/package.json +5 -4
- package/templates/clean-architecture/js/src/domain/models/User.js +3 -1
- package/templates/clean-architecture/js/src/index.js.ejs +2 -0
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +12 -3
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +25 -2
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +27 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +3 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.spec.js.ejs +49 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.spec.js.ejs +14 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +41 -4
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +69 -4
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -6
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +38 -21
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +10 -5
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +1 -1
- package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +15 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +4 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +34 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +3 -2
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js.ejs +27 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js.ejs +36 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +14 -0
- package/templates/clean-architecture/js/src/usecases/GetUserById.js.ejs +36 -0
- package/templates/clean-architecture/js/src/usecases/GetUserById.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +28 -0
- package/templates/clean-architecture/js/src/utils/errorMessages.js +1 -0
- package/templates/clean-architecture/js/src/utils/httpCodes.js +2 -0
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +12 -3
- package/templates/clean-architecture/ts/src/domain/user.ts +3 -1
- package/templates/clean-architecture/ts/src/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +55 -9
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +32 -3
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +26 -6
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +57 -15
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +38 -23
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +14 -8
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +33 -10
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +15 -5
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +1 -1
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +9 -1
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +16 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +3 -2
- package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +35 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +1 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts.ejs +24 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts.ejs +21 -0
- package/templates/clean-architecture/ts/src/usecases/getUserById.spec.ts.ejs +47 -0
- package/templates/clean-architecture/ts/src/usecases/getUserById.ts.ejs +23 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +1 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +25 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +1 -0
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +1 -0
- package/templates/common/.cursorrules.ejs +9 -0
- package/templates/common/.env.example.ejs +17 -10
- package/templates/common/.gitlab-ci.yml.ejs +3 -1
- package/templates/common/Jenkinsfile.ejs +10 -1
- package/templates/common/README.md.ejs +64 -19
- package/templates/common/_circleci/config.yml.ejs +96 -0
- package/templates/common/_github/workflows/ci.yml.ejs +1 -1
- package/templates/common/auth/js/controllers/authController.js.ejs +168 -0
- package/templates/common/auth/js/controllers/authController.spec.js.ejs +148 -0
- package/templates/common/auth/js/middleware/authMiddleware.js.ejs +58 -0
- package/templates/common/auth/js/middleware/authMiddleware.spec.js.ejs +108 -0
- package/templates/common/auth/js/routes/authRoutes.js.ejs +16 -0
- package/templates/common/auth/js/services/jwtService.js.ejs +54 -0
- package/templates/common/auth/js/services/jwtService.spec.js.ejs +84 -0
- package/templates/common/auth/ts/controllers/authController.spec.ts.ejs +161 -0
- package/templates/common/auth/ts/controllers/authController.ts.ejs +165 -0
- package/templates/common/auth/ts/middleware/authMiddleware.spec.ts.ejs +128 -0
- package/templates/common/auth/ts/middleware/authMiddleware.ts.ejs +59 -0
- package/templates/common/auth/ts/routes/authRoutes.ts.ejs +20 -0
- package/templates/common/auth/ts/services/jwtService.spec.ts.ejs +89 -0
- package/templates/common/auth/ts/services/jwtService.ts.ejs +60 -0
- package/templates/common/bitbucket-pipelines.yml.ejs +60 -0
- package/templates/common/caching/clean/js/CreateUser.js.ejs +14 -5
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +2 -1
- package/templates/common/caching/clean/js/GetUserById.js.ejs +39 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +2 -1
- package/templates/common/caching/clean/ts/createUser.ts.ejs +14 -6
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +2 -1
- package/templates/common/caching/clean/ts/getUserById.ts.ejs +32 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +2 -2
- package/templates/common/database/js/models/User.js.ejs +14 -1
- package/templates/common/database/js/models/User.js.mongoose.ejs +7 -0
- package/templates/common/database/js/models/User.spec.js.ejs +12 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +10 -0
- package/templates/common/database/ts/models/User.ts.ejs +17 -0
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +8 -0
- package/templates/common/docker-compose.yml.ejs +14 -0
- package/templates/common/ecosystem.config.js.ejs +9 -3
- package/templates/common/eslint.config.mjs.ejs +3 -0
- package/templates/common/jest.config.js.ejs +11 -9
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +1 -1
- package/templates/common/kafka/js/services/kafkaService.js.ejs +1 -1
- package/templates/common/migrations/init.js.ejs +5 -4
- package/templates/common/package.json.ejs +10 -2
- package/templates/common/prompts/project-context.md.ejs +8 -1
- package/templates/common/scripts/run-e2e.js.ejs +26 -10
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +149 -107
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +88 -47
- package/templates/common/swagger.yml.ejs +148 -0
- package/templates/common/tsconfig.eslint.json +15 -0
- package/templates/common/tsconfig.json +2 -1
- package/templates/common/views/ejs/index.ejs +264 -30
- package/templates/common/views/ejs/login.ejs.ejs +244 -0
- package/templates/common/views/ejs/signup.ejs.ejs +282 -0
- package/templates/common/views/pug/index.pug +269 -38
- package/templates/common/views/pug/login.pug.ejs +195 -0
- package/templates/common/views/pug/signup.pug.ejs +241 -0
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +6 -0
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +6 -0
- package/templates/mvc/js/src/config/env.js.ejs +12 -3
- package/templates/mvc/js/src/controllers/userController.js.ejs +29 -5
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +27 -12
- package/templates/mvc/js/src/graphql/context.js.ejs +14 -3
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +36 -21
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +10 -5
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +32 -10
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +1 -1
- package/templates/mvc/js/src/index.js.ejs +16 -3
- package/templates/mvc/js/src/routes/api.js.ejs +14 -0
- package/templates/mvc/js/src/routes/api.spec.js.ejs +3 -0
- package/templates/mvc/js/src/utils/errorMessages.js +1 -0
- package/templates/mvc/js/src/utils/httpCodes.js +1 -0
- package/templates/mvc/ts/src/config/env.ts.ejs +12 -3
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +95 -7
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +68 -11
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +36 -23
- package/templates/mvc/ts/src/graphql/context.ts.ejs +15 -6
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +32 -10
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +15 -5
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +1 -1
- package/templates/mvc/ts/src/index.ts.ejs +15 -3
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +6 -0
- package/templates/mvc/ts/src/routes/api.ts.ejs +15 -0
- package/templates/mvc/ts/src/utils/errorMessages.ts +1 -0
- package/templates/mvc/ts/src/utils/httpCodes.ts +1 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.js +0 -12
- package/templates/clean-architecture/js/src/usecases/CreateUser.js +0 -14
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js +0 -11
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +0 -12
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js +0 -11
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +0 -13
- package/templates/clean-architecture/ts/src/usecases/createUser.ts +0 -13
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +0 -9
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +0 -10
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts +0 -9
- package/templates/mvc/js/src/routes/api.js +0 -10
- package/templates/mvc/ts/src/routes/api.ts +0 -12
package/lib/prompts.js
CHANGED
|
@@ -74,7 +74,7 @@ export const getProjectDetails = async (options = {}) => {
|
|
|
74
74
|
type: 'select',
|
|
75
75
|
name: 'ciProvider',
|
|
76
76
|
message: 'Select CI/CD Provider:',
|
|
77
|
-
choices: ['None', 'GitHub Actions', 'Jenkins', 'GitLab CI'],
|
|
77
|
+
choices: ['None', 'GitHub Actions', 'Jenkins', 'GitLab CI', 'CircleCI', 'Bitbucket Pipelines'],
|
|
78
78
|
default: 'None',
|
|
79
79
|
when: !options.ciProvider
|
|
80
80
|
},
|
|
@@ -85,6 +85,25 @@ export const getProjectDetails = async (options = {}) => {
|
|
|
85
85
|
choices: ['No', 'Yes'],
|
|
86
86
|
default: "No",
|
|
87
87
|
when: (answers) => options.includeSecurity === undefined && (options.ciProvider || answers.ciProvider) !== 'None'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'select',
|
|
91
|
+
name: 'advancedOptions',
|
|
92
|
+
message: 'Enable Advanced Options (Authentication, etc.)?',
|
|
93
|
+
choices: ['No', 'Yes'],
|
|
94
|
+
default: 'No',
|
|
95
|
+
when: options.advancedOptions === undefined
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
type: 'select',
|
|
99
|
+
name: 'auth',
|
|
100
|
+
message: 'Select Authentication Mode:',
|
|
101
|
+
choices: ['None', 'JWT'],
|
|
102
|
+
default: 'None',
|
|
103
|
+
when: (answers) => {
|
|
104
|
+
const advanced = options.advancedOptions !== undefined ? options.advancedOptions : answers.advancedOptions;
|
|
105
|
+
return (advanced === 'Yes' || advanced === true) && !options.auth;
|
|
106
|
+
}
|
|
88
107
|
}
|
|
89
108
|
];
|
|
90
109
|
|
|
@@ -96,5 +115,25 @@ export const getProjectDetails = async (options = {}) => {
|
|
|
96
115
|
result.includeSecurity = result.includeSecurity === 'Yes';
|
|
97
116
|
}
|
|
98
117
|
|
|
118
|
+
if (typeof result.advancedOptions === 'string') {
|
|
119
|
+
result.advancedOptions = result.advancedOptions === 'Yes';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Normalize auth to array if it's a string from the list prompt
|
|
123
|
+
if (typeof result.auth === 'string') {
|
|
124
|
+
result.auth = [result.auth];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Default auth if not provided
|
|
128
|
+
if (!result.auth) {
|
|
129
|
+
result.auth = ['None'];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Filter out 'None' if 'JWT' is selected
|
|
133
|
+
if (result.auth.includes('JWT')) {
|
|
134
|
+
result.auth = result.auth.filter(a => a !== 'None');
|
|
135
|
+
}
|
|
136
|
+
|
|
99
137
|
return result;
|
|
100
138
|
};
|
|
139
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodejs-quickstart-structure",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The ultimate nodejs quickstart structure CLI to scaffold Node.js microservices with MVC or Clean Architecture",
|
|
6
6
|
"main": "bin/index.js",
|
|
@@ -63,11 +63,12 @@
|
|
|
63
63
|
"inquirer": "^13.3.2"
|
|
64
64
|
},
|
|
65
65
|
"overrides": {
|
|
66
|
-
"esbuild": "^0.25.0"
|
|
66
|
+
"esbuild": "^0.25.0",
|
|
67
|
+
"vite": "^6.4.2"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
69
70
|
"snyk": "^1.1303.2",
|
|
70
|
-
"vitepress": "^1.
|
|
71
|
+
"vitepress": "^1.6.4"
|
|
71
72
|
},
|
|
72
73
|
"files": [
|
|
73
74
|
"bin",
|
|
@@ -76,4 +77,4 @@
|
|
|
76
77
|
"README.md",
|
|
77
78
|
"CHANGELOG.md"
|
|
78
79
|
]
|
|
79
|
-
}
|
|
80
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const startServer = require('./infrastructure/webserver/server');
|
|
2
|
+
<% if (database !== 'None' || communication === 'Kafka') { -%>
|
|
2
3
|
const logger = require('./infrastructure/log/logger');
|
|
4
|
+
<% } -%>
|
|
3
5
|
<% if (communication === 'Kafka') { -%>
|
|
4
6
|
const { connectKafka } = require('./infrastructure/messaging/kafkaClient');
|
|
5
7
|
<% } -%>
|
|
@@ -33,15 +33,24 @@ const envSchema = z.object({
|
|
|
33
33
|
<%_ if (communication === 'Kafka') { -%>
|
|
34
34
|
KAFKA_BROKER: z.string(),
|
|
35
35
|
<%_ } -%>
|
|
36
|
+
<%_ if (auth.includes('JWT')) { -%>
|
|
37
|
+
JWT_SECRET: z.string(),
|
|
38
|
+
JWT_EXPIRES_IN: z.string().default('15m'),
|
|
39
|
+
JWT_REFRESH_SECRET: z.string().default('your-secret-refresh-key'),
|
|
40
|
+
JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),
|
|
41
|
+
<%_ } -%>
|
|
36
42
|
});
|
|
37
43
|
|
|
38
44
|
const _env = envSchema.safeParse(process.env);
|
|
39
45
|
|
|
40
46
|
if (!_env.success) {
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
48
|
+
logger.error('❌ Invalid environment variables:', _env.error.format());
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
logger.warn('⚠️ Environment validation failed. Continuing in test mode.');
|
|
43
52
|
}
|
|
44
53
|
|
|
45
|
-
const env = _env.data;
|
|
54
|
+
const env = _env.success ? _env.data : process.env;
|
|
46
55
|
|
|
47
56
|
module.exports = { env };
|
package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs
CHANGED
|
@@ -6,8 +6,17 @@ class UserRepository {
|
|
|
6
6
|
const newUser = await UserModel.create(user);
|
|
7
7
|
return newUser;
|
|
8
8
|
<%_ } else { -%>
|
|
9
|
-
const newUser = await UserModel.create({
|
|
10
|
-
|
|
9
|
+
const newUser = await UserModel.create({
|
|
10
|
+
name: user.name,
|
|
11
|
+
email: user.email,
|
|
12
|
+
<% if (auth.includes('JWT')) { %>password: user.password<% } %>
|
|
13
|
+
});
|
|
14
|
+
<%_ if (auth.includes('JWT')) { -%>
|
|
15
|
+
// Ensure password is not returned in the save result
|
|
16
|
+
const result = { ...user, id: newUser.id || newUser._id.toString() };
|
|
17
|
+
delete result.password;
|
|
18
|
+
return result;
|
|
19
|
+
<%_ } else if (database === 'MongoDB') { -%>
|
|
11
20
|
return { ...user, id: newUser._id.toString() };
|
|
12
21
|
<%_ } else { -%>
|
|
13
22
|
return { ...user, id: newUser.id };
|
|
@@ -15,6 +24,20 @@ class UserRepository {
|
|
|
15
24
|
<%_ } -%>
|
|
16
25
|
}
|
|
17
26
|
|
|
27
|
+
async findById(id) {
|
|
28
|
+
<%_ if (database === 'None') { -%>
|
|
29
|
+
return await UserModel.findByPk(id);
|
|
30
|
+
<%_ } else if (database === 'MongoDB') { -%>
|
|
31
|
+
const user = await UserModel.findById(id);
|
|
32
|
+
if (!user) return null;
|
|
33
|
+
return { id: user._id.toString(), name: user.name, email: user.email };
|
|
34
|
+
<%_ } else { -%>
|
|
35
|
+
const user = await UserModel.findByPk(id);
|
|
36
|
+
if (!user) return null;
|
|
37
|
+
return { id: user.id || 0, name: user.name, email: user.email };
|
|
38
|
+
<%_ } -%>
|
|
39
|
+
}
|
|
40
|
+
|
|
18
41
|
async getUsers() {
|
|
19
42
|
<%_ if (database === 'None') { -%>
|
|
20
43
|
return await UserModel.find();
|
package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs
CHANGED
|
@@ -46,6 +46,33 @@ describe('UserRepository', () => {
|
|
|
46
46
|
});
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
describe('findById', () => {
|
|
50
|
+
it('should return a user if found (Happy Path)', async () => {
|
|
51
|
+
const id = '1';
|
|
52
|
+
const userData = { id, name: 'Test User', email: 'test@example.com' };
|
|
53
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
54
|
+
UserModel.findById.mockResolvedValue({ _id: id, ...userData });
|
|
55
|
+
<%_ } else if (database === 'None') { -%>
|
|
56
|
+
UserModel.findByPk.mockResolvedValue(userData);
|
|
57
|
+
<%_ } else { -%>
|
|
58
|
+
UserModel.findByPk.mockResolvedValue(userData);
|
|
59
|
+
<%_ } -%>
|
|
60
|
+
const result = await userRepository.findById(id);
|
|
61
|
+
expect(result).toEqual(userData);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should return null if user not found (Error Handling)', async () => {
|
|
65
|
+
const id = '999';
|
|
66
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
67
|
+
UserModel.findById.mockResolvedValue(null);
|
|
68
|
+
<%_ } else { -%>
|
|
69
|
+
UserModel.findByPk.mockResolvedValue(null);
|
|
70
|
+
<%_ } -%>
|
|
71
|
+
const result = await userRepository.findById(id);
|
|
72
|
+
expect(result).toBeNull();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
49
76
|
describe('getUsers', () => {
|
|
50
77
|
it('should return a list of mapped UserEntities (Happy Path)', async () => {
|
|
51
78
|
<%_ if (database === 'MongoDB') { -%>
|
|
@@ -10,6 +10,8 @@ const apiRoutes = require('../../interfaces/routes/api');
|
|
|
10
10
|
const swaggerUi = require('swagger-ui-express');
|
|
11
11
|
const swaggerSpecs = require('./swagger');
|
|
12
12
|
<%_ } -%>
|
|
13
|
+
<%_ if (auth.includes('JWT')) { -%>const authRoutes = require('../../interfaces/routes/authRoutes');<%_ } -%>
|
|
14
|
+
|
|
13
15
|
<%_ if (communication === 'GraphQL') { -%>
|
|
14
16
|
const { ApolloServer } = require('@apollo/server');
|
|
15
17
|
const { expressMiddleware } = require('@as-integrations/express4');
|
|
@@ -33,6 +35,7 @@ const startServer = async () => {
|
|
|
33
35
|
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
34
36
|
app.use('/api', apiRoutes);
|
|
35
37
|
<%_ } -%>
|
|
38
|
+
<%_ if (auth.includes('JWT')) { -%>app.use('/api/auth', authRoutes);<%_ } -%>
|
|
36
39
|
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
37
40
|
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
|
|
38
41
|
<%_ } -%>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const startServer = require('@/infrastructure/webserver/server');
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const logger = require('@/infrastructure/log/logger');
|
|
4
|
+
|
|
5
|
+
jest.mock('express', () => {
|
|
6
|
+
const mRouter = {
|
|
7
|
+
get: jest.fn(),
|
|
8
|
+
post: jest.fn(),
|
|
9
|
+
patch: jest.fn(),
|
|
10
|
+
delete: jest.fn(),
|
|
11
|
+
use: jest.fn(),
|
|
12
|
+
};
|
|
13
|
+
const mApp = {
|
|
14
|
+
use: jest.fn(),
|
|
15
|
+
listen: jest.fn((port, cb) => {
|
|
16
|
+
if (cb) cb();
|
|
17
|
+
return { close: jest.fn() };
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
const mExpress = jest.fn(() => mApp);
|
|
21
|
+
mExpress.Router = jest.fn(() => mRouter);
|
|
22
|
+
mExpress.json = jest.fn(() => (req, res, next) => next());
|
|
23
|
+
mExpress.static = jest.fn(() => (req, res, next) => next());
|
|
24
|
+
mExpress.urlencoded = jest.fn(() => (req, res, next) => next());
|
|
25
|
+
return mExpress;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
jest.mock('@/infrastructure/log/logger', () => ({
|
|
29
|
+
info: jest.fn(),
|
|
30
|
+
error: jest.fn(),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
jest.mock('@/utils/gracefulShutdown', () => jest.fn());
|
|
34
|
+
|
|
35
|
+
// Mock standard middlewares as pass-through
|
|
36
|
+
jest.mock('cors', () => () => (req, res, next) => next());
|
|
37
|
+
jest.mock('helmet', () => () => (req, res, next) => next());
|
|
38
|
+
jest.mock('hpp', () => () => (req, res, next) => next());
|
|
39
|
+
jest.mock('express-rate-limit', () => () => (req, res, next) => next());
|
|
40
|
+
jest.mock('morgan', () => () => (req, res, next) => next());
|
|
41
|
+
|
|
42
|
+
describe('Server initialization', () => {
|
|
43
|
+
it('should start the server successfully', async () => {
|
|
44
|
+
await expect(startServer()).resolves.not.toThrow();
|
|
45
|
+
const app = express();
|
|
46
|
+
expect(app.listen).toHaveBeenCalled();
|
|
47
|
+
expect(logger.info).toHaveBeenCalledWith(expect.stringContaining('Server running on port'));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<% if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
2
|
+
const swaggerDocument = require('@/infrastructure/webserver/swagger');
|
|
3
|
+
|
|
4
|
+
jest.mock('yamljs', () => ({
|
|
5
|
+
load: jest.fn().mockReturnValue({ swagger: '2.0', info: { title: 'Test API' } }),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
describe('Swagger Documentation', () => {
|
|
9
|
+
it('should load the swagger document', () => {
|
|
10
|
+
expect(swaggerDocument).toBeDefined();
|
|
11
|
+
expect(swaggerDocument.swagger).toBe('2.0');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
<% } -%>
|
|
@@ -2,6 +2,7 @@ const CreateUser = require('../../usecases/CreateUser');
|
|
|
2
2
|
const GetAllUsers = require('../../usecases/GetAllUsers');
|
|
3
3
|
const UpdateUser = require('../../usecases/UpdateUser');
|
|
4
4
|
const DeleteUser = require('../../usecases/DeleteUser');
|
|
5
|
+
const GetUserById = require('../../usecases/GetUserById');
|
|
5
6
|
const UserRepository = require('../../infrastructure/repositories/UserRepository');
|
|
6
7
|
const ERROR_MESSAGES = require('../../utils/errorMessages');
|
|
7
8
|
<% if (communication !== 'GraphQL') { -%>
|
|
@@ -20,6 +21,7 @@ class UserController {
|
|
|
20
21
|
this.getAllUsersUseCase = new GetAllUsers(this.userRepository);
|
|
21
22
|
this.updateUserUseCase = new UpdateUser(this.userRepository);
|
|
22
23
|
this.deleteUserUseCase = new DeleteUser(this.userRepository);
|
|
24
|
+
this.getUserByIdUseCase = new GetUserById(this.userRepository);
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
<% if (communication === 'GraphQL') { -%>
|
|
@@ -33,9 +35,14 @@ class UserController {
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
async createUser(data) {
|
|
36
|
-
const { name, email } = data;
|
|
38
|
+
const { name, email, password } = data;
|
|
37
39
|
try {
|
|
38
|
-
|
|
40
|
+
<% if (auth.includes('JWT')) { %>
|
|
41
|
+
if (!password) {
|
|
42
|
+
throw new Error('Password is required');
|
|
43
|
+
}
|
|
44
|
+
<% } %>
|
|
45
|
+
const user = await this.createUserUseCase.execute(name, email, password);
|
|
39
46
|
<%_ if (communication === 'Kafka') { -%>
|
|
40
47
|
await sendMessage('user-topic', JSON.stringify({
|
|
41
48
|
action: KAFKA_ACTIONS.USER_CREATED,
|
|
@@ -49,6 +56,17 @@ class UserController {
|
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
async getUserById(id) {
|
|
60
|
+
try {
|
|
61
|
+
const user = await this.getUserByIdUseCase.execute(id);
|
|
62
|
+
if (!user) throw new Error(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
63
|
+
return user;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.error(`${ERROR_MESSAGES.FETCH_USER_ERROR}:`, error);
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
async updateUser(id, data) {
|
|
53
71
|
try {
|
|
54
72
|
const user = await this.updateUserUseCase.execute(id, data);
|
|
@@ -94,9 +112,14 @@ class UserController {
|
|
|
94
112
|
}
|
|
95
113
|
|
|
96
114
|
async createUser(req, res, next) {
|
|
97
|
-
const { name, email } = req.body || {};
|
|
115
|
+
const { name, email, password } = req.body || {};
|
|
98
116
|
try {
|
|
99
|
-
|
|
117
|
+
<% if (auth.includes('JWT')) { %>
|
|
118
|
+
if (!password) {
|
|
119
|
+
return res.status(HTTP_STATUS.BAD_REQUEST).json({ error: 'Password is required' });
|
|
120
|
+
}
|
|
121
|
+
<% } %>
|
|
122
|
+
const user = await this.createUserUseCase.execute(name, email, password);
|
|
100
123
|
<%_ if (communication === 'Kafka') { -%>
|
|
101
124
|
await sendMessage('user-topic', JSON.stringify({
|
|
102
125
|
action: KAFKA_ACTIONS.USER_CREATED,
|
|
@@ -150,6 +173,20 @@ class UserController {
|
|
|
150
173
|
next(error);
|
|
151
174
|
}
|
|
152
175
|
}
|
|
176
|
+
|
|
177
|
+
async getUserById(req, res, next) {
|
|
178
|
+
const { id } = req.params;
|
|
179
|
+
try {
|
|
180
|
+
const user = await this.getUserByIdUseCase.execute(id);
|
|
181
|
+
if (!user) {
|
|
182
|
+
return res.status(HTTP_STATUS.NOT_FOUND).json({ error: ERROR_MESSAGES.USER_NOT_FOUND });
|
|
183
|
+
}
|
|
184
|
+
res.json(user);
|
|
185
|
+
} catch (error) {
|
|
186
|
+
logger.error(`${ERROR_MESSAGES.FETCH_USER_ERROR}:`, error);
|
|
187
|
+
next(error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
153
190
|
<% } -%>
|
|
154
191
|
}
|
|
155
192
|
|
package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const UserController = require('@/interfaces/controllers/userController');
|
|
2
|
+
<% if (communication === 'GraphQL') { -%>
|
|
2
3
|
const ERROR_MESSAGES = require('@/utils/errorMessages');
|
|
4
|
+
<% } -%>
|
|
3
5
|
const CreateUser = require('@/usecases/CreateUser');
|
|
4
6
|
const GetAllUsers = require('@/usecases/GetAllUsers');
|
|
5
7
|
const UpdateUser = require('@/usecases/UpdateUser');
|
|
@@ -9,6 +11,7 @@ jest.mock('@/usecases/CreateUser');
|
|
|
9
11
|
jest.mock('@/usecases/GetAllUsers');
|
|
10
12
|
jest.mock('@/usecases/UpdateUser');
|
|
11
13
|
jest.mock('@/usecases/DeleteUser');
|
|
14
|
+
jest.mock('@/usecases/GetUserById');
|
|
12
15
|
<%_ if (communication === 'Kafka') { -%>
|
|
13
16
|
jest.mock('@/infrastructure/messaging/kafkaClient', () => ({
|
|
14
17
|
sendMessage: jest.fn().mockResolvedValue(undefined)
|
|
@@ -22,6 +25,7 @@ describe('UserController (Clean Architecture)', () => {
|
|
|
22
25
|
let mockGetAllUsersUseCase;
|
|
23
26
|
let mockUpdateUserUseCase;
|
|
24
27
|
let mockDeleteUserUseCase;
|
|
28
|
+
let mockGetUserByIdUseCase;
|
|
25
29
|
<%_ if (communication !== 'GraphQL') { -%>
|
|
26
30
|
let mockRequest;
|
|
27
31
|
let mockResponse;
|
|
@@ -38,6 +42,8 @@ describe('UserController (Clean Architecture)', () => {
|
|
|
38
42
|
mockGetAllUsersUseCase = GetAllUsers.mock.instances[0];
|
|
39
43
|
mockUpdateUserUseCase = UpdateUser.mock.instances[0];
|
|
40
44
|
mockDeleteUserUseCase = DeleteUser.mock.instances[0];
|
|
45
|
+
const GetUserById = require('@/usecases/GetUserById');
|
|
46
|
+
mockGetUserByIdUseCase = GetUserById.mock.instances[0];
|
|
41
47
|
|
|
42
48
|
<%_ if (communication !== 'GraphQL') { -%>
|
|
43
49
|
mockRequest = {
|
|
@@ -82,13 +88,14 @@ describe('UserController (Clean Architecture)', () => {
|
|
|
82
88
|
|
|
83
89
|
describe('createUser', () => {
|
|
84
90
|
it('should successfully create a new user (Happy Path)', async () => {
|
|
85
|
-
const payload = { name: 'Alice', email: 'alice@example.com' };
|
|
91
|
+
const payload = { name: 'Alice', email: 'alice@example.com'<% if (auth.includes('JWT')) { %>, password: 'password123'<% } %> };
|
|
86
92
|
<%_ if (communication === 'GraphQL') { -%>
|
|
87
93
|
const dataArg = payload;
|
|
88
94
|
<%_ } else { -%>
|
|
89
95
|
mockRequest.body = payload;
|
|
90
96
|
<%_ } -%>
|
|
91
97
|
const expectedUser = { id: '1', ...payload };
|
|
98
|
+
if (expectedUser.password) delete expectedUser.password;
|
|
92
99
|
|
|
93
100
|
mockCreateUserUseCase.execute.mockResolvedValue(expectedUser);
|
|
94
101
|
|
|
@@ -105,7 +112,51 @@ describe('UserController (Clean Architecture)', () => {
|
|
|
105
112
|
|
|
106
113
|
expect(mockResponse.json).toHaveBeenCalledWith(expectedUser);
|
|
107
114
|
<%_ } -%>
|
|
108
|
-
expect(mockCreateUserUseCase.execute).toHaveBeenCalledWith(payload.name, payload.email);
|
|
115
|
+
expect(mockCreateUserUseCase.execute).toHaveBeenCalledWith(payload.name, payload.email, payload.password);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
<% if (auth.includes('JWT')) { %>
|
|
119
|
+
it('should fail to create a user when password is missing and JWT is enabled', async () => {
|
|
120
|
+
const payload = { name: 'Alice', email: 'alice@example.com' };
|
|
121
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
122
|
+
await expect(userController.createUser(payload)).rejects.toThrow('Password is required');
|
|
123
|
+
<%_ } else { -%>
|
|
124
|
+
mockRequest.body = payload;
|
|
125
|
+
await userController.createUser(mockRequest, mockResponse, mockNext);
|
|
126
|
+
expect(mockResponse.status).toHaveBeenCalledWith(400);
|
|
127
|
+
expect(mockResponse.json).toHaveBeenCalledWith({ error: 'Password is required' });
|
|
128
|
+
<%_ } -%>
|
|
129
|
+
});
|
|
130
|
+
<% } %>
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('getUserById', () => {
|
|
134
|
+
it('should return a user if found (Happy Path)', async () => {
|
|
135
|
+
const id = '1';
|
|
136
|
+
const userMock = { id, name: 'Test', email: 'test@example.com' };
|
|
137
|
+
mockGetUserByIdUseCase.execute.mockResolvedValue(userMock);
|
|
138
|
+
|
|
139
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
140
|
+
const result = await userController.getUserById(id);
|
|
141
|
+
expect(result).toEqual(userMock);
|
|
142
|
+
<%_ } else { -%>
|
|
143
|
+
mockRequest.params = { id };
|
|
144
|
+
await userController.getUserById(mockRequest, mockResponse, mockNext);
|
|
145
|
+
expect(mockResponse.json).toHaveBeenCalledWith(userMock);
|
|
146
|
+
<%_ } -%>
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should return 404 if user not found (Error Handling)', async () => {
|
|
150
|
+
const id = '999';
|
|
151
|
+
mockGetUserByIdUseCase.execute.mockResolvedValue(null);
|
|
152
|
+
|
|
153
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
154
|
+
await expect(userController.getUserById(id)).rejects.toThrow(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
155
|
+
<%_ } else { -%>
|
|
156
|
+
mockRequest.params = { id };
|
|
157
|
+
await userController.getUserById(mockRequest, mockResponse, mockNext);
|
|
158
|
+
expect(mockResponse.status).toHaveBeenCalledWith(404);
|
|
159
|
+
<%_ } -%>
|
|
109
160
|
});
|
|
110
161
|
});
|
|
111
162
|
|
|
@@ -138,6 +189,19 @@ describe('UserController (Clean Architecture)', () => {
|
|
|
138
189
|
expect(mockUpdateUserUseCase.execute).toHaveBeenCalledWith(id, payload);
|
|
139
190
|
});
|
|
140
191
|
|
|
192
|
+
it('should return 404 if user to update is not found (Error Handling)', async () => {
|
|
193
|
+
const id = '999';
|
|
194
|
+
mockUpdateUserUseCase.execute.mockResolvedValue(null);
|
|
195
|
+
|
|
196
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
197
|
+
await expect(userController.updateUser(id, { name: 'Fail' })).rejects.toThrow(ERROR_MESSAGES.USER_NOT_FOUND);
|
|
198
|
+
<%_ } else { -%>
|
|
199
|
+
mockRequest.params = { id };
|
|
200
|
+
await userController.updateUser(mockRequest, mockResponse, mockNext);
|
|
201
|
+
expect(mockResponse.status).toHaveBeenCalledWith(404);
|
|
202
|
+
<%_ } -%>
|
|
203
|
+
});
|
|
204
|
+
|
|
141
205
|
it('should handle database errors during update (Error Handling)', async () => {
|
|
142
206
|
const id = '1';
|
|
143
207
|
const error = new Error('Database Error');
|
|
@@ -145,6 +209,7 @@ describe('UserController (Clean Architecture)', () => {
|
|
|
145
209
|
<%_ if (communication === 'GraphQL') { -%>
|
|
146
210
|
await expect(userController.updateUser(id, { name: 'Fail' })).rejects.toThrow(error);
|
|
147
211
|
<%_ } else { -%>
|
|
212
|
+
mockRequest.params = { id };
|
|
148
213
|
await userController.updateUser(mockRequest, mockResponse, mockNext);
|
|
149
214
|
expect(mockNext).toHaveBeenCalledWith(error);
|
|
150
215
|
<%_ } -%>
|
|
@@ -207,9 +272,9 @@ describe('UserController (Clean Architecture)', () => {
|
|
|
207
272
|
const error = new Error('Database Error');
|
|
208
273
|
mockCreateUserUseCase.execute.mockRejectedValue(error);
|
|
209
274
|
<%_ if (communication === 'GraphQL') { -%>
|
|
210
|
-
await expect(userController.createUser({ name: 'Alice', email: 'alice@example.com' })).rejects.toThrow(error);
|
|
275
|
+
await expect(userController.createUser({ name: 'Alice', email: 'alice@example.com'<% if (auth.includes('JWT')) { %>, password: 'password123'<% } %> })).rejects.toThrow(error);
|
|
211
276
|
<%_ } else { -%>
|
|
212
|
-
mockRequest.body = { name: 'Alice', email: 'alice@example.com' };
|
|
277
|
+
mockRequest.body = { name: 'Alice', email: 'alice@example.com'<% if (auth.includes('JWT')) { %>, password: 'password123'<% } %> };
|
|
213
278
|
await userController.createUser(mockRequest, mockResponse, mockNext);
|
|
214
279
|
expect(mockNext).toHaveBeenCalledWith(error);
|
|
215
280
|
<%_ } -%>
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
+
<% if (auth.includes('JWT')) { %>const JwtService = require('../../infrastructure/auth/jwtService');<% } %>
|
|
1
2
|
const UserRepository = require('../../infrastructure/repositories/UserRepository');
|
|
2
3
|
|
|
3
4
|
const gqlContext = async ({ req }) => {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
token,
|
|
9
|
-
userRepository
|
|
5
|
+
const context = {
|
|
6
|
+
userRepository: new UserRepository()
|
|
10
7
|
};
|
|
8
|
+
<% if (auth.includes('JWT')) { %>
|
|
9
|
+
const authHeader = req.headers.authorization || '';
|
|
10
|
+
if (authHeader.startsWith('Bearer ')) {
|
|
11
|
+
const token = authHeader.split(' ')[1];
|
|
12
|
+
const user = JwtService.verifyToken(token);
|
|
13
|
+
if (user) {
|
|
14
|
+
context.user = user;
|
|
15
|
+
}
|
|
16
|
+
}<% } %>
|
|
17
|
+
return context;
|
|
11
18
|
};
|
|
12
19
|
|
|
13
20
|
module.exports = { gqlContext };
|
|
@@ -1,31 +1,48 @@
|
|
|
1
|
+
<% if (auth.includes('JWT')) { %>const JwtService = require('@/infrastructure/auth/jwtService');
|
|
2
|
+
jest.mock('@/infrastructure/auth/jwtService');<% } %>
|
|
1
3
|
const { gqlContext } = require('@/interfaces/graphql/context');
|
|
2
|
-
const { resolvers } = require('@/interfaces/graphql/
|
|
3
|
-
const { typeDefs } = require('@/interfaces/graphql/typeDefs');
|
|
4
|
+
const { resolvers } = require('@/interfaces/graphql/index');
|
|
5
|
+
const { typeDefs } = require('@/interfaces/graphql/typeDefs/index');
|
|
4
6
|
|
|
5
7
|
jest.mock('@/infrastructure/repositories/UserRepository');
|
|
6
8
|
|
|
7
9
|
describe('GraphQL Context', () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
expect(typeDefs).toBeDefined();
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
jest.clearAllMocks();
|
|
11
12
|
});
|
|
12
|
-
it('should return context with token when authorization header is present', async () => {
|
|
13
|
-
const mockRequest = {
|
|
14
|
-
headers: {
|
|
15
|
-
authorization: 'Bearer token123',
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
it('should return context with user when authorization header is present and valid', async () => {
|
|
15
|
+
<% if (auth.includes('JWT')) { %>
|
|
16
|
+
const mockUser = { id: '1', email: 'test@test.com' };
|
|
17
|
+
JwtService.verifyToken.mockReturnValue(mockUser);
|
|
18
|
+
const mockRequest = {
|
|
19
|
+
headers: {
|
|
20
|
+
authorization: 'Bearer valid-token',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const context = await gqlContext({ req: mockRequest });
|
|
25
|
+
expect(context.user).toEqual(mockUser);
|
|
26
|
+
expect(JwtService.verifyToken).toHaveBeenCalledWith('valid-token');
|
|
27
|
+
<% } else { %>
|
|
28
|
+
const mockRequest = {
|
|
29
|
+
headers: {
|
|
30
|
+
authorization: 'Bearer token123',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const context = await gqlContext({ req: mockRequest });
|
|
34
|
+
expect(context.user).toBeUndefined();
|
|
35
|
+
<% } %>
|
|
36
|
+
expect(context.userRepository).toBeDefined();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return context without user when authorization header is missing', async () => {
|
|
40
|
+
const mockRequest = {
|
|
41
|
+
headers: {},
|
|
42
|
+
};
|
|
27
43
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
const context = await gqlContext({ req: mockRequest });
|
|
45
|
+
expect(context.user).toBeUndefined();
|
|
46
|
+
expect(context.userRepository).toBeDefined();
|
|
47
|
+
});
|
|
31
48
|
});
|
package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs
CHANGED
|
@@ -4,18 +4,23 @@ const userController = new UserController();
|
|
|
4
4
|
|
|
5
5
|
const userResolvers = {
|
|
6
6
|
Query: {
|
|
7
|
-
getAllUsers: async () => {
|
|
7
|
+
getAllUsers: async (_, __, <% if (auth.includes('JWT')) { %>{ user }<% } else { %>_context<% } %>) => {
|
|
8
|
+
<% if (auth.includes('JWT')) { %>if (!user) throw new Error('Unauthorized');<% } %>
|
|
8
9
|
return await userController.getUsers();
|
|
9
10
|
}
|
|
10
11
|
},
|
|
11
12
|
Mutation: {
|
|
12
|
-
createUser: async (_, { name, email }) => {
|
|
13
|
-
|
|
13
|
+
createUser: async (_, { name, email<% if (auth.some(a => a !=='None')) { %>, password<% } %> }) => {
|
|
14
|
+
// Create user is typically public for registration
|
|
15
|
+
const user = await userController.createUser({ name, email<% if (auth.some(a => a !=='None')) { %>, password<% } %> });
|
|
16
|
+
return user;
|
|
14
17
|
},
|
|
15
|
-
updateUser: async (_, { id, name, email }) => {
|
|
18
|
+
updateUser: async (_, { id, name, email }, <% if (auth.includes('JWT')) { %>{ user }<% } else { %>_context<% } %>) => {
|
|
19
|
+
<% if (auth.includes('JWT')) { %>if (!user) throw new Error('Unauthorized');<% } %>
|
|
16
20
|
return await userController.updateUser(id, { name, email });
|
|
17
21
|
},
|
|
18
|
-
deleteUser: async (_, { id }) => {
|
|
22
|
+
deleteUser: async (_, { id }, <% if (auth.includes('JWT')) { %>{ user }<% } else { %>_context<% } %>) => {
|
|
23
|
+
<% if (auth.includes('JWT')) { %>if (!user) throw new Error('Unauthorized');<% } %>
|
|
19
24
|
return await userController.deleteUser(id);
|
|
20
25
|
}
|
|
21
26
|
}<%_ if (database === 'MongoDB') { -%>,
|