Commit 54e878be authored by root's avatar root

wip

parent f641ef58
<html>
<head>
<script type="text/javascript" src="../node_modules/rsvp/dist/rsvp-2.0.4.js">
</script>
<script type="text/javascript" src="../dist/jio-latest.js"></script>
</head>
<body> <p>jio example, see console</p>
<script type="text/javascript">
(
function (jio) {
var storage = jIO.createJIO({"type": "indexeddb", "database": "foo"});
console.log(storage);
function foo(message) {
return storage.put("doc1", message)
.push(function () { return storage.put("doc2", {"hello again":"hi again"})})
.push(function (result) {
console.log(result);
return storage.get("doc1");
})
.push(function (result) {console.log(result) })
.push(undefined, function (error) {
console.log(error);});
}
return foo({"hello":"hi"});
}(jIO)
);
</script>
<p> more text </p>
</body>
</html>
<html>
<head>
<script type="text/javascript" src="../node_modules/rsvp/dist/rsvp-2.0.4.js">
</script>
<script type="text/javascript" src="../dist/jio-latest.js"></script>
</head>
<body> <p>jio example, see console</p>
<script type="text/javascript">
(function (jIO) { // create a new jIO storage
var jio_instance = jIO.createJIO({type:"memory"});
<!-- var jio_instance = jIO.createJIO({type: "indexeddb", "database": "foo"}); -->
// post the metadata for "myVideo"
return jio_instance.put("document", {
title: "My Video",
type: "MovingImage",
format: "video/ogg",
description: "Images Compilation" })
// post a thumbnail attachment
.push(function () {
return jio_instance.putAttachment(
"document",
"thumbnail",
new Blob(["img.png"],{type: "image/jpeg"})
)
})
// post video attachment
.push(function () {
return jio_instance.putAttachment(
"document",
"video",
new Blob(["vid.txt"], {type: "video/ogg"})
)
})
// catch any errors and throw
.push(undefined, function(error) {
console.log(error); throw error;
});
}(jIO));
</script>
<p> more text </p>
</body>
</html>
(function (jIO) { // create a new jIO storage
var jio_instance = jIO.createJIO({type: "local"});
// post the metadata for "myVideo"
return jio_instance.put("document", {
title: "My Video",
type: "MovingImage",
format: "video/ogg",
description: "Images Compilation" })
// post a thumbnail attachment
.push(function () {
return jio_instance.putAttachment(
"document",
"thumbnail",
new Blob([my_image],
{type: "image/jpeg"})
})
// post video attachment
.push(function () {
return jio_instance.putAttachment(
"document",
"video",
new Blob([my_video],
{type: "video/ogg"})
})
// catch any errors and throw
.push(undefined, function(error) {
console.log(error); throw error;
});
}(jIO));
<html>
<head>
<script type="text/javascript" src="../node_modules/rsvp/dist/rsvp-2.0.4.js">
</script>
<script type="text/javascript" src="../dist/jio-latest.js"></script>
</head>
<body> <p>Retrieving local data:</p>
<script type="text/javascript">
(function (jIO) {
// create a new jIO storage
var jio_instance = jIO.createJIO({type: "indexeddb", "database":"foo"});
// post the metadata for "myVideo"
return jio_instance.put("document", {
title: "My Video"
})
// post a thumbnail attachment
.push(function () {
return jio_instance.putAttachment(
"document",
"thumbnail",
new Blob(["img.jpg"], {type: "image/jpeg"}))
})
.push(function () {
return jio_instance.getAttachment(
"document",
"thumbnail",
{format:"text"})
})
.push(function (res) {console.log(res)})
// catch any errors and throw
.push(undefined, function(error) {
console.log(error);
throw error;
});
}(jIO));
</script>
<p> more text </p>
</body>
</html>
test test image
<html>
<head>
<script type="text/javascript" src="../node_modules/rsvp/dist/rsvp-2.0.4.js">
</script>
<script type="text/javascript" src="../dist/jio-latest.js"></script>
</head>
<body> <p>jio example, see console</p>
<script type="text/javascript">
(function (jIO) {
var jio = jIO.createJIO({
type: "bryan",
sub_storage: {
type: "uuid",
sub_storage: {
//type: "memory"
type: "indexeddb",
database: "testdb_2"
}
}
}),
not_bryan = jIO.createJIO({
type: "uuid",
sub_storage: {
//type: "memory"
type: "indexeddb",
database: "testdb_2"
}
});
jio.put("A", {
"k": "v0"
})
.push(function () {
jio.put("A", {
"k": "v1"
})
})
.push(function () {
jio.put("A", {
"k": "v2"
})
})
.push(function () {
jio.put("A", {
"k": "v3"
})
})
.push(function () {
return not_bryan.buildQuery({
query: ""
});
})
.push(function (results) {
console.log(results);
},
function (err) {
console.log(err);
});
}(jIO));
</script>
<p> more text </p>
</body>
</html>
\ No newline at end of file
test test video test
/*global Blob*/
/*jslint nomen: true, maxlen: 80*/
(function (QUnit, jIO, Blob) {
"use strict";
var test = QUnit.test,
// equal = QUnit.equal,
expect = QUnit.expect,
ok = QUnit.ok,
stop = QUnit.stop,
start = QUnit.start,
deepEqual = QUnit.deepEqual,
module = QUnit.module,
ATTACHMENT = 'data',
i,
name_list = ['get', 'post', 'put', 'buildQuery',
'putAttachment', 'getAttachment', 'allAttachments'];
///////////////////////////////////////////////////////
// Fake Storage
///////////////////////////////////////////////////////
function resetCount(count) {
for (i = 0; i < name_list.length; i += 1) {
count[name_list[i]] = 0;
}
}
function MockStorage(spec) {
this._erp5_storage = jIO.createJIO({
type: "erp5",
url: "http://example.org"
});
this._sub_storage = jIO.createJIO({
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
});
this._options = spec.options;
resetCount(spec.options.count);
}
function mockFunction(name) {
MockStorage.prototype[name] = function () {
this._options.count[name] += 1;
if (this._options.mock.hasOwnProperty(name)) {
return this._options.mock[name].apply(this, arguments);
}
return this._sub_storage[name].apply(this._sub_storage, arguments);
};
}
for (i = 0; i < name_list.length; i += 1) {
mockFunction(name_list[i]);
}
MockStorage.prototype.hasCapacity = function (name) {
return this._erp5_storage.hasCapacity(name);
};
jIO.addStorage('mock', MockStorage);
///////////////////////////////////////////////////////
// Helpers
///////////////////////////////////////////////////////
function putFullDoc(storage, id, doc, attachment) {
return storage.put(id, doc)
.push(function () {
return storage.putAttachment(
id,
ATTACHMENT,
attachment
);
});
}
function equalStorage(storage, doc_tuple_list) {
return storage.allDocs()
.push(function (result) {
var i,
promise_list = [];
for (i = 0; i < result.data.rows.length; i += 1) {
promise_list.push(RSVP.all([
result.data.rows[i].id,
storage.get(result.data.rows[i].id),
storage.getAttachment(result.data.rows[i].id, ATTACHMENT)
]));
}
return RSVP.all(promise_list);
})
.push(function (result) {
deepEqual(result, doc_tuple_list, 'Storage content');
});
}
function isEmptyStorage(storage) {
return equalStorage(storage, []);
}
function equalRemoteStorageCallCount(mock_count, expected_count) {
for (i = 0; i < name_list.length; i += 1) {
if (!expected_count.hasOwnProperty(name_list[i])) {
expected_count[name_list[i]] = 0;
}
}
deepEqual(mock_count, expected_count, 'Expected method call count');
}
///////////////////////////////////////////////////////
// Module
///////////////////////////////////////////////////////
module("scenario_officejs", {
setup: function () {
this.remote_mock_options = {
mock: {
remove: function () {
throw new Error('remove not supported');
},
removeAttachment: function () {
throw new Error('removeAttachment not supported');
},
allAttachments: function () {
return {data: null};
},
post: function (doc) {
var context = this;
return this._sub_storage.post(doc)
.push(function (post_id) {
context._options.last_post_id = post_id;
return post_id;
});
}
},
count: {}
};
this.jio = jIO.createJIO({
type: "replicate",
query: {
query: 'portal_type:"Foo"',
sort_on: [["modification_date", "descending"]]
},
signature_hash_key: 'modification_date',
use_remote_post: true,
conflict_handling: 1,
check_local_attachment_modification: true,
check_local_attachment_creation: true,
check_remote_attachment_modification: true,
check_remote_attachment_creation: true,
check_remote_attachment_deletion: true,
check_local_deletion: false,
parallel_operation_amount: 10,
parallel_operation_attachment_amount: 10,
local_sub_storage: {
type: "query",
sub_storage: {
type: "uuid",
sub_storage: {
type: "memory"
}
}
},
signature_sub_storage: {
type: "query",
sub_storage: {
type: "memory"
}
},
remote_sub_storage: {
type: "saferepair",
sub_storage: {
type: "mock",
options: this.remote_mock_options
}
}
});
}
});
///////////////////////////////////////////////////////
// Do nothing cases
///////////////////////////////////////////////////////
test("empty: nothing to do", function () {
expect(2);
stop();
var test = this;
this.jio.repair()
.then(function () {
return RSVP.all([
isEmptyStorage(test.jio),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("already synced: nothing to do", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
blob = new Blob(['a']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
equalStorage(test.jio, [[doc_id, doc, blob]]),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////
// Remote creation
///////////////////////////////////////////////////////
test("remote document creation: copy", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
blob = new Blob(['a']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
equalStorage(test.jio, [[doc_id, doc, blob]]),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1, get: 1, getAttachment: 1, allAttachments: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////
// Remote modification
///////////////////////////////////////////////////////
test("remote document modification: copy", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
doc2 = {title: doc_id + 'a', portal_type: "Foo", modification_date: 'b'},
blob = new Blob(['a']),
blob2 = new Blob(['b']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
return putFullDoc(test.jio.__storage._remote_sub_storage, doc_id, doc2,
blob2);
})
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
equalStorage(test.jio, [[doc_id, doc2, blob2]]),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1, get: 1, getAttachment: 1, allAttachments: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////
// Remote hide
///////////////////////////////////////////////////////
test("remote document deletion: delete", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
blob = new Blob(['a']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
test.remote_mock_options.mock.buildQuery = function () {
return [];
};
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
isEmptyStorage(test.jio),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////
// Local creation
///////////////////////////////////////////////////////
test("local document creation: copy", function () {
expect(3);
stop();
var test = this,
doc_id = 'abc',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
blob = new Blob(['a']);
putFullDoc(this.jio, doc_id, doc, blob)
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
equalStorage(
test.jio,
[[test.remote_mock_options.last_post_id, doc, blob]]
),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1, post: 1, putAttachment: 1, allAttachments: 1}
)
]);
})
.then(function () {
return equalStorage(
test.jio.__storage._remote_sub_storage,
[[test.remote_mock_options.last_post_id, doc, blob]]
);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////
// Local modification
///////////////////////////////////////////////////////
test("local document modification: copy", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
doc2 = {title: doc_id + 'a', portal_type: "Foo", modification_date: 'b'},
blob = new Blob(['a']),
blob2 = new Blob(['b']),
last_id;
putFullDoc(this.jio, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
last_id = test.remote_mock_options.last_post_id;
return putFullDoc(test.jio, last_id, doc2, blob2);
})
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
equalStorage(
test.jio.__storage._remote_sub_storage,
[[last_id, doc2, blob2]]
),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1, put: 1,
allAttachments: 1, putAttachment: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////
// Conflict
///////////////////////////////////////////////////////
test("both modification: keep local", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
doc2 = {title: doc_id + 'a', portal_type: "Foo", modification_date: 'b'},
doc3 = {title: doc_id + 'c', portal_type: "Foo", modification_date: 'c'},
blob = new Blob(['a']),
blob2 = new Blob(['b']),
blob3 = new Blob(['c']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
return RSVP.all([
putFullDoc(test.jio.__storage._remote_sub_storage, doc_id,
doc2, blob2),
putFullDoc(test.jio, doc_id, doc3, blob3)
]);
})
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
equalStorage(
test.jio.__storage._remote_sub_storage,
[[doc_id, doc3, blob3]]
),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1, put: 1,
allAttachments: 1, putAttachment: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local modification / frozen remote", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
doc2 = {title: doc_id + 'a', portal_type: "Foo", modification_date: 'b'},
blob = new Blob(['a']),
blob2 = new Blob(['b']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
return putFullDoc(test.jio, doc_id, doc2, blob2);
})
.then(function () {
test.remote_mock_options.mock.put = function (id) {
if (id === doc_id) {
throw new jIO.util.jIOError('put not allowed', 403);
}
return id;
};
test.remote_mock_options.mock.putAttachment = function (id) {
if (id === doc_id) {
throw new jIO.util.jIOError('putattachment not allowed', 403);
}
return id;
};
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
ok(false, 'notimplemented');
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
///////////////////////////////////////////////////////
// Local deletion (aka, people playing manually with the browser storage)
///////////////////////////////////////////////////////
test("local document deletion: do nothing", function () {
expect(3);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
blob = new Blob(['a']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
return test.jio.remove(doc_id);
})
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
isEmptyStorage(test.jio),
equalStorage(
test.jio.__storage._remote_sub_storage,
[[doc_id, doc, blob]]
),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local attachment deletion: do nothing", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
blob = new Blob(['a']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
return test.jio.removeAttachment(doc_id, ATTACHMENT);
})
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
equalStorage(
test.jio.__storage._remote_sub_storage,
[[doc_id, doc, blob]]
),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
test("local deletion / remote modification", function () {
expect(2);
stop();
var test = this,
doc_id = 'foo_module/1',
doc = {title: doc_id, portal_type: "Foo", modification_date: 'a'},
doc2 = {title: doc_id + 'a', portal_type: "Foo", modification_date: 'b'},
blob = new Blob(['a']),
blob2 = new Blob(['b']);
putFullDoc(this.jio.__storage._remote_sub_storage, doc_id, doc, blob)
.then(function () {
return test.jio.repair();
})
.then(function () {
return RSVP.all([
putFullDoc(test.jio.__storage._remote_sub_storage, doc_id,
doc2, blob2),
test.jio.remove(doc_id)
]);
})
.then(function () {
resetCount(test.remote_mock_options.count);
return test.jio.repair();
})
.then(function () {
return RSVP.all([
equalStorage(
test.jio,
[[doc_id, doc2, blob2]]
),
equalStorage(
test.jio.__storage._remote_sub_storage,
[[doc_id, doc2, blob2]]
),
equalRemoteStorageCallCount(
test.remote_mock_options.count,
{buildQuery: 1, get: 1,
allAttachments: 1, getAttachment: 1}
)
]);
})
.fail(function (error) {
ok(false, error);
})
.always(function () {
start();
});
});
}(QUnit, jIO, Blob));
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OfficeJS Coverage Scenario</title>
<script src="../node_modules/rsvp/dist/rsvp-2.0.4.js"></script>
<script src="../dist/jio-latest.js"></script>
<link rel="stylesheet" href="../node_modules/grunt-contrib-qunit/test/libs/qunit.css" type="text/css" media="screen"/>
<script src="../node_modules/grunt-contrib-qunit/test/libs/qunit.js" type="text/javascript"></script>
<script src="scenario_officejs.js"></script>
</head>
<body>
<h1 id="qunit-header">OfficeJS Coverage Scenario</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup, will be hidden</div>
</body>
</html>
...@@ -156,9 +156,12 @@ ...@@ -156,9 +156,12 @@
local_sub_storage: { local_sub_storage: {
type: "history", type: "history",
sub_storage: { sub_storage: {
type: "uuid", type: "query",
sub_storage: { sub_storage: {
type: "memory" type: "uuid",
sub_storage: {
type: "memory"
}
} }
} }
}, },
......
...@@ -133,7 +133,6 @@ ...@@ -133,7 +133,6 @@
throw error; throw error;
} }
} }
return substorage.buildQuery(sub_options) return substorage.buildQuery(sub_options)
// Include docs if needed // Include docs if needed
...@@ -198,6 +197,7 @@ ...@@ -198,6 +197,7 @@
if (options.select_list) { if (options.select_list) {
options.select_list.push("__id"); options.select_list.push("__id");
} }
console.log("query:",options.query);
result = jIO.QueryFactory.create(options.query || "", result = jIO.QueryFactory.create(options.query || "",
context._key_schema). context._key_schema).
exec(data_rows, options); exec(data_rows, options);
......
...@@ -228,7 +228,6 @@ ...@@ -228,7 +228,6 @@
number_queue) { number_queue) {
var result_promise_list = [], var result_promise_list = [],
i; i;
function pushAndExecute(queue) { function pushAndExecute(queue) {
queue queue
.push(function () { .push(function () {
...@@ -1297,12 +1296,12 @@ ...@@ -1297,12 +1296,12 @@
} }
ReplicateStorage.prototype.repair = function () { ReplicateStorage.prototype.repair = function () {
var context = this, var context = this,
argument_list = arguments, argument_list = arguments,
skip_document_dict = {}, skip_document_dict = {},
skip_deleted_document_dict = {}, skip_deleted_document_dict = {},
cache = {}; cache = {};
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
// Ensure that the document storage is usable // Ensure that the document storage is usable
...@@ -1425,6 +1424,7 @@ ...@@ -1425,6 +1424,7 @@
context._check_remote_attachment_deletion) { context._check_remote_attachment_deletion) {
// Attachments are synchronized if and only if their parent document // Attachments are synchronized if and only if their parent document
// has been also marked as synchronized. // has been also marked as synchronized.
return context._signature_sub_storage.allDocs({ return context._signature_sub_storage.allDocs({
select_list: ['hash', 'attachment_hash', 'from_local'] select_list: ['hash', 'attachment_hash', 'from_local']
}) })
...@@ -1433,6 +1433,7 @@ ...@@ -1433,6 +1433,7 @@
local_argument_list = [], local_argument_list = [],
row, row,
len = result.data.total_rows; len = result.data.total_rows;
//skip_deleted_document_dict = {};
for (i = 0; i < len; i += 1) { for (i = 0; i < len; i += 1) {
row = result.data.rows[i]; row = result.data.rows[i];
......
...@@ -22,69 +22,74 @@ ...@@ -22,69 +22,74 @@
return re.test(id); return re.test(id);
} }
function removeOldRevs( function removeOldRevisions(
substorage, substorage,
results, results,
keepDoc keepDocument
) { ) {
var ind, var index,
promises = [], promise_list = [],
seen = {}, seen_dict = {},
docum, document,
log, log,
start_ind, start_index,
new_promises, new_promise_list,
doc_id, document_id;
checkIsId, for (index = 0; index < results.data.rows.length; index += 1) {
removeDoc; document = results.data.rows[index];
for (ind = 0; ind < results.data.rows.length; ind += 1) {
docum = results.data.rows[ind];
// Count the number of revisions of each document, and delete older // Count the number of revisions of each document, and delete older
// ones. // ones.
if (!seen.hasOwnProperty(docum.value.doc_id)) { if (!seen_dict.hasOwnProperty(document.value.document_id)) {
seen[docum.value.doc_id] = {count: 0}; seen_dict[document.value.document_id] = {count: 0};
} }
log = seen[docum.value.doc_id]; log = seen_dict[document.value.document_id];
log.count += 1; log.count += 1;
//log.id = docum.id; //log.id = docum.id;
// Record the index of the most recent edit that is before the cutoff // Record the index of the most recent edit that is before the cutoff
if (!log.hasOwnProperty("s") && !keepDoc({doc: docum, log: log})) { if (!log.hasOwnProperty("last_edit_before_cutoff") &&
log.s = ind; !keepDocument({document: document, log: log})) {
log.last_edit_before_cutoff = index;
} }
// Record the index of the most recent put or remove // Record the index of the most recent put or remove
if ((!log.hasOwnProperty("pr")) && if ((!log.hasOwnProperty("put_or_remove")) &&
(docum.value.op === "put" || docum.value.op === "remove")) { (document.value.op === "put" || document.value.op === "remove")) {
log.pr = ind; log.put_or_remove = index;
log.final = ind; log.final = index;
} }
if ((docum.op === "putAttachment" || docum.op === "removeAttachment") && if ((document.op === "putAttachment" ||
log.hasOwnProperty(docum.name) && document.op === "removeAttachment") &&
!log[docum.name].hasOwnProperty("prA")) { log.hasOwnProperty(document.name) &&
log[docum.name].prA = ind; !log[document.name].hasOwnProperty("put_or_remove_attachment")) {
log.final = ind; log[document.name].put_or_remove_attachment = index;
log.final = index;
} }
} }
checkIsId = function (d) { function checkIsId(document, document_id) {
return d.value.doc_id === doc_id; return document.value.document_id === document_id;
}; }
removeDoc = function (d) { function removeDocument(document) {
return substorage.remove(d.id); return substorage.remove(document.id);
}; }
for (doc_id in seen) { // Looping over unique documents in storage
if (seen.hasOwnProperty(doc_id)) { for (document_id in seen_dict) {
log = seen[doc_id]; if (seen_dict.hasOwnProperty(document_id)) {
start_ind = Math.max(log.s, log.final + 1);
new_promises = results.data.rows // Identify where to cut to avoid loss of data
.slice(start_ind) log = seen_dict[document_id];
start_index = Math.max(log.last_edit_before_cutoff, log.final + 1);
// Record all remove promises for this document
new_promise_list = results.data.rows
.slice(start_index)
.filter(checkIsId) .filter(checkIsId)
.map(removeDoc); .map(removeDocument);
promises = promises.concat(new_promises); promise_list = promise_list.concat(new_promise_list);
} }
} }
return RSVP.all(promises); return RSVP.all(promise_list);
} }
function throwCantFindError(id) { function throwCantFindError(id) {
...@@ -114,61 +119,60 @@ ...@@ -114,61 +119,60 @@
} else { } else {
this._include_revisions = false; this._include_revisions = false;
} }
var substorage = this._sub_storage;
} }
RevisionStorage.prototype.packOldRevisions = function (save_info) { RevisionStorage.prototype.packOldRevisions = function (save_info) {
/** /**
save_info has this form: save_info has this form:
{ {
keep_latest_num: 10, keep_latest_num: 10,
keep_active_revs: timestamp keep_active_revs: timestamp
} }
keep_latest_num = x: keep at most the x latest copies of each unique doc keep_latest_num = x: keep at most the x latest copies of each unique doc
keep_active_revs = x: throw away all outdated revisions from before x keep_active_revs = x: throw away all outdated revisions from before x
**/ **/
var substorage = this._sub_storage, var substorage = this._sub_storage,
options = { option_dict = {
sort_on: [["timestamp", "descending"]], sort_on: [["timestamp", "descending"]],
select_list: ["doc", "doc_id", "op"] select_list: ["document", "document_id", "operation"]
}, },
keep_fixed_num = save_info.hasOwnProperty("keep_latest_num"); keep_fixed_number = save_info.hasOwnProperty("keep_latest_number");
return substorage.allDocs(options) return substorage.allDocs(option_dict)
.push(function (results) { .push(function (results) {
if (keep_fixed_num) { if (keep_fixed_number) {
return removeOldRevs(substorage, results, function (data) { return removeOldRevisions(substorage, results, function (data) {
return data.log.count <= save_info.keep_latest_num; return data.log.count <= save_info.keep_latest_number;
});
}
return removeOldRevs(substorage, results, function (data) {
return data.doc.id > save_info.keep_active_revs;
}); });
}
return removeOldRevisions(substorage, results, function (data) {
return data.doc.id > save_info.keep_active_revisions;
}); });
}; });
};
RevisionStorage.prototype.get = function (id_in) { RevisionStorage.prototype.get = function (id_in) {
// Query to get the last edit made to this document // Query to get the last edit made to this document
var substorage = this._sub_storage, var substorage = this._sub_storage,
doc_id_query, document_id_query,
metadata_query, metadata_query,
options; option_dict;
if (this._include_revisions) { if (this._include_revisions) {
doc_id_query = new SimpleQuery({ document_id_query = new SimpleQuery({
operator: "<=", operator: "<=",
key: "timestamp", key: "timestamp",
value: id_in value: id_in
}); });
} else { } else {
doc_id_query = new SimpleQuery({key: "doc_id", value: id_in}); document_id_query = new SimpleQuery({key: "document_id", value: id_in});
} }
// Include id_in as value in query object for safety // Include id_in as value in query object for safety
metadata_query = new ComplexQuery({ metadata_query = new ComplexQuery({
operator: "AND", operator: "AND",
query_list: [ query_list: [
doc_id_query, document_id_query,
new ComplexQuery({ new ComplexQuery({
operator: "OR", operator: "OR",
query_list: [ query_list: [
...@@ -178,7 +182,7 @@ ...@@ -178,7 +182,7 @@
}) })
] ]
}); });
options = { option_dict = {
query: metadata_query, query: metadata_query,
select_list: ["op"], select_list: ["op"],
limit: [0, 1], limit: [0, 1],
...@@ -186,7 +190,7 @@ ...@@ -186,7 +190,7 @@
}; };
return substorage.allDocs(options) return substorage.allDocs(option_dict)
.push(function (results) { .push(function (results) {
if (results.data.total_rows > 0) { if (results.data.total_rows > 0) {
if (results.data.rows[0].value.op === "put") { if (results.data.rows[0].value.op === "put") {
...@@ -243,31 +247,31 @@ ...@@ -243,31 +247,31 @@
RevisionStorage.prototype.allAttachments = function (id) { RevisionStorage.prototype.allAttachments = function (id) {
var substorage = this._sub_storage, var substorage = this._sub_storage,
query_obj, query_object,
query_removed_check, query_removed_check,
options, option_dict,
query_doc_id, query_document_id,
options_remcheck, check_if_removed_option_list,
include_revs = this._include_revisions, include_revisions = this._include_revisions,
have_seen_id = false; have_seen_id = false;
// id is a timestamp, and allAttachments will return attachment versions // id is a timestamp, and allAttachments will return attachment versions
// up-to-and-including those made at time id // up-to-and-including those made at time id
if (include_revs) { if (include_revisions) {
query_doc_id = new SimpleQuery({ query_document_id = new SimpleQuery({
operator: "<=", operator: "<=",
key: "timestamp", key: "timestamp",
value: id value: id
}); });
} else { } else {
query_doc_id = new SimpleQuery({key: "doc_id", value: id}); query_document_id = new SimpleQuery({key: "doc_id", value: id});
have_seen_id = true; have_seen_id = true;
} }
query_removed_check = new ComplexQuery({ query_removed_check = new ComplexQuery({
operator: "AND", operator: "AND",
query_list: [ query_list: [
query_doc_id, query_document_id,
new ComplexQuery({ new ComplexQuery({
operator: "OR", operator: "OR",
query_list: [ query_list: [
...@@ -278,10 +282,10 @@ ...@@ -278,10 +282,10 @@
] ]
}); });
query_obj = new ComplexQuery({ query_object = new ComplexQuery({
operator: "AND", operator: "AND",
query_list: [ query_list: [
query_doc_id, query_document_id,
new ComplexQuery({ new ComplexQuery({
operator: "OR", operator: "OR",
query_list: [ query_list: [
...@@ -292,19 +296,19 @@ ...@@ -292,19 +296,19 @@
] ]
}); });
options_remcheck = { check_if_removed_option_list = {
query: query_removed_check, query: query_removed_check,
select_list: ["op", "timestamp"], select_list: ["op", "timestamp"],
limit: [0, 1], limit: [0, 1],
sort_on: [["timestamp", "descending"]] sort_on: [["timestamp", "descending"]]
}; };
options = { option_dict = {
query: query_obj, query: query_object,
sort_on: [["timestamp", "descending"]], sort_on: [["timestamp", "descending"]],
select_list: ["op", "name"] select_list: ["op", "name"]
}; };
return this._sub_storage.allDocs(options_remcheck) return this._sub_storage.allDocs(check_if_removed_option_list)
// Check the document exists and is not removed // Check the document exists and is not removed
.push(function (results) { .push(function (results) {
if (results.data.total_rows > 0) { if (results.data.total_rows > 0) {
...@@ -319,19 +323,19 @@ ...@@ -319,19 +323,19 @@
} }
}) })
.push(function () { .push(function () {
return substorage.allDocs(options); return substorage.allDocs(option_dict);
}) })
.push(function (results) { .push(function (results) {
var seen = {}, var seen_dict = {},
attachments = [], attachment_list = [],
attachment_promises = [], attachment_promise_list = [],
ind, i,
entry; entry;
// If input mapped to a real timestamp, then the first query result must // If input mapped to a real timestamp, then the first query result must
// have the inputted id. Otherwise, unexpected results could arise // have the inputted id. Otherwise, unexpected results could arise
// by inputting nonsensical strings as id when include_revisions = true // by inputting nonsensical strings as id when include_revisions = true
if (include_revs && if (include_revisions &&
results.data.total_rows > 0 && results.data.total_rows > 0 &&
results.data.rows[0].id !== id && results.data.rows[0].id !== id &&
!have_seen_id) { !have_seen_id) {
...@@ -341,20 +345,20 @@ ...@@ -341,20 +345,20 @@
// Only return attachments if: // Only return attachments if:
// (it is the most recent revision) AND (it is a putAttachment) // (it is the most recent revision) AND (it is a putAttachment)
attachments = results.data.rows.filter(function (docum) { attachment_list = results.data.rows.filter(function (document) {
if (!seen.hasOwnProperty(docum.value.name)) { if (!seen_dict.hasOwnProperty(document.value.name)) {
var output = (docum.value.op === "putAttachment"); var output = (document.value.op === "putAttachment");
seen[docum.value.name] = {}; seen_dict[document.value.name] = {};
return output; return output;
} }
}); });
// Assembles object of attachment_name: attachment_object // Assembles object of attachment_name: attachment_object
for (ind = 0; ind < attachments.length; ind += 1) { for (i = 0; i < attachment_list.length; i += 1) {
entry = attachments[ind]; entry = attachment_list[i];
attachment_promises[entry.value.name] = attachment_promise_list[entry.value.name] =
substorage.getAttachment(entry.id, entry.value.name); substorage.getAttachment(entry.id, entry.value.name);
} }
return RSVP.hash(attachment_promises); return RSVP.hash(attachment_promise_list);
}); });
}; };
...@@ -434,13 +438,13 @@ ...@@ -434,13 +438,13 @@
}) })
] ]
}), }),
options = { option_dict = {
query: metadata_query, query: metadata_query,
sort_on: [["timestamp", "descending"]], sort_on: [["timestamp", "descending"]],
limit: [0, 1], limit: [0, 1],
select_list: ["op", "name"] select_list: ["op", "name"]
}; };
return substorage.allDocs(options) return substorage.allDocs(option_dict)
.push(function (results) { .push(function (results) {
if (results.data.total_rows > 0) { if (results.data.total_rows > 0) {
// XXX: issue if attachments are put on a removed document // XXX: issue if attachments are put on a removed document
...@@ -474,67 +478,71 @@ ...@@ -474,67 +478,71 @@
return name === 'list' || return name === 'list' ||
name === 'include' || name === 'include' ||
name === 'query' || name === 'query' ||
name === 'select'; name === 'select' ||
name === 'limit' ||
name === 'sort';
}; };
RevisionStorage.prototype.buildQuery = function (options) { RevisionStorage.prototype.buildQuery = function (option_dict) {
// Set default values // Set default values
if (options === undefined) {options = {}; } if (option_dict === undefined) {option_dict = {}; }
if (options.query === undefined) {options.query = ""; } if (option_dict.query === undefined) {option_dict.query = ""; }
if (options.sort_on === undefined) {options.sort_on = []; } if (option_dict.sort_on === undefined) {option_dict.sort_on = []; }
if (options.select_list === undefined) {options.select_list = []; } if (option_dict.select_list === undefined) {option_dict.select_list = []; }
options.query = jIO.QueryFactory.create(options.query); //option_dict.query = jIO.QueryFactory.create(option_dict.query);
var meta_options = { var meta_option_dict = {
query: "", query: "",
sort_on: [["timestamp", "descending"]], sort_on: [["timestamp", "descending"]],
select_list: ["doc", "op", "doc_id", "timestamp"] select_list: ["doc", "op", "doc_id", "timestamp"]
}, },
include_revs = this._include_revisions; include_revisions = this._include_revisions;
if (include_revs) {// && options.query.key === "doc_id") { // When include_revisions is set, the document metadata will be queried
meta_options.query = options.query; // instead of the documents themselves.
if (include_revisions) {
meta_option_dict.query = option_dict.query;
} }
return this._sub_storage.allDocs(meta_options) return this._sub_storage.allDocs(meta_option_dict)
.push(function (results) { .push(function (result_list) {
results = results.data.rows; result_list = result_list.data.rows;
var seen = {}, var seen_dict = {},
docs_to_query, documents_to_query_list,
i; i;
if (include_revs) { if (include_revisions) {
// We only query on versions mapping to puts or putAttachments // We only query on versions mapping to puts or putAttachments
results = results.map(function (docum, ind) { result_list = result_list.map(function (document, index) {
var data_key; var data_key;
if (docum.value.op === "put") { if (document.value.op === "put") {
return docum; return document;
} }
if (docum.value.op === "remove") { if (document.value.op === "remove") {
docum.value.doc = {}; document.value.doc = {};
return docum; return document;
} }
if (docum.value.op === "putAttachment" || if (document.value.op === "putAttachment" ||
docum.value.op === "removeAttachment") { document.value.op === "removeAttachment") {
// putAttachment document does not contain doc metadata, so we // putAttachment document does not contain doc metadata, so we
// add it from the most recent non-removed put on same id // add it from the most recent non-removed put on same id
docum.value.doc = {}; document.value.doc = {};
for (i = ind + 1; i < results.length; i += 1) { for (i = index + 1; i < result_list.length; i += 1) {
if (results[i].value.doc_id === docum.value.doc_id) { if (result_list[i].value.doc_id === document.value.doc_id) {
if (results[i].value.op === "put") { if (result_list[i].value.op === "put") {
for (data_key in results[i].value.doc) { for (data_key in result_list[i].value.doc) {
if (results[i].value.doc.hasOwnProperty(data_key)) { if (result_list[i].value.doc.hasOwnProperty(data_key)) {
docum.value.doc[data_key] = document.value.doc[data_key] =
results[i].value.doc[data_key]; result_list[i].value.doc[data_key];
} }
} }
return docum; return document;
} }
// If most recent metadata edit before the attachment edit // If most recent metadata edit before the attachment edit
// was a remove, then leave doc empty // was a remove, then leave doc empty
if (results[i].value.op === "remove") { if (result_list[i].value.op === "remove") {
return docum; return document;
} }
} }
} }
...@@ -545,43 +553,43 @@ ...@@ -545,43 +553,43 @@
// Only query on latest revisions of non-removed documents/attachment // Only query on latest revisions of non-removed documents/attachment
// edits // edits
results = results.map(function (docum, ind) { result_list = result_list.map(function (document, index) {
var data_key; var data_key;
if (docum.value.op === "put") { if (document.value.op === "put") {
// Mark as read and include in query // Mark as read and include in query
if (!seen.hasOwnProperty(docum.value.doc_id)) { if (!seen_dict.hasOwnProperty(document.value.doc_id)) {
seen[docum.value.doc_id] = {}; seen_dict[document.value.doc_id] = {};
return docum; return document;
} }
} else if (docum.value.op === "remove" || } else if (document.value.op === "remove" ||
docum.value.op === "removeAttachment") { document.value.op === "removeAttachment") {
// Mark as read but do not include in query // Mark as read but do not include in query
seen[docum.value.doc_id] = {}; seen_dict[document.value.doc_id] = {};
} else if (docum.value.op === "putAttachment") { } else if (document.value.op === "putAttachment") {
// If latest edit, mark as read, add document metadata from most // If latest edit, mark as read, add documentent metadata from
// recent put, and add to query // most recent put, and add to query
if (!seen.hasOwnProperty(docum.value.doc_id)) { if (!seen_dict.hasOwnProperty(document.value.doc_id)) {
seen[docum.value.doc_id] = {}; seen_dict[document.value.doc_id] = {};
docum.value.doc = {}; document.value.doc = {};
for (i = ind + 1; i < results.length; i += 1) { for (i = index + 1; i < result_list.length; i += 1) {
if (results[i].value.doc_id === docum.value.doc_id) { if (result_list[i].value.doc_id === document.value.doc_id) {
if (results[i].value.op === "put") { if (result_list[i].value.op === "put") {
for (data_key in results[i].value.doc) { for (data_key in result_list[i].value.doc) {
if (results[i].value.doc.hasOwnProperty(data_key)) { if (result_list[i].value.doc.hasOwnProperty(data_key)) {
docum.value.doc[data_key] = document.value.doc[data_key] =
results[i].value.doc[data_key]; result_list[i].value.doc[data_key];
} }
} }
return docum; return document;
} }
if (results[i].value.op === "remove") { if (result_list[i].value.op === "remove") {
// If most recent edit on document was a remove before // If most recent edit on document was a remove before
// this attachment, then don't include attachment in query // this attachment, then don't include attachment in query
return false; return false;
} }
docum.value.doc = {}; document.value.doc = {};
} }
} }
} }
...@@ -589,31 +597,36 @@ ...@@ -589,31 +597,36 @@
return false; return false;
}); });
} }
docs_to_query = results documents_to_query_list = result_list
// Filter out all docs flagged as false in previous map call // Filter out all docs flagged as false in previous map call
.filter(function (docum) { .filter(function (document) {
return docum; return document;
}) })
// Put into correct format to be passed back to query storage // Put into correct format to be passed back to query storage
.map(function (docum) { .map(function (document) {
if (include_revisions) {
if (include_revs) { document.id = document.value.timestamp;
docum.id = docum.value.timestamp; //document.doc = document.value.doc;
//docum.doc = docum.value.doc;
} else { } else {
docum.id = docum.value.doc_id; document.id = document.value.doc_id;
//docum.doc = docum.value.doc; //document.doc = document.value.doc;
}
delete document.value.doc_id;
delete document.value.timestamp;
delete document.value.op;
var temp = document.value.doc;
document.value = {};
for (var selection in temp) {
if (option_dict.select_list.indexOf(selection) > -1) {
document.value[selection] = temp[selection];
}
} }
delete docum.value.doc_id; //document.value = {};
delete docum.value.timestamp; return document;
delete docum.value.op;
docum.value = docum.value.doc;
//docum.value = {};
return docum;
}); });
return docs_to_query; return documents_to_query_list;
}); });
}; };
......
/*jslint nomen: true*/
/*global jIO, RSVP*/
(function (jIO, RSVP) {
"use strict";
/**
* The jIO SafeRepairStorage extension
*
* @class SafeRepairStorage
* @constructor
*/
function SafeRepairStorage(spec) {
this._sub_storage = jIO.createJIO(spec.sub_storage);
this._id_dict = {};
}
SafeRepairStorage.prototype.get = function () {
return this._sub_storage.get.apply(this._sub_storage, arguments);
};
SafeRepairStorage.prototype.allAttachments = function () {
return this._sub_storage.allAttachments.apply(this._sub_storage, arguments);
};
SafeRepairStorage.prototype.post = function () {
return this._sub_storage.post.apply(this._sub_storage, arguments);
};
SafeRepairStorage.prototype.put = function (id, doc) {
var storage = this;
return this._sub_storage.put.apply(this._sub_storage, arguments)
.push(undefined, function (error) {
if (error instanceof jIO.util.jIOError &&
error.status_code === 403) {
if (storage._id_dict[id]) {
return storage._sub_storage.put(storage._id_dict[id], doc);
}
return storage._sub_storage.post(doc)
.push(function (sub_id) {
storage._id_dict[id] = sub_id;
return sub_id;
});
}
});
};
SafeRepairStorage.prototype.remove = function () {
return;
};
SafeRepairStorage.prototype.getAttachment = function () {
return this._sub_storage.getAttachment.apply(this._sub_storage, arguments);
};
SafeRepairStorage.prototype.putAttachment = function (id, attachment_id,
attachment) {
var storage = this;
return this._sub_storage.putAttachment.apply(this._sub_storage, arguments)
.push(undefined, function (error) {
if (error instanceof jIO.util.jIOError &&
error.status_code === 403) {
return new RSVP.Queue()
.push(function () {
if (storage._id_dict[id]) {
return storage._id_dict[id];
}
return storage._sub_storage.get(id)
.push(function (doc) {
return storage._sub_storage.post(doc);
});
})
.push(function (sub_id) {
storage._id_dict[id] = sub_id;
return storage._sub_storage.putAttachment(sub_id, attachment_id,
attachment);
});
}
});
};
SafeRepairStorage.prototype.removeAttachment = function () {
return;
};
SafeRepairStorage.prototype.repair = function () {
return this._sub_storage.repair.apply(this._sub_storage, arguments);
};
SafeRepairStorage.prototype.hasCapacity = function (name) {
return this._sub_storage.hasCapacity(name);
};
SafeRepairStorage.prototype.buildQuery = function () {
return this._sub_storage.buildQuery.apply(this._sub_storage,
arguments);
};
jIO.addStorage('saferepair', SafeRepairStorage);
}(jIO, RSVP));
\ No newline at end of file
...@@ -1414,13 +1414,20 @@ ...@@ -1414,13 +1414,20 @@
}) })
.push(function () { .push(function () {
return RSVP.all([ return RSVP.all([
jio.allDocs(), jio.allDocs({select_list: ['title']}),
jio.allDocs({query: "title: version0"}), jio.allDocs({
jio.allDocs({limit: [0, 1]}), query: "title: version0",
jio.allDocs({}) select_list: ['title']
}),
jio.allDocs({
limit: [0, 1],
select_list: ['title']
}),
jio.allDocs({select_list: ['title']})
]); ]);
}) })
.push(function (results) { .push(function (results) {
console.log(results);
var ind = 0; var ind = 0;
for (ind = 0; ind < results.length - 1; ind += 1) { for (ind = 0; ind < results.length - 1; ind += 1) {
deepEqual(results[ind], deepEqual(results[ind],
...@@ -1436,12 +1443,14 @@ ...@@ -1436,12 +1443,14 @@
"Exactly one result returned"); "Exactly one result returned");
deepEqual(results.data.rows[0], { deepEqual(results.data.rows[0], {
doc: {}, doc: {},
value: {}, value: {
title: "version0"
},
//timestamp: timestamp, //timestamp: timestamp,
id: "doc" id: "doc"
}, },
"Correct document format is returned." "Correct document format is returned."
); );///
return not_revision.allDocs(); return not_revision.allDocs();
}) })
.push(function (results) { .push(function (results) {
...@@ -1609,6 +1618,7 @@ ...@@ -1609,6 +1618,7 @@
); );
}) })
.push(function () { .push(function () {
console.log("about to fail");
return revision.allDocs({ return revision.allDocs({
query: "", query: "",
select_list: ["title", "subtitle"] select_list: ["title", "subtitle"]
...@@ -1690,7 +1700,7 @@ ...@@ -1690,7 +1700,7 @@
"A different storage type can retrieve all versions as expected."); "A different storage type can retrieve all versions as expected.");
}) })
.fail(function (error) { .fail(function (error) {
//console.log(error); console.log(error);
ok(false, error); ok(false, error);
}) })
.always(function () {start(); }); .always(function () {start(); });
...@@ -1826,14 +1836,13 @@ ...@@ -1826,14 +1836,13 @@
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
query: "NOT (date: > 2)", query: "NOT (date: > 2)",
select_list: ["date", "non-existent-key"], select_list: ["date"],//, "non-existent-key"],
sort_on: [["date", "ascending"], sort_on: [["date", "ascending"]]//,
["non-existent-key", "ascending"] //["non-existent-key", "ascending"]]
]
}); });
}) })
.push(function (results) { .push(function (results) {
equal(results.data.total_rows, 3); equal(results.data.total_rows, 3, "Correct number of results");
deepEqual(results.data.rows, [ deepEqual(results.data.rows, [
{ {
doc: {}, doc: {},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment