Able to create Google Docs but couldn't create Google Sheets - google-drive-api

I am making a feature in my Java backend to create a Google Sheet.
Now in that backend, it already has a function to create Google Docs and it is working well. Following is the function to create a Google Doc.
public String createFileFromHtml(Project project, String html) throws DocumentGatewayException {
// Determine the google drive ID for the given areaId
String areaId =
Strings.isNullOrEmpty(project.getAreaId()) ? Constants.TEST_AREA : project.getAreaId();
String areaGoogleDriveId = null;
try {
if (areaIdToGoogleDriveIdMap.isEmpty() || !areaIdToGoogleDriveIdMap.containsKey(areaId)) {
String pageToken = null;
do {
String query = "'" + ERD_GOOGLE_DRIVE_ID + "' in parents";
query += " and mimeType = '" + FOLDER_MIME_TYPE + "'";
query += "and trashed = false";
FileList result =
drive
.files()
.list()
.setQ(query)
.setPageToken(pageToken)
.setFields("nextPageToken, files(id, name)")
.execute();
for (File file : result.getFiles()) {
areaIdToGoogleDriveIdMap.put(file.getName(), file.getId());
}
pageToken = result.getNextPageToken();
} while (pageToken != null);
}
} catch (IOException e) {
throw new DocumentGatewayException(e);
}
areaGoogleDriveId = areaIdToGoogleDriveIdMap.get(areaId);
Preconditions.checkArgument(
!Strings.isNullOrEmpty(areaGoogleDriveId), areaId + " needs to have a google drive folder");
// Generate metadata to be used to create the file using the google drive ID above.
File fileMetadata = new File();
fileMetadata.setName(project.getTitle());
fileMetadata.setMimeType("application/vnd.google-apps.document");
fileMetadata.setParents(Collections.singletonList(areaGoogleDriveId));
try {
// Create file.
File file =
drive
.files()
.create(
fileMetadata,
new ByteArrayContent("text/html", html.getBytes(StandardCharsets.UTF_8)))
.setFields("id")
.execute();
// Make all authors writers
if (!project.getAuthorIds().isEmpty()) {
for (String authorId : project.getAuthorIds()) {
processor.setWritter(project.getUuid(), file.getId(), authorId, 0);
}
// Set owner
String authorId = EmailUtils.AuthorNameToEmail(project.getAuthorIds().get(0));
processor.setOwner(project.getUuid(), file.getId(), authorId, 0);
}
// Set commenter
processor.setCommenter(project.getUuid(), file.getId(), 0);
return file.getId();
} catch (IOException e) {
logger.error(
"Error creating document", KeyValue.string("project_uuid", project.getUuid()), e);
throw new DocumentGatewayException(e);
}
}
I wanted to create a Google Sheet instead. So I tried changing application/vnd.google-apps.document to application/vnd.google-apps.spreadsheet. But after this change, it was still creating Google Doc instead of Google Sheet.
So anyones knows why? I just need to create a Google Sheet.
Google Drive API version: google-api-services-drive-v3-rev130-1.25.0
Thanks.

Related

How do I deploy a Google Script project so that all my Google Sheets can access it?

