Resolve sync issues with Shared Folders & Personal OneDrive Accounts (#26)

* Resolve Shared Folders not syncing (OneDrive Personal)
* Resolve 'Key not found: fileSystemInfo' when then item is a remote item (OneDrive Personal)
* Resolve where file names are being truncated by 5 characters with each sync (OneDrive Business)
* remove extra // not required
* Remove commented out code to avoid confusion
This commit is contained in:
abraunegg 2018-06-17 12:02:58 +10:00 committed by GitHub
parent d6b4930d0a
commit 17e5138b5a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 32 deletions

View file

@ -190,12 +190,12 @@ final class OneDriveApi
// Return the details of the specified id
// https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_get
JSONValue getPathDetailsById(const(char)[] id)
JSONValue getPathDetailsById(const(char)[] driveId, const(char)[] id)
{
checkAccessTokenExpired();
const(char)[] url;
// string itemByIdUrl = "https://graph.microsoft.com/v1.0/me/drive/items/";
url = itemByIdUrl ~ id;
// string driveByIdUrl = "https://graph.microsoft.com/v1.0/drives/";
url = driveByIdUrl ~ driveId ~ "/items/" ~ id;
url ~= "?select=id,name,eTag,cTag,deleted,file,folder,root,fileSystemInfo,remoteItem,parentReference";
return get(url);
}

View file

@ -58,7 +58,13 @@ private Item makeItem(const ref JSONValue driveItem)
item.mtime = SysTime(0);
} else {
// Item is not in a deleted state
item.mtime = SysTime.fromISOExtString(driveItem["fileSystemInfo"]["lastModifiedDateTime"].str);
// Resolve 'Key not found: fileSystemInfo' when then item is a remote item
// https://github.com/abraunegg/onedrive/issues/11
if (isItemRemote(driveItem)) {
item.mtime = SysTime.fromISOExtString(driveItem["remoteItem"]["fileSystemInfo"]["lastModifiedDateTime"].str);
} else {
item.mtime = SysTime.fromISOExtString(driveItem["fileSystemInfo"]["lastModifiedDateTime"].str);
}
}
if (isItemFile(driveItem)) {
@ -251,7 +257,7 @@ final class SyncEngine
auto deleteDirectoryNoSync(string path)
{
// Use the global's as initialised via init() rather than performing unnecessary additional HTTPS calls
string rootId = defaultRootId;
const(char)[] rootId = defaultRootId;
// Attempt to delete the requested path within OneDrive without performing a sync
log.vlog("Attempting to delete the requested path within OneDrive");
@ -275,7 +281,7 @@ final class SyncEngine
} else {
// the folder was in the local database
// Handle the deletion and saving any update to the local database
log.vlog("The requested directory to delete was found in the local database. Processing the delection normally");
log.vlog("The requested directory to delete was found in the local database. Processing the deletion normally");
deleteByPath(path);
}
}
@ -304,26 +310,53 @@ final class SyncEngine
{
log.vlog("Applying changes of Path ID: " ~ id);
JSONValue changes;
string syncFolderName;
string deltaLink = itemdb.getDeltaLink(driveId, id);
JSONValue idDetails = onedrive.getPathDetailsById(id);
// Set the name of this folder
if ((idDetails["id"].str == id) && (isItemFolder(idDetails))){
syncFolderName = idDetails["name"].str;
// Query the name of this folder id
string syncFolderName;
JSONValue idDetails = parseJSON("{}");
try {
idDetails = onedrive.getPathDetailsById(driveId, id);
} catch (OneDriveException e) {
if (e.httpStatusCode == 404) {
// id was not found - possibly a remote (shared) folder
log.vlog("No details returned for given Path ID");
return;
}
}
// Get the name of this 'Path ID'
if (("id" in idDetails) != null) {
// valid response from onedrive.getPathDetailsById(driveId, id) - a JSON item object present
if ((idDetails["id"].str == id) && (isItemFolder(idDetails))){
syncFolderName = encodeComponent(idDetails["name"].str);
}
}
for (;;) {
// Due to differences in OneDrive API's between personal and business we need to get changes only from defaultRootId
// If we used the 'id' passed in & when using --single-directory with a business account we get:
// 'HTTP request returned status code 501 (Not Implemented): view.delta can only be called on the root.'
// To view changes correctly, we need to use the correct path id for the request
const(char)[] idToQuery;
if (driveId == defaultDriveId) {
// The drive id matches our users default drive id
idToQuery = defaultRootId.dup;
} else {
// The drive id does not match our users default drive id
// Potentially the 'path id' we are requesting the details of is a Shared Folder (remote item)
// Use the 'id' that was passed in
idToQuery = id;
}
try {
// Due to differences in OneDrive API's between personal and business we need to get changes only from defaultRootId
// If we used the 'id' passed in & when using --single-directory with a business account we get:
// 'HTTP request returned status code 501 (Not Implemented): view.delta can only be called on the root.'
// To view changes correctly, we need to use 'defaultRootId'
changes = onedrive.viewChangesById(driveId, defaultRootId, deltaLink);
// Fetch the changes relative to the path id we want to query
changes = onedrive.viewChangesById(driveId, idToQuery, deltaLink);
} catch (OneDriveException e) {
// HTTP request returned status code 410 (The requested resource is no longer available at the server)
if (e.httpStatusCode == 410) {
log.vlog("Delta link expired, resyncing...");
log.vlog("Delta link expired, re-syncing...");
deltaLink = null;
continue;
}
@ -339,9 +372,9 @@ final class SyncEngine
if (e.httpStatusCode == 504) {
// HTTP request returned status code 504 (Gateway Timeout)
// Retry
//log.vlog("OneDrive returned a 'HTTP 504 - Gateway Timeout' - gracefully handling error");
changes = onedrive.viewChangesById(driveId, defaultRootId, deltaLink);
// Retry by calling applyDifferences() again
log.vlog("OneDrive returned a 'HTTP 504 - Gateway Timeout' - gracefully handling error");
applyDifferences(driveId, idToQuery);
}
else throw e;
@ -354,12 +387,25 @@ final class SyncEngine
// Thus we cannot name check for 'root' below on deleted items
if(!isItemDeleted(item)){
// This is not a deleted item
// Test is this is the OneDrive Root?
// Test is this is the OneDrive Users Root?
// Use the global's as initialised via init() rather than performing unnecessary additional HTTPS calls
if ((id == defaultRootId) && (item["name"].str == "root")) { // fix for https://github.com/skilion/onedrive/issues/269
if ((id == defaultRootId) && (item["name"].str == "root")) {
// This IS the OneDrive Root
isRoot = true;
}
// Test is this a Shared Folder - which should also be classified as a 'root' item
if (changeHasParentReferenceId(item)) {
// item contains parentReference key
if (item["parentReference"]["driveId"].str != defaultDriveId) {
// The change parentReference driveId does not match the defaultDriveId - this could be a Shared Folder root item
string sharedDriveRootPath = "/drives/" ~ item["parentReference"]["driveId"].str ~ "/root:";
if (item["parentReference"]["path"].str == sharedDriveRootPath) {
// The drive path matches what a shared folder root item would equal
isRoot = true;
}
}
}
}
// How do we handle this change?
@ -380,10 +426,11 @@ final class SyncEngine
// This is a corner edge case - https://github.com/skilion/onedrive/issues/341
JSONValue oneDriveMovedNotDeleted;
try {
oneDriveMovedNotDeleted = onedrive.getPathDetailsById(item["id"].str);
oneDriveMovedNotDeleted = onedrive.getPathDetailsById(driveId, item["id"].str);
} catch (OneDriveException e) {
if (e.httpStatusCode == 404) {
// No .. that ID is GONE
log.vlog("Remote change discarded - item cannot be found");
return;
}
}
@ -514,13 +561,6 @@ final class SyncEngine
} else {
itemdb.insert(item);
}
// sync remote folder
// https://github.com/OneDrive/onedrive-api-docs/issues/764
/*if (isItemRemote(driveItem)) {
log.log("Syncing remote folder: ", path);
applyDifferences(item.remoteDriveId, item.remoteId);
}*/
}
// download an item that was not synced before
@ -542,7 +582,7 @@ final class SyncEngine
break;
case ItemType.dir:
case ItemType.remote:
log.log("Creating directory ", path);
log.log("Creating directory: ", path);
mkdirRecurse(path);
break;
}