Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cli):optimized parameter checks and error handling #1332

Merged
merged 1 commit into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ export class Commander {
.name('mqttx')
.description('An MQTT client for the command line')
.enablePositionalOptions()
.allowUnknownOption(false)
.version(`${version}\nhttps://mqttx.app/changelogs/v${version}`, '-v, --version')

this.program
.command('check')
.description('Check for updates.')
.allowUnknownOption(false)
.action(async () => {
await checkUpdate()
})
Expand Down Expand Up @@ -101,6 +103,7 @@ export class Commander {
'--config [PATH]',
'load the parameters from the local configuration file, which supports json and yaml format, default path is ./mqttx-cli-config.json',
)
.allowUnknownOption(false)
.action(conn)

this.program
Expand Down Expand Up @@ -195,10 +198,11 @@ export class Commander {
.option('-Pp, --protobuf-path <PATH>', 'the .proto file that defines the message format of protobuf')
.option('-Pmn, --protobuf-message-name <NAME>', 'the name of the protobuf message type')
.option(
'-Pf, --protobuf-format <TYPE>',
'-Pft, --protobuf-format-type <TYPE>',
'the format type of message body, support base64, json, hex',
parseFormat,
)
.allowUnknownOption(false)
.action(pub)

this.program
Expand Down Expand Up @@ -289,6 +293,7 @@ export class Commander {
)
.option('-Pp, --protobuf-path <PATH>', 'the .proto file that defines the message format of protobuf')
.option('-Pmn, --protobuf-message-name <NAME>', 'the name of the protobuf message type')
.allowUnknownOption(false)
.action(sub)

const benchCmd = this.program.command('bench').description('MQTT Benchmark in performance testing.')
Expand Down Expand Up @@ -356,6 +361,7 @@ export class Commander {
'--config [PATH]',
'load the parameters from the local configuration file, which supports json and yaml format, default path is ./mqttx-cli-config.json',
)
.allowUnknownOption(false)
.action(benchConn)

benchCmd
Expand Down Expand Up @@ -453,6 +459,7 @@ export class Commander {
'--config [PATH]',
'load the parameters from the local configuration file, which supports json and yaml format, default path is ./mqttx-cli-config.json',
)
.allowUnknownOption(false)
.action(benchPub)

benchCmd
Expand Down Expand Up @@ -539,6 +546,7 @@ export class Commander {
'--config [PATH]',
'load the parameters from the local configuration file, which supports json and yaml format, default path is ./mqttx-cli-config.json',
)
.allowUnknownOption(false)
.action(benchSub)

this.program
Expand Down Expand Up @@ -638,12 +646,14 @@ export class Commander {
'--config [PATH]',
'load the parameters from the local configuration file, which supports json and yaml format, default path is ./mqttx-cli-config.json',
)
.allowUnknownOption(false)
.action(simulatePub)

this.program
.command('ls')
.description('List information based on the provided options.')
.option('-sc, --scenarios', 'List all built-in scenarios')
.allowUnknownOption(false)
.action(ls)
}
}
Expand Down
12 changes: 6 additions & 6 deletions cli/src/lib/pub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ const send = (
message: string | Buffer
protobufPath: string | undefined
protobufMessageName: string | undefined
protobufFormat: FormatType | undefined
protobufFormatType: FormatType | undefined
opts: IClientPublishOptions
},
) => {
const client = mqtt.connect(connOpts)
basicLog.connecting(config, connOpts.hostname!, connOpts.port, pubOpts.topic, pubOpts.message.toString())
client.on('connect', () => {
basicLog.connected()
const { topic, message, protobufPath, protobufMessageName, protobufFormat } = pubOpts
const { topic, message, protobufPath, protobufMessageName, protobufFormatType } = pubOpts
basicLog.publishing()
let bufferMessage = serializeProtobufToBuffer(message, protobufPath, protobufMessageName, protobufFormat)
let bufferMessage = serializeProtobufToBuffer(message, protobufPath, protobufMessageName, protobufFormatType)
client.publish(topic, bufferMessage, pubOpts.opts, (err) => {
if (err) {
signale.warn(err)
Expand Down Expand Up @@ -59,7 +59,7 @@ const multisend = (
message: string | Buffer
protobufPath: string | undefined
protobufMessageName: string | undefined
protobufFormat: FormatType | undefined
protobufFormatType: FormatType | undefined
opts: IClientPublishOptions
},
maximumReconnectTimes: number,
Expand All @@ -72,9 +72,9 @@ const multisend = (
objectMode: true,
})
sender._write = (line, _enc, cb) => {
const { topic, opts, protobufPath, protobufMessageName, protobufFormat } = pubOpts
const { topic, opts, protobufPath, protobufMessageName, protobufFormatType } = pubOpts

let bufferMessage = serializeProtobufToBuffer(line.trim(), protobufPath, protobufMessageName, protobufFormat)
let bufferMessage = serializeProtobufToBuffer(line.trim(), protobufPath, protobufMessageName, protobufFormatType)
client.publish(topic, bufferMessage, opts, cb)
}

Expand Down
4 changes: 2 additions & 2 deletions cli/src/types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ declare global {
connUserProperties?: Record<string, string | string[]>
protobufPath?: string
protobufMessageName?: string
protobufFormat?: FormatType
protobufFormatType?: FormatType
}

interface SubscribeOptions extends ConnectOptions {
Expand All @@ -100,7 +100,7 @@ declare global {

type OmitPublishOptions = Omit<
PublishOptions,
'stdin' | 'multiline' | 'protobufPath' | 'protobufMessageName' | 'protobufFormat'
'stdin' | 'multiline' | 'protobufPath' | 'protobufMessageName' | 'protobufFormatType'
>

interface BenchPublishOptions extends OmitPublishOptions {
Expand Down
4 changes: 2 additions & 2 deletions cli/src/utils/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ const parsePublishOptions = (options: PublishOptions) => {
contentType,
protobufPath,
protobufMessageName,
protobufFormat,
protobufFormatType,
} = options

const publishOptions: IClientPublishOptions = {
Expand All @@ -317,7 +317,7 @@ const parsePublishOptions = (options: PublishOptions) => {
)
}

return { topic, message, protobufPath, protobufMessageName, protobufFormat, opts: publishOptions }
return { topic, message, protobufPath, protobufMessageName, protobufFormatType, opts: publishOptions }
}

const parseSubscribeOptions = (options: SubscribeOptions) => {
Expand Down
20 changes: 15 additions & 5 deletions cli/src/utils/protobuf.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import protobuf from 'protobufjs'
import signale from './signale'
import { transformPBJSError } from './protobufErrors'

const convertObject = (raw: string | Buffer, format?: FormatType | undefined) => {
switch (format) {
Expand All @@ -26,13 +27,15 @@ export const serializeProtobufToBuffer = (
const rawData = convertObject(raw, format)
const err = Message.verify(rawData)
if (err) {
signale.warn(err)
signale.error(`Message serialization error: ${err}`)
process.exit(1)
}
const data = Message.create(rawData)
const serializedMessage = Message.encode(data).finish()
bufferMessage = Buffer.from(serializedMessage)
} catch (err: unknown) {
signale.warn((err as Error).message.split('\n')[0])
} catch (error: unknown) {
signale.error(`Message format type error : ${(error as Error).message.split('\n')[0]}`)
process.exit(1)
}
}
return bufferMessage
Expand All @@ -49,12 +52,19 @@ export const deserializeBufferToProtobuf = (
const root = protobuf.loadSync(protobufPath)
const Message = root.lookupType(protobufMessageName)
const MessageData = Message.decode(payload)
const err = Message.verify(MessageData)
if (err) {
signale.error(`Message deserialization error: ${err}`)
process.exit(1)
}
if (to) {
return Buffer.from(JSON.stringify(MessageData.toJSON()))
}
return MessageData
} catch (err: unknown) {
signale.warn((err as Error).message.split('\n')[0])
} catch (error: unknown) {
let err = transformPBJSError(error as Error)
signale.error(err.message.split('\n')[0])
process.exit(1)
}
}
}
28 changes: 28 additions & 0 deletions cli/src/utils/protobufErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
function prepend(msg: string): string {
return `Message deserialization error: ${msg}`
}

function indexOutOfRange(msg: string): string {
function errMsg(pos: string, read: string, len: string): string {
return `Index out of range: the reader was at position ${pos} and tried to read ${read} more (bytes), but the given buffer was ${len} bytes`
}

const regex = /index out of range: ([0-9]+) \+ ([0-9]+) > ([0-9]+)/

const res = msg.match(regex)

if (res) {
const [matched, pos, read, len] = res
return msg.replace(matched, errMsg(pos, read, len))
}

return msg
}

export function transformPBJSError(e: Error): Error {
const transformers = [prepend, indexOutOfRange]

const message = transformers.reduce((msg, transform) => transform(msg), e.message)

return new Error(message)
}
Loading