Files

24 KiB
Raw Permalink Blame History

Devprom ALM REST API — рабочая шпаргалка

Справочник для загрузки Пожеланий, Заявок, Требований и вложений в Devprom ALM через REST API. Имена сущностей сверены с официальной таблицей справочника разработчика: /pm/all/apidocs/list (например, artsaterra.myalm.ru/pm/all/apidocs/list), где каждая строка — одна сущность с её ReferenceName (используется в REST API) и русским именем. Ключевые строки, участвующие в режиме Пожеланий из совещания:

ReferenceName Сущность
45 issue Пожелание
18 request Доработка (заявка с типом)
81 requesttype Тип пожелания
92 requirement Требование
57 attachment Приложение (файл)
33 company Компания
46 user Пользователь

Дополнительные эмпирические детали (поведение, которое в публичной документации не раскрыто явно) проверены на инстансе artsaterra.myalm.ru и отмечаются в тексте курсивом (проверено эмпирически).

Связанные ресурсы:


0. Базовое

  • Хост: https://<host>/pm/<project-slug>/api/latest
  • Аутентификация: заголовок Devprom-Auth-Key: <hex> (личный API-ключ пользователя)
  • Content-Type: application/json
  • Ответ: HTTP 200 всегда, ошибки валидации приходят в теле как {"error":"…"}
  • PATCH не поддерживается — HTTP 405. Обновление только через PUT.
  • Успешный ответ на создание: иногда возвращается как объект, иногда — как массив с одним элементом. Учитывать оба случая.

⚠ Критично: сервер молча отбрасывает незнакомые поля

Devprom REST API не возвращает ошибку, если имя поля в теле POST не соответствует схеме сущности. Поле просто игнорируется, запись создаётся с остальными полями, HTTP 200, UID возвращается.

Следствие: ответ POST нельзя считать подтверждением, что данные сохранены. Успешный HTTP 200 + валидный JSON с UID означают только что запись появилась, но часть полей может быть потеряна.

Правило: после КАЖДОГО POST на /issue/items (и любой другой сущности, где критично содержимое) делать контрольный GET и глазами или ассертом проверять реально сохранённые значения. Минимум:

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 это две разные сущности с разными ReferenceName (см. таблицу выше):

  • issue (Пожелание) — сущность из строки 45. Создаётся без типа, UID с префиксом U-. В UI живёт в разделе «Пожелания» (/module/requirements/issues).
  • request (Доработка) — сущность из строки 18. Создаётся с обязательным типом (Доработка / Ошибка), UID с префиксом I-. В UI живёт на доске заявок (/issues/board).
Логический класс Эндпоинт Префикс UID Поле Type UI-раздел
Пожелание (issue) /issue/items U- пусто /module/requirements/issues
Доработка (request) /request/items I- 397 (Доработка) / 398 (Ошибка) /issues/board

Проверено эмпирически: GET на /issue/items/{id} и /request/items/{id} возвращает одну и ту же запись (разные виды на один ресурс — внутренне это один класс pm_ChangeRequest). Разница проявляется при POST — сервер применяет разную логику автозаполнения в зависимости от эндпоинта.

Почему попасть в «Доску пожеланий» можно только через /issue/items

Проверено эмпирически: /request/items при POST всегда принудительно проставляет Type=397 (Доработка), даже если передать Type: null, Type: {} или Type: {"Id":""}. Все варианты PUT тоже бессильны — сервер восстанавливает 397. Подтверждено на 8 вариантах тела.

Поэтому для создания «чистого» Пожелания (с пустым Type и префиксом U-) используется эндпоинт именно сущности issuePOST на /issue/items.

2. Алгоритм создания Пожелания (рабочий рецепт)

Один POST на одно Пожелание. Без PUT, без привязки к требованиям.

