diff --git a/openspec/changes/aurora-1843-svod2-server-paging/design.md b/openspec/changes/aurora-1843-svod2-server-paging/design.md index 0384971..0090955 100644 --- a/openspec/changes/aurora-1843-svod2-server-paging/design.md +++ b/openspec/changes/aurora-1843-svod2-server-paging/design.md @@ -150,10 +150,10 @@ | параметр | тип | ограничение | |---|---|---| | `view` | `Literal["full","compact"] \| None` | | -| `search` | `str \| None` | `max_length=1000` | -| `smeta_type` | `str \| None` | `max_length=1000` | -| `smeta_direction` | `str \| None` | `max_length=1000` | -| `org_name` | `str \| None` | `max_length=1000` | +| `search` | `str \| None` | без ограничения длины | +| `smeta_type` | `str \| None` | без ограничения длины | +| `smeta_direction` | `str \| None` | без ограничения длины | +| `org_name` | `str \| None` | без ограничения длины | Значения фильтров — свободные строки, а не перечисления: семантика `equals` (регистр и пробелы по краям не важны) принимает и «кв», и «КВ ». Неизвестное значение даёт 0 строк, как на экране. @@ -442,7 +442,7 @@ PR создаются по готовности, merge — по команде - Совместимость с PG15.12 — отдельный шаг плана «проверка контроллером», контейнер `dfip-pg15`, порт 55434. **Фаза 1, `api/tests/unit`:** -- валидация (по образцу `test_query_validation.py`): 422 на `search`/фильтры длиннее 1000 и на прежние невалидные параметры; +- валидация (по образцу `test_query_validation.py`): 422 на прежние невалидные параметры; `search` и фильтры длиной 5000 символов дают 200 (без ограничения длины); - сервис: - `_allowed_org_ids` применяется к страницам и вариантам; - пустое пересечение даёт пустой ответ без SQL; @@ -624,6 +624,6 @@ PR создаются по готовности, merge — по команде 21. **2026-10-28 — значения для поиска строятся теми же выражениями, что `data` ответа (`header`, `contract_summary`, `contract_detail` листа), объединяются `jsonb ||` (перекрытие как у JS-спреда), числа приводятся к тексту функцией `svod2_js_number_text` (правила `Number::toString` поверх кратчайшей записи `float8`), обрезка — по набору пробельных символов ECMAScript.** Обоснование: решение человека 9 — точное повторение `matchesSearch`. Цена ошибки: если есть расхождение, оно ловится оракулом Node на данных стенда до слияния. -22. **2026-10-28 — ограничение длины `search` и значений фильтров — 1000 символов (422).** - Обоснование: защита от очень длинных URL. На экране ограничения нет, но поиск длиннее 1000 символов нереалистичен. - Цена ошибки: пользователь с таким запросом увидит ошибку вместо пустого результата. Снять ограничение — одна строка. +22. **2026-10-28 — ~~ограничение длины `search` и значений фильтров — 1000 символов (422)~~. Отменено контроллером 28.10: противоречит решению человека 9 («поведение абсолютно такое же»).** + Новая формулировка: API не ограничивает длину `search` и значений фильтров и не отвечает ошибкой из-за длины; длинный запрос даёт тот же результат, что на экране (совпадения есть или нет). Защита от злоупотребления — только невидимая пользователю: общий предел длины строки запроса у веб-сервера (uvicorn/h11 и прокси платформы, порядка килобайт и выше) остаётся как есть, отдельного предела в API не вводим. + Цена ошибки: очень длинный URL (десятки КБ) может упереться в предел прокси до приложения — на экране это нереалистичный ввод; если понадобится, фронт переведёт запросы на POST — полдня. diff --git a/openspec/changes/aurora-1843-svod2-server-paging/specs/summary2-api/spec.md b/openspec/changes/aurora-1843-svod2-server-paging/specs/summary2-api/spec.md index 2ddabe0..9d68073 100644 --- a/openspec/changes/aurora-1843-svod2-server-paging/specs/summary2-api/spec.md +++ b/openspec/changes/aurora-1843-svod2-server-paging/specs/summary2-api/spec.md @@ -45,7 +45,7 @@ 4. Значения берутся такими, какими они приходят в ответе этого листа. Например, на листе RF `contract_detail.expenses_q1..4` — это значения ответа (`COALESCE` договора и ЦКК). 5. Приведение к нижнему регистру SHALL NOT зависеть от локали базы данных. Для латиницы ASCII и кириллицы (включая Ё и буквы U+0400–U+042F, U+0490) результат SHALL совпадать с `toLowerCase()`. -Поиск сочетается с остальными условиями по «И». `search` длиннее 1000 символов SHALL отклоняться с 422. +Поиск сочетается с остальными условиями по «И». Длина `search` API не ограничивает: любой запрос даёт тот же результат, что поиск экрана (совпадения есть или их нет), без ошибки. #### Scenario: Поиск без учёта регистра по кириллице независимо от локали - **WHEN** в данных есть строка с `header.name = «Аренда помещения»`, база работает с `LC_CTYPE=C` или `en_US.utf8`, и выполняется запрос с `search=аРЕНДА` @@ -63,6 +63,10 @@ - **WHEN** у строки `contract_detail.ceiling_amount = 1234.50` в БД - **THEN** `search=1234.5` находит строку, а `search=1234.50` не находит +#### Scenario: Очень длинный поиск без ошибки +- **WHEN** передан `search` длиной 5000 символов, которого нет ни в одном значении +- **THEN** ответ — 200 с пустым `result` и `total` = 0, как пустой результат поиска на экране + #### Scenario: Пустой поиск не сужает - **WHEN** в новом режиме передан `search` из одних пробельных символов - **THEN** `total` равен `total` того же запроса без `search` @@ -72,7 +76,7 @@ - **THEN** множества и порядок `line_id` совпадают для каждого запроса ### Requirement: Колоночные фильтры с семантикой экрана -Параметры `smeta_type`, `smeta_direction` и `org_name` SHALL оставлять строки, у которых соответствующее значение (`data.header.smeta_type`, `data.header.smeta_direction`, `data.header.org_name`) не `null` и после `toLowerCase().trim()` равно значению параметра после `toLowerCase().trim()`. Это семантика фильтра `equals` таблицы экрана до изменения. Пустое значение параметра SHALL NOT сужать выборку. Параметры длиннее 1000 символов SHALL отклоняться с 422. Все условия, включая `org_ids` и `search`, сочетаются по «И». `org_ids`, как и раньше, пересекается с организациями, разрешёнными пользователю. +Параметры `smeta_type`, `smeta_direction` и `org_name` SHALL оставлять строки, у которых соответствующее значение (`data.header.smeta_type`, `data.header.smeta_direction`, `data.header.org_name`) не `null` и после `toLowerCase().trim()` равно значению параметра после `toLowerCase().trim()`. Это семантика фильтра `equals` таблицы экрана до изменения. Пустое значение параметра SHALL NOT сужать выборку. Длина значений API не ограничивает, ошибки из-за длины нет. Все условия, включая `org_ids` и `search`, сочетаются по «И». `org_ids`, как и раньше, пересекается с организациями, разрешёнными пользователю. #### Scenario: Фильтр по виду сметы - **WHEN** запрос с `smeta_type=КВ`