Разработка пользовательской интеграции
8 800 555-89-02
Войти
Для разработчиков
CTRL+K
Standalone2502

Разработка пользовательской интеграцииBETANEW

В этой статье

Этот раздел описывает инструменты и принципы, необходимые для разработки интеграции, включая работу с подключениями, параметрами, полями ввода и системными объектами (service, bundle). В рамках каждой интеграции выполняется соединение с внешними ресурсами, такими как API, базы данных или другие сервисы, с использованием настроек, указанных в блоках интеграции. Разделение интеграции на компоненты позволяет гибко настроить и адаптировать интеграции под различные требования.

Добавление интеграции в рабочее пространство

Чтобы создать собственную интеграцию в Proceset, выполните следующие шаги:

  1. В Панели управления в пространстве нажмите + Добавить и выберите Интеграцию. Добавление интеграции
  2. В открывшемся окне укажите следующие параметры:
    • Название интеграции
    • Ключ
    • Логотип — загрузите иконку системы, с которой будет сделана интеграция, в формате .svg
    • Описание (необязательно)
    Параметры интеграции
  3. Нажмите Создание.

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

Редактор интеграции

Описание кода

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

  • schema — номер версии интеграции
  • meta — метаданные интеграции
  • blocks — основные элементы интеграции, которые выполняют определенные действия. Каждый блок выполняет свою специфическую задачу, например, отправку данных в API, получение информации или выполнение вычислений.
  • connections — параметры подключения к внешним системам и сервисам

Блоки интеграции могут содержать поля ввода, которые задают параметры для выполнения действий, а также объекты bundle и service для обработки данных и взаимодействия с API.

Пример структуры интеграции:

{
    schema:1,
    meta: {
        key: 'integration_key',
        name: 'Название интеграции',
        description: 'Описание интеграции'
    },
    blocks: [],
    connections: []
}

Шаблон интеграции подробно описан в разделе Пример создания пользовательской интеграции.

Meta

Раздел meta содержит метаданные интеграции, такие как ключ, название и описание. Эти данные позволяют идентифицировать интеграцию и описать ее.

Пример метаданных интеграции:

{
    meta: {
        key: 'integration_key',
        name: 'Пример интеграции',
        description: 'Эта интеграция предназначена для демонстрации возможностей.'
    }
}

Blocks

Компоненты интеграции blocks — это основные элементы интеграции, выполняющие определенные задачи. Например, блок может получать данные из API или отправлять их в определенное приложение. Блоки необходимы для структурирования и выполнения конкретных шагов в процессе интеграции.

Пример структуры блока с полями ввода inputFields:

{
  meta: {
      key: 'fetch_data_block',
      name: 'Блок получения данных',
      description: 'Этот блок получает данные из внешнего API'
  },
  inputFields: [
      {
          key: "api_endpoint",
          type: "text",
          label: "API Endpoint",
          required: true
      },
      {
          key: "access_token",
          type: "text",
          label: "Access Token",
          required: true
      }
  ],
  executePagination: (service, bundle,context) => {
      var state = context || {
  page:0
      }
      var response = service.request({
          url: bundle.inputData.api_endpoint,
          method: 'GET',
          headers: {
              Authorization: 'Bearer ' + bundle.inputData.access_token
          }
      });

      if (response.status !== 200) {
          service.error.stringError('Ошибка запроса: ' + response.status);
      }

      return {
          output:[response.json],
          output_variables:[
          {
              type:"String",
              name:"text"
          }
          ],
          state:state,
          hasNext:false
      };
  }
}

Connections

Компонент интеграции connections — это параметры подключения к внешним системам и сервисам, которые используются для установления связи между интеграцией и внешними API или базами данных.

Пример структуры подключения с использованием аутентификации Basic Auth:

{
  meta: {
    key: "jira_base_connection",
    name: "Подключение к Jira",
    description: "Подключение по логину/паролю к Jira"
  },
  inputFields: [
    {
      key: "connection_login",
      type: "textPlain",
      label: "Логин",
      required: true
    },
    {
      key: "connection_password",
      type: "password",
      label: "Пароль",
      required: true
    },
    {
      key: "connection_base_url",
      type: "textPlain",
      label: "URL сервера Jira",
      required: true
    },
    {
      key: "authorize_button",
      type: "button",
      label: "Авторизоваться",
      required: false,
      executeWithSaveFields: (service, bundle) => {
        return {
          url: bundle.authData.connection_base_url,
          passHash:
            "Basic " + service.base64Encode(bundle.authData.connection_login + ":" + bundle.authData.connection_password)
        };
      },
      executeWithMessage: (service, bundle) => {
        if (bundle.authData.url && bundle.authData.passHash !== "") return "Успешно авторизован!";
        service.error.stringError("Не удалось авторизоваться!");
      }
    }
  ],
  execute: (service, bundle) => {}
}

