(NodeJs) Drive revision update - node.js

I'm trying to download a specific revision from google drive files and update that revision as the latest. But after doing so I'm getting some unformatted text over the sheets as shown in the picture here. Can someone help with this?
I'm trying to achieve a revision update with a specific revision download as a blob and updating this downloaded version to drive for that file.
const auth = new google.auth.GoogleAuth({
credentials: { ...Credentials },
scopes: [
'https://www.googleapis.com/auth/spreadsheets',
'https://www.googleapis.com/auth/drive',
],
});
const drive: drive_v2.Drive = google.drive({
version: 'v2',
auth,
});
const driveV3: drive_v3.Drive = google.drive({
version: 'v3',
auth,
});
const list = await driveV3.revisions.list({
fileId: process.env.PRODUCTS_SPREAD_SHEET_ID,
});
console.log(list.data.revisions);
const response = await drive.revisions.get({
fileId: process.env.PRODUCTS_SPREAD_SHEET_ID,
revisionId: '244',
});
const endPoint =
response.data.exportLinks[
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
];
console.log(endPoint);
const apiRes = await makeGetApiCall(
endPoint,
{ Authorization },
'blob'
);
Here I'm updating the file content that I've downloaded above as blob in variable apiRes.
const aa = await driveV3.files.update({
fileId: process.env.PRODUCTS_SPREAD_SHEET_ID,
media: {
body: apiRes,
mimeType: 'application/vnd.google-apps.spreadsheet'
}
});

Related

Update "shared" state of a file/folder with google drive API

I can't resolve how to change "shared" state to false with the google drive API.
Here is what i do:
I fetch all my folders & files witch are public with [this
one][1]
(I use q:"'me' in owners and visibility != 'limited'" filter)
I take file/folder ID and put it inside [this other one][2]
Inside the response object I got this line i want to change : "shared": true
I don't where I can set it to false, is someone getting any idea?
Have a nice day
Edit: I use NodeJs (netlify function), here is my code to get my files & folders :
const { google } = require('googleapis')
const CLIENT_ID = process.env.CLIENT_ID
const CLIENT_SECRET = process.env.CLIENT_SECRET
const REDIRECT_URI = 'https://developers.google.com/oauthplayground'
const REFRESH_TOKEN = process.env.REFRESH_TOKEN
exports.handler = async () => {
const oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
oauth2Client.setCredentials({ refresh_token: REFRESH_TOKEN })
const drive = google.drive({
version: 'v3',
auth: oauth2Client,
})
try {
const result = await drive.files.list({
q:"'me' in owners and visibility != 'limited'"
})
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ ...result, Body: result.toString('utf-8') })
}
} catch (e) {
console.log(e.message)
return { statusCode: 500, body: e.message }
}
}
To change visibility ("shared": true -> "shared": false),
I tried #Tanaike answer with :
const fetch = require('node-fetch')
const API_ENDPOINT_A = 'DELETE https://www.googleapis.com/drive/v3/files/'
const API_ENDPOINT_B = '/permissions/anyoneWithLink'
exports.handler = async (event) => {
try {
const itemId = '1-7ESYk_zKJ5Sdfg_z-XiuoXxrKKpHwSa' // event.queryStringParameters.itemId
const response = await fetch(API_ENDPOINT_A + itemId + API_ENDPOINT_B)
const data = await response.json()
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({ data })
}
} catch (error) {
console.log(error)
return {
statusCode: 500,
body: JSON.stringify({ error: 'Failed fetching data' })
}
}
}
But i don't know how I can pass my private info (api key...), I used OAuth 2 for fetch, should I use it too for edit visibility ?
[1]: https://developers.google.com/drive/api/v2/reference/files/list
[2]: https://developers.google.com/drive/api/v2/reference/files/patch
If I understand correctly, you are unable to see the place where you can change the file to "shared": true. If so, in the same link you provided from the official documentation you can find it in the "Request body".
From I fetch all my folders & files witch are public with this one (I use q:"'me' in owners and visibility != 'limited'" filter), when you want to change the permission of publicly shared to the restricted, you can achieve this using Drive API as follows.
Request:
Permissions: delete is used.
DELETE https://www.googleapis.com/drive/v3/files/###fileId###/permissions/anyoneWithLink
Sample curl:
curl --request DELETE \
-H 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
'https://www.googleapis.com/drive/v3/files/###fileId###/permissions/anyoneWithLink'
If the file is publicly shared and not shared with other specific users, when this request is run, "shared": true is changed to "shared": false.
Note:
If you want to remove the permission of the specific user, you can achieve this as follows.
Retrieve the permission ID using Permissions: list.
curl \
-H 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
'https://www.googleapis.com/drive/v3/files/###fileId###/permissions'
Using the retrieved permission ID, you can delete the permission as follows.
curl --request DELETE \
-H 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \
'https://www.googleapis.com/drive/v3/files/###fileId###/permissions/###permissionId###'
References:
Permissions: delete
Permissions: list
Added:
From your following replying,
I actualy use nodejs for drive API, I tried the http solution, but idk how can i pass my clientId and secretId (error 500), You can find my code on my question, i edited it
When you want to achieve to delete permissions with googleapis for Node.js, you can use the following script. In this case, please include the scope of https://www.googleapis.com/auth/drive.
Sample script:
const fileId = "###"; // Please set your file ID.
const drive = google.drive({ version: "v3", auth }); // Please use your client here.
drive.permissions.delete(
{
fileId: fileId,
permissionId: "anyoneWithLink",
},
(err, res) => {
if (err) {
console.error(err);
return;
}
console.log(res.data); // In this case, no values are returned.
}
);
In this sample script, the permission of the publicly shared file is deleted.
I got it working with the following code :
const result = await drive.permissions.delete({
fileId:"YOUR FILE ID",
permissionId:"anyoneWithLink"
})
Here is the full update code :
const { google } = require('googleapis')
const CLIENT_ID = process.env.CLIENT_ID
const CLIENT_SECRET = process.env.CLIENT_SECRET
const REDIRECT_URI = 'https://developers.google.com/oauthplayground'
const REFRESH_TOKEN = process.env.REFRESH_TOKEN
exports.handler = async () => {
const oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
oauth2Client.setCredentials({ refresh_token: REFRESH_TOKEN })
const drive = google.drive({
version: 'v3',
auth: oauth2Client,
})
try {
const result = await drive.permissions.delete({
fileId:"YOUR FILE ID",
permissionId:"anyoneWithLink"
})
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({ ...result, Body: result.toString('utf-8') })
}
} catch (e) {
console.log(e.message)
return { statusCode: 500, body: e.message }
}
}

