Skip to content

Commit 6561712

Browse files
committed
address copilot review: safeParse, instanceof ZodError, trim notice message, fix callback schemas and tests
1 parent dd19fed commit 6561712

7 files changed

Lines changed: 48 additions & 37 deletions

src/adapters/web-socket-adapter.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import cluster from 'cluster'
22
import { EventEmitter } from 'stream'
33
import { IncomingMessage as IncomingHttpMessage } from 'http'
44
import { WebSocket } from 'ws'
5+
import { ZodError } from 'zod'
56

67
import { ContextMetadata, Factory } from '../@types/base'
78
import { createNoticeMessage, createOutgoingEventMessage } from '../utils/messages'
@@ -179,9 +180,12 @@ export class WebSocketAdapter extends EventEmitter implements IWebSocketAdapter
179180
if (error instanceof Error) {
180181
if (error.name === 'AbortError') {
181182
console.error(`web-socket-adapter: abort from client ${this.clientId} (${this.getClientAddress()})`)
182-
} else if (error.name === 'SyntaxError' || error.name === 'ZodError') {
183+
} else if (error.name === 'SyntaxError' || error instanceof ZodError) {
183184
debug('invalid message client %s (%s): %s', this.clientId, this.getClientAddress(), error.message)
184-
this.sendMessage(createNoticeMessage(`invalid: ${error.message}`))
185+
const notice = error instanceof ZodError
186+
? `invalid: ${error.issues[0]?.message ?? error.message}`
187+
: `invalid: ${error.message}`
188+
this.sendMessage(createNoticeMessage(notice))
185189
} else {
186190
console.error('web-socket-adapter: unable to handle message:', error)
187191
}
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { pubkeySchema } from './base-schema'
2-
import Schema from 'joi'
2+
import { z } from 'zod'
33

4-
export const nodelessCallbackBodySchema = Schema.object({
5-
id: Schema.string(),
6-
uuid: Schema.string().required(),
7-
status: Schema.string().required(),
8-
amount: Schema.number().required(),
9-
metadata: Schema.object({
10-
requestId: pubkeySchema.label('metadata.requestId').required(),
11-
description: Schema.string().optional(),
12-
unit: Schema.string().optional(),
13-
createdAt: Schema.alternatives().try(Schema.string(), Schema.date()).optional(),
14-
}).unknown(true).required(),
15-
}).unknown(false)
4+
export const nodelessCallbackBodySchema = z.object({
5+
id: z.string().optional(),
6+
uuid: z.string(),
7+
status: z.string(),
8+
amount: z.number(),
9+
metadata: z.object({
10+
requestId: pubkeySchema,
11+
description: z.string().optional(),
12+
unit: z.string().optional(),
13+
createdAt: z.union([z.string(), z.date()]).optional(),
14+
}).passthrough(),
15+
}).strict()

src/utils/validation.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { z } from 'zod'
22

33
export const validateSchema = (schema: z.ZodTypeAny) => (input: unknown) => {
4-
try {
5-
return { value: schema.parse(input), error: undefined }
6-
} catch (error) {
7-
return { value: undefined, error: error as z.ZodError }
4+
const result = schema.safeParse(input)
5+
if (!result.success) {
6+
return { value: undefined, error: (result as z.SafeParseError<unknown>).error }
87
}
8+
return { value: result.data, error: undefined }
99
}
1010

1111
export const attemptValidation = (schema: z.ZodTypeAny) => (input: unknown) => schema.parse(input)

test/unit/schemas/lnbits-callback-schema.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ describe('LNbits Callback Schema', () => {
1313

1414
it('returns error if hmac is missing', () => {
1515
const result = validateSchema(lnbitsCallbackQuerySchema)({})
16-
expect(result.error).to.have.nested.property('message', '"hmac" is required')
16+
expect(result.error).to.exist
17+
expect(result.error?.issues[0].path).to.deep.equal(['hmac'])
1718
})
1819

1920
it('returns error if hmac format is invalid', () => {
2021
const result = validateSchema(lnbitsCallbackQuerySchema)({ hmac: 'not-an-hmac' })
21-
expect(result.error).to.have.nested.property('message').that.matches(/"hmac" with value "not-an-hmac" fails to match the required pattern/)
22+
expect(result.error).to.exist
23+
expect(result.error?.issues[0].path).to.deep.equal(['hmac'])
2224
})
2325
})
2426

@@ -31,7 +33,8 @@ describe('LNbits Callback Schema', () => {
3133

3234
it('returns error if payment_hash is not 64 chars hex', () => {
3335
const result = validateSchema(lnbitsCallbackBodySchema)({ payment_hash: 'abc' })
34-
expect(result.error).to.have.nested.property('message', '"payment_hash" length must be 64 characters long')
36+
expect(result.error).to.exist
37+
expect(result.error?.issues[0].path).to.deep.equal(['payment_hash'])
3538
})
3639
})
3740
})

