From 718b2df9e8356cd60e99398d03abd921ad811c79 Mon Sep 17 00:00:00 2001 From: Red-Asuka Date: Sun, 9 Oct 2022 16:40:08 +0800 Subject: [PATCH] feat(cli): support setting the connect user properties in pub or sub command --- cli/src/index.ts | 104 +++++++++++++++++++++++++++----------- cli/src/lib/conn.ts | 1 - cli/src/lib/pub.ts | 7 ++- cli/src/lib/sub.ts | 3 +- cli/src/types/global.d.ts | 5 +- cli/src/utils/parse.ts | 14 +++-- 6 files changed, 91 insertions(+), 43 deletions(-) diff --git a/cli/src/index.ts b/cli/src/index.ts index 8415f97ae..ee7fd78da 100755 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -86,19 +86,16 @@ export class Commander { this.program .command('pub') .description('Publish a message to a topic.') - .option('-V, --mqtt-version <5/3.1.1/3.1>', 'the MQTT version', parseMQTTVersion, 5) - .option('-h, --hostname ', 'the broker host', 'localhost') - .option('-p, --port ', 'the broker port', parseNumber) - .option('-i, --client-id ', 'the client id', getClientId()) - .option('-q, --qos <0/1/2>', 'the QoS of the message', parseNumber, 0) .requiredOption('-t, --topic ', 'the message topic', parsePubTopic) - .option('-m, --message ', 'the message body', 'Hello From MQTT X CLI') + .option('-m, --message ', 'the message body', 'Hello From MQTT X CLI') + .option('-q, --qos <0/1/2>', 'the QoS of the message', parseNumber, 0) .option('-r, --retain', 'send a retained message') .option('-d, --dup', 'mark as duplicate flag') .option('-s, --stdin', 'read the message body from stdin') .option('-M, --multiline', 'read lines from stdin as multiple messages') + // properties options of MQTT 5.0 .option('-pf, --payload-format-indicator', 'the payload format indicator of the publish message') - .option('-me, --message-expiry-interval ', 'the lifetime of the publish message in seconds', parseNumber) + .option('-e, --message-expiry-interval ', 'the lifetime of the publish message in seconds', parseNumber) .option( '-ta, --topic-alias ', 'value that is used to identify the topic instead of using the topic name', @@ -109,8 +106,20 @@ export class Commander { '-cd, --correlation-data ', 'used by the sender of the request message to identify which request the response message is for when it is received', ) + .option( + '-up, --user-properties ', + 'the user properties of MQTT 5.0 (e.g. -up "name: mqttx cli")', + parseUserProperties, + ) .option('-si, --subscription-identifier ', 'the identifier of the subscription', parseNumber) .option('-ct, --content-type ', 'a description of the content of the publish message') + // connect options + .option('-V, --mqtt-version <5/3.1.1/3.1>', 'the MQTT version', parseMQTTVersion, 5) + .option('-h, --hostname ', 'the broker host', 'localhost') + .option('-p, --port ', 'the broker port', parseNumber) + .option('-i, --client-id ', 'the client id', getClientId()) + .option('--no-clean', 'set the clean session flag to false (default: true)') + .option('-k, --keepalive ', 'send a ping every SEC seconds', parseNumber, 30) .option('-u, --username ', 'the username') .option('-P, --password ', 'the password') .option('-l, --protocol ', 'the protocol to use, mqtt or mqtts (default: mqtt)', parseProtocol) @@ -118,6 +127,7 @@ export class Commander { .option('--cert ', 'path to the cert file') .option('--ca ', 'path to the ca certificate') .option('--insecure', 'do not verify the server certificate') + // connect properties options of MQTT 5.0 .option('-se, --session-expiry-interval ', 'the session expiry interval in seconds', parseNumber) .option('--rcv-max, --receive-maximum ', 'the receive maximum value', parseNumber) .option('--maximum-packet-size ', 'the maximum packet size the client is willing to accept', parseNumber) @@ -125,30 +135,35 @@ export class Commander { .option('--req-response-info', 'the client requests response information from the server') .option('--no-req-problem-info', 'the client requests problem information from the server') .option( - '-up, --user-properties ', - 'the user properties of MQTT 5.0 (e.g. -up "name: mqttx cli")', + '-Cup, --conn-user-properties ', + 'the connect user properties of MQTT 5.0 (e.g. -up "name: mqttx cli")', + parseUserProperties, + ) + // will message options + .option('-Wt, --will-topic ', 'the will topic') + .option('-Wm, --will-message ', 'the will message') + .option('-Wq, --will-qos <0/1/2>', 'the will qos', parseNumber) + .option('-Wr, --will-retain', 'send a will retained message') + // will message properties options of MQTT 5.0 + .option('-Wd, --will-delay-interval ', 'the will delay interval in seconds', parseNumber) + .option('-Wpf, --will-payload-format-indicator', 'will message is UTF-8 encoded character data or not') + .option('-We, --will-message-expiry-interval ', 'lifetime of the will message in seconds', parseNumber) + .option('-Wct, --will-content-type ', 'description of the will message’s content') + .option('-Wrt, --will-response-topic ', 'topic name for a response message') + .option('-Wcd, --will-correlation-data ', 'correlation data for the response message') + .option( + '-Wup, --will-user-properties ', + 'the user properties of will message', parseUserProperties, ) - .option('--will-topic ', 'the will topic') - .option('--will-message ', 'the will message') - .option('--will-qos <0/1/2>', 'the will qos', parseNumber, 0) - .option('--will-retain', 'send a will retained message') .action(pub) this.program .command('sub') .description('Subscribes to a topic.') - .option('-V, --mqtt-version <5/3.1.1/3.1>', 'the MQTT version', parseMQTTVersion, 5) - .option('-h, --hostname ', 'the broker host', 'localhost') - .option('-p, --port ', 'the broker port', parseNumber) - .option('-i, --client-id ', 'the client id', getClientId()) - .option('-q, --qos <0/1/2...>', 'the QoS of the message', parseQoS) - .option('--no-clean', 'set the clean session flag to false (default: true)') .requiredOption('-t, --topic ', 'the message topic') - .option('-k, --keepalive ', 'send a ping every SEC seconds', parseNumber, 30) - .option('-u, --username ', 'the username') - .option('-P, --password ', 'the password') - .option('-l, --protocol ', 'the protocol to use, mqtt or mqtts (default: mqtt)', parseProtocol) + .option('-q, --qos <0/1/2...>', 'the QoS of the message', parseQoS) + // properties options of MQTT 5.0 .option('-nl, --no_local [FLAG...]', 'the no local MQTT 5.0 flag', parseVariadicOfBooleanType) .option( '-rap, --retain-as-published [FLAG...]', @@ -157,10 +172,27 @@ export class Commander { ) .option('-rh, --retain-handling <0/1/2...>', 'the retain handling MQTT 5.0', parseQoS) .option('-si, --subscription-identifier ', 'the identifier of the subscription', parseNumber) + .option( + '-up, --user-properties ', + 'the user properties of MQTT 5.0 (e.g. -up "name: mqttx cli")', + parseUserProperties, + ) + .option('-v, --verbose', 'print the topic before the message') + // connect options + .option('-V, --mqtt-version <5/3.1.1/3.1>', 'the MQTT version', parseMQTTVersion, 5) + .option('-h, --hostname ', 'the broker host', 'localhost') + .option('-p, --port ', 'the broker port', parseNumber) + .option('-i, --client-id ', 'the client id', getClientId()) + .option('--no-clean', 'set the clean session flag to false (default: true)') + + .option('-u, --username ', 'the username') + .option('-P, --password ', 'the password') + .option('-l, --protocol ', 'the protocol to use, mqtt or mqtts (default: mqtt)', parseProtocol) .option('--key ', 'path to the key file') .option('--cert ', 'path to the cert file') .option('--ca ', 'path to the ca certificate') .option('--insecure', 'do not verify the server certificate') + // connect properties options of MQTT 5.0 .option('-se, --session-expiry-interval ', 'the session expiry interval in seconds', parseNumber) .option('--rcv-max, --receive-maximum ', 'the receive maximum value', parseNumber) .option('--maximum-packet-size ', 'the maximum packet size the client is willing to accept', parseNumber) @@ -168,15 +200,27 @@ export class Commander { .option('--req-response-info', 'the client requests response information from the server') .option('--no-req-problem-info', 'the client requests problem information from the server') .option( - '-up, --user-properties ', - 'the user properties of MQTT 5.0 (e.g. -up "name: mqttx cli")', + '-Cup, --conn-user-properties ', + 'the connect user properties of MQTT 5.0 (e.g. -up "name: mqttx cli")', + parseUserProperties, + ) + // will message options + .option('-Wt, --will-topic ', 'the will topic') + .option('-Wm, --will-message ', 'the will message') + .option('-Wq, --will-qos <0/1/2>', 'the will qos', parseNumber) + .option('-Wr, --will-retain', 'send a will retained message') + // will message properties options of MQTT 5.0 + .option('-Wd, --will-delay-interval ', 'the will delay interval in seconds', parseNumber) + .option('-Wpf, --will-payload-format-indicator', 'will message is UTF-8 encoded character data or not') + .option('-We, --will-message-expiry-interval ', 'lifetime of the will message in seconds', parseNumber) + .option('-Wct, --will-content-type ', 'description of the will message’s content') + .option('-Wrt, --will-response-topic ', 'topic name for a response message') + .option('-Wcd, --will-correlation-data ', 'correlation data for the response message') + .option( + '-Wup, --will-user-properties ', + 'the user properties of will message', parseUserProperties, ) - .option('--will-topic ', 'the will topic') - .option('--will-message ', 'the will message') - .option('--will-qos <0/1/2>', 'the will qos', parseNumber) - .option('--will-retain', 'send a will retained message') - .option('-v, --verbose', 'print the topic before the message') .action(sub) } } diff --git a/cli/src/lib/conn.ts b/cli/src/lib/conn.ts index fe7e042c5..08f13b3bb 100644 --- a/cli/src/lib/conn.ts +++ b/cli/src/lib/conn.ts @@ -1,5 +1,4 @@ import * as mqtt from 'mqtt' -import * as fs from 'fs' import signale from '../utils/signale' import { parseConnectOptions } from '../utils/parse' diff --git a/cli/src/lib/pub.ts b/cli/src/lib/pub.ts index 2fc517758..f3dfd06ae 100644 --- a/cli/src/lib/pub.ts +++ b/cli/src/lib/pub.ts @@ -1,5 +1,4 @@ import * as mqtt from 'mqtt' -import * as fs from 'fs' import pump from 'pump' import concat from 'concat-stream' import { Writable } from 'readable-stream' @@ -10,7 +9,7 @@ import { parseConnectOptions, parsePublishOptions } from '../utils/parse' const send = ( connOpts: IClientOptions, - pubOpts: { topic: string; message?: string | Buffer; opts: IClientPublishOptions }, + pubOpts: { topic: string; message: string | Buffer; opts: IClientPublishOptions }, ) => { const client = mqtt.connect(connOpts) signale.await('Connecting...') @@ -18,7 +17,7 @@ const send = ( signale.success('Connected') const { topic, message } = pubOpts signale.await('Message Publishing...') - client.publish(topic, message!, pubOpts.opts, (err) => { + client.publish(topic, message, pubOpts.opts, (err) => { if (err) { signale.warn(err) } else { @@ -35,7 +34,7 @@ const send = ( const multisend = ( connOpts: IClientOptions, - pubOpts: { topic: string; message?: string | Buffer; opts: IClientPublishOptions }, + pubOpts: { topic: string; message: string | Buffer; opts: IClientPublishOptions }, ) => { const client = mqtt.connect(connOpts) signale.await('Connecting...') diff --git a/cli/src/lib/sub.ts b/cli/src/lib/sub.ts index 5190f17ff..100265722 100644 --- a/cli/src/lib/sub.ts +++ b/cli/src/lib/sub.ts @@ -1,5 +1,4 @@ import * as mqtt from 'mqtt' -import * as fs from 'fs' import { signale, msgLog } from '../utils/signale' import { parseConnectOptions, parseSubscribeOptions } from '../utils/parse' @@ -13,7 +12,7 @@ const sub = (options: SubscribeOptions) => { client.on('connect', () => { signale.success('Connected') - const subOptsArray = parseSubscribeOptions(options, 'sub') + const subOptsArray = parseSubscribeOptions(options) const { topic } = options diff --git a/cli/src/types/global.d.ts b/cli/src/types/global.d.ts index b071e286d..ed94fd746 100644 --- a/cli/src/types/global.d.ts +++ b/cli/src/types/global.d.ts @@ -46,7 +46,7 @@ declare global { interface PublishOptions extends ConnectOptions { topic: string - message?: string | Buffer + message: string | Buffer qos: QoS retain?: boolean dup?: boolean @@ -60,16 +60,19 @@ declare global { correlationData?: string subscriptionIdentifier?: number contentType?: string + connUserProperties?: Record } interface SubscribeOptions extends ConnectOptions { topic: string[] qos?: QoS[] + // properties of MQTT 5.0 no_local?: boolean[] retainAsPublished?: boolean[] retainHandling?: QoS[] subscriptionIdentifier?: number[] verbose: boolean + connUserProperties?: Record } } diff --git a/cli/src/utils/parse.ts b/cli/src/utils/parse.ts index d31833d2d..49ddb420d 100644 --- a/cli/src/utils/parse.ts +++ b/cli/src/utils/parse.ts @@ -72,7 +72,10 @@ const parsePubTopic = (value: string) => { return value } -const parseConnectOptions = (options: ConnectOptions, commandType?: CommandType) => { +const parseConnectOptions = ( + options: ConnectOptions | PublishOptions | SubscribeOptions, + commandType?: CommandType, +) => { const { mqttVersion, hostname, @@ -93,7 +96,6 @@ const parseConnectOptions = (options: ConnectOptions, commandType?: CommandType) topicAliasMaximum, reqResponseInfo, reqProblemInfo, - userProperties, willTopic, willMessage, willQos, @@ -170,6 +172,8 @@ const parseConnectOptions = (options: ConnectOptions, commandType?: CommandType) if (mqttVersion === 3) { connectOptions.protocolId = 'MQIsdp' } else if (mqttVersion === 5) { + const userProperties = + commandType === 'conn' ? options.userProperties : (options).connUserProperties const properties = { sessionExpiryInterval, receiveMaximum, @@ -177,7 +181,7 @@ const parseConnectOptions = (options: ConnectOptions, commandType?: CommandType) topicAliasMaximum, requestResponseInformation: reqResponseInfo, requestProblemInformation: reqProblemInfo, - userProperties: commandType === 'conn' ? userProperties : undefined, + userProperties, } if (clean === false) { @@ -239,7 +243,7 @@ const parsePublishOptions = (options: PublishOptions) => { return { topic, message, opts: publishOptions } } -const parseSubscribeOptions = (options: SubscribeOptions, commandType?: CommandType) => { +const parseSubscribeOptions = (options: SubscribeOptions) => { const { mqttVersion, topic, @@ -264,7 +268,7 @@ const parseSubscribeOptions = (options: SubscribeOptions, commandType?: CommandT if (mqttVersion === 5) { const properties = { subscriptionIdentifier: getSpecialTypesOption(subscriptionIdentifier as number[], index), - userProperties: commandType === 'sub' ? userProperties : undefined, + userProperties, } subOptions.properties = Object.fromEntries(