I have a simple VIN Decoder script that I built for my Vehicle DB Sheet. I want to allow other sheets to use the functions defined in the script without copying the code to the script containers for each spreadsheet. I guess I essentially want to have a private (to my account or domain) add-on. I have tried reading about how to deploy an add-on to Google Workplace but all the tutorials are either old or just provide sample code that doesn't answer how to do it. I am sure this is not a huge project to deploy this code as an add-on. Anyone?
Here is the code I am trying to deploy...
const nhtsaGateway = 'https://vpic.nhtsa.dot.gov/api/';
const nhtsaVINDecode = '/vehicles/DecodeVin/';
function decodeVIN(theVIN,theVariable) {
var response, jsonData, retValue, success;
success = false;
if (typeof(theVIN) === 'undefined') {
theVIN = 'WD4PF0CD3KP053982';
Logger.log('No VIN Submitted -- Assuming this is a test\nUsing Test VIN = [' + theVIN + ']');
}
response = UrlFetchApp.fetch(nhtsaGateway + nhtsaVINDecode + theVIN +'?format=JSON');
jsonData = JSON.parse(response.getContentText());
Logger.log(jsonData.Message);
if (typeof(theVariable) === 'undefined') {
Logger.log(jsonData);
return(jsonData);
}
jsonData.Results.every(function(element, index) {
Logger.log('<<<' + index + '>>>');
Logger.log(element.Value);
Logger.log(element.ValueId);
Logger.log(element.Variable);
Logger.log(element.VariableId);
if (element.Variable === theVariable) {
Logger.log('Found theVariable = ' + element.Variable);
retValue = element.Value;
success = true;
return (false);
} else {
return (true);
}
})
if (success) {
Logger.log(retValue);
return (retValue);
} else {
Logger.log('We should not be here --> ' + theVariable + ' <-- is not defined in the NHTSA response.');
}
}
function vinYear(theVIN) {return (decodeVIN(theVIN,'Model Year'))}
function vinMake (theVIN) {return (decodeVIN(theVIN,'Make'))}
function vinSeries (theVIN) {return (decodeVIN(theVIN,'Series'))}
function vinModel (theVIN) {return (decodeVIN(theVIN,'Model'))}
function vinGVWR (theVin) {return (decodeVIN('1FTYR2CM2KKB15306', 'Gross Vehicle Weight Rating From'))}
So the usage in the target spreadsheet would be this formula in a cell
=vinModel("1FTYR2CM2KKB15306")
you don't need to make an addon or an extension, just a library:
https://developers.google.com/apps-script/guides/libraries
Try adding the name of the library in front of your function after adding the library, e.g.
Mylibrary.decodeVIN()

Getting past permissions for a file through the API/Apps Script

I'm trying to create a list of files stored in my Google Drive and also a list of their current and previous permissions. Specifically, I want to create a list of files in my Google Drive which at any point in the past have had the 'Anyone with a link can view/edit (etc)' permission set.
I have created a Google Apps Script to do this and I can iterate through all the files OK and I can get files which currently have that permission set, but I can't see a way to get the history of the file's permissions.
I have found and activated the revisions list API: https://developers.google.com/drive/api/v2/reference/revisions/list
This gets revisions but I can't see anywhere that it lists the sharing history of a revision.
Is what I'm attempting to do possible?
It's definitely possible using the Drive Activity API. You can use the Quickstart for Google Apps Script to view all the activity of an item (file or folder) or done by a User. In this case I modified the Quickstart to show the Permissions changes of a given Drive Id.
function listDriveActivity() {
var request = {
itemName: "items/1bFQvSJ8pMdss4jInrrg7bxdae3dKgu-tJqC1A2TktMs", //Id of the file
pageSize: 10};
var response = DriveActivity.Activity.query(request);
var activities = response.activities;
if (activities && activities.length > 0) {
Logger.log('Recent activity:');
for (var i = 0; i < activities.length; i++) {
var activity = activities[i];
var time = getTimeInfo(activity);
var action = getActionInfo(activity.primaryActionDetail);
var actors = activity.actors.map(getActorInfo);
var targets = activity.targets.map(getTargetInfo);
if (action == "permissionChange"){ //Only show permissionChange activity
Logger.log(
'%s: %s, %s, %s', time, truncated(actors), action,
truncated(targets));
}
}
} else {
Logger.log('No activity.');
}
}
/** Returns a string representation of the first elements in a list. */
function truncated(array, opt_limit) {
var limit = opt_limit || 2;
var contents = array.slice(0, limit).join(', ');
var more = array.length > limit ? ', ...' : '';
return '[' + contents + more + ']';
}
/** Returns the name of a set property in an object, or else "unknown". */
function getOneOf(object) {
for (var key in object) {
return key;
}
return 'unknown';
}
/** Returns a time associated with an activity. */
function getTimeInfo(activity) {
if ('timestamp' in activity) {
return activity.timestamp;
}
if ('timeRange' in activity) {
return activity.timeRange.endTime;
}
return 'unknown';
}
/** Returns the type of action. */
function getActionInfo(actionDetail) {
return getOneOf(actionDetail);
}
/** Returns user information, or the type of user if not a known user. */
function getUserInfo(user) {
if ('knownUser' in user) {
var knownUser = user.knownUser;
var isMe = knownUser.isCurrentUser || false;
return isMe ? 'people/me' : knownUser.personName;
}
return getOneOf(user);
}
/** Returns actor information, or the type of actor if not a user. */
function getActorInfo(actor) {
if ('user' in actor) {
return getUserInfo(actor.user)
}
return getOneOf(actor);
}
/** Returns the type of a target and an associated title. */
function getTargetInfo(target) {
if ('driveItem' in target) {
var title = target.driveItem.title || 'unknown';
return 'driveItem:"' + title + '"';
}
if ('drive' in target) {
var title = target.drive.title || 'unknown';
return 'drive:"' + title + '"';
}
if ('fileComment' in target) {
var parent = target.fileComment.parent || {};
var title = parent.title || 'unknown';
return 'fileComment:"' + title + '"';
}
return getOneOf(target) + ':unknown';
}
Remember to enable the Drive Activity API in Resources > Advanced Google Services
In my example this returns the logs:
You can also look deeper into the Permissions by using the permissionChange Parameters in the query.
If you have a business/enterprise/edu account the admin audit logs will tell you this for 6 months of data. Or it will at least tell you when a permission was changed from x to y.
Can't think of a method for personal.

