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,21 +1,29 @@
|
|
|
1
|
-
import { UserController } from '@/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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
1
|
+
import { UserController } from '@/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
|
+
};
|
|
@@ -1,15 +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
|
-
|
|
15
|
-
|
|
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
|
+
`;
|
|
@@ -1,154 +1,157 @@
|
|
|
1
|
-
import { env } from '@/config/env';
|
|
2
|
-
import express
|
|
3
|
-
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
app.use(
|
|
48
|
-
|
|
49
|
-
app.use(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
app.use(
|
|
53
|
-
|
|
54
|
-
app.use(
|
|
55
|
-
|
|
56
|
-
<%_ if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
app.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.
|
|
116
|
-
logger.
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
<%
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
1
|
+
import { env } from '@/config/env';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
<% if (viewEngine && viewEngine !== 'None') { -%>
|
|
4
|
+
import type { Request, Response } from 'express';
|
|
5
|
+
<% } -%>
|
|
6
|
+
import cors from 'cors';
|
|
7
|
+
import helmet from 'helmet';
|
|
8
|
+
import hpp from 'hpp';
|
|
9
|
+
import rateLimit from 'express-rate-limit';
|
|
10
|
+
import logger from '@/utils/logger';
|
|
11
|
+
import morgan from 'morgan';
|
|
12
|
+
import { errorMiddleware } from '@/utils/errorMiddleware';
|
|
13
|
+
import { setupGracefulShutdown } from '@/utils/gracefulShutdown';
|
|
14
|
+
import healthRoutes from '@/routes/healthRoute';
|
|
15
|
+
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
16
|
+
import apiRoutes from '@/routes/api';
|
|
17
|
+
import swaggerUi from 'swagger-ui-express';
|
|
18
|
+
import swaggerSpecs from '@/config/swagger';<%_ } %>
|
|
19
|
+
<%_ if (communication === 'Kafka') { -%>import { kafkaService } from '@/services/kafkaService';<%_ } -%>
|
|
20
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
21
|
+
import { ApolloServer } from '@apollo/server';
|
|
22
|
+
import { expressMiddleware } from '@as-integrations/express4';
|
|
23
|
+
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
|
|
24
|
+
import { unwrapResolverError } from '@apollo/server/errors';
|
|
25
|
+
import { ApiError } from '@/errors/ApiError';
|
|
26
|
+
import { typeDefs, resolvers } from '@/graphql';
|
|
27
|
+
import { gqlContext, MyContext } from '@/graphql/context';
|
|
28
|
+
<% } -%>
|
|
29
|
+
|
|
30
|
+
const app = express();
|
|
31
|
+
const port = env.PORT;
|
|
32
|
+
|
|
33
|
+
// Security Middleware
|
|
34
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
35
|
+
app.use(helmet({
|
|
36
|
+
crossOriginEmbedderPolicy: false,
|
|
37
|
+
contentSecurityPolicy: {
|
|
38
|
+
directives: {
|
|
39
|
+
imgSrc: [`'self'`, 'data:', 'apollo-server-landing-page.cdn.apollographql.com'],
|
|
40
|
+
scriptSrc: [`'self'`, `https: 'unsafe-inline'`],
|
|
41
|
+
manifestSrc: [`'self'`, 'apollo-server-landing-page.cdn.apollographql.com'],
|
|
42
|
+
frameSrc: [`'self'`, 'sandbox.embed.apollographql.com'],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}));
|
|
46
|
+
<%_ } else { -%>
|
|
47
|
+
app.use(helmet());
|
|
48
|
+
<%_ } -%>
|
|
49
|
+
app.use(hpp());
|
|
50
|
+
app.use(cors({ origin: '*', methods: ['GET', 'POST', 'PUT', 'DELETE'] }));
|
|
51
|
+
const limiter = rateLimit({ windowMs: 10 * 60 * 1000, max: 100 });
|
|
52
|
+
app.use(limiter);
|
|
53
|
+
|
|
54
|
+
app.use(express.json());
|
|
55
|
+
app.use(morgan('combined', { stream: { write: (message) => logger.info(message.trim()) } }));
|
|
56
|
+
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
57
|
+
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpecs));
|
|
58
|
+
<%_ } -%>
|
|
59
|
+
<%_ if (viewEngine === 'EJS' || viewEngine === 'Pug') { -%>
|
|
60
|
+
// View Engine Setup
|
|
61
|
+
import path from 'path';
|
|
62
|
+
app.set('views', path.join(__dirname, 'views'));
|
|
63
|
+
app.set('view engine', '<%= viewEngine.toLowerCase() %>');
|
|
64
|
+
app.use(express.static(path.join(__dirname, '../public')));<%_ } %>
|
|
65
|
+
<%_ if (communication === 'REST APIs' || communication === 'Kafka') { -%>
|
|
66
|
+
app.use('/api', apiRoutes);
|
|
67
|
+
<%_ } -%><% if (viewEngine && viewEngine !== 'None') { -%>
|
|
68
|
+
app.get('/', (req: Request, res: Response) => {
|
|
69
|
+
res.render('index', {
|
|
70
|
+
projectName: 'NodeJS Service',
|
|
71
|
+
architecture: 'MVC',
|
|
72
|
+
database: '<%= database %>',
|
|
73
|
+
communication: '<%= communication %>'
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
<% } -%>
|
|
77
|
+
app.use('/health', healthRoutes);
|
|
78
|
+
|
|
79
|
+
// Start Server Logic
|
|
80
|
+
const startServer = async () => {
|
|
81
|
+
<%_ if (communication === 'GraphQL') { -%>
|
|
82
|
+
// GraphQL Setup
|
|
83
|
+
const apolloServer = new ApolloServer<MyContext>({
|
|
84
|
+
typeDefs,
|
|
85
|
+
resolvers,
|
|
86
|
+
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })],
|
|
87
|
+
formatError: (formattedError, error) => {
|
|
88
|
+
const originalError = unwrapResolverError(error);
|
|
89
|
+
if (originalError instanceof ApiError) {
|
|
90
|
+
return {
|
|
91
|
+
...formattedError,
|
|
92
|
+
message: originalError.message,
|
|
93
|
+
extensions: {
|
|
94
|
+
...formattedError.extensions,
|
|
95
|
+
code: originalError.statusCode.toString(),
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
logger.error(`GraphQL Error: ${formattedError.message}`);
|
|
101
|
+
if (originalError instanceof Error && originalError.stack && process.env.NODE_ENV === 'development') {
|
|
102
|
+
logger.error(originalError.stack);
|
|
103
|
+
}
|
|
104
|
+
return formattedError;
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
await apolloServer.start();
|
|
108
|
+
app.use('/graphql', expressMiddleware(apolloServer, { context: gqlContext }));
|
|
109
|
+
<%_ } -%>
|
|
110
|
+
app.use(errorMiddleware);
|
|
111
|
+
const server = app.listen(port, () => {
|
|
112
|
+
logger.info(`Server running on port ${port}`);
|
|
113
|
+
<%_ if (communication === 'Kafka') { -%>
|
|
114
|
+
kafkaService.connect()
|
|
115
|
+
.then(async () => {
|
|
116
|
+
logger.info('Kafka connected');
|
|
117
|
+
})
|
|
118
|
+
.catch(err => {
|
|
119
|
+
logger.error('Failed to connect to Kafka after retries:', (err as Error).message);
|
|
120
|
+
});
|
|
121
|
+
<%_ } -%>
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
setupGracefulShutdown(server<% if(communication === 'Kafka') { %>, kafkaService<% } %>);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
<%_ if (database !== 'None') { -%>
|
|
128
|
+
// Database Sync
|
|
129
|
+
<%_ if (database !== 'None') { -%>
|
|
130
|
+
import <% if (database === 'MongoDB') { %>connectDB<% } else { %>sequelize<% } %> from '@/config/database';
|
|
131
|
+
<%_ } -%>
|
|
132
|
+
const syncDatabase = async () => {
|
|
133
|
+
let retries = 30;
|
|
134
|
+
while (retries) {
|
|
135
|
+
try {
|
|
136
|
+
<%_ if (database === 'MongoDB') { -%>
|
|
137
|
+
await connectDB();
|
|
138
|
+
<%_ } else { -%>
|
|
139
|
+
await sequelize.sync();
|
|
140
|
+
<%_ } -%>
|
|
141
|
+
logger.info('Database synced');
|
|
142
|
+
// Start Server after DB is ready
|
|
143
|
+
await startServer();
|
|
144
|
+
break;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
logger.error('Error syncing database:', error);
|
|
147
|
+
retries -= 1;
|
|
148
|
+
logger.info(`Retries left: ${retries}`);
|
|
149
|
+
await new Promise(res => setTimeout(res, 5000));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
syncDatabase();
|
|
155
|
+
<%_ } else { -%>
|
|
156
|
+
startServer();
|
|
154
157
|
<%_ } -%>
|
|
@@ -1,40 +1,59 @@
|
|
|
1
|
-
import request from 'supertest';
|
|
2
|
-
import express, { Express } from 'express';
|
|
3
|
-
import router from '@/routes/api';
|
|
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.
|
|
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
|
-
});
|
|
1
|
+
import request from 'supertest';
|
|
2
|
+
import express, { Express } from 'express';
|
|
3
|
+
import router from '@/routes/api';
|
|
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
|
+
const mockUpdateUser = jest.fn().mockImplementation((req, res) => res.status(200).json({ id: '1', name: 'Updated' }));
|
|
8
|
+
const mockDeleteUser = jest.fn().mockImplementation((req, res) => res.status(204).send());
|
|
9
|
+
|
|
10
|
+
jest.mock('@/controllers/userController', () => {
|
|
11
|
+
return {
|
|
12
|
+
UserController: jest.fn().mockImplementation(() => ({
|
|
13
|
+
getUsers: (...args: unknown[]) => mockGetUsers(...args),
|
|
14
|
+
createUser: (...args: unknown[]) => mockCreateUser(...args),
|
|
15
|
+
updateUser: (...args: unknown[]) => mockUpdateUser(...args),
|
|
16
|
+
deleteUser: (...args: unknown[]) => mockDeleteUser(...args)
|
|
17
|
+
}))
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('ApiRoutes', () => {
|
|
22
|
+
let app: Express;
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
app = express();
|
|
26
|
+
app.use(express.json());
|
|
27
|
+
app.use('/api', router);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('GET /api/users should call controller.getUsers', async () => {
|
|
31
|
+
await request(app)
|
|
32
|
+
.get('/api/users');
|
|
33
|
+
|
|
34
|
+
expect(mockGetUsers).toHaveBeenCalledTimes(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('POST /api/users should call controller.createUser', async () => {
|
|
38
|
+
await request(app)
|
|
39
|
+
.post('/api/users')
|
|
40
|
+
.send({ name: 'Test', email: 'test@example.com' });
|
|
41
|
+
|
|
42
|
+
expect(mockCreateUser).toHaveBeenCalledTimes(1);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('PATCH /api/users/:id should call controller.updateUser', async () => {
|
|
46
|
+
await request(app)
|
|
47
|
+
.patch('/api/users/1')
|
|
48
|
+
.send({ name: 'Updated' });
|
|
49
|
+
|
|
50
|
+
expect(mockUpdateUser).toHaveBeenCalledTimes(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('DELETE /api/users/:id should call controller.deleteUser', async () => {
|
|
54
|
+
await request(app)
|
|
55
|
+
.delete('/api/users/1');
|
|
56
|
+
|
|
57
|
+
expect(mockDeleteUser).toHaveBeenCalledTimes(1);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Router, Request, Response, NextFunction } from 'express';
|
|
2
|
-
import { UserController } from '@/controllers/userController';
|
|
3
|
-
|
|
4
|
-
const router = Router();
|
|
5
|
-
const userController = new UserController();
|
|
6
|
-
|
|
7
|
-
router.get('/users', (req: Request, res: Response, next: NextFunction) => userController.getUsers(req, res, next));
|
|
8
|
-
router.post('/users', (req: Request, res: Response, next: NextFunction) => userController.createUser(req, res, next));
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { Router, Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { UserController } from '@/controllers/userController';
|
|
3
|
+
|
|
4
|
+
const router = Router();
|
|
5
|
+
const userController = new UserController();
|
|
6
|
+
|
|
7
|
+
router.get('/users', (req: Request, res: Response, next: NextFunction) => userController.getUsers(req, res, next));
|
|
8
|
+
router.post('/users', (req: Request, res: Response, next: NextFunction) => userController.createUser(req, res, next));
|
|
9
|
+
router.patch('/users/:id', (req: Request, res: Response, next: NextFunction) => userController.updateUser(req, res, next));
|
|
10
|
+
router.delete('/users/:id', (req: Request, res: Response, next: NextFunction) => userController.deleteUser(req, res, next));
|
|
11
|
+
|
|
12
|
+
export default router;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const ERROR_MESSAGES = {
|
|
2
|
+
USER_NOT_FOUND: 'User not found',
|
|
3
|
+
RESOURCE_NOT_FOUND: 'Resource not found',
|
|
4
|
+
INVALID_USER_DATA: 'Invalid user event data',
|
|
5
|
+
INTERNAL_SERVER_ERROR: 'Internal Server Error',
|
|
6
|
+
BAD_REQUEST: 'Bad Request',
|
|
7
|
+
FETCH_USERS_ERROR: 'Error fetching users',
|
|
8
|
+
CREATE_USER_ERROR: 'Error creating user',
|
|
9
|
+
UPDATE_USER_ERROR: 'Error updating user',
|
|
10
|
+
DELETE_USER_ERROR: 'Error deleting user',
|
|
11
|
+
DATABASE_PING_FAILED: 'Health Check Database Ping Failed',
|
|
12
|
+
} as const;
|
package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.ts.ejs
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { User as UserEntity } from '@/domain/user';
|
|
2
|
-
import UserModel from '@/infrastructure/database/models/User';
|
|
3
|
-
|
|
4
|
-
export class UserRepository {
|
|
5
|
-
async save(user: UserEntity): Promise<UserEntity> {
|
|
6
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
7
|
-
const newUser = await UserModel.create({ name: user.name, email: user.email });
|
|
8
|
-
return { id: newUser._id.toString(), name: newUser.name, email: newUser.email };
|
|
9
|
-
<%_ } else if (database === 'None') { -%>
|
|
10
|
-
const newUser = await UserModel.create(user);
|
|
11
|
-
return newUser;
|
|
12
|
-
<%_ } else { -%>
|
|
13
|
-
const newUser = await UserModel.create({ name: user.name, email: user.email });
|
|
14
|
-
return { id: newUser.id, name: newUser.name, email: newUser.email };
|
|
15
|
-
<%_ } -%>
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async getUsers(): Promise<UserEntity[]> {
|
|
19
|
-
<%_ if (database === 'MongoDB') { -%>
|
|
20
|
-
const users = await UserModel.find();
|
|
21
|
-
return users.map(user => ({
|
|
22
|
-
id: user._id.toString(),
|
|
23
|
-
name: user.name,
|
|
24
|
-
email: user.email
|
|
25
|
-
}));
|
|
26
|
-
<%_ } else if (database === 'None') { -%>
|
|
27
|
-
return await UserModel.find();
|
|
28
|
-
<%_ } else { -%>
|
|
29
|
-
const users = await UserModel.findAll();
|
|
30
|
-
return users.map(user => ({
|
|
31
|
-
id: user.id,
|
|
32
|
-
name: user.name,
|
|
33
|
-
email: user.email
|
|
34
|
-
}));
|
|
35
|
-
<%_ } -%>
|
|
36
|
-
}
|
|
37
|
-
}
|