better handle the case when cTag is null

This commit is contained in:
skilion 2016-12-24 14:12:20 +01:00
parent e33f566ecf
commit 9ad9394b98
3 changed files with 67 additions and 95 deletions

View file

@ -1,4 +1,4 @@
import std.datetime, std.path, std.string;
import std.datetime, std.path, std.exception, std.string;
import sqlite;
enum ItemType
@ -21,6 +21,9 @@ struct Item
final class ItemDatabase
{
// increment this for every change in the db schema
immutable int itemDatabaseVersion = 1;
Database db;
Statement insertItemStmt;
Statement updateItemStmt;
@ -35,7 +38,7 @@ final class ItemDatabase
name TEXT NOT NULL,
type TEXT NOT NULL,
eTag TEXT NOT NULL,
cTag TEXT NOT NULL,
cTag TEXT,
mtime TEXT NOT NULL,
parentId TEXT,
crc32 TEXT,
@ -44,6 +47,8 @@ final class ItemDatabase
db.exec("CREATE INDEX IF NOT EXISTS name_idx ON item (name)");
db.exec("PRAGMA foreign_keys = ON");
db.exec("PRAGMA recursive_triggers = ON");
db.setVersion(itemDatabaseVersion);
insertItemStmt = db.prepare("INSERT OR REPLACE INTO item (id, name, type, eTag, cTag, mtime, parentId, crc32) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
updateItemStmt = db.prepare("
UPDATE item
@ -54,70 +59,28 @@ final class ItemDatabase
selectItemByParentIdStmt = db.prepare("SELECT id FROM item WHERE parentId = ?");
}
void insert(const(char)[] id, const(char)[] name, ItemType type, const(char)[] eTag, const(char)[] cTag, const(char)[] mtime, const(char)[] parentId, const(char)[] crc32)
void insert(const ref Item item)
{
with (insertItemStmt) {
bind(1, id);
bind(2, name);
string typeStr = void;
final switch (type) {
case ItemType.file: typeStr = "file"; break;
case ItemType.dir: typeStr = "dir"; break;
}
bind(3, typeStr);
bind(4, eTag);
bind(5, cTag);
bind(6, mtime);
bind(7, parentId);
bind(8, crc32);
exec();
}
bindItem(item, insertItemStmt);
insertItemStmt.exec();
}
void update(const(char)[] id, const(char)[] name, ItemType type, const(char)[] eTag, const(char)[] cTag, const(char)[] mtime, const(char)[] parentId, const(char)[] crc32)
void update(const ref Item item)
{
with (updateItemStmt) {
bind(1, id);
bind(2, name);
string typeStr = void;
final switch (type) {
case ItemType.file: typeStr = "file"; break;
case ItemType.dir: typeStr = "dir"; break;
}
bind(3, typeStr);
bind(4, eTag);
bind(5, cTag);
bind(6, mtime);
bind(7, parentId);
bind(8, crc32);
exec();
}
bindItem(item, updateItemStmt);
updateItemStmt.exec();
}
void upsert(const(char)[] id, const(char)[] name, ItemType type, const(char)[] eTag, const(char)[] cTag, const(char)[] mtime, const(char)[] parentId, const(char)[] crc32)
void upsert(const ref Item item)
{
auto s = db.prepare("SELECT COUNT(*) FROM item WHERE id = ?");
s.bind(1, id);
s.bind(1, item.id);
auto r = s.exec();
Statement* p;
if (r.front[0] == "0") p = &insertItemStmt;
else p = &updateItemStmt;
with (p) {
bind(1, id);
bind(2, name);
string typeStr = void;
final switch (type) {
case ItemType.file: typeStr = "file"; break;
case ItemType.dir: typeStr = "dir"; break;
}
bind(3, typeStr);
bind(4, eTag);
bind(5, cTag);
bind(6, mtime);
bind(7, parentId);
bind(8, crc32);
exec();
}
Statement* stmt;
if (r.front[0] == "0") stmt = &insertItemStmt;
else stmt = &updateItemStmt;
bindItem(item, *stmt);
stmt.exec();
}
Item[] selectChildren(const(char)[] id)
@ -218,6 +181,25 @@ final class ItemDatabase
return false;
}
private void bindItem(const ref Item item, ref Statement stmt)
{
with (stmt) with (item) {
bind(1, id);
bind(2, name);
string typeStr = null;
final switch (type) with (ItemType) {
case file: typeStr = "file"; break;
case dir: typeStr = "dir"; break;
}
bind(3, typeStr);
bind(4, eTag);
bind(5, cTag);
bind(6, mtime.toISOExtString());
bind(7, parentId);
bind(8, crc32);
}
}
private Item buildItem(Statement.Result result)
{
assert(!result.empty && result.front.length == 8);
@ -231,25 +213,25 @@ final class ItemDatabase
crc32: result.front[7].dup
};
switch (result.front[2]) {
case "file": item.type = ItemType.file; break;
case "dir": item.type = ItemType.dir; break;
default: assert(0);
case "file": item.type = ItemType.file; break;
case "dir": item.type = ItemType.dir; break;
default: assert(0);
}
return item;
}
// computes the path of the given item id
// the path is relative to the sync directory ex: "./Music/Turbo Killer.mp3"
// a trailing slash is never added
string computePath(const(char)[] id)
{
if (!id) return null;
string path;
auto s = db.prepare("SELECT name, parentId FROM item WHERE id = ?");
while (true) {
s.bind(1, id);
auto r = s.exec();
if (r.empty) {
// no results
break;
} else if (r.front[1]) {
enforce(!r.empty, "Unknow item id");
if (r.front[1]) {
if (path) path = r.front[0].idup ~ "/" ~ path;
else path = r.front[0].idup;
} else {

View file

@ -16,7 +16,7 @@ private immutable {
class OneDriveException: Exception
{
int httpStatusCode;
// error details
// https://dev.onedrive.com/misc/errors.htm
JSONValue error;
@nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__)
@ -36,7 +36,7 @@ class OneDriveException: Exception
{
this.httpStatusCode = httpStatusCode;
this.error = error;
string msg = format("HTTP request returned status code %d (%s)\n%s", httpStatusCode, reason, toJSON(&error, true));
string msg = format("HTTP request returned status code %d (%s)\n%s", httpStatusCode, reason, toJSON(error, true));
super(msg, file, line, next);
}
}

View file

@ -190,17 +190,6 @@ final class SyncEngine
return;
}
string cTag;
try {
cTag = item["cTag"].str;
} catch (JSONException e) {
// cTag is not returned if the Item is a folder
// https://dev.onedrive.com/resources/item.htm
cTag = "";
}
string mtime = item["fileSystemInfo"]["lastModifiedDateTime"].str;
string crc32;
if (type == ItemType.file) {
try {
@ -215,8 +204,8 @@ final class SyncEngine
name: name,
type: type,
eTag: eTag,
cTag: cTag,
mtime: SysTime.fromISOExtString(mtime),
cTag: "cTag" in item ? item["cTag"].str : null,
mtime: SysTime.fromISOExtString(item["fileSystemInfo"]["lastModifiedDateTime"].str),
parentId: parentId,
crc32: crc32
};
@ -229,9 +218,9 @@ final class SyncEngine
// save the item in the db
if (oldItem.id) {
itemdb.update(id, name, type, eTag, cTag, mtime, parentId, crc32);
itemdb.update(newItem);
} else {
itemdb.insert(id, name, type, eTag, cTag, mtime, parentId, crc32);
itemdb.insert(newItem);
}
}
@ -526,31 +515,32 @@ final class SyncEngine
saveItem(res);
}
private void saveItem(JSONValue item)
private void saveItem(JSONValue jsonItem)
{
string id = item["id"].str;
string id = jsonItem["id"].str;
ItemType type;
if (isItemFile(item)) {
if (isItemFile(jsonItem)) {
type = ItemType.file;
} else if (isItemFolder(item)) {
} else if (isItemFolder(jsonItem)) {
type = ItemType.dir;
} else {
assert(0);
}
string name = item["name"].str;
string eTag = item["eTag"].str;
string cTag = item["cTag"].str;
string mtime = item["fileSystemInfo"]["lastModifiedDateTime"].str;
string parentId = item["parentReference"]["id"].str;
string crc32;
Item item = {
name: jsonItem["name"].str,
eTag: jsonItem["eTag"].str,
cTag: "cTag" in jsonItem ? jsonItem["cTag"].str : null,
mtime: SysTime.fromISOExtString(jsonItem["fileSystemInfo"]["lastModifiedDateTime"].str),
parentId: jsonItem["parentReference"]["id"].str
};
if (type == ItemType.file) {
try {
crc32 = item["file"]["hashes"]["crc32Hash"].str;
item.crc32 = jsonItem["file"]["hashes"]["crc32Hash"].str;
} catch (JSONException e) {
// swallow exception
log.vlog("The hash is not available");
}
}
itemdb.upsert(id, name, type, eTag, cTag, mtime, parentId, crc32);
itemdb.upsert(item);
}
void uploadMoveItem(string from, string to)