Google Drive API to identify the changes in files - google-drive-api

I have a list of files and folder stored on a google drive folder. My objective is to identify any changes (modify/create/delete) related to any file/folder within a particular google drive folder i.e get the list of these file name and their drive path.
I have not registered any domain as our spring boot application is deployed on K8's pod and I do not want any notification on any domain or any webhook address. I only want the list of files being modified/deleted when I call the below method/api
Below is the code snippet for spring boot application.
#Component
public class DriveServiceProvider {
#Autowired
private DriveConfig dc;
public Drive getDriveService() throws IOException, GeneralSecurityException {
final GoogleCredentials credentials = GoogleCredentials
.fromStream(new ByteArrayInputStream(new JSONObject(dc.getCred()).toString().getBytes()))
.createScoped(Collections.singletonList("https://www.googleapis.com/auth/drive"));
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(credentials);
return new Drive.Builder(GoogleNetHttpTransport.newTrustedTransport(), JacksonFactory.getDefaultInstance(), requestInitializer)
.setApplicationName(ApplicationConstants.APPLICATION_NAME).build();
}
}
Controller:
#RestController
#RequestMapping("/admin/watch")
public class TestGoogleWatchController {
#Autowired
private DriveServiceProvider driveServiceProvider;
#PostMapping(value = "/googleWatchNotification")
#ResponseStatus(HttpStatus.OK)
public Channel getNotification() throws IOException, GeneralSecurityException {
try {
StartPageToken pageToken = driveServiceProvider.getDriveService().changes().getStartPageToken().execute();
String savedStartPageToken = pageToken.getStartPageToken();
System.err.println(" savedStartPageToken "+savedStartPageToken );
while (savedStartPageToken != null) {
ChangeList changes = driveServiceProvider.getDriveService().changes().list(savedStartPageToken).execute();
System.err.println("======= changes " + changes.toPrettyString());
System.err.println("============ size ::" + changes.getChanges().size());
System.err.println("============ ChangeList ::" + changes.getChanges());
for (Change changeObj : changes.getChanges()) {
System.err.println(" files "+changeObj.getFileId() + " Kind "+changeObj.getKind() + ", Team Drive ID "+changeObj.getTeamDriveId() + ", Type::"+changeObj.getType()+ ",File ::"+changeObj.getFile()+ ", Is Removed::"+changeObj.getRemoved()+ ",Time ::"+changeObj.getTime());
}
}
}catch(Exception ex) {
}
return null;
}
}
*Below is the output after executing:
savedStartPageToken 165
======= changes {
"changes" : [ ],
"kind" : "drive#changeList",
"newStartPageToken" : "165"
}
============ size ::0
============ ChangeList ::[]*
The savedStartPageToken is printing incrementing numeric value after we change anything in drive, however there the ChangeList is always blank.

I am glad that I am answering my own question. The issue was that I was passing the current token in the changeList api.
Actually, the solution is to find the changes since last token till the current token using a loop.

Related

Route exchange online public folder hierarchy requests failed with "Expected an item Id but received a folder Id"

Description: We are having the technical difficulties to route the public folder hierarchy if there is a Journal folder, and we are thinking the workaround to use grapy API to do this, but didn't find the information to use Graph API to access the public folder, anyone can share more information would be appreciated.
Environment: Exchange online public folder. And there is one journal folder and one sub folder under the journal folder.
Method: Folder.FindFolders EWS API to route the public folder hierarchy.
Exception: error code: ErrorCannotUseFolderIdForItemId, error: Microsoft.Exchange.WebServices.Data.ServiceResponseException: Expected an item Id but received a folder Id.
Following is the sample code:
public static void GetPublicFolders(Folder parentFolder, int pageSize, int offset, string parentFolderPath)
{
try
{
FindFoldersResults result;
do
{
FolderView folderView = new FolderView(pageSize, offset);
folderView.Traversal = FolderTraversal.Shallow;
result = parentFolder.FindFolders(folderView);
foreach (Folder folder in result)
{
var path = parentFolderPath + #"\" + folder.DisplayName;
System.Console.WriteLine(path);
GetPublicFolders(folder, pageSize, 0, path);
}
offset += pageSize;
}
while (result.MoreAvailable);
}
catch (Exception ex)
{
System.Console.WriteLine(ex);
}
}
Thank you.
Long
A fix is roling out!
This is a problem caused by the Microsoft Exchange WebServices (EWS) of Office365.
As per Microsoft they have found the failure on their Serverside.
Take a look at the comment from Davster (a MS developer) at GitHub (GitHub - EWS MangedAPI issue 204)

