Skip to content

Commit

Permalink
feat(web): fix the validation of subscribing to multiple topics
Browse files Browse the repository at this point in the history
  • Loading branch information
DM1-1 committed Mar 29, 2024
1 parent 1e8bc83 commit 0ef0531
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 67 deletions.
75 changes: 29 additions & 46 deletions web/src/components/SubscriptionsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,7 @@ import Contextmenu from '@/components/Contextmenu.vue'
import { updateConnection } from '@/utils/api/connection'
import time from '@/utils/time'
import { getSubscriptionId } from '@/utils/idGenerator'
enum SubscribeErrorReason {
normal,
qosSubFailed, // qos is abnormal
qosSubSysFailed, // qos is abnormal becauseof $SYS subscribe
emptySubFailed, // subscription returns empty array
}
import getErrorReason from '@/utils/mqttErrorReason'
@Component({
components: {
Expand Down Expand Up @@ -337,28 +331,6 @@ export default class SubscriptionsList extends Vue {
})
}
/**
* Get the error reason message corresponding to the enumeration.
* Check that errorReason not equal `SubscribeErrorReason.normal` before using.
* @return Return the message of failure subscribe
* @param errorReason - Type:enum, The reason cause the failed subscription
*/
private getErrorReasonMsg(errorReason: SubscribeErrorReason): VueI18n.TranslateResult {
if (errorReason === SubscribeErrorReason.normal) return ''
switch (errorReason) {
case errorReason & SubscribeErrorReason.qosSubFailed: {
return this.$t('connections.qosSubFailed')
}
case errorReason & SubscribeErrorReason.qosSubSysFailed: {
return this.$t('connections.qosSubSysFailed')
}
case errorReason & SubscribeErrorReason.emptySubFailed: {
return this.$t('connections.emptySubFailed')
}
}
return this.$t('connections.unknowSubFailed')
}
public async resubscribe() {
this.getCurrentConnection(this.connectionId)
for (let sub of this.subsList) {
Expand Down Expand Up @@ -392,6 +364,14 @@ export default class SubscriptionsList extends Vue {
}
}
private handleSubError(topic: string, qos: number) {
const errorReason = getErrorReason(this.record.mqttVersion, qos)
let errorReasonMsg: VueI18n.TranslateResult = ''
if (qos === 128 || qos === 135) errorReasonMsg = ', ' + this.$t('connections.qosSubFailed')
this.$message.error(`${this.$t('connections.subFailed', [topic, errorReason, qos]) + errorReasonMsg}`)
}
public async subscribe(
{ topic, alias, qos, nl, rap, rh, subscriptionIdentifier, disabled }: SubscriptionModel,
isAuto?: boolean,
Expand All @@ -407,9 +387,10 @@ export default class SubscriptionsList extends Vue {
this.subRecord.disabled = disabled
this.subRecord.color = getRandomColor()
}
let isFinshed = false
let isFinished = false
if (this.client.subscribe) {
const topicsArr = this.multiTopics ? topic.split(',') : topic
const topicsArr = this.multiTopics ? [...new Set(topic.split(','))].filter(Boolean) : topic
const aliasArr = this.multiTopics ? alias?.split(',') : alias
let properties: { subscriptionIdentifier: number } | undefined = undefined
if (this.record.mqttVersion === '5.0' && subscriptionIdentifier) {
Expand All @@ -423,17 +404,17 @@ export default class SubscriptionsList extends Vue {
this.$message.error(error)
return false
}
let errorReason = SubscribeErrorReason.normal
if (!granted || (Array.isArray(granted) && granted.length < 1)) {
} else if (![0, 1, 2].includes(granted[0].qos) && topic.match(/^(\$SYS)/i)) {
errorReason = SubscribeErrorReason.qosSubSysFailed
} else if (![0, 1, 2].includes(granted[0].qos)) {
errorReason = SubscribeErrorReason.qosSubFailed
}
if (errorReason !== SubscribeErrorReason.normal) {
const errorReasonMsg: VueI18n.TranslateResult = this.getErrorReasonMsg(errorReason)
const errorMsg: string = `${this.$t('connections.subFailed')} ${errorReasonMsg}`
this.$message.error(errorMsg)
const successSubscriptions: string[] = []
granted.forEach((grant) => {
if ([0, 1, 2].includes(grant.qos)) {
successSubscriptions.push(grant.topic)
} else {
setTimeout(() => {
this.handleSubError(grant.topic, grant.qos)
}, 0)
}
})
if (!successSubscriptions.length) {
return false
}
if (enable) {
Expand All @@ -443,7 +424,9 @@ export default class SubscriptionsList extends Vue {
this.saveTopicToSubList(topic, qos)
} else {
topicsArr.forEach((topic, index) => {
this.saveTopicToSubList(topic, qos, index, aliasArr as string[])
if (successSubscriptions.includes(topic)) {
this.saveTopicToSubList(topic, qos, index, aliasArr as string[])
}
})
}
}
Expand All @@ -453,18 +436,18 @@ export default class SubscriptionsList extends Vue {
this.changeSubs({ id: this.connectionId, subscriptions: this.subsList })
this.showDialog = false
}
isFinshed = true
isFinished = true
})
}
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
// TODO: maybe we should replace mqtt.js to mqtt-async.js
await new Promise(async (resolve) => {
// long pool query base on sleep
while (!isFinshed) {
while (!isFinished) {
await sleep(100)
}
resolve(isFinshed)
resolve(isFinished)
})
}
Expand Down
27 changes: 6 additions & 21 deletions web/src/lang/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,29 +90,14 @@ export default {
ja: 'サブスクリプションの編集',
},
subFailed: {
zh: '订阅失败',
en: 'Subscribe Failed',
ja: 'サブスクリプションが失敗しました',
},
qosSubSysFailed: {
zh: '拒绝了 $SYS 主题,错误的 QoS,MQTT Broker 拒绝了订阅。请检查 ACL 配置',
en: 'Rejected the $SYS topic,Unexpected QoS, MQTT Broker declined the subscription. Please check ACL configuration',
ja: '$SYSトピックを拒否しま。した予期しないQoS、MQTT Brokerはサブスクリプションを拒否しました。ACL構成を確認してください',
zh: '无法订阅 {0}, Error: {1} (Code: {2})',
en: 'Failed to Subscribe {0}, Error: {1}(Code: {2})',
ja: 'サブスクリプション失敗:{0}, エラー:{1} (コード:{2})',
},
qosSubFailed: {
zh: '错误的 QoS, SubACK 失败, 请检查 MQTT broker ACL 设置',
en: 'Unexpected QoS, SubACK failed, Please check MQTT broker ACL configuration',
ja: '予期しないQoS, SubACK失敗、MQTT Broker ACL構成を確認してください',
},
emptySubFailed: {
zh: '订阅为空',
en: 'Subscription is empty',
ja: 'サブスクリプションは空です',
},
unknowSubFailed: {
zh: '未知的订阅错误',
en: 'Unknown subscription error',
ja: '不明なサブスクリプションエラー',
zh: '请确保权限正确,并检查 MQTT Broker 的 ACL 配置',
en: 'Make sure the permissions are correct, and check MQTT broker ACL configuration',
ja: '権限が正しいことを確認し、MQTTブローカーのACL設定を確認してください',
},
connected: {
zh: '已连接',
Expand Down
54 changes: 54 additions & 0 deletions web/src/utils/mqttErrorReason.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const MqttErrorReason: Record<string, { [code: number]: string }> = {
'3.1.1': {
128: 'Not authorized',
},
'5.0': {
4: 'Disconnect with Will Message',
16: 'No matching subscribers',
17: 'No subscription existed',
24: 'Continue authentication',
25: 'Re-authenticate',
128: 'Unspecified error',
129: 'Malformed Packet',
130: 'Protocol Error',
131: 'Implementation specific error',
132: 'Unsupported Protocol Version',
133: 'Client Identifier not valid',
134: 'Bad User Name or Password',
135: 'Not authorized',
136: 'Server unavailable',
137: 'Server busy',
138: 'Banned',
139: 'Server shutting down',
140: 'Bad authentication method',
141: 'Keep Alive timeout',
142: 'Session taken over',
143: 'Topic Filter invalid',
144: 'Topic Name invalid',
145: 'Packet Identifier in use',
146: 'Packet Identifier not found',
147: 'Receive Maximum exceeded',
148: 'Topic Alias invalid',
149: 'Packet too large',
150: 'Message rate too high',
151: 'Quota exceeded',
152: 'Administrative action',
153: 'Payload format invalid',
154: 'Retain not supported',
155: 'QoS not supported',
156: 'Use another server',
157: 'Server moved',
158: 'Shared Subscriptions not supported',
159: 'Connection rate exceeded',
160: 'Maximum connect time',
161: 'Subscription Identifiers not supported',
162: 'Wildcard Subscriptions not supported',
},
}

const getErrorReason = (version: MqttVersion, code: number) => {
const versionMap = MqttErrorReason[version]
return versionMap[code] ?? 'Unknown error'
}

export default getErrorReason

0 comments on commit 0ef0531

Please sign in to comment.