3

Trying to create a new project with files with a call to the Drive SDK within Apps Script.

Where exactly would the following go in a UrlFetchApp request...

{
  "files": [
    {
      "id":"9basdfbd-749a-4as9b-b9d1-d64basdf803",
      "name":"Code",
      "type":"server_js",
      "source":"function doGet() {\n  return HtmlService.createHtmlOutputFromFile(\u0027index\u0027);\n}\n"
    },
    {
      "id":"3asf7c0d-1afb-4a9-8431-5asdfc79e7ae",
      "name":"index",
      "type":"html",
      "source":"\u003chtml\u003e\n  \u003cbody\u003e\n    New message!!\n  \u003c/body\u003e\n\u003c/html\u003e"
    }
  ]
}

It's from the import/export docs and in the example video Dan mentions the calls are for languages outside of Apps Script but requesting a list of script file types and the content of those files does work once the authorization is setup with Eric's oAuth2 library.

My most recent guess...

function createProject( ) {
  var token = getDriveService().getAccessToken(); // from Eric's oAuth2 lib

  var url = 'https://www.googleapis.com/upload/drive/v2/files?convert=true';

  // Where does this go?
  var files = {
    "files": [
      {
        "name":"Code",
        "type":"server_js",
        "source":"function doGet() {\n  return HtmlService.createHtmlOutputFromFile(\u0027index\u0027);\n}\n"
      },
      {
        "name":"index",
        "type":"html",
        "source":"\u003chtml\u003e\n  \u003cbody\u003e\n    Hello, world!!\n  \u003c/body\u003e\n\u003c/html\u003e"
      }
    ]
  };

  // Where does this go too?
  var metadata = {
    'title': 'script-test1',
    'mimeType': 'application/vnd.google-apps.script',
    "parents": [
      {
        "id": "0B2VkNbQMTnaCODBRVjZQcXBXckU"
      }
    ],
  };

  var options = {
    'headers' : {
      'Authorization': 'Bearer ' +  token,
      'Content-Type': 'application/vnd.google-apps.script+json',
    },
    'method' : 'POST',
    'payload' : files  // probably not right

  };

  var response = UrlFetchApp.fetch(url, options);
  Logger.log(response.getResponseCode());

}

An untitled Drive file of unknown type does get created and the payload does get inserted into it, but it's not converted to a script file type.

Going another route and just using...

var file = {
    "title": "Test script",
    "mimeType": "application/vnd.google-apps.script",
    "parents": [
      {
        "id": "[INSERT FOLDER ID HERE]"
      }
    ]
  };

Drive.Files.insert(file);

...throws an Internal error.

Also aware of the Drive insert docs that have a client-side JS example, but don't know how much of it should be translated over (if possible) to server-side Apps Script.

Bryan P
  • 5,031
  • 3
  • 30
  • 44
  • In your options variable, try changing the payload parameter for 'body:', and forget about the metadata. – Rivero Nov 25 '14 at 17:39
  • a new Drive file of unknown type still gets created but no text or anything is inserted. i don't see `body` as an advanced parameter in [UrlFetchApp docs](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app) and interpret `payload` as the equivalent. – Bryan P Nov 25 '14 at 18:44

1 Answers1

4

You're almost there, but you have a few mistakes. Your first approach (using UrlFetch) has some errors in the options variable. Here's a doGet() function that (after verifying authorization) creates a new Apps Script Project and writes the UrlFetch response to the page:

function doGet() {
  var driveService = getDriveService();
  if (!driveService.hasAccess()) {
    var authorizationUrl = driveService.getAuthorizationUrl();
    var template = HtmlService.createTemplate(
        '<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' +
        'Refresh the page when authorization complete.');
    template.authorizationUrl = authorizationUrl;
    var page = template.evaluate();
    return page;
  } else {     
    var url = "https://www.googleapis.com/upload/drive/v2/files?convert=true";   
    var requestBody =  {
      "files": [
        {
          "name":"Code",
          "type":"server_js",
          "source":"function doGet() {\n  return HtmlService.createHtmlOutputFromFile(\u0027index\u0027);\n}\n"
        },
        {
          "name":"index",
          "type":"html",
         "source":"\u003chtml\u003e\n  \u003cbody\u003e\n    Created with Apps Script.\n  \u003c/body\u003e\n\u003c/html\u003e"
        }
      ]
    };

    var options = {
      "headers": {
         'Authorization': 'Bearer ' +  driveService.getAccessToken(),
       }, 
      "contentType": "application/vnd.google-apps.script+json",
      "method" : "post",
      "payload": JSON.stringify(requestBody)
    }

    var response = UrlFetchApp.fetch(url, options);
    return HtmlService.createHtmlOutput(response.getContentText());
  }
}

This code creates an Apps Script project file called "Untitled" in the root Drive folder. The options object should specify POST as the method for creating projects (default is GET), and the payload needs to be converted to a string from a JSON object to be understood by UrlFetch.

Alternatively, you can do the same thing by using the Advanced Drive Service in Apps Script instead of UrlFetch. The following code creates the same Apps Script project (but places it in the specified folder and names the file 'Test script'):

function createGoogleFileInFolder3() {  
    var requestBody =  {
      "files": [
        {
          "name":"Code",
          "type":"server_js",
          "source":"function doGet() {\n  return HtmlService.createHtmlOutputFromFile(\u0027index\u0027);\n}\n"
        },
        {
          "name":"index",
          "type":"html",
          "source":"\u003chtml\u003e\n  \u003cbody\u003e\n    Created with Apps Script.\n  \u003c/body\u003e\n\u003c/html\u003e"
        }
      ]
    };

  var resource = {
    "title": "Test script",
    "parents": [
      {
        "id": "<parent folder id here>"
      }
    ]
  };

  var blob = Utilities.newBlob(JSON.stringify(requestBody), "application/vnd.google-apps.script+json");

  Drive.Files.insert(resource, blob, {"convert":"true"});
}
Ryan Roth
  • 1,394
  • 9
  • 15
  • `Utilities.newBlob()` is what I was missing out on. Thought the blob had to come from a `response.getBlob()` for some reason. Will implement in my add-on. thx a ton! – Bryan P Nov 26 '14 at 04:24
  • wait, did you manually insert the conversions for the unicode `\u003c` or did you use a library / mapping for that? – Bryan P Dec 11 '14 at 23:53
  • 2
    I would like to do this with Drive.Files.update(recource, fileId, blob) but I get "The app does not have the required scope to update Apps Scripts." ? – Riël May 16 '15 at 13:59
  • 1
    Using the Advanced Drive Service does not work, because you cannot set your custom token for it and can't have Apps Script ask it for you :( – Henrique G. Abreu Dec 29 '15 at 11:41
  • The optional query parameter `convert` for the `upload` method is now deprecated. It has no function. [Reference - Parameters](https://developers.google.com/drive/v2/reference/files/update#parameters) – Alan Wells Jan 01 '16 at 20:20
  • Sandy, the page you link to is for Drive.update; this sample is using Drive.insert, for which the convert parameter is still valid (at least for Drive v2): [Reference - Drive.insert](https://developers.google.com/drive/v2/reference/files/insert#parameters) – Ryan Roth Jan 06 '16 at 21:32