Страницы

суббота, 10 августа 2013 г.

Как подружить Node.js с GAS ContentService.

С помощью сервиса Google Apps Script ContentService удобно получать данные "снаружи". Для этого достаточно написать скрипт, который будет слушать запросы и в ответ раздавать соответствующий контент, после чего этот самый скрипт опубликовать. Таким образом любое приложение сможет получить необходимые данные путем отправки запроса в адрес развернутого скрипта. Как оказалось на Node.js эта тривиальная операция требует дополнительных усилий.

Для начала создадим скрипт: идем по адресу https://script.google.com/, пишем нехитрый код.
function doGet(e) {  
  return ContentService.createTextOutput(e.parameters.prefix + '(404)')
    .setMimeType(ContentService.MimeType.JAVASCRIPT);
}

Сохраняем версию скрипта: Файл - Версии - Сохранить новую версию.
Разворачиваем веб-приложение: Публикация - Развернуть как веб-приложение.

Тестируем.

Попробуем получить данные из клиентского скрипта.
Пишем код:
<!DOCTYPE html>
<html>
  <head>
  <title>Node + GAS CS</title>
  </head>
  <body>    
    <script type="text/javascript">   
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = 'https://script.google.com/macros/s/AKfycbyAM01B_KCQMdiDO0MOnK-wMW70GDBDuGeuoqSAZQdcTHN4atNL/exec?prefix=gotit';  
   document.getElementsByTagName("head")[0].appendChild(script);   
   function gotit(data) {
    console.log(data);
    document.body.appendChild(document.createTextNode(data)); // для визуализации
   }
  </script>
  </body>
</html>

Тестируем.

Переходим к созданию приложения на Node.js.
Открываем консоль, создаем каталог приложения.
Пишем код:
var https = require('https');
https.request({
  hostname: 'script.google.com',  
  path: '/macros/s/AKfycbyAM01B_KCQMdiDO0MOnK-wMW70GDBDuGeuoqSAZQdcTHN4atNL/exec?prefix=gotit'
 }, function(res) {
  console.log("statusCode: ", res.statusCode);  
  res.on('data', function(d) {
   process.stdout.write(d);
  }
 );
}).on('error', function(e) {
  console.error(e);
}).end();

Выполняем:

Косяк ... Попробуем напрямую по адресу редиректа.
var https = require('https');
https.request({
  hostname: 'script.googleusercontent.com',   
  path: '/macros/echo?user_content_key=F4Hjt2xbdst9ljd3lyc_vN9MlgYVX0xmG1Dh-CRvbWrPMhPKUKNxS61xIZVmcKJXC6DgPr_qiHyFa_iFPkmwYO5UbyAbFabVm5_BxDlH2jW0nuo2oDemN9CCS2h10ox_1xSncGQajx_ryfhECjZEnGZcsj79OnH81pv3MMZcONDCa5_DrZ1OP80LQMcWh5QmFGLNpwQWbMUhp3YMIK--MhE_KXqosiQC&lib=MX6uGWWZ6UAAk8HBgycIDYfDMovwSBqo9'  
 }, function(res) {
  console.log("statusCode: ", res.statusCode);  
  res.on('data', function(d) {
   process.stdout.write(d);
  }
 );
}).on('error', function(e) {
  console.error(e);
}).end();

Выполняем:

Тысяча чертей, сударь! Похоже придется парсить url с помощью соответствующего модуля.
var https = require('https'), url = require('url');
https.request({
  hostname: 'script.google.com',  
  path: '/macros/s/AKfycbyAM01B_KCQMdiDO0MOnK-wMW70GDBDuGeuoqSAZQdcTHN4atNL/exec?prefix=gotit'
 }, function(res) {
  console.log("statusCode: ", res.statusCode);  
  var location = url.parse(res.headers.location);  
  console.log('\n-----\n', 
   'hostname: ', location.hostname + '\n', 
   'pathname: ', location.pathname + '\n', 
   'search: ', location.search, 
   '\n-----\n');
}).on('error', function(e) {
  console.error(e);
}).end();

Выполняем код пару раз.

Значение ключа user_content_key разное. Что и следовало.
Остается сформировать и выполнить еще один запрос.
var https = require('https'), url = require('url');
https.request({
  hostname: 'script.google.com',  
  path: '/macros/s/AKfycbyAM01B_KCQMdiDO0MOnK-wMW70GDBDuGeuoqSAZQdcTHN4atNL/exec?prefix=gotit'
 }, function(res) {
  console.log("statusCode: ", res.statusCode);  
  var location = url.parse(res.headers.location);  
  console.log('\n-----\n', 
   'hostname: ', location.hostname + '\n', 
   'pathname: ', location.pathname + '\n', 
   'search: ', location.search, 
   '\n-----\n');  
  https.get({
   hostname: location.hostname, 
   path: location.pathname + location.search
  }, function(res) {
   console.log('statusCode: ', res.statusCode);
   res.on('data', function(d) {
    process.stdout.write(d);
   });
  });  
}).on('error', function(e) {
  console.error(e);
}).end();

Или снова с помощью https.request, например:
var https = require('https'), url = require('url');
https.request({
  hostname: 'script.google.com',  
  path: '/macros/s/AKfycbyAM01B_KCQMdiDO0MOnK-wMW70GDBDuGeuoqSAZQdcTHN4atNL/exec?prefix=gotit'
 }, function(res) {
  console.log("statusCode: ", res.statusCode);  
  var location = url.parse(res.headers.location);  
  console.log('\n-----\n', 
   'hostname: ', location.hostname + '\n', 
   'pathname: ', location.pathname + '\n', 
   'search: ', location.search, 
   '\n-----\n');  
  https.request({
   hostname: location.hostname, 
   path: location.pathname + location.search
  }, function(res) {
   console.log('statusCode: ', res.statusCode);
   res.on('data', function(d) {
    process.stdout.write(d);
   });
  }).on('error', function(e) {
   console.error(e);
  }).end();
  
}).on('error', function(e) {
  console.error(e);
}).end();

Выполняем.

Статус - 200, содержательное сообщение 404 - на месте :).

В завершение - вариант решения с использованием готового модуля.

Устанавливаем:
- npm install follow-redirects

Пишем код, который почти не отличается от вступительного на Node.js текущей статьи.
var https = require('follow-redirects').https;
https.request({
  hostname: 'script.google.com',  
  path: '/macros/s/AKfycbyAM01B_KCQMdiDO0MOnK-wMW70GDBDuGeuoqSAZQdcTHN4atNL/exec?prefix=gotit'
 }, function(res) {
  console.log("statusCode: ", res.statusCode);  
  res.on('data', function(d) {
   process.stdout.write(d);
  }
 );
}).on('error', function(e) {
  console.error(e);
}).end();

Выполняем.

Результат практически не отличается от полученного выше. Но иногда полезно написать свой "велосипед" :).