Retrieving files from a shared folder using Drive api V3,

I have read/write permissions to a shared folder with another Google user.
I want to list all the files (in all the folders) in that shared folder link using drive V3 API.
What is the best way to do this?
The solution currently employed is pretty slow in performance if the number of folders inside shared folders is large.
My Current solution:
1 - Finding all the folders in shared link
2 - Finding all files whose parent is folder
private void getFolderHierarchy(File Res, DriveService driveService, string localPath, string editorName, string projectName)
{
if (Res.MimeType == "application/vnd.google-apps.folder")
{
m_parent += Res.Name + #"\";
foreach (var res in ResourceFromFolder(driveService, Res.Id).ToList())
getFolderHierarchy(res, driveService, localPath, editorName, projectName);
m_parent = m_parent.Remove(m_parent.Length - Res.Name.Length - 1);
}
else if (Res.MimeType == "image/jpeg" || Res.MimeType == "image/png")
{
{
if (!m_DownloadedFromEditorFileId.Contains(Res.Id))
{
m_DownloadedGoogleFileId.Add(Res.Name);
m_parent = m_parent.Remove(m_parent.Length - Res.Name.Length);
}
}
}
}
public List<File> ResourceFromFolder(DriveService service, string folderId)
{
var request = service.Files.List();
request.PageSize = 100;
request.Q = "'" + folderId + "'" + " in parents and trashed=false";
request.Fields = "files(modifiedTime,id,parents,name,webContentLink,mimeType)";
List<File> TList = new List<File>();
do
{
var children = request.Execute();
foreach (var child in children.Files)
{
System.Threading.Thread.Sleep(10);
TList.Add(service.Files.Get(child.Id).Execute());
}
request.PageToken = children.NextPageToken;
} while (!String.IsNullOrEmpty(request.PageToken));
return TList;
}
The second option would be the best way to get the files from a shared folder. As stated in this related SO post use files/list with a parent query.
GET https://www.googleapis.com/drive/v2/files?q='FOLDERID'+in+parents&key={YOUR_API_KEY}
The speed will depend of how large/many the files under the that shared folder.
Hope this helps.

google drive api create sub folders

