Урок 18: Взаимодействие с внешними сервисами через вебхуки
Вебхук — это функция обратного вызова на основе HTTP, которую внешний сервис вызывает для уведомления о каком-либо событии, передавая вместе с ним полезную нагрузку (payload) с данными. Вебхуки позволяют различным веб-приложениям и сервисам взаимодействовать друг с другом.
Примеры сервисов, использующих вебхуки:
- GitHub — при отправке коммита в репозиторий
- Dropbox — при обновлении документа
- WooCommerce — при добавлении заказа
- Microsoft Teams — для получения форматированных текстовых сообщений и публикации в публичных каналах
- ConvertKit — при активации подписчика
С помощью Gato GraphQL можно создавать Persisted Queries, работающие как вебхуки:
- Поскольку Persisted Query доступна по собственному URL, она может использоваться в качестве цели для вебхука
- Каждая Persisted Query может обрабатывать исключительно определённый вебхук
В этом уроке туториала мы создадим Persisted Query для взаимодействия с ConvertKit.
Изучение документации вебхука
Первый шаг — прочитать документацию сайта и понять, какие данные передаются в полезной нагрузке.
Анализируя вебхуки в ConvertKit, события, связанные с подписчиками, отправляют запрос POST на наш URL с JSON-полезной нагрузкой следующего вида:
{
"subscriber": {
"id": 1,
"first_name": "John",
"email_address": "John@example.com",
"state": "active",
"created_at": "2018-02-15T19:40:24.913Z",
"fields": {
"My Custom Field": "Value"
}
}
}Извлечение данных из полезной нагрузки
Поскольку запрос отправляется через POST, необходимо извлечь данные из тела HTTP-запроса (это поддерживается расширением HTTP Request via Schema).
Если HTTP-запрос выполняется через GET, Persisted Query может напрямую получить элементы данных из параметров URL.
Этот GraphQL-запрос извлекает тело запроса и преобразует его в JSON-объект. Затем из JSON-объекта извлекаются элементы "subscriber.first_name" и "subscriber.email_address", которые экспортируются как динамические переменные:
query ExtractPayloadData {
body: _httpRequestBody
bodyJSONObject: _strDecodeJSONObject(string: $__body)
subscriberFirstName: _objectProperty(
object: $__bodyJSONObject,
by: { path: "subscriber.first_name" }
)
@export(as: "subscriberFirstName")
subscriberEmail: _objectProperty(
object: $__bodyJSONObject,
by: { path: "subscriber.email_address" }
)
@export(as: "subscriberEmail")
}Расширение HTTP Request via Schema позволяет получить все данные текущего HTTP-запроса через следующие поля:
_httpRequestBody: Тело HTTP-запроса_httpRequestClientHost: Хост клиента_httpRequestClientIP: IP-адрес клиента (илиnull, если сервер настроен неверно)_httpRequestCookie: Значение cookie запроса_httpRequestCookies: Cookies запроса_httpRequestDomain: Домен запрошенного URL_httpRequestFullURL: Запрошенный URL (включая параметры запроса)_httpRequestHasCookie: Содержит ли запрос определённый cookie?_httpRequestHasHeader: Содержит ли запрос определённый заголовок?_httpRequestHasParam: Содержит ли запрос определённый параметр?_httpRequestHeader: Значение заголовка запроса_httpRequestHeaders: Заголовки запроса_httpRequestHost: Хост запрошенного URL_httpRequestMethod: Метод запроса_httpRequestStringParam: Значение параметра (переданного через POST или GET) вида?param=value_httpRequestStringListParam: Значение параметра (переданного через POST или GET) вида?param[]=value1¶m[]=value2_httpRequestParams: Параметры, переданные через POST или через строку URL-запроса_httpRequestProtocol: Протокол запроса_httpRequestQuery: Строка параметров запроса_httpRequestReferer: Referer запроса_httpRequestRequestTime: Временная метка начала запроса_httpRequestScheme: Схема запрошенного URL_httpRequestServerIP: IP-адрес сервера_httpRequestURL: Запрошенный URL (без параметров запроса)_httpRequestURLPath: Абсолютный путь (начинающийся с «/») запрошенного URL_httpRequestUserAgent: User agent
Выполнение действия с данными
После извлечения данных из полезной нагрузки можно выполнить какое-либо действие с ними.
Этот GraphQL-запрос обрабатывает событие subscriber.subscriber_unsubscribe и отправляет письмо человеку, который отписался, с просьбой оставить отзыв.
query CreateEmailMessage
@depends(on: "ExtractPayloadData")
{
emailMessageTemplate: _strConvertMarkdownToHTML(
text: """
Hey {$subscriberFirstName}, it's sad to let you go!
Please be welcome to complete [this form](https://forms.gle/FpXNromWAsZYC1zB8) and let us know if there is anything we can do better.
Thanks. Hope to see you back!
"""
)
emailMessage: _strReplaceMultiple(
search: ["{$subscriberFirstName}"],
replaceWith: [$subscriberFirstName],
in: $__emailMessageTemplate
)
@export(as: "emailMessage")
}
mutation SendEmail @depends(on: "CreateEmailMessage") {
_sendEmail(
input: {
to: $subscriberEmail
subject: "Would you like to give us feedback on how we can improve?"
messageAs: {
html: $emailMessage
}
}
) {
status
}
}