@anthonylzq/simba.js 2.0.5 → 3.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.
@@ -4,8 +4,12 @@ const exec = util.promisify(require('child_process').exec)
4
4
  const writeFile = require('../utils/writeFile')
5
5
 
6
6
  /*
7
+ * Express api:
7
8
  * src
8
9
  * |- @types:
10
+ * |- |- custom:
11
+ * |- |- |- request: content, file
12
+ * |- |- |- response: content, file
9
13
  * |- |- dto:
10
14
  * |- |- |- user: content, file
11
15
  * |- |- models:
@@ -27,9 +31,9 @@ const writeFile = require('../utils/writeFile')
27
31
  * | | | |- user: content, file
28
32
  * | | | |- index: content, file
29
33
  * | | |- home: content, file
30
- * | | |- user: content, file
31
34
  * | | |- index: content, file
32
- * | | |- response: content, file
35
+ * | | |- user: content, file
36
+ * | |- response: content, file
33
37
  * | |- router: content, file
34
38
  * | |- server: content, file
35
39
  * | |- index: content, file
@@ -49,12 +53,63 @@ const writeFile = require('../utils/writeFile')
49
53
  * index.http: content, file
50
54
  */
51
55
 
56
+ /*
57
+ * Fastify api:
58
+ * src
59
+ * |- @types:
60
+ * |- |- dto:
61
+ * |- |- |- user: content, file
62
+ * |- |- models:
63
+ * |- |- |- user: content, file
64
+ * | |- index: content, file
65
+ * |- database:
66
+ * | |- mongo:
67
+ * | |- |- models:
68
+ * | |- |- |- index: content, file
69
+ * | |- |- |- user: content, file
70
+ * | |- |- queries:
71
+ * | |- |- |- index: content, file
72
+ * | |- |- |- user: content, file
73
+ * | |- |- index: content, file
74
+ * | |- index: content, file
75
+ * |- network:
76
+ * | |- routes:
77
+ * | | |- schemas:
78
+ * | | | |- user: content, file
79
+ * | | | |- index: content, file
80
+ * | | |- home: content, file
81
+ * | | |- user: content, file
82
+ * | | |- index: content, file
83
+ * | |- response: content, file
84
+ * | |- router: content, file
85
+ * | |- server: content, file
86
+ * | |- index: content, file
87
+ * |- services:
88
+ * | |- utils:
89
+ * | | |- messages:
90
+ * | | | |- user: content, file
91
+ * | | | |- index: content, file
92
+ * | | |- index: content, file
93
+ * | |- user: content, file
94
+ * | |- index: content, file
95
+ * |- .env: content, file
96
+ * |- index: content, file
97
+ * index.http: content, file
98
+ */
99
+
52
100
  /**
53
- * @param {String} projectName
54
- * @param {String} projectVersion
55
- * @param {String} email
101
+ * @param {Object} args
102
+ * @param {String} args.projectName
103
+ * @param {String} args.projectVersion
104
+ * @param {String} args.email
105
+ * @param {Boolean|undefined} args.fastify
56
106
  */
