Как устроена платформа плагинов

Плагин — это отдельное фронтовое приложение на JavaScript, которое запускается в iframe внутри приложения Трекера. Для его разработки предоставляется SDK. Приоритетным считается использование React. Также существует библиотека на чистом JavaScript, которая позволяет не привязываться к React.

Платформа покрывает полный жизненный цикл плагинов: предоставляет инструменты разработки и отладки, версионирования и публикации, а также репозиторий для хранения плагинов. Администраторы могут устанавливать, обновлять и отключать плагины, контролируя их доступность для пользователей и контекстов.

Выполнение плагина в iframe

Интерфейсная часть плагинов представляет собой статику, которая размещена на серверах Трекера. Она выполняется в изолированном iframe на безкуковом домене, которому запрещено почти всё, включая любые сетевые запросы. Поэтому всё общение с сервисами Яндекса, публичным API Трекера и внешним миром осуществляется через SDK.

SDK

Работа с Трекером осуществляется с помощью SDK, который подключается как библиотека. SDK предоставляет методы взаимодействия на уровне пользовательского интерфейса, методы для работы с публичным API Трекера и хранилище данных плагина.

SDK поставляется двумя npm-пакетами:

  • core — API-клиент и базовые примитивы интеграции (JS/TS).
  • react — React-обертки над core для удобной разработки UI.

CLI

CLI — это консольная утилита в виде NPM пакета, которая обеспечивает весь жизненный цикл плагина: создание, отладка, проверка и публикация.

UI-компоненты

Библиотека UI-компонентов в стилистике Трекера, доступных для использования в плагинах.

Манифест

Манифест — это файл manifest.json, лежащий в корне плагина. Манифест описывает метаданные плагина.

Основные поля:

  • id — уникальный идентификатор плагина. Доступны строчные буквы латиницы, цифры и дефис. Максимальная длина — 63 символа. Для выделения скоупа, то есть объединяющего признака для ваших плагинов, используйте два дефиса. Например, yandex-tracker--import-csv.

  • version — версия вашего плагина

  • permissionsтребуемые разрешения. Полученное разрешение необходимо, но не достаточно для совершения какого-либо действия, поскольку также учитываются права пользователя плагина.

  • slots — точки интеграции плагина в интерфейс Трекера. Подробнее о слотах.

    Плагин может быть интегрирован одновременно в разные точки интерфейса. Для каждого места интеграции можно указать различные входные точки в плагин в параметре entrypoint. Также в объекте слота есть параметр title, в котором содержится название кнопки/пункта меню, через которое плагин будет вызван.

    Для каждого слота необходимо указать параметр contextLevel, который определяет уровень контекста, доступного плагину:

    • basic — в контексте доступен только идентификатор текущей сущности и базовые метаданные. Подходит для большинства плагинов, которым не нужны полные данные сущности.
    • full — полный контекст с живыми данными сущности, например все поля тикета. Используйте этот уровень, только если плагину действительно нужны полные данные.
    "slots": {
      "tracker": {
        "issue.action": [
          {
            "entrypoint": "index.html",
            "contextLevel": "basic",
            "title": {
              "ru": "Мой плагин",
              "en": "My Plugin"
            }
          }
        ]
      }
    }
    
  • externalHost — позволяет указать внешний домен, на котором откроется интерфейс плагина. Этот способ не рекомендуется и требует явного согласования с командой Трекера.

Также в манифесте на данный момент находятся поля, которые будут отображаться пользователям в интерфейсе установки плагинов: author, support, keywords, whatsnew, description, name.

Информация про манифест для ИИ

При создании плагина через CLI, рядом с файлом манифеста будет создана JSON-схема. Если у ИИ возникнут проблемы в работе с манифестом, то можно явно указать на этот файл.

Разрешения

Существует 4 категории разрешений:

  • data - это разрешения на работу с данными. Имеют формат СЕРВИС:ДОМЕН:ДЕЙСТВИЕ, например tracker:issues:write - это разрешение на запись для тикетов в Трекере. Домен определяется первой частью в пути REST API.
  • device - это разрешения типа доступа к микрофону, камере, звуку.
  • ui - это разрешения на какие-либо действия с интерфейсом. Например, popup - показ всплывающего окна
  • external - это разрешения на доступ за пределы трекера

