How to create a new script file via Apps Script & Drive SDK

api, google-apps-script, google-drive-api

Solution

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"});
}

Problem

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.

Original source