Сервисный аккаунт нужен для доступа AutoFAQ к таблицам Google Sheets. Одного аккаунта достаточно для всех таблиц. При необходимости отключить доступ AutoFAQ ко всем таблицам просто отключите сервисный аккаунт.
3. Создайте в выбранном проекте сервисный аккаунт или выберите существующий сервисный аккаунт. При создании нового аккаунта достаточно выполнить шаг 1, редактировать доступы к проектам не обязательно. Сохраните имя и ID сервисного аккаунта (Service account ID) - они понадобится позднее.
3. У созданного сервисного аккаунта создайте ключ в формате JSON. ВНИМАНИЕ, сразу после создания ключа вам будет предложено сохранить файл - сохраните его в надежном месте. Если вы пропустили сохранение файла - удалите созданный ключ и сделайте новый, сохранив файл.
2. Выберите ваш проект и включите доступ проекта в Google Sheets API
3. Включите доступ для сервисной учетной записи
Доступ сервисного аккаунта к выбранной таблице
1. Создайте новую таблицу Google Sheets или открываете созданную ранее, проверьте наименование листа, там не должно быть символов на русском языке, при необходимости переименуйте название листа таблицы
2. Добавьте ID созданного ранее сервисного аккаунта в качестве редактора таблицы
3. Сохраните идентификатор таблицы Google Sheets
Подключение интеграции с Google к AutoFAQ
Создайте переменные сервиса с названием goggle_account_email и запишите в нее ID созданного ранее сервисного аккаунта
Создайте секретную переменную сервиса с названием google_private_key и сохраните в ней значение поля private_key из сохраненного JSON файла с ключом учетной записи. Сценарий будет использовать этот ключ для авторизации и получения токена.
Создайте секретную переменную сервиса с названием google_token с пустым значением для хранения временного токена. Сценарий будет использовать эту переменную для временного хранения токена с ограничением по времени жизни.
Пример сценария c сохранением данных в таблице Google Sheets
Ниже приведен пример сценария опроса пользователя и сохранения результатов опроса в таблицу Google Sheets.
//
// Сценарий опроса пользователя и сохранения результатов в таблице Google Sheet
//
// Идентификатор таблицы Google Sheet
установитьПеременную("sheetId", "1UyTG1ag6kb-tKCD0Wc5nD21zE38TvYSiMMQ_vzwxWbs")+
// Название листа в таблице Google Sheet
установитьПеременную("sheetName", "Sheet1")+
//
// Опрос пользователя
//
установитьПеременную("sendFio", "{userFullName}")+
если("{sendFio} == ").то(
задатьПользователюВопрос("Укажите как вас зовут, пожалуйста").сохранитьРезультат("sendFio")
)+
установитьПеременную("sendEmail", "{userEmail}")+
если("{sendEmail} == ").то(
задатьПользователюВопрос("Укажите адрес вашей электронной почты").сохранитьРезультат("sendEmail")
)+
задатьПользователюВопрос("Пожалуйста назовите ваш отдел").сохранитьРезультат("sendOtdel") +
задатьПользователюВопрос("Пожалуйста назовите вашу должность").сохранитьРезультат("sendDolznost") +
задатьПользователюВопрос("Уточните, пожалуйста, что для Вас является приоритетным?").сВариантамиОтвета("Получение максимальной суммы","Простота получения").сохранитьРезультат("sendPriority")+
задатьПользователюВопрос("Укажите, на какие цели предназначается займ?").сохранитьРезультат("sendGoal")+
//
// Отправка запроса в гугл
//
// Попытка получения сохраненного ранее токена для запросов в google из переменных сервиса
получитьПеременнуюСервиса("google_token", "g_token")+
// Разбор сохраненного токена
выполнитьJs("""
try {
var doc = JSON.parse(g_token);
} catch(e) {
var doc = {'token': '', 'ts': 0};
}
var token = '';
var now_ts = parseInt(Date.now() / 1000);
if (now_ts > doc.ts) {
var error = 1;
} else if (doc.ts == 0) {
var error = 2;
} else if (doc.token == '') {
var error = 3;
} else {
var error = 0;
var token = doc.token;
}
var exit = {'error':error, 'token':token, 'expired':doc.ts, 'now_ts':now_ts};
exit;
""") +
комментарий("Получил сохраненный токен, статус токена {error}")+
// Проверка актуальности сохраненного токена
если("{error} == 0").то(
// Токен записан и его время жизни не истекло
// Сценарий переходит непосредственно к запросу на добавление данных в таблицу
комментарий("Токен еще валиден, используем его")+
перейтиНаМетку("google_query")
)+
// Токен или не сохранен или его время жизни истекло
// Сценарий получает новый токен
комментарий("Токен не валиден, получаем новый токен")+
// Сценарий получает из переменных сервиса секретный ключ и адрес аккаунта гугл
получитьПеременнуюСервиса("google_private_key", "g_private_key") +
получитьПеременнуюСервиса("goggle_account_email", "goggle_account_email") +
// Проверка полученных данных
если("{g_private_key} == ").то(
комментарий("Нет приватного ключа от аккаунта, не могу получить токен.")+
завершить
)+
если("{goggle_account_email} == ").то(
комментарий("Нет адреса аккаунта гугл, не могу получить токен.")+
завершить
)+
// Сценарий формирует тело запроса на получение токена
выполнитьJs("""
var header = {
"alg": "RS256",
"typ": "JWT"
};
var data = {
"iss": goggle_account_email,
"scope": "https://www.googleapis.com/auth/spreadsheets",
"aud": "https://oauth2.googleapis.com/token",
"exp": parseInt(Date.now() / 1000) + 3600,
"iat": parseInt(Date.now() / 1000)
};
var sHeader = JSON.stringify(header);
var sPayload = JSON.stringify(data);
var s_g_private_key = g_private_key.replace(/\\n/g,'');
var signature = KJUR.jws.JWS.sign(header.alg, sHeader, sPayload, s_g_private_key);
var exit = {'signature':signature};
exit;
""") +
// Сценарий запрашивает гугла для получения токена
вызвать.внешнийСервис("https://oauth2.googleapis.com/token", "POST").сЗаголовками(("Content-Type", "application/json")).сТеломСообщения("{'grant_type':'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion':'{signature}'}").сохранитьРезультатКакСтроку("result")+
если("{http_code} > 210").то(
комментарий("Сценарий не смог получить токен для записи в таблицу. Код ошибки {http_code}, ответ {result}")+
сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Google.")+
завершить
)+
// Сценарий разбирает ответ от гугл на получения токена
выполнитьJs("""
try {
var resultParced = JSON.parse(result)
} catch(e) {
var resultParced = {'access_token':'','expires_in':0};
}
var token = resultParced.access_token;
var expires = resultParced.expires_in;
var g_token = {'token': token, 'ts': parseInt(Date.now() / 1000) + 3599};
var exit = {'token':token, 'expires':expires, 'g_token':JSON.stringify(g_token)};
exit;
""") +
// Сценарий разбирает ответ гугла
если("{token} == ").то(
комментарий("Сценарий не смог распознать токен в ответе от гугла. Ответ {result}")+
сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Google.")+
завершить
)+
// Сценарий сохраняет полученный токен для будущего использования
изменитьПеременнуюСервиса("google_token", "{g_token}")
комментарий("Обновил сохраненный токен для запросов в гугл")+
//
// Метка для запроса на запись данных в таблицу
установитьМетку("google_query") +
//
// Формирование тела запроса на добавление 1 строки в конец таблицы, количество столбцов не более 25
выполнитьJs("""
// Дата добавления
var sendDate = new Date();
// Список добавляемых значений
var addValues = [sendDate, sendEmail, sendFio, sendOtdel, sendDolznost, sendPriority, sendGoal];
var sheetsNames = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','R','S','T','U','V','W','X','Y','Z'];
var sheetEnd = sheetsNames[addValues.length];
var requestBody = {"range": sheetName+"!A1:"+sheetEnd+"1", "majorDimension": "ROWS", "values":[addValues,]};
var exit = {'requestBodyJSON':JSON.stringify(requestBody), 'sheetEnd':sheetEnd};
exit;
""") +
//
// Сценарий отправляет запрос на отправку данных в табличку
вызвать.внешнийСервис("https://sheets.googleapis.com/v4/spreadsheets/{sheetId}/values/{sheetName}!A1:{sheetEnd}1:append","POST").сПараметрами(("valueInputOption","USER_ENTERED")).сЗаголовками(("Authorization", "Bearer {token}"),("Content-Type","application/json")).сТеломСообщения("{requestBodyJSON}").сохранитьРезультатКакСтроку("result")+
// Сценарий разбирает результаты запроса
если("{http_code} > 210").то(
комментарий("Сценарий не смог отправить запрос на добавление строчки в гугл таблице. Код {http_code}, ответ {result}")+
сообщениеПользователю.сШаблоном("Спасибо за участие, но что-то сломалось и я не могу связаться с Google.")+
завершить
)+
// Запрос на добавление записи завершен успешно
комментарий("Добавил строчку в гугл таблицу.") +
сообщениеПользователю.сШаблоном("Все записал. Большое спасибо за участие!") +
завершить