2017-06-22 21:44:38 +02:00
|
|
|
import moment from 'moment';
|
2017-06-24 20:06:28 +02:00
|
|
|
import { time } from 'lib/time-utils.js';
|
|
|
|
import { dirname, basename } from 'lib/path-utils.js';
|
|
|
|
import { OneDriveApi } from 'lib/onedrive-api.js';
|
2017-06-22 21:44:38 +02:00
|
|
|
|
|
|
|
class FileApiDriverOneDrive {
|
|
|
|
|
2017-07-06 21:29:09 +02:00
|
|
|
constructor(api) {
|
|
|
|
this.api_ = api;
|
2017-06-22 23:52:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
api() {
|
|
|
|
return this.api_;
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
2017-07-19 00:14:20 +02:00
|
|
|
supportsDelta() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-06-22 21:44:38 +02:00
|
|
|
itemFilter_() {
|
|
|
|
return {
|
|
|
|
select: 'name,file,folder,fileSystemInfo',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
makePath_(path) {
|
2017-06-22 23:52:27 +02:00
|
|
|
return path;
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
makeItems_(odItems) {
|
|
|
|
let output = [];
|
|
|
|
for (let i = 0; i < odItems.length; i++) {
|
|
|
|
output.push(this.makeItem_(odItems[i]));
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
makeItem_(odItem) {
|
2017-07-18 22:03:07 +02:00
|
|
|
let output = {
|
2017-06-22 21:44:38 +02:00
|
|
|
path: odItem.name,
|
|
|
|
isDir: ('folder' in odItem),
|
|
|
|
};
|
2017-07-18 22:03:07 +02:00
|
|
|
|
|
|
|
if ('deleted' in odItem) {
|
|
|
|
output.isDeleted = true;
|
|
|
|
} else {
|
|
|
|
output.created_time = moment(odItem.fileSystemInfo.createdDateTime, 'YYYY-MM-DDTHH:mm:ss.SSSZ').format('x');
|
|
|
|
output.updated_time = moment(odItem.fileSystemInfo.lastModifiedDateTime, 'YYYY-MM-DDTHH:mm:ss.SSSZ').format('x');
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
2017-06-24 00:19:59 +02:00
|
|
|
async statRaw_(path) {
|
2017-06-23 23:32:24 +02:00
|
|
|
let item = null;
|
2017-06-22 23:52:27 +02:00
|
|
|
try {
|
2017-06-23 23:32:24 +02:00
|
|
|
item = await this.api_.execJson('GET', this.makePath_(path), this.itemFilter_());
|
2017-06-22 23:52:27 +02:00
|
|
|
} catch (error) {
|
2017-06-29 20:03:16 +02:00
|
|
|
if (error.code == 'itemNotFound') return null;
|
2017-06-22 23:52:27 +02:00
|
|
|
throw error;
|
|
|
|
}
|
2017-06-24 00:19:59 +02:00
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
async stat(path) {
|
|
|
|
let item = await this.statRaw_(path);
|
|
|
|
if (!item) return null;
|
2017-06-22 21:44:38 +02:00
|
|
|
return this.makeItem_(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
async setTimestamp(path, timestamp) {
|
|
|
|
let body = {
|
|
|
|
fileSystemInfo: {
|
|
|
|
lastModifiedDateTime: moment.unix(timestamp / 1000).utc().format('YYYY-MM-DDTHH:mm:ss.SSS') + 'Z',
|
|
|
|
}
|
|
|
|
};
|
2017-06-24 00:19:59 +02:00
|
|
|
let item = await this.api_.execJson('PATCH', this.makePath_(path), null, body);
|
|
|
|
return this.makeItem_(item);
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
2017-06-29 20:03:16 +02:00
|
|
|
async list(path, options = null) {
|
|
|
|
let query = this.itemFilter_();
|
|
|
|
let url = this.makePath_(path) + ':/children';
|
|
|
|
|
|
|
|
if (options.context) {
|
|
|
|
query = null;
|
|
|
|
url = options.context;
|
|
|
|
}
|
|
|
|
|
|
|
|
let r = await this.api_.execJson('GET', url, query);
|
|
|
|
|
|
|
|
return {
|
|
|
|
hasMore: !!r['@odata.nextLink'],
|
|
|
|
items: this.makeItems_(r.value),
|
|
|
|
context: r["@odata.nextLink"],
|
|
|
|
}
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
2017-07-06 23:30:45 +02:00
|
|
|
async get(path, options = null) {
|
|
|
|
if (!options) options = {};
|
|
|
|
|
2017-06-22 23:52:27 +02:00
|
|
|
try {
|
2017-07-06 23:30:45 +02:00
|
|
|
if (options.target == 'file') {
|
|
|
|
let response = await this.api_.exec('GET', this.makePath_(path) + ':/content', null, null, options);
|
|
|
|
return response;
|
|
|
|
} else {
|
|
|
|
let content = await this.api_.execText('GET', this.makePath_(path) + ':/content');
|
|
|
|
return content;
|
|
|
|
}
|
2017-06-22 23:52:27 +02:00
|
|
|
} catch (error) {
|
2017-06-29 20:03:16 +02:00
|
|
|
if (error.code == 'itemNotFound') return null;
|
2017-06-22 23:52:27 +02:00
|
|
|
throw error;
|
|
|
|
}
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
2017-06-23 23:32:24 +02:00
|
|
|
async mkdir(path) {
|
|
|
|
let item = await this.stat(path);
|
|
|
|
if (item) return item;
|
|
|
|
|
|
|
|
let parentPath = dirname(path);
|
|
|
|
item = await this.api_.execJson('POST', this.makePath_(parentPath) + ':/children', this.itemFilter_(), {
|
|
|
|
name: basename(path),
|
|
|
|
folder: {},
|
|
|
|
});
|
|
|
|
|
|
|
|
return this.makeItem_(item);
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
put(path, content) {
|
|
|
|
let options = {
|
|
|
|
headers: { 'Content-Type': 'text/plain' },
|
|
|
|
};
|
|
|
|
return this.api_.exec('PUT', this.makePath_(path) + ':/content', null, content, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(path) {
|
|
|
|
return this.api_.exec('DELETE', this.makePath_(path));
|
|
|
|
}
|
|
|
|
|
2017-06-23 23:32:24 +02:00
|
|
|
async move(oldPath, newPath) {
|
2017-07-13 00:32:08 +02:00
|
|
|
// Cannot work in an atomic way because if newPath already exist, the OneDrive API throw an error
|
|
|
|
// "An item with the same name already exists under the parent". Some posts suggest to use
|
|
|
|
// @name.conflictBehavior [0]but that doesn't seem to work. So until Microsoft fixes this
|
|
|
|
// it's not possible to do an atomic move.
|
|
|
|
//
|
|
|
|
// [0] https://stackoverflow.com/questions/29191091/onedrive-api-overwrite-on-move
|
|
|
|
throw new Error('NOT WORKING');
|
|
|
|
|
2017-06-24 00:19:59 +02:00
|
|
|
let previousItem = await this.statRaw_(oldPath);
|
|
|
|
|
2017-06-23 23:32:24 +02:00
|
|
|
let newDir = dirname(newPath);
|
|
|
|
let newName = basename(newPath);
|
|
|
|
|
2017-06-24 00:19:59 +02:00
|
|
|
// We don't want the modification date to change when we move the file so retrieve it
|
|
|
|
// now set it in the PATCH operation.
|
|
|
|
|
2017-06-23 23:32:24 +02:00
|
|
|
let item = await this.api_.execJson('PATCH', this.makePath_(oldPath), this.itemFilter_(), {
|
|
|
|
name: newName,
|
|
|
|
parentReference: { path: newDir },
|
2017-06-24 00:19:59 +02:00
|
|
|
fileSystemInfo: {
|
|
|
|
lastModifiedDateTime: previousItem.fileSystemInfo.lastModifiedDateTime,
|
|
|
|
},
|
2017-06-23 23:32:24 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
return this.makeItem_(item);
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
format() {
|
|
|
|
throw new Error('Not implemented');
|
|
|
|
}
|
|
|
|
|
2017-07-18 21:57:49 +02:00
|
|
|
async delta(path, options = null) {
|
|
|
|
let output = {
|
2017-07-18 22:03:07 +02:00
|
|
|
hasMore: false,
|
2017-07-18 21:57:49 +02:00
|
|
|
context: {},
|
|
|
|
items: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
let context = options ? options.context : null;
|
2017-07-24 20:01:40 +02:00
|
|
|
let url = context ? context.nextLink : null;
|
2017-07-18 21:57:49 +02:00
|
|
|
let query = null;
|
2017-07-24 20:01:40 +02:00
|
|
|
|
|
|
|
if (!url) {
|
2017-07-18 21:57:49 +02:00
|
|
|
url = this.makePath_(path) + ':/delta';
|
|
|
|
query = this.itemFilter_();
|
2017-07-18 22:03:07 +02:00
|
|
|
query.select += ',deleted';
|
2017-07-18 21:57:49 +02:00
|
|
|
}
|
|
|
|
|
2017-07-18 22:03:07 +02:00
|
|
|
let response = await this.api_.execJson('GET', url, query);
|
|
|
|
let items = this.makeItems_(response.value);
|
|
|
|
output.items = output.items.concat(items);
|
2017-07-18 21:57:49 +02:00
|
|
|
|
2017-07-18 22:03:07 +02:00
|
|
|
let nextLink = null;
|
|
|
|
|
|
|
|
if (response['@odata.nextLink']) {
|
|
|
|
nextLink = response['@odata.nextLink'];
|
|
|
|
output.hasMore = true;
|
|
|
|
} else {
|
|
|
|
if (!response['@odata.deltaLink']) throw new Error('Delta link missing: ' + JSON.stringify(response));
|
|
|
|
nextLink = response['@odata.deltaLink'];
|
2017-07-18 21:57:49 +02:00
|
|
|
}
|
|
|
|
|
2017-07-18 22:03:07 +02:00
|
|
|
output.context = { nextLink: nextLink };
|
|
|
|
|
2017-07-18 21:57:49 +02:00
|
|
|
// https://dev.onedrive.com/items/view_delta.htm
|
|
|
|
// The same item may appear more than once in a delta feed, for various reasons. You should use the last occurrence you see.
|
|
|
|
// So remove any duplicate item from the array.
|
|
|
|
let temp = [];
|
|
|
|
let seenPaths = [];
|
|
|
|
for (let i = output.items.length - 1; i >= 0; i--) {
|
|
|
|
let item = output.items[i];
|
|
|
|
if (seenPaths.indexOf(item.path) >= 0) continue;
|
|
|
|
temp.splice(0, 0, item);
|
|
|
|
seenPaths.push(item.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
output.items = temp;
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
2017-07-18 21:27:10 +02:00
|
|
|
|
2017-06-22 21:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export { FileApiDriverOneDrive };
|