nodejs-structure-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/bin/index.js +143 -0
- package/lib/generator.js +145 -0
- package/lib/modules/app-setup.js +479 -0
- package/lib/modules/caching-setup.js +76 -0
- package/lib/modules/config-files.js +151 -0
- package/lib/modules/database-setup.js +116 -0
- package/lib/modules/kafka-setup.js +249 -0
- package/lib/modules/project-setup.js +32 -0
- package/lib/prompts.js +128 -0
- package/package.json +66 -0
- package/templates/clean-architecture/js/src/domain/models/User.js.ejs +11 -0
- package/templates/clean-architecture/js/src/errors/ApiError.js +14 -0
- package/templates/clean-architecture/js/src/errors/BadRequestError.js +11 -0
- package/templates/clean-architecture/js/src/errors/BadRequestError.spec.js.ejs +22 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.js +11 -0
- package/templates/clean-architecture/js/src/errors/NotFoundError.spec.js.ejs +22 -0
- package/templates/clean-architecture/js/src/index.js.ejs +56 -0
- package/templates/clean-architecture/js/src/infrastructure/config/env.js.ejs +47 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.js +36 -0
- package/templates/clean-architecture/js/src/infrastructure/log/logger.spec.js.ejs +63 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.js.ejs +88 -0
- package/templates/clean-architecture/js/src/infrastructure/repositories/UserRepository.spec.js.ejs +142 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/middleware/errorMiddleware.js +30 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/server.js.ejs +93 -0
- package/templates/clean-architecture/js/src/infrastructure/webserver/swagger.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.js.ejs +190 -0
- package/templates/clean-architecture/js/src/interfaces/controllers/userController.spec.js.ejs +234 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.js.ejs +13 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/context.spec.js.ejs +31 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/index.js.ejs +5 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/index.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.js.ejs +27 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/resolvers/user.resolvers.spec.js.ejs +66 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/index.js.ejs +6 -0
- package/templates/clean-architecture/js/src/interfaces/graphql/typeDefs/user.types.js.ejs +19 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.js.ejs +17 -0
- package/templates/clean-architecture/js/src/interfaces/routes/api.spec.js.ejs +38 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.js.ejs +14 -0
- package/templates/clean-architecture/js/src/usecases/CreateUser.spec.js.ejs +51 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.js +11 -0
- package/templates/clean-architecture/js/src/usecases/DeleteUser.spec.js.ejs +47 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.js +12 -0
- package/templates/clean-architecture/js/src/usecases/GetAllUsers.spec.js.ejs +61 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.js.ejs +11 -0
- package/templates/clean-architecture/js/src/usecases/UpdateUser.spec.js.ejs +48 -0
- package/templates/clean-architecture/js/src/utils/errorMessages.js +14 -0
- package/templates/clean-architecture/js/src/utils/httpCodes.js +9 -0
- package/templates/clean-architecture/ts/src/config/env.ts.ejs +46 -0
- package/templates/clean-architecture/ts/src/config/swagger.ts.ejs +6 -0
- package/templates/clean-architecture/ts/src/domain/user.ts.ejs +9 -0
- package/templates/clean-architecture/ts/src/errors/ApiError.ts +15 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
- package/templates/clean-architecture/ts/src/errors/BadRequestError.ts +9 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.spec.ts.ejs +22 -0
- package/templates/clean-architecture/ts/src/errors/NotFoundError.ts +9 -0
- package/templates/clean-architecture/ts/src/index.ts.ejs +144 -0
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.spec.ts.ejs +63 -0
- package/templates/clean-architecture/ts/src/infrastructure/log/logger.ts +36 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/UserRepository.spec.ts.ejs +175 -0
- package/templates/clean-architecture/ts/src/infrastructure/repositories/userRepository.ts.ejs +125 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.spec.ts.ejs +331 -0
- package/templates/clean-architecture/ts/src/interfaces/controllers/userController.ts.ejs +208 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.spec.ts.ejs +32 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/context.ts.ejs +17 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/index.ts.ejs +3 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/resolvers/user.resolvers.ts.ejs +29 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/index.ts.ejs +4 -0
- package/templates/clean-architecture/ts/src/interfaces/graphql/typeDefs/user.types.ts.ejs +17 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.spec.ts.ejs +40 -0
- package/templates/clean-architecture/ts/src/interfaces/routes/userRoutes.ts.ejs +18 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.spec.ts.ejs +51 -0
- package/templates/clean-architecture/ts/src/usecases/createUser.ts.ejs +11 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.spec.ts.ejs +47 -0
- package/templates/clean-architecture/ts/src/usecases/deleteUser.ts +9 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.spec.ts.ejs +63 -0
- package/templates/clean-architecture/ts/src/usecases/getAllUsers.ts +10 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.spec.ts.ejs +48 -0
- package/templates/clean-architecture/ts/src/usecases/updateUser.ts.ejs +10 -0
- package/templates/clean-architecture/ts/src/utils/errorMessages.ts +12 -0
- package/templates/clean-architecture/ts/src/utils/errorMiddleware.ts.ejs +27 -0
- package/templates/clean-architecture/ts/src/utils/httpCodes.ts +7 -0
- package/templates/common/.cursorrules.ejs +60 -0
- package/templates/common/.dockerignore +12 -0
- package/templates/common/.env.example.ejs +60 -0
- package/templates/common/.gitattributes +46 -0
- package/templates/common/.gitlab-ci.yml.ejs +86 -0
- package/templates/common/.lintstagedrc +6 -0
- package/templates/common/.prettierrc +7 -0
- package/templates/common/.snyk.ejs +45 -0
- package/templates/common/Dockerfile +73 -0
- package/templates/common/Jenkinsfile.ejs +87 -0
- package/templates/common/README.md.ejs +148 -0
- package/templates/common/_github/workflows/ci.yml.ejs +46 -0
- package/templates/common/_github/workflows/security.yml.ejs +36 -0
- package/templates/common/_gitignore +5 -0
- package/templates/common/_husky/pre-commit +4 -0
- package/templates/common/caching/clean/js/CreateUser.js.ejs +29 -0
- package/templates/common/caching/clean/js/DeleteUser.js.ejs +27 -0
- package/templates/common/caching/clean/js/GetAllUsers.js.ejs +37 -0
- package/templates/common/caching/clean/js/UpdateUser.js.ejs +27 -0
- package/templates/common/caching/clean/ts/createUser.ts.ejs +27 -0
- package/templates/common/caching/clean/ts/deleteUser.ts.ejs +24 -0
- package/templates/common/caching/clean/ts/getAllUsers.ts.ejs +34 -0
- package/templates/common/caching/clean/ts/updateUser.ts.ejs +25 -0
- package/templates/common/caching/js/memoryCache.js.ejs +60 -0
- package/templates/common/caching/js/memoryCache.spec.js.ejs +101 -0
- package/templates/common/caching/js/redisClient.js.ejs +75 -0
- package/templates/common/caching/js/redisClient.spec.js.ejs +147 -0
- package/templates/common/caching/ts/memoryCache.spec.ts.ejs +102 -0
- package/templates/common/caching/ts/memoryCache.ts.ejs +73 -0
- package/templates/common/caching/ts/redisClient.spec.ts.ejs +157 -0
- package/templates/common/caching/ts/redisClient.ts.ejs +89 -0
- package/templates/common/database/js/database.js.ejs +19 -0
- package/templates/common/database/js/database.spec.js.ejs +56 -0
- package/templates/common/database/js/models/User.js.ejs +91 -0
- package/templates/common/database/js/models/User.js.mongoose.ejs +35 -0
- package/templates/common/database/js/models/User.spec.js.ejs +94 -0
- package/templates/common/database/js/mongoose.js.ejs +33 -0
- package/templates/common/database/js/mongoose.spec.js.ejs +43 -0
- package/templates/common/database/ts/database.spec.ts.ejs +56 -0
- package/templates/common/database/ts/database.ts.ejs +21 -0
- package/templates/common/database/ts/models/User.spec.ts.ejs +100 -0
- package/templates/common/database/ts/models/User.ts.ejs +102 -0
- package/templates/common/database/ts/models/User.ts.mongoose.ejs +34 -0
- package/templates/common/database/ts/mongoose.spec.ts.ejs +42 -0
- package/templates/common/database/ts/mongoose.ts.ejs +28 -0
- package/templates/common/docker-compose.yml.ejs +159 -0
- package/templates/common/ecosystem.config.js.ejs +40 -0
- package/templates/common/eslint.config.mjs.ejs +77 -0
- package/templates/common/health/js/healthRoute.js.ejs +50 -0
- package/templates/common/health/js/healthRoute.spec.js.ejs +70 -0
- package/templates/common/health/ts/healthRoute.spec.ts.ejs +76 -0
- package/templates/common/health/ts/healthRoute.ts.ejs +49 -0
- package/templates/common/jest.config.js.ejs +32 -0
- package/templates/common/jest.e2e.config.js.ejs +8 -0
- package/templates/common/kafka/js/config/kafka.js +9 -0
- package/templates/common/kafka/js/config/kafka.spec.js.ejs +27 -0
- package/templates/common/kafka/js/messaging/baseConsumer.js.ejs +30 -0
- package/templates/common/kafka/js/messaging/baseConsumer.spec.js.ejs +58 -0
- package/templates/common/kafka/js/messaging/userEventSchema.js.ejs +12 -0
- package/templates/common/kafka/js/messaging/userEventSchema.spec.js.ejs +27 -0
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.js.ejs +44 -0
- package/templates/common/kafka/js/messaging/welcomeEmailConsumer.spec.js.ejs +86 -0
- package/templates/common/kafka/js/services/kafkaService.js.ejs +93 -0
- package/templates/common/kafka/js/services/kafkaService.spec.js.ejs +106 -0
- package/templates/common/kafka/js/utils/kafkaEvents.js.ejs +7 -0
- package/templates/common/kafka/ts/config/kafka.spec.ts.ejs +27 -0
- package/templates/common/kafka/ts/config/kafka.ts +7 -0
- package/templates/common/kafka/ts/messaging/baseConsumer.spec.ts.ejs +50 -0
- package/templates/common/kafka/ts/messaging/baseConsumer.ts.ejs +27 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.spec.ts.ejs +51 -0
- package/templates/common/kafka/ts/messaging/userEventSchema.ts.ejs +12 -0
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.spec.ts.ejs +86 -0
- package/templates/common/kafka/ts/messaging/welcomeEmailConsumer.ts.ejs +38 -0
- package/templates/common/kafka/ts/services/kafkaService.spec.ts.ejs +81 -0
- package/templates/common/kafka/ts/services/kafkaService.ts.ejs +95 -0
- package/templates/common/kafka/ts/utils/kafkaEvents.ts.ejs +5 -0
- package/templates/common/migrate-mongo-config.js.ejs +31 -0
- package/templates/common/migrations/init.js.ejs +23 -0
- package/templates/common/package.json.ejs +137 -0
- package/templates/common/prompts/add-feature.md.ejs +26 -0
- package/templates/common/prompts/project-context.md.ejs +43 -0
- package/templates/common/prompts/troubleshoot.md.ejs +28 -0
- package/templates/common/public/css/style.css +147 -0
- package/templates/common/scripts/run-e2e.js.ejs +63 -0
- package/templates/common/shutdown/js/gracefulShutdown.js.ejs +65 -0
- package/templates/common/shutdown/js/gracefulShutdown.spec.js.ejs +149 -0
- package/templates/common/shutdown/ts/gracefulShutdown.spec.ts.ejs +179 -0
- package/templates/common/shutdown/ts/gracefulShutdown.ts.ejs +59 -0
- package/templates/common/sonar-project.properties.ejs +27 -0
- package/templates/common/src/config/auth.js.ejs +19 -0
- package/templates/common/src/config/auth.ts.ejs +19 -0
- package/templates/common/src/controllers/authController.js.ejs +101 -0
- package/templates/common/src/controllers/authController.ts.ejs +101 -0
- package/templates/common/src/middleware/auth.js.ejs +20 -0
- package/templates/common/src/middleware/auth.ts.ejs +25 -0
- package/templates/common/src/middleware/upload.js.ejs +31 -0
- package/templates/common/src/middleware/upload.ts.ejs +32 -0
- package/templates/common/src/routes/authRoutes.js.ejs +20 -0
- package/templates/common/src/routes/authRoutes.ts.ejs +20 -0
- package/templates/common/src/tests/e2e/e2e.users.test.js.ejs +120 -0
- package/templates/common/src/tests/e2e/e2e.users.test.ts.ejs +120 -0
- package/templates/common/src/utils/errorMiddleware.spec.js.ejs +79 -0
- package/templates/common/src/utils/errorMiddleware.spec.ts.ejs +94 -0
- package/templates/common/swagger.yml.ejs +118 -0
- package/templates/common/tsconfig.json +23 -0
- package/templates/common/views/ejs/index.ejs +55 -0
- package/templates/common/views/pug/index.pug +40 -0
- package/templates/db/mysql/V1__Initial_Setup.sql.ejs +10 -0
- package/templates/db/postgres/V1__Initial_Setup.sql.ejs +10 -0
- package/templates/mvc/js/src/config/env.js.ejs +46 -0
- package/templates/mvc/js/src/config/swagger.js.ejs +6 -0
- package/templates/mvc/js/src/controllers/userController.js.ejs +288 -0
- package/templates/mvc/js/src/controllers/userController.spec.js.ejs +481 -0
- package/templates/mvc/js/src/errors/ApiError.js +14 -0
- package/templates/mvc/js/src/errors/BadRequestError.js +11 -0
- package/templates/mvc/js/src/errors/BadRequestError.spec.js.ejs +22 -0
- package/templates/mvc/js/src/errors/NotFoundError.js +11 -0
- package/templates/mvc/js/src/errors/NotFoundError.spec.js.ejs +22 -0
- package/templates/mvc/js/src/graphql/context.js.ejs +7 -0
- package/templates/mvc/js/src/graphql/context.spec.js.ejs +29 -0
- package/templates/mvc/js/src/graphql/index.js.ejs +5 -0
- package/templates/mvc/js/src/graphql/resolvers/index.js.ejs +6 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.js.ejs +25 -0
- package/templates/mvc/js/src/graphql/resolvers/user.resolvers.spec.js.ejs +64 -0
- package/templates/mvc/js/src/graphql/typeDefs/index.js.ejs +6 -0
- package/templates/mvc/js/src/graphql/typeDefs/user.types.js.ejs +19 -0
- package/templates/mvc/js/src/index.js.ejs +141 -0
- package/templates/mvc/js/src/routes/api.js.ejs +15 -0
- package/templates/mvc/js/src/routes/api.spec.js.ejs +41 -0
- package/templates/mvc/js/src/utils/errorMessages.js +14 -0
- package/templates/mvc/js/src/utils/errorMiddleware.js +29 -0
- package/templates/mvc/js/src/utils/httpCodes.js +9 -0
- package/templates/mvc/js/src/utils/logger.js +40 -0
- package/templates/mvc/js/src/utils/logger.spec.js.ejs +63 -0
- package/templates/mvc/ts/src/config/env.ts.ejs +45 -0
- package/templates/mvc/ts/src/config/swagger.ts.ejs +6 -0
- package/templates/mvc/ts/src/controllers/userController.spec.ts.ejs +481 -0
- package/templates/mvc/ts/src/controllers/userController.ts.ejs +292 -0
- package/templates/mvc/ts/src/errors/ApiError.ts +15 -0
- package/templates/mvc/ts/src/errors/BadRequestError.spec.ts.ejs +22 -0
- package/templates/mvc/ts/src/errors/BadRequestError.ts +9 -0
- package/templates/mvc/ts/src/errors/NotFoundError.spec.ts.ejs +27 -0
- package/templates/mvc/ts/src/errors/NotFoundError.ts +9 -0
- package/templates/mvc/ts/src/graphql/context.spec.ts.ejs +30 -0
- package/templates/mvc/ts/src/graphql/context.ts.ejs +12 -0
- package/templates/mvc/ts/src/graphql/index.ts.ejs +3 -0
- package/templates/mvc/ts/src/graphql/resolvers/index.ts.ejs +4 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.spec.ts.ejs +68 -0
- package/templates/mvc/ts/src/graphql/resolvers/user.resolvers.ts.ejs +29 -0
- package/templates/mvc/ts/src/graphql/typeDefs/index.ts.ejs +4 -0
- package/templates/mvc/ts/src/graphql/typeDefs/user.types.ts.ejs +17 -0
- package/templates/mvc/ts/src/index.ts.ejs +157 -0
- package/templates/mvc/ts/src/routes/api.spec.ts.ejs +59 -0
- package/templates/mvc/ts/src/routes/api.ts.ejs +17 -0
- package/templates/mvc/ts/src/utils/errorMessages.ts +12 -0
- package/templates/mvc/ts/src/utils/errorMiddleware.ts.ejs +27 -0
- package/templates/mvc/ts/src/utils/httpCodes.ts +7 -0
- package/templates/mvc/ts/src/utils/logger.spec.ts.ejs +63 -0
- package/templates/mvc/ts/src/utils/logger.ts +36 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# <%= projectName %>
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
<% if (language === 'TypeScript') { %><% } else { %><% } %>
|
|
6
|
+
<% if (includeSecurity) { %>
|
|
7
|
+
[](https://snyk.io/)
|
|
8
|
+
[](https://sonarcloud.io/)
|
|
9
|
+
<% } %>
|
|
10
|
+
|
|
11
|
+
A production-ready Node.js microservice generated with **<%= architecture %>** and **<%= database %>**.
|
|
12
|
+
This project follows a strict **7-Step Production-Ready Process** to ensure quality and scalability from day one.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 🚀 7-Step Production-Ready Process
|
|
17
|
+
|
|
18
|
+
1. **Initialize Git**: `git init` (Required for Husky hooks and security gates).
|
|
19
|
+
2. **Install Dependencies**: `npm install`.
|
|
20
|
+
3. **Configure Environment**: Copy `.env.example` to `.env`.
|
|
21
|
+
4. **Start Infrastructure**: `docker-compose up -d<% if (database !== 'None') { %> db<% } %><% if (caching === 'Redis') { %> redis<% } %><% if (communication === 'Kafka') { %> kafka<% } %>`.
|
|
22
|
+
5. **Run Development**: `npm run dev`.
|
|
23
|
+
6. **Verify Standards**: `npm run lint` and `npm test` (Enforce 80% coverage).
|
|
24
|
+
7. **Build & Deploy**: `npm run build` followed by `npm run deploy` (via PM2).
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🚀 Key Features
|
|
29
|
+
|
|
30
|
+
- **Architecture**: <%= architecture %> (<% if (architecture === 'Clean Architecture') { %>Domain, UseCases, Infrastructure<% } else { %>MVC Pattern<% } %>).
|
|
31
|
+
- **Database**: <%= database %> <% if (database !== 'None') { %>(via <%= database === 'MongoDB' ? 'Mongoose' : 'Sequelize' %>)<% } %>.
|
|
32
|
+
- **Security**: Helmet, CORS, Rate Limiting, HPP, Snyk SCA.
|
|
33
|
+
- **Quality**: 80%+ Test Coverage, Eslint, Prettier, Husky.
|
|
34
|
+
- **DevOps**: Multi-stage Docker, CI/CD ready (GitHub/GitLab/Jenkins).
|
|
35
|
+
<% if (includeSecurity) { %>- **Enterprise Hardening**: SonarCloud SAST, Security Policies.<% } %>
|
|
36
|
+
|
|
37
|
+
## 📂 Project Structure
|
|
38
|
+
|
|
39
|
+
The project follows **<%= architecture %>** principles.
|
|
40
|
+
<% if (architecture === 'Clean Architecture') { -%>
|
|
41
|
+
- **Domain**: Pure business logic (Entities/Interfaces).
|
|
42
|
+
- **Use Case**: Application-specific business rules.
|
|
43
|
+
- **Infrastructure**: External concerns (DB, Messaging, Caching).
|
|
44
|
+
<% } else { -%>
|
|
45
|
+
- **Model**: Database schemas and data logic.
|
|
46
|
+
- **View**: Template engines or API responders.
|
|
47
|
+
- **Controller**: Orchestrates flow between Model and View.
|
|
48
|
+
<% } -%>
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 🛠️ Detailed Getting Started
|
|
53
|
+
|
|
54
|
+
Follow the **🚀 7-Step Production-Ready Process** summary at the top, or follow these detailed instructions:
|
|
55
|
+
|
|
56
|
+
### 1. Prerequisites
|
|
57
|
+
- Node.js (v18+)
|
|
58
|
+
- Docker & Docker Compose
|
|
59
|
+
|
|
60
|
+
### 2. Environment Setup
|
|
61
|
+
Copy the example environment file and adjust the values as needed:
|
|
62
|
+
```bash
|
|
63
|
+
cp .env.example .env
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 3. Infrastructure & App Launch
|
|
67
|
+
```bash
|
|
68
|
+
# Initialize Git for security hooks
|
|
69
|
+
git init
|
|
70
|
+
|
|
71
|
+
# Install dependencies
|
|
72
|
+
npm install
|
|
73
|
+
|
|
74
|
+
# Start required services
|
|
75
|
+
docker-compose up -d<% if (database !== 'None') { %> db<% } %><% if (caching === 'Redis') { %> redis<% } %><% if (communication === 'Kafka') { %> kafka<% } %>
|
|
76
|
+
|
|
77
|
+
# Run the app in development mode
|
|
78
|
+
npm run dev
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 4. Quality & Standards
|
|
82
|
+
```bash
|
|
83
|
+
# Lint & Format
|
|
84
|
+
npm run lint
|
|
85
|
+
npm run format
|
|
86
|
+
|
|
87
|
+
# Run Unit/Integration Tests
|
|
88
|
+
npm test
|
|
89
|
+
npm run test:coverage
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
<% if (communication === 'Kafka') { -%>
|
|
93
|
+
Microservices communication handled via **Kafka**.
|
|
94
|
+
<% } else if (communication === 'GraphQL') { -%>
|
|
95
|
+
API is exposed via **GraphQL**.
|
|
96
|
+
The Apollo Sandbox UI for API exploration and documentation is available natively, fully embedded for offline development:
|
|
97
|
+
- **URL**: `http://localhost:3000/graphql` (Dynamic based on PORT)
|
|
98
|
+
If you are opening `http://localhost:3000/graphql` in your browser, you can directly run the following in the Apollo Sandbox UI:
|
|
99
|
+
|
|
100
|
+
**Query to get all users:**
|
|
101
|
+
```graphql
|
|
102
|
+
query GetAllUsers {
|
|
103
|
+
getAllUsers {
|
|
104
|
+
id
|
|
105
|
+
name
|
|
106
|
+
email
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Mutation to create a user:**
|
|
112
|
+
```graphql
|
|
113
|
+
mutation CreateUser {
|
|
114
|
+
createUser(name: "John Doe", email: "john@example.com") {
|
|
115
|
+
id
|
|
116
|
+
name
|
|
117
|
+
email
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
<% } else { -%>
|
|
122
|
+
API is exposed via **REST**.
|
|
123
|
+
A Swagger UI for API documentation is available at:
|
|
124
|
+
- **URL**: `http://localhost:3000/api-docs` (Dynamic based on PORT)
|
|
125
|
+
|
|
126
|
+
### 🛣️ User Endpoints:
|
|
127
|
+
- `GET /api/users`: List all users.
|
|
128
|
+
- `POST /api/users`: Create a new user.
|
|
129
|
+
- `PATCH /api/users/:id`: Partially update a user.
|
|
130
|
+
- `DELETE /api/users/:id`: Delete a user (Soft Delete).
|
|
131
|
+
<%_ } -%>
|
|
132
|
+
|
|
133
|
+
## 🐳 Docker Deployment
|
|
134
|
+
This project uses a **Multi-Stage Dockerfile** for optimized production images.
|
|
135
|
+
|
|
136
|
+
<% if (database !== 'None' || caching === 'Redis' || communication === 'Kafka') { -%>
|
|
137
|
+
### 1. Running Locally (Development)
|
|
138
|
+
To run the Node.js application locally while using Docker for the infrastructure (Database, Redis, Kafka, etc.):
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Start infrastructure
|
|
142
|
+
docker-compose up -d<% if (database !== 'None') { %> db<% } %><% if (caching === 'Redis') { %> redis<% } %><% if (communication === 'Kafka') { %> kafka<% } %>
|
|
143
|
+
|
|
144
|
+
# Start the application
|
|
145
|
+
npm run dev
|
|
146
|
+
```
|
|
147
|
+
<% } -%>
|
|
148
|
+
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Node.js CI
|
|
2
|
+
|
|
3
|
+
env:
|
|
4
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [ "main" ]
|
|
9
|
+
pull_request:
|
|
10
|
+
branches: [ "main" ]
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Use Node.js 22.x
|
|
21
|
+
uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: 22.x
|
|
24
|
+
cache: 'npm'
|
|
25
|
+
|
|
26
|
+
- name: Install Dependencies
|
|
27
|
+
run: npm ci
|
|
28
|
+
|
|
29
|
+
- name: Lint Code
|
|
30
|
+
run: npm run lint
|
|
31
|
+
|
|
32
|
+
- name: Run Unit Tests
|
|
33
|
+
run: npm run test:coverage
|
|
34
|
+
|
|
35
|
+
- name: Run E2E Tests
|
|
36
|
+
run: npm run test:e2e
|
|
37
|
+
|
|
38
|
+
- name: Build
|
|
39
|
+
run: npm run build --if-present
|
|
40
|
+
<% if (includeSecurity) { %>
|
|
41
|
+
- name: SonarQube Scan
|
|
42
|
+
uses: SonarSource/sonarqube-scan-action@master
|
|
43
|
+
env:
|
|
44
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
45
|
+
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
|
46
|
+
<% } %>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Enterprise Security Scan
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "main" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "main" ]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: '0 0 * * 1' # Weekly scan
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
node-security:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Run Snyk to check for vulnerabilities
|
|
18
|
+
uses: snyk/actions/node@master
|
|
19
|
+
env:
|
|
20
|
+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
|
21
|
+
with:
|
|
22
|
+
args: --severity-threshold=high
|
|
23
|
+
|
|
24
|
+
container-security:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- name: Build Docker image
|
|
29
|
+
run: docker build -t <%= projectName %>:latest .
|
|
30
|
+
- name: Run Snyk to check Docker image for vulnerabilities
|
|
31
|
+
uses: snyk/actions/docker@master
|
|
32
|
+
env:
|
|
33
|
+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
|
34
|
+
with:
|
|
35
|
+
image: <%= projectName %>:latest
|
|
36
|
+
args: --file=Dockerfile --severity-threshold=high --skip-unused-projects
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const User = require('../domain/models/User');
|
|
2
|
+
<%_ if (caching === 'Redis') { -%>
|
|
3
|
+
const cacheService = require('../infrastructure/caching/redisClient');
|
|
4
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
5
|
+
const cacheService = require('../infrastructure/caching/memoryCache');
|
|
6
|
+
<%_ } -%>
|
|
7
|
+
const logger = require('../infrastructure/log/logger');
|
|
8
|
+
|
|
9
|
+
class CreateUser {
|
|
10
|
+
constructor(userRepository) {
|
|
11
|
+
this.userRepository = userRepository;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async execute(name, email) {
|
|
15
|
+
const user = new User(null, name, email);
|
|
16
|
+
const savedUser = await this.userRepository.save(user);
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
await cacheService.del('users:all');
|
|
20
|
+
logger.info('Invalidated users:all cache');
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.error('Cache error (del):', error);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return savedUser;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = CreateUser;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<%_ if (caching === 'Redis') { -%>
|
|
2
|
+
const cacheService = require('../infrastructure/caching/redisClient');
|
|
3
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
4
|
+
const cacheService = require('../infrastructure/caching/memoryCache');
|
|
5
|
+
<%_ } -%>
|
|
6
|
+
const logger = require('../infrastructure/log/logger');
|
|
7
|
+
|
|
8
|
+
class DeleteUser {
|
|
9
|
+
constructor(userRepository) {
|
|
10
|
+
this.userRepository = userRepository;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async execute(id) {
|
|
14
|
+
const result = await this.userRepository.delete(id);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await cacheService.del('users:all');
|
|
18
|
+
logger.info('Invalidated users:all cache');
|
|
19
|
+
} catch (error) {
|
|
20
|
+
logger.error('Cache error (del):', error);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = DeleteUser;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<%_ if (caching === 'Redis') { -%>
|
|
2
|
+
const cacheService = require('../infrastructure/caching/redisClient');
|
|
3
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
4
|
+
const cacheService = require('../infrastructure/caching/memoryCache');
|
|
5
|
+
<%_ } -%>
|
|
6
|
+
const logger = require('../infrastructure/log/logger');
|
|
7
|
+
|
|
8
|
+
class GetAllUsers {
|
|
9
|
+
constructor(userRepository) {
|
|
10
|
+
this.userRepository = userRepository;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async execute() {
|
|
14
|
+
const cacheKey = 'users:all';
|
|
15
|
+
try {
|
|
16
|
+
const cachedUsers = await cacheService.get(cacheKey);
|
|
17
|
+
if (cachedUsers) {
|
|
18
|
+
logger.info('Serving users from cache');
|
|
19
|
+
return cachedUsers;
|
|
20
|
+
}
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.error('Cache error (get):', error);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const users = await this.userRepository.getUsers();
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await cacheService.set(cacheKey, users, 60);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.error('Cache error (set):', error);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return users;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = GetAllUsers;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<%_ if (caching === 'Redis') { -%>
|
|
2
|
+
const cacheService = require('../infrastructure/caching/redisClient');
|
|
3
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
4
|
+
const cacheService = require('../infrastructure/caching/memoryCache');
|
|
5
|
+
<%_ } -%>
|
|
6
|
+
const logger = require('../infrastructure/log/logger');
|
|
7
|
+
|
|
8
|
+
class UpdateUser {
|
|
9
|
+
constructor(userRepository) {
|
|
10
|
+
this.userRepository = userRepository;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async execute(id, data) {
|
|
14
|
+
const updatedUser = await this.userRepository.update(id, data);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await cacheService.del('users:all');
|
|
18
|
+
logger.info('Invalidated users:all cache');
|
|
19
|
+
} catch (error) {
|
|
20
|
+
logger.error('Cache error (del):', error);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return updatedUser;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = UpdateUser;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { User } from '@/domain/user';
|
|
2
|
+
|
|
3
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
4
|
+
<%_ if (caching === 'Redis') { -%>
|
|
5
|
+
import cacheService from '@/infrastructure/caching/redisClient';
|
|
6
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
7
|
+
import cacheService from '@/infrastructure/caching/memoryCache';
|
|
8
|
+
<%_ } -%>
|
|
9
|
+
import logger from '@/infrastructure/log/logger';
|
|
10
|
+
|
|
11
|
+
export default class CreateUser {
|
|
12
|
+
constructor(private userRepository: UserRepository) {}
|
|
13
|
+
|
|
14
|
+
async execute(name: string, email: string) {
|
|
15
|
+
const user = new User(null, name, email);
|
|
16
|
+
const savedUser = await this.userRepository.save(user);
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
await cacheService.del('users:all');
|
|
20
|
+
logger.info('Invalidated users:all cache');
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.error('Cache error (del):', error);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return savedUser;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
2
|
+
<%_ if (caching === 'Redis') { -%>
|
|
3
|
+
import cacheService from '@/infrastructure/caching/redisClient';
|
|
4
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
5
|
+
import cacheService from '@/infrastructure/caching/memoryCache';
|
|
6
|
+
<%_ } -%>
|
|
7
|
+
import logger from '@/infrastructure/log/logger';
|
|
8
|
+
|
|
9
|
+
export default class DeleteUser {
|
|
10
|
+
constructor(private userRepository: UserRepository) {}
|
|
11
|
+
|
|
12
|
+
async execute(id: number | string) {
|
|
13
|
+
const result = await this.userRepository.delete(id);
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
await cacheService.del('users:all');
|
|
17
|
+
logger.info('Invalidated users:all cache');
|
|
18
|
+
} catch (error) {
|
|
19
|
+
logger.error('Cache error (del):', error);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
2
|
+
<%_ if (caching === 'Redis') { -%>
|
|
3
|
+
import cacheService from '@/infrastructure/caching/redisClient';
|
|
4
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
5
|
+
import cacheService from '@/infrastructure/caching/memoryCache';
|
|
6
|
+
<%_ } -%>
|
|
7
|
+
import logger from '@/infrastructure/log/logger';
|
|
8
|
+
|
|
9
|
+
export default class GetAllUsers {
|
|
10
|
+
constructor(private userRepository: UserRepository) {}
|
|
11
|
+
|
|
12
|
+
async execute() {
|
|
13
|
+
const cacheKey = 'users:all';
|
|
14
|
+
try {
|
|
15
|
+
const cachedUsers = await cacheService.get(cacheKey);
|
|
16
|
+
if (cachedUsers) {
|
|
17
|
+
logger.info('Serving users from cache');
|
|
18
|
+
return cachedUsers;
|
|
19
|
+
}
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger.error('Cache error (get):', error);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const users = await this.userRepository.getUsers();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await cacheService.set(cacheKey, users, 60);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logger.error('Cache error (set):', error);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return users;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { User } from '@/domain/user';
|
|
2
|
+
import { UserRepository } from '@/infrastructure/repositories/UserRepository';
|
|
3
|
+
<%_ if (caching === 'Redis') { -%>
|
|
4
|
+
import cacheService from '@/infrastructure/caching/redisClient';
|
|
5
|
+
<%_ } else if (caching === 'Memory Cache') { -%>
|
|
6
|
+
import cacheService from '@/infrastructure/caching/memoryCache';
|
|
7
|
+
<%_ } -%>
|
|
8
|
+
import logger from '@/infrastructure/log/logger';
|
|
9
|
+
|
|
10
|
+
export default class UpdateUser {
|
|
11
|
+
constructor(private userRepository: UserRepository) {}
|
|
12
|
+
|
|
13
|
+
async execute(id: number | string, data: { name?: string, email?: string }) {
|
|
14
|
+
const updatedUser = await this.userRepository.update(id, data);
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
await cacheService.del('users:all');
|
|
18
|
+
logger.info('Invalidated users:all cache');
|
|
19
|
+
} catch (error) {
|
|
20
|
+
logger.error('Cache error (del):', error);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return updatedUser;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const NodeCache = require('node-cache');
|
|
2
|
+
const logger = require('<%- loggerPath %>');
|
|
3
|
+
|
|
4
|
+
class MemoryCacheService {
|
|
5
|
+
constructor() {
|
|
6
|
+
// Default TTL of 0 (unlimited), check period of 120s
|
|
7
|
+
this.cache = new NodeCache({ stdTTL: 0, checkperiod: 120 });
|
|
8
|
+
logger.info('Memory Cache initialized');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
static getInstance() {
|
|
12
|
+
if (!MemoryCacheService.instance) {
|
|
13
|
+
MemoryCacheService.instance = new MemoryCacheService();
|
|
14
|
+
}
|
|
15
|
+
return MemoryCacheService.instance;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async get(key) {
|
|
19
|
+
try {
|
|
20
|
+
const data = this.cache.get(key);
|
|
21
|
+
if (data === undefined) return null;
|
|
22
|
+
return typeof data === 'string' ? JSON.parse(data) : data;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.error(`Memory Cache get error for key ${key}:`, error);
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async set(key, value, ttl) {
|
|
30
|
+
try {
|
|
31
|
+
const data = JSON.stringify(value);
|
|
32
|
+
if (ttl) {
|
|
33
|
+
this.cache.set(key, data, ttl);
|
|
34
|
+
} else {
|
|
35
|
+
this.cache.set(key, data);
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
logger.error(`Memory Cache set error for key ${key}:`, error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async del(key) {
|
|
43
|
+
try {
|
|
44
|
+
this.cache.del(key);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
logger.error(`Memory Cache del error for key ${key}:`, error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async getOrSet(key, fetcher, ttl = 3600) {
|
|
51
|
+
const cached = await this.get(key);
|
|
52
|
+
if (cached) return cached;
|
|
53
|
+
|
|
54
|
+
const data = await fetcher();
|
|
55
|
+
if (data) await this.set(key, data, ttl);
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = MemoryCacheService.getInstance();
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
jest.mock('node-cache', () => {
|
|
2
|
+
return jest.fn().mockImplementation(() => ({
|
|
3
|
+
get: jest.fn(),
|
|
4
|
+
set: jest.fn(),
|
|
5
|
+
del: jest.fn(),
|
|
6
|
+
}));
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
jest.mock('<%- loggerPath %>', () => ({
|
|
10
|
+
info: jest.fn(),
|
|
11
|
+
error: jest.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
describe('Memory Cache Client', () => {
|
|
15
|
+
let memoryCacheService;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.clearAllMocks();
|
|
19
|
+
jest.resetModules();
|
|
20
|
+
memoryCacheService = require('<% if (architecture === "MVC") { %>@/config/memoryCache<% } else { %>@/infrastructure/caching/memoryCache<% } %>');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should get data from memory cache', async () => {
|
|
24
|
+
const mockData = { test: 'data' };
|
|
25
|
+
memoryCacheService.cache.get.mockReturnValue(JSON.stringify(mockData));
|
|
26
|
+
|
|
27
|
+
const data = await memoryCacheService.get('test-key');
|
|
28
|
+
expect(data).toEqual(mockData);
|
|
29
|
+
expect(memoryCacheService.cache.get).toHaveBeenCalledWith('test-key');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should return null when key is not found', async () => {
|
|
33
|
+
memoryCacheService.cache.get.mockReturnValue(undefined);
|
|
34
|
+
const data = await memoryCacheService.get('non-existent');
|
|
35
|
+
expect(data).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return data as is if not a string', async () => {
|
|
39
|
+
const mockData = { test: 'data' };
|
|
40
|
+
memoryCacheService.cache.get.mockReturnValue(mockData);
|
|
41
|
+
const data = await memoryCacheService.get('binary-key');
|
|
42
|
+
expect(data).toEqual(mockData);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle errors in get', async () => {
|
|
46
|
+
memoryCacheService.cache.get.mockImplementation(() => { throw new Error('Cache Error'); });
|
|
47
|
+
const data = await memoryCacheService.get('error-key');
|
|
48
|
+
expect(data).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should set data to memory cache without TTL', async () => {
|
|
52
|
+
const mockData = { test: 'data' };
|
|
53
|
+
await memoryCacheService.set('test-key', mockData);
|
|
54
|
+
expect(memoryCacheService.cache.set).toHaveBeenCalledWith('test-key', JSON.stringify(mockData));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should handle errors in set', async () => {
|
|
58
|
+
memoryCacheService.cache.set.mockImplementation(() => { throw new Error('Cache Error'); });
|
|
59
|
+
await memoryCacheService.set('test-key', 'value');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should handle errors in del', async () => {
|
|
63
|
+
memoryCacheService.cache.del.mockImplementation(() => { throw new Error('Cache Error'); });
|
|
64
|
+
await memoryCacheService.del('test-key');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should use getOrSet and call fetcher if not cached', async () => {
|
|
68
|
+
memoryCacheService.get = jest.fn().mockResolvedValue(null);
|
|
69
|
+
memoryCacheService.set = jest.fn();
|
|
70
|
+
const fetcher = jest.fn().mockResolvedValue({ new: 'data' });
|
|
71
|
+
|
|
72
|
+
const result = await memoryCacheService.getOrSet('new-key', fetcher);
|
|
73
|
+
|
|
74
|
+
expect(result).toEqual({ new: 'data' });
|
|
75
|
+
expect(fetcher).toHaveBeenCalled();
|
|
76
|
+
// Result should be cached
|
|
77
|
+
expect(memoryCacheService.set).toHaveBeenCalledWith('new-key', { new: 'data' }, 3600);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return cached data in getOrSet', async () => {
|
|
81
|
+
const cachedData = { cached: 'data' };
|
|
82
|
+
memoryCacheService.get = jest.fn().mockResolvedValue(cachedData);
|
|
83
|
+
const fetcher = jest.fn();
|
|
84
|
+
|
|
85
|
+
const result = await memoryCacheService.getOrSet('cached-key', fetcher);
|
|
86
|
+
|
|
87
|
+
expect(result).toEqual(cachedData);
|
|
88
|
+
expect(fetcher).not.toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should handle falsy data from fetcher in getOrSet', async () => {
|
|
92
|
+
memoryCacheService.get = jest.fn().mockResolvedValue(null);
|
|
93
|
+
memoryCacheService.set = jest.fn();
|
|
94
|
+
const fetcher = jest.fn().mockResolvedValue(null);
|
|
95
|
+
|
|
96
|
+
const result = await memoryCacheService.getOrSet('empty-key', fetcher);
|
|
97
|
+
|
|
98
|
+
expect(result).toBeNull();
|
|
99
|
+
expect(memoryCacheService.set).not.toHaveBeenCalled();
|
|
100
|
+
});
|
|
101
|
+
});
|