nodejs-quickstart-structure 1.18.0 → 1.19.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 +17 -4
- package/README.md +2 -1
- package/bin/index.js +93 -92
- package/lib/generator.js +1 -1
- package/lib/modules/caching-setup.js +76 -73
- package/lib/modules/config-files.js +4 -0
- package/lib/modules/kafka-setup.js +249 -191
- package/lib/modules/project-setup.js +1 -0
- package/package.json +13 -2
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -10
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -21
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -10
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -21
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +69 -39
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -81
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +1 -1
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +156 -75
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -138
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -21
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -49
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -17
- package/templates/clean-architecture/js/src/interfaces/routes/api.js +12 -10
- 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/UpdateUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -8
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -21
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -8
- package/templates/clean-architecture/ts/src/index.ts.ejs +1 -1
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -85
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +74 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -185
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +173 -84
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -21
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -15
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts +13 -11
- 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/updateUser.spec.ts.ejs +48 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts +9 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
- package/templates/common/.gitattributes +46 -0
- package/templates/common/.snyk.ejs +45 -0
- package/templates/common/Dockerfile +17 -9
- package/templates/common/README.md.ejs +295 -263
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
- package/templates/common/caching/ts/memoryCache.ts.ejs +73 -64
- package/templates/common/caching/ts/redisClient.ts.ejs +89 -80
- package/templates/common/database/js/models/User.js.ejs +79 -53
- package/templates/common/database/js/models/User.js.mongoose.ejs +23 -19
- package/templates/common/database/js/models/User.spec.js.ejs +94 -84
- package/templates/common/database/ts/models/User.spec.ts.ejs +100 -84
- package/templates/common/database/ts/models/User.ts.ejs +87 -61
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +30 -25
- package/templates/common/health/js/healthRoute.js.ejs +50 -47
- package/templates/common/health/ts/healthRoute.ts.ejs +49 -46
- package/templates/common/jest.e2e.config.js.ejs +8 -8
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -30
- package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -11
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -31
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -49
- package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -93
- package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -51
- package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -11
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -49
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -25
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -95
- package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
- package/templates/common/package.json.ejs +10 -2
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -61
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -160
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -158
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -55
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -49
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -49
- package/templates/common/swagger.yml.ejs +118 -66
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -9
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -9
- package/templates/mvc/js/src/controllers/userController.js.ejs +246 -105
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -209
- package/templates/mvc/js/src/errors/BadRequestError.js +11 -10
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -21
- package/templates/mvc/js/src/errors/NotFoundError.js +11 -10
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -21
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -19
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -47
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -17
- package/templates/mvc/js/src/index.js.ejs +1 -1
- package/templates/mvc/js/src/routes/api.js +10 -8
- package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -36
- package/templates/mvc/js/src/utils/errorMessages.js +14 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -203
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +248 -107
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -21
- package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -8
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -21
- package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -8
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -51
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -21
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -15
- package/templates/mvc/ts/src/index.ts.ejs +156 -153
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -40
- package/templates/mvc/ts/src/routes/api.ts +12 -10
- package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs +0 -37
|
@@ -1,55 +1,59 @@
|
|
|
1
|
-
import { Server } from 'http';
|
|
2
|
-
<%_ if (architecture === 'MVC') { -%>
|
|
3
|
-
import logger from '@/utils/logger';
|
|
4
|
-
<%_ } else { -%>
|
|
5
|
-
import logger from '@/infrastructure/log/logger';
|
|
6
|
-
<%_ } -%>
|
|
7
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
8
|
-
import mongoose from 'mongoose';
|
|
9
|
-
<%_ } else if (database !== 'None') { -%>
|
|
10
|
-
import sequelize from '<% if (architecture === "MVC") { %>@/config/database<% } else { %>@/infrastructure/database/database<% } %>';
|
|
11
|
-
<%_ } -%>
|
|
12
|
-
<%_ if (caching === 'Redis') { -%>
|
|
13
|
-
import redisService from '<% if (architecture === "MVC") { %>@/config/redisClient<% } else { %>@/infrastructure/caching/redisClient<% } %>';
|
|
14
|
-
<%_ } -%>
|
|
15
|
-
|
|
16
|
-
export const setupGracefulShutdown = (server: Server<% if (communication === 'Kafka') { %>, kafkaService: { disconnect: () => Promise<void> }<% } %>) => {
|
|
17
|
-
const gracefulShutdown = async (signal: string) => {
|
|
18
|
-
logger.info(`Received ${signal}. Shutting down gracefully...`);
|
|
19
|
-
server.close(async () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<%_
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<%_ } -%>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<%_ } -%>
|
|
35
|
-
<%_ if (
|
|
36
|
-
await
|
|
37
|
-
logger.info('
|
|
38
|
-
<%_ } -%>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
};
|
|
1
|
+
import { Server } from 'http';
|
|
2
|
+
<%_ if (architecture === 'MVC') { -%>
|
|
3
|
+
import logger from '@/utils/logger';
|
|
4
|
+
<%_ } else { -%>
|
|
5
|
+
import logger from '@/infrastructure/log/logger';
|
|
6
|
+
<%_ } -%>
|
|
7
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
8
|
+
import mongoose from 'mongoose';
|
|
9
|
+
<%_ } else if (database !== 'None') { -%>
|
|
10
|
+
import sequelize from '<% if (architecture === "MVC") { %>@/config/database<% } else { %>@/infrastructure/database/database<% } %>';
|
|
11
|
+
<%_ } -%>
|
|
12
|
+
<%_ if (caching === 'Redis') { -%>
|
|
13
|
+
import redisService from '<% if (architecture === "MVC") { %>@/config/redisClient<% } else { %>@/infrastructure/caching/redisClient<% } %>';
|
|
14
|
+
<%_ } -%>
|
|
15
|
+
|
|
16
|
+
export const setupGracefulShutdown = (server: Server<% if (communication === 'Kafka') { %>, kafkaService: { disconnect: () => Promise<void> }<% } %>) => {
|
|
17
|
+
const gracefulShutdown = async (signal: string) => {
|
|
18
|
+
logger.info(`Received ${signal}. Shutting down gracefully...`);
|
|
19
|
+
server.close(async (err: Error | undefined) => {
|
|
20
|
+
if (err) {
|
|
21
|
+
logger.error('Error closing HTTP server:', err);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
logger.info('HTTP server closed.');
|
|
25
|
+
try {
|
|
26
|
+
<%_ if (database !== 'None') { -%>
|
|
27
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
28
|
+
await mongoose.connection.close(false);
|
|
29
|
+
logger.info('MongoDB connection closed.');
|
|
30
|
+
<%_ } else { -%>
|
|
31
|
+
await sequelize.close();
|
|
32
|
+
logger.info('Database connection closed.');
|
|
33
|
+
<%_ } -%>
|
|
34
|
+
<%_ } -%>
|
|
35
|
+
<%_ if (caching === 'Redis') { -%>
|
|
36
|
+
await redisService.quit();
|
|
37
|
+
logger.info('Redis connection closed.');
|
|
38
|
+
<%_ } -%>
|
|
39
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
40
|
+
await kafkaService.disconnect();
|
|
41
|
+
logger.info('Kafka connection closed.');
|
|
42
|
+
<%_ } -%>
|
|
43
|
+
logger.info('Graceful shutdown fully completed.');
|
|
44
|
+
process.exit(0);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
logger.error('Error during shutdown:', err);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
logger.error('Could not close connections in time, forcefully shutting down');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}, 15000);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
58
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
59
|
+
};
|
|
@@ -1,49 +1,120 @@
|
|
|
1
|
-
const request = require('supertest');
|
|
2
|
-
|
|
3
|
-
const SERVER_URL = process.env.TEST_URL || `http://127.0.0.1:${process.env.PORT || 3001}`;
|
|
4
|
-
|
|
5
|
-
describe('E2E User Tests', () => {
|
|
6
|
-
// Global setup and teardown hooks can be added here
|
|
7
|
-
// typically for database seeding or external authentication checks prior to E2E.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
const request = require('supertest');
|
|
2
|
+
|
|
3
|
+
const SERVER_URL = process.env.TEST_URL || `http://127.0.0.1:${process.env.PORT || 3001}`;
|
|
4
|
+
|
|
5
|
+
describe('E2E User Tests', () => {
|
|
6
|
+
// Global setup and teardown hooks can be added here
|
|
7
|
+
// typically for database seeding or external authentication checks prior to E2E.
|
|
8
|
+
let userId;
|
|
9
|
+
const uniqueEmail = `test_${Date.now()}@example.com`;
|
|
10
|
+
|
|
11
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
12
|
+
it('should create a user via GraphQL', async () => {
|
|
13
|
+
const query = `
|
|
14
|
+
mutation {
|
|
15
|
+
createUser(name: "Test User", email: "${uniqueEmail}") {
|
|
16
|
+
id
|
|
17
|
+
name
|
|
18
|
+
email
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
const response = await request(SERVER_URL)
|
|
23
|
+
.post('/graphql')
|
|
24
|
+
.send({ query });
|
|
25
|
+
|
|
26
|
+
expect(response.statusCode).toBe(200);
|
|
27
|
+
userId = response.body.data.createUser.id;
|
|
28
|
+
expect(userId).toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should update a user via GraphQL', async () => {
|
|
32
|
+
const query = `
|
|
33
|
+
mutation {
|
|
34
|
+
updateUser(id: "${userId}", name: "Updated User") {
|
|
35
|
+
id
|
|
36
|
+
name
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
const response = await request(SERVER_URL)
|
|
41
|
+
.post('/graphql')
|
|
42
|
+
.send({ query });
|
|
43
|
+
|
|
44
|
+
expect(response.statusCode).toBe(200);
|
|
45
|
+
expect(response.body.data.updateUser.name).toBe("Updated User");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should delete a user via GraphQL', async () => {
|
|
49
|
+
const query = `
|
|
50
|
+
mutation {
|
|
51
|
+
deleteUser(id: "${userId}")
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
const response = await request(SERVER_URL)
|
|
55
|
+
.post('/graphql')
|
|
56
|
+
.send({ query });
|
|
57
|
+
|
|
58
|
+
expect(response.statusCode).toBe(200);
|
|
59
|
+
expect(response.body.data.deleteUser).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
<%_ } else if (communication === 'Kafka') { -%>
|
|
62
|
+
it('should trigger Kafka event for user creation', async () => {
|
|
63
|
+
const response = await request(SERVER_URL)
|
|
64
|
+
.post('/api/users')
|
|
65
|
+
.send({ name: 'Test User', email: uniqueEmail });
|
|
66
|
+
|
|
67
|
+
expect([201, 202]).toContain(response.statusCode);
|
|
68
|
+
userId = response.body.id || response.body._id;
|
|
69
|
+
expect(userId).toBeDefined();
|
|
70
|
+
|
|
71
|
+
// Wait for Kafka to process...
|
|
72
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should trigger Kafka event for user update', async () => {
|
|
76
|
+
const response = await request(SERVER_URL)
|
|
77
|
+
.patch(`/api/users/${userId}`)
|
|
78
|
+
.send({ name: 'Updated User' });
|
|
79
|
+
|
|
80
|
+
expect([200, 202, 204]).toContain(response.statusCode);
|
|
81
|
+
|
|
82
|
+
// Wait for Kafka to process...
|
|
83
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should trigger Kafka event for user deletion', async () => {
|
|
87
|
+
const response = await request(SERVER_URL)
|
|
88
|
+
.delete(`/api/users/${userId}`);
|
|
89
|
+
|
|
90
|
+
expect([200, 202, 204]).toContain(response.statusCode);
|
|
91
|
+
|
|
92
|
+
// Wait for Kafka to process...
|
|
93
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
94
|
+
});
|
|
95
|
+
<%_ } else { -%>
|
|
96
|
+
it('should create a user successfully via REST', async () => {
|
|
97
|
+
const response = await request(SERVER_URL)
|
|
98
|
+
.post('/api/users')
|
|
99
|
+
.send({ name: 'Test User', email: uniqueEmail });
|
|
100
|
+
|
|
101
|
+
expect(response.statusCode).toBe(201);
|
|
102
|
+
userId = response.body.id || response.body._id;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should update a user successfully via REST', async () => {
|
|
106
|
+
const response = await request(SERVER_URL)
|
|
107
|
+
.patch(`/api/users/${userId}`)
|
|
108
|
+
.send({ name: 'Updated User' });
|
|
109
|
+
|
|
110
|
+
expect(response.statusCode).toBe(200);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should delete a user successfully via REST', async () => {
|
|
114
|
+
const response = await request(SERVER_URL)
|
|
115
|
+
.delete(`/api/users/${userId}`);
|
|
116
|
+
|
|
117
|
+
expect(response.statusCode).toBe(200);
|
|
118
|
+
});
|
|
119
|
+
<%_ } -%>
|
|
120
|
+
});
|
|
@@ -1,49 +1,120 @@
|
|
|
1
|
-
import request from 'supertest';
|
|
2
|
-
|
|
3
|
-
const SERVER_URL = process.env.TEST_URL || `http://127.0.0.1:${process.env.PORT || 3001}`;
|
|
4
|
-
|
|
5
|
-
describe('E2E User Tests', () => {
|
|
6
|
-
// Global setup and teardown hooks can be added here
|
|
7
|
-
// typically for database seeding or external authentication checks prior to E2E.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
import request from 'supertest';
|
|
2
|
+
|
|
3
|
+
const SERVER_URL = process.env.TEST_URL || `http://127.0.0.1:${process.env.PORT || 3001}`;
|
|
4
|
+
|
|
5
|
+
describe('E2E User Tests', () => {
|
|
6
|
+
// Global setup and teardown hooks can be added here
|
|
7
|
+
// typically for database seeding or external authentication checks prior to E2E.
|
|
8
|
+
let userId: string;
|
|
9
|
+
const uniqueEmail = `test_${Date.now()}@example.com`;
|
|
10
|
+
|
|
11
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
12
|
+
it('should create a user via GraphQL', async () => {
|
|
13
|
+
const query = `
|
|
14
|
+
mutation {
|
|
15
|
+
createUser(name: "Test User", email: "${uniqueEmail}") {
|
|
16
|
+
id
|
|
17
|
+
name
|
|
18
|
+
email
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
const response = await request(SERVER_URL)
|
|
23
|
+
.post('/graphql')
|
|
24
|
+
.send({ query });
|
|
25
|
+
|
|
26
|
+
expect(response.statusCode).toBe(200);
|
|
27
|
+
userId = response.body.data.createUser.id;
|
|
28
|
+
expect(userId).toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should update a user via GraphQL', async () => {
|
|
32
|
+
const query = `
|
|
33
|
+
mutation {
|
|
34
|
+
updateUser(id: "${userId}", name: "Updated User") {
|
|
35
|
+
id
|
|
36
|
+
name
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
const response = await request(SERVER_URL)
|
|
41
|
+
.post('/graphql')
|
|
42
|
+
.send({ query });
|
|
43
|
+
|
|
44
|
+
expect(response.statusCode).toBe(200);
|
|
45
|
+
expect(response.body.data.updateUser.name).toBe("Updated User");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should delete a user via GraphQL', async () => {
|
|
49
|
+
const query = `
|
|
50
|
+
mutation {
|
|
51
|
+
deleteUser(id: "${userId}")
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
const response = await request(SERVER_URL)
|
|
55
|
+
.post('/graphql')
|
|
56
|
+
.send({ query });
|
|
57
|
+
|
|
58
|
+
expect(response.statusCode).toBe(200);
|
|
59
|
+
expect(response.body.data.deleteUser).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
<%_ } else if (communication === 'Kafka') { -%>
|
|
62
|
+
it('should trigger Kafka event for user creation', async () => {
|
|
63
|
+
const response = await request(SERVER_URL)
|
|
64
|
+
.post('/api/users')
|
|
65
|
+
.send({ name: 'Test User', email: uniqueEmail });
|
|
66
|
+
|
|
67
|
+
expect([201, 202]).toContain(response.statusCode);
|
|
68
|
+
userId = response.body.id || response.body._id;
|
|
69
|
+
expect(userId).toBeDefined();
|
|
70
|
+
|
|
71
|
+
// Wait for Kafka to process...
|
|
72
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should trigger Kafka event for user update', async () => {
|
|
76
|
+
const response = await request(SERVER_URL)
|
|
77
|
+
.patch(`/api/users/${userId}`)
|
|
78
|
+
.send({ name: 'Updated User' });
|
|
79
|
+
|
|
80
|
+
expect([200, 202, 204]).toContain(response.statusCode);
|
|
81
|
+
|
|
82
|
+
// Wait for Kafka to process...
|
|
83
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should trigger Kafka event for user deletion', async () => {
|
|
87
|
+
const response = await request(SERVER_URL)
|
|
88
|
+
.delete(`/api/users/${userId}`);
|
|
89
|
+
|
|
90
|
+
expect([200, 202, 204]).toContain(response.statusCode);
|
|
91
|
+
|
|
92
|
+
// Wait for Kafka to process...
|
|
93
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
94
|
+
});
|
|
95
|
+
<%_ } else { -%>
|
|
96
|
+
it('should create a user successfully via REST', async () => {
|
|
97
|
+
const response = await request(SERVER_URL)
|
|
98
|
+
.post('/api/users')
|
|
99
|
+
.send({ name: 'Test User', email: uniqueEmail });
|
|
100
|
+
|
|
101
|
+
expect(response.statusCode).toBe(201);
|
|
102
|
+
userId = response.body.id || response.body._id;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should update a user successfully via REST', async () => {
|
|
106
|
+
const response = await request(SERVER_URL)
|
|
107
|
+
.patch(`/api/users/${userId}`)
|
|
108
|
+
.send({ name: 'Updated User' });
|
|
109
|
+
|
|
110
|
+
expect(response.statusCode).toBe(200);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should delete a user successfully via REST', async () => {
|
|
114
|
+
const response = await request(SERVER_URL)
|
|
115
|
+
.delete(`/api/users/${userId}`);
|
|
116
|
+
|
|
117
|
+
expect(response.statusCode).toBe(200);
|
|
118
|
+
});
|
|
119
|
+
<%_ } -%>
|
|
120
|
+
});
|
|
@@ -1,66 +1,118 @@
|
|
|
1
|
-
openapi: 3.0.0
|
|
2
|
-
info:
|
|
3
|
-
title: <%= projectName %> API
|
|
4
|
-
version: 1.0.0
|
|
5
|
-
description: API documentation for <%= projectName %>
|
|
6
|
-
servers:
|
|
7
|
-
- url: http://localhost:3000
|
|
8
|
-
description: Local Server
|
|
9
|
-
components:
|
|
10
|
-
schemas:
|
|
11
|
-
User:
|
|
12
|
-
type: object
|
|
13
|
-
required:
|
|
14
|
-
- name
|
|
15
|
-
- email
|
|
16
|
-
properties:
|
|
17
|
-
id:
|
|
18
|
-
type: integer
|
|
19
|
-
description: The auto-generated id of the user
|
|
20
|
-
name:
|
|
21
|
-
type: string
|
|
22
|
-
description: The name of the user
|
|
23
|
-
email:
|
|
24
|
-
type: string
|
|
25
|
-
description: The email of the user
|
|
26
|
-
example:
|
|
27
|
-
id: 1
|
|
28
|
-
name: John Doe
|
|
29
|
-
email: john@example.com
|
|
30
|
-
tags:
|
|
31
|
-
- name: Users
|
|
32
|
-
description: The users managing API
|
|
33
|
-
paths:
|
|
34
|
-
/api/users:
|
|
35
|
-
get:
|
|
36
|
-
summary: Returns the list of all the users
|
|
37
|
-
tags:
|
|
38
|
-
- Users
|
|
39
|
-
responses:
|
|
40
|
-
'200':
|
|
41
|
-
description: The list of the users
|
|
42
|
-
content:
|
|
43
|
-
application/json:
|
|
44
|
-
schema:
|
|
45
|
-
type: array
|
|
46
|
-
items:
|
|
47
|
-
$ref: '#/components/schemas/User'
|
|
48
|
-
post:
|
|
49
|
-
summary: Create a new user
|
|
50
|
-
tags:
|
|
51
|
-
- Users
|
|
52
|
-
requestBody:
|
|
53
|
-
required: true
|
|
54
|
-
content:
|
|
55
|
-
application/json:
|
|
56
|
-
schema:
|
|
57
|
-
$ref: '#/components/schemas/User'
|
|
58
|
-
responses:
|
|
59
|
-
'201':
|
|
60
|
-
description: The created user.
|
|
61
|
-
content:
|
|
62
|
-
application/json:
|
|
63
|
-
schema:
|
|
64
|
-
$ref: '#/components/schemas/User'
|
|
65
|
-
'500':
|
|
66
|
-
description: Some server error
|
|
1
|
+
openapi: 3.0.0
|
|
2
|
+
info:
|
|
3
|
+
title: <%= projectName %> API
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
description: API documentation for <%= projectName %>
|
|
6
|
+
servers:
|
|
7
|
+
- url: http://localhost:3000
|
|
8
|
+
description: Local Server
|
|
9
|
+
components:
|
|
10
|
+
schemas:
|
|
11
|
+
User:
|
|
12
|
+
type: object
|
|
13
|
+
required:
|
|
14
|
+
- name
|
|
15
|
+
- email
|
|
16
|
+
properties:
|
|
17
|
+
id:
|
|
18
|
+
type: integer
|
|
19
|
+
description: The auto-generated id of the user
|
|
20
|
+
name:
|
|
21
|
+
type: string
|
|
22
|
+
description: The name of the user
|
|
23
|
+
email:
|
|
24
|
+
type: string
|
|
25
|
+
description: The email of the user
|
|
26
|
+
example:
|
|
27
|
+
id: 1
|
|
28
|
+
name: John Doe
|
|
29
|
+
email: john@example.com
|
|
30
|
+
tags:
|
|
31
|
+
- name: Users
|
|
32
|
+
description: The users managing API
|
|
33
|
+
paths:
|
|
34
|
+
/api/users:
|
|
35
|
+
get:
|
|
36
|
+
summary: Returns the list of all the users
|
|
37
|
+
tags:
|
|
38
|
+
- Users
|
|
39
|
+
responses:
|
|
40
|
+
'200':
|
|
41
|
+
description: The list of the users
|
|
42
|
+
content:
|
|
43
|
+
application/json:
|
|
44
|
+
schema:
|
|
45
|
+
type: array
|
|
46
|
+
items:
|
|
47
|
+
$ref: '#/components/schemas/User'
|
|
48
|
+
post:
|
|
49
|
+
summary: Create a new user
|
|
50
|
+
tags:
|
|
51
|
+
- Users
|
|
52
|
+
requestBody:
|
|
53
|
+
required: true
|
|
54
|
+
content:
|
|
55
|
+
application/json:
|
|
56
|
+
schema:
|
|
57
|
+
$ref: '#/components/schemas/User'
|
|
58
|
+
responses:
|
|
59
|
+
'201':
|
|
60
|
+
description: The created user.
|
|
61
|
+
content:
|
|
62
|
+
application/json:
|
|
63
|
+
schema:
|
|
64
|
+
$ref: '#/components/schemas/User'
|
|
65
|
+
'500':
|
|
66
|
+
description: Some server error
|
|
67
|
+
/api/users/{id}:
|
|
68
|
+
patch:
|
|
69
|
+
summary: Update an existing user
|
|
70
|
+
tags:
|
|
71
|
+
- Users
|
|
72
|
+
parameters:
|
|
73
|
+
- in: path
|
|
74
|
+
name: id
|
|
75
|
+
schema:
|
|
76
|
+
type: string
|
|
77
|
+
required: true
|
|
78
|
+
description: The user id
|
|
79
|
+
requestBody:
|
|
80
|
+
required: true
|
|
81
|
+
content:
|
|
82
|
+
application/json:
|
|
83
|
+
schema:
|
|
84
|
+
type: object
|
|
85
|
+
properties:
|
|
86
|
+
name:
|
|
87
|
+
type: string
|
|
88
|
+
email:
|
|
89
|
+
type: string
|
|
90
|
+
responses:
|
|
91
|
+
'200':
|
|
92
|
+
description: The updated user
|
|
93
|
+
content:
|
|
94
|
+
application/json:
|
|
95
|
+
schema:
|
|
96
|
+
$ref: '#/components/schemas/User'
|
|
97
|
+
'404':
|
|
98
|
+
description: User not found
|
|
99
|
+
'500':
|
|
100
|
+
description: Some server error
|
|
101
|
+
delete:
|
|
102
|
+
summary: Delete an existing user
|
|
103
|
+
tags:
|
|
104
|
+
- Users
|
|
105
|
+
parameters:
|
|
106
|
+
- in: path
|
|
107
|
+
name: id
|
|
108
|
+
schema:
|
|
109
|
+
type: string
|
|
110
|
+
required: true
|
|
111
|
+
description: The user id
|
|
112
|
+
responses:
|
|
113
|
+
'200':
|
|
114
|
+
description: User deleted successfully
|
|
115
|
+
'404':
|
|
116
|
+
description: User not found
|
|
117
|
+
'500':
|
|
118
|
+
description: Some server error
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
CREATE TABLE users (
|
|
2
|
-
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
3
|
-
name VARCHAR(255) NOT NULL,
|
|
4
|
-
email VARCHAR(255) NOT NULL UNIQUE,
|
|
5
|
-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
6
|
-
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
CREATE TABLE users (
|
|
2
|
+
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
3
|
+
name VARCHAR(255) NOT NULL,
|
|
4
|
+
email VARCHAR(255) NOT NULL UNIQUE,
|
|
5
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
6
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
7
|
+
deleted_at TIMESTAMP NULL
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
INSERT INTO users (name, email) VALUES ('Admin User', 'admin@example.com');
|