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

Плагин — это отдельное фронтовое приложение на 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 — необязательное описание на русском и английском языках;

Используйте 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, способ доступа к ним (access) и параметры авторизации. Основной способ — запросы через прокси платформы (proxy). Прямые запросы из браузера (direct) не поддерживают авторизацию и не рекомендуются к использованию. Подробнее см. в разделе Внешние API.

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

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

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

Платформа предоставляет плагину собственное JSON-хранилище — storageApi. Данные хранятся на стороне платформы, поэтому не зависят от устройства пользователя. Область видимости записи определяет контекст:

  • orgShared — общая запись на всю организацию. Подходит для общих настроек плагина и кешей справочников.
  • user — личная запись текущего пользователя. Подходит для персональных настроек и состояния интерфейса.
  • resource — запись, привязанная к ресурсу Трекера по идентификатору resourceId, например issue:MYQUEUE-123 или queue:MYQUEUE. Запись общая для всех пользователей, которые обращаются к этому ресурсу.

Внутри контекста плагин может хранить несколько независимых записей — они разделяются по ключу bucket. Операция get читает запись, patch создает или изменяет ее по merge-семантике: значение null удаляет поле. Конкурентные изменения контролируются версией записи (optimistic locking).

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

Доступ к внешним API через прокси

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

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

  • Авторизация прокси-запросов — автор плагина описывает в манифесте, какой токен нужен для домена и в какой заголовок его подставить. Сам токен в манифесте не указывается. Если токена еще нет, Трекер показывает пользователю диалог ввода токена с инструкцией автора плагина. Токен хранится на стороне платформы и не передается в iframe плагина: плагин узнает только, есть ли авторизация. При запросах через прокси платформа подставляет токен автоматически. Поддерживаются только личные токены пользователя — контекст user.

Подробнее о том, как вызывать внешние API из плагина, — в разделе Внешние 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.