post_body = {
    "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 в теле не передаются: привязка к документу требований — задача пользователя в UI, не API. На этапе создания Пожелания она и не нужна — заказчик подтверждает пожелания, формализует требования и уже потом вручную связывает одно с другим.

Примечание для других сценариев. Если всё-таки нужно привязать Пожелание к существующему требованию из API (например, массовая миграция из внешней системы), это делается вторым шагом через PUT /issue/items/{id} с тем же телом + Requirement.Id — в одном POST эти поля молча игнорируются. Для режима «Пожелания из совещания» этот путь не нужен.

Retry-политика

Egress-прокси периодически роняет запросы с HTTP 502/503/504 («DNS cache overflow»). Всегда оборачивать вызов в цикл retries=3 с sleep(2..3). Это не связано с Devprom — лечится только ретраями.

3. Поля Пожелания — полный справочник

Поле Тип / формат Обязательность Комментарий
Caption string обязательно Заголовок пожелания
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)
Company.Id company.id опционально Клиент-источник пожелания. Принимается в POST напрямую.
Requirement.Id requirement.id не передавать в режиме Пожелания из совещания Привязка к документу требований — задача пользователя в UI. Для других сценариев (миграция) — только через отдельный PUT после POST.
RequirementDocument Не передавать, заполняется сервером автоматически по родителю Requirement
Type.Id requesttype.id / пусто НЕ передавать для пожелания Любая попытка обнулить через API бесполезна; если передать — получится «Заявка» с префиксом I-

4. Удаление

Эндпоинт DELETE работает? Поведение
DELETE /issue/items/{id} ДА — универсальный HTTP 200, запись пропадает. Работает для ЛЮБОЙ записи — и для U- (созданных через /issue/items), и для I- (созданных через /request/items). Сущности issue и request указывают на один класс pm_ChangeRequest в БД, но DELETE разрешён только на пути issue/items.
DELETE /request/items/{id} НЕТ HTTP 200 с {"error":"Unable access entity"}, запись не удаляется. Сервер возвращает эту ошибку даже для записей, созданных через /request/items.
DELETE /requirement/items/{id} Не гарантировано Возвращает HTTP 200 с {"error":"Unable access entity"}, но GET после этого иногда показывает запись пустой (UID='', Caption=''). Эффект — неявное удаление / повреждение записи. Использовать с осторожностью.

Практический вывод: для удаления любой записи Request/Issue — всегда DELETE /issue/items/{id}, независимо от того, как она была создана.

5. Справочные эндпоинты (read-only для нашего ключа)

Эндпоинт Назначение
/requesttype/items Типы заявок: Доработка, Ошибка
/priority/items Приоритеты
/feature/items Подсистемы/функции (имя "feature", не "function"!)
/issueauthor/items Авторы пожеланий (инстанс-справочник)
/user/items Пользователи проекта
/company/items Компании-клиенты
/requirement/items Требования и документы требований
/state/items Состояния

6. Что НЕ работает через API (заблокировано политикой прав)

Операция Ответ API Обход
POST /issueauthor/items HTTP 200 + {"error":"Lack of permissions to create object of IssueAuthor"} Только UI: /pm/<project>/issueauthor или inline-форма в карточке пожелания
POST /user/items То же Только UI
DELETE /request/items/{id} {"error":"Unable access entity"} Не препятствие: удалять через DELETE /issue/items/{id} — он работает для любой записи Request (см. раздел 4).

Пытаться обойти через write-only ключ проекта, api/v1, api/v2, инстанс-путь без /pm/<project>/бесполезно, все варианты дают тот же отказ.

7. Создание документа требований

⚠️ В режиме «Пожелания из совещания» этот путь НЕ используется. Структуру документов требований (requirement, строка 92 таблицы сущностей) выстраивает владелец проекта/аналитик вручную через UI Devprom — постепенно, по мере формализации требований, согласованных с заказчиком. Привязку Пожеланий (issue) к требованиям пользователь тоже делает вручную в UI. Режим извлечения пожеланий в эту структуру не вмешивается. Раздел оставлен для справки — на случай других задач (миграция требований, импорт из внешней системы и т.п.).

POST /requirement/items
body = {
    "Caption": "Корневой документ",
    "Content": "<p>описание</p>",
    # "ParentPage": {"Id": "..."}  # если создаём подстраницу
}

Корневой документ — без ParentPage. Подстраница — с ParentPage.Id = <id_корня>. UID корневых документов присваивается по правилам шаблона проекта (например, О-1, R-35).

8. Attachment (приложить файл)

По официальной документации:

POST /attachment/items
body = {
    "FileExt":     "transcript.txt",
    "ObjectId":    "<request.id>",
    "ObjectClass": "Request",          # именно Request — общий класс для issue и request
    "File":        "<base64 содержимого>",
}

9. Альтернативные и несуществующие эндпоинты

Тестировались, но возвращают {"error":"Unknown entity: ..."}:

  • /userwish/items, /wish/items, /userrequest/items, /suggestion/items
  • /admin/request/items, /api/latest/admin/request/items
  • /pmcustomclass/items, /customclass/items, /class/items

