fix(1c-analyst): поле Description (не Content) + обязательная верификация GET-ом

Критичный баг-фикс по режиму «Пожелания из совещания».

Проблема: сервер Devprom на POST /issue/items молча игнорирует поле
Content, упомянутое в публичной документации /docs/8835.html.
Запись создаётся с пустым телом, HTTP 200, UID возвращается —
никакого сигнала об ошибке. Правильное имя поля для тела пожелания
на эндпоинте /issue/items — Description.

Выявлено при проверке U-6186 (Артур): поле Description пустое,
хотя POST вернул валидный UID. Та же проблема обнаружилась на 6
пожеланиях Тепловин U-6189..U-6194 — все с пустым телом.
Пересоздано как U-6198..U-6203 с полем Description, размер тела
1565–2172 символа, верификация пройдена.

Правки скилла:
- devprom-alm-api.md §0: новый блок «Критично: сервер молча отбрасывает
  незнакомые поля» + обязательная схема контрольного GET с ассертами.
  Список известных тихих ловушек (Content вместо Description, Type.Id=''
  в request/items, Requirement в POST).
- devprom-alm-api.md §2: рецепт с Description + встроенные ассерты.
- devprom-alm-api.md §3: справочник полей — Description с пояснением
  про Content и документацию, требование GET-проверки.
- devprom-alm-api.md appendix: curl-примеры с Description.
- meeting-wishes-extraction.md Шаг 6: Description в инструкции + ссылка
  на секцию «тихое отбрасывание».
- meeting-wishes-extraction.md Шаг 7: жёсткая формулировка
  «Ответ POST — недостаточное подтверждение успеха», ассерты
  на префикс UID, содержимое Description, Priority, Function;
  визуальная проверка в UI.
- meeting-wishes-extraction.md Python-шаблон: Description + ассерты
  на размер тела (>100 символов).
