Создание приложения Slack с нативной интеграцией SFDC

Синхронизация данных

Наконец-то мы подошли к заключительной части нашей серии статей об использовании Slack Starter Kit от Salesforce для быстрого создания развертываемого приложения Slack, взаимодействующего с данными Salesforce. Slack Starter Kit значительно упрощает аутентификацию в Salesforce, организацию кода в многократно используемые фрагменты и развертывание проекта на Heroku для живого доступа. Мы во многом основывали эту серию статей на этих видеоуроках, демонстрирующих, как создавать приложения Slack Apps.

В первом посте мы познакомились с набором Slack Starter Kit и настроили среду разработки. Во втором посте наше приложение Slack выполнило запрос на получение данных из Salesforce org, а затем представило результат с помощью компонентов пользовательского интерфейса из Block Kit. Теперь мы собираемся расширить этот шаблон, чтобы показать, как можно редактировать данные Salesforce полностью в Slack. Давайте приступим!

Фото Edgar Moran on Unsplash

Создание ярлыка

Помимо пользовательского интерфейса Block Kit, Slack поддерживает еще две системы интерактивности: Slash-команды и ярлыки. Slash-команды вводятся пользователем в текстовый ввод Slack (доступный в каждом канале), в то время как ярлыки имеют графическую природу. Поскольку их легче визуализировать, мы продемонстрируем ярлыки, создав ярлык, который будет получать список контактов и давать нам возможность редактировать их.

Добавление ярлыка (или Slash-команды, если на то пошло) требует, чтобы вы сообщили Slack имя команды. Перейдите на страницу Обзор вашего приложения в Slack, а затем нажмите Интерактивность и ярлыки:

Нажмите Создать новый ярлык и выберите Глобальный в качестве типа ярлыка, затем нажмите Далее. На следующей странице введите следующие значения:

  • Имя: Редактировать контакты
  • Краткое описание: Редактирование контактов в SFDC
  • Идентификатор обратного вызова: edit-contact-shortcut.

Нажмите кнопку Создать, затем нажмите кнопку Сохранить изменения. Переключитесь на свое рабочее пространство Slack и нажмите на знак плюс в текстовой области. Вы сможете просмотреть все ярлыки вашего рабочего пространства. Здесь вы также увидите новый ярлык, который вы только что создали:

Этот ярлык пока ничего не делает, но этот процесс необходим для того, чтобы Slack узнал о вашем ярлыке. Далее добавим код для обработки события, которое срабатывает всякий раз, когда кто-то нажимает на этот ярлык.

Подключение ярлыка

В редакторе кода перейдите к apps/slack-salesforce-starter-app/listeners/shortcuts/index.js. Это место, где мы связываем события ярлыка с выполняемым кодом. Уже есть один ярлык, предоставленный нам стартовым набором: whoami. Данная строка подсказывает нам, что нужно сделать: Мы вызываем функцию shortcut и передаем ей строку и имя функции. В данном случае наша строка — это идентификатор обратного вызова, который мы определили ранее, а имя функции — это код, который нам еще предстоит написать. Измените содержимое файла так, чтобы оно выглядело следующим образом:

const { whoamiCallback } = require('./whoami');
const { editContactCallback } = require('./edit-contact');

module.exports.register = (app) => {
   app.shortcut('who_am_i', whoamiCallback);
   app.shortcut('edit-contact-shortcut', editContactCallback);
};
Вход в полноэкранный режим Выйти из полноэкранного режима

Здесь мы закладываем основу, говоря: «Slack App, если вы получили ярлык с идентификатором обратного вызова edit-contact-shortcut, запустите editContactCallback«.

В этой же папке создайте файл edit-contact.js и вставьте в него эти строки:

'use strict';

const {
  editContactResponse,
  authorize_sf_prompt
} = require('../../user-interface/modals');