test/unit/schemas/nodeless-callback-schema.spec.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ describe('Nodeless Callback Schema', () => {
1919
})
2020

2121
it('returns no error if body contains additional metadata', () => {
22-
const body = {
23-
...validBody,
24-
metadata: {
25-
...validBody.metadata,
26-
createdAt: '2023-01-01T00:00:00Z',
22+
const body = {
23+
...validBody,
24+
metadata: {
25+
...validBody.metadata,
26+
createdAt: '2023-01-01T00:00:00Z',
2727
description: 'test payment',
2828
unit: 'sats',
2929
},
@@ -36,13 +36,15 @@ describe('Nodeless Callback Schema', () => {
3636
const body = { ...validBody }
3737
delete (body as any).uuid
3838
const result = validateSchema(nodelessCallbackBodySchema)(body)
39-
expect(result.error).to.have.nested.property('message', '"uuid" is required')
39+
expect(result.error).to.exist
40+
expect(result.error?.issues[0].path).to.deep.equal(['uuid'])
4041
})
4142

4243
it('returns error if metadata.requestId is not a valid pubkey', () => {
4344
const body = { ...validBody, metadata: { requestId: 'deadbeef' } }
4445
const result = validateSchema(nodelessCallbackBodySchema)(body)
45-
expect(result.error).to.have.nested.property('message', '"metadata.requestId" length must be 64 characters long')
46+
expect(result.error).to.exist
47+
expect(result.error?.issues[0].path).to.deep.equal(['metadata', 'requestId'])
4648
})
4749
})
4850
})

test/unit/schemas/opennode-callback-schema.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ describe('OpenNode Callback Schema', () => {
1616
})
1717

1818
it('returns no error if body contains additional expected fields', () => {
19-
const body = {
20-
...validBody,
21-
amount: 1000,
19+
const body = {
20+
...validBody,
21+
amount: 1000,
2222
created_at: 1672531200,
2323
lightning_invoice: { payreq: 'lnbc1...' },
2424
}
@@ -30,7 +30,8 @@ describe('OpenNode Callback Schema', () => {
3030
const body = { ...validBody }
3131
delete (body as any).order_id
3232
const result = validateSchema(opennodeCallbackBodySchema)(body)
33-
expect(result.error).to.have.nested.property('message', '"order_id" is required')
33+
expect(result.error).to.exist
34+
expect(result.error?.issues[0].path).to.deep.equal(['order_id'])
3435
})
3536
})
3637
})

test/unit/schemas/zebedee-callback-schema.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ describe('Zebedee Callback Schema', () => {
2222
})
2323

2424
it('returns no error if body contains unknown additional fields', () => {
25-
const body = {
26-
...validBody,
27-
extraProperty: true,
25+
const body = {
26+
...validBody,
27+
extraProperty: true,
2828
}
2929
const result = validateSchema(zebedeeCallbackBodySchema)(body)
3030
expect(result.error).to.be.undefined
@@ -33,7 +33,8 @@ describe('Zebedee Callback Schema', () => {
3333
it('returns error if internalId is not a valid pubkey', () => {
3434
const body = { ...validBody, internalId: 'deadbeef' }
3535
const result = validateSchema(zebedeeCallbackBodySchema)(body)
36-
expect(result.error).to.have.nested.property('message', '"internalId" length must be 64 characters long')
36+
expect(result.error).to.exist
37+
expect(result.error?.issues[0].path).to.deep.equal(['internalId'])
3738
})
3839
})
3940
})

0 commit comments

Comments
 (0)