Уровень доступа

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

Хранилище данных плагинов

Платформа предоставляет единое хранилище данных плагинов и API для работы с ним, которое поддерживает два режима:

  • Контекстное хранение [Скоро] — данные привязываются к конкретной сущности Трекера и уровню гранулярности (organization, user, queue, portfolio, project, issue, issue-comment). Это подходит для настроек и состояния, зависящих от организации, пользователя, очереди или конкретного тикета. Контекст хранения не обязан совпадать с контекстом установки плагина.

  • Key-value режим — данные хранятся как пары key → value в namespace плагина. Это подходит для простых конфигураций и служебных данных, где важнее быстрый доступ по ключу, чем привязка к доменной сущности.

В обоих режимах API покрывает чтение/запись/обновление/удаление данных, поддерживает контроль конкурентных изменений (версионирование/optimistic locking) и хранит pluginVersion для управляемых миграций данных при обновлении плагина.

Подробнее о текущей реализации хранилища и его API — в разделе Хранилище данных плагина.

Доступ к внешним API (OAuth и Proxy)

Платформа предоставляет безопасный механизм интеграций с внешними сервисами: generic proxy для выполнения HTTP-запросов наружу и сервис-секретницу для хранения OAuth-токенов.

  • Generic proxy — единая точка для вызовов внешних API от имени плагина. Плагин отправляет параметры запроса: URL, метод, заголовки, тело. Платформа выполняет запрос на бекенде и возвращает результат. Доступ ограничивается декларативно (allowlist доменов). Платформа блокирует небезопасные направления (приватные сети/localhost, неразрешенные редиректы), ведет аудит и применяет rate limit по pluginId и организации.

  • OAuth и Секретница — платформа берет на себя OAuth-flow и хранение токенов: access/refresh токены сохраняются в секретнице и никогда не попадают в iframe плагина. Токены поддерживаются в двух контекстах: user (персональные) и organization (общие для организации, создаются администратором). При proxy-запросах платформа автоматически подставляет нужный токен, а при истечении — выполняет refresh на бекенде или сигнализирует о необходимости переавторизации.

Подробнее о том, как вызывать внешние API из плагина, — в разделе Внешние API.

Точки интеграции (слоты)

Место, в котором плагин будет открыт, а также механизм его вызова (кнопка или как часть формы, например), определяется слотом. Слот указывается в манифесте.

Доступные слоты и детали их использования описаны в разделе Точки интеграции.

Контекст запуска плагина

Контекст запуска плагина определяется слотом, в котором он находится и содержит следующую информацию:

  • Текущая тема хоста
  • Текущий язык хоста
  • Контекст слота — данные из окружения страницы, в которой запущен плагин. Например, в слоте issue.action вернется информация по открытому тикету. Формат контекста максимально приближен к типам публичного API.

Уровни контекста слота

Уровень контекста задается параметром contextLevel в описании слота в manifest.json и определяет, какие данные будут доступны плагину через useTrackerPluginContext.

basic

Доступен только идентификатор текущей сущности и базовые метаданные:

type BasicContext = {
    /** Идентификатор текущей сущности */
    entityId: string;
    /** Дополнительная базовая информация о сущности.
     *  Например, для комментария здесь будет идентификатор родительского тикета. */
    entityMeta?: Record<string, string>;
};

Используйте basic, если плагину достаточно знать идентификатор сущности — это более легкий вариант без лишней нагрузки.

full

Полный контекст с живыми данными сущности. Формат совпадает с типами публичного API. Например, для слота issue.action — полный объект Issue. Данные актуальны на момент открытия плагина.

Используйте full, только если плагину действительно нужны полные данные сущности.

Выбор уровня контекста

Запрашивайте только тот уровень контекста, который реально нужен плагину. При отсутствии необходимости в полных данных сущности используйте basic.