Skip to content

Commit

Permalink
Merge branch 'dev/1.9.1' into enterprise
Browse files Browse the repository at this point in the history
  • Loading branch information
Kinplemelon committed Jun 7, 2024
2 parents 2f11a29 + 961c57c commit 0709cc0
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 335 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_emqx_for_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
env:
EMQX_NAME: ${{ inputs.emqx-name }}
OTP_VSN: '25.3.2-2'
ELIXIR_VSN: '1.14.5'
ELIXIR_VSN: '1.15.7'

steps:
- name: checkout emqx code
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"element-plus": "~2.3.0",
"genson-js": "^0.0.8",
"highlight.js": "10.7.3",
"hocon-parser": "^1.0.1",
"js-base64": "^3.7.2",
"js-sql-parser": "^1.4.1",
"json-bigint": "^1.0.0",
Expand Down
10 changes: 0 additions & 10 deletions src/common/docLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ type DocKey =
| 'documentation'
| 'forum'
| 'gitHub'
| 'ruleEvent'
| 'bridgeAsFrom'
| 'ruleEventMsgPub'
| 'upgrade'
| 'dashboard'
| 'mqttStudy'
Expand Down Expand Up @@ -78,13 +75,6 @@ const createDocLinks = (lang: string): DocMap => {
documentation: `https://docs.emqx.com/${lang}/enterprise/${EMQX_VERSION}/?${QUERY_FOR_HELP}`,
forum: lang === 'en' ? `https://www.emqx.io/forum/` : `https://askemq.com/`,
gitHub: `https://github.com/emqx/emqx`,
ruleEvent: `https://docs.emqx.com/${lang}/enterprise/${EMQX_VERSION}/data-integration/rule-sql-events-and-fields.html`,
bridgeAsFrom: `https://docs.emqx.com/${lang}/enterprise/${EMQX_VERSION}/data-integration/rule-sql-events-and-fields.html#${
lang === 'zh' ? '数据桥接' : 'data-bridges'
}`,
ruleEventMsgPub: `https://docs.emqx.com/${lang}/enterprise/${EMQX_VERSION}/data-integration/rule-sql-events-and-fields.html#${
lang === 'zh' ? 'mqtt-消息' : 'mqtt-message'
}`,
upgrade: `https://www.emqx.com/${lang}/lp/upgrade-emqx/enterprise?${QUERY_FOR_GO_UPGRADE}`,
blog: `https://www.emqx.com/${lang}/blog/category/emqx?${QUERY_FOR_HELP}`,
dashboard: `https://docs.emqx.com/${lang}/enterprise/${EMQX_VERSION}/dashboard/introduction.html?${QUERY_FOR_HELP}`,
Expand Down
10 changes: 5 additions & 5 deletions src/components/ListenerDrawer/CustomConfigs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ const props = defineProps({
const emits = defineEmits(['update:modelValue'])
const { objectToString, stringToObject } = useListenerUtils()
const { objectToHocon, hoconToObject } = useListenerUtils()
const errorMsg = ref('')
const rawListener = ref(objectToString(props.modelValue))
const rawListener = ref(objectToHocon(props.modelValue))
watch(
() => props.type,
Expand All @@ -51,20 +51,20 @@ watch(
watch(
() => props.modelValue,
(val) => {
rawListener.value = objectToString(val)
rawListener.value = objectToHocon(val)
},
)
const defaultPlaceHolder = ref('')
function setDefaultPlaceHolder(type: 'ssl' | 'tcp' | 'ws' | 'wss') {
defaultPlaceHolder.value = objectToString(unexposedConfigs[type])
defaultPlaceHolder.value = objectToHocon(unexposedConfigs[type])
}
setDefaultPlaceHolder(props.type)
async function resetRawData() {
try {
const parsed = await stringToObject(rawListener.value)
const parsed = await hoconToObject(rawListener.value)
emits('update:modelValue', parsed)
} catch (error) {
const err = error as Error
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/Config/useListenerDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export default (props: Props, emit: Emit): UseListenerDialogReturns => {
getListenerNameNTypeById,
transPort,
extractDifferences,
stringToObject,
hoconToObject,
} = useListenerUtils(props.gatewayName)

const listenerTypeOptList = computed(() => {
Expand Down Expand Up @@ -182,7 +182,7 @@ export default (props: Props, emit: Emit): UseListenerDialogReturns => {
const validateCustomConfig = async () => {
try {
if (customConfig.value) {
await stringToObject(customConfig.value.rawListener)
await hoconToObject(customConfig.value.rawListener)
}
return Promise.resolve()
} catch (error) {
Expand Down
111 changes: 42 additions & 69 deletions src/hooks/Config/useListenerUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Listener } from '@/types/listener'
import { cloneDeep } from 'lodash'
import useFormRules from '../useFormRules'
import useI18nTl from '../useI18nTl'
import parseHoconToObject from 'hocon-parser'

export interface ListenerUtils {
completeGatewayListenerTypeList: ListenerTypeForGateway[]
Expand Down Expand Up @@ -37,8 +38,8 @@ export interface ListenerUtils {
handleListenerDataWhenItIsIndependent: (listener: Listener) => Listener
transPort: (port: string) => string
extractDifferences: (type: keyof typeof unexposedConfigs, data: any) => Record<string, any>
objectToString: (obj: Record<string, any>, parentKey?: string) => string
stringToObject: (str: string) => Record<string, any>
objectToHocon(obj: Record<string, any>): string
hoconToObject: (hoconData: string) => Record<string, any>
}

export default (gatewayName?: string | undefined): ListenerUtils => {
Expand Down Expand Up @@ -369,77 +370,49 @@ export default (gatewayName?: string | undefined): ListenerUtils => {
return diff
}

/**
* Converts an object to a string representation.
*
* @param {Record<string, any>} obj - The object to convert.
* @param {string} [parentKey=''] - The parent key for nested objects.
* @returns {string} The string representation of the object.
*/
function objectToString(obj: Record<string, any>, parentKey = '') {
let str = ''
Object.keys(obj).forEach((key) => {
const value = obj[key]
const newKey = parentKey ? `${parentKey}.${key}` : key
if (typeof value === 'object' && !Array.isArray(value)) {
str += objectToString(value, newKey)
} else {
if (Array.isArray(value)) {
str += `${newKey}: ${value.join(', ')}\n`
const wordReg = /^[a-zA-Z_]+$/
const numReg = /^[\d]+$/

const getValueStr = (value: string) => {
if (wordReg.test(value) || numReg.test(value)) {
return value
}
return `"${value}"`
}

const objectToHocon = (data: Record<string, any>): string => {
let result = ''
const walk = (data: Record<string, any>, level = 0) => {
for (const key in data) {
const value = data[key]
if (typeof value === 'object' && !Array.isArray(value)) {
result += `${' '.repeat(level)}${key} {\n`
walk(value, level + 1)
result += `${' '.repeat(level)}}\n`
} else if (Array.isArray(value)) {
result += `${' '.repeat(level)}${key} = [\n`
for (const item of value) {
result += `${' '.repeat(level + 1)}${getValueStr(item)}\n`
}
result += `${' '.repeat(level)}]\n`
} else {
str += `${newKey}: ${value}\n`
result += `${' '.repeat(level)}${key} = ${getValueStr(value)}\n`
}
}
})
return str
}
}

type NestedObject = {
[key: string]: string | number | boolean | NestedObject | string[]
}
function parseValue(value: string): string | number | boolean | string[] {
if (value === 'true') return true
if (value === 'false') return false
if (!isNaN(Number(value))) return Number(value)
if (value.includes(', ')) return value.split(', ').map((v) => v.trim())
return value
walk(data, 1)

return `{\n${result}}`
}

/**
* Converts a string into a nested object.
*
* @param str - The string to convert.
* @returns A promise that resolves to the nested object.
* @throws If the string has an invalid format or if there is a path conflict or invalid nesting.
*/
function stringToObject(str: string): Promise<NestedObject> {
return new Promise((resolve, reject) => {
const result: NestedObject = {}
const lines = str.split('\n').filter((line) => line.trim() !== '')
lines.forEach((line) => {
const parts = line.split(/:(.+)/).map((part) => part.trim())
if (parts.length < 2 || !parts[0] || !parts[1]) {
reject(new Error(`Invalid format for line: "${line}". Expected 'key: value'.`))
}
const [rawKey, value] = parts
const keys = rawKey.split('.')
let current: Record<string, NestedObject | string | number | boolean | string[]> = result
keys.forEach((key, index) => {
if (index === keys.length - 1) {
current[key] = parseValue(value)
} else {
if (!(key in current)) {
current[key] = {}
}
if (typeof current[key] !== 'object' || Array.isArray(current[key])) {
reject(new Error(`Path conflict or invalid nesting for key: "${rawKey}".`))
}
current = current[key] as NestedObject
}
})
})
resolve(result)
})
const hoconToObject = (hoconData: string): Record<string, any> => {
try {
const parsedData = parseHoconToObject(hoconData)
return Promise.resolve(parsedData)
} catch (error) {
return Promise.reject(error)
}
}

return {
Expand All @@ -466,7 +439,7 @@ export default (gatewayName?: string | undefined): ListenerUtils => {
handleListenerDataWhenItIsIndependent,
transPort,
extractDifferences,
objectToString,
stringToObject,
objectToHocon,
hoconToObject,
}
}
29 changes: 0 additions & 29 deletions src/hooks/Rule/rule/useRuleSourceEvents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import useDocLink from '@/hooks/useDocLink'
import useI18nTl from '@/hooks/useI18nTl'
import { EventForRule } from '@/types/enum'
import { camelCase } from 'lodash'
import { computed } from 'vue'
import { useStore } from 'vuex'
Expand All @@ -11,7 +9,6 @@ export default (): {
isMsgPubEvent: (event: string) => boolean
getEventLabel: ({ zh, en }: { zh: string; en: string }) => string
getEventDesc: (event: string) => string
getEventDocLink: (event: string) => string
} => {
const { state } = useStore()
const { tl } = useI18nTl('RuleEngine')
Expand All @@ -20,40 +17,14 @@ export default (): {
const getEventLabel = ({ zh, en }: { zh: string; en: string }) => (isZh.value ? zh : en)
const getEventDesc = (event: string) => tl(`${camelCase(event.slice(8))}Desc`)

const zhHookMap: Record<string, string> = {
[EventForRule.MessageDelivered]: '消息投递事件',
[EventForRule.MessageAcked]: '消息确认事件',
[EventForRule.MessageDropped]: '消息在转发的过程中被丢弃事件',
[EventForRule.ClientConnected]: '终端连接成功事件',
[EventForRule.ClientDisconnected]: '终端连接断开事件',
[EventForRule.ClientConnack]: '连接确认事件',
[EventForRule.ClientCheckAuthzComplete]: '鉴权完成事件',
[EventForRule.SessionSubscribed]: '终端订阅成功事件',
[EventForRule.SessionUnsubscribed]: '取消终端订阅成功事件',
[EventForRule.DeliveryDropped]: '消息在投递的过程中被丢弃事件',
}

const isZh = computed(() => state.lang === 'zh')

const { docMap } = useDocLink()
const { isMsgPubEvent } = useRuleUtils()

const getEventDocLink = (event: string) => {
if (isMsgPubEvent(event)) {
return docMap.ruleEventMsgPub
}
let hook = event.slice(1).replace(/(\/|_)/g, '-')
if (isZh.value) {
hook = `${zhHookMap[event] || ''}-${hook}`
}
return `${docMap.ruleEvent}#${hook}`
}

return {
eventDoNotNeedShow,
isMsgPubEvent,
getEventLabel,
getEventDesc,
getEventDocLink,
}
}
4 changes: 2 additions & 2 deletions src/i18n/Gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,8 @@ export default {
en: 'Custom Configuration',
},
customConfigDescription: {
zh: '使用键值对配置更多监听器参数。',
en: 'Use key-value pairs to configure more listener parameters.',
zh: '使用 Hocon 配置更多监听器参数。',
en: 'Use hocon to configure more listener parameters.',
},
maxLenOfFrame: {
zh: '最大帧长度',
Expand Down
1 change: 1 addition & 0 deletions src/shims-vue.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ declare module 'js-sql-parser'
declare module 'highlight.js/lib/core'
declare module 'highlight.js/lib/languages/sql'
declare module 'json-bigint'
declare module 'hocon-parser'
declare module 'to-json-schema'
Loading

0 comments on commit 0709cc0

Please sign in to comment.