Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
J
jio-main
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
Hardik Juneja
jio-main
Commits
d54f3480
Commit
d54f3480
authored
Mar 11, 2013
by
Tristan Cavelier
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'replicaterevisionstorage2'
parents
c048fd03
8e7efe86
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
2176 additions
and
1645 deletions
+2176
-1645
src/jio.storage/replicaterevisionstorage.js
src/jio.storage/replicaterevisionstorage.js
+477
-289
src/jio.storage/revisionstorage.js
src/jio.storage/revisionstorage.js
+708
-1072
src/jio/commands/command.js
src/jio/commands/command.js
+4
-1
src/jio/jio.core.js
src/jio/jio.core.js
+65
-5
src/jio/storages/storage.js
src/jio/storages/storage.js
+12
-0
test/jiotests.js
test/jiotests.js
+910
-278
No files found.
src/jio.storage/replicaterevisionstorage.js
View file @
d54f3480
...
...
@@ -20,13 +20,11 @@ jIO.addStorageType('replicaterevision', function (spec, my) {
priv
.
storage_list_key
=
"
storage_list
"
;
priv
.
storage_list
=
spec
[
priv
.
storage_list_key
];
my
.
env
=
my
.
env
||
spec
.
env
||
{};
priv
.
emptyFunction
=
function
()
{};
that
.
specToStore
=
function
()
{
var
o
=
{};
o
[
priv
.
storage_list_key
]
=
priv
.
storage_list
;
o
.
env
=
my
.
env
;
return
o
;
};
...
...
@@ -68,21 +66,6 @@ jIO.addStorageType('replicaterevision', function (spec, my) {
return
newlist
;
};
/**
* Generates the next revision
* @method generateNextRevision
* @param {number|string} previous_revision The previous revision
* @param {string} docid The document id
* @return {string} The next revision
*/
priv
.
generateNextRevision
=
function
(
previous_revision
,
docid
)
{
my
.
env
[
docid
].
id
+=
1
;
if
(
typeof
previous_revision
===
"
string
"
)
{
previous_revision
=
parseInt
(
previous_revision
.
split
(
"
-
"
)[
0
],
10
);
}
return
(
previous_revision
+
1
)
+
"
-
"
+
my
.
env
[
docid
].
id
.
toString
();
};
/**
* Checks a revision format
* @method checkRevisionFormat
...
...
@@ -93,34 +76,6 @@ jIO.addStorageType('replicaterevision', function (spec, my) {
return
(
/^
[
0-9
]
+-
[
0-9a-zA-Z_
]
+$/
.
test
(
revision
));
};
/**
* Initalize document environment object
* @method initEnv
* @param {string} docid The document id
* @return {object} The reference to the environment
*/
priv
.
initEnv
=
function
(
docid
)
{
my
.
env
[
docid
]
=
{
"
id
"
:
0
,
"
distant_revisions
"
:
{},
"
my_revisions
"
:
{},
"
last_revisions
"
:
[]
};
return
my
.
env
[
docid
];
};
priv
.
updateEnv
=
function
(
doc_env
,
doc_env_rev
,
index
,
doc_rev
)
{
doc_env
.
last_revisions
[
index
]
=
doc_rev
;
if
(
doc_rev
!==
undefined
)
{
if
(
!
doc_env
.
my_revisions
[
doc_env_rev
])
{
doc_env
.
my_revisions
[
doc_env_rev
]
=
[];
doc_env
.
my_revisions
[
doc_env_rev
].
length
=
priv
.
storage_list
.
length
;
}
doc_env
.
my_revisions
[
doc_env_rev
][
index
]
=
doc_rev
;
doc_env
.
distant_revisions
[
doc_rev
]
=
doc_env_rev
;
}
};
/**
* Clones an object in deep (without functions)
* @method clone
...
...
@@ -188,106 +143,490 @@ jIO.addStorageType('replicaterevision', function (spec, my) {
};
/**
* Post the document metadata to all sub storages
* @method post
* @param {object} command The JIO command
* Use "send" method to all sub storages.
* Calling "callback" only with the first response
* @method sendToAllFastestResponseOnly
* @param {string} method The request method
* @param {object} doc The document object
* @param {object} option The request option
* @param {function} callback The callback. Parameters:
* - {string} The request method
* - {object} The error object
* - {object} The response object
*/
that
.
post
=
function
(
command
)
{
var
functions
=
{},
doc_env
,
revs_info
,
doc
,
my_rev
;
functions
.
begin
=
function
()
{
doc
=
command
.
cloneDoc
();
priv
.
sendToAllFastestResponseOnly
=
function
(
method
,
doc
,
option
,
callback
)
{
var
i
,
callbackWrapper
,
error_count
,
last_error
;
error_count
=
0
;
callbackWrapper
=
function
(
method
,
index
,
err
,
response
)
{
if
(
err
)
{
error_count
+=
1
;
last_error
=
err
;
if
(
error_count
===
priv
.
storage_list
.
length
)
{
return
callback
(
method
,
err
,
response
);
}
}
callback
(
method
,
err
,
response
);
};
for
(
i
=
0
;
i
<
priv
.
storage_list
.
length
;
i
+=
1
)
{
priv
.
send
(
method
,
i
,
doc
,
option
,
callbackWrapper
);
}
};
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
;
/**
* Use "sendToAll" method, calling "callback" at the last response with
* the response list
* @method sendToAllGetResponseList
* @param {string} method The request method
* @param {object} doc The document object
* @param {object} option The request option
* @return {function} callback The callback. Parameters:
* - {string} The request method
* - {object} The error object
* - {object} The response object
*/
priv
.
sendToAllGetResponseList
=
function
(
method
,
doc
,
option
,
callback
)
{
var
wrapper
,
callback_count
=
0
,
response_list
=
[],
error_list
=
[];
response_list
.
length
=
priv
.
storage_list
.
length
;
wrapper
=
function
(
method
,
index
,
err
,
response
)
{
error_list
[
index
]
=
err
;
response_list
[
index
]
=
response
;
callback_count
+=
1
;
if
(
callback_count
===
priv
.
storage_list
.
length
)
{
callback
(
error_list
,
response_list
);
}
if
(
typeof
doc
.
_id
!==
"
string
"
)
{
doc
.
_id
=
priv
.
generateUuid
();
};
priv
.
sendToAll
(
method
,
doc
,
option
,
wrapper
);
};
/**
* Checks if the sub storage are identical
* @method check
* @param {object} command The JIO command
*/
that
.
check
=
function
(
command
)
{
function
callback
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
that
.
success
(
response
);
}
if
(
priv
.
post_allowed
===
undefined
)
{
priv
.
post_allowed
=
true
;
priv
.
check
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
callback
);
};
/**
* Repair the sub storages to make them identical
* @method repair
* @param {object} command The JIO command
*/
that
.
repair
=
function
(
command
)
{
function
callback
(
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
doc_env
=
my
.
env
[
doc
.
_id
];
if
(
!
doc_env
||
!
doc_env
.
id
)
{
doc_env
=
priv
.
initEnv
(
doc
.
_id
);
that
.
success
(
response
);
}
my_rev
=
priv
.
generateNextRevision
(
doc
.
_rev
||
0
,
doc
.
_id
);
functions
.
sendDocument
();
priv
.
repair
(
command
.
cloneDoc
(),
command
.
cloneOption
(),
true
,
callback
);
};
functions
.
sendDocument
=
function
()
{
var
i
,
cloned_doc
;
priv
.
check
=
function
(
doc
,
option
,
success
,
error
)
{
priv
.
repair
(
doc
,
option
,
false
,
success
,
error
);
};
priv
.
repair
=
function
(
doc
,
option
,
repair
,
callback
)
{
var
functions
=
{};
callback
=
callback
||
priv
.
emptyFunction
;
option
=
option
||
{};
functions
.
begin
=
function
()
{
// };
// functions.repairAllSubStorages = function () {
var
i
;
for
(
i
=
0
;
i
<
priv
.
storage_list
.
length
;
i
+=
1
)
{
cloned_doc
=
priv
.
clone
(
doc
);
if
(
typeof
cloned_doc
.
_rev
===
"
string
"
&&
doc_env
.
my_revisions
[
cloned_doc
.
_rev
]
!==
undefined
)
{
cloned_doc
.
_rev
=
doc_env
.
my_revisions
[
cloned_doc
.
_rev
][
i
];
}
priv
.
send
(
doc_env
.
last_revisions
[
i
]
===
"
unique_
"
+
i
||
priv
.
put_only
?
"
put
"
:
"
post
"
,
repair
?
"
repair
"
:
"
check
"
,
i
,
cloned_
doc
,
command
.
cloneOption
()
,
functions
.
checkSendResult
doc
,
option
,
functions
.
repairAllSubStoragesCallback
);
}
};
functions
.
checkSendResult
=
function
(
method
,
index
,
err
,
response
)
{
functions
.
repair_sub_storages_count
=
0
;
functions
.
repairAllSubStoragesCallback
=
function
(
method
,
index
,
err
,
response
)
{
if
(
err
)
{
if
(
err
.
status
===
409
)
{
if
(
method
!==
"
put
"
)
{
functions
.
sendDocumentIndex
(
"
put
"
,
index
,
functions
.
checkSendResult
return
that
.
error
(
err
);
}
functions
.
repair_sub_storages_count
+=
1
;
if
(
functions
.
repair_sub_storages_count
===
priv
.
storage_list
.
length
)
{
functions
.
getAllDocuments
(
functions
.
newParam
(
doc
,
option
,
repair
));
}
};
functions
.
newParam
=
function
(
doc
,
option
,
repair
)
{
var
param
=
{
"
doc
"
:
doc
,
// the document to repair
"
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]]
],
"
attachments
"
:
{
// attachmentA : {_id: attachmentA, _revs_info, _mimetype: ..}
// attachmentB : {_id: attachmentB, _revs_info, _mimetype: ..}
}
},
"
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
)
{
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
)
{
return
function
(
method
,
index
,
err
,
response
)
{
var
response_object
=
{};
if
(
param
.
deal_result_state
!==
"
ok
"
)
{
// deal result is in a wrong state, exit
return
;
}
if
(
err
)
{
if
(
err
.
status
!==
404
)
{
// get document failed, exit
param
.
deal_result_state
=
"
error
"
;
callback
({
"
status
"
:
40
,
"
statusText
"
:
"
Check Failed
"
,
"
error
"
:
"
check_failed
"
,
"
message
"
:
"
An error occured on the sub storage
"
,
"
reason
"
:
err
.
reason
},
undefined
);
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
return
;
}
// this is now the last response
functions
.
makeResponsesStats
(
param
.
responses
);
if
(
param
.
responses
.
stats_items
.
length
===
1
)
{
// the responses are equals!
response_object
.
ok
=
true
;
response_object
.
id
=
param
.
doc
.
_id
;
if
(
doc
.
_rev
)
{
response_object
.
rev
=
doc
.
_rev
;
// "rev": (typeof param.responses.list[0] === "object" ?
// param.responses.list[0]._rev : undefined)
}
callback
(
undefined
,
response_object
);
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
"
},
undefined
);
return
;
}
// repair
functions
.
getAttachments
(
param
);
};
};
functions
.
addConflicts
=
function
(
param
,
list
)
{
var
i
;
list
=
list
||
[];
for
(
i
=
0
;
i
<
list
.
length
;
i
+=
1
)
{
param
.
conflicts
[
list
[
i
]]
=
true
;
}
};
functions
.
makeResponsesStats
=
function
(
responses
)
{
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
.
getAttachments
=
function
(
param
)
{
var
response
,
parsed_response
,
attachment
;
for
(
response
in
param
.
responses
.
stats
)
{
if
(
param
.
responses
.
stats
.
hasOwnProperty
(
response
))
{
parsed_response
=
JSON
.
parse
(
response
);
for
(
attachment
in
parsed_response
.
_attachments
)
{
if
((
parsed_response
.
_attachments
).
hasOwnProperty
(
attachment
))
{
functions
.
get_attachment_count
+=
1
;
priv
.
send
(
"
get
"
,
param
.
responses
.
stats
[
response
][
0
],
{
"
_id
"
:
param
.
doc
.
_id
+
"
/
"
+
attachment
,
"
_rev
"
:
JSON
.
parse
(
response
).
_rev
},
param
.
option
,
functions
.
getAttachmentsCallback
(
param
,
attachment
,
param
.
responses
.
stats
[
response
]
)
);
}
}
}
}
};
functions
.
get_attachment_count
=
0
;
functions
.
getAttachmentsCallback
=
function
(
param
,
attachment_id
,
index_list
)
{
return
function
(
method
,
index
,
err
,
response
)
{
if
(
err
)
{
callback
({
"
status
"
:
40
,
"
statusText
"
:
"
Check Failed
"
,
"
error
"
:
"
check_failed
"
,
"
message
"
:
"
Unable to retreive attachments
"
,
"
reason
"
:
err
.
reason
},
undefined
);
return
;
}
functions
.
get_attachment_count
-=
1
;
param
.
responses
.
attachments
[
attachment_id
]
=
response
;
if
(
functions
.
get_attachment_count
===
0
)
{
functions
.
synchronizeAllSubStorage
(
param
);
if
(
param
.
option
.
synchronize_conflicts
!==
false
)
{
functions
.
synchronizeConflicts
(
param
);
}
}
};
};
functions
.
synchronizeAllSubStorage
=
function
(
param
)
{
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
]
);
}
}
priv
.
updateEnv
(
doc_env
,
my_rev
,
index
,
null
);
functions
.
error
(
err
);
}
functions
.
finished_count
-=
1
;
};
functions
.
synchronizeResponseToSubStorage
=
function
(
param
,
response
,
storage_list
)
{
var
i
,
new_doc
,
attachment_to_put
=
[];
if
(
response
===
undefined
)
{
// no response to sync
return
;
}
// success
priv
.
updateEnv
(
doc_env
,
my_rev
,
new_doc
=
JSON
.
parse
(
response
);
new_doc
.
_revs
=
new_doc
.
_revisions
;
delete
new_doc
.
_rev
;
delete
new_doc
.
_revisions
;
delete
new_doc
.
_conflicts
;
for
(
i
in
new_doc
.
_attachments
)
{
if
(
new_doc
.
_attachments
.
hasOwnProperty
(
i
))
{
attachment_to_put
.
push
({
"
_id
"
:
i
,
"
_mimetype
"
:
new_doc
.
_attachments
[
i
].
content_type
,
"
_revs_info
"
:
new_doc
.
_revs_info
});
}
}
for
(
i
=
0
;
i
<
storage_list
.
length
;
i
+=
1
)
{
functions
.
finished_count
+=
attachment_to_put
.
length
||
1
;
priv
.
send
(
"
put
"
,
storage_list
[
i
],
new_doc
,
param
.
option
,
functions
.
putAttachments
(
param
,
attachment_to_put
)
);
}
functions
.
finished_count
+=
1
;
functions
.
finished
();
};
functions
.
synchronizeConflicts
=
function
(
param
)
{
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
.
putAttachments
=
function
(
param
,
attachment_to_put
)
{
return
function
(
method
,
index
,
err
,
response
)
{
var
i
,
attachment
;
if
(
err
)
{
return
callback
({
"
status
"
:
40
,
"
statusText
"
:
"
Check Failed
"
,
"
error
"
:
"
check_failed
"
,
"
message
"
:
"
Unable to copy attachments
"
,
"
reason
"
:
err
.
reason
},
undefined
);
}
for
(
i
=
0
;
i
<
attachment_to_put
.
length
;
i
+=
1
)
{
attachment
=
{
"
_id
"
:
param
.
doc
.
_id
,
"
_attachment
"
:
attachment_to_put
[
i
].
_id
,
"
_mimetype
"
:
attachment_to_put
[
i
].
_mimetype
,
"
_revs_info
"
:
attachment_to_put
[
i
].
_revs_info
,
// "_revs_info": param.responses.list[index]._revs_info,
"
_data
"
:
param
.
responses
.
attachments
[
attachment_to_put
[
i
].
_id
]
};
attachment
.
_id
+=
"
/
"
+
attachment
.
_attachment
;
delete
attachment
.
_attachment
;
priv
.
send
(
"
putAttachment
"
,
index
,
response
.
rev
||
"
unique_
"
+
index
attachment
,
option
,
functions
.
putAttachmentCallback
(
param
)
);
functions
.
success
({
"
ok
"
:
true
,
"
id
"
:
doc
.
_id
,
"
rev
"
:
my_rev
});
}
if
(
attachment_to_put
.
length
===
0
)
{
functions
.
finished
();
}
};
};
functions
.
putAttachmentCallback
=
function
(
param
)
{
return
function
(
method
,
index
,
err
,
response
)
{
if
(
err
)
{
return
callback
(
err
,
undefined
);
}
functions
.
finished
();
};
functions
.
success
=
function
(
response
)
{
// can be called once
that
.
success
(
response
);
functions
.
success
=
priv
.
emptyFunction
;
};
functions
.
error_count
=
0
;
functions
.
error
=
function
(
err
)
{
functions
.
error_count
+=
1
;
if
(
functions
.
error_count
===
priv
.
storage_list
.
length
)
{
that
.
error
(
err
);
functions
.
error
=
priv
.
emptyFunction
;
functions
.
finished_count
=
0
;
functions
.
finished
=
function
()
{
var
response_object
=
{};
functions
.
finished_count
-=
1
;
if
(
functions
.
finished_count
===
0
)
{
response_object
.
ok
=
true
;
response_object
.
id
=
doc
.
_id
;
if
(
doc
.
_rev
)
{
response_object
.
rev
=
doc
.
_rev
;
}
callback
(
undefined
,
response_object
);
}
};
functions
.
begin
();
};
/**
* The generic method to use
* @method genericRequest
* @param {object} command The JIO command
* @param {string} method The method to use
*/
that
.
genericRequest
=
function
(
command
,
method
)
{
var
doc
=
command
.
cloneDoc
();
doc
.
_id
=
doc
.
_id
||
priv
.
generateUuid
();
priv
.
sendToAllFastestResponseOnly
(
method
,
doc
,
command
.
cloneOption
(),
function
(
method
,
err
,
response
)
{
if
(
err
)
{
return
that
.
error
(
err
);
}
that
.
success
(
response
);
}
);
};
/**
* Post the document metadata to all sub storages
* @method post
* @param {object} command The JIO command
*/
that
.
post
=
function
(
command
)
{
that
.
genericRequest
(
command
,
"
put
"
);
};
/**
* Put the document metadata to all sub storages
* @method put
* @param {object} command The JIO command
*/
that
.
put
=
function
(
command
)
{
priv
.
put_only
=
true
;
that
.
post
(
command
);
that
.
genericRequest
(
command
,
"
post
"
);
};
/**
...
...
@@ -295,195 +634,44 @@ jIO.addStorageType('replicaterevision', function (spec, my) {
* @method putAttachment
* @param {object} command The JIO command
*/
//
that.putAttachment = function (command) {
//
};
that
.
putAttachment
=
function
(
command
)
{
that
.
genericRequest
(
command
,
"
putAttachment
"
);
};
/**
* Get the document
or attachment
from all sub storages, get the fastest.
* Get the document from all sub storages, get the fastest.
* @method get
* @param {object} command The JIO command
*/
that
.
get
=
function
(
command
)
{
var
functions
=
{},
doc_env
,
doc
,
my_rev
,
revs_array
=
[];
functions
.
begin
=
function
()
{
var
i
;
doc
=
command
.
cloneDoc
();
doc_env
=
my
.
env
[
doc
.
_id
];
if
(
!
doc_env
||
!
doc_env
.
id
)
{
// document environment is not set
doc_env
=
priv
.
initEnv
(
doc
.
_id
);
}
// document environment is set now
revs_array
.
length
=
priv
.
storage_list
.
length
;
my_rev
=
doc
.
_rev
;
if
(
my_rev
)
{
functions
.
update_env
=
false
;
}
for
(
i
=
0
;
i
<
priv
.
storage_list
.
length
;
i
+=
1
)
{
// request all sub storages
if
(
doc_env
.
my_revisions
[
my_rev
])
{
// if my_rev exist, convert it to distant revision
doc
.
_rev
=
doc_env
.
my_revisions
[
my_rev
][
i
];
}
priv
.
send
(
"
get
"
,
i
,
doc
,
command
.
cloneOption
(),
functions
.
callback
);
}
};
functions
.
update_env
=
true
;
functions
.
callback
=
function
(
method
,
index
,
err
,
response
)
{
if
(
err
)
{
revs_array
[
index
]
=
null
;
functions
.
error
(
err
);
return
;
}
doc_env
.
last_revisions
[
index
]
=
response
.
_rev
||
"
unique_
"
+
index
;
revs_array
[
index
]
=
response
.
_rev
||
"
unique_
"
+
index
;
if
(
doc_env
.
distant_revisions
[
response
.
_rev
||
"
unique_
"
+
index
])
{
// the document revision is already known
if
(
functions
.
update_env
===
true
)
{
my_rev
=
doc_env
.
distant_revisions
[
response
.
_rev
||
"
unique_
"
+
index
];
}
}
else
{
// the document revision is unknown
if
(
functions
.
update_env
===
true
)
{
my_rev
=
priv
.
generateNextRevision
(
0
,
doc
.
_id
);
doc_env
.
my_revisions
[
my_rev
]
=
revs_array
;
doc_env
.
distant_revisions
[
response
.
_rev
||
"
unique_
"
+
index
]
=
my_rev
;
}
functions
.
update_env
=
false
;
}
response
.
_rev
=
my_rev
;
functions
.
success
(
response
);
};
functions
.
success
=
function
(
response
)
{
var
i
,
start
,
tmp
,
tmp_object
;
functions
.
success
=
priv
.
emptyFunction
;
if
(
doc_env
.
my_revisions
[
my_rev
])
{
// this was not a specific revision
// we can convert revisions recieved by the sub storage
if
(
response
.
_conflicts
)
{
// convert conflicting revisions to replicate revisions
tmp_object
=
{};
for
(
i
=
0
;
i
<
response
.
_conflicts
.
length
;
i
+=
1
)
{
tmp_object
[
doc_env
.
distant_revisions
[
response
.
_conflicts
[
i
]]
||
response
.
_conflicts
[
i
]]
=
true
;
}
response
.
_conflicts
=
priv
.
dictKeys2Array
(
tmp_object
);
}
if
(
response
.
_revisions
)
{
// convert revisions history to replicate revisions
tmp_object
=
{};
start
=
response
.
_revisions
.
start
;
for
(
i
=
0
;
i
<
response
.
_revisions
.
ids
.
length
;
i
+=
1
,
start
-=
1
)
{
tmp
=
doc_env
.
distant_revisions
[
start
+
"
-
"
+
response
.
_revisions
.
ids
[
i
]
];
if
(
tmp
)
{
response
.
_revisions
.
ids
[
i
]
=
tmp
.
split
(
"
-
"
).
slice
(
1
).
join
(
"
-
"
);
}
}
}
if
(
response
.
_revs_info
)
{
// convert revs info to replicate revisions
for
(
i
=
0
;
i
<
response
.
_revs_info
.
length
;
i
+=
1
)
{
tmp
=
doc_env
.
distant_revisions
[
response
.
_revs_info
[
i
].
rev
];
if
(
tmp
)
{
response
.
_revs_info
[
i
].
rev
=
tmp
;
}
}
}
}
that
.
success
(
response
);
};
functions
.
error_count
=
0
;
functions
.
error
=
function
(
err
)
{
functions
.
error_count
+=
1
;
if
(
functions
.
error_count
===
priv
.
storage_list
.
length
)
{
that
.
error
(
err
);
functions
.
error
=
priv
.
emptyFunction
;
}
that
.
genericRequest
(
command
,
"
get
"
);
};
functions
.
begin
();
/**
* Get the attachment from all sub storages, get the fastest.
* @method getAttachment
* @param {object} command The JIO command
*/
that
.
getAttachment
=
function
(
command
)
{
that
.
genericRequest
(
command
,
"
getAttachment
"
);
};
/**
* Remove the document
or attachment
from all sub storages.
* Remove the document from all sub storages.
* @method remove
* @param {object} command The JIO command
*/
that
.
remove
=
function
(
command
)
{
var
functions
=
{},
doc_env
,
revs_info
,
doc
,
my_rev
;
functions
.
begin
=
function
()
{
doc
=
command
.
cloneDoc
();
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
;
}
doc_env
=
my
.
env
[
doc
.
_id
];
if
(
!
doc_env
||
!
doc_env
.
id
)
{
doc_env
=
priv
.
initEnv
(
doc
.
_id
);
}
my_rev
=
priv
.
generateNextRevision
(
doc
.
_rev
||
0
,
doc
.
_id
);
functions
.
sendDocument
();
};
functions
.
sendDocument
=
function
()
{
var
i
,
cloned_doc
;
for
(
i
=
0
;
i
<
priv
.
storage_list
.
length
;
i
+=
1
)
{
cloned_doc
=
priv
.
clone
(
doc
);
if
(
typeof
cloned_doc
.
_rev
===
"
string
"
&&
doc_env
.
my_revisions
[
cloned_doc
.
_rev
]
!==
undefined
)
{
cloned_doc
.
_rev
=
doc_env
.
my_revisions
[
cloned_doc
.
_rev
][
i
];
}
priv
.
send
(
"
remove
"
,
i
,
cloned_doc
,
command
.
cloneOption
(),
functions
.
checkSendResult
);
}
that
.
end
();
};
functions
.
checkSendResult
=
function
(
method
,
index
,
err
,
response
)
{
if
(
err
)
{
priv
.
updateEnv
(
doc_env
,
my_rev
,
index
,
null
);
functions
.
error
(
err
);
return
;
}
// success
priv
.
updateEnv
(
doc_env
,
my_rev
,
index
,
response
.
rev
||
"
unique_
"
+
index
);
functions
.
success
({
"
ok
"
:
true
,
"
id
"
:
doc
.
_id
,
"
rev
"
:
my_rev
});
};
functions
.
success
=
function
(
response
)
{
// can be called once
that
.
success
(
response
);
functions
.
success
=
priv
.
emptyFunction
;
};
functions
.
error_count
=
0
;
functions
.
error
=
function
(
err
)
{
functions
.
error_count
+=
1
;
if
(
functions
.
error_count
===
priv
.
storage_list
.
length
)
{
that
.
error
(
err
);
functions
.
error
=
priv
.
emptyFunction
;
}
that
.
genericRequest
(
command
,
"
remove
"
);
};
functions
.
begin
();
/**
* Remove the attachment from all sub storages.
* @method remove
* @param {object} command The JIO command
*/
that
.
removeAttachment
=
function
(
command
)
{
that
.
genericRequest
(
command
,
"
removeAttachment
"
);
};
return
that
;
...
...
src/jio.storage/revisionstorage.js
View file @
d54f3480
...
...
@@ -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,825 @@ 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
);
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
)
{
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
)
{
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
)
{
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
(
typeof
docid
!==
"
string
"
)
{
doc
.
_id
=
priv
.
generateUuid
();
docid
=
doc
.
_id
;
if
(
specific_parameter
.
attachment_id
)
{
doc
.
_attachment
=
specific_parameter
.
attachment_id
;
}
f
.
getDocumentTree
=
function
()
{
var
option
=
command
.
cloneOption
();
if
(
option
.
max_retry
===
0
)
{
option
.
max_retry
=
3
;
callback
.
begin
=
function
()
{
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
);
}
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
:
// 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
)
{
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
"
;
f
.
error
(
err
);
break
;
return
onEnd
(
err
,
undefined
);
}
}
);
};
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
;
doc_tree
=
response
||
priv
.
newDocTree
();
if
(
specific_parameter
.
get
||
specific_parameter
.
getAttachment
)
{
if
(
!
doc
.
_rev
)
{
winner_info
=
priv
.
getWinnerRevsInfo
(
doc_tree
);
if
(
winner_info
.
length
===
0
)
{
return
onEnd
(
priv
.
notFoundError
(
"
Document not found
"
,
"
missing
"
),
undefined
);
}
if
(
winner_info
[
0
].
status
===
"
deleted
"
)
{
return
onEnd
(
priv
.
notFoundError
(
"
Document not found
"
,
"
deleted
"
),
undefined
);
}
err
.
message
=
"
Cannot retrieve document
"
;
f
.
error
(
err
);
doc
.
_rev
=
winner_info
[
0
].
rev
;
}
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
.
updateRevsInfo
=
function
()
{
if
(
doc
.
_revs
)
{
revs_info
=
priv
.
updateDocumentTree
(
doctree
,
doc
.
_revs
);
}
else
{
revs_info
=
priv
.
postToDocumentTree
(
doctree
,
doc
);
if
(
!
generate_new_revision
&&
specific_parameter
.
putAttachment
)
{
prev_doc
.
_rev
=
doc
.
_revs_info
[
0
].
rev
;
}
}
// 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
)
{
return
onEnd
(
priv
.
notFoundError
(
"
Unable to remove an inexistent document
"
,
"
missing
"
),
undefined
);
}
priv
.
putDocument
(
doc
,
option
,
callback
.
putDocument
);
};
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
();
callback
.
getDocument
=
function
(
err
,
res_doc
)
{
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
);
}
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
.
putAttachment
||
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
);
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
)
{
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
(
undefined
,
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
)
{
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
(
attachment_list
[
i
]
&&
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
)
{
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
)
{
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
src/jio/commands/command.js
View file @
d54f3480
...
...
@@ -2,6 +2,7 @@
/*global postCommand: true, putCommand: true, getCommand: true,
removeCommand: true, allDocsCommand: true,
putAttachmentCommand: true, failStatus: true, doneStatus: true,
checkCommand: true, repairCommand: true,
hex_md5: true */
var
command
=
function
(
spec
,
my
)
{
var
that
=
{},
...
...
@@ -16,7 +17,9 @@ var command = function (spec, my) {
'
get
'
:
getCommand
,
'
remove
'
:
removeCommand
,
'
allDocs
'
:
allDocsCommand
,
'
putAttachment
'
:
putAttachmentCommand
'
putAttachment
'
:
putAttachmentCommand
,
'
check
'
:
checkCommand
,
'
repair
'
:
repairCommand
};
// creates the good command thanks to his label
if
(
spec
.
label
&&
priv
.
commandlist
[
spec
.
label
])
{
...
...
src/jio/jio.core.js
View file @
d54f3480
...
...
@@ -4,7 +4,7 @@
storage_type_object: true, invalidStorageType: true, jobRules: true,
job: true, postCommand: true, putCommand: true, getCommand:true,
allDocsCommand: true, putAttachmentCommand: true,
removeCommand: true */
removeCommand: true
, checkCommand: true, repairCommand: true
*/
// Class jio
var
that
=
{},
priv
=
{},
jio_id_array_name
=
'
jio/id_array
'
;
spec
=
spec
||
{};
...
...
@@ -377,10 +377,10 @@ Object.defineProperty(that, "allDocs", {
* Put an attachment to a document.
* @method putAttachment
* @param {object} doc The document object. Contains at least:
* - {string} id The document id: "doc_id/attchment_id"
* - {string} data Base64 attachment data
* - {string} mimetype The attachment mimetype
* - {string} rev The attachment revision
* - {string}
_
id The document id: "doc_id/attchment_id"
* - {string}
_
data Base64 attachment data
* - {string}
_
mimetype The attachment mimetype
* - {string}
_
rev The attachment revision
* @param {object} options (optional) Contains some options:
* - {number} max_retry The number max of retries, 0 = infinity.
* - {boolean} revs Include revision history of the document.
...
...
@@ -408,3 +408,63 @@ Object.defineProperty(that, "putAttachment", {
});
}
});
/**
* Check a document.
* @method check
* @param {object} doc The document object. Contains at least:
* - {string} _id The document id
* @param {object} options (optional) Contains some options:
* - {number} max_retry The number max of retries, 0 = infinity.
* @param {function} callback (optional) The callback(err,response).
* @param {function} error (optional) The callback on error, if this
* callback is given in parameter, "callback" is changed as "success",
* called on success.
*/
Object
.
defineProperty
(
that
,
"
check
"
,
{
configurable
:
false
,
enumerable
:
false
,
writable
:
false
,
value
:
function
(
doc
,
options
,
success
,
callback
)
{
var
param
=
priv
.
parametersToObject
(
[
options
,
success
,
callback
],
{
max_retry
:
3
}
);
priv
.
addJob
(
checkCommand
,
{
doc
:
doc
,
options
:
param
.
options
,
callbacks
:
{
success
:
param
.
success
,
error
:
param
.
error
}
});
}
});
/**
* Repair a document.
* @method repair
* @param {object} doc The document object. Contains at least:
* - {string} _id The document id
* @param {object} options (optional) Contains some options:
* - {number} max_retry The number max of retries, 0 = infinity.
* @param {function} callback (optional) The callback(err,response).
* @param {function} error (optional) The callback on error, if this
* callback is given in parameter, "callback" is changed as "success",
* called on success.
*/
Object
.
defineProperty
(
that
,
"
repair
"
,
{
configurable
:
false
,
enumerable
:
false
,
writable
:
false
,
value
:
function
(
doc
,
options
,
success
,
callback
)
{
var
param
=
priv
.
parametersToObject
(
[
options
,
success
,
callback
],
{
max_retry
:
3
}
);
priv
.
addJob
(
repairCommand
,
{
doc
:
doc
,
options
:
param
.
options
,
callbacks
:
{
success
:
param
.
success
,
error
:
param
.
error
}
});
}
});
src/jio/storages/storage.js
View file @
d54f3480
...
...
@@ -159,6 +159,18 @@ var storage = function (spec, my) {
});
};
that
.
check
=
function
(
command
)
{
setTimeout
(
function
()
{
that
.
success
({
"
ok
"
:
true
,
"
id
"
:
command
.
getDocId
()});
});
};
that
.
repair
=
function
(
command
)
{
setTimeout
(
function
()
{
that
.
success
({
"
ok
"
:
true
,
"
id
"
:
command
.
getDocId
()});
});
};
that
.
success
=
function
()
{};
that
.
retry
=
function
()
{};
that
.
error
=
function
()
{};
...
...
test/jiotests.js
View file @
d54f3480
...
...
@@ -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
);
},
...
...
@@ -646,7 +651,6 @@ test ("Similar Jobs at the same time (Update)", function () {
o
.
jio
.
put
({
"
_id
"
:
"
file
"
,
"
content
"
:
"
content
"
},
o
.
f2
);
// 2
o
.
jio
.
put
({
"
_id
"
:
"
file
"
,
"
content
"
:
"
content
"
},
o
.
f3
);
// 3
deepEqual
(
getLastJob
(
o
.
jio
.
getId
()).
id
,
1
,
"
Check job queue
"
);
console
.
log
(
JSON
.
parse
(
JSON
.
stringify
(
localStorage
)));
o
.
tick
(
o
,
1000
,
"
f
"
);
o
.
tick
(
o
,
"
f2
"
);
o
.
tick
(
o
,
"
f3
"
);
...
...
@@ -1264,7 +1268,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 +1298,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
(){
...
...
@@ -1550,19 +1639,21 @@ test("Put Attachment", function () {
// putAttachment without doc id
// error 20 -> document id required
o
.
spy
(
o
,
"
status
"
,
20
,
"
PutAttachment without doc id
"
);
o
.
spy
(
o
,
"
status
"
,
20
,
"
PutAttachment without doc id
"
+
"
-> 20 document id required
"
);
o
.
jio
.
putAttachment
({},
o
.
f
);
o
.
tick
(
o
);
// putAttachment without attachment id
// erorr 22 -> attachment id required
o
.
spy
(
o
,
"
status
"
,
22
,
"
PutAttachment without attachment id
"
);
o
.
spy
(
o
,
"
status
"
,
22
,
"
PutAttachment without attachment id
"
+
"
-> 22 attachment id required
"
);
o
.
jio
.
putAttachment
({
"
_id
"
:
"
putattmt1
"
},
o
.
f
);
o
.
tick
(
o
);
// 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
},
...
...
@@ -1596,14 +1687,23 @@ test("Put Attachment", function () {
),
""
,
"
Check attachment
"
);
// adding a metadata to the document
o
.
doc
=
localstorage
.
getItem
(
"
jio/localstorage/urevputattmt/arevputattmt/doc1.
"
+
o
.
rev
);
o
.
doc
.
title
=
"
My Title
"
;
localstorage
.
setItem
(
"
jio/localstorage/urevputattmt/arevputattmt/doc1.
"
+
o
.
rev
,
o
.
doc
);
// update attachment
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
},
...
...
@@ -1622,6 +1722,7 @@ test("Put Attachment", function () {
),
{
"
_id
"
:
"
doc1.
"
+
o
.
rev
,
"
title
"
:
"
My Title
"
,
"
_attachments
"
:
{
"
attmt1
"
:
{
"
length
"
:
3
,
...
...
@@ -1646,9 +1747,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
},
...
...
@@ -1667,6 +1768,7 @@ test("Put Attachment", function () {
),
{
"
_id
"
:
"
doc1.
"
+
o
.
rev
,
"
title
"
:
"
My Title
"
,
"
_attachments
"
:
{
"
attmt1
"
:
{
"
length
"
:
3
,
...
...
@@ -1710,12 +1812,14 @@ test ("Get", function(){
o
.
localpath
=
"
jio/localstorage/urevget/arevget
"
;
// get inexistent document
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent document (winner)
"
);
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent document (winner)
"
+
"
-> 404 Not Found
"
);
o
.
jio
.
get
({
"
_id
"
:
"
get1
"
},
o
.
f
);
o
.
tick
(
o
);
// get inexistent attachment
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent attachment (winner)
"
);
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent attachment (winner)
"
+
"
-> 404 Not Found
"
);
o
.
jio
.
get
({
"
_id
"
:
"
get1/get2
"
},
o
.
f
);
o
.
tick
(
o
);
...
...
@@ -1723,15 +1827,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 +1853,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
"
]
=
[{
...
...
@@ -1772,7 +1878,8 @@ test ("Get", function(){
o
.
tick
(
o
);
// get inexistent specific document
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get document (inexistent specific revision)
"
);
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get document (inexistent specific revision)
"
+
"
-> 404 Not Found
"
);
o
.
jio
.
get
({
"
_id
"
:
"
get1
"
,
"
_rev
"
:
"
1-rev0
"
},
{
"
revs_info
"
:
true
,
"
revs
"
:
true
,
"
conflicts
"
:
true
,
},
o
.
f
);
...
...
@@ -1780,6 +1887,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 +1901,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)
"
);
...
...
@@ -1812,7 +1918,8 @@ test ("Get", function(){
o
.
tick
(
o
);
// get inexistent attachment specific rev
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent attachment (specific revision)
"
);
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent attachment (specific revision)
"
+
"
-> 404 Not Found
"
);
o
.
jio
.
get
({
"
_id
"
:
"
get1/get2
"
,
"
_rev
"
:
"
1-rev1
"
},
{
"
revs_info
"
:
true
,
"
revs
"
:
true
,
"
conflicts
"
:
true
,
},
o
.
f
);
...
...
@@ -1820,13 +1927,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 +1942,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,194 +1969,139 @@ 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
();
});
module
(
"
Jio Revision Storage + Local Storage
"
);
test
(
"
Scenario
"
,
function
(){
var
o
=
generateTools
(
this
);
...
...
@@ -2196,9 +2248,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
;
...
...
@@ -2253,15 +2303,15 @@ module ("JIO Replicate Revision Storage");
// check document
o
.
doc
.
_id
=
o
.
uuid
;
o
.
revision
=
{
"
start
"
:
0
,
"
ids
"
:
[]};
o
.
rev
=
"
1-1
"
;
o
.
local_rev
=
"
1-
"
+
generateRevisionHash
(
o
.
doc
,
o
.
revision
)
;
o
.
revision
s
=
{
"
start
"
:
0
,
"
ids
"
:
[]};
o
.
rev
_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
revisions
)
;
o
.
rev
=
"
1-
"
+
o
.
rev_hash
;
o
.
leavesAction
(
function
(
storage_description
,
param
)
{
var
suffix
=
""
,
doc
=
clone
(
o
.
doc
);
if
(
param
.
revision
)
{
deepEqual
(
o
.
response_rev
,
o
.
rev
,
"
Check revision
"
);
doc
.
_id
+=
"
.
"
+
o
.
local_
rev
;
suffix
=
"
.
"
+
o
.
local_
rev
;
doc
.
_id
+=
"
.
"
+
o
.
rev
;
suffix
=
"
.
"
+
o
.
rev
;
}
deepEqual
(
localstorage
.
getItem
(
generateLocalPath
(
storage_description
)
+
...
...
@@ -2274,10 +2324,10 @@ module ("JIO Replicate Revision Storage");
o
.
spy
(
o
,
"
value
"
,
{
"
_id
"
:
o
.
uuid
,
"
title
"
:
"
post document without id
"
,
"
_rev
"
:
"
1-1
"
,
"
_revisions
"
:
{
"
start
"
:
1
,
"
ids
"
:
[
"
1
"
]},
"
_revs_info
"
:
[{
"
rev
"
:
"
1-1
"
,
"
status
"
:
"
available
"
}]
},
"
Get the
previous document (without revision)
"
);
"
_rev
"
:
o
.
rev
,
"
_revisions
"
:
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
rev_hash
]},
"
_revs_info
"
:
[{
"
rev
"
:
o
.
rev
,
"
status
"
:
"
available
"
}]
},
"
Get the
generated document, the winner
"
);
o
.
jio
.
get
({
"
_id
"
:
o
.
uuid
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
...
...
@@ -2287,8 +2337,12 @@ module ("JIO Replicate Revision Storage");
// 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
.
rev1_1_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
revisions
);
o
.
rev1_1
=
"
1-
"
+
o
.
rev1_1_hash
;
o
.
rev1_1_history
=
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
rev1_1_hash
]};
o
.
rev1_1_revs_info
=
[{
"
rev
"
:
o
.
rev1_1
,
"
status
"
:
"
available
"
}];
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev1_1
},
"
Post new document with an id
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
...
...
@@ -2297,15 +2351,11 @@ module ("JIO Replicate Revision Storage");
// 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
;
doc
.
_id
+=
"
.
"
+
o
.
rev1_1
;
suffix
=
"
.
"
+
o
.
rev1_1
;
}
deepEqual
(
localstorage
.
getItem
(
generateLocalPath
(
storage_description
)
+
...
...
@@ -2318,9 +2368,9 @@ module ("JIO Replicate Revision Storage");
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
"
}]
"
_rev
"
:
o
.
rev1_1
,
"
_revisions
"
:
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
rev1_1_hash
]},
"
_revs_info
"
:
[{
"
rev
"
:
o
.
rev1_1
,
"
status
"
:
"
available
"
}]
},
"
Get the previous document (without revision)
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
{
"
conflicts
"
:
true
,
...
...
@@ -2331,8 +2381,11 @@ module ("JIO Replicate Revision Storage");
// 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
},
o
.
rev1_2_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
revisions
);
o
.
rev1_2
=
"
1-
"
+
o
.
rev1_2_hash
;
o
.
rev1_2_history
=
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
rev1_2_hash
]};
o
.
rev1_2_revs_info
=
[{
"
rev
"
:
o
.
rev1_2
,
"
status
"
:
"
available
"
}];
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev1_2
},
"
Post same document (without revision)
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
...
...
@@ -2342,12 +2395,11 @@ module ("JIO Replicate Revision Storage");
// 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
;
doc
.
_id
+=
"
.
"
+
o
.
rev1_2
;
suffix
=
"
.
"
+
o
.
rev1_2
;
}
deepEqual
(
localstorage
.
getItem
(
generateLocalPath
(
storage_description
)
+
...
...
@@ -2357,9 +2409,17 @@ module ("JIO Replicate Revision Storage");
});
// 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
},
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
post new revision
"
,
"
_rev
"
:
o
.
rev1_2
};
o
.
revisions
.
start
+=
1
;
o
.
revisions
.
ids
.
unshift
(
o
.
rev1_2_hash
);
o
.
rev2_3_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
revisions
);
o
.
rev2_3
=
"
2-
"
+
o
.
rev2_3_hash
;
o
.
rev2_3_history
=
clone
(
o
.
rev1_2_history
);
o
.
rev2_3_history
.
start
+=
1
;
o
.
rev2_3_history
.
ids
.
unshift
(
o
.
rev2_3_hash
);
o
.
rev2_3_revs_info
=
clone
(
o
.
rev1_2_revs_info
);
o
.
rev2_3_revs_info
.
unshift
({
"
rev
"
:
o
.
rev2_3
,
"
status
"
:
"
available
"
});
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev2_3
},
"
Post document (with revision)
"
);
o
.
jio
.
post
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
...
...
@@ -2371,17 +2431,12 @@ module ("JIO Replicate Revision Storage");
// 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
;
doc
.
_id
+=
"
.
"
+
o
.
rev2_3
;
suffix
=
"
.
"
+
o
.
rev2_3
;
}
deepEqual
(
localstorage
.
getItem
(
generateLocalPath
(
storage_description
)
+
...
...
@@ -2394,31 +2449,15 @@ module ("JIO Replicate Revision Storage");
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
"
]
"
_rev
"
:
o
.
rev1_2
,
"
_revisions
"
:
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
rev1_2_hash
]},
"
_revs_info
"
:
[{
"
rev
"
:
o
.
rev1_2
,
"
status
"
:
"
available
"
}],
"
_conflicts
"
:
[
o
.
rev1_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
},
{
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev1_2
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
,
"
revs_info
"
:
true
},
o
.
f
);
o
.
tick
(
o
);
...
...
@@ -2429,8 +2468,11 @@ module ("JIO Replicate Revision Storage");
// 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
},
o
.
rev1_4_hash
=
generateRevisionHash
(
o
.
doc
,
{
"
start
"
:
0
,
"
ids
"
:
[]});
o
.
rev1_4
=
"
1-
"
+
o
.
rev1_4_hash
;
o
.
rev1_4_history
=
{
"
start
"
:
1
,
"
ids
"
:
[
o
.
rev1_4_hash
]};
o
.
rev1_4_revs_info
=
[{
"
rev
"
:
o
.
rev1_4
,
"
status
"
:
"
available
"
}];
o
.
spy
(
o
,
"
value
"
,
{
"
id
"
:
"
doc1
"
,
"
ok
"
:
true
,
"
rev
"
:
o
.
rev1_4
},
"
Put document without rev
"
)
o
.
jio
.
put
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
...
...
@@ -2442,10 +2484,14 @@ module ("JIO Replicate Revision Storage");
// 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
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
title
"
:
"
put new revision
"
,
"
_rev
"
:
o
.
rev1_4
};
o
.
rev2_5_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
rev1_4_history
);
o
.
rev2_5
=
"
2-
"
+
o
.
rev2_5_hash
o
.
rev2_5_history
=
{
"
start
"
:
2
,
"
ids
"
:
[
o
.
rev2_5_hash
,
o
.
rev1_4_hash
]};
o
.
rev2_5_revs_info
=
clone
(
o
.
rev1_4_revs_info
);
o
.
rev2_5_revs_info
.
unshift
({
"
rev
"
:
o
.
rev2_5
,
"
status
"
:
"
available
"
});
o
.
spy
(
o
,
"
value
"
,
{
"
id
"
:
"
doc1
"
,
"
ok
"
:
true
,
"
rev
"
:
o
.
rev2_5
},
"
Put new revision
"
)
o
.
jio
.
put
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
...
...
@@ -2456,39 +2502,362 @@ module ("JIO Replicate Revision Storage");
// 2-3 2-5
// putAttachment to inexistent document
o
.
doc
=
{
"
_id
"
:
"
doc2
"
,
"
_mimetype
"
:
"
text/plain
"
,
"
_data
"
:
"
doc 2 - attachment 1
"
,
"
_attachment
"
:
"
attachment1
"
};
o
.
rev_hash
=
generateRevisionHash
(
o
.
doc
,
{
"
start
"
:
0
,
"
ids
"
:
[]});
o
.
rev
=
"
1-
"
+
o
.
rev_hash
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc2/attachment1
"
,
"
rev
"
:
o
.
rev
},
"
Put an attachment to an inexistent document
"
);
o
.
doc
.
_id
+=
"
/
"
+
o
.
doc
.
_attachment
;
delete
o
.
doc
.
_attachment
;
o
.
jio
.
putAttachment
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// putAttachment
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_mimetype
"
:
"
text/plain
"
,
"
_data
"
:
"
doc 1 - attachment 1
"
,
"
_attachment
"
:
"
attachment1
"
,
"
_rev
"
:
o
.
rev2_5
};
o
.
rev3_6_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
rev2_5_history
);
o
.
rev3_6
=
"
3-
"
+
o
.
rev3_6_hash
;
o
.
rev3_6_history
=
clone
(
o
.
rev2_5_history
);
o
.
rev3_6_history
.
start
+=
1
;
o
.
rev3_6_history
.
ids
.
unshift
(
o
.
rev3_6_hash
);
o
.
rev3_6_revs_info
=
clone
(
o
.
rev2_5_revs_info
);
o
.
rev3_6_revs_info
.
unshift
({
"
rev
"
:
o
.
rev3_6
,
"
status
"
:
"
available
"
});
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1/attachment1
"
,
"
rev
"
:
o
.
rev3_6
},
"
Put an attachment to the first document
"
);
o
.
doc
.
_id
+=
"
/
"
+
o
.
doc
.
_attachment
;
delete
o
.
doc
.
_attachment
;
o
.
jio
.
putAttachment
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// __/__
// / | \
// 1-1 1-2 1-4
// | |
// 2-3 2-5
// |
// 3-6+a1
// get document
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev3_6
,
"
_revisions
"
:
o
.
rev3_6_history
,
"
_revs_info
"
:
o
.
rev3_6_revs_info
,
"
_conflicts
"
:
[
o
.
rev2_3
,
o
.
rev1_1
],
"
_attachments
"
:
{
"
attachment1
"
:
{
"
length
"
:
"
doc 1 - attachment 1
"
.
length
,
"
content_type
"
:
"
text/plain
"
,
"
digest
"
:
"
md5-0505c1fb6aae02dd1695d33841726564
"
}
},
"
title
"
:
"
put new revision
"
};
o
.
spy
(
o
,
"
value
"
,
o
.
doc
,
"
Get document, the winner
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
},
o
.
f
);
o
.
tick
(
o
);
// get attachment
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_attachment
"
:
"
attachment1
"
};
o
.
spy
(
o
,
"
value
"
,
"
doc 1 - attachment 1
"
,
"
Get the winner's attachment
"
);
o
.
doc
.
_id
+=
"
/
"
+
o
.
doc
.
_attachment
;
delete
o
.
doc
.
_attachment
;
o
.
jio
.
get
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// put document
// get document
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev3_6
,
"
title
"
:
"
Put revision, attachment must be copied
"
};
o
.
rev4_7_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
rev3_6_history
);
o
.
rev4_7
=
"
4-
"
+
o
.
rev4_7_hash
;
o
.
rev4_7_history
=
clone
(
o
.
rev3_6_history
);
o
.
rev4_7_history
.
start
+=
1
;
o
.
rev4_7_history
.
ids
.
unshift
(
o
.
rev4_7_hash
);
o
.
rev4_7_revs_info
=
clone
(
o
.
rev3_6_revs_info
);
o
.
rev4_7_revs_info
.
unshift
({
"
rev
"
:
o
.
rev4_7
,
"
status
"
:
"
available
"
});
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev4_7
},
"
Update document, attachment should be copied
"
);
o
.
jio
.
put
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// __/__
// / | \
// 1-1 1-2 1-4
// | |
// 2-3 2-5
// |
// 3-6+a1
// |
// 4-7+a1
// get document, attachment must be copied
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev4_7
,
"
title
"
:
o
.
doc
.
title
,
"
_attachments
"
:
{
"
attachment1
"
:
{
"
length
"
:
"
doc 1 - attachment 1
"
.
length
,
"
content_type
"
:
"
text/plain
"
,
"
digest
"
:
"
md5-0505c1fb6aae02dd1695d33841726564
"
}
},
"
_conflicts
"
:
[
o
.
rev2_3
,
o
.
rev1_1
],
"
_revisions
"
:
o
.
rev4_7_history
,
"
_revs_info
"
:
o
.
rev4_7_revs_info
};
o
.
spy
(
o
,
"
value
"
,
o
.
doc
,
"
Get the new winner document and its attachment metadata
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
},
o
.
f
);
o
.
tick
(
o
);
// get attachment
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_attachment
"
:
"
attachment1
"
};
o
.
spy
(
o
,
"
value
"
,
"
doc 1 - attachment 1
"
,
"
Get the winner's attachment again
"
);
o
.
doc
.
_id
+=
"
/
"
+
o
.
doc
.
_attachment
;
delete
o
.
doc
.
_attachment
;
o
.
jio
.
get
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// remove attachment
// get document
// get inexistent attachment
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_attachment
"
:
"
attachment1
"
,
"
_rev
"
:
o
.
rev4_7
};
o
.
rev5_8_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
rev4_7_history
);
o
.
rev5_8
=
"
5-
"
+
o
.
rev5_8_hash
;
o
.
rev5_8_history
=
clone
(
o
.
rev4_7_history
);
o
.
rev5_8_history
.
start
+=
1
;
o
.
rev5_8_history
.
ids
.
unshift
(
o
.
rev5_8_hash
);
o
.
rev5_8_revs_info
=
clone
(
o
.
rev4_7_revs_info
);
o
.
rev5_8_revs_info
.
unshift
({
"
rev
"
:
o
.
rev5_8
,
"
status
"
:
"
available
"
});
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1/attachment1
"
,
"
rev
"
:
o
.
rev5_8
},
"
Remove attachment
"
);
o
.
doc
.
_id
+=
"
/
"
+
o
.
doc
.
_attachment
;
delete
o
.
doc
.
_attachment
;
o
.
jio
.
remove
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// 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
);
// __/__
// / | \
// 1-1 1-2 1-4
// | |
// 2-3 2-5
// |
// 3-6+a1
// |
// 4-7+a1
// |
// 5-8
// get document to check attachment existence
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev5_8
,
"
title
"
:
"
Put revision, attachment must be copied
"
,
"
_conflicts
"
:
[
o
.
rev2_3
,
o
.
rev1_1
],
"
_revisions
"
:
o
.
rev5_8_history
,
"
_revs_info
"
:
o
.
rev5_8_revs_info
};
o
.
spy
(
o
,
"
value
"
,
o
.
doc
,
"
Get the new winner document, no attachment must be provided
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
},
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
);
// get specific document
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev4_7
,
"
title
"
:
o
.
doc
.
title
,
"
_attachments
"
:
{
"
attachment1
"
:
{
"
length
"
:
"
doc 1 - attachment 1
"
.
length
,
"
content_type
"
:
"
text/plain
"
,
"
digest
"
:
"
md5-0505c1fb6aae02dd1695d33841726564
"
}
},
"
_conflicts
"
:
[
o
.
rev2_3
,
o
.
rev1_1
],
"
_revisions
"
:
o
.
rev4_7_history
,
"
_revs_info
"
:
o
.
rev4_7_revs_info
};
o
.
spy
(
o
,
"
value
"
,
o
.
doc
,
"
Get the new winner document and its attachment metadata
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev4_7
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
},
o
.
f
);
o
.
tick
(
o
);
// get inexistent attachment
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent winner attachment
"
+
"
-> 404 Not Found
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1/attachment1
"
},
o
.
f
);
o
.
tick
(
o
);
// get specific attachment
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_attachment
"
:
"
attachment1
"
,
"
_rev
"
:
o
.
rev3_6
};
o
.
spy
(
o
,
"
value
"
,
"
doc 1 - attachment 1
"
,
"
Get a specific attachment
"
);
o
.
doc
.
_id
+=
"
/
"
+
o
.
doc
.
_attachment
;
delete
o
.
doc
.
_attachment
;
o
.
jio
.
get
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// remove specific document and conflict
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev1_1
};
// generate with deleted_flag
o
.
rev2_9_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
rev1_1_history
,
true
);
o
.
rev2_9
=
"
2-
"
+
o
.
rev2_9_hash
;
o
.
rev2_9_history
=
clone
(
o
.
rev1_1_history
);
o
.
rev2_9_history
.
start
+=
1
;
o
.
rev2_9_history
.
ids
.
unshift
(
o
.
rev2_9_hash
);
o
.
rev2_9_revs_info
=
clone
(
o
.
rev1_1_revs_info
);
o
.
rev2_9_revs_info
.
unshift
({
"
rev
"
:
o
.
rev2_9
,
"
status
"
:
"
available
"
});
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev2_9
},
"
Remove specific document, and one conflict
"
);
o
.
jio
.
remove
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// __/___
// / | \
// 1-1 1-2 1-4
// | | |
// D2-9 2-3 2-5
// |
// 3-6+a1
// |
// 4-7+a1
// |
// 5-8
// remove specific document and conflict
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev2_3
};
o
.
rev3_10_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
rev2_3_history
,
true
);
o
.
rev3_10
=
"
3-
"
+
o
.
rev3_10_hash
;
o
.
rev3_10_history
=
clone
(
o
.
rev2_3_history
);
o
.
rev3_10_history
.
start
+=
1
;
o
.
rev3_10_history
.
ids
.
unshift
(
o
.
rev3_10_hash
);
o
.
rev3_10_revs_info
=
clone
(
o
.
rev2_3_revs_info
);
o
.
rev3_10_revs_info
.
unshift
({
"
rev
"
:
o
.
rev3_10
,
"
status
"
:
"
available
"
});
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev3_10
},
"
Remove specific document, and one conflict
"
);
o
.
jio
.
remove
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// ___/____
// / | \
// 1-1 1-2 1-4
// | | |
// D2-9 2-3 2-5
// | |
// D3-10 3-6+a1
// |
// 4-7+a1
// |
// 5-8
// get document no more conflict
o
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev5_8
,
"
title
"
:
"
Put revision, attachment must be copied
"
,
"
_revisions
"
:
o
.
rev5_8_history
,
"
_revs_info
"
:
o
.
rev5_8_revs_info
};
o
.
spy
(
o
,
"
value
"
,
o
.
doc
,
"
Get the new winner document, no more conflicts
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
},
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
.
doc
=
{
"
_id
"
:
"
doc1
"
,
"
_rev
"
:
o
.
rev5_8
};
o
.
rev6_11_hash
=
generateRevisionHash
(
o
.
doc
,
o
.
rev5_8_history
,
true
);
o
.
rev6_11
=
"
6-
"
+
o
.
rev6_11_hash
;
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev6_11
},
"
Remove the last document
"
);
o
.
jio
.
remove
(
o
.
doc
,
o
.
f
);
o
.
tick
(
o
);
// ___/____
// / | \
// 1-1 1-2 1-4
// | | |
// D2-9 2-3 2-5
// | |
// D3-10 3-6+a1
// |
// 4-7+a1
// |
// 5-8
// |
// D6-11
// get inexistent document
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent document
"
);
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get inexistent document -> 404 Not Found
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc3
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revisions
"
:
true
},
o
.
f
);
o
.
tick
(
o
);
// get specific deleted document
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get deleted document -> 404 Not Found
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
,
"
rev
"
:
o
.
rev3_10
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
"
revs_info
"
:
true
},
o
.
f
);
o
.
tick
(
o
);
// get specific deleted document
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get deleted document -> 404 Not Found
"
);
o
.
jio
.
get
({
"
_id
"
:
"
doc1
"
},
{
"
conflicts
"
:
true
,
"
revs
"
:
true
,
...
...
@@ -2501,7 +2870,7 @@ module ("JIO Replicate Revision Storage");
};
test
(
"
[Revision + Local Storage] Scenario
"
,
function
()
{
testReplicateRevisionStorage
Generator
(
this
,
{
testReplicateRevisionStorage
(
this
,
{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
revision
"
,
...
...
@@ -2514,7 +2883,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 +2899,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 +2958,269 @@ module ("JIO Replicate Revision Storage");
}]
});
});
var
replicateStorageSynchronisationGenerator
=
function
(
that
,
description
,
index
)
{
var
o
=
generateTools
(
that
);
o
.
jio
=
JIO
.
newJio
(
description
);
o
.
localpath1
=
"
jio/localstorage/usyncreprevlocloc1/
"
+
index
;
o
.
localpath2
=
"
jio/localstorage/usyncreprevlocloc2/
"
+
index
;
o
.
localpath3
=
"
jio/localstorage/usyncreprevlocloc3/
"
+
index
;
o
.
localpath4
=
"
jio/localstorage/usyncreprevlocloc4/
"
+
index
;
// 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
.
localpath3
+
"
/doc1.revision_tree.json
"
,
o
.
doctree1_1
);
localstorage
.
setItem
(
o
.
localpath4
+
"
/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
);
localstorage
.
setItem
(
o
.
localpath3
+
"
/
"
+
o
.
doc1_1
.
_id
,
o
.
doc1_1
);
localstorage
.
setItem
(
o
.
localpath4
+
"
/
"
+
o
.
doc1_1
.
_id
,
o
.
doc1_1
);
// no synchronisation
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
},
"
Check document
"
);
o
.
jio
.
check
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
},
"
Repair document
"
);
o
.
jio
.
repair
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
);
// check documents from localstorage
deepEqual
(
localstorage
.
getItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
),
o
.
doctree1_1
,
"
Check revision tree 1, no synchro done
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath2
+
"
/doc1.revision_tree.json
"
),
o
.
doctree1_1
,
"
Check revision tree 2, no synchro done
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath3
+
"
/doc1.revision_tree.json
"
),
o
.
doctree1_1
,
"
Check revision tree 3, no synchro done
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath4
+
"
/doc1.revision_tree.json
"
),
o
.
doctree1_1
,
"
Check revision tree 4, no synchro done
"
);
// 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
"
,
"
_attachments
"
:
{
"
haha
"
:
{
"
length
"
:
3
,
"
digest
"
:
"
md5-900150983cd24fb0d6963f7d28e17f72
"
,
"
content_type
"
:
"
text/plain
"
}
}
};
localstorage
.
setItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
,
o
.
doctree2_2
);
localstorage
.
setItem
(
o
.
localpath1
+
"
/
"
+
o
.
doc2_2
.
_id
,
o
.
doc2_2
)
localstorage
.
setItem
(
o
.
localpath1
+
"
/
"
+
o
.
doc2_2
.
_id
+
"
/haha
"
,
"
abc
"
);
// document synchronisation without conflict
o
.
spy
(
o
,
"
status
"
,
41
,
"
Check document
"
);
o
.
jio
.
check
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
,
50000
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
},
"
Repair document
"
);
o
.
jio
.
repair
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
,
50000
);
// check documents from localstorage
deepEqual
(
localstorage
.
getItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
),
o
.
doctree2_2
,
"
Check revision tree 1, no synchro done
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath2
+
"
/doc1.revision_tree.json
"
),
o
.
doctree2_2
,
"
Check revision tree 2, revision synchro done
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath3
+
"
/doc1.revision_tree.json
"
),
o
.
doctree2_2
,
"
Check revision tree 3, revision synchro done
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath3
+
"
/doc1.2-222
"
),
o
.
doc2_2
,
"
Check document 3
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath3
+
"
/doc1.2-222/haha
"
),
"
abc
"
,
"
Check attachment 3
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath4
+
"
/doc1.revision_tree.json
"
),
o
.
doctree2_2
,
"
Check revision tree 4, revision synchro done
"
);
// add documents to localstorage
o
.
doctree2_3
=
clone
(
o
.
doctree2_2
);
o
.
doctree2_3
.
children
[
0
].
children
.
unshift
({
"
rev
"
:
"
2-223
"
,
"
status
"
:
"
available
"
,
"
children
"
:
[]
});
o
.
doc2_3
=
{
"
_id
"
:
"
doc1.2-223
"
,
"
title
"
:
"
C
"
};
localstorage
.
setItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
,
o
.
doctree2_3
);
localstorage
.
setItem
(
o
.
localpath1
+
"
/
"
+
o
.
doc2_3
.
_id
,
o
.
doc2_3
);
// document synchronisation with conflict
o
.
spy
(
o
,
"
status
"
,
41
,
"
Check document
"
);
o
.
jio
.
check
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
,
50000
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
doc1
"
},
"
Repair document
"
);
o
.
jio
.
repair
({
"
_id
"
:
"
doc1
"
},
o
.
f
);
o
.
tick
(
o
,
50000
);
// check documents from localstorage
deepEqual
(
localstorage
.
getItem
(
o
.
localpath1
+
"
/doc1.revision_tree.json
"
),
o
.
doctree2_3
,
"
Check revision tree 1, rev synchro
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath2
+
"
/doc1.revision_tree.json
"
),
o
.
doctree2_3
,
"
Check revision tree 2, rev synchro
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath3
+
"
/doc1.revision_tree.json
"
),
o
.
doctree2_3
,
"
Check revision tree 3, rev synchro
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath3
+
"
/doc1.2-223
"
),
o
.
doc2_3
,
"
Check document 3
"
);
deepEqual
(
localstorage
.
getItem
(
o
.
localpath4
+
"
/doc1.revision_tree.json
"
),
o
.
doctree2_3
,
"
Check revision tree 4, rev synchro
"
);
o
.
jio
.
stop
();
};
test
(
"
Storage Synchronisation (Repair) 4x [Rev + Local]
"
,
function
()
{
replicateStorageSynchronisationGenerator
(
this
,
{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc1
"
,
"
application_name
"
:
"
1
"
}
},
{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc2
"
,
"
application_name
"
:
"
1
"
}
},
{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc3
"
,
"
application_name
"
:
"
1
"
}
},
{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc4
"
,
"
application_name
"
:
"
1
"
}
}]
},
"
1
"
);
});
test
(
"
Storage Synchronisation (Repair) 2x [Rep 2x [Rev + Local]]
"
,
function
()
{
replicateStorageSynchronisationGenerator
(
this
,
{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc1
"
,
"
application_name
"
:
"
2
"
}
},
{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc2
"
,
"
application_name
"
:
"
2
"
}
}]
},
{
"
type
"
:
"
replicaterevision
"
,
"
storage_list
"
:
[{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc3
"
,
"
application_name
"
:
"
2
"
}
},
{
"
type
"
:
"
revision
"
,
"
sub_storage
"
:
{
"
type
"
:
"
local
"
,
"
username
"
:
"
usyncreprevlocloc4
"
,
"
application_name
"
:
"
2
"
}
}]
}]
},
"
2
"
);
});
/*
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