3

I created a forms addon with scope:

https://www.googleapis.com/auth/drive.file

and created a picker:

function onOpen(e)
{
  FormApp.getUi().createAddonMenu()
  .addItem('Sync to Drive', 'showPicker')
  .addToUi();
}

function getOAuthToken() {
  return ScriptApp.getOAuthToken();
}

function showPicker() {
  var html = HtmlService.createHtmlOutputFromFile('Picker.html')
    .setWidth(600)
    .setHeight(425)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);
  FormApp.getUi().showModalDialog(html, 'Select Folder');
}

// Use Advanced Drive Service - https://developers.google.com/apps-script/advanced/drive
function getFileWithAdvancedDriveService(fileId)
{
  var drivefl = Drive.Files.get(fileId);
  FormApp.getUi().alert('File: '+drivefl);
  return drivefl;
}

// Use Drive Service - https://developers.google.com/apps-script/reference/drive
function getFileWithDriveService(fileId)
{
  var drivefl = DriveApp.getFileById(fileId);
  FormApp.getUi().alert('File: '+drivefl);
  return drivefl;
}

After I open a file using this picker, I am trying to read it in the pickerCallback:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css" />
    <script type="text/javascript">
      var DIALOG_DIMENSIONS = {
        width: 600,
        height: 425,
      };
      var authToken;
      var pickerApiLoaded = false;

      function onApiLoad() {
        gapi.load('picker', {
          callback: function () {
            pickerApiLoaded = true;
          },
        });
        google.script.run.withSuccessHandler(createPicker).withFailureHandler(showError).getOAuthToken();
      }

      function createPicker(token) {
        authToken = token;
        if (pickerApiLoaded && authToken) {
          var docsView = new google.picker.DocsView()
            .setIncludeFolders(true);

          var picker = new google.picker.PickerBuilder()
            .addView(docsView)
            .enableFeature(google.picker.Feature.NAV_HIDDEN)
            .hideTitleBar()
            .setOAuthToken(authToken)
            .setCallback(pickerCallback)
            .setOrigin('https://docs.google.com')
            .build();

          picker.setVisible(true);
        } else {
          showError('Unable to load the file picker.');
        }
      }

      function pickerCallback(data, ctx) {
        var action = data[google.picker.Response.ACTION];
        if(action == google.picker.Action.PICKED) {
          var doc = data[google.picker.Response.DOCUMENTS][0];
          var id = doc[google.picker.Document.ID];

          // Option 1 - Advanced Drive Service in apps script
          return google.script.run.withSuccessHandler(showMessage).withFailureHandler(showError).getFileWithAdvancedDriveService(id);

          // Option 2 - Drive Service in apps script
          return google.script.run.withSuccessHandler(showMessage).withFailureHandler(showError).getFileWithDriveService(id);

          // Option 3 - Drive Service in client
          return gapi.load('client', function () {
              gapi.client.load('drive', 'v2', function () {
                  gapi.client.setToken({ access_token: authToken });
                  var file = gapi.client.drive.files.get({ 'fileId': id });
                  file.execute(function (resp) {
                    showMessage(resp);
                  });
              });
          });
        } else if(action == google.picker.Action.CANCEL) {
          google.script.host.close();
        }
      }

      function showMessage(message) {
        document.getElementById('result').innerHTML = '<div>Message:</div> ' + JSON.stringify(message);
      }

      function showError(message) {
        document.getElementById('result').innerHTML = `<div>Error:</div> <div style="color:red;">${message}</div>`;
      }
    </script>
  </head>

  <body>
    <div>
      <p id="result"></p>
    </div>
    <script type="text/javascript" src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
  </body>
</html>
  1. gapi.client.drive.files.get fails with the error: code: 404, message: "File not found: 1d0cqiT3aipgjMfLPolzgWVrnsl4xPxUJ1_7pH3ONVzU"

  2. I tried the same on the server side (apps script) and got similar error:

    DriveApp.getFolderById(fileId)

returns:

You do not have permission to call DriveApp.getFolderById. Required permissions: (https://www.googleapis.com/auth/drive.readonly || https://www.googleapis.com/auth/drive)
  1. Same with advanced drive api:

    Drive.Files.get(fileId)

returns:

GoogleJsonResponseException: API call to drive.files.get failed with error: File not found: 1d0cqiT3aipgjMfLPolzgWVrnsl4xPxUJ1_7pH3ONVzU

Do I need drive.readonly scope to read the file opened by the user using Google Picker?

13
  • What is gapi? Is this client or server? Commented May 31, 2022 at 12:06
  • @TheMaster Its in the client - Javascript callback for Google Picker. Commented May 31, 2022 at 12:21
  • @TheMaster gapi is for loading google's javascript libraries on the client: <script type="text/javascript" src="apis.google.com/js/api.js?onload=onApiLoad"></script> which loads Google Picker: gapi.load('picker', { }); Commented May 31, 2022 at 12:43
  • So this: DriveApp.getFolderById is a server side error(There is no DriveApp on the client). If it's the client, you need to quote the error on the client side. You also need to say where you got that server side error or how you made the connection between gapi.client.drive.files.get and DriveApp.getFolderById or what made you think they're related? Commented May 31, 2022 at 12:49
  • @TheMaster Sorry! I copy/pasted the error when I tried the server side option with DriveApp. I have rewritten the question with all the 3 options I tried. It works if I use drive.readonly oauth scope though. Commented May 31, 2022 at 13:07

2 Answers 2

1

Yes, as the https://www.googleapis.com/auth/drive.file scope only allows you to:

View and manage Google Drive files and folders that you have opened or created with this app

And https://www.googleapis.com/auth/drive.readonly:

See and download all your Google Drive files

You can review the full list of scopes here.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! drive.file scope says "View and manage Google Drive files and folders that you have opened or created with this app". When I open a file using the addon's file picker, is that not considered opened with this app?
From what I know, you only can view files created by this app.
I just wrestled this for hours. If you only want to use drive.file ( which is a far better user experience ), you need to make sure you include your app id when creating the picker.
0

I found the problem. It works if I move this gapi.load code from pickerCallback:

      return gapi.load('client', function () {
          gapi.client.load('drive', 'v2', function () {
              gapi.client.setToken({ access_token: authToken });
              var file = gapi.client.drive.files.get({ 'fileId': id });
              file.execute(function (resp) {
                showMessage(resp);
              });
          });
      });

to javascript onload:

  function onGsiLoad()
  {
    return gapi.load('client', function () {
      return gapi.client.load('drive', 'v2', function () {
        gsiLoaded = true;
        maybeEnablePicker();
      });
    });
  }

And only have gapi.client.drive.files.get in pickerCallback:

      var file = gapi.client.drive.files.get({ 'fileId': fileId });
      file.execute(function (resp) {
        showMessage(resp);
      });

Working test case is here: https://docs.google.com/forms/d/1h3FWKVpGbCApg1_2unD3L86QlOmh9CIwK-W1Q97UTYQ/edit?usp=sharing

Buggy one is here: https://docs.google.com/forms/d/1jpEa-mp8ccCZhEGlgBN52AML2i1oHIShFpY8Oe3GC44/edit?usp=sharing

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.