const editContactCallback = async ({ shortcut, ack, client, context }) => {
  try {
    await ack();
    if (context.hasAuthorized) {
      const conn = context.sfconnection;
      await client.views.open({
        trigger_id: shortcut.trigger_id,
        view: await editContactResponse(conn)
      });
    } else {
      // Get BotInfo
      const botInfo = await client.bots.info({ bot: context.botId });
      // Open a Modal with message to navigate to App Home for authorization
      await client.views.open({
        trigger_id: shortcut.trigger_id,
        view: authorize_sf_prompt(context.teamId, botInfo.bot.app_id)
      });
    }
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(error);
  }
};

module.exports = { editContactCallback };
Войти в полноэкранный режим Выйти из полноэкранного режима

Это может показаться пугающим, но большая часть этих строк просто относится к шаблону аутентификации, гарантируя, что у пользователя есть активное подключение к SFDC. В первом логическом пути (если context.hasAuthorized равно true) мы выполняем функцию editContactResponse, которая принимает наше открытое соединение Salesforce conn. В отрицательном случае мы просим пользователя перейти на вкладку Home для повторной аутентификации, как мы делали в первой части этого руководства.

Перейдите в папку apps/slack-salesforce-starter-app/user-interface/modals и создайте файл edit-contact-response.js. Здесь мы откроем модальное окно с контактной информацией, аналогичное строкам, которые мы видели на вкладке «Главная» во второй части этого руководства:

'use strict';
const { Elements, Modal, Blocks } = require('slack-block-builder');

const editContactResponse = async (conn) => {
  const result = await conn.query(
    `Select Id, Name, Description FROM Contact`
  );
  let records = result.records;

  let blockCollection = records.map((record) => {
    return Blocks.Section({
      text: `*${record.Name}*n${record.Description}`
    }).accessory(
      Elements.Button()
        .text(`Edit`)
        .actionId(`edit_contact`)
        .value(record.Id)
    );
  });

  return Modal({ title: 'Salesforce Slack App', close: 'Close' })
    .blocks(blockCollection)
    .buildToJSON();
};

module.exports = { editContactResponse };
Вход в полноэкранный режим Выход из полноэкранного режима

Основное различие между кодом во второй части и этим блоком заключается в том, что мы используем массив под названием blockCollection, который позволяет нам построить массив блоков (в данном случае блоков Section). blocks знает, как взять этот массив и преобразовать его в формат, который понимает Slack, что делает создание данных через зацикленный массив очень простым, как мы сделали здесь. Во второй части нашей серии мы создали гигантскую строку данных. Однако, используя BlockCollection, мы можем присоединить другие элементы Slack, например, кнопки, что мы и сделали здесь.

Наконец, в apps/slack-salesforce-starter-app/user-interface/modals/index.js нам нужно экспортировать эту функцию, чтобы она могла быть импортирована нашей функцией edit-contact.js:

'use strict';
const { whoamiresponse } = require('./whoami-response');
const { editContactResponse } = require('./edit-contact-response');
const { authorize_sf_prompt } = require('./authorize-sf-prompt');

module.exports = { whoamiresponse, editContactResponse, authorize_sf_prompt };
Вход в полноэкранный режим Выход из полноэкранного режима

После того как вы развернули новый код на Heroku с помощью git push, переключитесь в рабочее пространство Slack и попробуйте выполнить ярлык; вас встретит диалоговое окно, похожее на это:

Обновление данных Salesforce

Мы можем получать и отображать данные Salesforce. Теперь пришло время подключить кнопку Edit, чтобы изменить данные Salesforce!

Многие интерактивные компоненты Slack имеют action_id, который, как и callback_id, служит для идентификации элемента, над которым действовал пользователь. Как и все остальное в Starter Kit, существует специальный каталог, в котором вы можете определить слушателей для этих идентификаторов действий: apps/slack-salesforce-starter-app/listeners/actions. В файле index.js добавим новую строку, которая свяжет идентификатор действия с нашей еще не написанной функциональностью:

'use strict';
const { appHomeAuthorizeButtonCallback } = require('./app-home-authorize-btn');
const { editContactButtonCallback } = require('./edit-contact-btn');

