Новости‎ > ‎

Доступ к Google Drive API через Cordova

Отправлено 3 апр. 2016 г., 8:18 пользователем Denis Baskovsky   [ обновлено 5 апр. 2016 г., 21:36 ]
С Cordova трудно работать. Особенно когда используешь с ней сторонние web-сервисы. Так и теперь, типичный парсер использующий Google Spreadsheet занял у меня почти 2 недели.

Обо всём этом по-порядку.

В 1or2 решили хранить и модифицировать клиентские данные в одной совместной таблице на Spreadsheet. Об этом я писал ещё в предыдущей новости. Короче, сделать выгрузку в CSV легко не получилось, поэтому заюзал Apps Script. С ним можно взаимодействовать со многими Google сервисами по REST-API. Получилось вот так (наработки по выгрузке CSV сохранил здесь). Apps Script, в целом, мне не понравился. Такая древняя технология, основанная на EcmaScript 3, с простеньким веб-редактором, дебаггером, версионностью и легкой интеграцией с консолью Google.

Правильные шаги получения результата из Apps Script такие:

1. Загрузить gapi либу;
2. Показать окно с авторизацией;
3. Ввести логин+пароль и получить токен;
4. Исполтзовать токен для доступа к данным.

Успешно оттестировав на браузере при портировании под Cordova получил ошибку:
Refused to display... бла-бла-бла ...in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'.

Оказалось gapi не работает с cordova. Путем долгого чтения док и стэковерфлоу все же нашел решение.
Его листинг приложил ниже:

function googleOAuth(CLIENT_ID, SCOPES) {

  return new Promise((resolve, reject) => {

    if (!window.cordova) {

      window.gapi.auth.authorize({
        client_id: CLIENT_ID,
        scope: SCOPES,
        response_type: 'token',
        immediate: false
      }, (authResult) => {
        resolve(authResult);
      });

    } else {

      if (window.cordova.InAppBrowser) {
        const redirect_uri = 'http://localhost/callback';

        const browserRef = window.cordova.InAppBrowser.open(
          `https://accounts.google.com/o/oauth2/auth?client_id=${CLIENT_ID}` +
          `&redirect_uri=${redirect_uri}` +
          `&scope=${SCOPES.join(' ')}` +
          '&approval_prompt=force' +
          '&response_type=token id_token',
          '_blank',
          'location=no,clearsessioncache=yes,clearcache=yes'
        );

        browserRef.addEventListener('loadstart', (event) => {

          if ((event.url).indexOf(redirect_uri) === 0) {
            browserRef.removeEventListener('exit', () => {
            });
            browserRef.close();

            let callbackResponse = (event.url).split('#')[1];
            let responseParameters = (callbackResponse).split('&');
            let parameterMap = [];

            for (let i = 0; i < responseParameters.length; i++) {
              parameterMap[responseParameters[i].split('=')[0]] = responseParameters[i].split('=')[1];
            }

            if (parameterMap.access_token !== undefined && parameterMap.access_token !== null) {
              resolve({
                access_token: parameterMap.access_token,
                expires_in: parameterMap.expires_in,
                id_token: parameterMap.id_token,
                token_type: parameterMap.token_type
              });
            } else {
              reject('Problem authenticating');
            }
          }

        });

        browserRef.addEventListener('exit', () => {
          reject('The sign in flow was canceled');
        });

      } else {
        reject('Could not find InAppBrowser plugin');
      }

    }

  });

}

Залив очередной билд на девайс получил уже другую ошибку. Теперь попросту не открывалось окно авторизации. Приложение ругалось на запросы через file:///
Пофиксил это установив плагин InAppBrowser.

Далее окно авторизации уже стало работать. Но с ошибкой. Не нравился мой callback-запрос.
В консоле убрал последний слэш в запросе http://localhost/callback
Далее приложение ругалось на невозможность выполнения запросов.
Пофиксил это, установив плагин whitelist и добавил в config.xml следующее содержимое:

<access origin="*" launch-external="yes" />
<allow-navigation href="https://ssl.gstatic.com/*" />
<allow-navigation href="https://apis.google.com/*" />
<allow-navigation href="https://*.googleusercontent.com/*" />
<allow-navigation href="https://accounts.google.com/*" />
<allow-navigation href="https://www.googleapis.com/*" />
<allow-navigation href="https://*.googleapis.com/*" />
<allow-intent href="*" />
<allow-intent href="https://*.googleapis.com/*" />
<allow-intent href="https://apis.google.com/*" />

На всякий случай добавил вот такую мету в index.html:
<meta http-equiv="Content-Security-Policy"
content="default-src ; img-src 'self' data: blob: filesystem:; script-src 'self' 'unsafe-inline' 'unsafe-eval' ; style-src 'self' 'unsafe-inline' *">

Короче говоря, пока занимался такой вот тривиальной задачей так устал, что думал вообще останавливать запил игрули. Но спустя две недели я получил необходимый массив значений на девайсе. Но вот, честно сказать, никакой радости от этого я уже не испытываю.

Либу cordova-google-oauth2 выложил с лицензией MIT.