Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Gdrive shortcuts #377

Merged
merged 4 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions KeeAnywhere/StorageProviders/GoogleDrive/GoogleDriveHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,14 @@ public static async Task<DriveService> GetClient(TokenResponse token)
return client;
}

public static async Task<File> GetFileByPath(this DriveService api, string path)
public static async Task<File> GetFileByPath(this DriveService api, string path, bool resolveFinalShortcut)
{
var parts = path.Split('/');
File file = null;

foreach (var part in parts)
var partsLength = parts.Count();
for (int i = 0; i < partsLength; i++)
{
var part = parts[i];
var query = api.Files.List();
query.Q = string.Format("name = '{0}' and '{1}' in parents and trashed = false", part,
//query.Q = string.Format("title = '{0}'", part,
Expand All @@ -102,6 +103,19 @@ public static async Task<File> GetFileByPath(this DriveService api, string path)

file = result.Files.FirstOrDefault();
if (file == null) return null;
if (resolveFinalShortcut || i != partsLength-1)
{
if (file.MimeType == "application/vnd.google-apps.shortcut")
{
if (file.ShortcutDetails == null)
{
var fileQuery = api.Files.Get(file.Id);
fileQuery.Fields = "shortcutDetails";
file = await fileQuery.ExecuteAsync();
}
file = await api.Files.Get(file.ShortcutDetails.TargetId).ExecuteAsync();
}
}
}

return file;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public async Task<Stream> Load(string path)
{
var api = await GetApi();

var file = await api.GetFileByPath(path);
var file = await api.GetFileByPath(path, true);
if (file == null)
return null;

Expand All @@ -48,7 +48,7 @@ public async Task Save(Stream stream, string path)

IUploadProgress progress;

var file = await api.GetFileByPath(path);
var file = await api.GetFileByPath(path, true);
if (file != null)
{
progress = await api.Files.Update(null, file.Id, stream, "application/octet-stream").UploadAsync();
Expand All @@ -66,7 +66,7 @@ public async Task Save(Stream stream, string path)

if (!string.IsNullOrEmpty(folderName))
{
var folder = await api.GetFileByPath(folderName);
var folder = await api.GetFileByPath(folderName, true);
if (folder == null)
throw new InvalidOperationException(string.Format("Folder does not exist: {0}", folderName));

Expand All @@ -84,12 +84,12 @@ public async Task Copy(string sourcePath, string destPath)
{
var api = await GetApi();

var sourceFile = await api.GetFileByPath(sourcePath);
var sourceFile = await api.GetFileByPath(sourcePath, true);
if (sourceFile == null)
throw new FileNotFoundException("Google Drive: File not found.", sourcePath);

var destFolder = CloudPath.GetDirectoryName(destPath);
var parentFolder = await api.GetFileByPath(destFolder);
var parentFolder = await api.GetFileByPath(destFolder, true);
if (parentFolder == null)
throw new FileNotFoundException("Google Drive: File not found.", destFolder);

Expand All @@ -107,7 +107,7 @@ public async Task Delete(string path)
{
var api = await GetApi();

var file = await api.GetFileByPath(path);
var file = await api.GetFileByPath(path, false);
if (file == null)
throw new FileNotFoundException("Goolge Drive: File not found.", path);

Expand All @@ -129,47 +129,38 @@ public async Task<IEnumerable<StorageProviderItem>> GetChildrenByParentItem(Stor
var api = await GetApi();
var query = api.Files.List();
query.Q = string.Format("'{0}' in parents and trashed = false", parent.Id);
query.Fields = "nextPageToken, files(id, name, mimeType, shortcutDetails, modifiedTime, parents)"; //The shortcutDetails field isn't returned in queries by default. Unless we request it, it's always null. The downside is, now we have to spell out every field we *do* want. Forgetting something we need will mean it's always set to null in the returned query, File object, etc. and things will break. This already happened once when I forgot I needed to explicitly request nextPageToken.

var items = await query.ExecuteAsync();
var newItems = items.Files.Select(_ => new StorageProviderItem()
{
Id = _.Id,
Name = _.Name,
Type =
_.MimeType == "application/vnd.google-apps.folder"
? StorageProviderItemType.Folder
: StorageProviderItemType.File,
LastModifiedDateTime = _.ModifiedTime,
ParentReferenceId = parent.Id,
});
var newItems = items.Files.Select(async _ =>
{
var result = await MakeStorageProviderItem(_, api);
result.ParentReferenceId = parent.Id;
return result;
});


while (items.NextPageToken != null)
{
query.PageToken = items.NextPageToken;

items = await query.ExecuteAsync();
newItems = newItems.Concat(items.Files.Select(_ => new StorageProviderItem()
{
Id = _.Id,
Name = _.Name,
Type =
_.MimeType == "application/vnd.google-apps.folder"
? StorageProviderItemType.Folder
: StorageProviderItemType.File,
LastModifiedDateTime = _.ModifiedTime,
ParentReferenceId = parent.Id,
}));
newItems = newItems.Concat(items.Files.Select(async _ =>
{
var result = await MakeStorageProviderItem(_, api);
result.ParentReferenceId = parent.Id; return result;
}));
}

return newItems.ToArray();
return await Task.WhenAll(newItems.ToArray());
}

public async Task<IEnumerable<StorageProviderItem>> GetChildrenByParentPath(string path)
{
var api = await GetApi();
var item = await api.GetFileByPath(path);
var item = await api.GetFileByPath(path, true);
if (item == null)
throw new FileNotFoundException("Goolge Drive: File not found.", path);
throw new FileNotFoundException("Google Drive: File not found.", path);

return await GetChildrenByParentItem(new StorageProviderItem {Id = item.Id});
}
Expand All @@ -189,5 +180,41 @@ protected async Task<DriveService> GetApi()

return _api;
}

protected async Task<StorageProviderItem> MakeStorageProviderItem(File _, DriveService api)
{
var isShortcut = false;
if (_.MimeType == "application/vnd.google-apps.shortcut")
{
isShortcut = true;
if (_.ShortcutDetails==null)
{
var fileQuery = api.Files.Get(_.Id);
fileQuery.Fields = "shortcutDetails";

_ = await fileQuery.ExecuteAsync();
}
}
var result = new StorageProviderItem()
{
Id =
isShortcut
? _.ShortcutDetails.TargetId
: _.Id,
Name = _.Name,
Type =
isShortcut
? _.ShortcutDetails.TargetMimeType == "application/vnd.google-apps.folder"
? StorageProviderItemType.Folder
: StorageProviderItemType.File
: _.MimeType == "application/vnd.google-apps.folder"
? StorageProviderItemType.Folder
: StorageProviderItemType.File,
LastModifiedDateTime = _.ModifiedTime,
ParentReferenceId = _.Parents.FirstOrDefault(),
};

return result;
}
}
}