@jcbuisson/express-x 1.3.3 → 1.3.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jcbuisson/express-x",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "src/index.mjs",
@@ -12,7 +12,7 @@
12
12
  "license": "MIT",
13
13
  "private": false,
14
14
  "scripts": {
15
- "test": "mocha --require esm 'test/**/*.test.js'",
15
+ "test": "node --test",
16
16
  "generate": "npx prisma generate",
17
17
  "migrate": "npx prisma migrate dev --name init"
18
18
  },
@@ -23,12 +23,7 @@
23
23
  "axios": "^1.4.0",
24
24
  "bcrypt": "^5.1.0",
25
25
  "config": "^3.3.9",
26
- "esm": "^3.2.25",
27
26
  "express": "^4.18.2",
28
27
  "socket.io": "^4.6.0"
29
- },
30
- "devDependencies": {
31
- "chai": "^4.3.7",
32
- "mocha": "^10.2.0"
33
28
  }
34
29
  }
package/src/server.mjs CHANGED
@@ -19,9 +19,9 @@ export function expressX(options = {}) {
19
19
  let lastConnectionId = 1
20
20
 
21
21
  // logging function - a winston logger must be configured first
22
- app.log = (severity, ...messages) => {
22
+ app.log = (severity, message) => {
23
23
  const logger = app.get('logger')
24
- if (logger) logger.log(severity, ...messages)
24
+ if (logger) logger.log(severity, message)
25
25
  }
26
26
 
27
27
  /*
@@ -74,8 +74,8 @@ export function expressX(options = {}) {
74
74
 
75
75
  // call method
76
76
  const result = await method(...context.args)
77
- app.log('debug', 'result', result)
78
-
77
+ app.log('debug', `result ${result}`)
78
+
79
79
  // call 'after' hooks
80
80
  const afterMethodHooks = service?.hooks?.after && service.hooks.after[methodName] || []
81
81
  const afterAllHooks = service?.hooks?.after?.all || []
@@ -127,81 +127,68 @@ export function expressX(options = {}) {
127
127
  http: { name: service.name }
128
128
  }
129
129
 
130
- // introspect table schema
131
- async function getFieldTypes() {
132
- const fieldTypes = {}
133
- if (service.prisma._activeProvider === 'sqlite') {
134
- const fieldInfo = await service.prisma.$queryRawUnsafe(`
135
- PRAGMA table_info(${service.entity})
136
- `)
137
- fieldInfo.forEach(column => {
138
- fieldTypes[column.name] = column.type.toLowerCase()
139
- })
140
- } else if (service.prisma._activeProvider === 'postgresql') {
141
- const fieldInfo = await service.prisma.$queryRawUnsafe(`
142
- SELECT column_name, data_type
143
- FROM information_schema.columns
144
- WHERE table_name = '${service.entity}';
145
- `)
146
- fieldInfo.forEach(column => {
147
- fieldTypes[column.column_name] = column.data_type
148
- })
149
- }
150
- return fieldTypes
130
+ // introspect schema
131
+ async function getTypesMap() {
132
+ const dmmf = await service.prisma._getDmmf()
133
+ const fieldDescriptions = dmmf.modelMap[service.name].fields
134
+ return fieldDescriptions.reduce((accu, descr) => {
135
+ accu[descr.name] = descr.type
136
+ return accu
137
+ }, {})
151
138
  }
152
139
 
153
140
 
154
141
  app.post(path, async (req, res) => {
155
- app.log('verbose', "http request POST", req.url)
142
+ app.log('verbose', `http request POST ${req.url}`)
156
143
  context.http.req = req
157
144
  try {
158
145
  const value = await service.__create(context, { data: req.body })
159
146
  publish(service, 'create', value)
160
147
  res.json(value)
161
148
  } catch(err) {
162
- console.log('callErr', err)
149
+ app.log('error', err)
163
150
  res.status(500).send(err.toString())
164
151
  }
165
152
  })
166
153
 
167
154
  app.get(path, async (req, res) => {
168
- app.log('verbose', "http request GET", req.url)
155
+ app.log('verbose', `http request GET ${req.url}`)
169
156
  context.http.req = req
170
157
  const query = { ...req.query }
171
158
  try {
172
159
  // the values in `req.query` are all strings, but Prisma need proper types
173
160
  // we need to introspect column types and do the proper transtyping
174
161
  for (const fieldName in query) {
175
- if (!service.fieldTypes) service.fieldTypes = await getFieldTypes()
176
- const fieldType = service.fieldTypes[fieldName]
162
+ const typesDict = await getTypesMap()
163
+ const fieldType = typesDict[fieldName]
177
164
 
178
- if (fieldType === 'integer') {
165
+ if (fieldType === 'Int') {
179
166
  query[fieldName] = parseInt(query[fieldName])
180
- } else if (fieldType === 'numeric') {
167
+ } else if (fieldType === 'Float') {
181
168
  query[fieldName] = parseFloat(query[fieldName])
182
- } else if (fieldType === 'boolean') {
169
+ } else if (fieldType === 'Boolean') {
183
170
  query[fieldName] = (query[fieldName] === 't') ? true : false
184
- } else if (fieldType === 'text' || fieldType === 'character varying') {
171
+ } else if (fieldType === 'String') {
185
172
  query[fieldName] = query[fieldName]
186
173
  } else {
187
174
  // ?
188
175
  query[fieldName] = query[fieldName]
189
176
  }
190
177
  }
191
-
178
+ // call __findMany
192
179
  const values = await service.__findMany(context, {
193
180
  where: query,
194
181
  })
195
182
  publish(service, 'findMany', values)
196
183
  res.json(values)
197
184
  } catch(err) {
198
- console.log('callErr', err)
185
+ app.log('error', err)
199
186
  res.status(500).send(err.toString())
200
187
  }
201
188
  })
202
189
 
203
190
  app.get(`${path}/:id`, async (req, res) => {
204
- app.log('verbose', "http request GET", req.url)
191
+ app.log('verbose', `http request GET ${req.url}`)
205
192
  context.http.req = req
206
193
  try {
207
194
  const value = await service.__findUnique(context, {
@@ -212,13 +199,13 @@ export function expressX(options = {}) {
212
199
  publish(service, 'findUnique', value)
213
200
  res.json(value)
214
201
  } catch(err) {
215
- console.log('callErr', err)
202
+ app.log('error', err)
216
203
  res.status(500).send(err.toString())
217
204
  }
218
205
  })
219
206
 
220
207
  app.patch(`${path}/:id`, async (req, res) => {
221
- app.log('verbose', "http request PATCH", req.url)
208
+ app.log('verbose', `http request PATCH ${req.url}`)
222
209
  context.http.req = req
223
210
  try {
224
211
  const value = await service.__update(context, {
@@ -230,13 +217,13 @@ export function expressX(options = {}) {
230
217
  publish(service, 'update', value)
231
218
  res.json(value)
232
219
  } catch(err) {
233
- console.log('callErr', err)
220
+ app.log('error', err)
234
221
  res.status(500).send(err.toString())
235
222
  }
236
223
  })
237
224
 
238
225
  app.delete(`${path}/:id`, async (req, res) => {
239
- app.log('verbose', "http request DELETE", req.url)
226
+ app.log('verbose', `http request DELETE ${req.url}`)
240
227
  context.http.req = req
241
228
  try {
242
229
  const value = await service.__delete(context, {
@@ -247,7 +234,7 @@ export function expressX(options = {}) {
247
234
  publish(service, 'delete', value)
248
235
  res.json(value)
249
236
  } catch(err) {
250
- console.log('callErr', err)
237
+ app.log('error', err)
251
238
  res.status(500).send(err.toString())
252
239
  }
253
240
  })
@@ -275,7 +262,7 @@ export function expressX(options = {}) {
275
262
  }
276
263
  // store connection in cache
277
264
  connections[connection.id] = connection
278
- app.log('verbose', 'active connections', Object.keys(connections))
265
+ app.log('verbose', `active connections ${Object.keys(connections)}`)
279
266
 
280
267
  // emit 'connection' event for app (expressjs extends EventEmitter)
281
268
  app.emit('connection', connection)
@@ -284,7 +271,7 @@ export function expressX(options = {}) {
284
271
  socket.emit('connected', connection.id)
285
272
 
286
273
  socket.on('disconnect', () => {
287
- app.log('verbose', 'Client disconnected', connection.id)
274
+ app.log('verbose', `Client disconnected ${connection.id}`)
288
275
  delete connections[connection.id]
289
276
  })
290
277
 
@@ -294,7 +281,7 @@ export function expressX(options = {}) {
294
281
  * Emit in return a 'client-response' message
295
282
  */
296
283
  socket.on('client-request', async ({ uid, name, action, args }) => {
297
- app.log('verbose', "client-request", uid, name, action, args)
284
+ app.log('verbose', `client-request ${uid} ${name} ${action} ${args}`)
298
285
  if (name in services) {
299
286
  const service = services[name]
300
287
  try {
@@ -314,7 +301,7 @@ export function expressX(options = {}) {
314
301
  // pub/sub: send event on associated channels
315
302
  publish(service, action, result)
316
303
  } catch(err) {
317
- console.log('callErr', err)
304
+ app.log('error', err)
318
305
  io.emit('client-response', {
319
306
  uid,
320
307
  error: err.toString(),
@@ -347,12 +334,12 @@ export function expressX(options = {}) {
347
334
  const publishFunc = service.publishCallback
348
335
  if (publishFunc) {
349
336
  const channelNames = await publishFunc(result, app)
350
- app.log('verbose', 'publish channels', service.name, action, channelNames)
337
+ app.log('verbose', `publish channels ${service.name} ${action} ${channelNames}`)
351
338
  for (const channelName of channelNames) {
352
- app.log('verbose', 'service-event', service.name, action, channelName)
339
+ app.log('verbose', `service-event ${service.name} ${action} ${channelName}`)
353
340
  const connectionList = Object.values(connections).filter(cnx => cnx.channelNames.has(channelName))
354
341
  for (const connection of connectionList) {
355
- app.log('verbose', 'emit to', connection.id, service.name, action, result)
342
+ app.log('verbose', `emit to ${connection.id} ${service.name} ${action} ${result}`)
356
343
  connection.socket.emit('service-event', {
357
344
  name: service.name,
358
345
  action,
@@ -4,7 +4,9 @@ import axios from 'axios'
4
4
  import io from 'socket.io-client'
5
5
 
6
6
 
7
- import { assert } from 'chai'
7
+ // import { assert } from 'chai'
8
+ import { describe, it, before, after, beforeEach, afterEach } from 'node:test'
9
+ import { strict as assert } from 'node:assert'
8
10
 
9
11
  import { expressX, expressXClient } from '../src/index.mjs'
10
12
 
@@ -12,13 +14,18 @@ import { expressX, expressXClient } from '../src/index.mjs'
12
14
  // `app` is a regular express application, enhanced with services and real-time features
13
15
  const app = expressX()
14
16
 
15
- app.createDatabaseService('User')
16
- app.createDatabaseService('Post')
17
-
18
17
 
19
18
 
20
19
  describe('ExpressX API (no running server)', () => {
21
20
 
21
+ before(async () => {
22
+ app.createDatabaseService('User')
23
+ app.createDatabaseService('Post')
24
+
25
+ await app.service('User').deleteMany()
26
+ await app.service('Post').deleteMany()
27
+ })
28
+
22
29
  it("can delete all users", async () => {
23
30
  const res = await app.service('User').deleteMany()
24
31
  console.log('res delete', res)
@@ -61,24 +68,41 @@ describe('HTTP/REST API', () => {
61
68
 
62
69
  let chris
63
70
 
64
- before(() => {
71
+ before(async () => {
72
+ console.log("before")
65
73
  // add body parsers for http requests
66
74
  app.use(bodyParser.json())
67
75
  app.use(bodyParser.urlencoded({ extended: false }))
68
76
 
77
+ app.createDatabaseService('User')
78
+ app.createDatabaseService('Post')
79
+
80
+ await app.service('User').deleteMany()
81
+ await app.service('Post').deleteMany()
82
+
69
83
  // add http/rest endpoints
70
84
  app.addHttpRest('/api/user', app.service('User'))
71
85
  app.addHttpRest('/api/post', app.service('Post'))
72
86
 
73
- app.server.listen(8008, () => console.log(`App listening at http://localhost:8008`))
87
+ await new Promise((resolve) => {
88
+ app.server.listen(8008, () => {
89
+ console.log(`App listening at http://localhost:8008`)
90
+ resolve()
91
+ })
92
+ })
93
+ })
94
+
95
+ after(() => {
96
+ console.log("after")
97
+ app.server.close()
74
98
  })
75
99
 
76
100
  it("can create a user", async () => {
77
101
  const res = await axios.post('http://localhost:8008/api/user', {
78
- name: "carole",
79
- email: 'carole@mail.fr'
102
+ name: "chris",
103
+ email: 'chris@mail.fr'
80
104
  })
81
- assert(res?.data?.name === 'carole')
105
+ assert(res?.data?.name === 'chris')
82
106
  })
83
107
 
84
108
  it("can find users", async () => {
@@ -110,23 +134,32 @@ describe('HTTP/REST API', () => {
110
134
  })
111
135
 
112
136
  after(async () => {
113
- await app.server.close()
137
+ app.server.close()
114
138
  })
115
139
  })
116
140
 
117
141
 
118
- // test compatibility with `express-x-client`
142
+ // test compatibility with client API
119
143
  describe('Client API', () => {
120
144
 
121
145
  let clientApp, socket
122
146
 
123
- before(() => {
124
- app.server.listen(8008, () => console.log(`App listening at http://localhost:8008`))
147
+ before(async () => {
148
+ await new Promise((resolve) => {
149
+ app.server.listen(8008, () => {
150
+ console.log(`App listening at http://localhost:8008`)
151
+ resolve()
152
+ })
153
+ })
125
154
 
126
155
  socket = io('http://localhost:8008', { transports: ["websocket"] })
127
156
  clientApp = expressXClient(socket)
128
157
  })
129
158
 
159
+ after(async () => {
160
+ app.server.close()
161
+ })
162
+
130
163
  it("can create a user", async () => {
131
164
  const user = await clientApp.service('User').create({
132
165
  data: {