module.exports.register = (app) => {
  app.action('authorize-with-salesforce', appHomeAuthorizeButtonCallback);
  app.action('edit_contact', editContactButtonCallback);
};
Войти в полноэкранный режим Выйти из полноэкранного режима

В этой же папке создайте новый файл edit-contact-btn.js и вставьте в него эти строки:

'use strict';
const {
  editIndividualContact,
  authorize_sf_prompt
} = require('../../user-interface/modals');

const editContactButtonCallback = async ({ body, ack, client, context }) => {
  const contactId = body.actions[0].value;
  try {
    await ack();
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(error);
  }

  if (context.hasAuthorized) {
    const conn = context.sfconnection;
    const result = await conn.query(
      `SELECT Id, Name, Description FROM Contact WHERE Id='${contactId}'`
    );
    let record = result.records[0];
    await client.views.push({
      trigger_id: body.trigger_id,
      view: editIndividualContact(record)
    });
  } else {
    // Get BotInfo
    const botInfo = await client.bots.info({ bot: context.botId });
    // Open a Modal with message to navigate to App Home for authorization
    await client.views.push({
      trigger_id: body.trigger_id,
      view: authorize_sf_prompt(context.teamId, botInfo.bot.app_id)
    });
  }
};

module.exports = { editContactButtonCallback };
Войти в полноэкранный режим Выйти из полноэкранного режима

Начало и конец этого файла должны выглядеть знакомо: Мы отправляем ответ ack обратно в Slack, чтобы дать ему знать, что наше приложение получило полезную нагрузку события (в данном случае от нажатия на кнопку Edit). Мы также проверяем, прошли ли мы аутентификацию. Здесь мы выполняем единственный поиск в БД, используя ID контакта, который мы прикрепили в качестве значения к кнопке Edit при создании нашего пользовательского интерфейса.

Этот фрагмент кода создает еще один модальный дизайн, который нам нужно определить. Вернувшись в apps/slack-salesforce-starter-app/user-interface/modals, создайте файл под названием edit-individual-contact.js и вставьте в него эти строки:

'use strict';
const { Elements, Modal, Blocks } = require('slack-block-builder');

const editIndividualContact = (record) => {
  return Modal({ title: 'Edit Contact', close: 'Close' })
    .blocks(
      Blocks.Input({ blockId: 'description-block', label: record.Name }).element(
        Elements.TextInput({
          placeholder: record.Description,
          actionId: record.Id
        })
     )
   )
   .submit('Save')
   .callbackId('edit-individual')
   .buildToJSON();
};

module.exports = { editIndividualContact };
Войти в полноэкранный режим Выйти из полноэкранного режима

Здесь мы создали модал с единственным блоком: элементом ввода. Этот элемент будет предварительно заполнен описанием контакта. Мы можем отредактировать этот блок и изменить описание на то, что нам нужно.

В этом фрагменте кода есть два важных замечания:

  1. Обратите внимание, что мы прикрепляем actionId к элементу ввода. Это аналогично ID, который мы ранее прикрепили к кнопке Edit, только на этот раз он генерируется динамически на основе ID редактируемой записи.
  2. Вы также заметите, что у нас есть еще один ID, callbackID, который прикреплен к самому модалу. Помните о существовании этих идентификаторов: мы рассмотрим их в ближайшее время. А пока откройте файл index.js в этой же директории и require/export эту новую функцию создания модала:
const { editIndividualContact } = require('./edit-individual-contact');
// ...
module.exports = {
  whoamiresponse,
  editContactResponse,
  editIndividualContact,
  authorize_sf_prompt
};
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь, когда вы нажмете кнопку Edit, вам будет предложено изменить описание:

Теперь нам нужно отправить этот обновленный текст в Salesforce. Нажмите на кнопку Сохранить и… ничего не произойдет. Почему? У Slack есть другой набор событий для подобных взаимодействий, называемых представлениями. Starter Kit обеспечивает хорошую отправную точку при создании приложения, но он не обрабатывает все случаи использования Slack, включая этот. Но это не проблема — мы добавим функциональность сами!