How to upload a file from an export in Google Drive API without saving the file

I'm exporting a file sheet file from drive and uploading it back to drive in pdf format. The problem is that in order to upload it I need to save it to file on a server first.
I've tried to read a response from drive.files.export in fs.createReadStream, but it didn't work. Is there another way?
const res = await drive.files.export(
{ fileId, mimeType: "application/pdf" }
);
var media = {
mimeType: 'application/pdf',
body: fs.createReadStream(res) // TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string or an instance of Buffer or URL. Received an instance of Object
};
const resCreate = await drive.files.create({
uploadType: "media",
media: media,
resource: fileMetadata,
fields: "id"
}, function (err, file) {...});
I believe your goal as follows.
You want to export a Google Spreadsheet as the PDF data and want to upload it to Google Drive.
At that time, you want to achieve this without creating a file.
You want to achieve this using googleapis for Node.js.
In this case, how about the following modification?
Modified script:
const res = await drive.files.export(
{ fileId, mimeType: "application/pdf" },
{ responseType: "stream" }
);
var media = {
mimeType: "application/pdf",
body: res.data,
};
const resCreate = await drive.files.create({
uploadType: "media",
media: media,
resource: fileMetadata,
fields: "id",
});
console.log(resCreate.data.id);
Before you use this modified script, please set fileId and fileMetadata.
The exported file is retrieved the stream data with responseType: "stream". By this, the returned data can be used for media.
Reference:
google-api-nodejs-client

How to change google profile picture via API?