57
- module.exports = async (projectName, projectVersion, email) => {
107
+ module.exports = async ({
108
+ projectName,
109
+ projectVersion,
110
+ email,
111
+ fastify = false
112
+ }) => {
58
113
  const data = {
59
114
  '@types': {
60
115
  index: {
@@ -69,45 +124,18 @@ export {}
69
124
  '@types/dto': {
70
125
  user: {
71
126
  content: `interface DtoUser {
72
- id?: string
73
- lastName?: string
74
- name?: string
127
+ id: string
128
+ lastName: string
129
+ name: string
75
130
  }
76
131
  `,
77
132
  file: `${projectName}/src/@types/dto/user.d.ts`
78
133
  }
79
134
  },
80
- '@types/custom': {
81
- request: {
82
- content: `type ExpressRequest = import('express').Request
83
-
84
- interface CustomRequest extends ExpressRequest {
85
- body: {
86
- args?: DtoUser
87
- }
88
- // We can add custom headers via intersection, remember that for some reason
89
- // headers must be in Snake-Pascal-Case
90
- headers: import('http').IncomingHttpHeaders & {
91
- 'Custom-Header'?: string
92
- }
93
- }
94
- `,
95
- file: `${projectName}/src/@types/custom/request.d.ts`
96
- },
97
- response: {
98
- content: `type ExpressResponse = import('express').Response
99
-
100
- interface CustomResponse extends ExpressResponse {
101
- newValue?: string
102
- }
103
- `,
104
- file: `${projectName}/src/@types/custom/response.d.ts`
105
- }
106
- },
107
135
  '@types/models': {
108
136
  user: {
109
137
  content: `interface IUser {
110
- _id: import('mongoose').Types.ObjectId
138
+ id: string
111
139
  name: string
112
140
  lastName: string
113
141
  updatedAt: Date
@@ -154,14 +182,14 @@ const User = new Schema<IUser>(
154
182
  createdAt: false,
155
183
  updatedAt: true
156
184
  },
185
+ versionKey: false,
157
186
  toJSON: {
158
187
  transform(_, ret) {
159
- ret.id = ret._id
188
+ ret.id = ret._id.toString()
189
+ ret.updatedAt = ret.updatedAt.toISOString()
160
190
  delete ret._id
161
191
  delete ret.__v
162
- delete ret.updatedAt
163
192
  },
164
- versionKey: false,
165
193
  virtuals: true
166
194
  }
167
195
  }
@@ -180,12 +208,13 @@ export { UserModel }
180
208
  file: `${projectName}/src/database/mongo/queries/index.ts`
181
209
  },
182
210
  user: {
183
- content: `import { UserModel } from '../models'
211
+ content: `import { UserModel } from '..'
184
212
 
185
213
  const store = async (userData: DtoUser): Promise<IUser> => {
186
214
  const user = new UserModel(userData)
215
+ await user.save()
187
216
 
188
- return await user.save()
217
+ return user.toJSON()
189
218
  }
190
219
 
191
220
  const remove = async (
@@ -199,15 +228,22 @@ const remove = async (
199
228
  const get = async (
200
229
  id: string | null = null
201
230
  ): Promise<IUser[] | IUser | null> => {
202
- if (id) return await UserModel.findById(id)
231
+ if (id) {
232
+ const user = await UserModel.findById(id)
233
+
234
+ return user ? user.toJSON() : null
235
+ }
236
+
237
+ const users = await UserModel.find({})
203
238
 
204
- return await UserModel.find({})
239
+ return users.map(u => u.toJSON())
205
240
  }
206
241
 
207
242
  const update = async (userData: DtoUser): Promise<IUser | null> => {
208
243
  const { id, ...rest } = userData
244
+ const user = await UserModel.findByIdAndUpdate(id, rest, { new: true })
209
245
 
210
- return await UserModel.findByIdAndUpdate(id, rest, { new: true })
246
+ return user ? user.toJSON() : null
211
247
  }
212
248
 
213
249
  export { store, remove, get, update }
@@ -221,126 +257,353 @@ export { store, remove, get, update }
221
257
  export * from './server'
222
258
  `,
223
259
  file: `${projectName}/src/network/index.ts`
260
+ }
261
+ },
262
+ services: {
263
+ index: {
264
+ content: "export * from './user'\n",
265
+ file: `${projectName}/src/services/index.ts`
224
266
  },
225
- response: {
226
- content: `interface ResponseProps {
227
- error: boolean
228
- message: unknown
229
- res: CustomResponse
230
- status: number
231
- }
267
+ user: {
268
+ content: `import httpErrors from 'http-errors'
232
269
 
233
- const response = ({ error, message, res, status }: ResponseProps): void => {
234
- res.status(status).send({ error, message })
270
+ import { store, remove, get, update } from 'database'
271
+ import { EFU, MFU, GE, errorHandling } from './utils'
272
+
273
+ type Process = {
274
+ type: 'store' | 'getAll' | 'deleteAll' | 'getOne' | 'update' | 'delete'
235
275
  }
236
276
 
237
- export { response }
238
- `,
239
- file: `${projectName}/src/network/response.ts`
240
- },
241
- router: {
242
- content: `import { Application, Response, Request, Router, NextFunction } from 'express'
243
- import swaggerUi from 'swagger-ui-express'
244
- import httpErrors from 'http-errors'
277
+ class UserService {
278
+ private _args: Partial<DtoUser> | null
245
279
 
246
- import { response } from './response'
247
- import { Home, User } from './routes'
248
- import { docs } from 'utils'
280
+ constructor(args: Partial<DtoUser> | null = null) {
281
+ this._args = args
282
+ }
249
283
 
250
- const routers = [User]
284
+ public process({ type }: Process): Promise<string | IUser[] | IUser> {
285
+ switch (type) {
286
+ case 'store':
287
+ return this._store()
288
+ case 'getAll':
289
+ return this._getAll()
290
+ case 'deleteAll':
291
+ return this._deleteAll()
292
+ case 'getOne':
293
+ return this._getOne()
294
+ case 'update':
295
+ return this._update()
296
+ case 'delete':
297
+ return this._delete()
298
+ default:
299
+ throw new httpErrors.InternalServerError(GE.INTERNAL_SERVER_ERROR)
300
+ }
301
+ }
251
302
 
252
- const applyRoutes = (app: Application): void => {
253
- app.use('/', Home)
254
- app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(docs))
255
- routers.forEach((router: Router): Application => app.use('/api', router))
303
+ private async _store(): Promise<IUser> {
304
+ try {
305
+ const result = await store(this._args as DtoUser)
256
306
 
257
- // Handling 404 error
258
- app.use((req, res, next) => {
259
- next(new httpErrors.NotFound('This route does not exists'))
260
- })
261
- app.use(
262
- (
263
- error: httpErrors.HttpError,
264
- req: Request,
265
- res: Response,
266
- next: NextFunction
267
- ) => {
268
- response({
269
- error: true,
270
- message: error.message,
271
- res,
272
- status: error.status
273
- })
274
- next()
307
+ return result
308
+ } catch (e) {
309
+ return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
275
310
  }
276
- )
277
- }
311
+ }
278
312
 
279
- export { applyRoutes }
280
- `,
281
- file: `${projectName}/src/network/router.ts`
282
- },
283
- server: {
284
- content: `import express from 'express'
285
- import mongoose from 'mongoose'
286
- import morgan from 'morgan'
313
+ private async _getAll(): Promise<IUser[]> {
314
+ try {
315
+ const users = (await get()) as IUser[]
287
316
 
288
- import { applyRoutes } from './router'
317
+ return users
318
+ } catch (e) {
319
+ return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
320
+ }
321
+ }
289
322
 
290
- const PORT = (process.env.PORT as string) || '1996'
323
+ private async _deleteAll(): Promise<string> {
324
+ try {
325
+ const usersDeleted = (await remove()) as number
291
326
 
292
- class Server {
293
- private _app: express.Application
294
- private _connection: mongoose.Connection | undefined
327
+ if (usersDeleted >= 1) return MFU.ALL_USERS_DELETED
295
328
 
296
- constructor() {
297
- this._app = express()
298
- this._config()
329
+ if (usersDeleted === 0)
330
+ throw new httpErrors.Conflict(EFU.NOTHING_TO_DELETE)
331
+
332
+ throw new httpErrors.InternalServerError(GE.INTERNAL_SERVER_ERROR)
333
+ } catch (e) {
334
+ return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
335
+ }
299
336
  }
300
337
 
301
- private _config() {
302
- this._app.set('port', PORT)
303
- this._app.use(morgan('dev'))
304
- this._app.use(express.json())
305
- this._app.use(express.urlencoded({ extended: false }))
306
- this._app.use(
307
- (
308
- req: express.Request,
309
- res: express.Response,
310
- next: express.NextFunction
311
- ) => {
312
- res.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
313
- res.header('Access-Control-Allow-Origin', '*')
314
- res.header(
315
- 'Access-Control-Allow-Headers',
316
- 'Authorization, Content-Type'
317
- )
318
- next()
319
- }
320
- )
338
+ private async _getOne(): Promise<IUser> {
339
+ const { id } = this._args as DtoUser
321
340
 
322
- applyRoutes(this._app)
341
+ try {
342
+ const user = (await get(id as string)) as IUser | null
343
+
344
+ if (!user) throw new httpErrors.NotFound(EFU.NOT_FOUND)
345
+
346
+ return user
347
+ } catch (e) {
348
+ return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
349
+ }
323
350
  }
324
351
 
325
- private async _mongo(): Promise<void> {
326
- this._connection = mongoose.connection
327
- const connection = {
328
- keepAlive: true,
329
- useNewUrlParser: true,
330
- useUnifiedTopology: true
352
+ private async _update(): Promise<IUser> {
353
+ try {
354
+ const updatedUser = await update(this._args as DtoUser)
355
+
356
+ if (!updatedUser) throw new httpErrors.NotFound(EFU.NOT_FOUND)
357
+
358
+ return updatedUser
359
+ } catch (e) {
360
+ return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
331
361
  }
332
- this._connection.on('connected', () => {
333
- console.log('Mongo connection established.')
334
- })
335
- this._connection.on('reconnected', () => {
336
- console.log('Mongo connection reestablished')
337
- })
338
- this._connection.on('disconnected', () => {
339
- console.log('Mongo connection disconnected')
340
- console.log('Trying to reconnected to Mongo...')
341
- setTimeout(() => {
342
- mongoose.connect(process.env.MONGO_URI as string, {
343
- ...connection,
362
+ }
363
+
364
+ private async _delete(): Promise<string> {
365
+ const { id } = this._args as DtoUser
366
+
367
+ try {
368
+ const deletedUser = await remove(id)
369
+
370
+ if (!deletedUser) throw new httpErrors.NotFound(EFU.NOT_FOUND)
371
+
372
+ return MFU.USER_DELETED
373
+ } catch (e) {
374
+ return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
375
+ }
376
+ }
377
+ }
378
+
379
+ export { UserService }
380
+ `,
381
+ file: `${projectName}/src/services/user.ts`
382
+ }
383
+ },
384
+ 'services/utils': {
385
+ index: {
386
+ content: `import httpErrors from 'http-errors'
387
+
388
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
389
+ const errorHandling = (e: any, message?: string): never => {
390
+ console.error(e)
391
+
392
+ if (e instanceof httpErrors.HttpError) throw e
393
+
394
+ throw new httpErrors.InternalServerError(message ?? e.message)
395
+ }
396
+
397
+ export { errorHandling }
398
+ export * from './messages'
399
+ `,
400
+ file: `${projectName}/src/services/utils/index.ts`
401
+ }
402
+ },
403
+ 'services/utils/messages': {
404
+ index: {
405
+ content: `enum GenericErrors {
406
+ INTERNAL_SERVER_ERROR = 'Something went wrong'
407
+ }
408
+
409
+ export { GenericErrors as GE }
410
+ export * from './user'
411
+ `,
412
+ file: `${projectName}/src/services/utils/messages/index.ts`
413
+ },
414
+ user: {
415
+ content: `enum ErrorForUser {
416
+ NOT_FOUND = 'The requested user does not exists',
417
+ NOTHING_TO_DELETE = 'There is no user to be deleted'
418
+ }
419
+
420
+ enum MessageForUser {
421
+ ALL_USERS_DELETED = 'All the users were deleted successfully',
422
+ USER_DELETED = 'The requested user was successfully deleted'
423
+ }
424
+
425
+ export { ErrorForUser as EFU, MessageForUser as MFU }
426
+ `,
427
+ file: `${projectName}/src/services/utils/messages/user.ts`
428
+ }
429
+ },
430
+ test: {
431
+ index: {
432
+ content: `### Testing store a user
433
+ POST http://localhost:1996/api/users
434
+ Content-Type: application/json
435
+
436
+ {
437
+ "args": {
438
+ "lastName": "Lzq",
439
+ "name": "Anthony"
440
+ }
441
+ }
442
+
443
+ ### Testing getAll users
444
+ GET http://localhost:1996/api/users
445
+
446
+ ### Testing deleteAll users
447
+ DELETE http://localhost:1996/api/users
448
+
449
+ ### Testing getOne user
450
+ GET http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
451
+
452
+ ### Testing update user
453
+ PATCH http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
454
+ Content-Type: application/json
455
+
456
+ {
457
+ "args": {
458
+ "name": "Anthony",
459
+ "lastName": "Luzquiños"
460
+ }
461
+ }
462
+
463
+ ### Testing delete user
464
+ DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
465
+ `,
466
+ file: `${projectName}/index.http`
467
+ }
468
+ },
469
+ '.env': {
470
+ content: `MONGO_URI = ${
471
+ process.env.LOCAL
472
+ ? process.env.MONGO_URI
473
+ : `mongodb://mongo:mongo@mongo:27017/${projectName}`
474
+ }`,
475
+ file: `${projectName}/.env`
476
+ },
477
+ index: {
478
+ content: `import { Server } from './network'
479
+
480
+ Server.start()
481
+ `,
482
+ file: `${projectName}/src/index.ts`
483
+ }
484
+ }
485
+
486
+ const expressData = {
487
+ network: {
488
+ response: {
489
+ content: `interface ResponseProps {
490
+ error: boolean
491
+ message: unknown
492
+ res: CustomResponse
493
+ status: number
494
+ }
495
+
496
+ const response = ({ error, message, res, status }: ResponseProps): void => {
497
+ res.status(status).send({ error, message })
498
+ }
499
+
500
+ export { response }
501
+ `,
502
+ file: `${projectName}/src/network/response.ts`
503
+ },
504
+ router: {
505
+ content: `import { Application, Response, Request, Router, NextFunction } from 'express'
506
+ import swaggerUi from 'swagger-ui-express'
507
+ import httpErrors from 'http-errors'
508
+
509
+ import { response } from './response'
510
+ import { Home, User } from './routes'
511
+ import { docs } from 'utils'
512
+
513
+ const routers = [User]
514
+
515
+ const applyRoutes = (app: Application): void => {
516
+ app.use('/', Home)
517
+ app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(docs))
518
+ routers.forEach((router: Router): Application => app.use('/api', router))
519
+
520
+ // Handling 404 error
521
+ app.use((req, res, next) => {
522
+ next(new httpErrors.NotFound('This route does not exists'))
523
+ })
524
+ app.use(
525
+ (
526
+ error: httpErrors.HttpError,
527
+ req: Request,
528
+ res: Response,
529
+ next: NextFunction
530
+ ) => {
531
+ response({
532
+ error: true,
533
+ message: error.message,
534
+ res,
535
+ status: error.status
536
+ })
537
+ next()
538
+ }
539
+ )
540
+ }
541
+
542
+ export { applyRoutes }
543
+ `,
544
+ file: `${projectName}/src/network/router.ts`
545
+ },
546
+ server: {
547
+ content: `import express from 'express'
548
+ import mongoose from 'mongoose'
549
+ import morgan from 'morgan'
550
+
551
+ import { applyRoutes } from './router'
552
+
553
+ const PORT = (process.env.PORT as string) || '1996'
554
+
555
+ class Server {
556
+ private _app: express.Application
557
+ private _connection: mongoose.Connection | undefined
558
+
559
+ constructor() {
560
+ this._app = express()
561
+ this._config()
562
+ }
563
+
564
+ private _config() {
565
+ this._app.set('port', PORT)
566
+ this._app.use(morgan('dev'))
567
+ this._app.use(express.json())
568
+ this._app.use(express.urlencoded({ extended: false }))
569
+ this._app.use(
570
+ (
571
+ req: express.Request,
572
+ res: express.Response,
573
+ next: express.NextFunction
574
+ ) => {
575
+ res.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
576
+ res.header('Access-Control-Allow-Origin', '*')
577
+ res.header(
578
+ 'Access-Control-Allow-Headers',
579
+ 'Authorization, Content-Type'
580
+ )
581
+ next()
582
+ }
583
+ )
584
+
585
+ applyRoutes(this._app)
586
+ }
587
+
588
+ private async _mongo(): Promise<void> {
589
+ this._connection = mongoose.connection
590
+ const connection = {
591
+ keepAlive: true,
592
+ useNewUrlParser: true,
593
+ useUnifiedTopology: true
594
+ }
595
+ this._connection.on('connected', () => {
596
+ console.log('Mongo connection established.')
597
+ })
598
+ this._connection.on('reconnected', () => {
599
+ console.log('Mongo connection reestablished')
600
+ })
601
+ this._connection.on('disconnected', () => {
602
+ console.log('Mongo connection disconnected')
603
+ console.log('Trying to reconnected to Mongo...')
604
+ setTimeout(() => {
605
+ mongoose.connect(process.env.MONGO_URI as string, {
606
+ ...connection,
344
607
  connectTimeoutMS: 3000,
345
608
  socketTimeoutMS: 3000
346
609
  })
@@ -376,6 +639,33 @@ export { server as Server }
376
639
  file: `${projectName}/src/network/server.ts`
377
640
  }
378
641
  },
642
+ '@types/custom': {
643
+ request: {
644
+ content: `type ExpressRequest = import('express').Request
645
+
646
+ interface CustomRequest extends ExpressRequest {
647
+ body: {
648
+ args?: DtoUser
649
+ }
650
+ // We can add custom headers via intersection, remember that for some reason
651
+ // headers must be in Snake-Pascal-Case
652
+ headers: import('http').IncomingHttpHeaders & {
653
+ 'Custom-Header'?: string
654
+ }
655
+ }
656
+ `,
657
+ file: `${projectName}/src/@types/custom/request.d.ts`
658
+ },
659
+ response: {
660
+ content: `type ExpressResponse = import('express').Response
661
+
662
+ interface CustomResponse extends ExpressResponse {
663
+ newValue?: string
664
+ }
665
+ `,
666
+ file: `${projectName}/src/@types/custom/response.d.ts`
667
+ }
668
+ },
379
669
  'network/routes': {
380
670
  home: {
381
671
  content: `import { Response, Request, Router } from 'express'
@@ -410,7 +700,7 @@ import { ValidationError } from 'joi'
410
700
 
411
701
  import { response } from 'network/response'
412
702
  import { UserService } from 'services/user'
413
- import { idSchema, userSchema } from './schemas'
703
+ import { idSchema, userSchema, storeUserSchema } from './schemas'
414
704
 
415
705
  const User = Router()
416
706
 
@@ -424,65 +714,12 @@ User.route('/users')
424
714
  const {
425
715
  body: { args }
426
716
  } = req
427
- const us = new UserService(args as DtoUser)
428
717
 
429
718
  try {
719
+ await storeUserSchema.validateAsync(args)
720
+ const us = new UserService(args)
430
721
  const result = await us.process({ type: 'store' })
431
722
  response({ error: false, message: result, res, status: 201 })
432
- } catch (e) {
433
- next(e)
434
- }
435
- }
436
- )
437
- .get(
438
- async (
439
- req: CustomRequest,
440
- res: CustomResponse,
441
- next: NextFunction
442
- ): Promise<void> => {
443
- const us = new UserService()
444
-
445
- try {
446
- const result = await us.process({ type: 'getAll' })
447
- response({ error: false, message: result, res, status: 200 })
448
- } catch (e) {
449
- next(e)
450
- }
451
- }
452
- )
453
- .delete(
454
- async (
455
- req: CustomRequest,
456
- res: CustomResponse,
457
- next: NextFunction
458
- ): Promise<void> => {
459
- const us = new UserService()
460
-
461
- try {
462
- const result = await us.process({ type: 'deleteAll' })
463
- response({ error: false, message: result, res, status: 200 })
464
- } catch (e) {
465
- next(e)
466
- }
467
- }
468
- )
469
-
470
- User.route('/user/:id')
471
- .get(
472
- async (
473
- req: CustomRequest,
474
- res: CustomResponse,
475
- next: NextFunction
476
- ): Promise<void> => {
477
- const {
478
- params: { id }
479
- } = req
480
-
481
- try {
482
- await idSchema.validateAsync(id)
483
- const us = new UserService({ id } as DtoUser)
484
- const result = await us.process({ type: 'getOne' })
485
- response({ error: false, message: result, res, status: 200 })
486
723
  } catch (e) {
487
724
  if (e instanceof ValidationError)
488
725
  return next(new httpErrors.UnprocessableEntity(e.message))
@@ -491,30 +728,18 @@ User.route('/user/:id')
491
728
  }
492
729
  }
493
730
  )
494
- .patch(
731
+ .get(
495
732
  async (
496
733
  req: CustomRequest,
497
734
  res: CustomResponse,
498
735
  next: NextFunction
499
736
  ): Promise<void> => {
500
- const {
501
- body: { args },
502
- params: { id }
503
- } = req
504
- const user: DtoUser = {
505
- id,
506
- ...args
507
- }
737
+ const us = new UserService()
508
738
 
509
739
  try {
510
- await userSchema.validateAsync(user)
511
- const us = new UserService(user)
512
- const result = await us.process({ type: 'update' })
740
+ const result = await us.process({ type: 'getAll' })
513
741
  response({ error: false, message: result, res, status: 200 })
514
742
  } catch (e) {
515
- if (e instanceof ValidationError)
516
- return next(new httpErrors.UnprocessableEntity(e.message))
517
-
518
743
  next(e)
519
744
  }
520
745
  }
@@ -525,259 +750,126 @@ User.route('/user/:id')
525
750
  res: CustomResponse,
526
751
  next: NextFunction
527
752
  ): Promise<void> => {
528
- const {
529
- params: { id }
530
- } = req
531
-
532
- try {
533
- await idSchema.validateAsync(id)
534
- const us = new UserService({ id } as DtoUser)
535
- const result = await us.process({ type: 'delete' })
536
- response({ error: false, message: result, res, status: 200 })
537
- } catch (e) {
538
- if (e instanceof ValidationError)
539
- return next(new httpErrors.UnprocessableEntity(e.message))
540
-
541
- next(e)
542
- }
543
- }
544
- )
545
-
546
- export { User }
547
- `,
548
- file: `${projectName}/src/network/routes/user.ts`
549
- }
550
- },
551
- 'network/routes/schemas': {
552
- index: {
553
- content: `import Joi from 'joi'
554
-
555
- const idSchema = Joi.string().length(24).required()
556
-
557
- export { idSchema }
558
- export * from './user'
559
- `,
560
- file: `${projectName}/src/network/routes/schemas/index.ts`
561
- },
562
- user: {
563
- content: `import Joi from 'joi'
564
-
565
- const userSchema = Joi.object().keys({
566
- id: Joi.string().length(24).required(),
567
- lastName: Joi.string().required(),
568
- name: Joi.string().required()
569
- })
570
-
571
- export { userSchema }
572
- `,
573
- file: `${projectName}/src/network/routes/schemas/user.ts`
574
- }
575
- },
576
- services: {
577
- index: {
578
- content: "export * from './user'\n",
579
- file: `${projectName}/src/services/index.ts`
580
- },
581
- user: {
582
- content: `import httpErrors from 'http-errors'
583
-
584
- import { store, remove, get, update } from 'database'
585
- import { EFU, MFU, GE, errorHandling } from './utils'
586
-
587
- type Process = {
588
- type: 'store' | 'getAll' | 'deleteAll' | 'getOne' | 'update' | 'delete'
589
- }
590
-
591
- class UserService {
592
- private _args: DtoUser | null
593
-
594
- constructor(args: DtoUser | null = null) {
595
- this._args = args
596
- }
597
-
598
- public process({ type }: Process): Promise<string | IUser[] | IUser> {
599
- switch (type) {
600
- case 'store':
601
- return this._store()
602
- case 'getAll':
603
- return this._getAll()
604
- case 'deleteAll':
605
- return this._deleteAll()
606
- case 'getOne':
607
- return this._getOne()
608
- case 'update':
609
- return this._update()
610
- case 'delete':
611
- return this._delete()
612
- default:
613
- throw new httpErrors.InternalServerError(GE.INTERNAL_SERVER_ERROR)
614
- }
615
- }
616
-
617
- private async _store(): Promise<IUser> {
618
- try {
619
- const result = await store(this._args as DtoUser)
620
-
621
- return result
622
- } catch (e) {
623
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
624
- }
625
- }
626
-
627
- private async _getAll(): Promise<IUser[]> {
628
- try {
629
- const users = (await get()) as IUser[]
630
-
631
- return users
632
- } catch (e) {
633
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
634
- }
635
- }
636
-
637
- private async _deleteAll(): Promise<string> {
638
- try {
639
- const usersDeleted = (await remove()) as number
640
-
641
- if (usersDeleted >= 1) return MFU.ALL_USERS_DELETED
642
-
643
- if (usersDeleted === 0)
644
- throw new httpErrors.Conflict(EFU.NOTHING_TO_DELETE)
753
+ const us = new UserService()
645
754
 
646
- throw new httpErrors.InternalServerError(GE.INTERNAL_SERVER_ERROR)
647
- } catch (e) {
648
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
755
+ try {
756
+ const result = await us.process({ type: 'deleteAll' })
757
+ response({ error: false, message: result, res, status: 200 })
758
+ } catch (e) {
759
+ next(e)
760
+ }
649
761
  }
650
- }
651
-
652
- private async _getOne(): Promise<IUser> {
653
- const { id } = this._args as DtoUser
762
+ )
654
763
 
655
- try {
656
- const user = (await get(id as string)) as IUser | null
764
+ User.route('/user/:id')
765
+ .get(
766
+ async (
767
+ req: CustomRequest,
768
+ res: CustomResponse,
769
+ next: NextFunction
770
+ ): Promise<void> => {
771
+ const {
772
+ params: { id }
773
+ } = req
657
774
 
658
- if (!user) throw new httpErrors.NotFound(EFU.NOT_FOUND)
775
+ try {
776
+ await idSchema.validateAsync(id)
777
+ const us = new UserService({ id })
778
+ const result = await us.process({ type: 'getOne' })
779
+ response({ error: false, message: result, res, status: 200 })
780
+ } catch (e) {
781
+ if (e instanceof ValidationError)
782
+ return next(new httpErrors.UnprocessableEntity(e.message))
659
783
 
660
- return user
661
- } catch (e) {
662
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
784
+ next(e)
785
+ }
663
786
  }
664
- }
665
-
666
- private async _update(): Promise<IUser> {
667
- try {
668
- const updatedUser = await update(this._args as DtoUser)
787
+ )
788
+ .patch(
789
+ async (
790
+ req: CustomRequest,
791
+ res: CustomResponse,
792
+ next: NextFunction
793
+ ): Promise<void> => {
794
+ const {
795
+ body: { args },
796
+ params: { id }
797
+ } = req
798
+ const user = {
799
+ id,
800
+ ...args
801
+ }
669
802
 
670
- if (!updatedUser) throw new httpErrors.NotFound(EFU.NOT_FOUND)
803
+ try {
804
+ await userSchema.validateAsync(user)
805
+ const us = new UserService(user)
806
+ const result = await us.process({ type: 'update' })
807
+ response({ error: false, message: result, res, status: 200 })
808
+ } catch (e) {
809
+ if (e instanceof ValidationError)
810
+ return next(new httpErrors.UnprocessableEntity(e.message))
671
811
 
672
- return updatedUser
673
- } catch (e) {
674
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
812
+ next(e)
813
+ }
675
814
  }
676
- }
677
-
678
- private async _delete(): Promise<string> {
679
- const { id } = this._args as DtoUser
680
-
681
- try {
682
- const deletedUser = await remove(id)
815
+ )
816
+ .delete(
817
+ async (
818
+ req: CustomRequest,
819
+ res: CustomResponse,
820
+ next: NextFunction
821
+ ): Promise<void> => {
822
+ const {
823
+ params: { id }
824
+ } = req
683
825
 
684
- if (!deletedUser) throw new httpErrors.NotFound(EFU.NOT_FOUND)
826
+ try {
827
+ await idSchema.validateAsync(id)
828
+ const us = new UserService({ id })
829
+ const result = await us.process({ type: 'delete' })
830
+ response({ error: false, message: result, res, status: 200 })
831
+ } catch (e) {
832
+ if (e instanceof ValidationError)
833
+ return next(new httpErrors.UnprocessableEntity(e.message))
685
834
 
686
- return MFU.USER_DELETED
687
- } catch (e) {
688
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
835
+ next(e)
836
+ }
689
837
  }
690
- }
691
- }
838
+ )
692
839
 
693
- export { UserService }
840
+ export { User }
694
841
  `,
695
- file: `${projectName}/src/services/user.ts`
842
+ file: `${projectName}/src/network/routes/user.ts`
696
843
  }
697
844
  },
698
- 'services/utils': {
845
+ 'network/routes/schemas': {
699
846
  index: {
700
- content: `import httpErrors from 'http-errors'
701
-
702
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
703
- const errorHandling = (e: any, message?: string): never => {
704
- console.error(e)
705
-
706
- if (e instanceof httpErrors.HttpError) throw e
707
-
708
- throw new httpErrors.InternalServerError(message ?? e.message)
709
- }
847
+ content: `import Joi from 'joi'
710
848
 
711
- export { errorHandling }
712
- export * from './messages'
713
- `,
714
- file: `${projectName}/src/services/utils/index.ts`
715
- }
716
- },
717
- 'services/utils/messages': {
718
- index: {
719
- content: `enum GenericErrors {
720
- INTERNAL_SERVER_ERROR = 'Something went wrong'
721
- }
849
+ const idSchema = Joi.string().length(24).required()
722
850
 
723
- export { GenericErrors as GE }
851
+ export { idSchema }
724
852
  export * from './user'
725
853
  `,
726
- file: `${projectName}/src/services/utils/messages/index.ts`
854
+ file: `${projectName}/src/network/routes/schemas/index.ts`
727
855
  },
728
856
  user: {
729
- content: `enum ErrorForUser {
730
- NOT_FOUND = 'The requested user does not exists',
731
- NOTHING_TO_DELETE = 'There is no user to be deleted'
732
- }
733
-
734
- enum MessageForUser {
735
- ALL_USERS_DELETED = 'All the users were deleted successfully',
736
- USER_DELETED = 'The requested user was successfully deleted'
737
- }
738
-
739
- export { ErrorForUser as EFU, MessageForUser as MFU }
740
- `,
741
- file: `${projectName}/src/services/utils/messages/user.ts`
742
- }
743
- },
744
- test: {
745
- index: {
746
- content: `### Testing store a user
747
- POST http://localhost:1996/api/users
748
- Content-Type: application/json
749
-
750
- {
751
- "args": {
752
- "lastName": "Lzq",
753
- "name": "Anthony"
754
- }
755
- }
756
-
757
- ### Testing getAll users
758
- GET http://localhost:1996/api/users
759
-
760
- ### Testing deleteAll users
761
- DELETE http://localhost:1996/api/users
762
-
763
- ### Testing getOne user
764
- GET http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
857
+ content: `import Joi from 'joi'
765
858
 
766
- ### Testing update user
767
- PATCH http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
768
- Content-Type: application/json
859
+ const userSchema = Joi.object().keys({
860
+ id: Joi.string().length(24).required(),
861
+ lastName: Joi.string().required(),
862
+ name: Joi.string().required()
863
+ })
769
864
 
770
- {
771
- "args": {
772
- "name": "Anthony",
773
- "lastName": "Luzquiños"
774
- }
775
- }
865
+ const storeUserSchema = Joi.object().keys({
866
+ lastName: Joi.string().required(),
867
+ name: Joi.string().required()
868
+ })
776
869
 
777
- ### Testing delete user
778
- DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
870
+ export { userSchema, storeUserSchema }
779
871
  `,
780
- file: `${projectName}/index.http`
872
+ file: `${projectName}/src/network/routes/schemas/user.ts`
781
873
  }
782
874
  },
783
875
  utils: {
@@ -933,8 +1025,70 @@ DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
933
1025
  "tags": [
934
1026
  "user"
935
1027
  ],
936
- "summary": "Get an specific user",
937
- "operationId": "getOne",
1028
+ "summary": "Get an specific user",
1029
+ "operationId": "getOne",
1030
+ "parameters": [
1031
+ {
1032
+ "name": "id",
1033
+ "in": "path",
1034
+ "description": "MongoDB user id",
1035
+ "required": true,
1036
+ "style": "simple",
1037
+ "explode": false,
1038
+ "schema": {
1039
+ "type": "string"
1040
+ }
1041
+ }
1042
+ ],
1043
+ "responses": {
1044
+ "200": {
1045
+ "description": "User stored in the database",
1046
+ "content": {
1047
+ "application/json": {
1048
+ "schema": {
1049
+ "$ref": "#/components/schemas/User"
1050
+ }
1051
+ }
1052
+ }
1053
+ },
1054
+ "404": {
1055
+ "description": "User not found",
1056
+ "content": {
1057
+ "application/json": {
1058
+ "schema": {
1059
+ "$ref": "#/components/schemas/DefaultError"
1060
+ }
1061
+ }
1062
+ }
1063
+ },
1064
+ "422": {
1065
+ "description": "Invalid request format",
1066
+ "content": {
1067
+ "application/json": {
1068
+ "schema": {
1069
+ "$ref": "#/components/schemas/DefaultError"
1070
+ }
1071
+ }
1072
+ }
1073
+ },
1074
+ "500": {
1075
+ "description": "Internal server error",
1076
+ "content": {
1077
+ "application/json": {
1078
+ "schema": {
1079
+ "$ref": "#/components/schemas/DefaultError"
1080
+ }
1081
+ }
1082
+ }
1083
+ }
1084
+ }
1085
+ },
1086
+ "patch": {
1087
+ "tags": [
1088
+ "user"
1089
+ ],
1090
+ "summary": "Update the user data",
1091
+ "operationId": "update",
938
1092
  "parameters": [
939
1093
  {
940
1094
  "name": "id",
@@ -948,9 +1102,12 @@ DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
948
1102
  }
949
1103
  }
950
1104
  ],
1105
+ "requestBody": {
1106
+ "$ref": "#/components/requestBodies/DtoUser"
1107
+ },
951
1108
  "responses": {
952
1109
  "200": {
953
- "description": "User stored in the database",
1110
+ "description": "User successfully updated",
954
1111
  "content": {
955
1112
  "application/json": {
956
1113
  "schema": {
@@ -991,12 +1148,12 @@ DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
991
1148
  }
992
1149
  }
993
1150
  },
994
- "patch": {
1151
+ "delete": {
995
1152
  "tags": [
996
1153
  "user"
997
1154
  ],
998
- "summary": "Update the user data",
999
- "operationId": "update",
1155
+ "summary": "Delete one user from the database",
1156
+ "operationId": "delete",
1000
1157
  "parameters": [
1001
1158
  {
1002
1159
  "name": "id",
@@ -1010,16 +1167,13 @@ DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
1010
1167
  }
1011
1168
  }
1012
1169
  ],
1013
- "requestBody": {
1014
- "$ref": "#/components/requestBodies/DtoUser"
1015
- },
1016
1170
  "responses": {
1017
1171
  "200": {
1018
- "description": "User successfully updated",
1172
+ "description": "User successfully deleted",
1019
1173
  "content": {
1020
1174
  "application/json": {
1021
1175
  "schema": {
1022
- "$ref": "#/components/schemas/User"
1176
+ "$ref": "#/components/schemas/DefaultSuccess"
1023
1177
  }
1024
1178
  }
1025
1179
  }
@@ -1034,199 +1188,583 @@ DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
1034
1188
  }
1035
1189
  }
1036
1190
  },
1037
- "422": {
1038
- "description": "Invalid request format",
1039
- "content": {
1040
- "application/json": {
1041
- "schema": {
1042
- "$ref": "#/components/schemas/DefaultError"
1043
- }
1044
- }
1191
+ "422": {
1192
+ "description": "Invalid request format",
1193
+ "content": {
1194
+ "application/json": {
1195
+ "schema": {
1196
+ "$ref": "#/components/schemas/DefaultError"
1197
+ }
1198
+ }
1199
+ }
1200
+ },
1201
+ "500": {
1202
+ "description": "Internal server error",
1203
+ "content": {
1204
+ "application/json": {
1205
+ "schema": {
1206
+ "$ref": "#/components/schemas/DefaultError"
1207
+ }
1208
+ }
1209
+ }
1210
+ }
1211
+ }
1212
+ }
1213
+ }
1214
+ },
1215
+ "components": {
1216
+ "schemas": {
1217
+ "User": {
1218
+ "type": "object",
1219
+ "properties": {
1220
+ "id": {
1221
+ "type": "string"
1222
+ },
1223
+ "lastName": {
1224
+ "type": "string"
1225
+ },
1226
+ "name": {
1227
+ "type": "string"
1228
+ }
1229
+ }
1230
+ },
1231
+ "DefaultSuccess": {
1232
+ "type": "object",
1233
+ "properties": {
1234
+ "error": {
1235
+ "type": "boolean",
1236
+ "default": false
1237
+ },
1238
+ "message": {
1239
+ "type": "object",
1240
+ "properties": {
1241
+ "result": {
1242
+ "type": "string"
1243
+ }
1244
+ }
1245
+ }
1246
+ }
1247
+ },
1248
+ "DefaultError": {
1249
+ "type": "object",
1250
+ "properties": {
1251
+ "error": {
1252
+ "type": "boolean",
1253
+ "default": true
1254
+ },
1255
+ "message": {
1256
+ "type": "object",
1257
+ "properties": {
1258
+ "result": {
1259
+ "type": "string"
1260
+ }
1261
+ }
1262
+ }
1263
+ }
1264
+ }
1265
+ },
1266
+ "requestBodies": {
1267
+ "DtoUser": {
1268
+ "description": "User name and last name",
1269
+ "content": {
1270
+ "application/json": {
1271
+ "schema": {
1272
+ "type": "object",
1273
+ "properties": {
1274
+ "args": {
1275
+ "type": "object",
1276
+ "properties": {
1277
+ "name": {
1278
+ "type": "string"
1279
+ },
1280
+ "lastName": {
1281
+ "type": "string"
1282
+ }
1283
+ }
1284
+ }
1285
+ }
1286
+ }
1287
+ }
1288
+ },
1289
+ "required": true
1290
+ }
1291
+ }
1292
+ }
1293
+ }`,
1294
+ file: `${projectName}/src/utils/docs.json`
1295
+ },
1296
+ index: {
1297
+ content: "export { default as docs } from './docs.json'\n",
1298
+ file: `${projectName}/src/utils/index.ts`
1299
+ }
1300
+ }
1301
+ }
1302
+
1303
+ const fastifyData = {
1304
+ network: {
1305
+ response: {
1306
+ content: `import { FastifyReply } from 'fastify'
1307
+
1308
+ const response = ({
1309
+ error,
1310
+ message,
1311
+ reply,
1312
+ status
1313
+ }: {
1314
+ error: boolean
1315
+ message: unknown
1316
+ reply: FastifyReply
1317
+ status: number
1318
+ }): void => {
1319
+ reply.code(status).send({ error, message })
1320
+ }
1321
+
1322
+ export { response }
1323
+ `,
1324
+ file: `${projectName}/src/network/response.ts`
1325
+ },
1326
+ router: {
1327
+ content: `import { FastifyInstance } from 'fastify'
1328
+ import { HttpError } from 'http-errors'
1329
+
1330
+ import { response } from './response'
1331
+ import { Home, User, Docs } from './routes'
1332
+
1333
+ const routers = [Docs, User]
1334
+
1335
+ const applyRoutes = (app: FastifyInstance): void => {
1336
+ Home(app)
1337
+ routers.forEach(router => router(app))
1338
+
1339
+ // Handling 404 error
1340
+ app.setNotFoundHandler((request, reply) => {
1341
+ response({
1342
+ error: true,
1343
+ message: 'This route does not exists',
1344
+ reply,
1345
+ status: 404
1346
+ })
1347
+ })
1348
+ app.setErrorHandler<HttpError>((error, request, reply) => {
1349
+ response({
1350
+ error: true,
1351
+ message: error.message,
1352
+ reply,
1353
+ status: error.status ?? 500
1354
+ })
1355
+ })
1356
+ }
1357
+
1358
+ export { applyRoutes }
1359
+ `,
1360
+ file: `${projectName}/src/network/router.ts`
1361
+ },
1362
+ server: {
1363
+ content: `import Fastify, { FastifyInstance } from 'fastify'
1364
+ import mongoose from 'mongoose'
1365
+
1366
+ import { applyRoutes } from './router'
1367
+
1368
+ const PORT = process.env.PORT ?? '1996'
1369
+
1370
+ class Server {
1371
+ private _app: FastifyInstance
1372
+ private _connection: mongoose.Connection | undefined
1373
+
1374
+ constructor() {
1375
+ this._app = Fastify({ logger: true })
1376
+ this._config()
1377
+ }
1378
+
1379
+ private _config() {
1380
+ this._app.addHook('preHandler', (req, reply, done) => {
1381
+ reply.header('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE')
1382
+ reply.header('Access-Control-Allow-Origin', '*')
1383
+ reply.header(
1384
+ 'Access-Control-Allow-Headers',
1385
+ 'Authorization, Content-Type'
1386
+ )
1387
+ done()
1388
+ })
1389
+ applyRoutes(this._app)
1390
+ }
1391
+
1392
+ private async _mongo(): Promise<void> {
1393
+ this._connection = mongoose.connection
1394
+ const connection = {
1395
+ keepAlive: true,
1396
+ useNewUrlParser: true,
1397
+ useUnifiedTopology: true
1398
+ }
1399
+ this._connection.on('connected', () => {
1400
+ this._app.log.info('Mongo connection established.')
1401
+ })
1402
+ this._connection.on('reconnected', () => {
1403
+ this._app.log.info('Mongo connection reestablished')
1404
+ })
1405
+ this._connection.on('disconnected', () => {
1406
+ this._app.log.info('Mongo connection disconnected')
1407
+ this._app.log.info('Trying to reconnected to Mongo...')
1408
+ setTimeout(() => {
1409
+ mongoose.connect(process.env.MONGO_URI as string, {
1410
+ ...connection,
1411
+ connectTimeoutMS: 3000,
1412
+ socketTimeoutMS: 3000
1413
+ })
1414
+ }, 3000)
1415
+ })
1416
+ this._connection.on('close', () => {
1417
+ this._app.log.info('Mongo connection closed')
1418
+ })
1419
+ this._connection.on('error', (e: Error) => {
1420
+ this._app.log.info('Mongo connection error:')
1421
+ this._app.log.error(e)
1422
+ })
1423
+ await mongoose.connect(process.env.MONGO_URI as string, connection)
1424
+ }
1425
+
1426
+ public async start(): Promise<void> {
1427
+ try {
1428
+ await this._app.listen(PORT)
1429
+ this._mongo()
1430
+ } catch (e) {
1431
+ console.error(e)
1432
+ }
1433
+ }
1434
+ }
1435
+
1436
+ const server = new Server()
1437
+
1438
+ export { server as Server }
1439
+ `,
1440
+ file: `${projectName}/src/network/server.ts`
1441
+ }
1442
+ },
1443
+ 'network/routes': {
1444
+ docs: {
1445
+ content: `import { FastifyInstance } from 'fastify'
1446
+ import fastifySwagger from 'fastify-swagger'
1447
+
1448
+ const Docs = (app: FastifyInstance, prefix = '/api'): void => {
1449
+ app.register(fastifySwagger, {
1450
+ routePrefix: \`\${prefix}/docs\`,
1451
+ openapi: {
1452
+ info: {
1453
+ title: 'Test swagger',
1454
+ description: 'Testing the Fastify swagger API',
1455
+ version: '0.1.0',
1456
+ contact: {
1457
+ email: 'sluzquinosa@uni.pe'
1458
+ },
1459
+ license: {
1460
+ name: 'MIT',
1461
+ url: 'https://opensource.org/licenses/MIT'
1462
+ }
1463
+ },
1464
+ servers: [
1465
+ {
1466
+ url: 'http://localhost:1996/api',
1467
+ description: 'test-fastify local API'
1468
+ }
1469
+ ],
1470
+ tags: [
1471
+ {
1472
+ name: 'user',
1473
+ description: 'User related endpoints'
1474
+ }
1475
+ ]
1476
+ },
1477
+ exposeRoute: true
1478
+ })
1479
+ }
1480
+
1481
+ export { Docs }
1482
+ `,
1483
+ file: `${projectName}/src/network/routes/docs.ts`
1484
+ },
1485
+ home: {
1486
+ content: `import { FastifyInstance } from 'fastify'
1487
+ import { response } from 'network/response'
1488
+
1489
+ const Home = (app: FastifyInstance, prefix = '/'): void => {
1490
+ app.get(\`\${prefix}\`, (request, reply) => {
1491
+ response({
1492
+ error: false,
1493
+ message: 'Welcome to your Fastify Backend!',
1494
+ reply,
1495
+ status: 200
1496
+ })
1497
+ })
1498
+ }
1499
+
1500
+ export { Home }
1501
+ `,
1502
+ file: `${projectName}/src/network/routes/home.ts`
1503
+ },
1504
+ index: {
1505
+ content: `export * from './home'
1506
+ export * from './user'
1507
+ export * from './docs'
1508
+ `,
1509
+ file: `${projectName}/src/network/routes/index.ts`
1510
+ },
1511
+ user: {
1512
+ content: `import { FastifyInstance } from 'fastify'
1513
+
1514
+ import { response } from 'network/response'
1515
+ import { userSchema, idSchema, storeUserSchema } from './schemas'
1516
+ import { UserService } from 'services'
1517
+
1518
+ const User = (app: FastifyInstance, prefix = '/api'): void => {
1519
+ app
1520
+ .post<{ Body: { args: Omit<DtoUser, 'id'> } }>(
1521
+ \`\${prefix}/users\`,
1522
+ {
1523
+ schema: {
1524
+ body: {
1525
+ args: storeUserSchema
1526
+ },
1527
+ response: {
1528
+ 200: {
1529
+ error: {
1530
+ type: 'boolean'
1531
+ },
1532
+ message: userSchema
1045
1533
  }
1046
1534
  },
1047
- "500": {
1048
- "description": "Internal server error",
1049
- "content": {
1050
- "application/json": {
1051
- "schema": {
1052
- "$ref": "#/components/schemas/DefaultError"
1053
- }
1054
- }
1055
- }
1056
- }
1535
+ tags: ['user']
1057
1536
  }
1058
1537
  },
1059
- "delete": {
1060
- "tags": [
1061
- "user"
1062
- ],
1063
- "summary": "Delete one user from the database",
1064
- "operationId": "delete",
1065
- "parameters": [
1066
- {
1067
- "name": "id",
1068
- "in": "path",
1069
- "description": "MongoDB user id",
1070
- "required": true,
1071
- "style": "simple",
1072
- "explode": false,
1073
- "schema": {
1074
- "type": "string"
1075
- }
1538
+ async (request, reply) => {
1539
+ const {
1540
+ body: {
1541
+ args: { lastName, name }
1076
1542
  }
1077
- ],
1078
- "responses": {
1079
- "200": {
1080
- "description": "User successfully deleted",
1081
- "content": {
1082
- "application/json": {
1083
- "schema": {
1084
- "$ref": "#/components/schemas/DefaultSuccess"
1085
- }
1086
- }
1087
- }
1088
- },
1089
- "404": {
1090
- "description": "User not found",
1091
- "content": {
1092
- "application/json": {
1093
- "schema": {
1094
- "$ref": "#/components/schemas/DefaultError"
1095
- }
1543
+ } = request
1544
+ const us = new UserService({ lastName, name })
1545
+ const user = await us.process({ type: 'store' })
1546
+
1547
+ response({
1548
+ error: false,
1549
+ message: user,
1550
+ reply,
1551
+ status: 200
1552
+ })
1553
+ }
1554
+ )
1555
+ .get(
1556
+ \`\${prefix}/users\`,
1557
+ {
1558
+ schema: {
1559
+ response: {
1560
+ 200: {
1561
+ error: {
1562
+ type: 'boolean'
1563
+ },
1564
+ message: {
1565
+ type: 'array',
1566
+ items: userSchema
1096
1567
  }
1097
1568
  }
1098
1569
  },
1099
- "422": {
1100
- "description": "Invalid request format",
1101
- "content": {
1102
- "application/json": {
1103
- "schema": {
1104
- "$ref": "#/components/schemas/DefaultError"
1105
- }
1570
+ tags: ['user']
1571
+ }
1572
+ },
1573
+ async (request, reply) => {
1574
+ const us = new UserService()
1575
+ const users = await us.process({ type: 'getAll' })
1576
+
1577
+ response({
1578
+ error: false,
1579
+ message: users,
1580
+ reply,
1581
+ status: 200
1582
+ })
1583
+ }
1584
+ )
1585
+ .delete(
1586
+ \`\${prefix}/users\`,
1587
+ {
1588
+ schema: {
1589
+ response: {
1590
+ 200: {
1591
+ error: {
1592
+ type: 'boolean'
1593
+ },
1594
+ message: {
1595
+ type: 'string'
1106
1596
  }
1107
1597
  }
1108
1598
  },
1109
- "500": {
1110
- "description": "Internal server error",
1111
- "content": {
1112
- "application/json": {
1113
- "schema": {
1114
- "$ref": "#/components/schemas/DefaultError"
1115
- }
1116
- }
1117
- }
1118
- }
1599
+ tags: ['user']
1119
1600
  }
1601
+ },
1602
+ async (request, reply) => {
1603
+ const us = new UserService()
1604
+ const result = await us.process({ type: 'deleteAll' })
1605
+
1606
+ response({
1607
+ error: false,
1608
+ message: result,
1609
+ reply,
1610
+ status: 200
1611
+ })
1120
1612
  }
1121
- }
1122
- },
1123
- "components": {
1124
- "schemas": {
1125
- "User": {
1126
- "type": "object",
1127
- "properties": {
1128
- "id": {
1129
- "type": "string"
1613
+ )
1614
+ .get<{ Params: { id: string } }>(
1615
+ \`\${prefix}/user/:id\`,
1616
+ {
1617
+ schema: {
1618
+ params: {
1619
+ id: idSchema
1130
1620
  },
1131
- "lastName": {
1132
- "type": "string"
1621
+ response: {
1622
+ 200: {
1623
+ error: {
1624
+ type: 'boolean'
1625
+ },
1626
+ message: userSchema
1627
+ }
1133
1628
  },
1134
- "name": {
1135
- "type": "string"
1136
- }
1629
+ tags: ['user']
1137
1630
  }
1138
1631
  },
1139
- "DefaultSuccess": {
1140
- "type": "object",
1141
- "properties": {
1142
- "error": {
1143
- "type": "boolean",
1144
- "default": false
1632
+ async (request, reply) => {
1633
+ const {
1634
+ params: { id }
1635
+ } = request
1636
+ const us = new UserService({ id })
1637
+ const user = await us.process({ type: 'getOne' })
1638
+
1639
+ response({
1640
+ error: false,
1641
+ message: user,
1642
+ reply,
1643
+ status: 200
1644
+ })
1645
+ }
1646
+ )
1647
+ .patch<{ Body: { args: Omit<DtoUser, 'id'> }; Params: { id: string } }>(
1648
+ \`\${prefix}/user/:id\`,
1649
+ {
1650
+ schema: {
1651
+ body: {
1652
+ args: userSchema
1145
1653
  },
1146
- "message": {
1147
- "type": "object",
1148
- "properties": {
1149
- "result": {
1150
- "type": "string"
1151
- }
1654
+ params: {
1655
+ id: idSchema
1656
+ },
1657
+ response: {
1658
+ 200: {
1659
+ error: {
1660
+ type: 'boolean'
1661
+ },
1662
+ message: userSchema
1152
1663
  }
1153
- }
1664
+ },
1665
+ tags: ['user']
1154
1666
  }
1155
1667
  },
1156
- "DefaultError": {
1157
- "type": "object",
1158
- "properties": {
1159
- "error": {
1160
- "type": "boolean",
1161
- "default": true
1668
+ async (request, reply) => {
1669
+ const {
1670
+ body: {
1671
+ args: { name, lastName }
1162
1672
  },
1163
- "message": {
1164
- "type": "object",
1165
- "properties": {
1166
- "result": {
1167
- "type": "string"
1168
- }
1169
- }
1170
- }
1171
- }
1673
+ params: { id }
1674
+ } = request
1675
+ const us = new UserService({ name, lastName, id })
1676
+ const user = await us.process({ type: 'update' })
1677
+
1678
+ response({
1679
+ error: false,
1680
+ message: user,
1681
+ reply,
1682
+ status: 200
1683
+ })
1172
1684
  }
1173
- },
1174
- "requestBodies": {
1175
- "DtoUser": {
1176
- "description": "User name and last name",
1177
- "content": {
1178
- "application/json": {
1179
- "schema": {
1180
- "type": "object",
1181
- "properties": {
1182
- "args": {
1183
- "type": "object",
1184
- "properties": {
1185
- "name": {
1186
- "type": "string"
1187
- },
1188
- "lastName": {
1189
- "type": "string"
1190
- }
1191
- }
1192
- }
1685
+ )
1686
+ .delete<{ Params: { id: string } }>(
1687
+ \`\${prefix}/user/:id\`,
1688
+ {
1689
+ schema: {
1690
+ params: {
1691
+ id: idSchema
1692
+ },
1693
+ response: {
1694
+ 200: {
1695
+ error: {
1696
+ type: 'boolean'
1697
+ },
1698
+ message: {
1699
+ type: 'string'
1193
1700
  }
1194
1701
  }
1195
- }
1196
- },
1197
- "required": true
1198
- }
1199
- }
1200
- }
1201
- }`,
1202
- file: `${projectName}/src/utils/docs.json`
1702
+ },
1703
+ tags: ['user']
1704
+ }
1203
1705
  },
1204
- index: {
1205
- content: "export { default as docs } from './docs.json'\n",
1206
- file: `${projectName}/src/utils/index.ts`
1706
+ async (request, reply) => {
1707
+ const {
1708
+ params: { id }
1709
+ } = request
1710
+ const us = new UserService({ id })
1711
+ const result = await us.process({ type: 'delete' })
1712
+
1713
+ response({
1714
+ error: false,
1715
+ message: result,
1716
+ reply,
1717
+ status: 200
1718
+ })
1719
+ }
1720
+ )
1721
+ }
1722
+
1723
+ export { User }
1724
+ `,
1725
+ file: `${projectName}/src/network/routes/user.ts`
1207
1726
  }
1208
1727
  },
1209
- '.env': {
1210
- content: `MONGO_URI = mongodb://mongo:mongo@mongo:27017/${projectName}`,
1211
- file: `${projectName}/.env`
1212
- },
1213
- index: {
1214
- content: `import { Server } from './network'
1728
+ 'network/routes/schemas': {
1729
+ index: {
1730
+ content: `import { Type } from '@sinclair/typebox'
1215
1731
 
1216
- Server.start()
1732
+ const idSchema = Type.String({ minLength: 24, maxLength: 24 })
1733
+
1734
+ export { idSchema }
1735
+ export * from './user'
1217
1736
  `,
1218
- file: `${projectName}/src/index.ts`
1737
+ file: `${projectName}/src/network/routes/schemas/index.ts`
1738
+ },
1739
+ user: {
1740
+ content: `import { Type } from '@sinclair/typebox'
1741
+
1742
+ const userSchema = Type.Object({
1743
+ id: Type.Optional(Type.String({ minLength: 24, maxLength: 24 })),
1744
+ lastName: Type.Optional(Type.String()),
1745
+ name: Type.Optional(Type.String()),
1746
+ updatedAt: Type.Optional(Type.String())
1747
+ })
1748
+
1749
+ const storeUserSchema = Type.Object({
1750
+ lastName: Type.String(),
1751
+ name: Type.String()
1752
+ })
1753
+
1754
+ export { userSchema, storeUserSchema }
1755
+ `,
1756
+ file: `${projectName}/src/network/routes/schemas/user.ts`
1757
+ }
1219
1758
  }
1220
1759
  }
1221
1760
 
1761
+ const expressFolders = `${projectName}/src/utils`
1762
+
1222
1763
  const createFoldersCommands = `mkdir ${projectName}/src \
1223
1764
  ${projectName}/src/@types \
1224
1765
  ${projectName}/src/@types/dto \
1225
1766
  ${projectName}/src/@types/custom \
1226
1767
  ${projectName}/src/@types/models \
1227
- ${projectName}/src/services \
1228
- ${projectName}/src/services/utils \
1229
- ${projectName}/src/services/utils/messages \
1230
1768
  ${projectName}/src/database \
1231
1769
  ${projectName}/src/database/mongo \
1232
1770
  ${projectName}/src/database/mongo/models \
@@ -1234,8 +1772,10 @@ ${projectName}/src/database/mongo/queries \
1234
1772
  ${projectName}/src/network \
1235
1773
  ${projectName}/src/network/routes \
1236
1774
  ${projectName}/src/network/routes/schemas \
1237
- ${projectName}/src/test \
1238
- ${projectName}/src/utils
1775
+ ${projectName}/src/services \
1776
+ ${projectName}/src/services/utils \
1777
+ ${projectName}/src/services/utils/messages \
1778
+ ${fastify ? '' : `${expressFolders}`}
1239
1779
  `
1240
1780
 
1241
1781
  if (os.platform() === 'win32')
@@ -1245,16 +1785,6 @@ ${projectName}/src/utils
1245
1785
  // /@types
1246
1786
  await writeFile(data['@types'].index.file, data['@types'].index.content)
1247
1787
 
1248
- // /@types/custom
1249
- await writeFile(
1250
- data['@types/custom'].request.file,
1251
- data['@types/custom'].request.content
1252
- )
1253
- await writeFile(
1254
- data['@types/custom'].response.file,
1255
- data['@types/custom'].response.content
1256
- )
1257
-
1258
1788
  // /@types/dto
1259
1789
  await writeFile(data['@types/dto'].user.file, data['@types/dto'].user.content)
1260
1790
 
@@ -1309,44 +1839,112 @@ ${projectName}/src/utils
1309
1839
 
1310
1840
  // /network
1311
1841
  await writeFile(data.network.index.file, data.network.index.content)
1312
- await writeFile(data.network.response.file, data.network.response.content)
1313
- await writeFile(data.network.router.file, data.network.router.content)
1314
- await writeFile(data.network.server.file, data.network.server.content)
1315
-
1316
- // /network/routes
1317
- await writeFile(
1318
- data['network/routes'].home.file,
1319
- data['network/routes'].home.content
1320
- )
1321
- await writeFile(
1322
- data['network/routes'].user.file,
1323
- data['network/routes'].user.content
1324
- )
1325
- await writeFile(
1326
- data['network/routes'].index.file,
1327
- data['network/routes'].index.content
1328
- )
1329
-
1330
- // /network/routes/schemas
1331
- await writeFile(
1332
- data['network/routes/schemas'].index.file,
1333
- data['network/routes/schemas'].index.content
1334
- )
1335
- await writeFile(
1336
- data['network/routes/schemas'].user.file,
1337
- data['network/routes/schemas'].user.content
1338
- )
1339
1842
 
1340
1843
  // /test
1341
1844
  await writeFile(data.test.index.file, data.test.index.content)
1342
1845
 
1343
- // /utils
1344
- await writeFile(data.utils.docs.file, data.utils.docs.content)
1345
- await writeFile(data.utils.index.file, data.utils.index.content)
1346
-
1347
1846
  // .env
1348
1847
  await writeFile(data['.env'].file, data['.env'].content)
1349
1848
 
1350
1849
  // index
1351
1850
  await writeFile(data.index.file, data.index.content)
1851
+
1852
+ if (fastify) {
1853
+ // /network
1854
+ await writeFile(
1855
+ fastifyData.network.response.file,
1856
+ fastifyData.network.response.content
1857
+ )
1858
+ await writeFile(
1859
+ fastifyData.network.router.file,
1860
+ fastifyData.network.router.content
1861
+ )
1862
+ await writeFile(
1863
+ fastifyData.network.server.file,
1864
+ fastifyData.network.server.content
1865
+ )
1866
+
1867
+ // /network/routes
1868
+ await writeFile(
1869
+ fastifyData['network/routes'].docs.file,
1870
+ fastifyData['network/routes'].docs.content
1871
+ )
1872
+ await writeFile(
1873
+ fastifyData['network/routes'].home.file,
1874
+ fastifyData['network/routes'].home.content
1875
+ )
1876
+ await writeFile(
1877
+ fastifyData['network/routes'].user.file,
1878
+ fastifyData['network/routes'].user.content
1879
+ )
1880
+ await writeFile(
1881
+ fastifyData['network/routes'].index.file,
1882
+ fastifyData['network/routes'].index.content
1883
+ )
1884
+
1885
+ // /network/routes/schemas
1886
+ await writeFile(
1887
+ fastifyData['network/routes/schemas'].index.file,
1888
+ fastifyData['network/routes/schemas'].index.content
1889
+ )
1890
+ await writeFile(
1891
+ fastifyData['network/routes/schemas'].user.file,
1892
+ fastifyData['network/routes/schemas'].user.content
1893
+ )
1894
+ } else {
1895
+ // /network
1896
+ await writeFile(
1897
+ expressData.network.response.file,
1898
+ expressData.network.response.content
1899
+ )
1900
+ await writeFile(
1901
+ expressData.network.router.file,
1902
+ expressData.network.router.content
1903
+ )
1904
+ await writeFile(
1905
+ expressData.network.server.file,
1906
+ expressData.network.server.content
1907
+ )
1908
+
1909
+ // /network/routes
1910
+ await writeFile(
1911
+ expressData['network/routes'].home.file,
1912
+ expressData['network/routes'].home.content
1913
+ )
1914
+ await writeFile(
1915
+ expressData['network/routes'].user.file,
1916
+ expressData['network/routes'].user.content
1917
+ )
1918
+ await writeFile(
1919
+ expressData['network/routes'].index.file,
1920
+ expressData['network/routes'].index.content
1921
+ )
1922
+
1923
+ // /network/routes/schemas
1924
+ await writeFile(
1925
+ expressData['network/routes/schemas'].index.file,
1926
+ expressData['network/routes/schemas'].index.content
1927
+ )
1928
+ await writeFile(
1929
+ expressData['network/routes/schemas'].user.file,
1930
+ expressData['network/routes/schemas'].user.content
1931
+ )
1932
+
1933
+ // /utils
1934
+ await writeFile(expressData.utils.docs.file, expressData.utils.docs.content)
1935
+ await writeFile(
1936
+ expressData.utils.index.file,
1937
+ expressData.utils.index.content
1938
+ )
1939
+
1940
+ // /@types/custom
1941
+ await writeFile(
1942
+ expressData['@types/custom'].request.file,
1943
+ expressData['@types/custom'].request.content
1944
+ )
1945
+ await writeFile(
1946
+ expressData['@types/custom'].response.file,
1947
+ expressData['@types/custom'].response.content
1948
+ )
1949
+ }
1352
1950
  }