@anthonylzq/simba.js 7.2.0 → 8.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,10 +4,17 @@ const exec = util.promisify(require('child_process').exec)
4
4
 
5
5
  const express = require('./express')
6
6
  const fastifyF = require('./fastify')
7
- const utils = require('./utils')
8
7
  const writeFile = require('../../utils/writeFile')
9
8
  const { ENVIRONMENTS_WITH_DB_URI } = require('../../utils/constants')
10
9
 
10
+ const dbDefaultPorts = {
11
+ mongo: 27017,
12
+ postgres: 5432,
13
+ mysql: 3306,
14
+ mariadb: 3306,
15
+ mssql: 1433
16
+ }
17
+
11
18
  /**
12
19
  * @param {Object} args
13
20
  * @param {String} args.projectName
@@ -40,12 +47,6 @@ Content-Type: application/json
40
47
  }
41
48
  }
42
49
 
43
- ### Testing getAll users
44
- GET http://localhost:1996/api/users
45
-
46
- ### Testing deleteAll users
47
- DELETE http://localhost:1996/api/users
48
-
49
50
  ### Testing getOne user
50
51
  GET http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
51
52
 
@@ -67,17 +68,20 @@ DELETE http://localhost:1996/api/user/60e7e3b93b01c1a7aa74cd6b
67
68
  }
68
69
  },
69
70
  '.env': {
70
- content: dbIsSQL
71
- ? `DB_URI = ${
72
- ENVIRONMENTS_WITH_DB_URI.includes(process.env.NODE_ENV)
73
- ? process.env.DB_URI
74
- : `${database}://${database}:${database}@${database}:27017/${projectName}`
75
- }`
76
- : `DB_URI = ${
77
- ENVIRONMENTS_WITH_DB_URI.includes(process.env.NODE_ENV)
78
- ? process.env.MONGO_URI
79
- : `mongodb://mongo:mongo@mongo:27017/${projectName}`
80
- }`,
71
+ content:
72
+ dbIsSQL && database !== 'sqlite'
73
+ ? `DATABASE_URL = ${
74
+ ENVIRONMENTS_WITH_DB_URI.includes(process.env.NODE_ENV)
75
+ ? process.env.DATABASE_URL
76
+ : `${database}://${database}:${database}@${database}:${dbDefaultPorts[database]}/${projectName}`
77
+ }`
78
+ : database === 'sqlite'
79
+ ? ''
80
+ : `DATABASE_URL = ${
81
+ ENVIRONMENTS_WITH_DB_URI.includes(process.env.NODE_ENV)
82
+ ? process.env.MONGO_URI
83
+ : `mongodb://mongo:mongo@mongo:27017/${projectName}`
84
+ }`,
81
85
  file: `${projectName}/.env`
82
86
  },
83
87
  index: {
@@ -104,17 +108,23 @@ Server.start()
104
108
  ]
105
109
 
106
110
  if (fastify)
107
- processes.push(fastifyF({ projectName, graphQL: graphql, database }))
111
+ processes.push(
112
+ fastifyF({
113
+ projectName,
114
+ email,
115
+ projectVersion,
116
+ graphQL: graphql,
117
+ database
118
+ })
119
+ )
108
120
  else
109
121
  processes.concat([
110
- express({ projectName, graphQL: graphql, database }),
111
- // /utils
112
- utils({
113
- express: !fastify,
122
+ express({
114
123
  projectName,
115
124
  email,
116
125
  projectVersion,
117
- graphql
126
+ graphQL: graphql,
127
+ database
118
128
  })
119
129
  ])
120
130
 
@@ -6,9 +6,10 @@ const writeFile = require('../../utils/writeFile')
6
6
  /**
7
7
  * @param {Object} args
8
8
  * @param {String} args.projectName
9
+ * @param {Boolean} args.dbIsSQL
9
10
  * @param {Boolean} args.graphql
10
11
  */