I am looking for a way how to change user profile photo programmatically.
Generally, I need to be able to change avatars of domain GSuite users, but if there are API for changing my own avatar is also good.
Had tried already:
https://developers.google.com/admin-sdk/directory/v1/reference/users/photos/update - this API is working but only changes the small icon of a user in admin panel itself. The main profile picture remains the same.
https://developers.google.com/people/api/rest/v1/people/updateContactPhoto - this API throws me an error Resource name "people/10244712xxxxx1564465" is not a valid contact person resource. when I am trying to replace my own profile image. people/me also isn't working.
There are Contacts API, but I am trying to change my own (or impersonated one) profile image, so I believe this API isn't for my case.
If you can point me in some direction to search I'll be very glad. I just do not believe that there is no way to change avatar, it can't be for real.
Update:
Checked Contact API - also doesn't work, looks like it changes an avatar only on me in my contact list, nobody else doesn't see changes, and the main profile picture remains the same.
Code:
// admin directory way
const { google } = require('googleapis');
const SafeBase64 = require('url-safe-base64');
const GOOGLE = require(process.env.GOOGLEAPIS_VAULT);
const encodedPhoto = SafeBase64.encode('R0lGODdhBAAEAIABAAMA/////ywAAAAABAAEAAACBkRiYJgHBQA7');
const JWTClient = new google.auth.JWT(
GOOGLE.client_email,
null,
GOOGLE.private_key,
[
'https://www.googleapis.com/auth/admin.directory.user',
],
'gsync#***.com', // sub
);
const directory = google.admin({ version: 'directory_v1', auth: JWTClient });
directory.users.photos.update({}, ctx.params.email);
const res = await directory.users.photos.update({
userKey: ctx.params.email,
resource: {
photoData: encodedPhoto,
},
});
// Contacts API way
const { google } = require('googleapis');
const SafeBase64 = require('url-safe-base64');
const GOOGLE = require(process.env.GOOGLEAPIS_VAULT);
const JWTClient = new google.auth.JWT(
GOOGLE.client_email,
null,
GOOGLE.private_key,
[
'https://www.google.com/m8/feeds/',
],
ctx.params.email, //sub
);
const res1 = await JWTClient.requestAsync({
headers: {
'GData-Version': 3.0,
},
params: {
alt: 'json',
q: 'arsenyp#***.com',
},
url: `https://www.google.com/m8/feeds/contacts/${ctx.params.email}/full`,
});
const contactIdFull = res1.data.feed.entry.filter((c) => c.gd$email[0].address === ctx.params.email)[0].id.$t;
const [, contactId] = /\/base\/([a-z0-9]+)$/.exec(contactIdFull);
// https://www.google.com/m8/feeds/contacts/{userEmail}/full/{contactId}
const res2 = await JWTClient.requestAsync({
headers: {
'GData-Version': 3.0,
},
params: {
alt: 'json',
},
url: `https://www.google.com/m8/feeds/contacts/${ctx.params.email}/full/${contactId}`,
});
const { href: image, gd$etag: etagJ } = res2.data.entry.link.filter((l) => l.rel === 'http://schemas.google.com/contacts/2008/rel#photo')[0];
const res3 = await axios({
method: 'GET',
url: image,
headers: {
Authorization: `Bearer "${(await JWTClient.getAccessTokenAsync()).token}"`,
},
responseType: 'arraybuffer',
});
const etag = JSON.parse(etagJ);
// PUT /m8/feeds/photos/media/default/contactId
// If-match: Etag
// Content-Type: image/*
const res4 = await axios({
method: 'PUT',
url: `https://www.google.com/m8/feeds/photos/media/default/${contactId}`,
headers: {
Authorization: `Bearer "${(await JWTClient.getAccessTokenAsync()).token}"`,
'Content-Type': 'image/png',
},
// responseType: 'arraybuffer',
data: Buffer.from('R0lGODdhBAAEAIABAAMA/////ywAAAAABAAEAAACBkRiYJgHBQA7', 'base64'),
});
// People API way (won't work, throwing an error)
const userId = '1024471xxxxx251564465';
const JWTClient = new google.auth.JWT(
GOOGLE.client_email,
null,
GOOGLE.private_key,
[
'https://www.googleapis.com/auth/contacts',
'profile',
],
ctx.params.email, // sub
);
const people = google.people({ version: 'v1', auth: JWTClient });
const res = await people.people.updateContactPhoto({
resourceName: `people/${userId}`,
resource: {
photoBytes: SafeBase64.encode('R0lGODdhBAAEAIABAAMA/////ywAAAAABAAEAAACBkRiYJgHBQA7'),
personFields: 'photos',
},
sources: [
'READ_SOURCE_TYPE_PROFILE',
],
});
Ok, so as #Sudhakar mention there is no such API.
But I've found that admin directory API actually changes the profile in most places.
The trick in that the user needs manually once remove the avatar from https://aboutme.google.com/ Profile picture because this photo has top priority against admin directories one.
If the user removes the photo from aboutme.google.com then changes it via admin directory v1 API this photo is seen in the calendar, Gmail, contacts...
So check my first code sample:
// admin directory way
const { google } = require('googleapis');
const SafeBase64 = require('url-safe-base64');
const GOOGLE = require(process.env.GOOGLEAPIS_VAULT);
const encodedPhoto = SafeBase64.encode('R0lGODdhBAAEAIABAAMA/////ywAAAAABAAEAAACBkRiYJgHBQA7');
const targerUser = 'arsenyp#***.com';
const JWTClient = new google.auth.JWT(
GOOGLE.client_email,
null,
GOOGLE.private_key,
[
'https://www.googleapis.com/auth/admin.directory.user',
],
'gsync#***.com', // sub - this is service user with proper permissions, not a target user
);
const directory = google.admin({ version: 'directory_v1', auth: JWTClient });
directory.users.photos.update({}, targerUser);
const res = await directory.users.photos.update({
userKey: targerUser,
resource: {
photoData: encodedPhoto,
},
});

