Skip to content

Система плагинов FlowCraft

FlowCraft предоставляет мощную и гибкую систему плагинов, которая позволяет расширять функциональность платформы без изменения основного кода. В этом документе описаны архитектура системы плагинов, процесс создания и публикации плагинов, а также лучшие практики разработки.

Обзор системы плагинов

Система плагинов FlowCraft построена на принципах модульности, изоляции и безопасности. Она позволяет разработчикам создавать различные типы расширений:

  1. Узлы (Nodes) - новые типы узлов для использования в рабочих процессах
  2. Триггеры (Triggers) - новые способы запуска рабочих процессов
  3. Коннекторы (Connectors) - интеграции с внешними сервисами и API
  4. Функции-помощники (Helpers) - утилиты для использования в рабочих процессах
  5. Темы (Themes) - кастомизация пользовательского интерфейса
  6. Расширения ядра (Core Extensions) - расширение базовой функциональности платформы

Архитектура системы плагинов

Компоненты системы плагинов

Система плагинов FlowCraft состоит из следующих основных компонентов:

  1. Менеджер плагинов (Plugin Manager)

    • Загрузка и инициализация плагинов
    • Управление жизненным циклом плагинов
    • Разрешение зависимостей между плагинами
    • Версионирование плагинов
  2. Реестр плагинов (Plugin Registry)

    • Хранение метаданных плагинов
    • Индексирование доступных плагинов
    • Поиск и фильтрация плагинов
  3. Песочница (Sandbox)

    • Изоляция выполнения кода плагинов
    • Ограничение доступа к ресурсам
    • Мониторинг производительности
    • Защита от вредоносного кода
  4. API плагинов (Plugin API)

    • Интерфейсы для взаимодействия с платформой
    • Доступ к контексту выполнения
    • Утилиты для разработки плагинов
  5. Маркетплейс плагинов (Plugin Marketplace)

    • Публикация и распространение плагинов
    • Рейтинги и отзывы
    • Управление версиями
    • Монетизация плагинов

Жизненный цикл плагина

Жизненный цикл плагина в FlowCraft включает следующие этапы:

  1. Установка

    • Загрузка пакета плагина
    • Проверка совместимости с текущей версией платформы
    • Разрешение зависимостей
    • Распаковка и установка файлов
  2. Инициализация

    • Загрузка кода плагина
    • Регистрация компонентов плагина
    • Подключение к системным событиям
    • Инициализация ресурсов
  3. Выполнение

    • Использование компонентов плагина в рабочих процессах
    • Взаимодействие с другими плагинами
    • Доступ к API платформы
  4. Деактивация

    • Временное отключение плагина
    • Сохранение состояния
    • Освобождение ресурсов
  5. Обновление

    • Проверка наличия обновлений
    • Загрузка новой версии
    • Миграция данных и настроек
    • Перезапуск плагина
  6. Удаление

    • Деактивация плагина
    • Удаление файлов
    • Очистка данных и настроек

Создание плагинов

Структура плагина

Плагин 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 содержит метаданные плагина и его зависимости:

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 является точкой входа плагина и экспортирует все компоненты плагина:

javascript
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) - это основной компонент плагина, который выполняет определенную операцию в рабочем процессе. Пример создания узла:

javascript
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) - это компонент, который запускает выполнение рабочего процесса при наступлении определенного события. Пример создания триггера:

javascript
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) определяет, какие данные необходимы для аутентификации в внешнем сервисе. Пример определения типа учетных данных:

javascript
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
      };
    }
  }
};

Тестирование плагинов

Модульное тестирование

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

javascript
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 предоставляет специальный тестовый фреймворк для этой цели:

javascript
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({
      // Ожидаемые свойства результата
    }));
  });
});

Публикация плагинов

Подготовка плагина к публикации

Перед публикацией плагина необходимо выполнить следующие шаги:

  1. Проверка кода

    • Убедитесь, что код соответствует стандартам качества
    • Выполните статический анализ кода
    • Исправьте все предупреждения и ошибки
  2. Тестирование

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

    • Создайте подробную документацию по использованию плагина
    • Добавьте примеры использования
    • Опишите все параметры и возвращаемые значения
  4. Сборка

    • Соберите плагин для production
    • Минимизируйте размер пакета
    • Убедитесь, что все необходимые файлы включены в пакет

