Синхронизация данных
Наконец-то мы подошли к заключительной части нашей серии статей об использовании 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 };
Здесь мы создали модал с единственным блоком: элементом ввода. Этот элемент будет предварительно заполнен описанием контакта. Мы можем отредактировать этот блок и изменить описание на то, что нам нужно.
В этом фрагменте кода есть два важных замечания:
- Обратите внимание, что мы прикрепляем
actionId
к элементу ввода. Это аналогично ID, который мы ранее прикрепили к кнопке Edit, только на этот раз он генерируется динамически на основе ID редактируемой записи. - Вы также заметите, что у нас есть еще один 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, чтобы узнать больше о том, что вы можете создать!