Google drive API: can't upload files to the root folder using nodejs

I'm using google drive API to upload files.
It works if I specify the id of subfolders. But when I pass the id of the root folder, the files are not uploaded.
Here is my request:
drive.files.create(
{
auth: this.ggToken,
fields: 'id',
supportsAllDrives: true,
media: {
body: this.convertBufferToStream(file.buffer),
},
requestBody: {
mimeType: file.mimetype,
name: file.originalname,
parents: ['root', '0AASRHiHHtzxrUk9PVA'],
},
},
(e: Error, f: any) => {
if (e) {
console.error(e);
}
console.log(f);
},
);
0AASRHiHHtzxrUk9PVA is the ID of the root folder (I get it by using drive.files.get API)
So what's wrong with my code? How can I upload files to the root folder?
Thanks.
Update 1
Here is my script:
ggToken: JWT;
constructor() {
this.ggToken = new google.auth.JWT(
process.env.GG_DRIVE_CLIENT_EMAIL,
null,
process.env.GG_DRIVE_PRIVATE_KEY,
['https://www.googleapis.com/auth/drive'],
null,
);
}
async uploadFiles(file: any) {
const credentials = await this.ggToken.authorize();
this.ggToken.setCredentials(credentials);
const uploadedFile = await drive.files.create({
auth: this.ggToken,
fields:
'id, name, mimeType, webViewLink, webContentLink, iconLink, size, originalFilename',
media: {
body: this.convertBufferToStream(file.buffer),
},
requestBody: {
mimeType: file.mimetype,
name: file.originalname,
parents: ['root'],
properties: {},
},
});
return {
driveResource: uploadedFile.data,
};
}
I got process.env.GG_DRIVE_CLIENT_EMAIL and process.env.GG_DRIVE_PRIVATE_KEY from the json file after creating server account key.
Thank #Tanaike very much.
Here is his answer:
Thank you for replying and adding the script. I could understand about the reason of your issue. The service account is different from your own account. So the Google Drive of service account is different from that of your account. By this, when a file is uploaded to the root folder using your script, the file is uploaded to the root folder of the Drive of service account. By this, the file cannot be seen at the drive of your account. If you want to upload the file to the root folder of the Drive of your account, please use OAuth2
With Oauth2, I can upload files to the root folder.
Here is my code:
oauth2Client: OAuth2Client;
constructor() {
this.oauth2Client = new google.auth.OAuth2({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: 'https://developers.google.com/oauthplayground',
});
this.oauth2Client.setCredentials({
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
});
}
async uploadFiles(file: any) {
const uploadedFile = await drive.files.create({
auth: this.oauth2Client,
fields:
'id, name, mimeType, webViewLink, webContentLink, iconLink, size, originalFilename',
media: {
body: this.convertBufferToStream(file.buffer),
},
requestBody: {
mimeType: file.mimetype,
name: file.originalname,
parents: ['root'],
},
});
return {
driveResource: uploadedFile.data,
};
}

Can't see the video uploaded through youtube-api

The code doesn't give any errors but on the youtube page there is nothing. The token is ok and I can see the log saying it has finished but on youtube nothing.
How much time it take to display the video on youtube?
const Youtube = require("youtube-api"),
fs = require("fs"),
readJson = require("r-json"),
Logger = require("bug-killer"),
prettyBytes = require("pretty-bytes");
// I downloaded the file from OAuth2 -> Download JSON
const CREDENTIALS = readJson(`${__dirname}/credentials.json`);
// Authenticate
let oauth = Youtube.authenticate({
type: "oauth",
client_id: CREDENTIALS.web.client_id,
client_secret: CREDENTIALS.web.client_secret,
redirect_url: CREDENTIALS.web.redirect_uris[0]
});
//the token obtained with getToken.js script
var tokens = readJson(`${__dirname}/tokens.json`);
//set the token
oauth.setCredentials(tokens);
var req = Youtube.videos.insert({
resource: {
snippet: {
title: "Testing",
description: "Test video upload via YouTube API"
},
status: {
privacyStatus: "public"
}
},
part: "snippet,status",
media: {
body: fs.createReadStream("video.mp4")
}
}, (err, data) => {
console.log("Done.");
process.exit();
});
setInterval(function() {
Logger.log(`${prettyBytes(req.req.connection._bytesDispatched)} bytes uploaded.`);
}, 250);
Resolved, you have to enable the youtube Api v3 for your google app. Stupid issue.

Resources