Пример структуры подключения с использованием аутентификации OAuth 2.0 представлен ниже.

{
      meta: {
        key: "connection",
        name: "Подключение OAuth 2.0",
        description: "Подключение OAuth 2.0"
      },
      inputFields: [
      {
          key: "subdomain_amo_crm",
          type: "textPlain",
          label: "Cубдомен AmoCRM",
          required: true,
        },
        {
          key: "client_id",
          type: "password",
          label: "Client ID",
          required: true,
        },
        {
          key: "client_secret",
          type: "password",
          label: "Client secret",
          required: true,
        },
        {
          key: "show_button",
          type: "button",
          label: "Получить redirect URL",
          required: false,
          executeWithSaveFields: (service, bundle) => {
            return { redirect_url: bundle.authData.BASE_URL };
          },
        },
        {
          key: "redirect_url",
          type: "textPlain",
          label: "Redirect URL",
          required: false,
        },
        {
          key: "authorize_button",
          type: "button",
          label: "Авторизоваться",
          required: false,
          executeWithRedirect: (z, bundle) => {
            return (
              "https://www.amocrm.ru/oauth" +
              "?mode=post_message" +
              "&client_id=" +
              bundle.authData.client_id +
              "&redirect_uri=" +
              bundle.authData.redirect_url
            );
          },
          executeWithMessage: (service, bundle) => {
            if (bundle.authData.accessToken && bundle.authData.accessToken !== "")
              return "Успешно авторизован!";
            service.error.stringError("Не удалось авторизоваться!");
          },
          executeWithSaveFields: (z, bundle) => {
            const CLIENT_id = bundle.authData.client_id;
            const CLIENT_secret = bundle.authData.client_secret;
            const guid = bundle.authData.redirect_url
              .match(/\/webhook\/.+/)[0]
              .replace("/webhook/", "");

            const AUTH_Code = z.hook(
              (url, headers) => {
                return url
                  .match(/code=[^&]+&?/)[0]
                  .replace("code=", "")
                  .replace("&", "");
              },
              guid,
              20
            );

            if (AUTH_Code === undefined)
              z.error.stringError("Не удалось получить Authorization Code.");

            const exchangeCode = z.request({
              url:
                "https://" + bundle.authData.subdomain_amo_crm + ".amocrm.ru/oauth2/access_token",
              method: "POST",
              headers: {
                ["Content-Type"]: "application/json",
              },
              jsonBody: {
                client_id: CLIENT_id,
                client_secret: CLIENT_secret,
                grant_type: "authorization_code",
                code: AUTH_Code,
                redirect_uri: bundle.authData.redirect_url,
              },
            });
            var accTok = exchangeCode.response.access_token;
            if (accTok === undefined)
              service.error.stringError(
                "Не удалось выполнить обмен Authorization Code на access_token.\n" +
                  JSON.stringify(exchangeCode.response)
              );
            return {
              accessToken: accTok,
              refreshToken: exchangeCode.response.refresh_token,
            };
          },
        },
        {
          key: "test_button",
          type: "button",
          label: "Проверить подключение",
          required: true,
          executeWithMessage: (z, bundle) => {
            if (!bundle.authData.accessToken && bundle.authData.accessToken !== "")
              return "Не удалось авторизоваться!";
            const req = z.request({
              url:
                "https://" +
                bundle.authData.subdomain_amo_crm +
                ".amocrm.ru/api/v4/leads/pipelines",
              method: "GET",
              headers: {
                Authorization: "Bearer " + bundle.authData.accessToken,
              },
            });
            const status = req.status;
            if (Number(status) >= 200 && Number(status) < 300)
              return "Успешное подключение! Статус: " + status;
            z.error.stringError(
              "Не удалось подключиться! Статус: " + status + "\n" + JSON.stringify(req)
            );
          },
        },
        {
          key: "un_authorize_button",
          type: "button",
          label: "Забыть учётную запись",
          required: false,
          executeWithMessage: (z, bundle) => {
            return "Я забыл твой токен.";
          },
          executeWithSaveFields: (z, bundle) => {
            return { accessToken: "", refreshToken: "" };
          },
        },
      ],
      execute: (z, bundle) => {},
    }

Поля ввода inputFields

Поля inputFields — это поля ввода, которые позволяют пользователю задавать параметры для блоков интеграции. Они необходимы для настройки параметров, которые будут использоваться в ходе выполнения блока.

Ниже приведены примеры различных полей ввода.

Тип поляНазначениеПример использования
textВвод текстаКомментарий к действию
textPlainВвод простого текста без форматированияКлюч API, идентификатор объекта
switcherПереключатель (вкл/выкл)Активация сессий
numberPlainВвод числовых значенийКоличество, идентификатор
textAreaВвод большого объема текстаСообщение
sqlAreaВвод SQL-запросаЗапрос к базе данных
jsAreaВвод JavaScript-кодаВычисления
jsonAreaВвод JSON-объектаПередача вложенных параметров в запросе
selectВыбор одного значения из спискаМетод HTTP-запроса
multiselectВыбор нескольких значений из спискаРоли пользователей, теги
keyValueВвод пар ключ-значениеHTTP-заголовки, параметры запроса

Поле text

Поле text — однострочное текстовое поле для ввода строковых значений.

{
key: "comment",
type: "text",
label: "Комментарий",
required: false
}

Поле text

Поле textPlain

Поле textPlain — аналог поля text для ввода простого текста без форматирования.

{
key: "api_key",
type: "textPlain",
label: "Ключ API",
required: true
}

Поле textPlain

Поле switcher

Поле switcher — переключатель, может быть включен или выключен.

{
key: "session_mode",
type: "switcher",
label: "Активировать режим сессий",
required: false
}

Поле switcher

Поле numberPlain

Поле numberPlain — предназначено для ввода числовых значений.

{
key: "value",
type: "numberPlain",
label: "Значение",
required: true
}

Поле numberPlain

Поле textArea

Поле textArea — предназначено для ввода большого количества текста.

{
key: "message",
type: "textArea",
label: "Сообщение",
required: true
}

Поле textArea

Поле sqlArea

Поле sqlArea — предназначено для ввода SQL-запросов.

{
key: "sql_query",
type: "sqlArea",
label: "SQL-запрос",
required: true
}

Поле sqlArea

Поле jsArea

Поле jsArea — предназначено для ввода JS-кода.

{
key: 'javascript',
type: 'jsArea',
label: 'JavaScript',
required: true
}

Поле jsArea

Поле jsonArea

Поле jsonArea — предназначено для ввода JSON-объектов.

{
key: 'returned_data',
type: 'jsonArea',
label: 'Возвращаемые данные',
required: false
}

Поле jsonArea

Поля select и multiSelect

Поля select и multiSelect — предназначены для выбора из предопределенных значений в виде раскрывающегося списка:

  • select — поле для выбора одного значения из списка
  • multiSelect — поле для выбора нескольких значений из списка

Оба типа полей могут принимать значения для choices в трех форматах:

  • Объект с парой «ключ-значение»
    {
        key: "http_method",
        type: "select",
        label: "Метод",
        required: true,
        choices: {
            GET: "GET",
            POST: "POST"
        }
    },
    
    Объект с парой ключ-значение
  • Массив объектов с полями label и value
    {
    key: "user_roles",
    type: "multiSelect",
    label: "Роли пользователей",
    required: true,
    choices: [
        { label: "Администратор", value: "admin" },
        { label: "Пользователь", value: "user" },
        { label: "Гость", value: "guest" }
    ]
    },
    
    Массив объектов
  • Массив строк
    {
    key: "data_type",
    type: "multiSelect",
    label: "Тип данных",
    required: false,
    choices: ["Строка", "Число", "Дата"]
    }
    
    Массив строк

Поле keyValue

Поле keyValue — предназначено для ввода пар «ключ-значение» и может быть использовано при создании параметров запроса. Этот тип поля можно настроить двумя способами:

  • Указание типов полей для ключа и значения
    {
    key: "headers",
    type: "keyValue",
    label: "Заголовки",
    label2: "Ключ и значение",
    subFieldKeyType: "textPlain",
    subFieldValueType: "textPlain",
    required: true
    }
    
    Указание типов полей
  • Указание списка ключей, которые будут иметь текстовые значения
    {
        key: "keyValue_2",
        type: "keyValue",
        label: "Дополнительные параметры",
        label2: "Имя параметра и значение",
        subFieldKeyType: "textPlain",
        subFieldValueType: "textPlain",
        keys: ["Параметр 1", "Параметр 2", "Параметр 3"],
        required: false
    }
    
    Указание списка ключей

