From 48691e79f239eb4646c8d2d4a846c1595cf66cf3 Mon Sep 17 00:00:00 2001 From: NurAlan Date: Mon, 12 Jan 2026 14:07:56 +0700 Subject: [PATCH] chore: change validation unitNumber to alphabet --- src/helpers/alphabet_conversion.ts | 34 ++++++++++++++++++++++++++ src/modules/device/commands/service.ts | 4 ++- src/modules/device/queries/service.ts | 5 ++-- src/modules/device/schema.ts | 4 +-- 4 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 src/helpers/alphabet_conversion.ts diff --git a/src/helpers/alphabet_conversion.ts b/src/helpers/alphabet_conversion.ts new file mode 100644 index 0000000..9ed0a1c --- /dev/null +++ b/src/helpers/alphabet_conversion.ts @@ -0,0 +1,34 @@ +/** + * Converts a single alphabet character (A-Z) into its corresponding string number (01-26). + * Case-insensitive. Non-alphabet characters will return an empty string or could be handled as needed. + * @param char The alphabet character to convert. + * @returns The two-digit string representation of the alphabet's position. + */ +export const alphabetToNumberString = (char: string): string => { + if (!char || char.length !== 1) return '' + + const upperChar = char.toUpperCase() + const code = upperChar.charCodeAt(0) + + // A is 65, Z is 90 + if (code >= 65 && code <= 90) { + const position = code - 64 + return position.toString().padStart(2, '0') + } + + return '' +} + +/** + * Converts a string of alphabet characters into a string of numeric representations. + * Example: "ABC" -> "010203" + * @param input The string to convert. + * @returns The converted string of numbers. + */ +export const convertAlphabetString = (input: string): string => { + return input + .split('') + .map(alphabetToNumberString) + .filter((s) => s !== '') + .join('') +} diff --git a/src/modules/device/commands/service.ts b/src/modules/device/commands/service.ts index ea0c196..526df2c 100644 --- a/src/modules/device/commands/service.ts +++ b/src/modules/device/commands/service.ts @@ -1,6 +1,7 @@ import { dataMapping } from '~/helpers/utils' import { DeviceCommandBody } from '../schema' import { sendCommandToThirdParty } from './command' +import { alphabetToNumberString } from '~/helpers/alphabet_conversion' export default abstract class CommandService { static async sendCommand(body: DeviceCommandBody): Promise<{ @@ -8,7 +9,8 @@ export default abstract class CommandService { message: string receivedAt: string }> { - const topicData = `${body.merchantName}/${body.floorName}_${body.unitNumber}-${body.deviceName}-${body.roomName}-${body.deviceType}-${body.commandType}-${body.towerNumber}` + const unitNumber = alphabetToNumberString(body.unitNumber) + const topicData = `${body.merchantName}/${body.floorName}_${unitNumber}-${body.deviceName}-${body.roomName}-${body.deviceType}-${body.commandType}-${body.towerNumber}` const cmd = dataMapping.find((device) => device.deviceName == body.deviceName) const request = await sendCommandToThirdParty(topicData, `${cmd?.command}${body.payload.action}`) if (request.status !== 200) { diff --git a/src/modules/device/queries/service.ts b/src/modules/device/queries/service.ts index 09ed64b..3001011 100644 --- a/src/modules/device/queries/service.ts +++ b/src/modules/device/queries/service.ts @@ -1,3 +1,4 @@ +import { alphabetToNumberString } from '~/helpers/alphabet_conversion' import { sendQueryToThirdParty } from '../commands/command' import { DeviceQueryParams } from '../schema' @@ -17,8 +18,8 @@ export default abstract class QueryService { } | null }> { // TODO: Check nonce for replay attack when DB is implemented - - const topic = `${query.merchantName}/${query.floorName}_${query.unitNumber}-${query.deviceName}-${query.roomName}-${query.deviceType}-S-${query.towerNumber}` + const unitNumber = alphabetToNumberString(query.unitNumber) + const topic = `${query.merchantName}/${query.floorName}_${unitNumber}-${query.deviceName}-${query.roomName}-${query.deviceType}-S-${query.towerNumber}` const request = await sendQueryToThirdParty(topic) diff --git a/src/modules/device/schema.ts b/src/modules/device/schema.ts index 2b9159f..2c3cc58 100644 --- a/src/modules/device/schema.ts +++ b/src/modules/device/schema.ts @@ -4,7 +4,7 @@ import { z } from 'zod' export const deviceCommandBody = z.object({ merchantName: z.string(), floorName: z.string(), - unitNumber: z.string(), + unitNumber: z.string().regex(/^[A-Z]+$/, 'unitNumber must contain only uppercase alphabet characters'), deviceName: z.string(), roomName: z.string(), deviceType: z.string(), @@ -20,7 +20,7 @@ export type DeviceCommandBody = z.infer export const deviceQueryParams = z.object({ merchantName: z.string(), floorName: z.string(), - unitNumber: z.string(), + unitNumber: z.string().regex(/^[A-Z]+$/, 'unitNumber must contain only uppercase alphabet characters'), deviceName: z.string(), roomName: z.string(), deviceType: z.string(),