This commit is contained in:
creator
2026-04-22 07:02:53 +00:00
parent dee7d51c19
commit 7365242875
2 changed files with 93 additions and 31 deletions
+46 -9
View File
@@ -36,6 +36,36 @@
- **PATCH не поддерживается** — HTTP 405. Обновление только через PUT.
- **Успешный ответ на создание:** иногда возвращается как объект, иногда — как массив с одним элементом. Учитывать оба случая.
### ⚠ Критично: сервер молча отбрасывает незнакомые поля
Devprom REST API **не возвращает ошибку**, если имя поля в теле POST
не соответствует схеме сущности. Поле просто игнорируется, запись
создаётся с остальными полями, HTTP 200, `UID` возвращается.
**Следствие:** ответ POST нельзя считать подтверждением, что данные
сохранены. Успешный HTTP 200 + валидный JSON с `UID` означают только
что запись появилась, но часть полей может быть потеряна.
**Правило:** после КАЖДОГО POST на `/issue/items` (и любой другой
сущности, где критично содержимое) делать контрольный GET и глазами
или ассертом проверять реально сохранённые значения. Минимум:
```python
code, text = call("GET", f"issue/items/{new_id}")
g = json.loads(text)
assert g["UID"].startswith("U-"), "неверный префикс UID"
assert len(g.get("Description","")) > 100, "тело пожелания пустое"
# опционально: проверить Caption, Priority.Id, Function.Id, Company.Id
```
Известные «тихие ловушки»:
- `Content` вместо `Description` в теле POST на `/issue/items`
поле игнорируется, запись создаётся с пустым телом.
- `Type: {"Id":""}` при POST на `/request/items` → игнорируется,
сервер ставит `Type=397` (Доработка).
- `Requirement` при POST на `/issue/items` → игнорируется
(привязку делать через PUT или в UI).
## 1. Пожелания vs Заявки — КРИТИЧНО
В официальной схеме Devprom это **две разные сущности** с разными
@@ -74,14 +104,20 @@ _Проверено эмпирически:_ `/request/items` при POST **вс
```python
post_body = {
"Caption": "<заголовок>",
"Content": "<html-описание>", # по документации; Description тоже принимается
"Priority": {"Id": "2"}, # 1=Критично, 2=Высокий, 3=Обычный
"Author": {"Id": "<issueauthor.id>"},
"Function": {"Id": "<feature.id>"}, # подсистема (группировка)
"Company": {"Id": "<company.id>"}, # опционально: клиент-источник
"Caption": "<заголовок>",
"Description": "<html-тело>", # ← именно Description, НЕ Content
"Priority": {"Id": "2"}, # 1=Критично, 2=Высокий, 3=Обычный
"Author": {"Id": "<issueauthor.id>"},
"Function": {"Id": "<feature.id>"}, # подсистема (группировка)
"Company": {"Id": "<company.id>"}, # опционально: клиент-источник
}
# POST {BASE}/issue/items → {UID: "U-xxxx", Id: "xxxx", ...}
# ⚠ ОБЯЗАТЕЛЬНЫЙ контрольный GET — сервер может молча отбросить поля
code, text = call("GET", f"issue/items/{new_id}")
g = json.loads(text)
assert g["UID"].startswith("U-"), f"префикс не U-: {g['UID']}"
assert len(g.get("Description","")) > 100, "тело пожелания пустое"
```
Поля `Requirement` и `RequirementDocument` в теле **не передаются**:
@@ -106,7 +142,7 @@ Egress-прокси периодически роняет запросы с HTTP
| Поле | Тип / формат | Обязательность | Комментарий |
|-----------------------|------------------------------|----------------|-------------|
| `Caption` | string | обязательно | Заголовок пожелания |
| `Content` | HTML-string | обязательно | Описание. Разрешены: `<p> <b> <ul> <ol> <li> <a> <blockquote> <code>`. Поле `Description` на входе тоже принимается как алиас. |
| `Description` | HTML-string | обязательно | **Тело пожелания.** Имя поля — `Description`, не `Content`. Эмпирически: `Content` на `/issue/items` при POST молча игнорируется (принимается, но не сохраняется). В публичной документации (`/docs/8835.html`) упомянуто поле `Content` — это относится к `/request/items` и/или документации в целом устаревшее; для `/issue/items` используйте только `Description`. Разрешённые теги HTML: `<p> <b> <ul> <ol> <li> <a> <blockquote> <code>`. **После POST обязательно делать GET и проверять `len(Description) > 100`** — сервер не возвращает ошибку при отброшенном поле. |
| `Priority.Id` | "1" / "2" / "3" | по смыслу | 1=Критично, 2=Высокий, 3=Обычный |
| `Author.Id` | issueauthor.id | обязательно | Автор пожелания. Справочник инстанс-уровня. |
| `Function.Id` | feature.id | рекомендовано | Группировка по подсистемам (создаются через `/feature/items`) |
@@ -255,8 +291,9 @@ curl -X POST \
curl -X POST \
-H "Devprom-Auth-Key: <KEY>" -H "Content-Type: application/json" \
https://<host>/pm/<proj>/api/latest/issue/items \
-d '{"Caption":"Пожелание","Content":"<p>Описание</p>","Priority":{"Id":"2"},"Author":{"Id":"1"},"Function":{"Id":"190"},"Company":{"Id":"146"}}'
-d '{"Caption":"Пожелание","Description":"<p>Тело</p>","Priority":{"Id":"2"},"Author":{"Id":"1"},"Function":{"Id":"190"},"Company":{"Id":"146"}}'
# → UID=U-xxxx, Type=пусто
# ⚠ После создания — GET и проверить len(Description) > 100
```
### Привязка к документу требований (не нужна в режиме «Пожелания из совещания»)
@@ -267,7 +304,7 @@ curl -X POST \
curl -X PUT \
-H "Devprom-Auth-Key: <KEY>" -H "Content-Type: application/json" \
https://<host>/pm/<proj>/api/latest/issue/items/6179 \
-d '{"Caption":"Пожелание","Content":"<p>Описание</p>","Priority":{"Id":"2"},"Author":{"Id":"1"},"Function":{"Id":"190"},"Requirement":{"Id":"4378"}}'
-d '{"Caption":"Пожелание","Description":"<p>Тело</p>","Priority":{"Id":"2"},"Author":{"Id":"1"},"Function":{"Id":"190"},"Requirement":{"Id":"4378"}}'
```
### Удаление