add mapper. add v2 handler. add tag description short and full versions. delete topic[1-5]. #5

Merged
APervov merged 5 commits from edge5modbus into main 2026-06-19 06:15:29 +00:00
Contributor
  1. Удалены демонстрационные топики topic1-5.
  2. Добавлены файлы маппинга: *.json - англоязычное описание наименований тегов. *_rus.json - русскоязычные наименования тегов. *_unit.json - полное описание, которое включает англоязычное, русскоязычное описание, а также единицы измерения.
  3. Добавлены новые хэндлеры demo-plc.ts и edge-modbus-v2.ts
1. Удалены демонстрационные топики topic1-5. 2. Добавлены файлы маппинга: *.json - англоязычное описание наименований тегов. *_rus.json - русскоязычные наименования тегов. *_unit.json - полное описание, которое включает англоязычное, русскоязычное описание, а также единицы измерения. 3. Добавлены новые хэндлеры demo-plc.ts и edge-modbus-v2.ts
APervov added 1 commit 2026-06-09 22:50:25 +00:00
NBizyaev reviewed 2026-06-10 09:37:52 +00:00
@@ -0,0 +13,4 @@
}
type DemoPlcMetric = DemoPlcPayload & {
edge: string
Owner

Избыточная валидация

Избыточная валидация
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-10 09:38:02 +00:00
@@ -0,0 +18,4 @@
}
function isDemoPlcPayload(value: unknown): value is DemoPlcPayload {
if (!value || typeof value !== 'object') {
Owner

Избыточная валидация

Избыточная валидация
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-10 09:38:32 +00:00
@@ -0,0 +25,4 @@
const candidate = value as Partial<DemoPlcPayload>
return (
typeof candidate.tag === 'string' &&
Owner

Избыточная валидация

Избыточная валидация
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-10 09:40:36 +00:00
@@ -0,0 +51,4 @@
})
if (!response.ok) {
const text = await response.text()
Owner

Просто бросить исключение new Error(text)

Просто бросить исключение new Error(text)
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-10 09:40:51 +00:00
@@ -0,0 +75,4 @@
return
}
if (!isDemoPlcPayload(value)) {
Owner

Избыточная валидация

Избыточная валидация
NBizyaev reviewed 2026-06-10 09:42:04 +00:00
@@ -0,0 +42,4 @@
})
if (!response.ok) {
const text = await response.text()
Owner

Просто бросить исключение new Error(text)

Просто бросить исключение new Error(text)
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-10 09:50:21 +00:00
@@ -0,0 +68,4 @@
let value: unknown
try {
Owner

Обработка ошибок хандлера должна быть не в самом хандлере (это избыточно перегружает его код валидациями так, что при чтении кода теряется смысл и цель происходящего - интерпретация данных для последующей отправки).

Обработка ошибок всех хандлеров должна быть вообще на уровне роутинга входящих пакетов из топиков на хандлеры (app\src\router.ts), а все проблемы/ошибки/несоответствия ожиданиям/ваыпадения из позитивного сценария -- должны бросать исключение
в том числе некорректные данные,
в том числе недоступность или ошибки принимающих сервисов,
в том числе вообще все ошибки, которые не будут покрыты отдельным специальным if.

В этом смысле чем меньше отдельных специальных if - тем лучше, потому что проблемы всё равно обработаются и выловятся уровнем выше, но кода при этом будет меньше.

Обработка ошибок хандлера должна быть не в самом хандлере (это избыточно перегружает его код валидациями так, что при чтении кода теряется смысл и цель происходящего - интерпретация данных для последующей отправки). Обработка ошибок всех хандлеров должна быть вообще на уровне роутинга входящих пакетов из топиков на хандлеры (app\src\router.ts), а все проблемы/ошибки/несоответствия ожиданиям/ваыпадения из позитивного сценария -- должны бросать исключение в том числе некорректные данные, в том числе недоступность или ошибки принимающих сервисов, в том числе вообще все ошибки, которые не будут покрыты отдельным специальным if. В этом смысле чем меньше отдельных специальных if - тем лучше, потому что проблемы всё равно обработаются и выловятся уровнем выше, но кода при этом будет меньше.
NBizyaev reviewed 2026-06-10 09:52:14 +00:00
@@ -0,0 +59,4 @@
let values: number[]
try {
Owner

Обработка ошибок хандлера должна быть не в самом хандлере, а выше - на уровне роутинга входящих пакетов из топиков на хандлеры (app\src\router.ts), а все проблемы/ошибки/несоответствия ожиданиям/ваыпадения из позитивного сценария -- должны бросать исключение
в том числе некорректные данные,
в том числе недоступность или ошибки принимающих сервисов,
в том числе вообще все ошибки, которые не будут покрыты отдельным специальным if.
В этом смысле чем меньше отдельных специальных if - тем лучше, потому что проблемы всё равно обработаются и выловятся уровнем выше, но кода при этом будет меньше.

Обработка ошибок хандлера должна быть не в самом хандлере, а выше - на уровне роутинга входящих пакетов из топиков на хандлеры (app\src\router.ts), а все проблемы/ошибки/несоответствия ожиданиям/ваыпадения из позитивного сценария -- должны бросать исключение в том числе некорректные данные, в том числе недоступность или ошибки принимающих сервисов, в том числе вообще все ошибки, которые не будут покрыты отдельным специальным if. В этом смысле чем меньше отдельных специальных if - тем лучше, потому что проблемы всё равно обработаются и выловятся уровнем выше, но кода при этом будет меньше.
NBizyaev reviewed 2026-06-10 09:54:21 +00:00
@@ -0,0 +26,4 @@
filePath: string | URL
}
function resolveMappingPath(filePath: string | URL): string {
Owner

Просто в комментарии в коде напиши, в каком формате и где ты задаёшь и ожидаешь filePath,
соблюдай утвержденный самим собой контракт,
и удали функцию проверки filePath.

Просто в комментарии в коде напиши, в каком формате и где ты задаёшь и ожидаешь filePath, соблюдай утвержденный самим собой контракт, и удали функцию проверки filePath.
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-10 09:55:24 +00:00
@@ -0,0 +38,4 @@
const resolvedPath = resolveMappingPath(filePath)
let raw: unknown
try {
Owner

Обработка ошибок хандлера должна быть не в самом хандлере, а выше - на уровне роутинга входящих пакетов из топиков на хандлеры (app\src\router.ts), а все проблемы/ошибки/несоответствия ожиданиям/ваыпадения из позитивного сценария -- должны бросать исключение
в том числе некорректные данные,
в том числе отсутствие каких-либо файлов,
в том числе вообще все ошибки, которые не будут покрыты отдельным специальным if.
В этом смысле чем меньше отдельных специальных if - тем лучше, потому что проблемы всё равно обработаются и выловятся уровнем выше, но кода при этом будет меньше.

Обработка ошибок хандлера должна быть не в самом хандлере, а выше - на уровне роутинга входящих пакетов из топиков на хандлеры (app\src\router.ts), а все проблемы/ошибки/несоответствия ожиданиям/ваыпадения из позитивного сценария -- должны бросать исключение в том числе некорректные данные, в том числе отсутствие каких-либо файлов, в том числе вообще все ошибки, которые не будут покрыты отдельным специальным if. В этом смысле чем меньше отдельных специальных if - тем лучше, потому что проблемы всё равно обработаются и выловятся уровнем выше, но кода при этом будет меньше.
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-10 09:56:16 +00:00
@@ -0,0 +59,4 @@
const candidate = value as Partial<MappingConfig>
return (
typeof candidate.edge === 'string' &&
Owner

Избыточная валидация

Избыточная валидация
NBizyaev reviewed 2026-06-10 09:56:23 +00:00
@@ -0,0 +67,4 @@
)
}
function isMappingTagEntry(value: unknown): value is MappingTagEntry {
Owner

Избыточная валидация

Избыточная валидация
NBizyaev reviewed 2026-06-10 09:58:44 +00:00
@@ -0,0 +79,4 @@
)
}
function getTag(entry: MappingTagEntry): string {
Owner

вместо этой функции достаточно написать "entry.tag"
9 символов
эта функция не нужна, она избыточна, она перегружает код,
делает его восприятие сложнее,
делает требования у уровню читающего - выше.
И всё это - при нулевой полезности такой функции.
Удали её.

вместо этой функции достаточно написать "entry.tag" 9 символов эта функция не нужна, она избыточна, она перегружает код, делает его восприятие сложнее, делает требования у уровню читающего - выше. И всё это - при нулевой полезности такой функции. Удали её.
NBizyaev reviewed 2026-06-10 09:59:01 +00:00
@@ -0,0 +95,4 @@
edge: config.edge,
tagCount: tags.length,
mapValues: (values, timestamp) => {
if (values.length !== tags.length) {
Owner

Избыточная валидация

Избыточная валидация
NBizyaev reviewed 2026-06-10 10:02:54 +00:00
@@ -0,0 +39,4 @@
let raw: unknown
try {
raw = JSON.parse(readFileSync(resolvedPath, 'utf8')) as unknown
Owner

Если ты используешь чтение внешнего файла, то этот файл должен существовать
в контейнере, в котором этот код будет задеплоен,
это значит, что копирование этих файлов должно появиться в Dockerfile
или (если они туда уже копируются) хотя бы комментарий в Dockerfile о том, что там что-то такое есть, что программа будет искать, считывать и потом использовать.

Если ты используешь чтение внешнего файла, то этот файл должен существовать в контейнере, в котором этот код будет задеплоен, это значит, что копирование этих файлов должно появиться в Dockerfile или (если они туда уже копируются) хотя бы комментарий в Dockerfile о том, что там что-то такое есть, что программа будет искать, считывать и потом использовать.
NBizyaev marked this conversation as resolved
APervov added 1 commit 2026-06-11 23:39:18 +00:00
APervov added 1 commit 2026-06-14 22:41:56 +00:00
NBizyaev reviewed 2026-06-15 06:41:57 +00:00
@@ -0,0 +15,4 @@
function parseValues(payload: Buffer): number[] {
let value: unknown
try {
Owner

избыточная валидация
JSON.parse при некорректной JSON‑строке сам выкинет исключение SyntaxError

избыточная валидация JSON.parse при некорректной JSON‑строке сам выкинет исключение SyntaxError
NBizyaev marked this conversation as resolved
APervov added 1 commit 2026-06-17 06:40:56 +00:00
NBizyaev reviewed 2026-06-17 06:56:16 +00:00
@@ -0,0 +3,4 @@
export const cloudIngestUrl =
process.env.CLOUD_INGEST_URL ?? DEFAULT_CLOUD_INGEST_URL
function getHeaders(): Record<string, string> {
Owner

function getHeaders(): Record<string, string> {
return {
'content-type': 'application/json',
'x-api-key': process.env.CLOUD_INGEST_API_KEY
}
}

function getHeaders(): Record<string, string> { return { 'content-type': 'application/json', 'x-api-key': process.env.CLOUD_INGEST_API_KEY } }
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-17 06:56:57 +00:00
@@ -0,0 +19,4 @@
export function postCloudIngest(body: unknown): Promise<Response> {
return fetch(cloudIngestUrl, {
method: 'POST',
headers: getHeaders(),
Owner

headers: {
'content-type': 'application/json',
'x-api-key': process.env.CLOUD_INGEST_API_KEY
},

headers: { 'content-type': 'application/json', 'x-api-key': process.env.CLOUD_INGEST_API_KEY },
NBizyaev reviewed 2026-06-17 07:15:21 +00:00
app/package.json Outdated
@@ -5,3 +5,2 @@
"scripts": {
"dev": "tsx watch src/index.ts",
"start": "tsx src/index.ts",
"dev": "node --env-file-if-exists=.env ./node_modules/tsx/dist/cli.mjs watch src/index.ts",
Owner

флаги указывающие на
--env-file=.env
--env-file-if-exists=.env
просто пробрасываются к node:

"dev": "tsx watch --env-file=.env src/index.ts"
флаги указывающие на `--env-file=.env` `--env-file-if-exists=.env` просто пробрасываются к node: ``` "dev": "tsx watch --env-file=.env src/index.ts" ```
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-17 07:24:38 +00:00
app/package.json Outdated
@@ -6,2 +6,2 @@
"dev": "tsx watch src/index.ts",
"start": "tsx src/index.ts",
"dev": "node --env-file-if-exists=.env ./node_modules/tsx/dist/cli.mjs watch src/index.ts",
"start": "node --env-file-if-exists=.env ./node_modules/tsx/dist/cli.mjs src/index.ts",
Owner

Скрипт start тут вообще не нужен.
В продакшне

FROM node:22-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build         # tsc → dist/
CMD ["node", "dist/index.js"] # скомпилированный код запускается с помощью node

а переменные окружения возьмутся собственно из окружения (например из портейнера)

Скрипт start тут вообще не нужен. В продакшне ``` FROM node:22-alpine WORKDIR /app COPY . . RUN npm install RUN npm run build # tsc → dist/ CMD ["node", "dist/index.js"] # скомпилированный код запускается с помощью node ``` а переменные окружения возьмутся собственно из окружения (например из портейнера)
NBizyaev marked this conversation as resolved
NBizyaev reviewed 2026-06-17 07:33:11 +00:00
app/package.json Outdated
@@ -7,2 +6,3 @@
"start": "tsx src/index.ts",
"dev": "node --env-file-if-exists=.env ./node_modules/tsx/dist/cli.mjs watch src/index.ts",
"start": "node --env-file-if-exists=.env ./node_modules/tsx/dist/cli.mjs src/index.ts",
"build": "tsc",
Owner

ну обычно так и оставляют

"scripts": {
  "dev":   "tsx watch --env-file=.env src/index.ts", // запуск во время разработки
  "build": "tsc", // компиляция, сборка в бандл dist
  "start": "node --env-file=.env dist/index.js" // проверка бандла
}
ну обычно так и оставляют ```json "scripts": { "dev": "tsx watch --env-file=.env src/index.ts", // запуск во время разработки "build": "tsc", // компиляция, сборка в бандл dist "start": "node --env-file=.env dist/index.js" // проверка бандла } ```
APervov added 1 commit 2026-06-19 05:17:53 +00:00
NBizyaev reviewed 2026-06-19 05:50:16 +00:00
@@ -0,0 +1,15 @@
const DEFAULT_CLOUD_INGEST_URL = 'http://localhost:3101/ingest'
Owner

Значения по умолчанию для переменных окружения должны быть вынесены в отдельный модуль валидации значений переменных окружения.

Значение переменной CLOUD_INGEST_URL является критическим для такого сервиса как mqtt-ingest, при не указанном значении CLOUD_INGEST_URL сервис не сможет отправлять данные, а значит - выполнять свою функцию, а значит в случае отсутствия значения CLOUD_INGEST_URL - приложение при старте должно ругнуться и не запуститься вовсе, чтобы при деплое на продакшн, это сразу было заметно.

Значения по умолчанию для переменных окружения должны быть вынесены в отдельный модуль валидации значений переменных окружения. Значение переменной CLOUD_INGEST_URL является критическим для такого сервиса как mqtt-ingest, при не указанном значении CLOUD_INGEST_URL сервис не сможет отправлять данные, а значит - выполнять свою функцию, а значит в случае отсутствия значения CLOUD_INGEST_URL - приложение при старте должно ругнуться и не запуститься вовсе, чтобы при деплое на продакшн, это сразу было заметно.
APervov merged commit 381bb5db31 into main 2026-06-19 06:15:29 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: NBizyaev/mqtt-ingest#5