- `GET /api/v1/metrics/{name}?from&to&layer` — ряд точек за период; конверт
объявляет слой, измеренный род, его применимость к отданному ряду и границу
окна измерения, а сам ряд собирается из одного слоя, выбранного по охвату
точек внутри периода
- use-case вынесен в `internal/points`, чтение — одним входом `store.ReadSeries`
под одной транзакцией; правило выбора слоя остаётся в домене и приходит в
хранилище колбэком
- `writeJSON` перестал экранировать HTML-символы и перестал глушить отказ
записи: дословность содержимого точки иначе не удерживается, а оборванное
тело уходило под видом успешного `200`
6.0 KiB
Why
Точки лежат в витрине и наружу не отдаются: «вес за год» достаётся только
sqlite3 на хосте. Каталог уже отвечает, что есть, — но ни один из трёх
потребителей не может получить сами значения.
Ответ обязан быть самоописательным. Метрика лежит сразу в нескольких слоях подробности, а род её свёртки не объявлен источником, а измерен окном в 48 самых свежих общих часов — окном, которое замирает при выключенной минутной автоматизации HAE и продолжает объявлять род. Клиент, не видящий ни слоя, ни границы этого окна, принимает решение по числу, происхождения которого не знает.
What Changes
- Новый читающий маршрут
GET /api/v1/metrics/{name}?from&to&layer— все точки метрики за период, одним запросом, без доступа к файлу базы. - Конверт ответа объявляет фактические параметры выдачи:
metric,from,to,layer,bucket,aggregation(род, его применимость к отданному ряду и граница окна измерения),points. Поля присутствуют всегда — клиент не выводит их наличием или отсутствием. - Слой выбирается правилом, а не молча: параметр
layerзадаёт разрез явно, без него берётся слой с наибольшим охватом точек внутри запрошенного периода, при равенстве — самый мелкий. Смены слоя внутри одного ответа не бывает. - Род свёртки в конверте — тот же измеренный
catalog.Style, что у каталога, и измеряется он тем же правилом и тем же окном. Род неизвестен — так и сказано словомunknown, а не молчанием. Рядом едет применимость:cumulativeна нижнем слое HAE объявляется неприменимым, иначе конверт приглашает потребителя сложить интерполяцию и завысить втрое. - Свёртка в этом изменении не выполняется:
bucketвсегдаnull, а присутствие параметраbucketв запросе отвергается400, а не игнорируется молча. - Ответ снимается одной транзакцией чтения, а метка ответа включает
канонизированную форму запроса и горизонт измерения — иначе соседняя
задача условного запроса подтвердит
304на сменившемся роде. - Форма провода — по образцу каталога (ADR
ADR-2026-08-04-forma-provoda-prinadlezhit-transportu): типы*Wireвinternal/httpapi, перевод присваиванием, строка в таблице образцовwire_internal_test.go.
Схема базы не трогается: обе выборки отвечают по существующим ключу и
покрывающему индексу bucket_catalog.
Capabilities
New Capabilities
points: точки метрики за период — форма запроса и его разбор, правило выбора слоя, состав конверта ответа, объявление измеренного рода вместе с границей окна измерения, дословность значений точки.
Modified Capabilities
read-api: добавляются два общих правила читающих маршрутов — сериализация ответа не экранирует содержимое (иначе&внутри дословно сохранённой точки уезжает как&) и ответ чтения помечается непригодным для разделяемого кеша. Оба механизма общие (writeJSON,setReadHeaders), и решать их заново на каждом маршруте — тот же второй способ.
catalog (правило измерения рода) применяется новым маршрутом без изменения его
требований.
Impact
internal/httpapi— маршрут, разбор параметров, форма провода точек, выключение HTML-экранирования в общемwriteJSON.internal/points(новый) — use-case «точки метрики за период»: выбор слоя, сборка ряда, измерение рода и метка ответа по одной метрике.internal/store— один вход чтения ряда (ReadSeries) в одной транзакции: охваты слоёв в периоде, точки выбранного слоя, объекты окна измерения.docs/architecture.md— разделы «Read API» (форма ответа, правило выбора слоя) и таблица компонентов.- Потребители: HTTP-клиенты; следом этот же конверт копируют свёртка по сетке, условный запрос по точкам, тренировки, записи и MCP.