В папке apps/slack-salesforce-starter-app/user-interface создайте новую папку views. Как и раньше, наша кнопка Save здесь имеет идентификатор действия для идентификации: edit-individual-contact. Мы вернемся в apps/slack-salesforce-starter-app/listeners/actions/index.js, чтобы настроить это действие на функцию:

const { editIndividualButtonCallback } = require('./edit-individual-btn);

// ... 

app.action('edit-individual-contact', editIndividualButtonCallback);
Вход в полноэкранный режим Выйти из полноэкранного режима

Создайте новый файл edit-individual-contact.js и вставьте в него эти строки:

'use strict';

const { submitEditCallback } = require('./submit-edit');

module.exports.register = (app) => {
  app.view('edit-individual', submitEditCallback);
};
Войти в полноэкранный режим Выйти из полноэкранного режима

Этот формат идентичен другим слушателям, предоставляемым Slack Starter Kit. Единственное отличие заключается в том, что мы вызываем метод view. Нам также необходимо зарегистрировать этот слушатель наряду с другими. Откройте apps/slack-salesforce-starter-app/listeners/index.js и require/register новый слушатель view:

const viewListener = require('./views');

module.exports.registerListeners = (app) => {
  // ...
  viewListener.register(app);
};
Вход в полноэкранный режим Выход из полноэкранного режима

Далее, в apps/slack-salesforce-starter-app/listeners/views, создайте еще один файл под названием submit-edit.js и вставьте в него эти строки:

'use strict';
const { editContactResponse, authorize_sf_prompt } = require('../../user-interface/modals');

const submitEditCallback = async ({ view, ack, client, context }) => {
  try {
    await ack();
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(error);
  }

  if (context.hasAuthorized) {
    const contactId = view.blocks[0].element.action_id;
    const newDescription =
      view.state.values['description-block'][contactId].value;
    const conn = context.sfconnection;
    await conn.sobject('Contact').update({
      Id: contactId,
      Description: newDescription
    });

    await client.views.open({
      trigger_id: view.trigger_id,
      view: await editContactResponse(conn)
    });
  } else {
    // Get BotInfo
    const botInfo = await client.bots.info({ bot: context.botId });
    // Open a Modal with message to navigate to App Home for authorization
    await client.views.push({
      trigger_id: view.trigger_id,
       view: authorize_sf_prompt(context.teamId, botInfo.bot.app_id)
    });
  }
};

module.exports = { submitEditCallback };
Войти в полноэкранный режим Выйти из полноэкранного режима

Давайте обсудим те идентификаторы, которые мы задали ранее. Когда Slack отправляет полезную нагрузку событий в наше приложение, он автоматически генерирует ID для каждого элемента ввода по умолчанию. Это происходит потому, что Slack не знает, что лежит в основе данных. Это ваша ответственность — дать элементам имена с помощью идентификаторов действий. Slack использует эти идентификаторы в качестве ключей для заполнения полезной нагрузки. Когда вы получите полезную нагрузку от Slack, вы сможете использовать предоставленные вами ключи для анализа данных, введенных пользователем.

Теперь, если вы пройдете через поток для редактирования описания контакта, вы заметите, что модальное окно будет корректно сохранено. Чтобы проверить, что данные на стороне Salesforce были обновлены, запустите sfdx force:org:open в терминале и перейдите на вкладку Контакты.

Заключение

Slack Starter Kit позволил с абсолютной легкостью создать приложение Slack, которое слушает события пользователя. Но помимо этого, он также делает взаимодействие с Salesforce и Heroku абсолютным удовольствием. Мы рассмотрели практически все возможности Starter Kit. Если вы хотите узнать больше о том, как Slack и Salesforce могут работать вместе, ознакомьтесь с нашей статьей в блоге о совместной работе. Кроме того, бэкенд-фреймворк, который взаимодействует с Salesforce, — это замечательный проект JSforce. Обязательно ознакомьтесь с его документацией и Salesforce API Reference, чтобы узнать больше о том, что вы можете создать!

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *