Currently working on a google sheet and implementing a script that will upload a file from Drive to BOX .
I got the authentication down (I am able to get the folder list through the script connecting to the API) but when I get to the uploading part, I am getting an empty request and the file is not getting uploaded to the target folder
My script below:
function uploadFile() {
var boundary = "test";
var blob = DriveApp.getFileById('<I got my file ID here>').getBlob();
var service = getService();
var attributes = "{\"name\":\"test document.pdf\", \"parent\":{\"id\":\"<I got my folder ID here>\"}}";
var requestBody = Utilities.newBlob(
"--"+boundary+"\r\n"
+ "Content-Disposition: form-data; name=\"attributes\"\r\n\r\n"
+ attributes+"\r\n"+"--"+boundary+"\r\n"
+ "Content-Disposition: form-data; name=\"file\"; filename=\""+blob.getName()+"\"\r\n"
+ "Content-Type: " + blob.getContentType()+"\r\n\r\n").getBytes()
.concat(blob.getBytes())
.concat(Utilities.newBlob("\r\n--"+boundary+"--\r\n").getBytes());
var options = {
method: "post",
contentType: "multipart/form-data; boundary="+boundary,
payload: requestBody,
muteHttpExceptions: true,
headers: {'Authorization': 'Bearer ' + service.getAccessToken()}
};
var request = UrlFetchApp.fetch("https://upload.box.com/api/2.0/files/content", options);
Logger.log(request.getContentText()); // empty response
}
Thanks
Modification points:
When I check the official document of "Upload file" of the BOX API, I found the following curl sample. Ref
$ curl -i -X POST "https://upload.box.com/api/2.0/files/content" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: multipart/form-data" \
-F attributes="{"name":"Contract.pdf", "parent":{"id":"11446498"}}" \
-F file=#<FILE_NAME>
When the request of multipart/form-data is run, it seems that at Google Apps Script, the content type is automatically created including the boundary.
When above curl command is converted to Google Apps Script, it becomes as follows.
Modified script:
function uploadFile() {
var blob = DriveApp.getFileById('<I got my file ID here>').getBlob();
var service = getService();
var attributes = {"name":"test document.pdf", "parent":{"id":"<I got my folder ID here>"}};
var requestBody = {attributes: Utilities.newBlob(JSON.stringify(attributes), "application/json"), file: blob};
var options = {
method: "post",
payload: requestBody,
muteHttpExceptions: true,
headers: {'Authorization': 'Bearer ' + service.getAccessToken()}
};
var request = UrlFetchApp.fetch("https://upload.box.com/api/2.0/files/content", options);
Logger.log(request.getContentText());
}
Note:
In this sample script, it supposes that your access token of service.getAccessToken() can be used for uploading the file using the API. Please be careful this.
References:
Upload file of BOX API
fetch(url, params)
Related
I'm trying to convert a Docs file to PDF in GAS, using Drive API.
Based on what I found, basically the steps are:
Get the Docs file
Convert to PDF
Upload to Drive
What I did was use the 'export' function to get the Docs and convert it to PDF, then 'create' function to upload. My code below:
function test(){
let service = getOAuthService();
service.reset();
// Get the Docs file
let url = encodeURI("https://www.googleapis.com/drive/v3/files/<id of file>/export?mimeType=application/pdf&supportsAllDrives=true");
let docFile = getGoogleAPI(url);
var fileName = "my-test-file.pdf";
var fileContent = docFile.getBlob();
var fileMetadata = {
name: fileName,
parents: ["<id of parent folder>"],
mimeType: "application/pdf"
};
url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true";
var header = {
"Authorization": "Bearer " + service.getAccessToken(),
"Content-Type": "multipart/related; boundary=<BOUNDARY_STRING>"
};
var body = "--<BOUNDARY_STRING>\r\n" +
"Content-Type: application/json; charset=UTF-8\r\n\r\n" +
JSON.stringify(fileMetadata) + "\r\n" +
"--<BOUNDARY_STRING>\r\n" +
"Content-Type: application/pdf\r\n\r\n" +
fileContent + "\r\n" +
"--<BOUNDARY_STRING>--";
let response = UrlFetchApp.fetch(url, {
method: "post",
headers: header,
payload: body,
muteHttpExceptions: true
});
var responseJson = JSON.parse(response.getContentText());
if (response.getResponseCode() == 200) {
Logger.log("File uploaded successfully with ID: " + responseJson.id);
} else {
Logger.log("Error uploading file: " + responseJson.error.message);
}
}
However the generate PDF file is either empty or not in the correct PDF format (cannot preview).
I think the first part (getting the Docs file using export) works, but it's the uploading that has the problem. Maybe an incorrect encoding?
Does anyone know where I am wrong on this?
Thanks in advance!
In your script, fileContent is Blob. Unfortunately, Blob cannot be directly used in this request body. I think that this is the reason for your current issue. In this case, how about the following modification?
Pattern 1:
When your request body is modified, please modify it as follows.
From:
"Content-Type: application/pdf\r\n\r\n" +
fileContent + "\r\n" +
To:
"Content-Transfer-Encoding: base64\r\n\r\n" +
Utilities.base64Encode(fileContent.getBytes()) + "\r\n" +
In your script, you are using text data as the request body. So, I used the above modification.
Pattern 2:
As another pattern, when your fileContent of Blob is directly used in the request body, please modify it as follows.
From:
var header = {
"Authorization": "Bearer " + service.getAccessToken(),
"Content-Type": "multipart/related; boundary=<BOUNDARY_STRING>"
};
var body = "--<BOUNDARY_STRING>\r\n" +
"Content-Type: application/json; charset=UTF-8\r\n\r\n" +
JSON.stringify(fileMetadata) + "\r\n" +
"--<BOUNDARY_STRING>\r\n" +
"Content-Type: application/pdf\r\n\r\n" +
fileContent + "\r\n" +
"--<BOUNDARY_STRING>--";
let response = UrlFetchApp.fetch(url, {
method: "post",
headers: header,
payload: body,
muteHttpExceptions: true
});
To:
let response = UrlFetchApp.fetch(url, {
payload: {
metadata: Utilities.newBlob(JSON.stringify(fileMetadata), "application/json"),
file: fileContent,
},
headers: { authorization: "Bearer " + service.getAccessToken() },
muteHttpExceptions: true
});
In this case, the structure of multipart/form-data is automatically created on the internal server side.
This modification can obtain the same result as the above one.
Note:
In this modification, it supposes that your value of fileContent is the correct blob, and also, it supposes that your access token of "Bearer " + service.getAccessToken() can be used for uploading the file to Google Drive. Please be careful about this.
Reference:
fetch(url, params)
This post is following my previous one ->
How to turn Postman API's request into Apps Script code?
I'm trying to convert 3 API requests into a single piece of code via Google Apps Script.
The purpose is to export automatically a set of data from my CRM platform into Google Sheet.
Based on my CRM platform documentation, I need to do 3 API requests in order to get the final set of data I want:
Creating an export job (using a POST request)
Retrieve the job status from previous request (using GET request)
Retrieve the exported data (using GET request)
Below is the code for the 1st API request (kindly provided by Tanaike):
function exportjob() {
var url = 'https://api.intercom.io/export/content/data';
var options = {
"method": "post",
"headers": {
"Authorization": "Bearer 123456789",
"Accept": "application/json",
},
"contentType": "application/json",
"payload": JSON.stringify({
"created_at_after": 1654041600,
"created_at_before": 1656547200
})
}
var response = UrlFetchApp.fetch(url, options);
console.log(response.getContentText())
}
The result of this request is the following
Info {"job_identifier":"5gf58ty4y5y45229r", "status":"pending", "download_url":"", "download_expires_at":""}
Next step is to add the 2 other API requests in my script (Retrieve the job status and retrieve the data).
However I have a couple of issues:
For the 2nd API request
How do I include the job_identifier ID in my code? (given in the 1st request)
For the 3rd API request
How do I retrieve the data with the URL provided in the 2nd API request?
The data comes as ZIP file, how do I convert it to CSV so Google Sheet can open it?
How do get the final set of data to be exported in a google sheet?
Apologies for the long post, I'm trying to summarize the documentation from my CRM platform as much as I can.
Any help on how to retrieve the job POST and retrieve the set of data to a google sheet via CSV would be highly appreciated.
Thank you
1. About For the 2nd API request
If this is for Retrieve a job status, it seems that the sample curl command is as follows.
curl https://api.intercom.io/export/content/data/v5xsp8c19ji0s82 \
-X GET \
-H 'Authorization:Bearer <Your access token>'
-H 'Accept: application/json'
It seems that the value of job_identifier can be retrieved from your 1st request shown in your question. Ref
When this request is done for the first time, it seems that status returns pending. By this, it seems that until status is changed to complete, it is required to wait. Ref
2. About For the 3rd API request
If this is for Retrieve the exported data, it seems that the sample curl command is as follows.
curl https://api.intercom.io/download/content/data/xyz1234 \
-X GET \
-H 'Authorization:Bearer <Your access token>' \
-H 'Accept: application/octet-stream’
In this case, the document says Your exported message data will be streamed continuously back down to you in a gzipped CSV format.. I thought that in this case, the returned data might be able to be ungzipped with Utilities.ungzip, and the ungzipped data might be able to be parsed with Utilities.parseCsv.
3. Using your 3 requests, how about the following sample script?
function exportjob2() {
var accessToken = "###your access token###"; // Please set your access token.
// 1st request: This is from https://stackoverflow.com/a/73032528
var url1 = 'https://api.intercom.io/export/content/data';
var options1 = {
"method": "post",
"headers": {
"Authorization": "Bearer " + accessToken,
"Accept": "application/json",
},
"contentType": "application/json",
"payload": JSON.stringify({
"created_at_after": 1654041600,
"created_at_before": 1656547200
})
}
var response1 = UrlFetchApp.fetch(url1, options1);
var { job_identifier } = JSON.parse(response1.getContentText());
// 2nd request <--- Modified
var url2 = 'https://api.intercom.io/export/content/data/' + job_identifier;
var options2 = {
"headers": {
"Authorization": "Bearer " + accessToken,
"Accept": "application/json",
},
}
var response2 = UrlFetchApp.fetch(url2, options2);
// console.log(response2.getContentText()); // for debug.
var { download_url, status } = JSON.parse(response2.getContentText());
while (status == "pending") {
Utilities.sleep(5000); // Please adjust this value. The current wait time is 5 seconds.
response2 = UrlFetchApp.fetch(url2, options2);
// console.log(response2.getContentText()); // for debug.
var obj = JSON.parse(response2.getContentText());
status = obj.status;
download_url = obj.download_url;
}
if (!download_url) {
throw new Error("download_url has no value.");
}
// 3rd request
var options3 = {
"headers": {
"Authorization": "Bearer " + accessToken,
"Accept": "application/octet-stream",
},
}
var response3 = UrlFetchApp.fetch(download_url, options3);
var blob = response3.getBlob().setContentType("application/zip");
var csvs = Utilities.unzip(blob);
// Create a new Spreadsheet and put the CSV data to the 1st tab.
var ss = SpreadsheetApp.create("sample Spreadsheet");
csvs.forEach((b, i) => {
var ar = Utilities.parseCsv(b.getDataAsString());
var sheet = i == 0 ? ss.getSheets()[i] : ss.insertSheet("sample" + (i + 1));
sheet.getRange(1, 1, ar.length, ar[0].length).setValues(ar);
});
}
I'm not sure whether the downloaded the gzip file has the correct mimeType. So I added the mimeType like var blob = response3.getBlob().setContentType("application/x-gzip").
Note:
When this script is run, the flow of your showing question is done. But, I cannot test this API because I have no account. So, when an error occurs, please check each value and your access token again. And, please provide the error message. By this, I would like to confirm it.
I thought that the value of download_url returned from the 1st request might be the same with the value of download_url returned from 2nd request. But, I cannot test this API because I have no account. So, please check it, and when my understanding is correct, you can modify the above script.
This sample script creates a new Spreadsheet. But, if you want to put the CSV data to the existing Spreadsheet, please modify the above script.
Reference:
fetch(url, params)
This question already exists:
How to convert this curl example to google apps script / javascript? [duplicate]
Closed 1 year ago.
So this part works:
function authenticateToggl()
{
url = "https://api.track.toggl.com/api/v8/me";
//Put it all together.
var options =
{
'method' : 'get',
'headers' : {"Authorization": 'Basic ' + Utilities.base64Encode('email#gmail.com:mypassword')},
};
//Make Login call to When I work.
var responseGetPlan = UrlFetchApp.fetch(url, options);
var strResponseGetPlan = responseGetPlan.getContentText();
Logger.log('Get Plan Response: ' + strResponseGetPlan); //Log response.
var parsedData = JSON.parse(strResponseGetPlan); //Parse into JSON format.
var strId = parsedData.data.api_token;
return strId;
}
That returned me a string api token, which I can use in api curl calls like this:
curl -v -u c8f4c3TOKENe5a580bfeab:api_token -X GET https://api.track.toggl.com/api/v8/workspaces/1001455/projects
Doing that I appropriately got back my list of projects per the api docs.
But when I tried to translate that into google apps script, it fails. I'm sure it is that my header is formatted wrong?
function nextStep(){
UrlFetchApp.fetch('https://api.track.toggl.com/api/v8/workspaces/1001455/projects', {//
'headers': {'Authorization': "Basic c8f4c3TOKENe5a580bfeab:api_token"},
'method':'get'
});
}
I get back a 403 authentication error. Does the word basic not apply here? using the same url, method, and token.
It seems you need to convert your string token to base64 encoding,
function nextStep(){
//API Key requires base64 encoding. Use Google Apps Scripts built in method
var base64token = Utilities.base64Encode("c8f4c3TOKENe5a580bfeab:api_token");
UrlFetchApp.fetch('https://api.track.toggl.com/api/v8/workspaces/1001455/projects', {
'headers': {'Authorization': "Basic "+base64token},
'method':'get'
});
}
Additional References:
Github - Google-Apps-Scripts-Toggl-Request
Github - toggl-appscript
I am not able to test my scripts.
If you can help with the code here - it is not working
As per documentation - https://developers.google.com/youtube/v3/docs/videos/insert
if (serviceYT.hasAccess()) {
url = 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&mine=true&key='+ API_KEY;
var data;
data = '{"snippet":{"title":"testtitle","description":"testdes","categoryId":"19","tags":["asdf","sadfds"]},"status":{"privacyStatus":"public"}}';
var options = {
'headers': {
'Authorization': 'Bearer ' + serviceYT.getAccessToken()
,'Accept': 'application/json'
},
'contentType': 'application/json',
'method' : 'POST',
'payload' : data,
'muteHttpExceptions' : true
};
//execute and handle the response
var response = UrlFetchApp.fetch(url, options);
var responseCode = response.getResponseCode();
var result = JSON.parse(response.getContentText());
Logger.log(result);
}
My questions -
where to put the video?
Resolution of the error I am getting:
{error={message='status', code=400.0, errors=[{reason=unexpectedPart, domain=youtube.part, message='status', location=part, locationType=parameter}]}}
A youtube object (a empty video basically) is successfully added to the youtube which is visible via studio. its a empty video but the title, description, status etc... other things are correcty setup.
Any ideas on how to add the media or video??
Where in the payload the video blob has to be added?
I am not able to get it in the documentation.
https://developers.google.com/youtube/v3/docs/videos/insert
I believe your goal is as follows.
You want to upload a movie file to your YouTube channel with the title, description, and so on.
The movie file is put in your Google Drive.
You want to achieve this by directly requesting to the endpoint using UrlFetchApp.
In this case, how about the following modified script?
Modified script:
Before you use this script, please enable YouTube Data API v3 and add the scope of https://www.googleapis.com/auth/youtube. And please set the file ID of the movie file in your Google Drive to const fileId = "###";.
serviceYT.getAccessToken() is from your script.
function myFunction() {
const fileId = "###"; // Please set the file ID of movie file on the Google Drive.
const metadata = {
"snippet": { "title": "testtitle", "description": "testdes", "categoryId": "19", "tags": ["asdf", "sadfds"] },
"status": { "privacyStatus": "public" }
};
const url = 'https://www.googleapis.com/upload/youtube/v3/videos?part=snippet%2Cstatus';
const file = DriveApp.getFileById(fileId);
const boundary = "xxxxxxxxxx";
let data = "--" + boundary + "\r\n";
data += "Content-Type: application/json; charset=UTF-8\r\n\r\n";
data += JSON.stringify(metadata) + "\r\n";
data += "--" + boundary + "\r\n";
data += "Content-Type: " + file.getMimeType() + "\r\n\r\n";
const payload = Utilities.newBlob(data).getBytes().concat(file.getBlob().getBytes()).concat(Utilities.newBlob("\r\n--" + boundary + "--").getBytes());
const options = {
method: "post",
contentType: "multipart/form-data; boundary=" + boundary,
payload: payload,
headers: { 'Authorization': 'Bearer ' + serviceYT.getAccessToken() },
muteHttpExceptions: true,
};
const res = UrlFetchApp.fetch(url, options).getContentText();
console.log(res);
}
In your script, you are using the endpoint of url = 'https://youtube.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&mine=true&key='+ API_KEY;. Your data has the properties of snippet and status. In this case, it is required to use the endpoint of https://www.googleapis.com/upload/youtube/v3/videos?part=snippet%2Cstatus.
In order to upload the movie file, it is required to request with multipart/form-data.
When this script is run, the movie file on Google Drive is uploaded to YouTube.
Note:
In the current stage, even when "privacyStatus": "public" is used, the uploaded video is not public. About this, you can see it at the official document as follows. Ref
All videos uploaded via the videos.insert endpoint from unverified API projects created after 28 July 2020 will be restricted to private viewing mode. To lift this restriction, each API project must undergo an audit to verify compliance with the Terms of Service. Please see the API Revision History for more details.
When you use the YouTube API at Advanced Google services, you can use the following simple script. Ref
function myFunction2() {
const fileId = "###"; // Please set the file ID of movie file on the Google Drive.
const res = YouTube.Videos.insert({
"snippet": { "title": "testtitle", "description": "testdes", "categoryId": "19", "tags": ["asdf", "sadfds"] },
"status": { "privacyStatus": "public" }
}, ["snippet", "status"], DriveApp.getFileById(fileId).getBlob());
console.log(res);
}
References:
Videos: insert
Multipart-POST Request Using Google Apps Script
fetch(url, params)
I'm trying to test the endpoints of an API and all the responses return blank with no errors.
I've tested using curl and the response actually has data in:
curl --request GET https://myapiurl.com/endpoint --header "authorization: bearer TOKEN"
Here's the segment of GAS code. The token property is set when I run a POST call to retrieve it, it expires after a while:
var url = 'https://myapiurl.com/endpoint';
var token = 'bearer TOKEN';
var headers = {
'authorization': token
};
var options = {
'headers' : headers
};
var response = UrlFetchApp.fetch(url,options);
var data = JSON.parse(response.getContentText());
The response should be a JSON object with a full list of keys/values. The response from curl is exactly what it should be, but the GAS version is blank, no errors.
I am completely at a loss, any help would be much appreciated!