Система плагинов FlowCraft
FlowCraft предоставляет мощную и гибкую систему плагинов, которая позволяет расширять функциональность платформы без изменения основного кода. В этом документе описаны архитектура системы плагинов, процесс создания и публикации плагинов, а также лучшие практики разработки.
Обзор системы плагинов
Система плагинов FlowCraft построена на принципах модульности, изоляции и безопасности. Она позволяет разработчикам создавать различные типы расширений:
- Узлы (Nodes) - новые типы узлов для использования в рабочих процессах
- Триггеры (Triggers) - новые способы запуска рабочих процессов
- Коннекторы (Connectors) - интеграции с внешними сервисами и API
- Функции-помощники (Helpers) - утилиты для использования в рабочих процессах
- Темы (Themes) - кастомизация пользовательского интерфейса
- Расширения ядра (Core Extensions) - расширение базовой функциональности платформы
Архитектура системы плагинов
Компоненты системы плагинов
Система плагинов FlowCraft состоит из следующих основных компонентов:
Менеджер плагинов (Plugin Manager)
- Загрузка и инициализация плагинов
- Управление жизненным циклом плагинов
- Разрешение зависимостей между плагинами
- Версионирование плагинов
Реестр плагинов (Plugin Registry)
- Хранение метаданных плагинов
- Индексирование доступных плагинов
- Поиск и фильтрация плагинов
Песочница (Sandbox)
- Изоляция выполнения кода плагинов
- Ограничение доступа к ресурсам
- Мониторинг производительности
- Защита от вредоносного кода
API плагинов (Plugin API)
- Интерфейсы для взаимодействия с платформой
- Доступ к контексту выполнения
- Утилиты для разработки плагинов
Маркетплейс плагинов (Plugin Marketplace)
- Публикация и распространение плагинов
- Рейтинги и отзывы
- Управление версиями
- Монетизация плагинов
Жизненный цикл плагина
Жизненный цикл плагина в FlowCraft включает следующие этапы:
Установка
- Загрузка пакета плагина
- Проверка совместимости с текущей версией платформы
- Разрешение зависимостей
- Распаковка и установка файлов
Инициализация
- Загрузка кода плагина
- Регистрация компонентов плагина
- Подключение к системным событиям
- Инициализация ресурсов
Выполнение
- Использование компонентов плагина в рабочих процессах
- Взаимодействие с другими плагинами
- Доступ к API платформы
Деактивация
- Временное отключение плагина
- Сохранение состояния
- Освобождение ресурсов
Обновление
- Проверка наличия обновлений
- Загрузка новой версии
- Миграция данных и настроек
- Перезапуск плагина
Удаление
- Деактивация плагина
- Удаление файлов
- Очистка данных и настроек
Создание плагинов
Структура плагина
Плагин FlowCraft представляет собой пакет с определенной структурой файлов:
my-plugin/
├── package.json # Метаданные плагина и зависимости
├── icon.svg # Иконка плагина
├── README.md # Документация
├── LICENSE # Лицензия
├── src/
│ ├── index.js # Точка входа плагина
│ ├── nodes/ # Узлы, предоставляемые плагином
│ │ ├── MyNode1.js
│ │ └── MyNode2.js
│ ├── triggers/ # Триггеры, предоставляемые плагином
│ │ └── MyTrigger.js
│ ├── helpers/ # Функции-помощники
│ │ └── myHelper.js
│ └── credentials/ # Типы учетных данных
│ └── MyCredentials.js
├── test/ # Тесты
│ ├── nodes.test.js
│ └── triggers.test.js
└── dist/ # Скомпилированные файлы (для production)
└── ...
Файл package.json
Файл package.json
содержит метаданные плагина и его зависимости:
{
"name": "flowcraft-plugin-myservice",
"version": "1.0.0",
"description": "Integration with MyService API",
"author": "Your Name",
"license": "MIT",
"keywords": ["flowcraft", "plugin", "myservice"],
"flowcraft": {
"displayName": "MyService Integration",
"icon": "icon.svg",
"version": "1.0.0",
"compatibility": {
"flowcraft": ">=1.0.0"
},
"nodes": [
"MyNode1",
"MyNode2"
],
"triggers": [
"MyTrigger"
],
"credentials": [
"MyCredentials"
]
},
"main": "dist/index.js",
"scripts": {
"build": "webpack",
"test": "jest"
},
"dependencies": {
"axios": "^0.24.0"
},
"devDependencies": {
"webpack": "^5.65.0",
"jest": "^27.4.5"
}
}
Точка входа плагина
Файл index.js
является точкой входа плагина и экспортирует все компоненты плагина:
import MyNode1 from './nodes/MyNode1';
import MyNode2 from './nodes/MyNode2';
import MyTrigger from './triggers/MyTrigger';
import MyCredentials from './credentials/MyCredentials';
export default {
// Метод, вызываемый при инициализации плагина
init(context) {
// Регистрация компонентов
context.registerNode('MyNode1', MyNode1);
context.registerNode('MyNode2', MyNode2);
context.registerTrigger('MyTrigger', MyTrigger);
context.registerCredentials('MyCredentials', MyCredentials);
// Подписка на события
context.on('workflow.started', (workflow) => {
console.log(`Workflow ${workflow.id} started`);
});
return {
// Метод, вызываемый при деактивации плагина
cleanup() {
// Освобождение ресурсов
}
};
}
};
Создание узла
Узел (Node) - это основной компонент плагина, который выполняет определенную операцию в рабочем процессе. Пример создания узла:
export default {
// Метаданные узла
type: 'action',
name: 'MyNode1',
displayName: 'My Custom Node',
description: 'Performs a custom operation',
icon: 'icon.svg',
category: 'Custom',
version: 1,
// Определение входных параметров
properties: {
inputs: [
{
name: 'inputParam1',
type: 'string',
displayName: 'Input Parameter 1',
description: 'First input parameter',
required: true,
default: ''
},
{
name: 'inputParam2',
type: 'number',
displayName: 'Input Parameter 2',
description: 'Second input parameter',
required: false,
default: 0
}
],
outputs: [
{
name: 'output',
type: 'object',
displayName: 'Output',
description: 'Output data'
}
]
},
// Метод, выполняющий операцию узла
async execute(context) {
const { inputParam1, inputParam2 } = context.inputs;
// Выполнение операции
const result = await someOperation(inputParam1, inputParam2);
// Возврат результата
return {
output: result
};
}
};
Создание триггера
Триггер (Trigger) - это компонент, который запускает выполнение рабочего процесса при наступлении определенного события. Пример создания триггера:
export default {
// Метаданные триггера
name: 'MyTrigger',
displayName: 'My Custom Trigger',
description: 'Triggers workflow on custom event',
icon: 'icon.svg',
category: 'Custom',
version: 1,
// Определение параметров
properties: {
inputs: [
{
name: 'interval',
type: 'number',
displayName: 'Interval (seconds)',
description: 'Interval between checks',
required: true,
default: 60
}
],
outputs: [
{
name: 'data',
type: 'object',
displayName: 'Trigger Data',
description: 'Data from the trigger'
}
]
},
// Метод, вызываемый при активации триггера
async activate(context) {
const { interval } = context.inputs;
// Настройка триггера
const timerId = setInterval(async () => {
// Проверка условия
const data = await checkCondition();
if (data) {
// Запуск рабочего процесса
context.emit({
data: data
});
}
}, interval * 1000);
// Сохранение идентификатора таймера
context.setState({ timerId });
return {
// Метод, вызываемый при деактивации триггера
deactivate() {
const { timerId } = context.getState();
clearInterval(timerId);
}
};
}
};
Определение типа учетных данных
Тип учетных данных (Credentials) определяет, какие данные необходимы для аутентификации в внешнем сервисе. Пример определения типа учетных данных:
export default {
// Метаданные типа учетных данных
name: 'MyCredentials',
displayName: 'My Service Credentials',
description: 'Credentials for My Service API',
// Определение полей
properties: [
{
name: 'apiKey',
type: 'string',
displayName: 'API Key',
description: 'API Key for My Service',
required: true,
secret: true
},
{
name: 'apiUrl',
type: 'string',
displayName: 'API URL',
description: 'URL of the API',
required: true,
default: 'https://api.myservice.com'
}
],
// Метод для тестирования учетных данных
async test(credentials) {
const { apiKey, apiUrl } = credentials;
try {
// Тестирование подключения
const response = await fetch(`${apiUrl}/test`, {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
if (response.ok) {
return {
success: true
};
} else {
return {
success: false,
error: `API returned status ${response.status}`
};
}
} catch (error) {
return {
success: false,
error: error.message
};
}
}
};
Тестирование плагинов
Модульное тестирование
Для тестирования отдельных компонентов плагина рекомендуется использовать модульные тесты. Пример теста для узла:
import MyNode1 from '../src/nodes/MyNode1';
describe('MyNode1', () => {
it('should process input correctly', async () => {
// Создание мок-контекста
const context = {
inputs: {
inputParam1: 'test',
inputParam2: 42
}
};
// Выполнение узла
const result = await MyNode1.execute(context);
// Проверка результата
expect(result).toHaveProperty('output');
expect(result.output).toEqual(expect.objectContaining({
// Ожидаемые свойства результата
}));
});
it('should handle errors gracefully', async () => {
// Создание мок-контекста с некорректными данными
const context = {
inputs: {
inputParam1: '',
inputParam2: -1
}
};
// Проверка обработки ошибок
await expect(MyNode1.execute(context)).rejects.toThrow();
});
});
Интеграционное тестирование
Для тестирования взаимодействия компонентов плагина с платформой рекомендуется использовать интеграционные тесты. FlowCraft предоставляет специальный тестовый фреймворк для этой цели:
import { TestRunner } from '@flowcraft/test-utils';
describe('MyPlugin Integration', () => {
let testRunner;
beforeAll(async () => {
// Инициализация тестового окружения
testRunner = new TestRunner();
await testRunner.init();
// Загрузка плагина
await testRunner.loadPlugin('./dist');
});
afterAll(async () => {
// Очистка тестового окружения
await testRunner.cleanup();
});
it('should execute workflow with custom nodes', async () => {
// Создание тестового рабочего процесса
const workflow = {
nodes: [
{
id: 'node1',
type: 'MyNode1',
parameters: {
inputParam1: 'test',
inputParam2: 42
}
},
{
id: 'node2',
type: 'MyNode2',
parameters: {
// Параметры узла
}
}
],
connections: [
{
source: 'node1',
target: 'node2',
sourceOutput: 'output',
targetInput: 'input'
}
]
};
// Выполнение рабочего процесса
const result = await testRunner.executeWorkflow(workflow);
// Проверка результата
expect(result.success).toBe(true);
expect(result.nodeResults).toHaveProperty('node2');
expect(result.nodeResults.node2).toEqual(expect.objectContaining({
// Ожидаемые свойства результата
}));
});
});
Публикация плагинов
Подготовка плагина к публикации
Перед публикацией плагина необходимо выполнить следующие шаги:
Проверка кода
- Убедитесь, что код соответствует стандартам качества
- Выполните статический анализ кода
- Исправьте все предупреждения и ошибки
Тестирование
- Запустите все модульные и интеграционные тесты
- Проверьте покрытие кода тестами
- Убедитесь, что плагин работает корректно в различных сценариях
Документация
- Создайте подробную документацию по использованию плагина
- Добавьте примеры использования
- Опишите все параметры и возвращаемые значения
Сборка
- Соберите плагин для production
- Минимизируйте размер пакета
- Убедитесь, что все необходимые файлы включены в пакет
Публикация в маркетплейсе
Для публикации плагина в маркетплейсе FlowCraft выполните следующие шаги:
Создание аккаунта разработчика
- Зарегистрируйтесь в маркетплейсе FlowCraft
- Подтвердите свою личность и контактные данные
- Создайте профиль разработчика
Загрузка плагина
- Войдите в панель разработчика
- Создайте новый плагин
- Загрузите пакет плагина
- Заполните информацию о плагине (название, описание, категория и т.д.)
Проверка плагина
- Плагин будет проверен командой FlowCraft на соответствие требованиям
- Будет выполнена проверка безопасности и качества кода
- При необходимости вам будет предложено внести изменения
Публикация
- После успешной проверки плагин будет опубликован в маркетплейсе
- Вы получите уведомление о публикации
- Плагин станет доступен для установки пользователями
Обновление плагина
Для обновления плагина выполните следующие шаги:
Подготовка новой версии
- Обновите версию в файле
package.json
- Внесите необходимые изменения в код
- Обновите документацию
- Проведите тестирование
- Обновите версию в файле
Загрузка обновления
- Войдите в панель разработчика
- Выберите плагин для обновления
- Загрузите новую версию пакета
- Опишите изменения в новой версии
Проверка и публикация
- Новая версия пройдет проверку
- После успешной проверки обновление будет опубликовано
- Пользователи получат уведомление о доступном обновлении
Лучшие практики разработки плагинов
Производительность
Оптимизация кода
- Используйте эффективные алгоритмы и структуры данных
- Минимизируйте использование ресурсов
- Избегайте блокирующих операций
Асинхронная обработка
- Используйте асинхронные операции для длительных задач
- Избегайте блокировки основного потока
- Правильно обрабатывайте Promise и async/await
Кэширование
- Кэшируйте результаты дорогостоящих операций
- Используйте механизмы кэширования платформы
- Правильно инвалидируйте кэш при изменении данных
Безопасность
Валидация входных данных
- Проверяйте все входные данные
- Используйте строгую типизацию
- Защищайтесь от инъекций и других атак
Безопасное хранение данных
- Не храните чувствительные данные в открытом виде
- Используйте механизмы шифрования платформы
- Минимизируйте доступ к чувствительным данным
Ограничение доступа
- Запрашивайте только необходимые разрешения
- Следуйте принципу наименьших привилегий
- Используйте механизмы авторизации платформы
Надежность
Обработка ошибок
- Корректно обрабатывайте все возможные ошибки
- Предоставляйте информативные сообщения об ошибках
- Реализуйте механизмы восстановления после сбоев
Повторные попытки
- Реализуйте механизм повторных попыток для нестабильных операций
- Используйте экспоненциальную задержку между попытками
- Ограничивайте количество повторных попыток
Тестирование
- Покрывайте код тестами
- Тестируйте граничные случаи и обработку ошибок
- Используйте автоматизированное тестирование
Удобство использования
Понятный интерфейс
- Используйте понятные названия и описания
- Группируйте связанные параметры
- Предоставляйте подсказки и примеры
Разумные значения по умолчанию
- Устанавливайте разумные значения по умолчанию
- Минимизируйте количество обязательных параметров
- Предоставляйте возможность настройки для продвинутых пользователей
Информативная обратная связь
- Предоставляйте информативные сообщения о ходе выполнения
- Отображайте прогресс для длительных операций
- Четко сообщайте о результатах операций
Примеры плагинов
Пример 1: Интеграция с API
Плагин для интеграции с внешним API:
// src/nodes/ApiRequest.js
export default {
type: 'action',
name: 'ApiRequest',
displayName: 'API Request',
description: 'Makes a request to an external API',
icon: 'icon.svg',
category: 'API',
version: 1,
properties: {
inputs: [
{
name: 'method',
type: 'options',
displayName: 'Method',
options: [
{ name: 'GET', value: 'get' },
{ name: 'POST', value: 'post' },
{ name: 'PUT', value: 'put' },
{ name: 'DELETE', value: 'delete' }
],
default: 'get',
required: true
},
{
name: 'url',
type: 'string',
displayName: 'URL',
description: 'The URL to make the request to',
required: true
},
{
name: 'headers',
type: 'json',
displayName: 'Headers',
description: 'Request headers',
default: '{}'
},
{
name: 'data',
type: 'json',
displayName: 'Data',
description: 'Request body data',
default: '{}'
}
],
outputs: [
{
name: 'response',
type: 'object',
displayName: 'Response',
description: 'The response from the API'
},
{
name: 'statusCode',
type: 'number',
displayName: 'Status Code',
description: 'The HTTP status code'
}
]
},
async execute(context) {
const { method, url, headers, data } = context.inputs;
try {
const response = await fetch(url, {
method: method.toUpperCase(),
headers: JSON.parse(headers),
body: method !== 'get' ? JSON.stringify(JSON.parse(data)) : undefined
});
const responseData = await response.json();
return {
response: responseData,
statusCode: response.status
};
} catch (error) {
throw new Error(`API Request failed: ${error.message}`);
}
}
};
Пример 2: Обработка данных
Плагин для трансформации данных:
// src/nodes/DataTransformer.js
export default {
type: 'action',
name: 'DataTransformer',
displayName: 'Data Transformer',
description: 'Transforms data using JSONPath expressions',
icon: 'icon.svg',
category: 'Data',
version: 1,
properties: {
inputs: [
{
name: 'data',
type: 'object',
displayName: 'Input Data',
description: 'The data to transform',
required: true
},
{
name: 'transformations',
type: 'array',
displayName: 'Transformations',
description: 'List of transformations to apply',
typeOptions: {
multipleValues: true
},
options: [
{
name: 'field',
displayName: 'Output Field',
type: 'string',
description: 'Name of the output field',
required: true
},
{
name: 'expression',
displayName: 'JSONPath Expression',
type: 'string',
description: 'JSONPath expression to extract data',
required: true
}
]
}
],
outputs: [
{
name: 'result',
type: 'object',
displayName: 'Result',
description: 'The transformed data'
}
]
},
async execute(context) {
const { data, transformations } = context.inputs;
try {
const result = {};
for (const transformation of transformations) {
const { field, expression } = transformation;
// Используем библиотеку JSONPath для извлечения данных
const value = JSONPath.query(data, expression);
// Если выражение возвращает массив с одним элементом, используем этот элемент
result[field] = value.length === 1 ? value[0] : value;
}
return {
result
};
} catch (error) {
throw new Error(`Data transformation failed: ${error.message}`);
}
}
};
Заключение
Система плагинов FlowCraft предоставляет мощный механизм для расширения функциональности платформы. Следуя рекомендациям и лучшим практикам, разработчики могут создавать высококачественные плагины, которые будут полезны для пользователей FlowCraft.
Ключевые преимущества системы плагинов:
- Модульность - плагины могут быть установлены, обновлены или удалены независимо друг от друга
- Безопасность - плагины выполняются в изолированной среде с ограниченным доступом к ресурсам
- Расширяемость - платформа может быть расширена для поддержки новых сервисов и функций
- Гибкость - разработчики могут создавать различные типы расширений
- Экосистема - маркетплейс плагинов обеспечивает простой способ распространения и монетизации плагинов
Используя систему плагинов, вы можете адаптировать FlowCraft под свои специфические потребности и расширить его возможности для решения уникальных задач вашего бизнеса.