Using google drive appDataFolder to store app state with javascript on the client side
Asked Answered
A

2

6

I've been trying to create this very simple web app that is using google drive appDataFolder to store and read its state, in json format. However, the documentation for v3 drive api doesn't contain comprehensive examples on how to achieve this easily.

So far I'm able to authorize and load the google drive api, but I wasn't able to create the configuration json file if it doesn't exist, or update its contents or read if it exists.

The calls I'm making are to: gapi.client.drive.files.get to retrieve the config json, gapi.client.drive.files.create to create the config json and gapi.client.drive.files.update to update its contents.

I've been trying to use the javascript Blob object to represent my file and its contents, but nothing has worked.

Given I have the config like for example { test: true } stringified and the file name my-app.json, how would I call the gapi.client.drive.files API to be able to create/update/read this config?

The retrieve part I tried to do it by first getting a list of the files in the app folder, match the config file by name, get its id, then request again for that file using the id. But since I wasn't able to create the file, I'm not sure if it works.

Code currently looks like this (important to note that this code gets build and it's run in the client browser, not on the server):

var config = require('../config/google-drive-config');

var authorize = function (immediate) {
  return gapi.auth.authorize({
    'client_id': config.clientId,
    scope: config.scopes.join(' '),
    immediate: !!immediate
  });
};

var loadDriveAPI = function () {
  return gapi.client.load(
    config.apiName,
    config.apiVersion
  );
};

var loadAppDataFileId = function () {
  return gapi.client.drive.files
    .list({
      spaces: 'appDataFolder'
    })
    .then(function(response) {
      return _(response.files)
        .find({ name: config.appDataFile })
        .get('id')
        .value();
    });
};

var loadAppData = function (fileId) {
  return gapi.client.drive.files
    .get({
      'fileId': fileId
    });
};

var saveAppData = function (appData, fileId) {
  var resource = {
    'name': config.appDataFile,
    'parents': 'appDataFolder'
  };

  var media = {
    mimeType: 'application/json',
    body: new Blob([JSON.stringify(appData)], { type: 'application/json' })
  };

  if (fileId) {
    return gapi.client.drive.files
      .update({
        fileId: fileId,
        media: media
      });
  }

  return gapi.client.drive.files
    .create({
      resource: resource,
      media: media,
      fields: 'id'
    });
};

module.exports = {
  authorize: authorize,
  loadDriveAPI: loadDriveAPI,
  loadAppDataFileId: loadAppDataFileId,
  loadAppData: loadAppData,
  saveAppData: saveAppData
};
Abshire answered 5/7, 2016 at 21:45 Comment(8)
I have exactly the same problem at the moment. I want to update my file, I have the fileId. and have some json data I wish to store.Rumble
Welcome to stackoverflow. see how to write a good stackoverflow question to improve your question, show us the create code that isnt working.Daffi
Yeah, I realize now the question wasn't formulated properly. I'll update with my code. Thanks for the feedback @ZigMandelAbshire
@AdrianBota your load is missing something: var loadAppData = function (fileId, callback) { return gapi.client.drive.files .get({ 'fileId': fileId, alt: "media" }).execute(callback); }; This way you can load the content of thet file. I need to find how to run the gapi.client.drive.files.update function - you need that for saving files, not create (at least, I think that is how you should do it at least)Rumble
@AdrianBota I made a mistake in my previous comment, you do actually call update.Rumble
@MarcWent I'm looking at the Node.js example here (although I'm running this on the client) and I don't see that execute call in there developers.google.com/drive/v3/web/… I'll give a try thoughAbshire
I thinking maybe v3 of drive api is not set to handle calls from client sideAbshire
@AdrianBota I know v3 can store the data. I managed to do it through the obscure route mentioned across the web. With gapi.client.request. And it is kind of messy. I doubt it doesn't work, but the sweet working combination is hard to find >,<Rumble
A
12

After a lot of trial and error, I finally got it working. I couldn't get gapi.client.drive.files.update to work, but it works with gapi.client.request:

var auth = function (immediate) {
  return gapi.auth.authorize({
    'client_id': 'YOUR CLIENT ID GOES HERE',
    // Permissions here can be more restrictive
    scope: 'https://www.googleapis.com/auth/drive',
    immediate: immediate
  });
};

var silentAuth = function () {
  return auth(true);
};

var popupAuth = function () {
  return auth(false);
};

var loadDriveAPI = function () {
  return global.gapi.client.load('drive', 'v3');
};

var getAppDataFile = function () {
  return gapi.client.drive.files
    .list({
      q: 'name="your-app-data-file-name.json"',
      spaces: 'appDataFolder',
      fields: 'files(id)'
    }).then(
      function (data) {
        if (_.isEmpty(data.result.files)) {
          throw "no files found";
        }

        return {
          fileId: data.result.files[0].id
        }
      }
    );
};

var createAppDataFile = function () {
  return gapi.client.drive.files
    .create({
      resource: {
        name: 'your-app-data-file-name.json',
        parents: ['appDataFolder']
      },
      fields: 'id'
    }).then(function (data) {
      return {
        fileId: data.result.id
      };
    });
};

var getAppDataFileContent = function (fileId) {
  return gapi.client.drive.files
    .get({
      fileId: fileId,
      // Download a file — files.get with alt=media file resource
      alt: 'media'
    }).then(function (data) {
      return {
        fileId: fileId,
        appData: data.result
      };
    });
};

var saveAppData = function (fileId, appData) {
  return gapi.client.request({
    path: '/upload/drive/v3/files/' + fileId,
    method: 'PATCH',
    params: {
      uploadType: 'media'
    },
    body: JSON.stringify(appData)
  });
};

module.exports = {
  silentAuth: silentAuth,
  popupAuth: popupAuth,
  loadDriveAPI: loadDriveAPI,
  getAppDataFile: getAppDataFile,
  createAppDataFile: createAppDataFile,
  getAppDataFileContent: getAppDataFileContent,
  saveAppData: saveAppData
};

All the methods above return a promise. There is a dependency on lodash

Abshire answered 18/7, 2016 at 22:55 Comment(3)
i cannot express my gratitude towards this answer. works flawlessly, and has resolved a lot of problems that the google api documentations seems to be mute about. thank you!Ismaelisman
It should be official tutorial on google pages. I found it after 3 hours of reading messy google docs and almost resigned from implementation google drive support... But this works great.Gibbous
Thank you for this snippet, just saved me a bunch of time debuggingAceae
A
3

I've created a library that takes care of all the boilerplate and exposes a very simple, straight-forward API: https://github.com/adrianbota/gdrive-appdata

Abshire answered 25/7, 2016 at 10:5 Comment(1)
It might be helpful to explain a bit about what that boilerplate is, as well.Thrift

© 2022 - 2024 — McMap. All rights reserved.