Публикация в маркетплейсе

Для публикации плагина в маркетплейсе FlowCraft выполните следующие шаги:

  1. Создание аккаунта разработчика

    • Зарегистрируйтесь в маркетплейсе FlowCraft
    • Подтвердите свою личность и контактные данные
    • Создайте профиль разработчика
  2. Загрузка плагина

    • Войдите в панель разработчика
    • Создайте новый плагин
    • Загрузите пакет плагина
    • Заполните информацию о плагине (название, описание, категория и т.д.)
  3. Проверка плагина

    • Плагин будет проверен командой FlowCraft на соответствие требованиям
    • Будет выполнена проверка безопасности и качества кода
    • При необходимости вам будет предложено внести изменения
  4. Публикация

    • После успешной проверки плагин будет опубликован в маркетплейсе
    • Вы получите уведомление о публикации
    • Плагин станет доступен для установки пользователями

Обновление плагина

Для обновления плагина выполните следующие шаги:

  1. Подготовка новой версии

    • Обновите версию в файле package.json
    • Внесите необходимые изменения в код
    • Обновите документацию
    • Проведите тестирование
  2. Загрузка обновления

    • Войдите в панель разработчика
    • Выберите плагин для обновления
    • Загрузите новую версию пакета
    • Опишите изменения в новой версии
  3. Проверка и публикация

    • Новая версия пройдет проверку
    • После успешной проверки обновление будет опубликовано
    • Пользователи получат уведомление о доступном обновлении

Лучшие практики разработки плагинов

Производительность

  • Оптимизация кода

    • Используйте эффективные алгоритмы и структуры данных
    • Минимизируйте использование ресурсов
    • Избегайте блокирующих операций
  • Асинхронная обработка

    • Используйте асинхронные операции для длительных задач
    • Избегайте блокировки основного потока
    • Правильно обрабатывайте Promise и async/await
  • Кэширование

    • Кэшируйте результаты дорогостоящих операций
    • Используйте механизмы кэширования платформы
    • Правильно инвалидируйте кэш при изменении данных

Безопасность

  • Валидация входных данных

    • Проверяйте все входные данные
    • Используйте строгую типизацию
    • Защищайтесь от инъекций и других атак
  • Безопасное хранение данных

    • Не храните чувствительные данные в открытом виде
    • Используйте механизмы шифрования платформы
    • Минимизируйте доступ к чувствительным данным
  • Ограничение доступа

    • Запрашивайте только необходимые разрешения
    • Следуйте принципу наименьших привилегий
    • Используйте механизмы авторизации платформы

Надежность

  • Обработка ошибок

    • Корректно обрабатывайте все возможные ошибки
    • Предоставляйте информативные сообщения об ошибках
    • Реализуйте механизмы восстановления после сбоев
  • Повторные попытки

    • Реализуйте механизм повторных попыток для нестабильных операций
    • Используйте экспоненциальную задержку между попытками
    • Ограничивайте количество повторных попыток
  • Тестирование

    • Покрывайте код тестами
    • Тестируйте граничные случаи и обработку ошибок
    • Используйте автоматизированное тестирование

Удобство использования

  • Понятный интерфейс

    • Используйте понятные названия и описания
    • Группируйте связанные параметры
    • Предоставляйте подсказки и примеры
  • Разумные значения по умолчанию

    • Устанавливайте разумные значения по умолчанию
    • Минимизируйте количество обязательных параметров
    • Предоставляйте возможность настройки для продвинутых пользователей
  • Информативная обратная связь

    • Предоставляйте информативные сообщения о ходе выполнения
    • Отображайте прогресс для длительных операций
    • Четко сообщайте о результатах операций

Примеры плагинов

Пример 1: Интеграция с API

Плагин для интеграции с внешним API:

javascript
// 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: Обработка данных

Плагин для трансформации данных:

javascript
// 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 под свои специфические потребности и расширить его возможности для решения уникальных задач вашего бизнеса.

Выпущено под лицензией MIT.