@jcbuisson/express-x 1.4.0 → 1.4.2
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 +1 -1
- package/src/server.mjs +43 -23
- package/test/index.test.js +1 -39
package/package.json
CHANGED
package/src/server.mjs
CHANGED
|
@@ -14,9 +14,16 @@ export function expressX(prisma, options = {}) {
|
|
|
14
14
|
if (options.ws === undefined) options.ws = { ws_prefix: "expressx" }
|
|
15
15
|
|
|
16
16
|
const services = {}
|
|
17
|
-
const connections = {}
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
app.connections = {}
|
|
19
|
+
let lastConnectionId = options.initialConnectionId || 1
|
|
20
|
+
|
|
21
|
+
app.printCnx = (label) => {
|
|
22
|
+
console.log(label)
|
|
23
|
+
for (const id in app.connections) {
|
|
24
|
+
console.log(`CNX ${id}, data ${JSON.stringify(app.connections[id].data)}`)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
20
27
|
|
|
21
28
|
// logging function - a winston logger must be configured first
|
|
22
29
|
app.log = (severity, message) => {
|
|
@@ -62,20 +69,21 @@ export function expressX(prisma, options = {}) {
|
|
|
62
69
|
const beforeMethodHooks = service?.hooks?.before && service.hooks.before[methodName] || []
|
|
63
70
|
const beforeAllHooks = service?.hooks?.before?.all || []
|
|
64
71
|
for (const hook of [...beforeMethodHooks, ...beforeAllHooks]) {
|
|
65
|
-
|
|
72
|
+
await hook(context)
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
// call method
|
|
69
76
|
const result = await method(...context.args)
|
|
70
|
-
|
|
77
|
+
// put result into context
|
|
78
|
+
context.result = result
|
|
71
79
|
|
|
72
80
|
// call 'after' hooks
|
|
73
81
|
const afterMethodHooks = service?.hooks?.after && service.hooks.after[methodName] || []
|
|
74
82
|
const afterAllHooks = service?.hooks?.after?.all || []
|
|
75
83
|
for (const hook of [...afterMethodHooks, ...afterAllHooks]) {
|
|
76
|
-
|
|
84
|
+
await hook(context)
|
|
77
85
|
}
|
|
78
|
-
return result
|
|
86
|
+
return context.result
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
// hooked version of method: `create`, etc., to be called from backend with no context
|
|
@@ -117,7 +125,8 @@ export function expressX(prisma, options = {}) {
|
|
|
117
125
|
async function addHttpRest(path, service) {
|
|
118
126
|
const context = {
|
|
119
127
|
app,
|
|
120
|
-
|
|
128
|
+
transport: 'http',
|
|
129
|
+
params: { name: service.name }
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
// introspect schema and return a map: field name => prisma type
|
|
@@ -133,7 +142,7 @@ export function expressX(prisma, options = {}) {
|
|
|
133
142
|
|
|
134
143
|
app.post(path, async (req, res) => {
|
|
135
144
|
app.log('verbose', `http request POST ${req.url}`)
|
|
136
|
-
context.
|
|
145
|
+
context.params.req = req
|
|
137
146
|
try {
|
|
138
147
|
const value = await service.__create(context, { data: req.body })
|
|
139
148
|
publish(service, 'create', value)
|
|
@@ -146,7 +155,7 @@ export function expressX(prisma, options = {}) {
|
|
|
146
155
|
|
|
147
156
|
app.get(path, async (req, res) => {
|
|
148
157
|
app.log('verbose', `http request GET ${req.url}`)
|
|
149
|
-
context.
|
|
158
|
+
context.params.req = req
|
|
150
159
|
const query = { ...req.query }
|
|
151
160
|
try {
|
|
152
161
|
// the values in `req.query` are all strings, but Prisma need proper types
|
|
@@ -182,7 +191,7 @@ export function expressX(prisma, options = {}) {
|
|
|
182
191
|
|
|
183
192
|
app.get(`${path}/:id`, async (req, res) => {
|
|
184
193
|
app.log('verbose', `http request GET ${req.url}`)
|
|
185
|
-
context.
|
|
194
|
+
context.params.req = req
|
|
186
195
|
try {
|
|
187
196
|
const value = await service.__findUnique(context, {
|
|
188
197
|
where: {
|
|
@@ -199,7 +208,7 @@ export function expressX(prisma, options = {}) {
|
|
|
199
208
|
|
|
200
209
|
app.patch(`${path}/:id`, async (req, res) => {
|
|
201
210
|
app.log('verbose', `http request PATCH ${req.url}`)
|
|
202
|
-
context.
|
|
211
|
+
context.params.req = req
|
|
203
212
|
try {
|
|
204
213
|
const value = await service.__update(context, {
|
|
205
214
|
where: {
|
|
@@ -217,7 +226,7 @@ export function expressX(prisma, options = {}) {
|
|
|
217
226
|
|
|
218
227
|
app.delete(`${path}/:id`, async (req, res) => {
|
|
219
228
|
app.log('verbose', `http request DELETE ${req.url}`)
|
|
220
|
-
context.
|
|
229
|
+
context.params.req = req
|
|
221
230
|
try {
|
|
222
231
|
const value = await service.__delete(context, {
|
|
223
232
|
where: {
|
|
@@ -252,11 +261,13 @@ export function expressX(prisma, options = {}) {
|
|
|
252
261
|
id: lastConnectionId++,
|
|
253
262
|
socket,
|
|
254
263
|
channelNames: new Set(),
|
|
255
|
-
data: {
|
|
264
|
+
data: {
|
|
265
|
+
a: 123
|
|
266
|
+
},
|
|
256
267
|
}
|
|
257
268
|
// store connection in cache
|
|
258
|
-
connections[connection.id] = connection
|
|
259
|
-
app.log('verbose', `
|
|
269
|
+
app.connections[connection.id] = connection
|
|
270
|
+
app.log('verbose', `Connection ids: ${Object.keys(app.connections)}`)
|
|
260
271
|
|
|
261
272
|
// emit 'connection' event for app (expressjs extends EventEmitter)
|
|
262
273
|
app.emit('connection', connection)
|
|
@@ -267,15 +278,22 @@ export function expressX(prisma, options = {}) {
|
|
|
267
278
|
socket.on('disconnect', () => {
|
|
268
279
|
app.log('verbose', `Client disconnected ${connection.id}`)
|
|
269
280
|
|
|
270
|
-
//
|
|
271
|
-
|
|
281
|
+
// remove connection record after 1mn (leaves time in case of connection transfer)
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
app.log('verbose', `Delete connection ${connection.id}`)
|
|
284
|
+
delete app.connections[connection.id]
|
|
285
|
+
}, 60 * 1000)
|
|
272
286
|
})
|
|
273
287
|
|
|
274
|
-
|
|
288
|
+
|
|
289
|
+
// handle connection data transfer caused by a disconnection/reconnection (page reload, network issue, etc.)
|
|
275
290
|
socket.on('cnx-transfer', async ({ from, to }) => {
|
|
276
291
|
app.log('verbose', `cnx-transfer from ${from} to ${to}`)
|
|
277
|
-
|
|
278
|
-
|
|
292
|
+
if (!app.connections[from]) return
|
|
293
|
+
app.connections[to] = app.connections[from]
|
|
294
|
+
app.connections[to].socket = socket
|
|
295
|
+
delete app.connections[from]
|
|
296
|
+
// app.printCnx('AFTER TRANSFER')
|
|
279
297
|
})
|
|
280
298
|
|
|
281
299
|
|
|
@@ -292,11 +310,13 @@ export function expressX(prisma, options = {}) {
|
|
|
292
310
|
if (serviceMethod) {
|
|
293
311
|
const context = {
|
|
294
312
|
app,
|
|
295
|
-
|
|
313
|
+
transport: 'ws',
|
|
314
|
+
params: { connection, name, action, args },
|
|
296
315
|
}
|
|
297
316
|
|
|
298
317
|
try {
|
|
299
318
|
const result = await serviceMethod(context, ...args)
|
|
319
|
+
app.log('verbose', `client-response ${uid} ${JSON.stringify(result)}`)
|
|
300
320
|
socket.emit('client-response', {
|
|
301
321
|
uid,
|
|
302
322
|
result,
|
|
@@ -304,7 +324,7 @@ export function expressX(prisma, options = {}) {
|
|
|
304
324
|
// pub/sub: send event on associated channels
|
|
305
325
|
publish(service, action, result)
|
|
306
326
|
} catch(err) {
|
|
307
|
-
app.log('error', err)
|
|
327
|
+
app.log('error', err.toString())
|
|
308
328
|
io.emit('client-response', {
|
|
309
329
|
uid,
|
|
310
330
|
error: err.toString(),
|
|
@@ -340,7 +360,7 @@ export function expressX(prisma, options = {}) {
|
|
|
340
360
|
app.log('verbose', `publish channels ${service.name} ${action} ${channelNames}`)
|
|
341
361
|
for (const channelName of channelNames) {
|
|
342
362
|
app.log('verbose', `service-event ${service.name} ${action} ${channelName}`)
|
|
343
|
-
const connectionList = Object.values(connections).filter(cnx => cnx.channelNames.has(channelName))
|
|
363
|
+
const connectionList = Object.values(app.connections).filter(cnx => cnx.channelNames.has(channelName))
|
|
344
364
|
for (const connection of connectionList) {
|
|
345
365
|
app.log('verbose', `emit to ${connection.id} ${service.name} ${action} ${result}`)
|
|
346
366
|
connection.socket.emit('service-event', {
|
package/test/index.test.js
CHANGED
|
@@ -8,7 +8,7 @@ import { PrismaClient } from '@prisma/client'
|
|
|
8
8
|
import { describe, it, before, after, beforeEach, afterEach } from 'node:test'
|
|
9
9
|
import { strict as assert } from 'node:assert'
|
|
10
10
|
|
|
11
|
-
import { expressX
|
|
11
|
+
import { expressX } from '../src/index.mjs'
|
|
12
12
|
|
|
13
13
|
const prisma = new PrismaClient({
|
|
14
14
|
datasources: {
|
|
@@ -144,41 +144,3 @@ describe('HTTP/REST API', () => {
|
|
|
144
144
|
app.server.close()
|
|
145
145
|
})
|
|
146
146
|
})
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// test compatibility with client API
|
|
150
|
-
describe('Client API', () => {
|
|
151
|
-
|
|
152
|
-
let clientApp, socket
|
|
153
|
-
|
|
154
|
-
before(async () => {
|
|
155
|
-
await new Promise((resolve) => {
|
|
156
|
-
app.server.listen(8008, () => {
|
|
157
|
-
console.log(`App listening at http://localhost:8008`)
|
|
158
|
-
resolve()
|
|
159
|
-
})
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
socket = io('http://localhost:8008', { transports: ["websocket"] })
|
|
163
|
-
clientApp = expressXClient(socket)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
after(async () => {
|
|
167
|
-
app.server.close()
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
it("can create a user", async () => {
|
|
171
|
-
const user = await clientApp.service('User').create({
|
|
172
|
-
data: {
|
|
173
|
-
name: "chris",
|
|
174
|
-
email: 'chris@mail.fr'
|
|
175
|
-
},
|
|
176
|
-
})
|
|
177
|
-
assert(user.name === 'chris')
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
after(async () => {
|
|
181
|
-
await socket.close()
|
|
182
|
-
await app.server.close()
|
|
183
|
-
})
|
|
184
|
-
})
|