Google Drive API - How to make sub-folder into the Team Drive

I'm developing small system using Google Drive API with JAVA,
I am wondering about the TeamDrive API among Google Drive APIs.
First of all, I created team drive folder,
Create Team Drive
public static void createTeamDrive(String accessToken) throws IOException {
long startTime = System.currentTimeMillis();
TeamDrive teamDriveMetadata = new TeamDrive();
teamDriveMetadata.setName("Team Drive API Test");
String requestId = UUID.randomUUID().toString();
TeamDrive teamDrive = getDriveService(accessToken).teamdrives().insert(requestId, teamDriveMetadata).execute();
System.out.println("[Create TeamDrive] execution time : " + (System.currentTimeMillis() - startTime));
}
and then I shared the user.
Share User
public static Permission ShareUser(String teamDriveFolderId, String googleMail, String accessToken) {
long startTime = System.currentTimeMillis();
Permission userPermission = new Permission().setRole("reader")
.setType("user").setEmailAddress(googleMail)
.setValue(googleMail).setWithLink(false);
try {
userPermission = getDriveService(accessToken).permissions().insert(teamDriveFolderId, userPermission)
.setSupportsTeamDrives(true).execute();
System.out.println(userPermission.toPrettyString());
} catch (IOException e) {
System.out.println(e);
}
System.out.println("[Give Permission] execution time : " + (System.currentTimeMillis() - startTime));
return userPermission;
}
And then I tried to create subfolder using google-drive library several times,
But I failed to create sub-folder continuously.
Create Sub-folder
public static void createSubFolder(String accessToken, String teamDriveFolderId) throws IOException {
long startTime = System.currentTimeMillis();
File fileMetaData = new File();
fileMetaData.setTitle("SubFolder Title ");
fileMetaData.setMimeType("application/vnd.google-apps.folder");
fileMetaData.setTeamDriveId(teamDriveFolderId);
fileMetaData.setParents(Arrays.asList(new ParentReference().setId(teamDriveFolderId)));
fileMetaData.set("supportsTeamDrives", true);
fileMetaData.set("fields", "id");
File file = null;
try {
file = getDriveService(accessToken).files().insert(fileMetaData).execute();
System.out.println(file.getId());
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("[Create Sub folder] execution time : " + (System.currentTimeMillis() - startTime));
}
When I call the 'createSubfolderFunction'
I can get 404 response like this.
com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 Not Found
{
"code" : 404,
"errors" : [ {
"domain" : "global",
"location" : "file",
"locationType" : "other",
"message" : "File not found: 0AHGrQOmlzQZUUk9PVA",
"reason" : "notFound"
} ],
"message" : "File not found: 0AHGrQOmlzQZUUk9PVA"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
'0AHGrQOmlzQZUUk9PVA' is the exact result from createTeamDrive function.
I referred to this question.enter link description here
Please look at my source-code and point out what went wrong.
I found problem myself,
I should add setSupportTeamDrive(true) when I use the insert method.
file = getDriveService(accessToken).files().insert(fileMetaData).setSupportsTeamDrives(true).execute();
I thing issue with the your folder Id check its your parent folder id of google drive is correct or not
$folderId = '0AIuzzEYPQu9CUk9PVA';
Id is present in the google drive folder URL, after you open the parent folder

Google Drive 403 Forbidden error

there are already a few people that have asked questions in regards to this, but I still wasn't able to solve my problem.
So, I did the gradle/java quickstart to Print the names and IDs for up to 10 files in my Googledrive. That was fine, but I want to write a java script to upload files on my drive, I inserted the main function below in my java file.
public class DriveCommandLine {
/** Application name. */
private static final String APPLICATION_NAME =
"Drive API Java Quickstart";
/** Directory to store user credentials for this application. */
private static final java.io.File DATA_STORE_DIR = new java.io.File(
System.getProperty("user.home"), ".credentials/drive-java-quickstart");
/** Global instance of the {#link FileDataStoreFactory}. */
private static FileDataStoreFactory DATA_STORE_FACTORY;
/** Global instance of the JSON factory. */
private static final JsonFactory JSON_FACTORY =
JacksonFactory.getDefaultInstance();
/** Global instance of the HTTP transport. */
private static HttpTransport HTTP_TRANSPORT;
/** Global instance of the scopes required by this quickstart.
*
* If modifying these scopes, delete your previously saved credentials
* at ~/.credentials/drive-java-quickstart
*/
private static final List<String> SCOPES =
Arrays.asList(DriveScopes.DRIVE_METADATA_READONLY);
static {
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
} catch (Throwable t) {
t.printStackTrace();
System.exit(1);
}
}
/**
* Creates an authorized Credential object.
* #return an authorized Credential object.
* #throws IOException
*/
public static Credential authorize() throws IOException {
// Load client secrets.
InputStream in =
DriveCommandLine.class.getResourceAsStream("/client_secret.json");
GoogleClientSecrets clientSecrets =
GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(DATA_STORE_FACTORY)
.setAccessType("offline")
.build();
Credential credential = new AuthorizationCodeInstalledApp(
flow, new LocalServerReceiver()).authorize("user");
System.out.println(
"Credentials saved to " + DATA_STORE_DIR.getAbsolutePath());
return credential;
}
/**
* Build and return an authorized Drive client service.
* #return an authorized Drive client service
* #throws IOException
*/
public static Drive getDriveService() throws IOException {
Credential credential = authorize();
return new Drive.Builder(
HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME)
.build();
}
public static void main(String[] args) throws IOException{
//Insert a file
Drive service = getDriveService();
File body = new File();
body.setDescription("A test document");
body.setMimeType("text/plain");
java.io.File fileContent = new java.io.File("D:\\MyFiles\\nkonstantinidis\\Study\\IntelliJ_Shortcuts.txt");
FileContent mediaContent = new FileContent("text/plain", fileContent);
File file = service.files().insert(body, mediaContent).execute();
System.out.println("file ID: " + file.getId());
}
}
And I'm getting an error message shown below when I run it
Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
{
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Insufficient Permission",
"reason" : "insufficientPermissions"
} ],
"message" : "Insufficient Permission"
}
at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:432)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
at DriveCommandLine.main(DriveCommandLine.java:114)
I have created the OAuth client ID and enabled google drive but this still doing it's thing.
Could you maybe help me, or even better direct to tutorial for uploading files on google drive, I'm quite bad in Java so bear with me.
Thanks in advance.
You must change SCOPE used for connection (Insufficient Permission message) into appropriate one
private static final List<String> SCOPES = Arrays.asList(DriveScopes.DRIVE_METADATA_READONLY);
From DriveScope.class:
/** View and manage the files in your Google Drive. */
public static final String DRIVE = "https://www.googleapis.com/auth/drive";
/** View and manage its own configuration data in your Google Drive. */
public static final String DRIVE_APPDATA = "https://www.googleapis.com/auth/drive.appdata";
/** View and manage Google Drive files and folders that you have opened or created with this app. */
public static final String DRIVE_FILE = "https://www.googleapis.com/auth/drive.file";
/** View and manage metadata of files in your Google Drive. */
public static final String DRIVE_METADATA = "https://www.googleapis.com/auth/drive.metadata";
/** View metadata for files in your Google Drive. */
public static final String DRIVE_METADATA_READONLY = "https://www.googleapis.com/auth/drive.metadata.readonly";
/** View the photos, videos and albums in your Google Photos. */
public static final String DRIVE_PHOTOS_READONLY = "https://www.googleapis.com/auth/drive.photos.readonly";
/** View the files in your Google Drive. */
public static final String DRIVE_READONLY = "https://www.googleapis.com/auth/drive.readonly";
/** Modify your Google Apps Script scripts' behavior. */
public static final String DRIVE_SCRIPTS = "https://www.googleapis.com/auth/drive.scripts";
Remember to delete saved credentials (created by application) after changing SCOPE

