Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
jio
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Bryan Kaperick
jio
Commits
d7fcbbaf
Commit
d7fcbbaf
authored
Mar 01, 2013
by
Tristan Cavelier
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revision storage redesigned to avoid some unknown errors
parent
049ea125
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1555 additions
and
1469 deletions
+1555
-1469
src/jio.storage/replicaterevisionstorage.js
src/jio.storage/replicaterevisionstorage.js
+250
-0
src/jio.storage/revisionstorage.js
src/jio.storage/revisionstorage.js
+719
-1072
test/jiotests.js
test/jiotests.js
+586
-397
No files found.
src/jio.storage/replicaterevisionstorage.js
View file @
d7fcbbaf
...
...
@@ -187,6 +187,253 @@ jIO.addStorageType('replicaterevision', function (spec, my) {
}
};
////////////////////////////////////////////////////////////////////////////////
that
.
check
=
function
(
command
)
{
priv
.
check
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
that
.
success
,
that
.
error
);
};
that
.
repair
=
function
(
command
)
{
priv
.
repair
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
true
,
that
.
success
,
that
.
error
);
};
priv
.
check
=
function
(
doc
,
option
,
success
,
error
)
{
priv
.
repair
(
doc
,
option
,
false
,
success
,
error
);
};
priv
.
repair
=
function
(
doc
,
option
,
repair
,
callback
)
{
var
functions
=
{};
// console.log("priv.repair");
callback
=
callback
||
priv
.
emptyFunction
;
option
=
option
||
{};
functions
.
begin
=
function
()
{
// console.log("repair begin");
functions
.
getAllDocuments
(
functions
.
newParam
(
doc
,
option
,
repair
));
};
functions
.
newParam
=
function
(
doc
,
option
,
repair
)
{
// console.log("repair new param");
var
param
=
{
"
doc
"
:
doc
,
"
option
"
:
option
,
"
repair
"
:
repair
,
"
responses
"
:
{
"
count
"
:
0
,
"
list
"
:
[
// 0: response0
// 1: response1
// 2: response2
],
"
stats
"
:
{
// responseA: [0, 1]
// responseB: [2]
},
"
stats_items
"
:
[
// 0: [responseA, [0, 1]]
// 1: [responseB, [2]]
]
},
"
conflicts
"
:
{
// revC: true
// revD: true
},
"
deal_result_state
"
:
"
ok
"
,
"
my_rev
"
:
undefined
};
param
.
responses
.
list
.
length
=
priv
.
storage_list
.
length
;
return
param
;
};
functions
.
getAllDocuments
=
function
(
param
)
{
// console.log("repair getAllDocument");
var
i
,
doc
=
priv
.
clone
(
param
.
doc
),
option
=
priv
.
clone
(
param
.
option
);
option
.
conflicts
=
true
;
option
.
revs
=
true
;
option
.
revs_info
=
true
;
for
(
i
=
0
;
i
<
priv
.
storage_list
.
length
;
i
+=
1
)
{
// if the document is not loaded
priv
.
send
(
"
get
"
,
i
,
doc
,
option
,
functions
.
dealResults
(
param
));
}
functions
.
finished_count
+=
1
;
};
functions
.
dealResults
=
function
(
param
)
{
// console.log("repair dealResults");
return
function
(
method
,
index
,
err
,
response
)
{
if
(
param
.
deal_result_state
!==
"
ok
"
)
{
// deal result is in a wrong state, exit
// console.log("repair dealResults wrong state");
return
;
}
if
(
err
)
{
if
(
err
.
status
!==
404
)
{
// get document failed, exit
param
.
deal_result_state
=
"
error
"
;
// console.log("repair dealResults error");
callback
({
"
status
"
:
40
,
"
statusText
"
:
"
Check Failed
"
,
"
error
"
:
"
check_failed
"
,
"
message
"
:
"
An error occured on the sub storage
"
,
"
reason
"
:
err
.
reason
});
return
;
}
}
// success to get the document
// add the response in memory
param
.
responses
.
count
+=
1
;
param
.
responses
.
list
[
index
]
=
response
;
// add the conflicting revision for other synchronizations
functions
.
addConflicts
(
param
,
(
response
||
{}).
_conflicts
);
if
(
param
.
responses
.
count
!==
param
.
responses
.
list
.
length
)
{
// this is not the last response, wait for the next response
// console.log("repair dealResults not last");
return
;
}
// console.log("repair dealResults last");
// this is now the last response
functions
.
makeResponsesStats
(
param
.
responses
);
if
(
param
.
responses
.
stats_items
.
length
===
1
)
{
// the responses are equals!
// console.log("repair dealResults OK");
callback
(
undefined
,
{
"
ok
"
:
true
,
"
id
"
:
param
.
doc
.
_id
,
"
rev
"
:
(
typeof
param
.
responses
.
list
[
0
]
===
"
object
"
?
param
.
responses
.
list
[
0
].
_rev
:
undefined
)
});
return
;
}
// the responses are different
if
(
param
.
repair
===
false
)
{
// do not repair
callback
({
"
status
"
:
41
,
"
statusText
"
:
"
Check Not Ok
"
,
"
error
"
:
"
check_not_ok
"
,
"
message
"
:
"
Some documents are different in the sub storages
"
,
"
reason
"
:
"
Storage contents differ
"
});
return
;
}
// repair
functions
.
synchronizeAllSubStorage
(
param
);
if
(
param
.
option
.
synchronize_conflicts
!==
false
)
{
functions
.
synchronizeConflicts
(
param
);
}
};
};
functions
.
addConflicts
=
function
(
param
,
list
)
{
// console.log("repair addConflicts");
var
i
;
list
=
list
||
[];
for
(
i
=
0
;
i
<
list
.
length
;
i
+=
1
)
{
param
.
conflicts
[
list
[
i
]]
=
true
;
}
};
functions
.
makeResponsesStats
=
function
(
responses
)
{
// console.log("repair makeResponseStats");
var
i
,
str_response
;
for
(
i
=
0
;
i
<
responses
.
count
;
i
+=
1
)
{
str_response
=
JSON
.
stringify
(
responses
.
list
[
i
]);
if
(
responses
.
stats
[
str_response
]
===
undefined
)
{
responses
.
stats
[
str_response
]
=
[];
responses
.
stats_items
.
push
([
str_response
,
responses
.
stats
[
str_response
]
]);
}
responses
.
stats
[
str_response
].
push
(
i
);
}
};
functions
.
synchronizeAllSubStorage
=
function
(
param
)
{
// console.log("repair synchronizeAllSubStorage");
var
i
,
j
,
len
=
param
.
responses
.
stats_items
.
length
;
for
(
i
=
0
;
i
<
len
;
i
+=
1
)
{
// browsing responses
for
(
j
=
0
;
j
<
len
;
j
+=
1
)
{
// browsing storage list
if
(
i
!==
j
)
{
functions
.
synchronizeResponseToSubStorage
(
param
,
param
.
responses
.
stats_items
[
i
][
0
],
param
.
responses
.
stats_items
[
j
][
1
]
);
}
}
}
functions
.
finished_count
-=
1
;
};
functions
.
synchronizeResponseToSubStorage
=
function
(
param
,
response
,
storage_list
)
{
// console.log("repair synchronizeResponseToSubStorage");
var
i
,
new_doc
;
if
(
response
===
undefined
)
{
// no response to sync
return
;
}
for
(
i
=
0
;
i
<
storage_list
.
length
;
i
+=
1
)
{
new_doc
=
JSON
.
parse
(
response
);
new_doc
.
_revs
=
new_doc
.
_revisions
;
delete
new_doc
.
_rev
;
delete
new_doc
.
_revisions
;
delete
new_doc
.
_conflicts
;
functions
.
finished_count
+=
1
;
priv
.
send
(
"
put
"
,
storage_list
[
i
],
new_doc
,
param
.
option
,
functions
.
finished
);
}
};
functions
.
synchronizeConflicts
=
function
(
param
)
{
// console.log("repair synchronizeConflicts");
var
rev
,
new_doc
,
new_option
;
new_option
=
priv
.
clone
(
param
.
option
);
new_option
.
synchronize_conflict
=
false
;
for
(
rev
in
param
.
conflicts
)
{
if
(
param
.
conflicts
.
hasOwnProperty
(
rev
))
{
new_doc
=
priv
.
clone
(
param
.
doc
);
new_doc
.
_rev
=
rev
;
// no need to synchronize all the conflicts again, do it once
functions
.
getAllDocuments
(
functions
.
newParam
(
new_doc
,
new_option
,
param
.
repair
));
}
}
};
functions
.
finished_count
=
0
;
functions
.
finished
=
function
()
{
// console.log("repair finished " + functions.finished_count);
functions
.
finished_count
-=
1
;
if
(
functions
.
finished_count
===
0
)
{
// console.log("repair ended");
callback
(
undefined
,
{
"
ok
"
:
true
,
"
id
"
:
doc
.
_id
});
}
};
functions
.
begin
();
};
////////////////////////////////////////////////////////////////////////////////
/**
* Post the document metadata to all sub storages
* @method post
...
...
@@ -397,6 +644,9 @@ jIO.addStorageType('replicaterevision', function (spec, my) {
}
}
that
.
success
(
response
);
setTimeout
(
function
()
{
priv
.
repair
({
"
_id
"
:
doc
.
_id
},
command
.
cloneOption
(),
true
);
});
};
functions
.
error_count
=
0
;
functions
.
error
=
function
(
err
)
{
...
...
src/jio.storage/revisionstorage.js
View file @
d7fcbbaf
...
...
@@ -9,20 +9,31 @@
* "sub_storage": <sub storage description>
* }
*/
jIO
.
addStorageType
(
'
revision
'
,
function
(
spec
,
my
)
{
jIO
.
addStorageType
(
"
revision
"
,
function
(
spec
,
my
)
{
"
use strict
"
;
var
that
,
priv
=
{};
var
that
=
{}
,
priv
=
{};
spec
=
spec
||
{};
that
=
my
.
basicStorage
(
spec
,
my
);
// ATTRIBUTES //
priv
.
doc_tree_suffix
=
"
.revision_tree.json
"
;
priv
.
sub_storage
=
spec
.
sub_storage
;
// METHODS //
/**
* Constructor
*/
priv
.
RevisionStorage
=
function
()
{
// no init
};
priv
.
substorage_key
=
"
sub_storage
"
;
priv
.
doctree_suffix
=
"
.revision_tree.json
"
;
priv
.
substorage
=
spec
[
priv
.
substorage_key
];
/**
* Description to store in order to be restored later
* @method specToStore
* @return {object} Descriptions to store
*/
that
.
specToStore
=
function
()
{
var
o
=
{};
o
[
priv
.
substorage_key
]
=
priv
.
substorage
;
return
o
;
return
{
"
sub_storage
"
:
priv
.
sub_storage
}
;
};
/**
...
...
@@ -70,1200 +81,836 @@ jIO.addStorageType('revision', function (spec, my) {
};
/**
*
Returns an array version of a revision string
* @method
revisionToArray
* @param {
string} revision The revision string
* @return {
array} Array containing a revision number and a hash
*
Checks a revision format
* @method
checkDocumentRevisionFormat
* @param {
object} doc The document object
* @return {
object} null if ok, else error object
*/
priv
.
revisionToArray
=
function
(
revision
)
{
if
(
typeof
revision
===
"
string
"
)
{
return
[
parseInt
(
revision
.
split
(
'
-
'
)[
0
],
10
),
revision
.
split
(
'
-
'
)[
1
]];
priv
.
checkDocumentRevisionFormat
=
function
(
doc
)
{
var
send_error
=
function
(
message
)
{
return
{
"
status
"
:
31
,
"
statusText
"
:
"
Wrong Revision Format
"
,
"
error
"
:
"
wrong_revision_format
"
,
"
message
"
:
message
,
"
reason
"
:
"
Revision is wrong
"
};
};
if
(
typeof
doc
.
_rev
===
"
string
"
)
{
if
(
/^
[
0-9
]
+-
[
0-9a-zA-Z
]
+$/
.
test
(
doc
.
_rev
)
===
false
)
{
return
send_error
(
"
The document revision does not match
"
+
"
^[0-9]+-[0-9a-zA-Z]+$
"
);
}
}
if
(
typeof
doc
.
_revs
===
"
object
"
)
{
if
(
typeof
doc
.
_revs
.
start
!==
"
number
"
||
typeof
doc
.
_revs
.
ids
!==
"
object
"
||
typeof
doc
.
_revs
.
ids
.
length
!==
"
number
"
)
{
return
send_error
(
"
The document revision history is not well formated
"
);
}
}
if
(
typeof
doc
.
_revs_info
===
"
object
"
)
{
if
(
typeof
doc
.
_revs_info
.
length
!==
"
number
"
)
{
return
send_error
(
"
The document revision information
"
+
"
is not well formated
"
);
}
}
return
revision
;
};
/**
* Convert the revision history object to an array of revisions.
* @method revisionHistoryToArray
* @param {object} revs The revision history
* @return {array} The revision array
* Creates a new document tree
* @method newDocTree
* @return {object} The new document tree
*/
priv
.
revisionHistoryToArray
=
function
(
revs
)
{
var
i
,
start
=
revs
.
start
,
newlist
=
[];
for
(
i
=
0
;
i
<
revs
.
ids
.
length
;
i
+=
1
,
start
-=
1
)
{
newlist
.
push
(
start
+
"
-
"
+
revs
.
ids
[
i
]);
}
return
newlist
;
priv
.
newDocTree
=
function
()
{
return
{
"
children
"
:
[]};
};
/**
* Generates the next revision of [previous_revision]. [string] helps us
* to generate a hash code.
* @methode generateNextRev
* @param {string} previous_revision The previous revision
* @param {object} doc The document metadata
* @param {object} revisions The revision history
* @param {boolean} deleted_flag The deleted flag
* @return {array} 0:The next revision number and 1:the hash code
* Convert revs_info to a simple revisions history
* @method revsInfoToHistory
* @param {array} revs_info The revs info
* @return {object} The revisions history
*/
priv
.
generateNextRevision
=
function
(
previous_revision
,
doc
,
revisions
,
deleted_flag
)
{
var
string
=
JSON
.
stringify
(
doc
)
+
JSON
.
stringify
(
revisions
)
+
JSON
.
stringify
(
deleted_flag
?
true
:
false
);
if
(
typeof
previous_revision
===
"
number
"
)
{
return
[
previous_revision
+
1
,
priv
.
hashCode
(
string
)];
priv
.
revsInfoToHistory
=
function
(
revs_info
)
{
var
i
,
revisions
=
{
"
start
"
:
0
,
"
ids
"
:
[]
};
revs_info
=
revs_info
||
[];
if
(
revs_info
.
length
>
0
)
{
revisions
.
start
=
parseInt
(
revs_info
[
0
].
rev
.
split
(
'
-
'
)[
0
],
10
);
}
for
(
i
=
0
;
i
<
revs_info
.
length
;
i
+=
1
)
{
revisions
.
ids
.
push
(
revs_info
[
i
].
rev
.
split
(
'
-
'
)[
1
]);
}
previous_revision
=
priv
.
revisionToArray
(
previous_revision
);
return
[
previous_revision
[
0
]
+
1
,
priv
.
hashCode
(
string
)];
return
revisions
;
};
/**
* C
hecks a revision format
* @method
checkRevisionForma
t
* @param {
string} revision The revision string
* @return {
boolean} True if ok, else false
* C
onvert the revision history object to an array of revisions.
* @method
revisionHistoryToLis
t
* @param {
object} revs The revision history
* @return {
array} The revision array
*/
priv
.
checkRevisionFormat
=
function
(
revision
)
{
return
(
/^
[
0-9
]
+-
[
0-9a-zA-Z
]
+$/
.
test
(
revision
));
priv
.
revisionHistoryToList
=
function
(
revs
)
{
var
i
,
start
=
revs
.
start
,
new_list
=
[];
for
(
i
=
0
;
i
<
revs
.
ids
.
length
;
i
+=
1
,
start
-=
1
)
{
new_list
.
push
(
start
+
"
-
"
+
revs
.
ids
[
i
]);
}
return
new_list
;
};
/**
* Creates an empty document tree
* @method createDocumentTree
* @param {array} children An array of children (optional)
* @return {object} The new document tree
* Convert revision list to revs info.
* @method revisionListToRevsInfo
* @param {array} revision_list The revision list
* @param {object} doc_tree The document tree
* @return {array} The document revs info
*/
priv
.
createDocumentTree
=
function
(
children
)
{
return
{
"
children
"
:
children
||
[]
priv
.
revisionListToRevsInfo
=
function
(
revision_list
,
doc_tree
)
{
var
revisionListToRevsInfoRec
,
revs_info
=
[],
j
;
for
(
j
=
0
;
j
<
revision_list
.
length
;
j
+=
1
)
{
revs_info
.
push
({
"
rev
"
:
revision_list
[
j
],
"
status
"
:
"
missing
"
});
}
revisionListToRevsInfoRec
=
function
(
index
,
doc_tree
)
{
var
child
,
i
;
if
(
index
<
0
)
{
return
;
}
for
(
i
=
0
;
i
<
doc_tree
.
children
.
length
;
i
+=
1
)
{
child
=
doc_tree
.
children
[
i
];
if
(
child
.
rev
===
revision_list
[
index
])
{
revs_info
[
index
].
status
=
child
.
status
;
revisionListToRevsInfoRec
(
index
-
1
,
child
);
}
}
};
revisionListToRevsInfoRec
(
revision_list
.
length
-
1
,
doc_tree
);
return
revs_info
;
};
/**
* Creates a new document tree node
* @method createDocumentTreeNode
* @param {string} revision The node revision
* @param {string} status The node status
* @param {array} children An array of children (optional)
* @return {object} The new document tree node
* Update a document metadata revision properties
* @method fillDocumentRevisionProperties
* @param {object} doc The document object
* @param {object} doc_tree The document tree
*/
priv
.
createDocumentTreeNode
=
function
(
revision
,
status
,
children
)
{
return
{
"
rev
"
:
revision
,
"
status
"
:
status
,
"
children
"
:
children
||
[]
};
priv
.
fillDocumentRevisionProperties
=
function
(
doc
,
doc_tree
)
{
if
(
doc
.
_revs_info
)
{
doc
.
_revs
=
priv
.
revsInfoToHistory
(
doc
.
_revs_info
);
}
else
if
(
doc
.
_revs
)
{
doc
.
_revs_info
=
priv
.
revisionListToRevsInfo
(
priv
.
revisionHistoryToList
(
doc
.
_revs
),
doc_tree
);
}
else
if
(
doc
.
_rev
)
{
doc
.
_revs_info
=
priv
.
getRevisionInfo
(
doc
.
_rev
,
doc_tree
);
doc
.
_revs
=
priv
.
revsInfoToHistory
(
doc
.
_revs_info
);
}
else
{
doc
.
_revs_info
=
[];
doc
.
_revs
=
{
"
start
"
:
0
,
"
ids
"
:
[]};
}
if
(
doc
.
_revs
.
start
>
0
)
{
doc
.
_rev
=
doc
.
_revs
.
start
+
"
-
"
+
doc
.
_revs
.
ids
[
0
];
}
else
{
delete
doc
.
_rev
;
}
};
/**
* Ge
ts the specific revision from a document tree
.
* @method
getRevisionFromDocumentTree
* @param {object} doc
ument_tree The document tree
* @param {
string} revision The specific revision
* @return {array}
The good revs info array
* Ge
nerates the next revision of a document
.
* @method
e generateNextRevision
* @param {object} doc
The document metadata
* @param {
boolean} deleted_flag The deleted flag
* @return {array}
0:The next revision number and 1:the hash code
*/
priv
.
getRevisionFromDocumentTree
=
function
(
document_tree
,
revision
)
{
var
result
,
search
,
revs_info
=
[];
result
=
[];
// search method fills "result" with the good revs info
search
=
function
(
document_tree
)
{
var
i
;
if
(
document_tree
.
rev
!==
undefined
)
{
// node is not root
priv
.
generateNextRevision
=
function
(
doc
,
deleted_flag
)
{
var
string
,
revision_history
,
revs_info
,
pseudo_revision
;
doc
=
priv
.
clone
(
doc
)
||
{};
revision_history
=
doc
.
_revs
;
revs_info
=
doc
.
_revs_info
;
delete
doc
.
_rev
;
delete
doc
.
_revs
;
delete
doc
.
_revs_info
;
string
=
JSON
.
stringify
(
doc
)
+
JSON
.
stringify
(
revision_history
)
+
JSON
.
stringify
(
deleted_flag
?
true
:
false
);
console
.
log
(
string
);
revision_history
.
start
+=
1
;
revision_history
.
ids
.
unshift
(
priv
.
hashCode
(
string
));
doc
.
_revs
=
revision_history
;
doc
.
_rev
=
revision_history
.
start
+
"
-
"
+
revision_history
.
ids
[
0
];
revs_info
.
unshift
({
"
rev
"
:
document_tree
.
rev
,
"
status
"
:
document_tree
.
status
"
rev
"
:
doc
.
_
rev
,
"
status
"
:
deleted_flag
?
"
deleted
"
:
"
available
"
});
if
(
document_tree
.
rev
===
revision
)
{
result
=
revs_info
;
return
;
}
}
// This node has children
for
(
i
=
0
;
i
<
document_tree
.
children
.
length
;
i
+=
1
)
{
// searching deeper to find the good rev
search
(
document_tree
.
children
[
i
]);
if
(
result
.
length
>
0
)
{
// The result is already found
return
;
}
revs_info
.
shift
();
}
};
search
(
document_tree
);
return
result
;
doc
.
_revs_info
=
revs_info
;
return
doc
;
};
/**
* Gets the
winner revision from a document tree.
*
The winner is the deeper revision on the left.
* @
method getWinnerRevisionFromDocumentTree
* @param {object} doc
ument
_tree The document tree
* @return {array} The
winner revs info array
* Gets the
revs info from the document tree
*
@method getRevisionInfo
* @
param {string} revision The revision to search for
* @param {object} doc_tree The document tree
* @return {array} The
revs info
*/
priv
.
getWinnerRevisionFromDocumentTree
=
function
(
document_tree
)
{
var
result
,
search
,
revs_info
=
[];
result
=
[];
// search method fills "result" with the winner revs info
search
=
function
(
document_tree
,
deep
)
{
var
i
;
if
(
document_tree
.
rev
!==
undefined
)
{
// node is not root
revs_info
.
unshift
({
"
rev
"
:
document_tree
.
rev
,
"
status
"
:
document_tree
.
status
});
}
if
(
document_tree
.
children
.
length
===
0
&&
document_tree
.
status
!==
"
deleted
"
)
{
// This node is a leaf
if
(
result
.
length
<
deep
)
{
// The leaf is deeper than result
result
=
[];
for
(
i
=
0
;
i
<
revs_info
.
length
;
i
+=
1
)
{
result
.
push
(
revs_info
[
i
]);
}
}
return
;
priv
.
getRevisionInfo
=
function
(
revision
,
doc_tree
)
{
var
getRevisionInfoRec
;
getRevisionInfoRec
=
function
(
doc_tree
)
{
var
i
,
child
,
revs_info
;
for
(
i
=
0
;
i
<
doc_tree
.
children
.
length
;
i
+=
1
)
{
child
=
doc_tree
.
children
[
i
];
if
(
child
.
rev
===
revision
)
{
return
[{
"
rev
"
:
child
.
rev
,
"
status
"
:
child
.
status
}];
}
revs_info
=
getRevisionInfoRec
(
child
);
if
(
revs_info
.
length
>
0
||
revision
===
undefined
)
{
revs_info
.
push
({
"
rev
"
:
child
.
rev
,
"
status
"
:
child
.
status
});
return
revs_info
;
}
// This node has children
for
(
i
=
0
;
i
<
document_tree
.
children
.
length
;
i
+=
1
)
{
// searching deeper to find the deeper leaf
search
(
document_tree
.
children
[
i
],
deep
+
1
);
revs_info
.
shift
();
}
return
[];
};
search
(
document_tree
,
0
);
return
result
;
return
getRevisionInfoRec
(
doc_tree
);
};
/**
* Add a document revision branch to the document tree
* @method updateDocumentTree
* @param {object} doctree The document tree object
* @param {object|array} revs The revision history object or a revision array
* @param {boolean} deleted The deleted flag
* @param {array} The document revs_info
*/
priv
.
updateDocumentTree
=
function
(
doctree
,
revs
,
deleted
)
{
var
revs_info
,
doctree_iterator
,
flag
,
i
,
rev
;
revs_info
=
[];
if
(
revs
.
ids
)
{
// revs is a revision history object
revs
=
priv
.
revisionHistoryToArray
(
revs
);
}
else
{
// revs is an array of revisions
revs
=
priv
.
clone
(
revs
);
priv
.
updateDocumentTree
=
function
(
doc
,
doc_tree
)
{
var
revs_info
,
updateDocumentTreeRec
,
next_rev
;
doc
=
priv
.
clone
(
doc
);
revs_info
=
doc
.
_revs_info
;
updateDocumentTreeRec
=
function
(
doc_tree
,
revs_info
)
{
var
i
,
child
,
info
;
if
(
revs_info
.
length
===
0
)
{
return
;
}
doctree_iterator
=
doctree
;
while
(
revs
.
length
>
0
)
{
rev
=
revs
.
pop
(
0
);
revs_info
.
unshift
({
"
rev
"
:
rev
,
"
status
"
:
"
missing
"
});
for
(
i
=
0
;
i
<
doctree_iterator
.
children
.
length
;
i
+=
1
)
{
if
(
doctree_iterator
.
children
[
i
].
rev
===
rev
)
{
doctree_iterator
=
doctree_iterator
.
children
[
i
];
revs_info
[
0
].
status
=
doctree_iterator
.
status
;
rev
=
undefined
;
break
;
info
=
revs_info
.
pop
();
for
(
i
=
0
;
i
<
doc_tree
.
children
.
length
;
i
+=
1
)
{
child
=
doc_tree
.
children
[
i
];
if
(
child
.
rev
===
info
.
rev
)
{
return
updateDocumentTreeRec
(
child
,
revs_info
);
}
}
if
(
rev
)
{
doctree_iterator
.
children
.
unshift
({
"
rev
"
:
rev
,
"
status
"
:
"
missing
"
,
doc_tree
.
children
.
unshift
({
"
rev
"
:
info
.
rev
,
"
status
"
:
info
.
status
,
"
children
"
:
[]
});
doctree_iterator
=
doctree_iterator
.
children
[
0
];
}
updateDocumentTreeRec
(
doc_tree
.
children
[
0
],
revs_info
);
};
updateDocumentTreeRec
(
doc_tree
,
priv
.
clone
(
revs_info
));
};
priv
.
send
=
function
(
method
,
doc
,
option
,
callback
)
{
that
.
addJob
(
method
,
priv
.
sub_storage
,
doc
,
option
,
function
(
success
)
{
callback
(
undefined
,
success
);
},
function
(
err
)
{
callback
(
err
,
undefined
);
}
flag
=
deleted
===
true
?
"
deleted
"
:
"
available
"
;
revs_info
[
0
].
status
=
flag
;
doctree_iterator
.
status
=
flag
;
return
revs_info
;
);
};
/**
* Add a document revision to the document tree
* @method postToDocumentTree
* @param {object} doctree The document tree object
* @param {object} doc The document object
* @param {boolean} set_node_to_deleted Set the revision to deleted
* @return {array} The added document revs_info
*/
priv
.
postToDocumentTree
=
function
(
doctree
,
doc
,
set_node_to_deleted
)
{
var
i
,
revs_info
,
next_rev
,
next_rev_str
,
selectNode
,
selected_node
,
flag
;
flag
=
set_node_to_deleted
===
true
?
"
deleted
"
:
"
available
"
;
revs_info
=
[];
selected_node
=
doctree
;
selectNode
=
function
(
node
)
{
priv
.
getWinnerRevsInfo
=
function
(
doc_tree
)
{
var
revs_info
=
[],
getWinnerRevsInfoRec
;
getWinnerRevsInfoRec
=
function
(
doc_tree
,
tmp_revs_info
)
{
var
i
;
if
(
node
.
rev
!==
undefined
)
{
// node is not root
revs_info
.
unshift
({
"
rev
"
:
node
.
rev
,
"
status
"
:
node
.
status
});
if
(
doc_tree
.
rev
)
{
tmp_revs_info
.
unshift
({
"
rev
"
:
doc_tree
.
rev
,
"
status
"
:
doc_tree
.
status
});
}
if
(
node
.
rev
===
doc
.
_rev
)
{
selected_node
=
node
;
return
"
node_selected
"
;
if
(
doc_tree
.
children
.
length
===
0
)
{
if
(
revs_info
.
length
<
tmp_revs_info
.
length
||
(
revs_info
.
length
>
0
&&
revs_info
[
0
].
status
===
"
deleted
"
))
{
revs_info
=
priv
.
clone
(
tmp_revs_info
);
}
for
(
i
=
0
;
i
<
node
.
children
.
length
;
i
+=
1
)
{
if
(
selectNode
(
node
.
children
[
i
])
===
"
node_selected
"
)
{
return
"
node_selected
"
;
}
revs_info
.
shift
();
for
(
i
=
0
;
i
<
doc_tree
.
children
.
length
;
i
+=
1
)
{
getWinnerRevsInfoRec
(
doc_tree
.
children
[
i
],
tmp_revs_info
);
}
tmp_revs_info
.
shift
();
};
if
(
typeof
doc
.
_rev
===
"
string
"
)
{
// document has a previous revision
if
(
selectNode
(
selected_node
)
!==
"
node_selected
"
)
{
// no node was selected, so add a node with a specific rev
revs_info
.
unshift
({
"
rev
"
:
doc
.
_rev
,
"
status
"
:
"
missing
"
});
selected_node
.
children
.
unshift
(
priv
.
createDocumentTreeNode
(
doc
.
_rev
,
"
missing
"
));
selected_node
=
selected_node
.
children
[
0
];
}
getWinnerRevsInfoRec
(
doc_tree
,
[]);
return
revs_info
;
};
priv
.
getConflicts
=
function
(
revision
,
doc_tree
)
{
var
conflicts
=
[],
getConflictsRec
;
getConflictsRec
=
function
(
doc_tree
)
{
var
i
;
if
(
doc_tree
.
rev
===
revision
)
{
return
;
}
next_rev
=
priv
.
generateNextRevision
(
doc
.
_rev
||
0
,
doc
,
priv
.
revsInfoToHistory
(
revs_info
),
set_node_to_deleted
);
next_rev_str
=
next_rev
.
join
(
"
-
"
);
// don't add if the next rev already exists
for
(
i
=
0
;
i
<
selected_node
.
children
.
length
;
i
+=
1
)
{
if
(
selected_node
.
children
[
i
].
rev
===
next_rev_str
)
{
revs_info
.
unshift
({
"
rev
"
:
next_rev_str
,
"
status
"
:
flag
});
if
(
selected_node
.
children
[
i
].
status
!==
flag
)
{
selected_node
.
children
[
i
].
status
=
flag
;
if
(
doc_tree
.
children
.
length
===
0
)
{
if
(
doc_tree
.
status
!==
"
deleted
"
)
{
conflicts
.
push
(
doc_tree
.
rev
);
}
return
revs_info
;
}
for
(
i
=
0
;
i
<
doc_tree
.
children
.
length
;
i
+=
1
)
{
getConflictsRec
(
doc_tree
.
children
[
i
]);
}
revs_info
.
unshift
({
"
rev
"
:
next_rev
.
join
(
'
-
'
),
"
status
"
:
flag
})
;
};
getConflictsRec
(
doc_tree
);
return
conflicts
.
length
===
0
?
undefined
:
conflicts
;
}
;
selected_node
.
children
.
unshift
(
priv
.
createDocumentTreeNode
(
next_rev
.
join
(
'
-
'
),
flag
));
priv
.
get
=
function
(
doc
,
option
,
callback
)
{
priv
.
send
(
"
get
"
,
doc
,
option
,
callback
);
};
priv
.
put
=
function
(
doc
,
option
,
callback
)
{
console
.
log
(
doc
);
priv
.
send
(
"
put
"
,
doc
,
option
,
callback
);
};
priv
.
remove
=
function
(
doc
,
option
,
callback
)
{
priv
.
send
(
"
remove
"
,
doc
,
option
,
callback
);
};
priv
.
putAttachment
=
function
(
attachment
,
option
,
callback
)
{
priv
.
send
(
"
putAttachment
"
,
attachment
,
option
,
callback
);
};
return
revs_info
;
priv
.
getDocument
=
function
(
doc
,
option
,
callback
)
{
doc
=
priv
.
clone
(
doc
);
doc
.
_id
=
doc
.
_id
+
"
.
"
+
doc
.
_rev
;
delete
doc
.
_attachment
;
delete
doc
.
_rev
;
delete
doc
.
_revs
;
delete
doc
.
_revs_info
;
priv
.
get
(
doc
,
option
,
callback
);
};
priv
.
getAttachment
=
priv
.
get
;
priv
.
putDocument
=
function
(
doc
,
option
,
callback
)
{
doc
=
priv
.
clone
(
doc
);
doc
.
_id
=
doc
.
_id
+
"
.
"
+
doc
.
_rev
;
delete
doc
.
_attachment
;
delete
doc
.
_data
;
delete
doc
.
_mimetype
;
delete
doc
.
_rev
;
delete
doc
.
_revs
;
delete
doc
.
_revs_info
;
priv
.
put
(
doc
,
option
,
callback
);
};
/**
* Gets an array of leaves revisions from document tree
* @method getLeavesFromDocumentTree
* @param {object} document_tree The document tree
* @param {string} except The revision to except
* @return {array} The array of leaves revisions
*/
priv
.
getLeavesFromDocumentTree
=
function
(
document_tree
,
except
)
{
var
result
,
search
;
result
=
[];
// search method fills [result] with the winner revision
search
=
function
(
document_tree
)
{
var
i
;
if
(
except
!==
undefined
&&
except
===
document_tree
.
rev
)
{
priv
.
getRevisionTree
=
function
(
doc
,
option
,
callback
)
{
doc
=
priv
.
clone
(
doc
);
doc
.
_id
=
doc
.
_id
+
priv
.
doc_tree_suffix
;
priv
.
get
(
doc
,
option
,
callback
);
};
priv
.
getAttachmentList
=
function
(
doc
,
option
,
callback
)
{
console
.
log
(
"
p getAttachmentList
"
);
var
attachment_id
,
dealResults
,
state
=
"
ok
"
,
result_list
=
[],
count
=
0
;
dealResults
=
function
(
attachment_id
,
attachment_meta
)
{
return
function
(
err
,
attachment
)
{
if
(
state
!==
"
ok
"
)
{
return
;
}
if
(
document_tree
.
children
.
length
===
0
&&
document_tree
.
status
!==
"
deleted
"
)
{
// This node is a leaf
result
.
push
(
document_tree
.
rev
);
return
;
count
-=
1
;
if
(
err
)
{
if
(
err
.
status
===
404
)
{
result_list
.
push
(
undefined
);
}
else
{
state
=
"
error
"
;
return
callback
(
err
,
undefined
);
}
// This node has children
for
(
i
=
0
;
i
<
document_tree
.
children
.
length
;
i
+=
1
)
{
// searching deeper to find the deeper leaf
search
(
document_tree
.
children
[
i
]);
}
result_list
.
push
({
"
_attachment
"
:
attachment_id
,
"
_data
"
:
attachment
,
"
_mimetype
"
:
attachment_meta
.
content_type
});
if
(
count
===
0
)
{
state
=
"
finished
"
;
callback
(
undefined
,
result_list
);
}
};
search
(
document_tree
);
return
result
;
};
/**
* Check if revision is a leaf
* @method isRevisionALeaf
* @param {string} revision revision to check
* @param {array} leaves all leaves on tree
* @return {boolean} true/false
*/
priv
.
isRevisionALeaf
=
function
(
document_tree
,
revision
)
{
var
result
,
search
;
result
=
undefined
;
// search method fills "result" with the good revs info
search
=
function
(
document_tree
)
{
var
i
;
if
(
document_tree
.
rev
!==
undefined
)
{
// node is not root
if
(
document_tree
.
rev
===
revision
)
{
if
(
document_tree
.
children
.
length
===
0
)
{
// This node is a leaf
result
=
true
;
return
;
for
(
attachment_id
in
doc
.
_attachments
)
{
if
(
doc
.
_attachments
.
hasOwnProperty
(
attachment_id
))
{
count
+=
1
;
priv
.
get
(
{
"
_id
"
:
doc
.
_id
+
"
/
"
+
attachment_id
},
option
,
dealResults
(
attachment_id
,
doc
.
_attachments
[
attachment_id
])
);
}
result
=
false
;
return
;
}
if
(
count
===
0
)
{
callback
(
undefined
,
[]);
}
// This node has children
for
(
i
=
0
;
i
<
document_tree
.
children
.
length
;
i
+=
1
)
{
// searching deeper to find the good rev
search
(
document_tree
.
children
[
i
]);
if
(
result
!==
undefined
)
{
// The result is already found
};
priv
.
putAttachmentList
=
function
(
doc
,
option
,
attachment_list
,
callback
)
{
console
.
log
(
"
p putAttachmentList
"
);
var
i
,
dealResults
,
state
=
"
ok
"
,
count
=
0
,
attachment
;
attachment_list
=
attachment_list
||
[];
dealResults
=
function
(
index
)
{
return
function
(
err
,
response
)
{
if
(
state
!==
"
ok
"
)
{
return
;
}
count
-=
1
;
if
(
err
)
{
state
=
"
error
"
;
return
callback
(
err
,
undefined
);
}
if
(
count
===
0
)
{
state
=
"
finished
"
;
callback
(
undefined
,
{
"
id
"
:
doc
.
_id
,
"
ok
"
:
true
});
}
};
search
(
document_tree
);
return
result
||
false
;
};
/**
* Convert revs_info to a simple revisions history
* @method revsInfoToHistory
* @param {array} revs_info The revs info
* @return {object} The revisions history
*/
priv
.
revsInfoToHistory
=
function
(
revs_info
)
{
var
revisions
=
{
"
start
"
:
0
,
"
ids
"
:
[]
},
i
;
if
(
revs_info
.
length
>
0
)
{
revisions
.
start
=
parseInt
(
revs_info
[
0
].
rev
.
split
(
'
-
'
)[
0
],
10
);
for
(
i
=
0
;
i
<
attachment_list
.
length
;
i
+=
1
)
{
attachment
=
attachment_list
[
i
];
if
(
attachment
!==
undefined
)
{
count
+=
1
;
attachment
.
_id
=
doc
.
_id
+
"
.
"
+
doc
.
_rev
+
"
/
"
+
attachment
.
_attachment
;
delete
attachment
.
_attachment
;
priv
.
putAttachment
(
attachment
,
option
,
dealResults
(
i
));
}
for
(
i
=
0
;
i
<
revs_info
.
length
;
i
+=
1
)
{
revisions
.
ids
.
push
(
revs_info
[
i
].
rev
.
split
(
'
-
'
)[
1
]);
}
return
revisions
;
if
(
count
===
0
)
{
return
callback
(
undefined
,
{
"
id
"
:
doc
.
_id
,
"
ok
"
:
true
});
}
};
/**
* Returns the revision of the revision position from a revs_info array.
* @method getRevisionFromPosition
* @param {array} revs_info The revs_info array
* @param {number} rev_pos The revision position number
* @return {string} The revision of the good position (empty string if fail)
*/
priv
.
getRevisionFromPosition
=
function
(
revs_info
,
rev_pos
)
{
var
i
;
for
(
i
=
revs_info
.
length
-
1
;
i
>=
0
;
i
-=
1
)
{
if
(
priv
.
revisionToArray
(
revs_info
[
i
].
rev
)[
0
]
===
rev_pos
)
{
return
revs_info
[
i
].
rev
;
}
}
return
''
;
priv
.
putDocumentTree
=
function
(
doc
,
option
,
doc_tree
,
callback
)
{
doc_tree
=
priv
.
clone
(
doc_tree
);
doc_tree
.
_id
=
doc
.
_id
+
priv
.
doc_tree_suffix
;
priv
.
put
(
doc_tree
,
option
,
callback
);
};
/**
* Post the document metadata and create or update a document tree.
* Options:
* - {boolean} keep_revision_history To keep the previous revisions
* (false by default) (NYI).
* @method post
* @param {object} command The JIO command
*/
that
.
post
=
function
(
command
)
{
var
f
=
{},
doctree
,
revs_info
,
doc
,
docid
,
prev_doc
;
doc
=
command
.
cloneDoc
();
docid
=
command
.
getDocId
();
priv
.
notFoundError
=
function
(
message
,
reason
)
{
return
{
"
status
"
:
404
,
"
statusText
"
:
"
Not Found
"
,
"
error
"
:
"
not_found
"
,
"
message
"
:
message
,
"
reason
"
:
reason
};
};
if
(
typeof
doc
.
_rev
===
"
string
"
&&
!
priv
.
checkRevisionFormat
(
doc
.
_rev
))
{
that
.
error
({
"
status
"
:
31
,
"
statusText
"
:
"
Wrong Revision Format
"
,
"
error
"
:
"
wrong_revision_format
"
,
"
message
"
:
"
The document previous revision does not match
"
+
"
^[0-9]+-[0-9a-zA-Z]+$
"
,
"
reason
"
:
"
Previous revision is wrong
"
});
return
;
priv
.
conflictError
=
function
(
message
,
reason
)
{
return
{
"
status
"
:
409
,
"
statusText
"
:
"
Conflict
"
,
"
error
"
:
"
conflict
"
,
"
message
"
:
message
,
"
reason
"
:
reason
};
};
priv
.
revisionGenericRequest
=
function
(
doc
,
option
,
specific_parameter
,
onEnd
)
{
var
prev_doc
,
doc_tree
,
attachment_list
,
callback
=
{};
if
(
specific_parameter
.
doc_id
)
{
doc
.
_id
=
specific_parameter
.
doc_id
;
}
if
(
specific_parameter
.
attachment_id
)
{
doc
.
_attachment
=
specific_parameter
.
attachment_id
;
}
callback
.
begin
=
function
()
{
console
.
log
(
"
c begin
"
);
var
check_error
;
doc
.
_id
=
doc
.
_id
||
priv
.
generateUuid
();
if
(
specific_parameter
.
revision_needed
&&
!
doc
.
_rev
)
{
return
onEnd
(
priv
.
conflictError
(
"
Document update conflict
"
,
"
No document revision was provided
"
),
undefined
);
}
// check revision format
check_error
=
priv
.
checkDocumentRevisionFormat
(
doc
);
if
(
check_error
!==
undefined
)
{
return
onEnd
(
check_error
,
undefined
);
}
priv
.
getRevisionTree
(
doc
,
option
,
callback
.
getRevisionTree
);
};
callback
.
getRevisionTree
=
function
(
err
,
response
)
{
console
.
log
(
"
c getRevisionTree
"
);
var
winner_info
,
previous_revision
=
doc
.
_rev
,
generate_new_revision
=
doc
.
_revs
||
doc
.
_revs_info
?
false
:
true
;
if
(
err
)
{
if
(
err
.
status
!==
404
)
{
err
.
message
=
"
Cannot get document revision tree
"
;
return
onEnd
(
err
,
undefined
);
}
}
doc_tree
=
response
||
priv
.
newDocTree
();
if
(
specific_parameter
.
get
||
specific_parameter
.
getAttachment
)
{
if
(
!
doc
.
_rev
)
{
console
.
log
(
JSON
.
stringify
(
doc_tree
));
winner_info
=
priv
.
getWinnerRevsInfo
(
doc_tree
);
console
.
log
(
winner_info
);
if
(
winner_info
.
length
===
0
)
{
return
onEnd
(
priv
.
notFoundError
(
"
Document not found
"
,
"
missing
"
),
undefined
);
}
if
(
typeof
docid
!==
"
string
"
)
{
doc
.
_id
=
priv
.
generateUuid
();
docid
=
doc
.
_id
;
if
(
winner_info
[
0
].
status
===
"
deleted
"
)
{
return
onEnd
(
priv
.
notFoundError
(
"
Document not found
"
,
"
deleted
"
),
undefined
);
}
f
.
getDocumentTree
=
function
()
{
var
option
=
command
.
cloneOption
();
if
(
option
.
max_retry
===
0
)
{
option
.
max_retry
=
3
;
doc
.
_rev
=
winner_info
[
0
].
rev
;
}
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
docid
+
priv
.
doctree_suffix
,
option
,
function
(
response
)
{
doctree
=
response
;
f
.
updateRevsInfo
();
f
.
getDocument
();
},
function
(
err
)
{
switch
(
err
.
status
)
{
case
404
:
doctree
=
priv
.
createDocumentTree
();
f
.
updateRevsInfo
();
f
.
getDocument
();
break
;
default
:
err
.
message
=
"
Cannot get document revision tree
"
;
f
.
error
(
err
);
break
;
priv
.
fillDocumentRevisionProperties
(
doc
,
doc_tree
);
return
priv
.
getDocument
(
doc
,
option
,
callback
.
getDocument
);
}
priv
.
fillDocumentRevisionProperties
(
doc
,
doc_tree
);
if
(
generate_new_revision
)
{
if
(
previous_revision
&&
doc
.
_revs_info
.
length
===
0
)
{
// the document history has changed, it means that the document
// revision was wrong. Add a pseudo history to the document
doc
.
_rev
=
previous_revision
;
doc
.
_revs
=
{
"
start
"
:
parseInt
(
previous_revision
.
split
(
"
-
"
)[
0
],
10
),
"
ids
"
:
[
previous_revision
.
split
(
"
-
"
)[
1
]]
};
doc
.
_revs_info
=
[{
"
rev
"
:
previous_revision
,
"
status
"
:
"
missing
"
}];
}
doc
=
priv
.
generateNextRevision
(
doc
,
specific_parameter
.
remove
);
}
if
(
doc
.
_revs_info
.
length
>
1
)
{
prev_doc
=
{
"
_id
"
:
doc
.
_id
,
"
_rev
"
:
doc
.
_revs_info
[
1
].
rev
};
f
.
getDocument
=
function
()
{
if
(
revs_info
[
1
]
===
undefined
)
{
f
.
postDocument
([]);
}
else
{
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
command
.
getDocId
()
+
"
.
"
+
revs_info
[
1
].
rev
,
command
.
getOption
(),
function
(
response
)
{
var
attachment_list
=
[],
i
;
prev_doc
=
response
;
for
(
i
in
response
.
_attachments
)
{
if
(
response
.
_attachments
.
hasOwnProperty
(
i
))
{
attachment_list
.
push
({
"
id
"
:
i
,
"
attachment
"
:
{
"
_id
"
:
command
.
getDocId
()
+
"
.
"
+
revs_info
[
0
].
rev
+
"
/
"
+
i
,
"
_mimetype
"
:
response
.
_attachments
[
i
].
content_type
,
"
_data
"
:
undefined
}});
}
}
f
.
postDocument
(
attachment_list
);
},
function
(
err
)
{
if
(
err
.
status
===
404
)
{
f
.
postDocument
([]);
return
;
}
err
.
message
=
"
Cannot retrieve document
"
;
f
.
error
(
err
);
// force revs_info status
doc
.
_revs_info
[
0
].
status
=
(
specific_parameter
.
remove
?
"
deleted
"
:
"
available
"
);
priv
.
updateDocumentTree
(
doc
,
doc_tree
);
if
(
prev_doc
)
{
return
priv
.
getDocument
(
prev_doc
,
option
,
callback
.
getDocument
);
}
);
if
(
specific_parameter
.
remove
||
specific_parameter
.
removeAttachment
)
{
console
.
log
(
"
this one
"
);
return
onEnd
(
priv
.
notFoundError
(
"
Unable to remove an inexistent document
"
,
"
missing
"
),
undefined
);
}
priv
.
putDocument
(
doc
,
option
,
callback
.
putDocument
);
};
f
.
updateRevsInfo
=
function
()
{
if
(
doc
.
_revs
)
{
revs_info
=
priv
.
updateDocumentTree
(
doctree
,
doc
.
_revs
);
}
else
{
revs_info
=
priv
.
postToDocumentTree
(
doctree
,
doc
);
callback
.
getDocument
=
function
(
err
,
res_doc
)
{
console
.
log
(
"
c getDocument
"
);
var
k
,
conflicts
;
if
(
err
)
{
if
(
err
.
status
===
404
)
{
if
(
specific_parameter
.
remove
||
specific_parameter
.
removeAttachment
)
{
return
onEnd
(
priv
.
conflictError
(
"
Document update conflict
"
,
"
Document is missing
"
),
undefined
);
}
if
(
specific_parameter
.
get
)
{
return
onEnd
(
priv
.
notFoundError
(
"
Unable to find the document
"
,
"
missing
"
),
undefined
);
}
};
f
.
postDocument
=
function
(
attachment_list
)
{
doc
.
_id
=
docid
+
"
.
"
+
revs_info
[
0
].
rev
;
delete
doc
.
_rev
;
delete
doc
.
_revs
;
that
.
addJob
(
"
post
"
,
priv
.
substorage
,
doc
,
command
.
cloneOption
(),
function
()
{
var
i
;
if
(
attachment_list
.
length
===
0
)
{
f
.
sendDocumentTree
();
res_doc
=
{};
}
else
{
f
.
send_document_tree_count
=
attachment_list
.
length
;
for
(
i
=
0
;
i
<
attachment_list
.
length
;
i
+=
1
)
{
f
.
copyAttachment
(
attachment_list
[
i
].
id
,
attachment_list
[
i
].
attachment
);
err
.
message
=
"
Cannot get document
"
;
return
onEnd
(
err
,
undefined
);
}
}
},
function
(
err
)
{
switch
(
err
.
status
)
{
case
409
:
// file already exists
f
.
sendDocumentTree
();
break
;
default
:
err
.
message
=
"
Cannot upload document
"
;
f
.
error
(
err
);
break
;
if
(
specific_parameter
.
get
)
{
res_doc
.
_id
=
doc
.
_id
;
res_doc
.
_rev
=
doc
.
_rev
;
if
(
option
.
conflicts
===
true
)
{
conflicts
=
priv
.
getConflicts
(
doc
.
_rev
,
doc_tree
);
if
(
conflicts
)
{
res_doc
.
_conflicts
=
conflicts
;
}
}
);
};
f
.
copyAttachment
=
function
(
attachmentid
,
attachment
)
{
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
prev_doc
.
_id
+
"
/
"
+
attachmentid
,
command
.
cloneOption
(),
function
(
response
)
{
attachment
.
_data
=
response
;
that
.
addJob
(
"
putAttachment
"
,
priv
.
substorage
,
attachment
,
command
.
cloneOption
(),
function
(
response
)
{
f
.
sendDocumentTree
();
},
function
(
err
)
{
err
.
message
=
"
Cannot copy previous attachment
"
;
f
.
error
(
err
);
if
(
option
.
revs
===
true
)
{
res_doc
.
_revisions
=
doc
.
_revs
;
}
);
},
function
(
err
)
{
err
.
message
=
"
Cannot get previous attachment
"
;
f
.
error
(
err
);
if
(
option
.
revs_info
===
true
)
{
res_doc
.
_revs_info
=
doc
.
_revs_info
;
}
);
};
f
.
send_document_tree_count
=
0
;
f
.
sendDocumentTree
=
function
()
{
f
.
send_document_tree_count
-=
1
;
if
(
f
.
send_document_tree_count
>
0
)
{
return
;
return
onEnd
(
undefined
,
res_doc
);
}
doctree
.
_id
=
docid
+
priv
.
doctree_suffix
;
that
.
addJob
(
"
put
"
,
priv
.
substorage
,
doctree
,
command
.
cloneOption
(),
function
()
{
that
.
success
({
"
ok
"
:
true
,
"
id
"
:
docid
,
"
rev
"
:
revs_info
[
0
].
rev
});
},
function
(
err
)
{
// xxx do we try to delete the posted document ?
err
.
message
=
"
Cannot save document revision tree
"
;
f
.
error
(
err
);
if
(
specific_parameter
.
removeAttachment
)
{
// copy metadata (not beginning by "_" to document
for
(
k
in
res_doc
)
{
if
(
res_doc
.
hasOwnProperty
(
k
)
&&
!
k
.
match
(
"
^_
"
))
{
doc
[
k
]
=
res_doc
[
k
];
}
);
};
f
.
error
=
function
(
err
)
{
f
.
error
=
function
()
{};
that
.
error
(
err
);
};
f
.
getDocumentTree
();
};
/**
* Update the document metadata and update a document tree.
* Options:
* - {boolean} keep_revision_history To keep the previous revisions
* (false by default) (NYI).
* @method put
* @param {object} command The JIO command
*/
that
.
put
=
function
(
command
)
{
that
.
post
(
command
);
};
/**
* Create/Update the document attachment and update a document tree.
* Options:
* - {boolean} keep_revision_history To keep the previous revisions
* (false by default) (NYI).
* @method putAttachment
* @param {object} command The JIO command
*/
that
.
putAttachment
=
function
(
command
)
{
var
functions
=
{},
doc
,
doctree
,
revs_info
,
prev_doc
;
doc
=
command
.
cloneDoc
();
functions
.
begin
=
function
()
{
if
(
typeof
doc
.
_rev
===
"
string
"
&&
!
priv
.
checkRevisionFormat
(
doc
.
_rev
))
{
that
.
error
({
"
status
"
:
31
,
"
statusText
"
:
"
Wrong Revision Format
"
,
"
error
"
:
"
wrong_revision_format
"
,
"
message
"
:
"
The document previous revision does not match
"
+
"
^[0-9]+-[0-9a-zA-Z]+$
"
,
"
reason
"
:
"
Previous revision is wrong
"
});
return
;
}
functions
.
getDocumentTree
();
};
functions
.
getDocumentTree
=
function
()
{
var
option
=
command
.
cloneOption
();
if
(
option
.
max_retry
===
0
)
{
option
.
max_retry
=
3
;
}
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
command
.
getDocId
()
+
priv
.
doctree_suffix
,
option
,
function
(
response
)
{
doctree
=
response
;
functions
.
updateRevsInfo
();
functions
.
getDocument
();
},
function
(
err
)
{
switch
(
err
.
status
)
{
case
404
:
doctree
=
priv
.
createDocumentTree
();
functions
.
updateRevsInfo
();
functions
.
getDocument
();
break
;
default
:
err
.
message
=
"
Cannot get document revision tree
"
;
that
.
error
(
err
);
break
;
}
}
);
};
functions
.
updateRevsInfo
=
function
()
{
if
(
doc
.
_revs
)
{
revs_info
=
priv
.
updateDocumentTree
(
doctree
,
doc
.
_revs
);
if
(
specific_parameter
.
remove
)
{
priv
.
putDocumentTree
(
doc
,
option
,
doc_tree
,
callback
.
putDocumentTree
);
}
else
{
revs_info
=
priv
.
postToDocumentTree
(
doctree
,
doc
);
console
.
log
(
"
res_doc
"
);
console
.
log
(
res_doc
);
priv
.
getAttachmentList
(
res_doc
,
option
,
callback
.
getAttachmentList
);
}
};
functions
.
postEmptyDocument
=
function
()
{
that
.
addJob
(
"
post
"
,
priv
.
substorage
,
{
"
_id
"
:
command
.
getDocId
()
+
"
.
"
+
revs_info
[
0
].
rev
},
command
.
getOption
(),
function
(
response
)
{
doc
.
_rev
=
response
.
rev
;
functions
.
postAttachment
();
},
function
(
err
)
{
err
.
message
=
"
Cannot upload document
"
;
that
.
error
(
err
);
callback
.
getAttachmentList
=
function
(
err
,
res_list
)
{
console
.
log
(
"
c getAttachmentList
"
);
var
i
,
attachment_found
=
false
;
if
(
err
)
{
err
.
message
=
"
Cannot get attachment
"
;
return
onEnd
(
err
,
undefined
);
}
);
};
functions
.
getDocument
=
function
()
{
if
(
revs_info
[
1
]
===
undefined
)
{
functions
.
postEmptyDocument
();
}
else
{
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
command
.
getDocId
()
+
"
.
"
+
revs_info
[
1
].
rev
,
command
.
getOption
(),
function
(
response
)
{
var
attachment_list
=
[],
i
;
prev_doc
=
response
;
for
(
i
in
response
.
_attachments
)
{
if
(
response
.
_attachments
.
hasOwnProperty
(
i
))
{
attachment_list
.
push
({
"
id
"
:
i
,
"
attachment
"
:
{
"
_id
"
:
command
.
getDocId
()
+
"
.
"
+
revs_info
[
0
].
rev
+
"
/
"
+
i
,
"
_mimetype
"
:
response
.
_attachments
[
i
].
content_type
,
"
_data
"
:
undefined
}});
}
}
functions
.
postDocument
(
attachment_list
);
},
function
(
err
)
{
if
(
err
.
status
===
404
)
{
functions
.
postDocument
([]);
return
;
attachment_list
=
res_list
||
[];
if
(
specific_parameter
.
getAttachment
)
{
// getting specific attachment
for
(
i
=
0
;
i
<
attachment_list
.
length
;
i
+=
1
)
{
if
(
attachment_list
[
i
]
&&
doc
.
_attachment
===
attachment_list
[
i
].
_attachment
)
{
return
onEnd
(
attachment_list
[
i
].
_data
);
}
err
.
message
=
"
Cannot upload document
"
;
that
.
error
(
err
);
}
);
return
onEnd
(
priv
.
notFoundError
(
"
Unable to get an inexistent attachment
"
,
"
missing
"
),
undefined
);
}
};
functions
.
postDocument
=
function
(
attachment_list
)
{
that
.
addJob
(
"
post
"
,
priv
.
substorage
,
command
.
getDocId
()
+
"
.
"
+
revs_info
[
0
].
rev
,
command
.
getOption
(),
function
(
response
)
{
var
i
;
if
(
attachment_list
.
length
===
0
)
{
functions
.
postAttachment
();
}
else
{
functions
.
post_attachment_count
=
attachment_list
.
length
;
if
(
specific_parameter
.
remove_from_attachment_list
)
{
// removing specific attachment
for
(
i
=
0
;
i
<
attachment_list
.
length
;
i
+=
1
)
{
functions
.
copyAttachment
(
attachment_list
[
i
].
id
,
attachment_list
[
i
].
attachment
);
}
if
(
attachment_list
[
i
]
&&
specific_parameter
.
remove_from_attachment_list
.
_attachment
===
attachment_list
[
i
].
_attachment
)
{
attachment_found
=
true
;
attachment_list
[
i
]
=
undefined
;
break
;
}
},
function
(
err
)
{
err
.
message
=
"
Cannot upload document
"
;
that
.
error
(
err
);
}
);
};
functions
.
copyAttachment
=
function
(
attachmentid
,
attachment
)
{
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
prev_doc
.
_id
+
"
/
"
+
attachmentid
,
command
.
cloneOption
(),
function
(
response
)
{
attachment
.
_data
=
response
;
that
.
addJob
(
"
putAttachment
"
,
priv
.
substorage
,
attachment
,
command
.
cloneOption
(),
function
(
response
)
{
functions
.
postAttachment
();
},
function
(
err
)
{
err
.
message
=
"
Cannot copy previous attachment
"
;
functions
.
error
(
err
);
if
(
!
attachment_found
)
{
return
onEnd
(
priv
.
notFoundError
(
"
Unable to remove an inexistent attachment
"
,
"
missing
"
),
undefined
);
}
);
},
function
(
err
)
{
err
.
message
=
"
Cannot copy previous attachment
"
;
functions
.
error
(
err
);
}
);
priv
.
putDocument
(
doc
,
option
,
callback
.
putDocument
);
};
functions
.
post_attachment_count
=
0
;
functions
.
postAttachment
=
function
()
{
functions
.
post_attachment_count
-=
1
;
if
(
functions
.
post_attachment_count
>
0
)
{
return
;
callback
.
putDocument
=
function
(
err
,
response
)
{
console
.
log
(
"
c putDocument
"
);
var
i
,
attachment_found
=
false
;
if
(
err
)
{
err
.
message
=
"
Cannot post the document
"
;
return
onEnd
(
err
,
undefined
);
}
that
.
addJob
(
"
putAttachment
"
,
priv
.
substorage
,
{
"
_id
"
:
command
.
getDocId
()
+
"
.
"
+
revs_info
[
0
].
rev
+
"
/
"
+
command
.
getAttachmentId
(),
"
_mimetype
"
:
command
.
getAttachmentMimeType
(),
"
_data
"
:
command
.
getAttachmentData
()
},
command
.
cloneOption
(),
function
()
{
functions
.
sendDocumentTree
();
},
function
(
err
)
{
switch
(
err
.
status
)
{
case
409
:
// file already exists
functions
.
sendDocumentTree
();
break
;
default
:
err
.
message
=
"
Cannot upload attachment
"
;
functions
.
error
(
err
);
if
(
specific_parameter
.
add_to_attachment_list
)
{
// adding specific attachment
attachment_list
=
attachment_list
||
[];
for
(
i
=
0
;
i
<
attachment_list
.
length
;
i
+=
1
)
{
if
(
specific_parameter
.
add_to_attachment_list
.
_attachment
===
attachment_list
[
i
].
_attachment
)
{
attachment_found
=
true
;
attachment_list
[
i
]
=
specific_parameter
.
add_to_attachment_list
;
break
;
}
}
if
(
!
attachment_found
)
{
attachment_list
.
unshift
(
specific_parameter
.
add_to_attachment_list
);
}
}
priv
.
putAttachmentList
(
doc
,
option
,
attachment_list
,
callback
.
putAttachmentList
);
};
functions
.
sendDocumentTree
=
function
()
{
doctree
.
_id
=
command
.
getDocId
()
+
priv
.
doctree_suffix
;
that
.
addJob
(
"
put
"
,
priv
.
substorage
,
doctree
,
command
.
cloneOption
(),
function
()
{
that
.
success
({
"
ok
"
:
true
,
"
id
"
:
command
.
getDocId
()
+
"
/
"
+
command
.
getAttachmentId
(),
"
rev
"
:
revs_info
[
0
].
rev
});
},
function
(
err
)
{
// xxx do we try to delete the posted document ?
err
.
message
=
"
Cannot save document revision tree
"
;
functions
.
error
(
err
);
callback
.
putAttachmentList
=
function
(
err
,
response
)
{
console
.
log
(
"
c putAttachmentList
"
);
if
(
err
)
{
err
.
message
=
"
Cannot copy attacments to the document
"
;
return
onEnd
(
err
,
undefined
);
}
);
priv
.
putDocumentTree
(
doc
,
option
,
doc_tree
,
callback
.
putDocumentTree
);
};
functions
.
error
=
function
(
err
)
{
functions
.
error
=
function
()
{};
that
.
error
(
err
);
callback
.
putDocumentTree
=
function
(
err
,
response
)
{
console
.
log
(
"
c putDocumentTree
"
);
if
(
err
)
{
err
.
message
=
"
Cannot update the document history
"
;
return
onEnd
(
err
,
undefined
);
}
onEnd
(
undefined
,
{
"
ok
"
:
true
,
"
id
"
:
doc
.
_id
+
(
specific_parameter
.
putAttachment
||
specific_parameter
.
removeAttachment
||
specific_parameter
.
getAttachment
?
"
/
"
+
doc
.
_attachment
:
""
),
"
rev
"
:
doc
.
_rev
});
// if (option.keep_revision_history !== true) {
// // priv.remove(prev_doc, option, function () {
// // - change "available" status to "deleted"
// // - remove attachments
// // - done, no callback
// // });
// }
};
functions
.
begin
();
callback
.
begin
();
};
/**
*
Get the document metadata or attachment
.
*
Post the document metadata and create or update a document tree
.
* Options:
* - {boolean} revs Add simple revision history (false by default).
* - {boolean} revs_info Add revs info (false by default).
* - {boolean} conflicts Add conflict object (false by default).
* @method get
* - {boolean} keep_revision_history To keep the previous revisions
* (false by default) (NYI).
* @method post
* @param {object} command The JIO command
*/
that
.
get
=
function
(
command
)
{
var
f
=
{},
doctree
,
revs_info
,
prev_rev
,
option
;
option
=
command
.
cloneOption
();
if
(
option
.
max_retry
===
0
)
{
option
.
max_retry
=
3
;
}
prev_rev
=
command
.
getDocInfo
(
"
_rev
"
);
if
(
typeof
prev_rev
===
"
string
"
)
{
if
(
!
priv
.
checkRevisionFormat
(
prev_rev
))
{
that
.
error
({
"
status
"
:
31
,
"
statusText
"
:
"
Wrong Revision Format
"
,
"
error
"
:
"
wrong_revision_format
"
,
"
message
"
:
"
The document previous revision does not match
"
+
"
[0-9]+-[0-9a-zA-Z]+
"
,
"
reason
"
:
"
Previous revision is wrong
"
});
return
;
}
}
f
.
getDocumentTree
=
function
()
{
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
{
"
_id
"
:
command
.
getDocId
()
+
priv
.
doctree_suffix
,
"
_rev
"
:
command
.
getDocInfo
(
"
_rev
"
)
},
option
,
function
(
response
)
{
doctree
=
response
;
if
(
prev_rev
===
undefined
)
{
revs_info
=
priv
.
getWinnerRevisionFromDocumentTree
(
doctree
);
if
(
revs_info
.
length
>
0
)
{
prev_rev
=
revs_info
[
0
].
rev
;
}
else
{
that
.
error
({
"
status
"
:
404
,
"
statusText
"
:
"
Not Found
"
,
"
error
"
:
"
not_found
"
,
"
message
"
:
"
Cannot find the document
"
,
"
reason
"
:
"
Document is deleted
"
});
return
;
}
}
else
{
revs_info
=
priv
.
getRevisionFromDocumentTree
(
doctree
,
prev_rev
);
}
f
.
getDocument
(
command
.
getDocId
()
+
"
.
"
+
prev_rev
,
command
.
getAttachmentId
());
},
function
(
err
)
{
switch
(
err
.
status
)
{
case
404
:
that
.
error
(
err
);
break
;
default
:
err
.
message
=
"
Cannot get document revision tree
"
;
that
.
error
(
err
);
break
;
}
}
);
};
f
.
getDocument
=
function
(
docid
,
attmtid
)
{
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
{
"
_id
"
:
docid
,
"
_rev
"
:
command
.
getDocInfo
(
"
_rev
"
)},
option
,
function
(
response
)
{
var
attmt
;
if
(
typeof
response
!==
"
string
"
)
{
if
(
attmtid
!==
undefined
)
{
if
(
response
.
_attachments
!==
undefined
)
{
attmt
=
response
.
_attachments
[
attmtid
];
if
(
attmt
!==
undefined
)
{
prev_rev
=
priv
.
getRevisionFromPosition
(
revs_info
,
attmt
.
revpos
);
f
.
getDocument
(
command
.
getDocId
()
+
"
.
"
+
prev_rev
+
"
/
"
+
attmtid
);
return
;
}
}
that
.
error
({
"
status
"
:
404
,
"
statusText
"
:
"
Not Found
"
,
"
error
"
:
"
not_found
"
,
"
message
"
:
"
Cannot find the attachment
"
,
"
reason
"
:
"
Attachment is missing
"
});
return
;
}
response
.
_id
=
command
.
getDocId
();
response
.
_rev
=
prev_rev
;
if
(
command
.
getOption
(
"
revs
"
)
===
true
)
{
response
.
_revisions
=
priv
.
revsInfoToHistory
(
revs_info
);
}
if
(
command
.
getOption
(
"
revs_info
"
)
===
true
)
{
response
.
_revs_info
=
revs_info
;
}
if
(
command
.
getOption
(
"
conflicts
"
)
===
true
)
{
response
.
_conflicts
=
priv
.
getLeavesFromDocumentTree
(
doctree
,
prev_rev
);
if
(
response
.
_conflicts
.
length
===
0
)
{
delete
response
.
_conflicts
;
}
}
that
.
post
=
function
(
command
)
{
priv
.
revisionGenericRequest
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
{},
function
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
that
.
success
(
response
);
},
function
(
err
)
{
that
.
error
(
err
);
}
);
};
if
(
command
.
getAttachmentId
()
&&
prev_rev
!==
undefined
)
{
f
.
getDocument
(
command
.
getDocId
()
+
"
.
"
+
prev_rev
+
"
/
"
+
command
.
getAttachmentId
());
}
else
{
f
.
getDocumentTree
();
}
};
/**
*
Remove document or attachment
.
*
Put the document metadata and create or update a document tree
.
* Options:
* - {boolean} keep_revision_history To keep the previous revisions
* @method remove
* (false by default) (NYI).
* @method put
* @param {object} command The JIO command
*/
that
.
remove
=
function
(
command
)
{
var
f
=
{},
del_rev
,
option
,
new_doc
,
revs_info
;
option
=
command
.
cloneOption
();
if
(
option
.
max_retry
===
0
)
{
option
.
max_retry
=
3
;
that
.
put
=
function
(
command
)
{
priv
.
revisionGenericRequest
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
{},
function
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
that
.
success
(
response
);
}
del_rev
=
command
.
getDoc
().
_rev
;
f
.
removeDocument
=
function
(
docid
,
doctree
)
{
if
(
command
.
getOption
(
"
keep_revision_history
"
)
!==
true
)
{
if
(
command
.
getAttachmentId
()
===
undefined
)
{
// update tree
revs_info
=
priv
.
postToDocumentTree
(
doctree
,
command
.
getDoc
(),
true
);
// remove revision
that
.
addJob
(
"
remove
"
,
priv
.
substorage
,
docid
,
option
,
function
()
{
// put tree
doctree
.
_id
=
command
.
getDocId
()
+
priv
.
doctree_suffix
;
that
.
addJob
(
"
put
"
,
priv
.
substorage
,
doctree
,
};
that
.
putAttachment
=
function
(
command
)
{
priv
.
revisionGenericRequest
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
function
()
{
that
.
success
({
"
ok
"
:
true
,
"
id
"
:
command
.
getDocId
(),
"
rev
"
:
revs_info
[
0
].
rev
});
{
"
doc_id
"
:
command
.
getDocId
(),
"
attachment_id
"
:
command
.
getAttachmentId
(),
"
add_to_attachment_list
"
:
{
"
_attachment
"
:
command
.
getAttachmentId
(),
"
_mimetype
"
:
command
.
getAttachmentMimeType
(),
"
_data
"
:
command
.
getAttachmentData
()
},
function
()
{
that
.
error
({
"
status
"
:
409
,
"
statusText
"
:
"
Conflict
"
,
"
error
"
:
"
conflict
"
,
"
message
"
:
"
Document update conflict.
"
,
"
reason
"
:
"
Cannot update document tree
"
});
return
;
}
);
"
putAttachment
"
:
true
},
function
()
{
that
.
error
({
"
status
"
:
404
,
"
statusText
"
:
"
Not Found
"
,
"
error
"
:
"
not_found
"
,
"
message
"
:
"
File not found
"
,
"
reason
"
:
"
Document was not found
"
});
return
;
function
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
that
.
success
(
response
);
}
);
}
else
{
// get previsous document
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
command
.
getDocId
()
+
"
.
"
+
del_rev
,
option
,
function
(
response
)
{
// update tree
revs_info
=
priv
.
postToDocumentTree
(
doctree
,
command
.
getDoc
());
new_doc
=
response
;
delete
new_doc
.
_attachments
;
new_doc
.
_id
=
new_doc
.
_id
+
"
.
"
+
revs_info
[
0
].
rev
;
};
// post new document version
that
.
addJob
(
"
post
"
,
priv
.
substorage
,
new_doc
,
command
.
cloneOption
(),
function
()
{
// put tree
doctree
.
_id
=
command
.
getDocId
()
+
priv
.
doctree_suffix
;
that
.
addJob
(
"
put
"
,
priv
.
substorage
,
doctree
,
that
.
remove
=
function
(
command
)
{
if
(
command
.
getAttachmentId
())
{
return
that
.
removeAttachment
(
command
);
}
priv
.
revisionGenericRequest
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
function
()
{
that
.
success
({
"
ok
"
:
true
,
"
id
"
:
new_doc
.
_id
,
"
rev
"
:
revs_info
[
0
].
rev
});
{
"
revision_needed
"
:
true
,
"
remove
"
:
true
},
function
(
err
)
{
err
.
message
=
"
Cannot save document revision tree
"
;
that
.
error
(
err
);
function
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
);
},
function
()
{
that
.
error
({
"
status
"
:
409
,
"
statusText
"
:
"
Conflict
"
,
"
error
"
:
"
conflict
"
,
"
message
"
:
"
Document update conflict.
"
,
"
reason
"
:
"
Cannot update document
"
});
return
;
that
.
success
(
response
);
}
);
},
function
()
{
that
.
error
({
"
status
"
:
404
,
"
statusText
"
:
"
Not Found
"
,
"
error
"
:
"
not_found
"
,
"
message
"
:
"
File not found
"
,
"
reason
"
:
"
Document was not found
"
});
return
;
};
that
.
removeAttachment
=
function
(
command
)
{
priv
.
revisionGenericRequest
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
{
"
doc_id
"
:
command
.
getDocId
(),
"
attachment_id
"
:
command
.
getAttachmentId
(),
"
revision_needed
"
:
true
,
"
removeAttachment
"
:
true
,
"
remove_from_attachment_list
"
:
{
"
_attachment
"
:
command
.
getAttachmentId
()
}
);
},
function
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
that
.
success
(
response
);
}
);
};
if
(
typeof
del_rev
===
"
string
"
)
{
if
(
!
priv
.
checkRevisionFormat
(
del_rev
))
{
that
.
error
({
"
status
"
:
31
,
"
statusText
"
:
"
Wrong Revision Format
"
,
"
error
"
:
"
wrong_revision_format
"
,
"
message
"
:
"
The document previous revision does not match
"
+
"
[0-9]+-[0-9a-zA-Z]+
"
,
"
reason
"
:
"
Previous revision is wrong
"
});
return
;
}
}
// get doctree
that
.
addJob
(
"
get
"
,
priv
.
substorage
,
command
.
getDocId
()
+
priv
.
doctree_suffix
,
option
,
function
(
response
)
{
response
.
_conflicts
=
priv
.
getLeavesFromDocumentTree
(
response
);
if
(
del_rev
===
undefined
)
{
// no revision provided
that
.
error
({
"
status
"
:
409
,
"
statusText
"
:
"
Conflict
"
,
"
error
"
:
"
conflict
"
,
"
message
"
:
"
Document update conflict.
"
,
"
reason
"
:
"
Cannot delete a document without revision
"
});
return
;
}
// revision provided
if
(
priv
.
isRevisionALeaf
(
response
,
del_rev
)
===
true
)
{
if
(
typeof
command
.
getAttachmentId
()
===
"
string
"
)
{
f
.
removeDocument
(
command
.
getDocId
()
+
"
.
"
+
del_rev
+
"
/
"
+
command
.
getAttachmentId
(),
response
);
}
else
{
f
.
removeDocument
(
command
.
getDocId
()
+
"
.
"
+
del_rev
,
response
);
}
}
else
{
that
.
error
({
"
status
"
:
409
,
"
statusText
"
:
"
Conflict
"
,
"
error
"
:
"
conflict
"
,
"
message
"
:
"
Document update conflict.
"
,
"
reason
"
:
"
Trying to remove non-latest revision
"
});
return
;
that
.
get
=
function
(
command
)
{
if
(
command
.
getAttachmentId
())
{
return
that
.
getAttachment
(
command
);
}
priv
.
revisionGenericRequest
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
{
"
get
"
:
true
},
function
()
{
that
.
error
({
"
status
"
:
404
,
"
statusText
"
:
"
Not Found
"
,
"
error
"
:
"
not_found
"
,
"
message
"
:
"
Document tree not found, please checkdocument ID
"
,
"
reason
"
:
"
Incorrect document ID
"
});
return
;
function
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
that
.
success
(
response
);
}
);
};
/**
* Get all documents
* @method allDocs
* @param {object} command The JIO command
*/
that
.
allDocs
=
function
()
{
setTimeout
(
function
()
{
that
.
error
({
"
status
"
:
405
,
"
statusText
"
:
"
Method Not Allowed
"
,
"
error
"
:
"
method_not_allowed
"
,
"
message
"
:
"
Your are not allowed to use this command
"
,
"
reason
"
:
"
LocalStorage forbids AllDocs command executions
"
});
});
that
.
getAttachment
=
function
(
command
)
{
priv
.
revisionGenericRequest
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
{
"
doc_id
"
:
command
.
getDocId
(),
"
attachment_id
"
:
command
.
getAttachmentId
(),
"
getAttachment
"
:
true
},
function
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
that
.
success
(
response
);
}
);
};
// END //
priv
.
RevisionStorage
();
return
that
;
});
});
// end RevisionStorage
test/jiotests.js
View file @
d7fcbbaf
...
...
@@ -34,7 +34,12 @@ clone = function (obj) {
// generates a revision hash from document metadata, revision history
// and the deleted_flag
generateRevisionHash
=
function
(
doc
,
revisions
,
deleted_flag
)
{
var
string
=
JSON
.
stringify
(
doc
)
+
JSON
.
stringify
(
revisions
)
+
var
string
;
doc
=
clone
(
doc
);
delete
doc
.
_rev
;
delete
doc
.
_revs
;
delete
doc
.
_revs_info
;
string
=
JSON
.
stringify
(
doc
)
+
JSON
.
stringify
(
revisions
)
+
JSON
.
stringify
(
deleted_flag
?
true
:
false
);
return
hex_sha256
(
string
);
},
...
...
@@ -1264,7 +1269,8 @@ test ("Post", function(){
o
.
doc
=
{
"
_id
"
:
"
post1
"
,
"
_rev
"
:
o
.
rev
,
"
title
"
:
"
myPost2
"
};
o
.
revisions
=
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
rev
.
split
(
'
-
'
)[
1
]]};
o
.
rev
=
"
2-
"
+
generateRevisionHash
(
o
.
doc
,
o
.
revisions
);
o
.
spy
(
o
,
"
status
"
,
undefined
,
"
Post + revision
"
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
post1
"
,
"
rev
"
:
o
.
rev
},
"
Post + revision
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
...
...
@@ -1293,8 +1299,92 @@ test ("Post", function(){
"
Check document tree
"
);
o
.
jio
.
stop
();
// add attachment
o
.
doc
.
_attachments
=
{
"
attachment_test
"
:
{
"
length
"
:
35
,
"
digest
"
:
"
A
"
,
"
content_type
"
:
"
oh/yeah
"
}
};
localstorage
.
setItem
(
o
.
localpath
+
"
/post1.
"
+
o
.
rev
,
o
.
doc
);
localstorage
.
setItem
(
o
.
localpath
+
"
/post1.
"
+
o
.
rev
+
"
/attachment_test
"
,
"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
"
);
// post + attachment copy
o
.
doc
=
{
"
_id
"
:
"
post1
"
,
"
_rev
"
:
o
.
rev
,
"
title
"
:
"
myPost2
"
};
o
.
revisions
=
{
"
start
"
:
2
,
"
ids
"
:
[
o
.
rev
.
split
(
'
-
'
)[
1
],
o
.
revisions
.
ids
[
0
]]
};
o
.
rev
=
"
3-
"
+
generateRevisionHash
(
o
.
doc
,
o
.
revisions
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
post1
"
,
"
rev
"
:
o
.
rev
},
"
Post + attachment copy
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// check attachment
deepEqual
(
localstorage
.
getItem
(
o
.
localpath
+
"
/post1.
"
+
o
.
rev
+
"
/attachment_test
"
),
"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
"
,
"
Check Attachment
"
);
// check document tree
o
.
doc_tree
.
_id
=
"
post1.revision_tree.json
"
;
o
.
doc_tree
.
children
[
0
].
children
[
0
].
children
.
unshift
({
"
rev
"
:
o
.
rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
});
deepEqual
(
localstorage
.
getItem
(
o
.
localpath
+
"
/post1.revision_tree.json
"
),
o
.
doc_tree
,
"
Check document tree
"
);
// post + wrong revision
o
.
doc
=
{
"
_id
"
:
"
post1
"
,
"
_rev
"
:
"
3-wr3
"
,
"
title
"
:
"
myPost3
"
};
o
.
revisions
=
{
"
start
"
:
3
,
"
ids
"
:
[
"
wr3
"
]};
o
.
rev
=
"
4-
"
+
generateRevisionHash
(
o
.
doc
,
o
.
revisions
);
o
.
spy
(
o
,
"
value
"
,
{
"
id
"
:
"
post1
"
,
"
ok
"
:
true
,
"
rev
"
:
o
.
rev
},
"
Postt + wrong revision
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// check document
deepEqual
(
localstorage
.
getItem
(
o
.
localpath
+
"
/post1.3-wr3
"
),
null
,
"
Check document
"
);
// check document
o
.
doc
.
_id
=
"
post1.
"
+
o
.
rev
;
delete
o
.
doc
.
_rev
;
deepEqual
(
localstorage
.
getItem
(
o
.
localpath
+
"
/post1.
"
+
o
.
rev
),
o
.
doc
,
"
Check document
"
);
// check document tree
o
.
doc_tree
.
_id
=
"
post1.revision_tree.json
"
;
o
.
doc_tree
.
children
.
unshift
({
"
rev
"
:
"
3-wr3
"
,
"
status
"
:
"
missing
"
,
"
children
"
:
[{
"
rev
"
:
o
.
rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
}]
});
deepEqual
(
localstorage
.
getItem
(
o
.
localpath
+
"
/post1.revision_tree.json
"
),
o
.
doc_tree
,
"
Check document tree
"
);
o
.
jio
.
stop
();
});
test
(
"
Put
"
,
function
(){
...
...
@@ -1562,7 +1652,7 @@ test("Put Attachment", function () {
// putAttachment without document
o
.
revisions
=
{
"
start
"
:
0
,
"
ids
"
:
[]}
o
.
rev_hash
=
generateRevisionHash
({
"
_id
"
:
"
doc1
/
attmt1
"
},
o
.
rev_hash
=
generateRevisionHash
({
"
_id
"
:
"
doc1
"
,
"
_attachment
"
:
"
attmt1
"
},
o
.
revisions
);
o
.
rev
=
"
1-
"
+
o
.
rev_hash
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1/attmt1
"
,
"
rev
"
:
o
.
rev
},
...
...
@@ -1601,9 +1691,9 @@ test("Put Attachment", function () {
o
.
prev_rev
=
o
.
rev
;
o
.
revisions
=
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
rev_hash
]}
o
.
rev_hash
=
generateRevisionHash
({
"
_id
"
:
"
doc1
/attmt1
"
,
"
_id
"
:
"
doc1
"
,
"
_data
"
:
"
abc
"
,
"
_
rev
"
:
o
.
prev_rev
"
_
attachment
"
:
"
attmt1
"
,
},
o
.
revisions
);
o
.
rev
=
"
2-
"
+
o
.
rev_hash
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1/attmt1
"
,
"
rev
"
:
o
.
rev
},
...
...
@@ -1646,9 +1736,9 @@ test("Put Attachment", function () {
o
.
prev_rev
=
o
.
rev
;
o
.
revisions
=
{
"
start
"
:
2
,
"
ids
"
:
[
o
.
rev_hash
,
o
.
revisions
.
ids
[
0
]]}
o
.
rev_hash
=
generateRevisionHash
({
"
_id
"
:
"
doc1
/attmt2
"
,
"
_id
"
:
"
doc1
"
,
"
_data
"
:
"
def
"
,
"
_
rev
"
:
o
.
prev_rev
"
_
attachment
"
:
"
attmt2
"
,
},
o
.
revisions
);
o
.
rev
=
"
3-
"
+
o
.
rev_hash
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1/attmt2
"
,
"
rev
"
:
o
.
rev
},
...
...
@@ -1723,15 +1813,16 @@ test ("Get", function(){
o
.
doctree
=
{
"
children
"
:[{
"
rev
"
:
"
1-rev1
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
}]};
o
.
doc_myget1
=
{
"
_id
"
:
"
get1
"
,
"
title
"
:
"
myGet1
"
};
o
.
doc_myget1
=
{
"
_id
"
:
"
get1
.1-rev1
"
,
"
title
"
:
"
myGet1
"
};
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.revision_tree.json
"
,
o
.
doctree
);
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.1-rev1
"
,
o
.
doc_myget1
);
// get document
o
.
doc_myget1_cloned
=
clone
(
o
.
doc_myget1
);
o
.
doc_myget1_cloned
[
"
_rev
"
]
=
"
1-rev1
"
;
o
.
doc_myget1_cloned
[
"
_revisions
"
]
=
{
"
start
"
:
1
,
"
ids
"
:
[
"
rev1
"
]};
o
.
doc_myget1_cloned
[
"
_revs_info
"
]
=
[{
o
.
doc_myget1_cloned
.
_id
=
"
get1
"
;
o
.
doc_myget1_cloned
.
_rev
=
"
1-rev1
"
;
o
.
doc_myget1_cloned
.
_revisions
=
{
"
start
"
:
1
,
"
ids
"
:
[
"
rev1
"
]};
o
.
doc_myget1_cloned
.
_revs_info
=
[{
"
rev
"
:
"
1-rev1
"
,
"
status
"
:
"
available
"
}];
o
.
spy
(
o
,
"
value
"
,
o
.
doc_myget1_cloned
,
"
Get document (winner)
"
);
...
...
@@ -1748,14 +1839,15 @@ test ("Get", function(){
"
rev
"
:
"
2-rev3
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
}]
}]};
o
.
doc_myget2
=
{
"
_id
"
:
"
get1
"
,
"
title
"
:
"
myGet2
"
};
o
.
doc_myget3
=
{
"
_id
"
:
"
get1
"
,
"
title
"
:
"
myGet3
"
};
o
.
doc_myget2
=
{
"
_id
"
:
"
get1
.1-rev2
"
,
"
title
"
:
"
myGet2
"
};
o
.
doc_myget3
=
{
"
_id
"
:
"
get1
.2-rev3
"
,
"
title
"
:
"
myGet3
"
};
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.revision_tree.json
"
,
o
.
doctree
);
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.1-rev2
"
,
o
.
doc_myget2
);
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.2-rev3
"
,
o
.
doc_myget3
);
// get document
o
.
doc_myget3_cloned
=
clone
(
o
.
doc_myget3
);
o
.
doc_myget3_cloned
.
_id
=
"
get1
"
;
o
.
doc_myget3_cloned
[
"
_rev
"
]
=
"
2-rev3
"
;
o
.
doc_myget3_cloned
[
"
_revisions
"
]
=
{
"
start
"
:
2
,
"
ids
"
:
[
"
rev3
"
,
"
rev2
"
]};
o
.
doc_myget3_cloned
[
"
_revs_info
"
]
=
[{
...
...
@@ -1780,6 +1872,7 @@ test ("Get", function(){
// get specific document
o
.
doc_myget2_cloned
=
clone
(
o
.
doc_myget2
);
o
.
doc_myget2_cloned
.
_id
=
"
get1
"
;
o
.
doc_myget2_cloned
[
"
_rev
"
]
=
"
1-rev2
"
;
o
.
doc_myget2_cloned
[
"
_revisions
"
]
=
{
"
start
"
:
1
,
"
ids
"
:
[
"
rev2
"
]};
o
.
doc_myget2_cloned
[
"
_revs_info
"
]
=
[{
...
...
@@ -1793,18 +1886,16 @@ test ("Get", function(){
o
.
tick
(
o
);
// adding an attachment
o
.
attmt_myget
2
=
{
o
.
attmt_myget
3
=
{
"
get2
"
:
{
"
length
"
:
3
,
"
digest
"
:
"
md5-dontcare
"
,
"
revpos
"
:
1
"
content_type
"
:
"
oh/yeah
"
}
};
o
.
doc_myget2
[
"
_attachments
"
]
=
o
.
attmt_myget2
;
o
.
doc_myget3
[
"
_attachments
"
]
=
o
.
attmt_myget2
;
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.1-rev2
"
,
o
.
doc_myget2
);
o
.
doc_myget3
.
_attachments
=
o
.
attmt_myget3
;
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.2-rev3
"
,
o
.
doc_myget3
);
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.
1-rev2
/get2
"
,
"
abc
"
);
localstorage
.
setItem
(
o
.
localpath
+
"
/get1.
2-rev3
/get2
"
,
"
abc
"
);
// get attachment winner
o
.
spy
(
o
,
"
value
"
,
"
abc
"
,
"
Get attachment (winner)
"
);
...
...
@@ -1820,13 +1911,13 @@ test ("Get", function(){
// get attachment specific rev
o
.
spy
(
o
,
"
value
"
,
"
abc
"
,
"
Get attachment (specific revision)
"
);
o
.
jio
.
get
({
"
_id
"
:
"
get1/get2
"
,
"
_rev
"
:
"
1-rev2
"
},
{
o
.
jio
.
get
({
"
_id
"
:
"
get1/get2
"
,
"
_rev
"
:
"
2-rev3
"
},
{
"
revs_info
"
:
true
,
"
revs
"
:
true
,
"
conflicts
"
:
true
,
},
o
.
f
);
o
.
tick
(
o
);
// get document with attachment (specific revision)
o
.
doc_myget2_cloned
[
"
_attachments
"
]
=
o
.
attmt_myget2
;
delete
o
.
doc_myget2_cloned
.
_attachments
;
o
.
spy
(
o
,
"
value
"
,
o
.
doc_myget2_cloned
,
"
Get document which have an attachment (specific revision)
"
);
o
.
jio
.
get
({
"
_id
"
:
"
get1
"
,
"
_rev
"
:
"
1-rev2
"
},
{
...
...
@@ -1835,7 +1926,7 @@ test ("Get", function(){
o
.
tick
(
o
);
// get document with attachment (winner)
o
.
doc_myget3_cloned
[
"
_attachments
"
]
=
o
.
attmt_myget2
;
o
.
doc_myget3_cloned
.
_attachments
=
o
.
attmt_myget3
;
o
.
spy
(
o
,
"
value
"
,
o
.
doc_myget3_cloned
,
"
Get document which have an attachment (winner)
"
);
o
.
jio
.
get
({
"
_id
"
:
"
get1
"
},
...
...
@@ -1862,188 +1953,135 @@ test ("Remove", function(){
o
.
localpath
=
"
jio/localstorage/urevrem/arevrem
"
;
// 1. remove document without revision
o
.
spy
(
o
,
"
status
"
,
404
,
"
Remove document (no doctree, no revision)
"
);
o
.
spy
(
o
,
"
status
"
,
409
,
"
Remove document without revision
"
+
"
-> 409 Conflict
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1
"
},
o
.
f
);
o
.
tick
(
o
);
// 2. remove attachment without revision
o
.
spy
(
o
,
"
status
"
,
404
,
"
Remove attachment (no doctree, no revision)
"
);
o
.
spy
(
o
,
"
status
"
,
409
,
"
Remove attachment without revision
"
+
"
-> 409 Conflict
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1/remove2
"
},
o
.
f
);
o
.
tick
(
o
);
// adding
two docu
ments
o
.
doc_myremove1
=
{
"
_id
"
:
"
remove1
"
,
"
title
"
:
"
myRemove1
"
};
o
.
doc_myremove2
=
{
"
_id
"
:
"
remove1
"
,
"
title
"
:
"
myRemove2
"
};
o
.
very_old_rev
=
"
1-veryoldrev
"
;
// adding
a document with attach
ments
o
.
doc_myremove1
=
{
"
_id
"
:
"
remove1.1-veryoldrev
"
,
"
title
"
:
"
myRemove1
"
}
;
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.
"
+
o
.
very_old_rev
,
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.1-veryoldrev
"
,
o
.
doc_myremove1
);
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.1-rev2
"
,
o
.
doc_myremove1
);
// add attachment
o
.
attmt_myremove1
=
{
"
remove2
"
:
{
o
.
doc_myremove1
.
_id
=
"
remove1.2-oldrev
"
;
o
.
attachment_remove2
=
{
"
length
"
:
3
,
"
digest
"
:
"
md5-dontcare
"
,
"
revpos
"
:
1
},
"
content_type
"
:
"
oh/yeah
"
}
o
.
attachment_remove3
=
{
"
length
"
:
5
,
"
digest
"
:
"
md5-865f5cc7fbd7854902eae9d8211f178a
"
,
"
content_type
"
:
"
he/ho
"
}
o
.
doc_myremove1
.
_attachments
=
{
"
remove2
"
:
o
.
attachment_remove2
,
"
remove3
"
:
o
.
attachment_remove3
};
o
.
doc_myremove1
=
{
"
_id
"
:
"
remove1
"
,
"
title
"
:
"
myRemove1
"
,
"
_attachments
"
:
o
.
attmt_myremove1
};
o
.
revisions
=
{
"
start
"
:
1
,
"
ids
"
:[
o
.
very_old_rev
.
split
(
'
-
'
),[
1
]]}
o
.
old_rev
=
"
2-
"
+
generateRevisionHash
(
o
.
doc_myremove1
,
o
.
revisions
);
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.
"
+
o
.
old_rev
,
o
.
doc_myremove1
);
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.
"
+
o
.
old_rev
+
"
/remove2
"
,
"
xyz
"
);
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.2-oldrev
"
,
o
.
doc_myremove1
);
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.2-oldrev/remove2
"
,
"
abc
"
);
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.2-oldrev/remove3
"
,
"
defgh
"
);
o
.
doctree
=
{
"
children
"
:[{
"
rev
"
:
o
.
very_old_rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
o
.
old_rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
// add document tree
o
.
doctree
=
{
"
children
"
:
[{
"
rev
"
:
"
1-veryoldrev
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
"
2-oldrev
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
}]
},{
"
rev
"
:
"
1-rev2
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
}]};
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.revision_tree.json
"
,
o
.
doctree
);
// 3. remove non existing attachment with revision
o
.
spy
(
o
,
"
status
"
,
404
,
"
Remove NON-existing attachment (revision)
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1.1-rev2/remove0
"
,
"
_rev
"
:
o
.
old_rev
},
o
.
f
);
o
.
tick
(
o
);
o
.
revisions
=
{
"
start
"
:
2
,
"
ids
"
:[
o
.
old_rev
.
split
(
'
-
'
)[
1
],
o
.
very_old_rev
.
split
(
'
-
'
)[
1
]
]};
o
.
doc_myremove1
=
{
"
_id
"
:
"
remove1/remove2
"
,
"
_rev
"
:
o
.
old_rev
};
o
.
rev
=
"
3-
"
+
generateRevisionHash
(
o
.
doc_myremove1
,
o
.
revisions
);
}]
};
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.revision_tree.json
"
,
o
.
doctree
);
// 4. remove existing attachment with revision
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
remove1.
"
+
o
.
rev
,
"
rev
"
:
o
.
rev
},
"
Remove existing attachment (revision)
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1/remove2
"
,
"
_rev
"
:
o
.
old_rev
},
o
.
f
);
// 3. remove inexistent attachment
o
.
spy
(
o
,
"
status
"
,
404
,
"
Remove inexistent attachment -> 404 Not Found
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1/remove0
"
,
"
_rev
"
:
"
2-oldrev
"
},
o
.
f
);
o
.
tick
(
o
);
o
.
testtree
=
{
"
children
"
:[{
"
rev
"
:
o
.
very_old_rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
o
.
old_rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
o
.
rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
// 4. remove existing attachment
o
.
rev_hash
=
generateRevisionHash
({
"
_id
"
:
"
remove1
"
,
"
_attachment
"
:
"
remove2
"
,
},
{
"
start
"
:
2
,
"
ids
"
:
[
"
oldrev
"
,
"
veryoldrev
"
]});
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
remove1/remove2
"
,
"
rev
"
:
"
3-
"
+
o
.
rev_hash
},
"
Remove existing attachment
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1/remove2
"
,
"
_rev
"
:
"
2-oldrev
"
},
o
.
f
);
o
.
tick
(
o
);
o
.
doctree
=
{
"
children
"
:[{
"
rev
"
:
"
1-veryoldrev
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
"
2-oldrev
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
"
3-
"
+
o
.
rev_hash
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
}]
}]
},{
"
rev
"
:
"
1-rev2
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
}]};
}]
};
// 5. check if document tree has been updated correctly
deepEqual
(
localstorage
.
getItem
(
"
jio/localstorage/urevrem/arevrem
/remove1.revision_tree.json
"
),
o
.
test
tree
,
"
Check document tree
"
);
o
.
localpath
+
"
/remove1.revision_tree.json
"
),
o
.
doc
tree
,
"
Check document tree
"
);
// 6. check if
attachment has been removed
// 6. check if
the attachment still exists
deepEqual
(
localstorage
.
getItem
(
"
jio/localstorage/urevrem/arevrem/remove1.
"
+
o
.
rev
+
"
/remove2
"
),
null
,
"
Check attachment
"
);
o
.
localpath
+
"
/remove1.2-oldrev
/remove2
"
),
"
abc
"
,
"
Check attachment -> still exists
"
);
// 7. check if document is updated
deepEqual
(
localstorage
.
getItem
(
"
jio/localstorage/urevrem/arevrem/remove1.
"
+
o
.
rev
),
{
"
_id
"
:
"
remove1.
"
+
o
.
rev
,
"
title
"
:
"
myRemove1
"
},
"
Check document
"
);
// add another attachment
o
.
attmt_myremove2
=
{
"
remove3
"
:
{
"
length
"
:
3
,
"
digest
"
:
"
md5-hello123
"
},
"
revpos
"
:
1
};
o
.
doc_myremove2
=
{
"
_id
"
:
"
remove1
"
,
"
title
"
:
"
myRemove2
"
,
"
_attachments
"
:
o
.
attmt_myremove2
};
o
.
revisions
=
{
"
start
"
:
1
,
"
ids
"
:[
"
rev2
"
]
};
o
.
second_old_rev
=
"
2-
"
+
generateRevisionHash
(
o
.
doc_myremove2
,
o
.
revisions
);
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.
"
+
o
.
second_old_rev
,
o
.
doc_myremove2
);
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.
"
+
o
.
second_old_rev
+
"
/remove3
"
,
"
stu
"
);
o
.
doctree
=
{
"
children
"
:[{
"
rev
"
:
o
.
very_old_rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
o
.
old_rev
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
o
.
rev
,
"
status
"
:
"
available
"
,
"
children
"
:[]
}]
}]
},{
"
rev
"
:
"
1-rev2
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[{
"
rev
"
:
o
.
second_old_rev
,
"
status
"
:
"
available
"
,
"
children
"
:[]
}]
}]};
localstorage
.
setItem
(
o
.
localpath
+
"
/remove1.revision_tree.json
"
,
o
.
doctree
);
// 8. remove non existing attachment without revision
o
.
spy
(
o
,
"
status
"
,
409
,
"
409 - Removing non-existing-attachment (no revision)
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1/remove0
"
},
o
.
f
);
o
.
tick
(
o
);
o
.
revisions
=
{
"
start
"
:
2
,
"
ids
"
:[
o
.
second_old_rev
.
split
(
'
-
'
)[
1
],
"
rev2
"
]};
o
.
doc_myremove3
=
{
"
_id
"
:
"
remove1/remove3
"
,
"
_rev
"
:
o
.
second_old_rev
};
o
.
second_rev
=
"
3-
"
+
generateRevisionHash
(
o
.
doc_myremove3
,
o
.
revisions
);
// 9. remove existing attachment without revision
o
.
spy
(
o
,
"
status
"
,
409
,
"
409 - Removing existing attachment (no revision)
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1/remove3
"
},
o
.
f
);
o
.
tick
(
o
);
// 10. remove wrong revision
o
.
spy
(
o
,
"
status
"
,
409
,
"
409 - Removing document (false revision)
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1
"
,
"
_rev
"
:
"
1-rev2
"
},
o
.
f
);
o
.
tick
(
o
);
o
.
revisions
=
{
"
start
"
:
3
,
"
ids
"
:[
o
.
rev
.
split
(
'
-
'
)[
1
],
o
.
old_rev
.
split
(
'
-
'
)[
1
],
o
.
very_old_rev
.
split
(
'
-
'
)[
1
]
]};
o
.
doc_myremove4
=
{
"
_id
"
:
"
remove1
"
,
"
_rev
"
:
o
.
rev
};
o
.
second_new_rev
=
"
4-
"
+
generateRevisionHash
(
o
.
doc_myremove4
,
o
.
revisions
,
true
);
// 11. remove document version with revision
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
remove1
"
,
"
rev
"
:
o
.
second_new_rev
},
"
Remove document (with revision)
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1
"
,
"
_rev
"
:
o
.
rev
},
o
.
f
);
o
.
localpath
+
"
/remove1.3-
"
+
o
.
rev_hash
),
{
"
_id
"
:
"
remove1.3-
"
+
o
.
rev_hash
,
"
title
"
:
"
myRemove1
"
,
"
_attachments
"
:
{
"
remove3
"
:
o
.
attachment_remove3
}
},
"
Check document
"
);
// 8. remove document with wrong revision
o
.
spy
(
o
,
"
status
"
,
409
,
"
Remove document with wrong revision
"
+
"
-> 409 Conflict
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1
"
,
"
_rev
"
:
"
1-a
"
},
o
.
f
);
o
.
tick
(
o
);
// 9. remove attachment wrong revision
o
.
spy
(
o
,
"
status
"
,
409
,
"
Remove attachment with wrong revision
"
+
"
-> 409 Conflict
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1/remove2
"
,
"
_rev
"
:
"
1-a
"
},
o
.
f
);
o
.
tick
(
o
);
// 10. remove document
o
.
last_rev
=
"
3-
"
+
o
.
rev_hash
;
o
.
rev_hash
=
generateRevisionHash
(
{
"
_id
"
:
"
remove1
"
},
{
"
start
"
:
3
,
"
ids
"
:
[
o
.
rev_hash
,
"
oldrev
"
,
"
veryoldrev
"
]},
true
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
remove1
"
,
"
rev
"
:
"
4-
"
+
o
.
rev_hash
},
"
Remove document
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1
"
,
"
_rev
"
:
o
.
last_rev
},
o
.
f
);
o
.
tick
(
o
);
o
.
testtree
[
"
children
"
][
0
][
"
children
"
][
0
][
"
children
"
][
0
][
"
children
"
].
push
({
"
rev
"
:
o
.
second_new_rev
,
// 11. check document tree
o
.
doctree
.
children
[
0
].
children
[
0
].
children
[
0
].
children
.
unshift
({
"
rev
"
:
"
4-
"
+
o
.
rev_hash
,
"
status
"
:
"
deleted
"
,
"
children
"
:
[]
});
o
.
testtree
[
"
children
"
][
1
][
"
children
"
].
push
({
"
rev
"
:
o
.
second_old_rev
,
"
status
"
:
"
available
"
,
"
children
"
:[]
});
deepEqual
(
localstorage
.
getItem
(
"
jio/localstorage/urevrem/arevrem/remove1.revision_tree.json
"
),
o
.
testtree
,
"
Check document tree
"
);
deepEqual
(
localstorage
.
getItem
(
"
jio/localstorage/urevrem/arevrem/remove1.
"
+
o
.
second_new_rev
+
"
/remove2
"
),
null
,
"
Check attachment
"
);
deepEqual
(
localstorage
.
getItem
(
"
jio/localstorage/urevrem/arevrem/remove1.
"
+
o
.
second_new_rev
),
null
,
"
Check document
"
);
// remove document without revision
o
.
spy
(
o
,
"
status
"
,
409
,
"
409 - Removing document (no revision)
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
remove1
"
},
o
.
f
);
o
.
tick
(
o
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath
+
"
/remove1.revision_tree.json
"
),
o
.
doctree
,
"
Check document tree
"
);
o
.
jio
.
stop
();
});
...
...
@@ -2196,9 +2234,7 @@ test ("Scenario", function(){
module
(
"
JIO Replicate Revision Storage
"
);
var
testReplicateRevisionStorageGenerator
=
function
(
sinon
,
jio_description
,
document_name_have_revision
)
{
var
testReplicateRevisionStorage
=
function
(
sinon
,
jio_description
)
{
var
o
=
generateTools
(
sinon
),
leavesAction
,
generateLocalPath
;
...
...
@@ -2285,223 +2321,223 @@ module ("JIO Replicate Revision Storage");
},
o
.
f
);
o
.
tick
(
o
);
// post a new document with id
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
post new doc with id
"
};
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev
},
"
Post document (with id)
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// /
// |
// 1-1
// check document
o
.
local_rev_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
revision
);
o
.
local_rev
=
"
1-
"
+
o
.
local_rev_hash
;
o
.
specific_rev_hash
=
o
.
local_rev_hash
;
o
.
specific_rev
=
o
.
local_rev
;
o
.
leavesAction
(
function
(
storage_description
,
param
)
{
var
suffix
=
""
,
doc
=
clone
(
o
.
doc
);
if
(
param
.
revision
)
{
doc
.
_id
+=
"
.
"
+
o
.
local_rev
;
suffix
=
"
.
"
+
o
.
local_rev
;
}
deepEqual
(
localstorage
.
getItem
(
generateLocalPath
(
storage_description
)
+
"
/doc1
"
+
suffix
),
doc
,
"
Check document
"
);
});
// get the post document without revision
o
.
spy
(
o
,
"
value
"
,
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
post new doc with id
"
,
"
_rev
"
:
"
1-1
"
,
"
_revisions
"
:
{
"
start
"
:
1
,
"
ids
"
:
[
"
1
"
]},
"
_revs_info
"
:
[{
"
rev
"
:
"
1-1
"
,
"
status
"
:
"
available
"
}]
},
"
Get the previous document (without revision)
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
},
o
.
f
);
o
.
tick
(
o
);
// post same document without revision
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
post same document without revision
"
};
o
.
rev
=
"
1-2
"
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev
},
"
Post same document (without revision)
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// /
// / \
// 1-1 1-2
// check document
o
.
local_rev
=
"
1-
"
+
generateRevisionHash
(
o
.
doc
,
o
.
revision
);
o
.
leavesAction
(
function
(
storage_description
,
param
)
{
var
suffix
=
""
,
doc
=
clone
(
o
.
doc
);
if
(
param
.
revision
)
{
doc
.
_id
+=
"
.
"
+
o
.
local_rev
;
suffix
=
"
.
"
+
o
.
local_rev
;
}
deepEqual
(
localstorage
.
getItem
(
generateLocalPath
(
storage_description
)
+
"
/doc1
"
+
suffix
),
doc
,
"
Check document
"
);
});
// post a new revision
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
post new revision
"
,
"
_rev
"
:
o
.
rev
};
o
.
rev
=
"
2-3
"
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev
},
"
Post document (with revision)
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// /
// / \
// 1-1 1-2
// |
// 2-3
// check document
o
.
revision
.
start
+=
1
;
o
.
revision
.
ids
.
unshift
(
o
.
local_rev
.
split
(
"
-
"
).
slice
(
1
).
join
(
"
-
"
));
o
.
doc
.
_rev
=
o
.
local_rev
;
o
.
local_rev
=
"
2-
"
+
generateRevisionHash
(
o
.
doc
,
o
.
revision
);
o
.
specific_rev_conflict
=
o
.
local_rev
;
o
.
leavesAction
(
function
(
storage_description
,
param
)
{
var
suffix
=
""
,
doc
=
clone
(
o
.
doc
);
delete
doc
.
_rev
;
if
(
param
.
revision
)
{
doc
.
_id
+=
"
.
"
+
o
.
local_rev
;
suffix
=
"
.
"
+
o
.
local_rev
;
}
deepEqual
(
localstorage
.
getItem
(
generateLocalPath
(
storage_description
)
+
"
/doc1
"
+
suffix
),
doc
,
"
Check document
"
);
});
// get the post document with revision
o
.
spy
(
o
,
"
value
"
,
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
post same document without revision
"
,
"
_rev
"
:
"
1-2
"
,
"
_revisions
"
:
{
"
start
"
:
1
,
"
ids
"
:
[
"
2
"
]},
"
_revs_info
"
:
[{
"
rev
"
:
"
1-2
"
,
"
status
"
:
"
available
"
}],
"
_conflicts
"
:
[
"
1-1
"
]
},
"
Get the previous document (with revision)
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
"
1-2
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
,
},
o
.
f
);
o
.
tick
(
o
);
// get the post document with specific revision
o
.
spy
(
o
,
"
value
"
,
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
post new doc with id
"
,
"
_rev
"
:
o
.
specific_rev
,
"
_revisions
"
:
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
specific_rev_hash
]},
"
_revs_info
"
:
[{
"
rev
"
:
o
.
specific_rev
,
"
status
"
:
"
available
"
}],
"
_conflicts
"
:
[
o
.
specific_rev_conflict
]
},
"
Get a previous document (with local storage revision)
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
specific_rev
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
,
},
o
.
f
);
o
.
tick
(
o
);
// put document without id
o
.
spy
(
o
,
"
status
"
,
20
,
"
Put document without id
"
)
o
.
jio
.
put
({},
o
.
f
);
o
.
tick
(
o
);
// put document without rev
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
put new document
"
};
o
.
rev
=
"
1-4
"
;
o
.
spy
(
o
,
"
value
"
,
{
"
id
"
:
"
doc1
"
,
"
ok
"
:
true
,
"
rev
"
:
o
.
rev
},
"
Put document without rev
"
)
o
.
jio
.
put
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// __/__
// / | \
// 1-1 1-2 1-4
// |
// 2-3
// put new revision
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
put new revision
"
,
"
_rev
"
:
"
1-4
"
};
o
.
rev
=
"
2-5
"
;
o
.
spy
(
o
,
"
value
"
,
{
"
id
"
:
"
doc1
"
,
"
ok
"
:
true
,
"
rev
"
:
o
.
rev
},
"
Put document without rev
"
)
o
.
jio
.
put
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// __/__
// / | \
// 1-1 1-2 1-4
// | |
// 2-3 2-5
// putAttachment to inexistent document
// putAttachment
// get document
// get attachment
// put document
// get document
// get attachment
// remove attachment
// get document
// get inexistent attachment
// remove document and conflict
o
.
rev
=
"
3-6
"
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev
},
"
Remove document
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
"
2-5
"
},
o
.
f
);
o
.
tick
(
o
);
// remove document and conflict
o
.
rev
=
"
3-7
"
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev
},
"
Remove document
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
"
2-3
"
},
o
.
f
);
o
.
tick
(
o
);
// remove document
o
.
rev
=
"
2-8
"
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev
},
"
Remove document
"
);
o
.
jio
.
remove
({
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
"
1-1
"
},
o
.
f
);
o
.
tick
(
o
);
// get inexistent document
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent document
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
},
o
.
f
);
o
.
tick
(
o
);
//
//
post a new document with id
//
o.doc = {"_id": "doc1", "title": "post new doc with id"};
//
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev},
//
"Post document (with id)");
//
o.jio.post(o.doc, o.f);
//
o.tick(o);
//
//
/
//
//
|
//
//
1-1
//
//
check document
//
o.local_rev_hash = generateRevisionHash(o.doc, o.revision);
//
o.local_rev = "1-" + o.local_rev_hash;
//
o.specific_rev_hash = o.local_rev_hash;
//
o.specific_rev = o.local_rev;
//
o.leavesAction(function (storage_description, param) {
//
var suffix = "", doc = clone(o.doc);
//
if (param.revision) {
//
doc._id += "." + o.local_rev;
//
suffix = "." + o.local_rev;
//
}
//
deepEqual(
//
localstorage.getItem(generateLocalPath(storage_description) +
//
"/doc1" + suffix),
//
doc, "Check document"
//
);
//
});
//
//
get the post document without revision
//
o.spy(o, "value", {
//
"_id": "doc1",
//
"title": "post new doc with id",
//
"_rev": "1-1",
//
"_revisions": {"start": 1, "ids": ["1"]},
//
"_revs_info": [{"rev": "1-1", "status": "available"}]
//
}, "Get the previous document (without revision)");
//
o.jio.get({"_id": "doc1"}, {
//
"conflicts": true,
//
"revs": true,
//
"revs_info": true
//
}, o.f);
//
o.tick(o);
//
//
post same document without revision
//
o.doc = {"_id": "doc1", "title": "post same document without revision"};
//
o.rev = "1-2";
//
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev},
//
"Post same document (without revision)");
//
o.jio.post(o.doc, o.f);
//
o.tick(o);
//
//
/
//
//
/ \
//
//
1-1 1-2
//
//
check document
//
o.local_rev = "1-" + generateRevisionHash(o.doc, o.revision);
//
o.leavesAction(function (storage_description, param) {
//
var suffix = "", doc = clone(o.doc);
//
if (param.revision) {
//
doc._id += "." + o.local_rev;
//
suffix = "." + o.local_rev;
//
}
//
deepEqual(
//
localstorage.getItem(generateLocalPath(storage_description) +
//
"/doc1" + suffix),
//
doc, "Check document"
//
);
//
});
//
//
post a new revision
//
o.doc = {"_id": "doc1", "title": "post new revision", "_rev": o.rev};
//
o.rev = "2-3";
//
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev},
//
"Post document (with revision)");
//
o.jio.post(o.doc, o.f);
//
o.tick(o);
//
//
/
//
//
/ \
//
//
1-1 1-2
//
//
|
//
//
2-3
//
//
check document
//
o.revision.start += 1;
//
o.revision.ids.unshift(o.local_rev.split("-").slice(1).join("-"));
//
o.doc._rev = o.local_rev;
//
o.local_rev = "2-" + generateRevisionHash(o.doc, o.revision);
//
o.specific_rev_conflict = o.local_rev;
//
o.leavesAction(function (storage_description, param) {
//
var suffix = "", doc = clone(o.doc);
//
delete doc._rev;
//
if (param.revision) {
//
doc._id += "." + o.local_rev;
//
suffix = "." + o.local_rev;
//
}
//
deepEqual(
//
localstorage.getItem(generateLocalPath(storage_description) +
//
"/doc1" + suffix),
//
doc, "Check document"
//
);
//
});
//
//
get the post document with revision
//
o.spy(o, "value", {
//
"_id": "doc1",
//
"title": "post same document without revision",
//
"_rev": "1-2",
//
"_revisions": {"start": 1, "ids": ["2"]},
//
"_revs_info": [{"rev": "1-2", "status": "available"}],
//
"_conflicts": ["1-1"]
//
}, "Get the previous document (with revision)");
//
o.jio.get({"_id": "doc1", "_rev": "1-2"}, {
//
"conflicts": true,
//
"revs": true,
//
"revs_info": true,
//
}, o.f);
//
o.tick(o);
//
//
get the post document with specific revision
//
o.spy(o, "value", {
//
"_id": "doc1",
//
"title": "post new doc with id",
//
"_rev": o.specific_rev,
//
"_revisions": {"start": 1, "ids": [o.specific_rev_hash]},
//
"_revs_info": [{"rev": o.specific_rev, "status": "available"}],
//
"_conflicts": [o.specific_rev_conflict]
//
}, "Get a previous document (with local storage revision)");
//
o.jio.get({"_id": "doc1", "_rev": o.specific_rev}, {
//
"conflicts": true,
//
"revs": true,
//
"revs_info": true,
//
}, o.f);
//
o.tick(o);
//
//
put document without id
//
o.spy(o, "status", 20, "Put document without id")
//
o.jio.put({}, o.f);
//
o.tick(o);
//
//
put document without rev
//
o.doc = {"_id": "doc1", "title": "put new document"};
//
o.rev = "1-4";
//
o.spy(o, "value", {"id": "doc1", "ok": true, "rev": o.rev},
//
"Put document without rev")
//
o.jio.put(o.doc, o.f);
//
o.tick(o);
//
//
__/__
//
//
/ | \
//
//
1-1 1-2 1-4
//
//
|
//
//
2-3
//
//
put new revision
//
o.doc = {"_id": "doc1", "title": "put new revision", "_rev": "1-4"};
//
o.rev = "2-5";
//
o.spy(o, "value", {"id": "doc1", "ok": true, "rev": o.rev},
//
"Put document without rev")
//
o.jio.put(o.doc, o.f);
//
o.tick(o);
//
//
__/__
//
//
/ | \
//
//
1-1 1-2 1-4
//
//
| |
//
//
2-3 2-5
//
//
putAttachment to inexistent document
//
//
putAttachment
//
//
get document
//
//
get attachment
//
//
put document
//
//
get document
//
//
get attachment
//
//
remove attachment
//
//
get document
//
//
get inexistent attachment
//
//
remove document and conflict
//
o.rev = "3-6";
//
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev},
//
"Remove document");
//
o.jio.remove({"_id": "doc1", "_rev": "2-5"}, o.f);
//
o.tick(o);
//
//
remove document and conflict
//
o.rev = "3-7";
//
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev},
//
"Remove document");
//
o.jio.remove({"_id": "doc1", "_rev": "2-3"}, o.f);
//
o.tick(o);
//
//
remove document
//
o.rev = "2-8";
//
o.spy(o, "value", {"ok": true, "id": "doc1", "rev": o.rev},
//
"Remove document");
//
o.jio.remove({"_id": "doc1", "_rev": "1-1"}, o.f);
//
o.tick(o);
//
//
get inexistent document
//
o.spy(o, "status", 404, "Get inexistent document");
//
o.jio.get({"_id": "doc1"}, {
//
"conflicts": true,
//
"revs": true,
//
"revs_info": true
//
}, o.f);
//
o.tick(o);
o
.
jio
.
stop
();
};
test
(
"
[Revision + Local Storage] Scenario
"
,
function
()
{
testReplicateRevisionStorage
Generator
(
this
,
{
testReplicateRevisionStorage
(
this
,
{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
revision
"
,
...
...
@@ -2514,7 +2550,7 @@ module ("JIO Replicate Revision Storage");
});
});
test
(
"
[Replicate Revision + Revision + Local Storage] Scenario
"
,
function
()
{
testReplicateRevisionStorage
Generator
(
this
,
{
testReplicateRevisionStorage
(
this
,
{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
replicaterevision
"
,
...
...
@@ -2530,27 +2566,27 @@ module ("JIO Replicate Revision Storage");
});
});
test
(
"
2x [Revision + Local Storage] Scenario
"
,
function
()
{
testReplicateRevisionStorage
Generator
(
this
,
{
testReplicateRevisionStorage
(
this
,
{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
ureprevlocloc1
"
,
"
application_name
"
:
"
areprevloc1
"
"
application_name
"
:
"
areprevloc
loc
1
"
}
},
{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
ureprevlocloc2
"
,
"
application_name
"
:
"
areprevloc2
"
"
application_name
"
:
"
areprevloc
loc
2
"
}
}]
});
});
test
(
"
2x [Replicate Rev + 2x [Rev + Local]] Scenario
"
,
function
()
{
testReplicateRevisionStorage
Generator
(
this
,
{
testReplicateRevisionStorage
(
this
,
{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
replicaterevision
"
,
...
...
@@ -2589,6 +2625,159 @@ module ("JIO Replicate Revision Storage");
}]
});
});
test
(
"
Synchronisation
"
,
function
()
{
var
o
=
generateTools
(
this
);
o
.
jio
=
JIO
.
newJio
({
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc1
"
,
"
application_name
"
:
"
asyncreprevlocloc1
"
}
},
{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc2
"
,
"
application_name
"
:
"
asyncreprevlocloc2
"
}
}]
});
o
.
localpath1
=
"
jio/localstorage/usyncreprevlocloc1/asyncreprevlocloc1
"
;
o
.
localpath2
=
"
jio/localstorage/usyncreprevlocloc2/asyncreprevlocloc2
"
;
// add documents to localstorage
o
.
doctree1_1
=
{
"
children
"
:
[{
"
rev
"
:
"
1-111
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[],
}]
};
o
.
doc1_1
=
{
"
_id
"
:
"
doc1.1-111
"
,
"
title
"
:
"
A
"
};
localstorage
.
setItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
,
o
.
doctree1_1
);
localstorage
.
setItem
(
o
.
localpath2
+
"
/doc1.revision_tree.json
"
,
o
.
doctree1_1
);
localstorage
.
setItem
(
o
.
localpath1
+
"
/
"
+
o
.
doc1_1
.
_id
,
o
.
doc1_1
);
localstorage
.
setItem
(
o
.
localpath2
+
"
/
"
+
o
.
doc1_1
.
_id
,
o
.
doc1_1
);
// no synchronisation
o
.
spy
(
o
,
"
value
"
,
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
"
1-1
"
,
"
title
"
:
"
A
"
},
"
Get document
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
);
// check documents from localstorage
deepEqual
([
localstorage
.
getItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
),
localstorage
.
getItem
(
o
.
localpath2
+
"
/doc1.revision_tree.json
"
),
],
[
o
.
doctree1_1
,
o
.
doctree1_1
],
"
Check revision trees, no synchro
"
);
// add documents to localstorage
o
.
doctree2_2
=
clone
(
o
.
doctree1_1
);
o
.
doctree2_2
.
children
[
0
].
children
.
push
({
"
rev
"
:
"
2-222
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
});
o
.
doc2_2
=
{
"
_id
"
:
"
doc1.2-222
"
,
"
title
"
:
"
B
"
};
localstorage
.
setItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
,
o
.
doctree2_2
);
localstorage
.
setItem
(
o
.
localpath1
+
"
/
"
+
o
.
doc2_2
.
_id
,
o
.
doc2_2
);
// document synchronisation without conflict
o
.
spy
(
o
,
"
value
"
,
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
"
1-2
"
,
"
title
"
:
"
B
"
},
"
Get document
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
,
50000
);
// check documents from localstorage
deepEqual
([
localstorage
.
getItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
),
localstorage
.
getItem
(
o
.
localpath2
+
"
/doc1.revision_tree.json
"
),
],
[
o
.
doctree2_2
,
o
.
doctree2_2
],
"
Check revision trees, rev synchro
"
);
// add documents to localstorage
o
.
doctree2_2
.
children
[
0
].
children
.
unshift
({
"
rev
"
:
"
2-223
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
});
o
.
doc2_2
=
{
"
_id
"
:
"
doc1.2-223
"
,
"
title
"
:
"
B
"
};
localstorage
.
setItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
,
o
.
doctree2_2
);
localstorage
.
setItem
(
o
.
localpath1
+
"
/
"
+
o
.
doc2_2
.
_id
,
o
.
doc2_2
);
// document synchronisation with conflict
o
.
spy
(
o
,
"
value
"
,
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
"
1-2
"
,
"
title
"
:
"
B
"
},
"
Get document
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
,
50000
);
// check documents from localstorage
deepEqual
([
localstorage
.
getItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
),
localstorage
.
getItem
(
o
.
localpath2
+
"
/doc1.revision_tree.json
"
),
],
[
o
.
doctree2_2
,
o
.
doctree2_2
],
"
Check revision trees, rev synchro
"
);
////////////////////////////////////////////////////////////////////////////////
// // add documents to localstorage
// o.doctree2_2 = clone(o.doctree1_1);
// o.doctree2_2.children[0].children.push({
// "rev": "2-222",
// "status": "available",
// "children": []
// });
// o.doc2_2 = {"_id": "doc1.2-222", "title": "B"};
// localstorage.setItem(o.localpath1 + "/doc1.revision_tree.json",
// o.doctree2_2);
// localstorage.setItem(o.localpath1 + "/" + o.doc2_2._id, o.doc2_2);
// // document synchronisation without conflict
// o.spy(o, "value", {"_id": "doc1", "_rev": "1-2", "title": "B"},
// "Get document");
// o.jio.get({"_id": "doc1"}, o.f);
// o.tick(o, 50000);
// // check documents from localstorage
// deepEqual([
// localstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"),
// localstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"),
// ], [o.doctree2_2, o.doctree2_2], "Check revision trees, rev synchro");
// // add documents to localstorage
// o.doctree2_2.children[0].children.unshift({
// "rev": "2-223",
// "status": "available",
// "children": []
// });
// o.doc2_2 = {"_id": "doc1.2-223", "title": "B"};
// localstorage.setItem(o.localpath1 + "/doc1.revision_tree.json",
// o.doctree2_2);
// localstorage.setItem(o.localpath1 + "/" + o.doc2_2._id, o.doc2_2);
// // document synchronisation with conflict
// o.spy(o, "value", {"_id": "doc1", "_rev": "1-2", "title": "B"},
// "Get document");
// o.jio.get({"_id": "doc1"}, o.f);
// o.tick(o, 50000);
// // check documents from localstorage
// deepEqual([
// localstorage.getItem(o.localpath1 + "/doc1.revision_tree.json"),
// localstorage.getItem(o.localpath2 + "/doc1.revision_tree.json"),
// ], [o.doctree2_2, o.doctree2_2], "Check revision trees, rev synchro");
o
.
jio
.
stop
();
});
/*
module ("Jio DAVStorage");
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment