Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
E
ecommerce-ui
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
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
ecommerce-ui
Commits
5c0cec38
Commit
5c0cec38
authored
Feb 10, 2014
by
Sven Franck
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added replicate and replicate revision storage
parent
72afe96c
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1766 additions
and
0 deletions
+1766
-0
js/libs/jio/replicaterevisionstorage.js
js/libs/jio/replicaterevisionstorage.js
+702
-0
js/libs/jio/revisionstorage.js
js/libs/jio/revisionstorage.js
+1064
-0
No files found.
js/libs/jio/replicaterevisionstorage.js
0 → 100644
View file @
5c0cec38
/*jslint indent: 2, maxlen: 80, nomen: true */
/*global jIO, define */
/**
* JIO Replicate Revision Storage.
* It manages storages that manage revisions and conflicts.
* Description:
* {
* "type": "replicaterevision",
* "storage_list": [
* <sub storage description>,
* ...
* ]
* }
*/
// define([module_name], [dependencies], module);
(
function
(
dependencies
,
module
)
{
"
use strict
"
;
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
return
define
(
dependencies
,
module
);
}
module
(
jIO
);
}([
'
jio
'
],
function
(
jIO
)
{
"
use strict
"
;
jIO
.
addStorage
(
'
replicaterevision
'
,
function
(
spec
)
{
var
that
=
this
,
priv
=
{};
spec
=
spec
||
{};
priv
.
storage_list_key
=
"
storage_list
"
;
priv
.
storage_list
=
spec
[
priv
.
storage_list_key
];
priv
.
emptyFunction
=
function
()
{
return
;
};
/**
* Generate a new uuid
*
* @return {string} The new uuid
*/
priv
.
generateUuid
=
function
()
{
var
S4
=
function
()
{
var
i
,
string
=
Math
.
floor
(
Math
.
random
()
*
0x10000
/* 65536 */
).
toString
(
16
);
for
(
i
=
string
.
length
;
i
<
4
;
i
+=
1
)
{
string
=
"
0
"
+
string
;
}
return
string
;
};
return
S4
()
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
S4
()
+
S4
();
};
/**
* Create an array containing dictionnary keys
*
* @param {object} dict The object to convert
* @return {array} The array of keys
*/
priv
.
dictKeys2Array
=
function
(
dict
)
{
var
k
,
newlist
=
[];
for
(
k
in
dict
)
{
if
(
dict
.
hasOwnProperty
(
k
))
{
newlist
.
push
(
k
);
}
}
return
newlist
;
};
/**
* Checks a revision format
*
* @param {string} revision The revision string
* @return {boolean} True if ok, else false
*/
priv
.
checkRevisionFormat
=
function
(
revision
)
{
return
(
/^
[
0-9
]
+-
[
0-9a-zA-Z_
]
+$/
.
test
(
revision
));
};
/**
* Clones an object in deep (without functions)
*
* @param {any} object The object to clone
* @return {any} The cloned object
*/
priv
.
clone
=
function
(
object
)
{
var
tmp
=
JSON
.
stringify
(
object
);
if
(
tmp
===
undefined
)
{
return
undefined
;
}
return
JSON
.
parse
(
tmp
);
};
/**
* Like addJob but also return the method and the index of the storage
*
* @param {string} method The request method
* @param {number} index The storage index
* @param {object} doc The document object
* @param {object} option The request object
* @param {function} callback The callback. Parameters:
* - {string} The request method
* - {number} The storage index
* - {object} The error object
* - {object} The response object
*/
priv
.
send
=
function
(
command
,
method
,
index
,
doc
,
option
,
callback
)
{
var
wrapped_callback_success
,
wrapped_callback_error
;
callback
=
callback
||
priv
.
emptyFunction
;
wrapped_callback_success
=
function
(
response
)
{
callback
(
method
,
index
,
undefined
,
response
);
};
wrapped_callback_error
=
function
(
err
)
{
callback
(
method
,
index
,
err
,
undefined
);
};
if
(
method
===
'
allDocs
'
)
{
command
.
storage
(
priv
.
storage_list
[
index
]).
allDocs
(
option
).
then
(
wrapped_callback_success
,
wrapped_callback_error
);
}
else
{
command
.
storage
(
priv
.
storage_list
[
index
])[
method
](
doc
,
option
).
then
(
wrapped_callback_success
,
wrapped_callback_error
);
}
};
/**
* Use "send" method to all sub storages.
* Calling "callback" for each storage response.
*
* @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
* - {number} The storage index
* - {object} The error object
* - {object} The response object
*/
priv
.
sendToAll
=
function
(
command
,
method
,
doc
,
option
,
callback
)
{
var
i
;
for
(
i
=
0
;
i
<
priv
.
storage_list
.
length
;
i
+=
1
)
{
priv
.
send
(
command
,
method
,
i
,
doc
,
option
,
callback
);
}
};
/**
* Use "send" method to all sub storages.
* Calling "callback" only with the first response
*
* @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
*/
priv
.
sendToAllFastestResponseOnly
=
function
(
command
,
method
,
doc
,
option
,
callback
)
{
var
i
,
callbackWrapper
,
error_count
;
error_count
=
0
;
callbackWrapper
=
function
(
method
,
index
,
err
,
response
)
{
/*jslint unparam: true */
if
(
err
)
{
error_count
+=
1
;
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
(
command
,
method
,
i
,
doc
,
option
,
callbackWrapper
);
}
};
/**
* Use "sendToAll" method, calling "callback" at the last response with
* the response list
*
* @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
(
command
,
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
)
{
/*jslint unparam: true */
error_list
[
index
]
=
err
;
response_list
[
index
]
=
response
;
callback_count
+=
1
;
if
(
callback_count
===
priv
.
storage_list
.
length
)
{
callback
(
error_list
,
response_list
);
}
};
priv
.
sendToAll
(
command
,
method
,
doc
,
option
,
wrapper
);
};
/**
* Checks if the sub storage are identical
* @method check
* @param {object} command The JIO command
*/
that
.
check
=
function
(
command
,
param
,
option
)
{
function
callback
(
err
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
();
}
if
(
!
param
.
_id
)
{
return
callback
({
"
status
"
:
501
});
}
priv
.
check
(
command
,
param
,
option
,
callback
);
};
/**
* Repair the sub storages to make them identical
* @method repair
* @param {object} command The JIO command
*/
that
.
repair
=
function
(
command
,
param
,
option
)
{
function
callback
(
err
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
();
}
if
(
!
param
.
_id
)
{
return
callback
({
"
status
"
:
501
});
}
priv
.
repair
(
command
,
param
,
option
,
true
,
callback
);
};
priv
.
check
=
function
(
command
,
doc
,
option
,
success
,
error
)
{
priv
.
repair
(
command
,
doc
,
option
,
false
,
success
,
error
);
};
priv
.
repair
=
function
(
command
,
doc
,
option
,
repair
,
callback
)
{
var
functions
=
{};
callback
=
callback
||
priv
.
emptyFunction
;
option
=
option
||
{};
functions
.
begin
=
function
()
{
// // XXX make revision storage check and repair
// // to enable check/repair sub storage from this storage
// // by calling this function just below
// //functions.repairAllSubStorages();
// // else we assume that sub storages are good
// functions.getAllDocuments(functions.newParam(
// doc,
// option,
// repair
// ));
// };
// functions.repairAllSubStorages = function () {
var
i
;
for
(
i
=
0
;
i
<
priv
.
storage_list
.
length
;
i
+=
1
)
{
priv
.
send
(
command
,
repair
?
"
repair
"
:
"
check
"
,
i
,
doc
,
option
,
functions
.
repairAllSubStoragesCallback
);
}
};
functions
.
repair_sub_storages_count
=
0
;
functions
.
repairAllSubStoragesCallback
=
function
(
method
,
index
,
err
,
response
)
{
/*jslint unparam: true */
if
(
err
)
{
return
command
.
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, _content_type: ..}
// attachmentB : {_id: attachmentB, _revs_info, _content_type: ..}
}
},
"
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
,
metadata
,
cloned_option
;
metadata
=
priv
.
clone
(
param
.
doc
);
cloned_option
=
priv
.
clone
(
param
.
option
);
cloned_option
.
conflicts
=
true
;
cloned_option
.
revs
=
true
;
cloned_option
.
revs_info
=
true
;
for
(
i
=
0
;
i
<
priv
.
storage_list
.
length
;
i
+=
1
)
{
// if the document is not loaded
priv
.
send
(
command
,
"
get
"
,
i
,
metadata
,
cloned_option
,
functions
.
dealResults
(
param
));
}
functions
.
finished_count
+=
1
;
};
functions
.
dealResults
=
function
(
param
)
{
return
function
(
method
,
index
,
err
,
response
)
{
/*jslint unparam: true */
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
"
:
409
,
"
message
"
:
"
An error occured on the sub storage
"
,
"
reason
"
:
err
.
reason
},
undefined
);
return
;
}
}
response
=
response
.
data
;
// 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
);
//console.log(JSON.parse(JSON.stringify(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
"
:
409
,
"
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
(
command
,
"
getAttachment
"
,
param
.
responses
.
stats
[
response
][
0
],
{
"
_id
"
:
param
.
doc
.
_id
,
"
_attachment
"
:
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
)
{
return
function
(
method
,
index
,
err
,
response
)
{
/*jslint unparam: true */
if
(
err
)
{
callback
({
"
status
"
:
409
,
"
message
"
:
"
Unable to retreive attachments
"
,
"
reason
"
:
err
.
reason
},
undefined
);
return
;
}
response
=
response
.
data
;
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
]
);
}
}
}
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
;
}
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
,
"
_content_type
"
:
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
(
command
,
"
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
)
{
/*jslint unparam: true */
var
i
,
attachment
;
if
(
err
)
{
return
callback
({
"
status
"
:
409
,
"
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
,
"
_content_type
"
:
attachment_to_put
[
i
].
_content_type
,
"
_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
]
};
priv
.
send
(
command
,
"
putAttachment
"
,
index
,
attachment
,
option
,
functions
.
putAttachmentCallback
(
param
)
);
}
if
(
attachment_to_put
.
length
===
0
)
{
functions
.
finished
();
}
};
};
functions
.
putAttachmentCallback
=
function
(
param
)
{
/*jslint unparam: true */
return
function
(
method
,
index
,
err
,
response
)
{
if
(
err
)
{
return
callback
(
err
,
undefined
);
}
functions
.
finished
();
};
};
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
,
param
,
option
)
{
var
doc
=
param
;
doc
.
_id
=
doc
.
_id
||
priv
.
generateUuid
();
priv
.
sendToAllFastestResponseOnly
(
command
,
method
,
doc
,
option
,
function
(
method
,
err
,
response
)
{
/*jslint unparam: true */
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
(
response
);
}
);
};
/**
* Post the document metadata to all sub storages
* @method post
* @param {object} command The JIO command
*/
that
.
post
=
function
(
command
,
metadata
,
option
)
{
that
.
genericRequest
(
command
,
"
post
"
,
metadata
,
option
);
};
/**
* Put the document metadata to all sub storages
* @method put
* @param {object} command The JIO command
*/
that
.
put
=
function
(
command
,
metadata
,
option
)
{
that
.
genericRequest
(
command
,
"
put
"
,
metadata
,
option
);
};
/**
* Put an attachment to a document to all sub storages
* @method putAttachment
* @param {object} command The JIO command
*/
that
.
putAttachment
=
function
(
command
,
param
,
option
)
{
that
.
genericRequest
(
command
,
"
putAttachment
"
,
param
,
option
);
};
/**
* Get the document from all sub storages, get the fastest.
* @method get
* @param {object} command The JIO command
*/
that
.
get
=
function
(
command
,
param
,
option
)
{
that
.
genericRequest
(
command
,
"
get
"
,
param
,
option
);
};
/**
* Get the attachment from all sub storages, get the fastest.
* @method getAttachment
* @param {object} command The JIO command
*/
that
.
getAttachment
=
function
(
command
,
param
,
option
)
{
that
.
genericRequest
(
command
,
"
getAttachment
"
,
param
,
option
);
};
/**
* Remove the document from all sub storages.
* @method remove
* @param {object} command The JIO command
*/
that
.
remove
=
function
(
command
,
param
,
option
)
{
that
.
genericRequest
(
command
,
"
remove
"
,
param
,
option
);
};
/**
* Remove the attachment from all sub storages.
* @method remove
* @param {object} command The JIO command
*/
that
.
removeAttachment
=
function
(
command
,
param
,
option
)
{
that
.
genericRequest
(
command
,
"
removeAttachment
"
,
param
,
option
);
};
});
}));
js/libs/jio/revisionstorage.js
0 → 100644
View file @
5c0cec38
/*jslint indent: 2, maxlen: 80, nomen: true */
/*global jIO, hex_sha256, define */
/**
* JIO Revision Storage.
* It manages document version and can generate conflicts.
* Description:
* {
* "type": "revision",
* "sub_storage": <sub storage description>
* }
*/
// define([module_name], [dependencies], module);
(
function
(
dependencies
,
module
)
{
"
use strict
"
;
if
(
typeof
define
===
'
function
'
&&
define
.
amd
)
{
return
define
(
dependencies
,
module
);
}
module
(
jIO
,
{
hex_sha256
:
hex_sha256
});
}([
'
jio
'
,
'
sha256
'
],
function
(
jIO
,
sha256
)
{
"
use strict
"
;
var
tool
=
{
"
readBlobAsBinaryString
"
:
jIO
.
util
.
readBlobAsBinaryString
,
"
uniqueJSONStringify
"
:
jIO
.
util
.
uniqueJSONStringify
};
jIO
.
addStorage
(
"
revision
"
,
function
(
spec
)
{
var
that
=
this
,
priv
=
{};
spec
=
spec
||
{};
// ATTRIBUTES //
priv
.
doc_tree_suffix
=
"
.revision_tree.json
"
;
priv
.
sub_storage
=
spec
.
sub_storage
;
// METHODS //
/**
* Clones an object in deep (without functions)
* @method clone
* @param {any} object The object to clone
* @return {any} The cloned object
*/
priv
.
clone
=
function
(
object
)
{
var
tmp
=
JSON
.
stringify
(
object
);
if
(
tmp
===
undefined
)
{
return
undefined
;
}
return
JSON
.
parse
(
tmp
);
};
/**
* Generate a new uuid
* @method generateUuid
* @return {string} The new uuid
*/
priv
.
generateUuid
=
function
()
{
var
S4
=
function
()
{
/* 65536 */
var
i
,
string
=
Math
.
floor
(
Math
.
random
()
*
0x10000
).
toString
(
16
);
for
(
i
=
string
.
length
;
i
<
4
;
i
+=
1
)
{
string
=
'
0
'
+
string
;
}
return
string
;
};
return
S4
()
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
"
-
"
+
S4
()
+
S4
()
+
S4
();
};
/**
* Generates a hash code of a string
* @method hashCode
* @param {string} string The string to hash
* @return {string} The string hash code
*/
priv
.
hashCode
=
function
(
string
)
{
return
sha256
.
hex_sha256
(
string
);
};
/**
* Checks a revision format
* @method checkDocumentRevisionFormat
* @param {object} doc The document object
* @return {object} null if ok, else error object
*/
priv
.
checkDocumentRevisionFormat
=
function
(
doc
)
{
var
send_error
=
function
(
message
)
{
return
{
"
status
"
:
409
,
"
message
"
:
message
,
"
reason
"
:
"
Wrong revision
"
};
};
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
"
);
}
}
};
/**
* Creates a new document tree
* @method newDocTree
* @return {object} The new document tree
*/
priv
.
newDocTree
=
function
()
{
return
{
"
children
"
:
[]};
};
/**
* 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
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
]);
}
return
revisions
;
};
/**
* Convert the revision history object to an array of revisions.
* @method revisionHistoryToList
* @param {object} revs The revision history
* @return {array} The revision array
*/
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
;
};
/**
* 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
.
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
;
};
/**
* Update a document metadata revision properties
* @method fillDocumentRevisionProperties
* @param {object} doc The document object
* @param {object} doc_tree The document tree
*/
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
;
}
};
/**
* Generates the next revision of a document.
* @methode 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
.
generateNextRevision
=
function
(
doc
,
deleted_flag
)
{
var
string
,
revision_history
,
revs_info
;
doc
=
priv
.
clone
(
doc
)
||
{};
revision_history
=
doc
.
_revs
;
revs_info
=
doc
.
_revs_info
;
delete
doc
.
_rev
;
delete
doc
.
_revs
;
delete
doc
.
_revs_info
;
string
=
tool
.
uniqueJSONStringify
(
doc
)
+
tool
.
uniqueJSONStringify
(
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
"
:
doc
.
_rev
,
"
status
"
:
deleted_flag
?
"
deleted
"
:
"
available
"
});
doc
.
_revs_info
=
revs_info
;
return
doc
;
};
/**
* 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
.
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
;
}
}
return
[];
};
return
getRevisionInfoRec
(
doc_tree
);
};
priv
.
updateDocumentTree
=
function
(
doc
,
doc_tree
)
{
var
revs_info
,
updateDocumentTreeRec
;
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
;
}
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
);
}
}
doc_tree
.
children
.
unshift
({
"
rev
"
:
info
.
rev
,
"
status
"
:
info
.
status
,
"
children
"
:
[]
});
updateDocumentTreeRec
(
doc_tree
.
children
[
0
],
revs_info
);
};
updateDocumentTreeRec
(
doc_tree
,
priv
.
clone
(
revs_info
));
};
priv
.
send
=
function
(
command
,
method
,
doc
,
option
,
callback
)
{
var
storage
=
command
.
storage
(
priv
.
sub_storage
);
function
onSuccess
(
success
)
{
callback
(
undefined
,
success
);
}
function
onError
(
err
)
{
callback
(
err
,
undefined
);
}
if
(
method
===
'
allDocs
'
)
{
storage
.
allDocs
(
option
).
then
(
onSuccess
,
onError
);
}
else
{
storage
[
method
](
doc
,
option
).
then
(
onSuccess
,
onError
);
}
};
priv
.
getWinnerRevsInfo
=
function
(
doc_tree
)
{
var
revs_info
=
[],
getWinnerRevsInfoRec
;
getWinnerRevsInfoRec
=
function
(
doc_tree
,
tmp_revs_info
)
{
var
i
;
if
(
doc_tree
.
rev
)
{
tmp_revs_info
.
unshift
({
"
rev
"
:
doc_tree
.
rev
,
"
status
"
:
doc_tree
.
status
});
}
if
(
doc_tree
.
children
.
length
===
0
)
{
if
(
revs_info
.
length
===
0
||
(
revs_info
[
0
].
status
!==
"
available
"
&&
tmp_revs_info
[
0
].
status
===
"
available
"
)
||
(
tmp_revs_info
[
0
].
status
===
"
available
"
&&
revs_info
.
length
<
tmp_revs_info
.
length
))
{
revs_info
=
priv
.
clone
(
tmp_revs_info
);
}
}
for
(
i
=
0
;
i
<
doc_tree
.
children
.
length
;
i
+=
1
)
{
getWinnerRevsInfoRec
(
doc_tree
.
children
[
i
],
tmp_revs_info
);
}
tmp_revs_info
.
shift
();
};
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
;
}
if
(
doc_tree
.
children
.
length
===
0
)
{
if
(
doc_tree
.
status
!==
"
deleted
"
)
{
conflicts
.
push
(
doc_tree
.
rev
);
}
}
for
(
i
=
0
;
i
<
doc_tree
.
children
.
length
;
i
+=
1
)
{
getConflictsRec
(
doc_tree
.
children
[
i
]);
}
};
getConflictsRec
(
doc_tree
);
return
conflicts
.
length
===
0
?
undefined
:
conflicts
;
};
priv
.
get
=
function
(
command
,
doc
,
option
,
callback
)
{
priv
.
send
(
command
,
"
get
"
,
doc
,
option
,
callback
);
};
priv
.
put
=
function
(
command
,
doc
,
option
,
callback
)
{
priv
.
send
(
command
,
"
put
"
,
doc
,
option
,
callback
);
};
priv
.
remove
=
function
(
command
,
doc
,
option
,
callback
)
{
priv
.
send
(
command
,
"
remove
"
,
doc
,
option
,
callback
);
};
priv
.
getAttachment
=
function
(
command
,
attachment
,
option
,
callback
)
{
priv
.
send
(
command
,
"
getAttachment
"
,
attachment
,
option
,
callback
);
};
priv
.
putAttachment
=
function
(
command
,
attachment
,
option
,
callback
)
{
priv
.
send
(
command
,
"
putAttachment
"
,
attachment
,
option
,
callback
);
};
priv
.
removeAttachment
=
function
(
command
,
attachment
,
option
,
callback
)
{
priv
.
send
(
command
,
"
removeAttachment
"
,
attachment
,
option
,
callback
);
};
priv
.
getDocument
=
function
(
command
,
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
(
command
,
doc
,
option
,
callback
);
};
priv
.
putDocument
=
function
(
command
,
doc
,
option
,
callback
)
{
doc
=
priv
.
clone
(
doc
);
doc
.
_id
=
doc
.
_id
+
"
.
"
+
doc
.
_rev
;
delete
doc
.
_attachment
;
delete
doc
.
_data
;
delete
doc
.
_mimetype
;
delete
doc
.
_content_type
;
delete
doc
.
_rev
;
delete
doc
.
_revs
;
delete
doc
.
_revs_info
;
priv
.
put
(
command
,
doc
,
option
,
callback
);
};
priv
.
getRevisionTree
=
function
(
command
,
doc
,
option
,
callback
)
{
doc
=
priv
.
clone
(
doc
);
doc
.
_id
=
doc
.
_id
+
priv
.
doc_tree_suffix
;
priv
.
get
(
command
,
doc
,
option
,
function
(
err
,
response
)
{
if
(
err
)
{
return
callback
(
err
,
response
);
}
if
(
response
.
data
&&
response
.
data
.
children
)
{
response
.
data
.
children
=
JSON
.
parse
(
response
.
data
.
children
);
}
return
callback
(
err
,
response
);
});
};
priv
.
getAttachmentList
=
function
(
command
,
doc
,
option
,
callback
)
{
var
attachment_id
,
dealResults
,
state
=
"
ok
"
,
result_list
=
[],
count
=
0
;
dealResults
=
function
(
attachment_id
,
attachment_meta
)
{
return
function
(
err
,
response
)
{
if
(
state
!==
"
ok
"
)
{
return
;
}
count
-=
1
;
if
(
err
)
{
if
(
err
.
status
===
404
)
{
result_list
.
push
(
undefined
);
}
else
{
state
=
"
error
"
;
return
callback
(
err
,
undefined
);
}
}
result_list
.
push
({
"
_attachment
"
:
attachment_id
,
"
_data
"
:
response
.
data
,
"
_content_type
"
:
attachment_meta
.
content_type
});
if
(
count
===
0
)
{
state
=
"
finished
"
;
callback
(
undefined
,
{
"
data
"
:
result_list
});
}
};
};
for
(
attachment_id
in
doc
.
_attachments
)
{
if
(
doc
.
_attachments
.
hasOwnProperty
(
attachment_id
))
{
count
+=
1
;
priv
.
getAttachment
(
command
,
{
"
_id
"
:
doc
.
_id
,
"
_attachment
"
:
attachment_id
},
option
,
dealResults
(
attachment_id
,
doc
.
_attachments
[
attachment_id
])
);
}
}
if
(
count
===
0
)
{
callback
(
undefined
,
{
"
data
"
:
[]});
}
};
priv
.
putAttachmentList
=
function
(
command
,
doc
,
option
,
attachment_list
,
callback
)
{
var
i
,
dealResults
,
state
=
"
ok
"
,
count
=
0
,
attachment
;
attachment_list
=
attachment_list
||
[];
dealResults
=
function
()
{
return
function
(
err
)
{
if
(
state
!==
"
ok
"
)
{
return
;
}
count
-=
1
;
if
(
err
)
{
state
=
"
error
"
;
return
callback
(
err
,
undefined
);
}
if
(
count
===
0
)
{
state
=
"
finished
"
;
callback
(
undefined
,
{});
}
};
};
for
(
i
=
0
;
i
<
attachment_list
.
length
;
i
+=
1
)
{
attachment
=
attachment_list
[
i
];
if
(
attachment
!==
undefined
)
{
count
+=
1
;
attachment
.
_id
=
doc
.
_id
+
"
.
"
+
doc
.
_rev
;
priv
.
putAttachment
(
command
,
attachment
,
option
,
dealResults
(
i
));
}
}
if
(
count
===
0
)
{
return
callback
(
undefined
,
{});
}
};
priv
.
putDocumentTree
=
function
(
command
,
doc
,
option
,
doc_tree
,
callback
)
{
doc_tree
=
priv
.
clone
(
doc_tree
);
doc_tree
.
_id
=
doc
.
_id
+
priv
.
doc_tree_suffix
;
if
(
doc_tree
.
children
)
{
doc_tree
.
children
=
JSON
.
stringify
(
doc_tree
.
children
);
}
priv
.
put
(
command
,
doc_tree
,
option
,
callback
);
};
priv
.
notFoundError
=
function
(
message
,
reason
)
{
return
{
"
status
"
:
404
,
"
message
"
:
message
,
"
reason
"
:
reason
};
};
priv
.
conflictError
=
function
(
message
,
reason
)
{
return
{
"
status
"
:
409
,
"
message
"
:
message
,
"
reason
"
:
reason
};
};
priv
.
revisionGenericRequest
=
function
(
command
,
doc
,
option
,
specific_parameter
,
onEnd
)
{
var
prev_doc
,
doc_tree
,
attachment_list
,
callback
=
{};
if
(
specific_parameter
.
doc_id
)
{
doc
.
_id
=
specific_parameter
.
doc_id
;
}
if
(
specific_parameter
.
attachment_id
)
{
doc
.
_attachment
=
specific_parameter
.
attachment_id
;
}
callback
.
begin
=
function
()
{
var
check_error
;
doc
.
_id
=
doc
.
_id
||
priv
.
generateUuid
();
// XXX should not generate id
if
(
specific_parameter
.
revision_needed
&&
!
doc
.
_rev
)
{
return
onEnd
(
priv
.
conflictError
(
"
Document update conflict
"
,
"
No document revision was provided
"
),
undefined
);
}
// check revision format
check_error
=
priv
.
checkDocumentRevisionFormat
(
doc
);
if
(
check_error
!==
undefined
)
{
return
onEnd
(
check_error
,
undefined
);
}
priv
.
getRevisionTree
(
command
,
doc
,
option
,
callback
.
getRevisionTree
);
};
callback
.
getRevisionTree
=
function
(
err
,
response
)
{
var
winner_info
,
previous_revision
,
generate_new_revision
;
previous_revision
=
doc
.
_rev
;
generate_new_revision
=
doc
.
_revs
||
doc
.
_revs_info
?
false
:
true
;
if
(
err
)
{
if
(
err
.
status
!==
404
)
{
err
.
message
=
"
Cannot get document revision tree
"
;
return
onEnd
(
err
,
undefined
);
}
}
doc_tree
=
(
response
&&
response
.
data
)
||
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
);
}
doc
.
_rev
=
winner_info
[
0
].
rev
;
}
priv
.
fillDocumentRevisionProperties
(
doc
,
doc_tree
);
return
priv
.
getDocument
(
command
,
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
};
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
(
command
,
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
(
command
,
doc
,
option
,
callback
.
putDocument
);
};
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
=
{
"
data
"
:
{}};
}
else
{
err
.
message
=
"
Cannot get document
"
;
return
onEnd
(
err
,
undefined
);
}
}
res_doc
=
res_doc
.
data
;
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
;
}
}
if
(
option
.
revs
===
true
)
{
res_doc
.
_revisions
=
doc
.
_revs
;
}
if
(
option
.
revs_info
===
true
)
{
res_doc
.
_revs_info
=
doc
.
_revs_info
;
}
return
onEnd
(
undefined
,
{
"
data
"
:
res_doc
});
}
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
];
}
}
}
if
(
specific_parameter
.
remove
)
{
priv
.
putDocumentTree
(
command
,
doc
,
option
,
doc_tree
,
callback
.
putDocumentTree
);
}
else
{
priv
.
getAttachmentList
(
command
,
res_doc
,
option
,
callback
.
getAttachmentList
);
}
};
callback
.
getAttachmentList
=
function
(
err
,
res_list
)
{
var
i
,
attachment_found
=
false
;
if
(
err
)
{
err
.
message
=
"
Cannot get attachment
"
;
return
onEnd
(
err
,
undefined
);
}
res_list
=
res_list
.
data
;
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
,
{
"
data
"
:
attachment_list
[
i
].
_data
});
}
}
return
onEnd
(
priv
.
notFoundError
(
"
Unable to get an inexistent attachment
"
,
"
missing
"
),
undefined
);
}
if
(
specific_parameter
.
remove_from_attachment_list
)
{
// removing specific attachment
for
(
i
=
0
;
i
<
attachment_list
.
length
;
i
+=
1
)
{
if
(
attachment_list
[
i
]
&&
specific_parameter
.
remove_from_attachment_list
.
_attachment
===
attachment_list
[
i
].
_attachment
)
{
attachment_found
=
true
;
attachment_list
[
i
]
=
undefined
;
break
;
}
}
if
(
!
attachment_found
)
{
return
onEnd
(
priv
.
notFoundError
(
"
Unable to remove an inexistent attachment
"
,
"
missing
"
),
undefined
);
}
}
priv
.
putDocument
(
command
,
doc
,
option
,
callback
.
putDocument
);
};
callback
.
putDocument
=
function
(
err
)
{
var
i
,
attachment_found
=
false
;
if
(
err
)
{
err
.
message
=
"
Cannot post the document
"
;
return
onEnd
(
err
,
undefined
);
}
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
(
command
,
doc
,
option
,
attachment_list
,
callback
.
putAttachmentList
);
};
callback
.
putAttachmentList
=
function
(
err
)
{
if
(
err
)
{
err
.
message
=
"
Cannot copy attacments to the document
"
;
return
onEnd
(
err
,
undefined
);
}
priv
.
putDocumentTree
(
command
,
doc
,
option
,
doc_tree
,
callback
.
putDocumentTree
);
};
callback
.
putDocumentTree
=
function
(
err
)
{
var
response_object
;
if
(
err
)
{
err
.
message
=
"
Cannot update the document history
"
;
return
onEnd
(
err
,
undefined
);
}
response_object
=
{
"
id
"
:
doc
.
_id
,
"
rev
"
:
doc
.
_rev
};
if
(
specific_parameter
.
putAttachment
||
specific_parameter
.
removeAttachment
||
specific_parameter
.
getAttachment
)
{
response_object
.
attachment
=
doc
.
_attachment
;
}
onEnd
(
undefined
,
response_object
);
// if (option.keep_revision_history !== true) {
// // priv.remove(command, prev_doc, option, function () {
// // - change "available" status to "deleted"
// // - remove attachments
// // - done, no callback
// // });
// }
};
callback
.
begin
();
};
/**
* 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
,
metadata
,
option
)
{
priv
.
revisionGenericRequest
(
command
,
metadata
,
option
,
{},
function
(
err
,
response
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
({
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
});
}
);
};
/**
* Put 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 put
* @param {object} command The JIO command
*/
that
.
put
=
function
(
command
,
metadata
,
option
)
{
priv
.
revisionGenericRequest
(
command
,
metadata
,
option
,
{},
function
(
err
,
response
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
({
"
rev
"
:
response
.
rev
});
}
);
};
that
.
putAttachment
=
function
(
command
,
param
,
option
)
{
tool
.
readBlobAsBinaryString
(
param
.
_blob
).
then
(
function
(
event
)
{
param
.
_content_type
=
param
.
_blob
.
type
;
param
.
_data
=
event
.
target
.
result
;
delete
param
.
_blob
;
priv
.
revisionGenericRequest
(
command
,
param
,
option
,
{
"
doc_id
"
:
param
.
_id
,
"
attachment_id
"
:
param
.
_attachment
,
"
add_to_attachment_list
"
:
{
"
_attachment
"
:
param
.
_attachment
,
"
_content_type
"
:
param
.
_content_type
,
"
_data
"
:
param
.
_data
},
"
putAttachment
"
:
true
},
function
(
err
,
response
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
({
"
rev
"
:
response
.
rev
});
}
);
},
function
()
{
command
.
error
(
"
conflict
"
,
"
broken blob
"
,
"
Cannot read data to put
"
);
});
};
that
.
remove
=
function
(
command
,
param
,
option
)
{
priv
.
revisionGenericRequest
(
command
,
param
,
option
,
{
"
revision_needed
"
:
true
,
"
remove
"
:
true
},
function
(
err
,
response
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
({
"
rev
"
:
response
.
rev
});
}
);
};
that
.
removeAttachment
=
function
(
command
,
param
,
option
)
{
priv
.
revisionGenericRequest
(
command
,
param
,
option
,
{
"
doc_id
"
:
param
.
_id
,
"
attachment_id
"
:
param
.
_attachment
,
"
revision_needed
"
:
true
,
"
removeAttachment
"
:
true
,
"
remove_from_attachment_list
"
:
{
"
_attachment
"
:
param
.
_attachment
}
},
function
(
err
,
response
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
({
"
rev
"
:
response
.
rev
});
}
);
};
that
.
get
=
function
(
command
,
param
,
option
)
{
priv
.
revisionGenericRequest
(
command
,
param
,
option
,
{
"
get
"
:
true
},
function
(
err
,
response
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
({
"
data
"
:
response
.
data
});
}
);
};
that
.
getAttachment
=
function
(
command
,
param
,
option
)
{
priv
.
revisionGenericRequest
(
command
,
param
,
option
,
{
"
doc_id
"
:
param
.
_id
,
"
attachment_id
"
:
param
.
_attachment
,
"
getAttachment
"
:
true
},
function
(
err
,
response
)
{
if
(
err
)
{
return
command
.
error
(
err
);
}
command
.
success
({
"
data
"
:
response
.
data
});
}
);
};
that
.
allDocs
=
function
(
command
,
param
,
option
)
{
/*jslint unparam: true */
var
rows
,
result
=
{
"
total_rows
"
:
0
,
"
rows
"
:
[]},
functions
=
{};
functions
.
finished
=
0
;
functions
.
falseResponseGenerator
=
function
(
response
,
callback
)
{
callback
(
undefined
,
response
);
};
functions
.
fillResultGenerator
=
function
(
doc_id
)
{
return
function
(
err
,
doc_tree
)
{
var
document_revision
,
row
,
revs_info
;
if
(
err
)
{
return
command
.
error
(
err
);
}
doc_tree
=
doc_tree
.
data
;
if
(
typeof
doc_tree
.
children
===
'
string
'
)
{
doc_tree
.
children
=
JSON
.
parse
(
doc_tree
.
children
);
}
revs_info
=
priv
.
getWinnerRevsInfo
(
doc_tree
);
document_revision
=
rows
.
document_revisions
[
doc_id
+
"
.
"
+
revs_info
[
0
].
rev
];
if
(
document_revision
)
{
row
=
{
"
id
"
:
doc_id
,
"
key
"
:
doc_id
,
"
value
"
:
{
"
rev
"
:
revs_info
[
0
].
rev
}
};
if
(
document_revision
.
doc
&&
option
.
include_docs
)
{
document_revision
.
doc
.
_id
=
doc_id
;
document_revision
.
doc
.
_rev
=
revs_info
[
0
].
rev
;
row
.
doc
=
document_revision
.
doc
;
}
result
.
rows
.
push
(
row
);
result
.
total_rows
+=
1
;
}
functions
.
success
();
};
};
functions
.
success
=
function
()
{
functions
.
finished
-=
1
;
if
(
functions
.
finished
===
0
)
{
command
.
success
({
"
data
"
:
result
});
}
};
priv
.
send
(
command
,
"
allDocs
"
,
null
,
option
,
function
(
err
,
response
)
{
var
i
,
row
,
selector
,
selected
;
if
(
err
)
{
return
command
.
error
(
err
);
}
response
=
response
.
data
;
selector
=
/
\.
revision_tree
\.
json$/
;
rows
=
{
"
revision_trees
"
:
{
// id.revision_tree.json: {
// id: blabla
// doc: {...}
// }
},
"
document_revisions
"
:
{
// id.rev: {
// id: blabla
// rev: 1-1
// doc: {...}
// }
}
};
while
(
response
.
rows
.
length
>
0
)
{
// filling rows
row
=
response
.
rows
.
shift
();
selected
=
selector
.
exec
(
row
.
id
);
if
(
selected
)
{
selected
=
selected
.
input
.
substring
(
0
,
selected
.
index
);
// this is a revision tree
rows
.
revision_trees
[
row
.
id
]
=
{
"
id
"
:
selected
};
if
(
row
.
doc
)
{
rows
.
revision_trees
[
row
.
id
].
doc
=
row
.
doc
;
}
}
else
{
// this is a simple revision
rows
.
document_revisions
[
row
.
id
]
=
{
"
id
"
:
row
.
id
.
split
(
"
.
"
).
slice
(
0
,
-
1
),
"
rev
"
:
row
.
id
.
split
(
"
.
"
).
slice
(
-
1
)
};
if
(
row
.
doc
)
{
rows
.
document_revisions
[
row
.
id
].
doc
=
row
.
doc
;
}
}
}
functions
.
finished
+=
1
;
for
(
i
in
rows
.
revision_trees
)
{
if
(
rows
.
revision_trees
.
hasOwnProperty
(
i
))
{
functions
.
finished
+=
1
;
if
(
rows
.
revision_trees
[
i
].
doc
)
{
functions
.
falseResponseGenerator
(
{
"
data
"
:
rows
.
revision_trees
[
i
].
doc
},
functions
.
fillResultGenerator
(
rows
.
revision_trees
[
i
].
id
)
);
}
else
{
priv
.
getRevisionTree
(
command
,
{
"
_id
"
:
rows
.
revision_trees
[
i
].
id
},
option
,
functions
.
fillResultGenerator
(
rows
.
revision_trees
[
i
].
id
)
);
}
}
}
functions
.
success
();
});
};
// XXX
that
.
check
=
function
(
command
)
{
command
.
success
();
};
// XXX
that
.
repair
=
function
(
command
)
{
command
.
success
();
};
});
// end RevisionStorage
}));
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