Getting domain installs for my Google Docs Add-on

We have a Google-docs Add-on (built on Google Apps Script) for which we enabled the Google Apps Marketplace SDK - so that Google Apps Administrators could install our add-on at the domain level.
We have noticed a few domains have now installed our add-on - but I can't seem to find a way to get information on which domains have installed us. Is it even possible?
I have tried the Marketplace license API https://developers.google.com/apps-marketplace/v2/reference/ but it fails with a 403 error - Not authorized to access the application ID.
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Not authorized to access the application ID"
}
],
"code": 403,
"message": "Not authorized to access the application ID"
}
}
I even tried by creating a service account and accessed the API using the service account but got the same error.
Any input would be awesome.
Here's my Java (Groovy technically) code so far (the response is the json I've pasted above):
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.http.GenericUrl
import com.google.api.client.http.HttpRequest
import com.google.api.client.http.HttpRequestFactory
import com.google.api.client.http.HttpTransport
import com.google.api.client.json.JsonFactory
import com.google.api.client.json.jackson2.JacksonFactory
import org.springframework.security.access.annotation.Secured
class DataController {
/**
* Be sure to specify the name of your application. If the application name is {#code null} or
* blank, the application will log a warning. Suggested format is "MyCompany-ProductName/1.0".
*/
private static final String APPLICATION_NAME = "My app name";
private static final String APPLICATION_ID = "12 digit project number";
/** E-mail address of the service account. */
private static final String SERVICE_ACCOUNT_EMAIL = "12digitproejectnumber-compute#developer.gserviceaccount.com";
/** Global instance of the HTTP transport. */
private static HttpTransport httpTransport;
/** Global instance of the JSON factory. */
private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
def getLicensedInfo() {
try {
try {
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/appsmarketplace.license"))
.setServiceAccountPrivateKeyFromP12File(new File("a/valid/path/to/key.p12"))
.build();
credential.refreshToken();
String token = credential.getAccessToken();
HttpRequestFactory requestFactory = httpTransport.createRequestFactory(credential);
GenericUrl url = new GenericUrl("https://www.googleapis.com/appsmarket/v2/licenseNotification/"+APPLICATION_ID);
HttpRequest request = requestFactory.buildGetRequest(url);
com.google.api.client.http.HttpResponse response = request.execute();
log.debug(response.parseAsString());
return;
} catch (IOException e) {
System.err.println(e.getMessage());
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
When making the request to the GAM License API you need to use the same Google Developers Console project that you used for your listing. In the case of an add-on, this is the console project associated with the script.

Setting Description in Metadata for Google Drive Android API

Is there any way to set the Metadata Description?
https://developer.android.com/reference/com/google/android/gms/drive/Metadata.html#getDescription()
If so, what is the length limit?
I can't see anything in the api: https://developer.android.com/reference/com/google/android/gms/drive/MetadataChangeSet.Builder.html
Unfortunately not at the moment, AFAIK. What I do right now is initializing both GDAA and RESTful API (see the 'trash solution' SO 22295903) like this:
private GoogleApiClient _gac;
private com.google.api.services.drive.Drive _svc;
public GoogleApiClient init(String email){
_gac = new GoogleApiClient.Builder(UT.ACTX).addApi(com.google.android.gms.drive.Drive.API)
.addScope(com.google.android.gms.drive.Drive.SCOPE_FILE).setAccountName(email).build();
com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential crd =
GoogleAccountCredential.usingOAuth2(UT.ACTX,
Arrays.asList(com.google.api.services.drive.DriveScopes.DRIVE_FILE));
crd.setSelectedAccountName(email);
_svc = new com.google.api.services.drive.Drive.Builder(
AndroidHttp.newCompatibleTransport(), new GsonFactory(), crd).build();
return this;
}
You get the description from DGAA (GoogleApiClient _gac above), but update/write it to RESTFul like this (off UI thread):
public void oldDescUpW(String titl, String mime, String desc) {
try {
final FileList gLst = _svc.files().list()
.setQ("title = '"+titl+".jpg' and mimeType = '"+mime+"' and trashed = false")
.setFields("items(id)").execute();
if (gLst.getItems().size() == 1) {
final String sId = gLst.getItems().get(0).getId();
com.google.api.services.drive.model.File body =
new com.google.api.services.drive.model.File();
body.setDescription(desc);
_svc.files().patch(sId, body).execute();
}
} catch (Exception e) {}
}
It is also possible to use 'resource ID' from GDAA to address the file in RESTful, but it is not always immediately available (if the file is created in GDAA). See SO 22874657
DISCLAIMER:
It is a HACK and should not stay alive past GDAA delivery of an alternative.