Доступные методы inputFields

Метод executeWithRedirect() используется для взаимодействия с Redirect, например, для открытия окна авторизации. Этот метод позволяет перенаправить пользователя на другую страницу или окно, что может быть полезно для аутентификации или получения разрешений.

Метод executeWithSaveFields() позволяет взаимодействовать с authData. Этот метод используется для сохранения данных аутентификации или других полей, которые могут понадобиться в дальнейшем. Он обеспечивает возможность работы с данными, полученными в процессе авторизации.

Метод executeWithMessage() выводит сообщение в блок. Этот метод позволяет отображать сообщения пользователю, например, об успешном завершении операции или об ошибках. Это улучшает взаимодействие с пользователем, предоставляя ему обратную связь о выполненных действиях.

Заметка

Методы выполняются в указанном порядке.

Использование объекта service

Объект service предоставляет набор методов для взаимодействия c API, вывода ошибок, а также работы с хуками и итераторами.

Доступные методы объекта service

Метод base64Encode — производит base64-кодирование входной строки. На вход принимает строку, которую необходимо закодировать.

service.base64Encode()

Метод base64Decode — производит base64-декодирование входной строки. На вход принимает строку, которую необходимо декодировать в формате base64.

service.base64Decode()

Метод request — выполняет HTTP-запрос. Ожидает на вход объект с конфигурацией запроса. Параметры этого объекта указаны в таблице ниже.

ПараметрОбязательныйОписание
urlДаСсылка на ресурс, на который нужно выполнить запрос
methodДаМетод с которым будет выполнен запрос.

Доступные методы:
- GET
- POST
- PATCH
- PUT
- DELETE
headersНетСписок заголовков для HTTP/1.1
jsonBody

multipartBody
Зависит от методаОбязательный для POST/PATCH/PUT
service.request({
            url: request_url,
            method: "POST",
            headers: {
              Authorization: `Bearer ${bundle.authData.access_token}`,
              "Content-Type": "application/json"
            },
            jsonBody: {
              note: bundle.inputData.note
            }
          })

Метод hasNext — возвращает true или false в зависимости от того, есть ли еще элементы во входном итераторе блока.

service.hasNext()

Метод next — если есть следующий элемент в итераторе, вызов этого метода обновит все значения полей маппинга на значения соответствующие следующему элементу итератора. Вызов этого метода автоматически сокращает количество элементов в выходном итераторе.

service.next()

Метод index — возвращает текущий номер итерации.

service.index()

Метод hook — обрабатывает ответ от ресурса, к которому происходит подключение.

{
  key: "authorize",
  type: "button",
  label: "Авторизоваться",
  required: false,
  executeWithRedirect: (service, bundle) => {
    return `https://gitlab.com/oauth/authorize?client_id=${bundle.authData.client_id}&redirect_uri=${bundle.authData.redirect_url}&response_type=code`;
  },
  executeWithSaveFields: (service, bundle) => {
    const match = bundle.authData.redirect_url.match(/\/webhook\/.+/);
    let guid = "";
    if (match) {
      guid = match[0].replace("/webhook/", "");
      const AUTH_Code = service.hook(
        (url, headers) => {
          return url
            .match(/code=[^&]+&?/)[0]
            .replace("code=", "")
            .replace("&", "");
        },
        guid,
        20
      );
      if (AUTH_Code === undefined) service.error.stringError("Не удалось получить Authorization Code.");
      const exchangeCode = service.request({
        url: `https://gitlab.com/oauth/token`,
        method: "POST",
        headers: {
          ["Content-Type"]: "application/json"
        },
        jsonBody: {
          client_id: bundle.authData.client_id,
          client_secret: bundle.authData.client_secret,
          grant_type: "authorization_code",
          code: AUTH_Code,
          redirect_uri: bundle.authData.redirect_url
        }
      });
      var accTok = exchangeCode.response.access_token;
      if (accTok === undefined)
        service.error.stringError(
          "Не удалось выполнить обмен Authorization Code на access_token.\n" + JSON.stringify(exchangeCode.response)
        );
      return {
        access_token: accTok,
        refresh_token: exchangeCode.response.refresh_token
      };
    } else {
      service.error.stringError("Не удалось авторизоваться!");
    }
  },
  executeWithMessage: (service, bundle) => {
    if (bundle.authData.access_token && bundle.authData.access_token !== "") return "Успешно авторизован!";
    service.error.stringError("Не удалось авторизоваться!");
  }
}