11
- module.exports = async ({ projectName, graphql }) => {
12
+ module.exports = async ({ projectName, dbIsSQL, graphQL }) => {
12
13
  const createFoldersCommand = `mkdir ${projectName}/src/schemas`
13
14
 
14
15
  if (platform() === 'win32')
@@ -17,54 +18,40 @@ module.exports = async ({ projectName, graphql }) => {
17
18
 
18
19
  const schemas = {
19
20
  index: {
20
- content: `import { Static, Type } from '@sinclair/typebox'
21
-
22
- const id = Type.String({
23
- pattern: '^[a-zA-Z0-9]{24,}$'
24
- })
25
-
26
- type Id = Static<typeof id>
27
-
28
- const idSchema = Type.Object({ id })
29
-
30
- type IdSchema = Static<typeof idSchema>
31
-
32
- export { id, Id, idSchema, IdSchema }
33
- export * from './user'
34
- `,
21
+ content: `export * from './id'\nexport * from './user'\n`,
35
22
  file: `${projectName}/src/schemas/index.ts`
36
23
  },
37
24
  user: {
38
- content: `import { Static, Type } from '@sinclair/typebox'
25
+ content: `import z from 'zod'
39
26
 
40
- import { id } from '.'
27
+ import { id } from './id'
41
28
 
42
- const user = Type.Object({
43
- lastName: Type.String(),
44
- name: Type.String()
29
+ const user = z.object({
30
+ lastName: z.string(),
31
+ name: z.string()
45
32
  })
46
33
 
47
- type User = Static<typeof user>
34
+ type User = z.infer<typeof user>
48
35
 
49
- const userWithId = Type.Intersect([user, Type.Object({ id })])
36
+ const userWithId = user.extend({ id })
50
37
 
51
- type UserWithId = Static<typeof userWithId>
38
+ type UserWithId = z.infer<typeof userWithId>
52
39
 
53
- const userDto = Type.Object({
54
- id: Type.Optional(id),
55
- lastName: Type.String(),
56
- name: Type.String(),
57
- createdAt: Type.Optional(Type.String()),
58
- updatedAt: Type.Optional(Type.String())
40
+ const userDto = z.object({
41
+ id: id.optional(),
42
+ lastName: z.string(),
43
+ name: z.string(),
44
+ createdAt: z.string().optional(),
45
+ updatedAt: z.string().optional()
59
46
  })
60
47
 
61
- type UserDTO = Static<typeof userDto>
48
+ type UserDTO = z.infer<typeof userDto>
62
49
 
63
- const storeUserDto = Type.Object({
50
+ const storeUserDto = z.object({
64
51
  args: user
65
52
  })
66
53
 
67
- type StoreUserDTO = Static<typeof storeUserDto>
54
+ type StoreUserDTO = z.infer<typeof storeUserDto>
68
55
 
69
56
  export {
70
57
  userDto,
@@ -75,37 +62,32 @@ export {
75
62
  User,
76
63
  storeUserDto,
77
64
  StoreUserDTO
78
- }
79
- `,
65
+ }\n`,
80
66
  file: `${projectName}/src/schemas/user.ts`
81
- }
82
- }
83
-
84
- if (graphql)
85
- schemas.index.content = `import { Static, Type } from '@sinclair/typebox'
86
- import Ajv from 'ajv/dist/2019.js'
87
- import addFormats from 'ajv-formats'
88
-
89
- const id = Type.String({
90
- pattern: '^[a-zA-Z0-9]{24,}$'
91
- })
67
+ },
68
+ id: {
69
+ content: `import z from 'zod'
92
70
 
93
- type ID = Static<typeof id>
71
+ const id = ${
72
+ dbIsSQL ? 'z.preprocess(val => Number(val), z.number())' : 'z.string()'
73
+ }
94
74
 
95
- const idSchema = Type.Object({ id })
75
+ type Id = z.infer<typeof id>
96
76
 
97
- type IDSchema = Static<typeof idSchema>
77
+ const idSchema = z.object({ id })
98
78
 
99
- const ajv = addFormats(new Ajv(), ['email'])
100
- .addKeyword('kind')
101
- .addKeyword('modifier')
79
+ type IdSchema = z.infer<typeof idSchema>
102
80
 
103
- export { id, ID, idSchema, IDSchema, ajv }
104
- export * from './user'
105
- `
81
+ export { id, Id, idSchema, IdSchema }\n`,
82
+ file: `${projectName}/src/schemas/id.ts`
83
+ }
84
+ }
106
85
 
107
- await Promise.all([
86
+ const processes = [
108
87
  writeFile(schemas.index.file, schemas.index.content),
109
- writeFile(schemas.user.file, schemas.user.content)
110
- ])
88
+ writeFile(schemas.user.file, schemas.user.content),
89
+ writeFile(schemas.id.file, schemas.id.content)
90
+ ]
91
+
92
+ await Promise.all(processes)
111
93
  }
@@ -1,6 +1,7 @@
1
1
  const { platform } = require('os')
2
2
  const { promisify } = require('util')
3
3
  const exec = promisify(require('child_process').exec)
4
+
4
5
  const writeFile = require('../../utils/writeFile')
5
6
 
6
7
  /**
@@ -18,201 +19,212 @@ ${projectName}/src/services/utils/messages`
18
19
 
19
20
  const services = {
20
21
  index: {
21
- content: "export * from './user'\n",
22
+ content: "export * from './User'\n",
22
23
  file: `${projectName}/src/services/index.ts`
23
24
  },
24
- user: {
25
- content: `import httpErrors from 'http-errors'
26
-
27
- import { store, remove, get, update } from 'database'
28
- import { User, UserDTO, UserWithId } from 'schemas'
29
- import { EFU, MFU, GE, errorHandling } from './utils'
30
-
31
- type Process = {
32
- type: 'store' | 'getAll' | 'deleteAll' | 'getOne' | 'update' | 'delete'
33
- }
34
-
35
- type Arguments = {
36
- id?: string
37
- user?: User
38
- userWithId?: UserWithId
39
- }
40
-
41
- class UserService {
42
- #args: Arguments
43
-
44
- constructor(args: Arguments = {}) {
45
- this.#args = args
25
+ base: {
26
+ content: `import { Debugger } from 'debug'
27
+ import httpErrors, {
28
+ HttpErrorConstructor,
29
+ NamedConstructors
30
+ } from 'http-errors'
31
+
32
+ import { Log } from 'utils'
33
+ import { GE } from './utils'
34
+
35
+ type FilterNumberKeys<T> = {
36
+ [K in keyof T]: T[K] extends HttpErrorConstructor<infer N>
37
+ ? N extends number
38
+ ? K
39
+ : never
40
+ : never
41
+ }[keyof T]
42
+
43
+ type ErrorCodes = FilterNumberKeys<NamedConstructors>
44
+
45
+ class BaseHttpService implements Log {
46
+ #debug: Debugger
47
+
48
+ constructor(debug: Debugger) {
49
+ this.#debug = debug
46
50
  }
47
51
 
48
- public process({ type }: Process): Promise<string | UserDTO | UserDTO[]> {
49
- switch (type) {
50
- case 'store':
51
- return this.#store()
52
- case 'getAll':
53
- return this.#getAll()
54
- case 'deleteAll':
55
- return this.#deleteAll()
56
- case 'getOne':
57
- return this.#getOne()
58
- case 'update':
59
- return this.#update()
60
- case 'delete':
61
- return this.#delete()
62
- default:
63
- throw new httpErrors.InternalServerError(GE.INTERNAL_SERVER_ERROR)
64
- }
52
+ log({
53
+ method,
54
+ value,
55
+ content
56
+ }: {
57
+ method: string
58
+ value: string
59
+ content: unknown
60
+ }) {
61
+ this.#debug(
62
+ \`Service invoked -> \${
63
+ this.constructor.name
64
+ } ~ \${method} ~ value: \${value} ~ content: \${JSON.stringify(content)}\`
65
+ )
65
66
  }
66
67
 
67
- async #store(): Promise<UserDTO> {
68
- try {
69
- if (!this.#args.user)
70
- throw new httpErrors.UnprocessableEntity(GE.INTERNAL_SERVER_ERROR)
71
-
72
- const result = await store(this.#args.user)
73
-
74
- return result
75
- } catch (e) {
76
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
68
+ errorHandling(
69
+ error: unknown,
70
+ {
71
+ message,
72
+ code
73
+ }:
74
+ | {
75
+ message?: string
76
+ code?: never
77
+ }
78
+ | {
79
+ message?: never
80
+ code: ErrorCodes
81
+ } = {
82
+ message: GE.INTERNAL_SERVER_ERROR
77
83
  }
84
+ ): never {
85
+ this.log({
86
+ method: this.errorHandling.name,
87
+ value: 'error',
88
+ content: error
89
+ })
90
+
91
+ if (code)
92
+ throw new httpErrors[code](
93
+ message ?? (error as { message: string }).message
94
+ )
95
+
96
+ throw new httpErrors.InternalServerError(
97
+ message ?? (error as { message: string }).message
98
+ )
78
99
  }
100
+ }
79
101
 
80
- async #getAll(): Promise<UserDTO[]> {
81
- try {
82
- const users = (await get()) as UserDTO[]
102
+ export { BaseHttpService }\n`,
103
+ file: `${projectName}/src/services/BaseHttp.ts`
104
+ },
105
+ user: {
106
+ content: `import debug from 'debug'
83
107
 
84
- return users
85
- } catch (e) {
86
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
87
- }
108
+ import { getById, removeById, store, update } from 'database'
109
+ import { Id, User as UserSchema } from 'schemas'
110
+ import { BaseHttpService } from './BaseHttp'
111
+ import { EFU, MFU, GE } from './utils'
112
+
113
+ class UserService extends BaseHttpService {
114
+ constructor() {
115
+ super(debug('App:Services:User'))
88
116
  }
89
117
 
90
- async #deleteAll(): Promise<string> {
118
+ async store(userDto: UserSchema) {
91
119
  try {
92
- const usersDeleted = (await remove()) as number
93
-
94
- if (usersDeleted >= 1) return MFU.ALL_USERS_DELETED
95
-
96
- if (usersDeleted === 0)
97
- throw new httpErrors.Conflict(EFU.NOTHING_TO_DELETE)
120
+ const result = await store(userDto)
121
+ super.log({ method: this.store.name, value: 'result', content: result })
98
122
 
99
- throw new httpErrors.InternalServerError(GE.INTERNAL_SERVER_ERROR)
123
+ return result
100
124
  } catch (e) {
101
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
125
+ return super.errorHandling(e)
102
126
  }
103
127
  }
104
128
 
105
- async #getOne(): Promise<UserDTO> {
129
+ async getById(id: Id) {
106
130
  try {
107
- if (!this.#args.id)
108
- throw new httpErrors.UnprocessableEntity(GE.INTERNAL_SERVER_ERROR)
109
-
110
- const { id } = this.#args
111
- const user = (await get(id)) as UserDTO | null
131
+ const user = await getById(id)
132
+ super.log({ method: this.getById.name, value: 'result', content: user })
112
133
 
113
- if (!user) throw new httpErrors.NotFound(EFU.NOT_FOUND)
134
+ if (!user)
135
+ return super.errorHandling(new Error(EFU.NOT_FOUND), { code: '404' })
114
136
 
115
137
  return user
116
138
  } catch (e) {
117
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
139
+ return super.errorHandling(e)
118
140
  }
119
141
  }
120
142
 
121
- async #update(): Promise<UserDTO> {
143
+ async update(id: Id, user: UserSchema) {
122
144
  try {
123
- if (!this.#args.userWithId || !this.#args.userWithId.id)
124
- throw new httpErrors.UnprocessableEntity(GE.INTERNAL_SERVER_ERROR)
125
-
126
- const updatedUser = await update(this.#args.userWithId)
145
+ const updatedUser = await update(id, user)
146
+ super.log({
147
+ method: this.update.name,
148
+ value: 'result',
149
+ content: updatedUser
150
+ })
127
151
 
128
- if (!updatedUser) throw new httpErrors.NotFound(EFU.NOT_FOUND)
152
+ if (!updatedUser)
153
+ return super.errorHandling(new Error(EFU.NOT_FOUND), { code: '404' })
129
154
 
130
155
  return updatedUser
131
156
  } catch (e) {
132
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
157
+ return super.errorHandling(e, { message: GE.INTERNAL_SERVER_ERROR })
133
158
  }
134
159
  }
135
160
 
136
- async #delete(): Promise<string> {
161
+ async deleteById(id: Id) {
137
162
  try {
138
- if (!this.#args.id)
139
- throw new httpErrors.UnprocessableEntity(GE.INTERNAL_SERVER_ERROR)
163
+ const deletedUser = await removeById(id)
164
+ super.log({
165
+ method: this.deleteById.name,
166
+ value: 'result',
167
+ content: deletedUser
168
+ })
140
169
 
141
- const { id } = this.#args
142
- const deletedUser = await remove(id)
143
-
144
- if (!deletedUser) throw new httpErrors.NotFound(EFU.NOT_FOUND)
170
+ if (!deletedUser)
171
+ return super.errorHandling(new Error(EFU.NOT_FOUND), { code: '404' })
145
172
 
146
173
  return MFU.USER_DELETED
147
174
  } catch (e) {
148
- return errorHandling(e, GE.INTERNAL_SERVER_ERROR)
175
+ return super.errorHandling(e)
149
176
  }
150
177
  }
151
178
  }
152
179
 
153
- export { UserService }
154
- `,
155
- file: `${projectName}/src/services/user.ts`
180
+ export { UserService }\n`,
181
+ file: `${projectName}/src/services/User.ts`
156
182
  },
157
183
  utils: {
158
184
  index: {
159
- content: `import httpErrors from 'http-errors'
160
-
161
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
- const errorHandling = (e: any, message?: string): never => {
163
- console.error(e)
164
-
165
- if (e instanceof httpErrors.HttpError) throw e
166
-
167
- throw new httpErrors.InternalServerError(message ?? e.message)
168
- }
169
-
170
- export { errorHandling }
171
- export * from './messages'
172
- `,
185
+ content: `export * from './messages'\n`,
173
186
  file: `${projectName}/src/services/utils/index.ts`
174
- }
175
- },
176
- 'utils/messages': {
177
- index: {
178
- content: `enum GenericErrors {
179
- INTERNAL_SERVER_ERROR = 'Something went wrong'
180
- }
181
-
182
- export { GenericErrors as GE }
183
- export * from './user'
184
- `,
185
- file: `${projectName}/src/services/utils/messages/index.ts`
186
187
  },
187
- user: {
188
- content: `enum ErrorForUser {
189
- NOT_FOUND = 'The requested user does not exists',
190
- NOTHING_TO_DELETE = 'There is no user to be deleted'
191
- }
188
+ messages: {
189
+ index: {
190
+ content: `const GenericErrors = {
191
+ INTERNAL_SERVER_ERROR: 'Something went wrong'
192
+ } as const
192
193
 
193
- enum MessageForUser {
194
- ALL_USERS_DELETED = 'All the users were deleted successfully',
195
- USER_DELETED = 'The requested user was successfully deleted'
196
- }
197
-
198
- export { ErrorForUser as EFU, MessageForUser as MFU }
199
- `,
200
- file: `${projectName}/src/services/utils/messages/user.ts`
194
+ export { GenericErrors as GE }
195
+ export * from './user'\n`,
196
+ file: `${projectName}/src/services/utils/messages/index.ts`
197
+ },
198
+ user: {
199
+ content: `const ErrorForUser = {
200
+ NOT_FOUND: 'The requested user does not exists',
201
+ NOTHING_TO_DELETE: 'There is no user to be deleted'
202
+ } as const
203
+
204
+ const MessageForUser = {
205
+ ALL_USERS_DELETED: 'All the users were deleted successfully',
206
+ USER_DELETED: 'The requested user was successfully deleted'
207
+ } as const
208
+
209
+ export { ErrorForUser as EFU, MessageForUser as MFU }\n`,
210
+ file: `${projectName}/src/services/utils/messages/user.ts`
211
+ }
201
212
  }
202
213
  }
203
214
  }
204
215
 
205
216
  await Promise.all([
206
217
  writeFile(services.index.file, services.index.content),
218
+ writeFile(services.base.file, services.base.content),
207
219
  writeFile(services.user.file, services.user.content),
208
220
  writeFile(services.utils.index.file, services.utils.index.content),
209
221
  writeFile(
210
- services['utils/messages'].index.file,
211
- services['utils/messages'].index.content
222
+ services.utils.messages.index.file,
223
+ services.utils.messages.index.content
212
224
  ),
213
225
  writeFile(
214
- services['utils/messages'].user.file,
215
- services['utils/messages'].user.content
226
+ services.utils.messages.user.file,
227
+ services.utils.messages.user.content
216
228
  )
217
229
  ])
218
230
  }