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

Плагин — это отдельное фронтовое приложение на 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 в корне проекта. Он описывает плагин, запрашиваемые разрешения и точки интеграции с Трекером.

JSON Schema

При создании проекта CLI добавляет рядом с манифестом файл manifest.schema.json и указывает его в поле $schema:

{
  "$schema": "./manifest.schema.json"
}

Редактор кода использует схему для проверки манифеста, автодополнения и показа допустимых значений. CLI проверяет манифест по встроенной схеме, даже если поле $schema отсутствует. Не редактируйте manifest.schema.json вручную. Чтобы обновить локальную схему и остальные файлы шаблона, выполните команду weavix up.

Ограничения и списки допустимых значений могут меняться. Проверяйте их в manifest.schema.json текущего проекта.

Поля манифеста

Поле

Тип

Обязательное

Описание

$schema

string

Нет

Путь к JSON Schema. CLI добавляет значение ./manifest.schema.json

manifest_version

number

Нет

Версия формата манифеста. CLI добавляет значение 1

supported_services

string[]

Нет

Сервисы, в которых работает плагин. Для плагина Трекера укажите ["tracker"]

slug

string

Да

Уникальное имя плагина из строчных латинских букв, цифр и дефисов. Длина — от 3 до 63 символов

id

string

Нет

Идентификатор плагина на платформе. CLI добавляет его при регистрации во время первой отправки командой submit. Не задавайте поле вручную

version

string

Да

Версия плагина в формате SemVer, например 1.2.0

name

object

Да

Название на русском и английском языках в полях ru и en. Длина каждого значения — от 1 до 30 символов

description

object

Да

Описание на русском и английском языках в полях ru и en. Длина каждого значения — от 1 до 255 символов

support

object[]

Да

Непустой список контактов поддержки. Каждый объект содержит type со значением email и адрес длиной до 100 символов в поле value

permissions

object

Да

Разрешения, необходимые плагину. В объекте обязательно поле data

slots

object

Нет

Точки интеграции плагина с интерфейсом сервисов

categories

string[]

Нет

Категории, по которым плагин отображается в каталоге

docsUrl

string

Нет

Адрес справки плагина

homepage

string

Нет

Адрес сайта плагина с протоколом HTTP или HTTPS

license

string

Нет

Идентификатор лицензии длиной до 32 символов, например MIT или Apache-2.0

keywords

string[]

Нет

Уникальные ключевые слова для поиска плагина. Длина каждого значения — до 30 символов

whatsnew

string

Нет

Описание изменений в версии длиной до 300 символов

externalHost

string

Нет

HTTPS-адрес, с которого загружается интерфейс плагина. Использование внешнего хоста требует согласования с командой Трекера

Настройка слотов

Плагин может использовать несколько точек интеграции. Для каждой точки укажите:

  • entrypoint — путь к входному HTML-файлу;
  • title — название элемента интерфейса на русском и английском языках;
  • contextLevel — уровень данных, доступных плагину:
    • basic — идентификатор текущей сущности и базовые метаданные;
    • full — полный объект сущности, например все поля задачи;
  • description — необязательное описание на русском и английском языках;
  • icon — необязательный объект с путем к значку в поле url. Используется в слоте dock.

Используйте full, только если плагину необходим полный объект сущности.

Поле slots не является обязательным по формату манифеста, но для проверки и публикации проекта необходимо настроить хотя бы один слот.

"slots": {
  "tracker": {
    "issue.action": [
      {
        "entrypoint": "index.html",
        "contextLevel": "basic",
        "title": {
          "ru": "Мой плагин",
          "en": "My Plugin"
        }
      }
    ]
  }
}

Разрешения

В манифесте укажите только те разрешения, которые необходимы плагину. Объект permissions содержит следующие поля:

  • data — обязательный массив разрешений на работу с данными Трекера. Разрешения имеют формат tracker:<ресурс>:read для чтения и tracker:<ресурс>:write для изменения данных. Например, tracker:issues:read позволяет читать задачи, tracker:issues:write — изменять их, а tracker:attachments:write — загружать вложения. Актуальный список допустимых значений приведен в manifest.schema.json;
  • device — доступ к возможностям устройства пользователя. Допустимые значения: clipboard-write, microphone, camera, geolocation, web-share и allow-downloads. Если это предусмотрено браузером, пользователь должен дополнительно разрешить доступ к выбранной возможности;
  • ui — дополнительные действия в интерфейсе Трекера. Добавляйте такое разрешение, только если оно указано в описании используемого метода SDK;
  • external — домены внешних API и параметры авторизации. Прямые запросы к таким API из плагина запрещены: их выполняет прокси платформы. Подробнее см. в разделе Внешние API.

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

Разрешение в манифесте не расширяет права пользователя. Все действия выполняются от имени текущего пользователя и в пределах выданных ему прав.

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

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

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

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

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

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

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

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

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

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