Существующие служебные варианты:

  • /api/v1/request/items, /api/v2/request/items — принимают POST, но принудительная логика Type=397 та же, что на /api/latest/request/items; использовать не нужно.

Примечание: эндпоинт /issue/items — не алиас /request/items, а отдельная сущность issue (Пожелание) из таблицы сущностей Devprom, строка 45. Под капотом они указывают на один и тот же класс pm_ChangeRequest, но серверная логика автозаполнения при POST у них разная (см. раздел 1).

10. Порядок подготовки проекта для загрузки пожеланий

Граница ответственности: структуру проекта готовит человек через UI, пожелания загружает скрипт через API. Скрипт не создаёт Company, Feature, документы требований, IssueAuthor — только читает их.

Делается вручную через UI (один раз на проект, силами аналитика)

  1. Company для каждого клиента, который генерирует пожелания.
    • Пример: «АРД» с доменом alreadycom.ru.
  2. Feature (подсистемы) — 3–5 на проект по подсистемам целевой конфигурации. Можно создать и скриптом (POST /feature/items) — на feature ограничений API нет, но обычно удобнее через UI.
    • Пример для КА 2.5: «НСИ и администрирование», «Продажи», «Склад», «Казначейство», «Производство».
  3. IssueAuthor для представителей заказчика — если нужно, чтобы автором пожелания был сам клиент, а не аналитик. Только через UI.
  4. Документы требований — структуру требований клиент/аналитик выстраивает постепенно, по мере работы над проектом. Режим извлечения Пожеланий в неё не вмешивается.

Делается скриптом через API (каждое новое совещание)

  1. Цикл по Пожеланиям — для каждого: один POST /issue/items с полями Caption, Content, Priority, Author, Function и опционально Company. Requirement не передаётся.
  2. GET /issue/items/{id} — верификация, что UID начинается на U-.
  3. Привязка Пожеланий к требованиямвне скрипта, это ручная работа пользователя в UI, когда требования появятся и согласованы с заказчиком.

11. URL-схема Devprom UI

Прямые ссылки на интерфейс — для отчётов пользователю и связывания сущностей между системами.

Что URL-формат
Карточка артефакта (любой префикс: U-, I-, R-, T-, TS- и т.д.) https://<host>/pm/<project>/<UID>
Список Пожеланий https://<host>/pm/<project>/module/requirements/issues
Доска Заявок (Доработки/Ошибки) https://<host>/pm/<project>/issues/board
Документы требований https://<host>/pm/<project>/module/requirements

Карточка по UID — самый универсальный способ дать прямую ссылку. UID сам определяет тип артефакта, никакая «секция» в URL не нужна.

Проверено эмпирически: ссылки с подразумеваемой «секцией» (/pm/<project>/issues/<UID>, /pm/<project>/requests/<UID> и т.п.) возвращают 404. Работает только /pm/<project>/<UID>.

Пример:

https://artsaterra.myalm.ru/pm/test-api-claude/U-6316

Appendix — примеры curl

Создание пожелания (как в документации, с Type=Доработка)

curl -X POST \
  -H "Devprom-Auth-Key: <KEY>" -H "Content-Type: application/json" \
  https://<host>/pm/<proj>/api/latest/request/items \
  -d '{"Caption":"Новая доработка","Content":"<p>Описание</p>","Type":{"Id":"397"}}'
# → UID=I-xxxx, Type=Доработка

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

curl -X POST \
  -H "Devprom-Auth-Key: <KEY>" -H "Content-Type: application/json" \
  https://<host>/pm/<proj>/api/latest/issue/items \
  -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

Привязка к документу требований (не нужна в режиме «Пожелания из совещания»)

Используется только для миграционных сценариев:

curl -X PUT \
  -H "Devprom-Auth-Key: <KEY>" -H "Content-Type: application/json" \
  https://<host>/pm/<proj>/api/latest/issue/items/6179 \
  -d '{"Caption":"Пожелание","Description":"<p>Тело</p>","Priority":{"Id":"2"},"Author":{"Id":"1"},"Function":{"Id":"190"},"Requirement":{"Id":"4378"}}'

Удаление

curl -X DELETE \
  -H "Devprom-Auth-Key: <KEY>" \
  https://<host>/pm/<proj>/api/latest/issue/items/6179
# → HTTP 200, []