I need to create sub folders in google drive using google drive api added using nuget package in console application.
I can get the folder id of root folder. Can get children of rot folder, can also upload file in root folder. Only problem is creation of sub folders in folders.
for (int i = 1; i < array.Count(); i++)
{
var subfoldername = new Google.Apis.Drive.v2.Data.File { Title = array[i], MimeType = "application/vnd.google-apps.folder" };
ChildrenResource.ListRequest request = service.Children.List(rootfolderid);
ChildList children = request.Execute();
if (children.Items.Count > 0)
{
foreach (ChildReference c in children.Items)
{
Google.Apis.Drive.v2.Data.File file = service.Files.Get(c.Id).Execute();
if (file.MimeType == "application/vnd.google-apps.folder")
{
List<GoogleDriveFile> googledrive = new List<GoogleDriveFile>();
googledrive.Add(new GoogleDriveFile
{
OriginalFilename = file.OriginalFilename
});
}
}
}
else
{
// here need to add sub folder in folder, but this line adds folder at root
var result = service.Files.Insert(foldername).Execute();
}
Here is the way i do, when creating a sub folder in google drive it must need a parents. So before execute the string q, we need to search for the parent root id
string findRootId = "mimeType = 'application/vnd.google-apps.folder' and title ='" + RootFolder + "' and trashed = false";
IList<File> _RootId = GoogleDriveHelper.GetFiles(service, findRootId);
if (_RootId.Count == 0) {
_RootId.Add(GoogleDriveHelper.createDirectory(service, RootFolder, "", "root"));
Console.WriteLine("Root folder {0} was created.", RootFolder);
}
var id = _RootId[0].Id;
string Q = "mimeType = 'application/vnd.google-apps.folder' and '" + id + "' in parents and title ='" + GoogleDriveFolderName + "' and trashed = false";
You must add the property parents while creating a Folder.
parents[]
Collection of parent folders which contain this file.
Setting this field will put the file in all of the provided folders. On insert, if no folders are provided, the file will be placed in the default root folder.
Sample Code:
function createSubFolder() {
var body = new Object();
body.title = 'SubFolder';
body.parents = [{'id':'0B5xvxYkWPFpCUjJtZVZiMWNBQlE'}];
body.mimeType = "application/vnd.google-apps.folder";
console.log(body)
var request = gapi.client.request({
'path': '/drive/v2/files',
'method': 'POST',
'body': JSON.stringify(body)
});
request.execute(function(resp) { console.log(resp); });
}
I'm using Drive v2 in JavaScript
Hope this helps

Google scripts, get a drive file url

I need my script to load the URL of a drive file to be added to a spreadsheet. For whatever reason 'File.getUrl()' returns null, but everything else seems to be working.
For example, 'File.getName()' works. So am I missing a permission or something?
Here is my code (abridged):
function loadURL(folderPath, name) {
var folder = loadFolder(folderPath);
// Find file
var files = folder.searchFiles('modifiedDate >= "' + getDate() + '" and title = "' + name + '"'),
file, found = 0;
for (; files.hasNext(); found++) {
file = files.next();
}
if (found == 1) {
/////////////////////////////////
// Here is 'getUrl'
/////////////////////////////////
var url = file.getUrl();
Logger.log("Loaded: " + url);
return url;
} else {
throw "Error loading file";
}
}
/**
* ** NOTE: THIS SEEMS TO BE WORKING FINE **
* Load a folder
* #param {string} url
* #return {Folder}
*/
function loadFolder(url) {
var folders = url.split('/'),
folder = DriveApp.getRootFolder();
Logger.log("Loading URL: " + url);
for (var key in folders) {
var fName = folders[key];
if (fName.length > 0) {
var fIt = folder.getFoldersByName(fName),
found = 0;
Logger.log(" -> " + fName);
for (; fIt.hasNext(); found++) {
folder = fIt.next();
}
if (found == 0) {
throw "Could not find the folder '" + fName + "'";
} else if (found > 1) {
throw "Found multiple matches to the folder '" + fName + "'";
}
}
}
return folder;
}
And the log if you're curious:
[14-02-26 19:56:15:980 EST] Loading URL: Work/PDF/
[14-02-26 19:56:15:981 EST] -> Work
[14-02-26 19:56:16:120 EST] -> PDF
[14-02-26 19:56:16:450 EST] Loaded: null
And if I change getUrl to getName it (kinda) works
[14-02-26 20:10:20:588 EST] Loading URL: Work/PDF/
[14-02-26 20:10:20:589 EST] -> Work
[14-02-26 20:10:20:727 EST] -> PDF
[14-02-26 20:10:21:058 EST] Loaded: Test
var url = DocsList.getFileById(fileId).getUrl();
Was recommended as a workaround by a poster on the issue thread that zig posted.
So, in your case, you'd use:
for (; files.hasNext(); found++) {
file = files.next();
}
if (found == 1) {
/////////////////////////////////
// Here is 'getUrl'
/////////////////////////////////
var fileId = file.getId();//new line added to getId of the file
//modified url line to reference doc by id
var url = DocsList.getFileById(fileId).getUrl();
Logger.log("Loaded: " + url);
return url;
} else {
throw "Error loading file";
}
I didn't get to test this right now because it's late and I'd have to build a the right structure to test it properly, but since you already have the structure change those two commented lines and let me know if that worked.
Using the id instead of the url works:
var url = "https://docs.google.com/document/d/" + file.getId() + "/";
Today started an issue with drive getUrl, google is aware of it and working on it.
Does work using docList or drive advanced services.