Метод error.stringError — служит для вывода текста ошибки. В качестве параметра принимает строку, которая отобразится в блоке в случае возникновения ошибки.

service.error.stringError()

Использование объекта bundle

Пользовательские данные из полей ввода доступны в коде интеграции через объект bundle.

Объект bundle содержит два основных компонента, которые предоставляют доступ к введенной информации:

С помощью этих данных блок настраивает запросы и выполняет необходимые действия.

bundle.authData

Компонент bundle.authData хранит учетные данные, которые настраиваются в разделе подключений (connections).

Все поля ввода из подключения автоматически становятся доступны через bundle.authData. Эти данные необходимы для взаимодействия с API и внешними сервисами.

Пример:

При настройке подключения с полями api_key и api_url эти значения станут доступны через bundle.authData для использования в запросах вашего блока.

execute: (service, bundle) => {
    var response = service.request({
        url: bundle.authData.api_url + '/data',
        method: 'GET',
        headers: {
            Authorization: 'Bearer ' + bundle.authData.api_key
        }
    });

    if (response.status !== 200) {
        service.error.stringError('Ошибка запроса: ' + response.status);
    }

    return {
        output: () => ({
            data: response.json
        })
    };
}

bundle.inputData

Компонент bundle.inputData хранит данные, которые пользователь вводит в поля ввода блока (inputFields).

Эти данные позволяют настраивать параметры запросов и выполнять другие действия внутри блока.

Все поля ввода, настроенные в блоке, автоматически становятся доступными через bundle.inputData.

Пример:

Если в блоке есть поля ввода api_endpoint и access_token, их значения будут доступны через bundle.inputData для использования при настройке запроса.

execute: (service, bundle) => {
    var response = service.request({
        url: bundle.inputData.api_endpoint,
        method: 'GET',
        headers: {
            Authorization: 'Bearer ' + bundle.inputData.access_token
        }
    });

    if (response.status !== 200) {
        service.error.stringError('Ошибка запроса: ' + response.status);
    }

    return {
        output: () => ({
            data: response.json
        })
    };
}

Передача файлов

Интеграции поддерживают передачу файлов во внешние системы с использованием multipartBody. Это используется, например, для прикрепления файлов к задачам в сторонних системах.

Заметка

Для загрузки файлов в систему используйте блоки автоматизации для работы с файлами.

multipartBody — это массив объектов, каждый из которых представляет собой часть данных, отправляемых в запросе.

В представленном ниже примере multipartBody содержит один объект, который описывает файл, предназначенный для прикрепления к задаче.

Пример:

var response = service.request({
  url: `${bundle.authData.url}/rest/api/2/issue/${bundle.inputData.issueIdOrKey}/attachments`,
  method: "POST",
  headers: {
    Authorization: bundle.authData.passHash,
    "X-Atlassian-Token": "nocheck"
  },
  multipartBody: [
    {
      key: "file",
      fileValue: bundle.inputData.attachment,
      fileName: bundle.inputData.attachment_name,
      contentType: "application/octet-stream"
    }
  ]
});

Структура объекта в multipartBody:

  • key: "file" — имя поля, под которым файл будет доступен на сервере. В приведенном примере поле называется "file", что соответствует ожиданиям API для загрузки вложений
  • fileValue: bundle.inputData.attachment — значение, представляющее собой сам файл, который будет загружен. Оно должно содержать данные файла, которые вы хотите прикрепить
  • fileName: bundle.inputData.attachment_name — имя файла, которое будет использоваться на сервере. Оно может включать расширение файла, чтобы сервер мог правильно определить тип содержимого
  • contentType: "application/octet-stream" — это MIME-тип файла. В приведенном примере используется общий тип для двоичных данных, что позволяет серверу обрабатывать файл как поток байтов. В зависимости от типа файла, можно указать более специфичный MIME-тип (например, image/jpeg для изображений или application/pdf для PDF-документов)

Была ли статья полезна?

Да
Нет
8 (800) 555-89-028 (495) 150-31-45team@infomaximum.com
Для бизнеса
© 20102025. ООО «Инфомаксимум»
Мы используем файлы cookies, чтобы сайт был лучше для вас.