Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
jio
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
18
Merge Requests
18
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
jio
Commits
eca94a2a
Commit
eca94a2a
authored
Dec 18, 2012
by
Sven Franck
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
POST and PUT working, created global storage utils
parent
31e65d17
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
945 additions
and
483 deletions
+945
-483
src/jio.dummystorages.js
src/jio.dummystorages.js
+1
-1
src/jio.storage/intro.js
src/jio.storage/intro.js
+481
-0
src/jio.storage/localstorage.js
src/jio.storage/localstorage.js
+137
-338
src/jio/commands/command.js
src/jio/commands/command.js
+7
-0
test/jiotests.js
test/jiotests.js
+319
-144
No files found.
src/jio.dummystorages.js
View file @
eca94a2a
src/jio.storage/intro.js
View file @
eca94a2a
...
...
@@ -7,6 +7,487 @@
* - CryptedStorage ('crypted')
* - ConflictManagerStorage ('conflictmanager')
*
* @module cross-storage methods
*/
var
utilities
=
{
/**
* @method isObjectEmpty - Check whether an object is empty
* @param {obj} object - object to test
* @returns {boolean} string- true/false
*/
isObjectEmpty
:
function
(
obj
)
{
var
key
;
if
(
obj
.
length
&&
obj
.
length
>
0
){
return
false
;
}
if
(
obj
.
length
&&
obj
.
length
===
0
){
return
true
;
}
for
(
key
in
obj
)
{
if
({}.
hasOwnProperty
.
call
(
obj
,
key
)){
return
false
;
}
}
return
true
;
},
/**
* @method isObjectSize - Check number of elements in object
* @param {obj} object - object to test
* @returns {size} integer - size
*/
isObjectSize
:
function
(
obj
)
{
var
size
=
0
,
key
;
for
(
key
in
obj
)
{
if
(
obj
.
hasOwnProperty
(
key
)){
size
++
;
}
}
return
size
;
},
/**
* @method isInObject - Check if revision is on tree
* @param {needle} string - revision
* @param {haystack} array - active leaves (versions of a document)
* @returns {boolean} string- true/false
*/
isInObject
:
function
(
needle
,
haystack
){
var
length
=
haystack
.
length
;
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
if
(
haystack
[
i
]
===
needle
){
return
true
;
}
}
return
false
;
},
/**
* @method isUUID - Check if docid is UUID
* @param {needle} string - docId
* @returns {boolean} string- true/false
*/
isUUID
:
function
(
documentId
){
var
reg
=
/^
[
0-9a-f
]{8}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{12}
$/i
.
test
(
documentId
);
return
reg
;
},
/**
* @method throwError - Creates the error object for all errors
* @param {code} string - the error code.
* @param {reason} string - the error reason
* @returns {e} object - error object
*/
throwError
:
function
(
code
,
reason
)
{
var
statusText
,
error
,
message
,
errorObject
;
switch
(
code
){
case
409
:
statusText
=
'
Conflict
'
;
error
=
'
conflict
'
;
message
=
'
Document update conflict.
'
;
break
;
case
403
:
statusText
=
'
Forbidden
'
;
error
=
'
forbidden
'
;
message
=
'
Forbidden
'
;
break
;
case
404
:
statusText
=
'
Not found
'
;
error
=
'
not found
'
;
message
=
'
Document not found.
'
;
break
;
}
// create object
errorObject
=
({
status
:
code
,
statusText
:
statusText
,
error
:
error
,
message
:
message
,
reason
:
reason
});
return
errorObject
;
},
/**
* Generates a hash code of a string
* @method hashCode
* @param {string} string The string to hash
* @return {string} The string hash code
*/
hashCode
:
function
(
string
)
{
return
hex_sha256
(
string
);
},
/**
* 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 {string} string String to help generate hash code
* @return {array} 0:The next revision number and 1:the hash code
*/
generateNextRevision
:
function
(
previous_revision
,
string
)
{
return
[
parseInt
(
previous_revision
.
split
(
'
-
'
)[
0
],
10
)
+
1
,
utilities
.
hashCode
(
previous_revision
+
string
)];
},
/**
* Replace substrings to others substring following a [list_of_replacement].
* It will be executed recusively to replace substrings which are not
* replaced substrings.
* It starts from the last element of the list of replacement.
* @method replaceSubString
* @param {string} string The string to replace
* @param {array} list_of_replacement A list containing arrays with 2
* values:
* - {string} The substring to replace
* - {string} The new substring
* ex: [['b', 'abc'], ['abc', 'cba']]
* @return {string} The new string
*/
replaceSubString
:
function
(
string
,
list_of_replacement
)
{
var
i
,
split_string
=
string
.
split
(
list_of_replacement
[
0
][
0
]);
if
(
list_of_replacement
[
1
])
{
for
(
i
=
0
;
i
<
split_string
.
length
;
i
+=
1
)
{
split_string
[
i
]
=
utilities
.
replaceSubString
(
split_string
[
i
],
list_of_replacement
.
slice
(
1
)
);
}
}
return
split_string
.
join
(
list_of_replacement
[
0
][
1
]);
},
/**
* It secures the [string] replacing all '%' by '%%' and '/' by '%2F'.
* @method secureString
* @param {string} string The string to secure
* @return {string} The secured string
*/
secureString
:
function
(
string
)
{
return
utilities
.
replaceSubString
(
string
,
[[
'
/
'
,
'
%2F
'
],[
'
%
'
,
'
%%
'
]]);
},
/**
* It replaces all '%2F' by '/' and '%%' by '%'.
* @method unsecureString
* @param {string} string The string to convert
* @return {string} The converted string
*/
unsecureString
:
function
(
string
)
{
return
utilities
.
replaceSubString
(
string
,
[[
'
%%
'
,
'
%
'
],[
'
%2F
'
,
'
/
'
]]);
},
// ============================ CREATE/UPDATE DOCUMENT =====================
/**
* @method createDocument - Creates a new document
* @info - docid POST = "" for POST, PUT = string
* @param {docid} string - id for the new document
* @param {docpath} string - the path where to store the document
* @stored - 'jio/local/USR/APP/FILE_NAME'
* @returns {doc} object - document object
*/
createDocument
:
function
(
docId
,
docPath
){
var
now
=
Date
.
now
(),
doc
=
{},
hash
=
utilities
.
hashCode
(
''
+
doc
+
'
'
+
now
+
''
);
doc
.
_id
=
docId
;
doc
.
_rev
=
'
1-
'
+
hash
;
doc
.
_revisions
=
{
start
:
1
,
ids
:
[
hash
]
};
doc
.
_revs_info
=
[{
rev
:
'
1-
'
+
hash
,
status
:
'
available
'
}];
return
doc
;
},
/**
* @method updateDocument - updates a document
* @info - called from PUT or PUTATTACHMENT
* @info - deletes old document (purge & replace)
* @param {docid} string - id for the new document
* @param {docpath} string - the path where to store the document
* @param {previousRevision} string - the previous revision
* @returns {doc} object - new document
*/
updateDocument
:
function
(
doc
,
docPath
,
previousRevision
){
var
now
=
Date
.
now
(),
rev
=
utilities
.
generateNextRevision
(
previousRevision
,
''
+
doc
+
'
'
+
now
+
''
);
// update document
doc
.
_rev
=
rev
.
join
(
'
-
'
);
doc
.
_revisions
.
ids
.
unshift
(
rev
[
1
]);
doc
.
_revisions
.
start
=
rev
[
0
];
doc
.
_revs_info
[
0
].
status
=
'
deleted
'
;
doc
.
_revs_info
.
unshift
({
"
rev
"
:
rev
.
join
(
'
-
'
),
"
status
"
:
"
available
"
});
return
doc
;
},
// ==================== CREATE/UPDATE DOCUMENT TREE ==================
/**
* @method createDocumentTree- Creates a new document.tree
* @param {doc } object - the document object
* @info: - the tree will include
* @key {type} string - "branch" or "leaf"
* @key {status} string - "available" or "deleted"
* @key {rev} string - revision string of this node
* @key {spawns} object - child branches/leaves
* @stored - 'jio/local/USR/APP/FILE_NAME/tree_revision'
* @info - one active leaf is needed to keep tree alive
* @info - deleted versions/branches = "status deleted"
* @info - no active leaves will delete tree, too
*/
createDocumentTree
:
function
(
doc
){
var
tree
=
{
type
:
'
leaf
'
,
status
:
'
available
'
,
rev
:
doc
.
_rev
,
kids
:[]
};
return
tree
;
},
/**
* @method updateDocumentTree- update a document tree
* @param {docTreeNode} object - document tree
* @param {old_rev} string - revision of the tree node to set to "branch"
* @param {new_rev } string - revison of the tree node to add as leaf
* @param {revs_info} object- history of new_rev to merge with remote tree
*/
updateDocumentTree
:
function
(
docTreeNode
,
old_rev
,
new_rev
,
revs_info
){
if
(
typeof
revs_info
===
"
object
"
){
// a new document version is being stored from another storage
utilities
.
mergeRemoteTree
(
docTreeNode
,
docTreeNode
,
old_rev
,
new_rev
,
revs_info
,
[],
false
);
}
else
{
// update an existing version of document = add a node to the tree
utilities
.
setTreeNode
(
docTreeNode
,
old_rev
,
new_rev
);
}
return
docTreeNode
;
},
// ==================== SET/MERGE/CHECK TREE NODES ==================
/**
* @method setTreeNode - adds a new tree node/changes leaf to branch
* @param {docTreeNode} object - document tree
* @param {old_rev} string - revision of the tree node to set to "branch"
* @param {new_rev } string - revison of the tree node to add as leaf
*/
setTreeNode
:
function
(
docTreeNode
,
old_rev
,
new_rev
){
var
kids
=
docTreeNode
[
'
kids
'
],
rev
=
docTreeNode
[
'
rev
'
],
numberOfKids
,
i
,
key
;
for
(
key
in
docTreeNode
){
if
(
key
===
"
rev
"
){
// grow the tree
if
(
old_rev
===
rev
&&
new_rev
!==
rev
){
docTreeNode
.
type
=
'
branch
'
;
docTreeNode
.
status
=
'
deleted
'
;
docTreeNode
.
kids
.
push
({
type
:
'
leaf
'
,
status
:
'
available
'
,
rev
:
new_rev
,
kids
:[]
});
}
else
{
// traverse until correct node is found!
if
(
utilities
.
isObjectEmpty
(
kids
)
===
false
){
numberOfKids
=
utilities
.
isObjectSize
(
kids
);
for
(
i
=
0
;
i
<
numberOfKids
;
i
+=
1
){
utilities
.
setTreeNode
(
kids
[
i
],
old_rev
,
new_rev
);
}
}
}
}
}
return
docTreeNode
;
},
/**
* @method mergeRemoteTree - merge revs_info into current tree
* @param {docTreeNode} object - document tree
* @param {old_rev} string - revision of the tree node to set to "branch"
* @param {new_rev } string - revison of the tree node to add as leaf
* @param {revs_info} object - these revisions need to be checked and added
* @param {addNodes} object - array for nodes to add to the tree
* @info: - old_rev = null here and it's not needed
* because new_rev will be used as the revision
* for the new tree node (we are copy&pasting
* from another storage)
*/
mergeRemoteTree
:
function
(
initialTree
,
docTreeNode
,
old_rev
,
new_rev
,
newDocumentRevisions
,
addNodes
,
onTree
){
var
sync_rev
=
newDocumentRevisions
[
0
].
rev
,
current_tree_rev
=
docTreeNode
[
'
rev
'
],
kids
=
docTreeNode
[
'
kids
'
],
addNodesLen
,
numberOfKids
,
key
,
i
,
j
;
for
(
key
in
docTreeNode
){
if
(
key
===
"
rev
"
){
// commeon ancestor? = does the revision on the current
// tree node match the currently checked remote tree
// revision
// match = common ancestor
if
(
sync_rev
===
current_tree_rev
){
onTree
=
true
;
// in order to loop we also add the revision of
// the common ancestor node to the array
// using push!
addNodes
.
unshift
(
current_tree_rev
);
// get length, now that we need it
addNodesLen
=
utilities
.
isObjectSize
(
addNodes
)
-
1
;
// the addNodes array will now look like this
// [current_node, all_missing_nodes]
for
(
j
=
0
;
j
<
addNodesLen
;
j
+=
1
){
utilities
.
setTreeNode
(
initialTree
,
addNodes
[
j
],
addNodes
[
j
+
1
]
);
}
// no match = continue down the tree
}
else
if
(
utilities
.
isObjectEmpty
(
kids
)
===
false
){
numberOfKids
=
utilities
.
isObjectSize
(
kids
);
for
(
i
=
0
;
i
<
numberOfKids
;
i
+=
1
){
utilities
.
mergeRemoteTree
(
initialTree
,
kids
[
i
],
old_rev
,
new_rev
,
newDocumentRevisions
,
addNodes
,
onTree
);
}
// end of tree = start over checking the next remote revision
}
else
if
(
onTree
===
false
){
// revision from _revs_info was not found in tree.
// add it to addNodes, remove it from newDocumentRevisions
// call mergeRemoteTree again with new modified arrays
// until a common ancestor is found
// remember to add this revision once an ancestor is found
// we use push here, because we later loop this from 0 to x
addNodes
.
unshift
(
sync_rev
);
// pop it off the revs_info
newDocumentRevisions
.
shift
();
// this should start over with the full document tree
// otherwise it will only continue on the current (last) node
utilities
.
mergeRemoteTree
(
initialTree
,
initialTree
,
old_rev
,
new_rev
,
newDocumentRevisions
,
addNodes
,
onTree
);
}
}
}
return
docTreeNode
;
},
/**
* @method getLeavesOnTree - finds all leaves on a tree
* @param {docTree} string - the tree for this document
* @returns {leaves} object - array with all leaves
* @info - find active (status = available ) leaves
*/
getLeavesOnTree
:
function
(
docTreeNode
){
var
revisions
=
[],
type
=
docTreeNode
[
'
type
'
],
status
=
docTreeNode
[
'
status
'
],
kids
=
docTreeNode
[
'
kids
'
],
rev
=
docTreeNode
[
'
rev
'
],
addLeaf
,
addLeaves
,
numberOfKids
,
i
,
key
;
for
(
key
in
docTreeNode
){
if
(
key
===
"
type
"
){
// node is a leaf, then it will have no kids!
if
(
type
===
'
leaf
'
&&
status
!==
'
deleted
'
){
addLeaf
=
docTreeNode
[
'
rev
'
];
}
// node has kid(s), must be a branch
if
(
utilities
.
isObjectEmpty
(
kids
)
===
false
){
numberOfKids
=
utilities
.
isObjectSize
(
kids
);
for
(
i
=
0
;
i
<
numberOfKids
;
i
+=
1
){
// recurse
addLeaves
=
utilities
.
getLeavesOnTree
(
kids
[
i
]
);
// single kid returns string 1-1234... = unshift
// multiple kids array [1-1234...,3-3412...] = concat
revisions
=
addLeaves
===
'
string
'
?
revisions
.
unshift
[
addLeaves
]
:
revisions
.
concat
(
addLeaves
);
}
}
}
}
// for recursiveness:
// no kids = passback string, multiple kids, pass back array
return
(
addLeaf
===
undefined
?
revisions
:
addLeaf
);
},
/**
* @method isDeadLeaf - Check if revision is branch or status deleted
* @param {node} string - revision
* @param {tree} object - active leaves (versions of a document)
* @returns - true/false
*/
isDeadLeaf
:
function
(
prev_rev
,
docTreeNode
){
var
type
=
docTreeNode
[
'
type
'
],
status
=
docTreeNode
[
'
status
'
],
kids
=
docTreeNode
[
'
kids
'
],
rev
=
docTreeNode
[
'
rev
'
],
result
=
false
,
numberOfKids
,
i
,
key
;
for
(
key
in
docTreeNode
){
if
(
key
===
"
rev
"
){
// if prev_rev is found, check if deleted or branch
if
(
prev_rev
===
rev
&&
(
type
===
'
branch
'
||
status
===
'
deleted
'
)
){
result
=
true
;
}
if
(
typeof
kids
===
"
object
"
&&
utilities
.
isObjectEmpty
(
kids
)
===
false
){
numberOfKids
=
utilities
.
isObjectSize
(
kids
);
for
(
i
=
0
;
i
<
numberOfKids
;
i
+=
1
){
// recurse
if
(
utilities
.
isDeadLeaf
(
prev_rev
,
kids
[
i
]
)
===
true
){
result
=
true
;
}
}
}
return
result
;
}
}
}
};
/*
* @module JIOStorages
*/
(
function
(
LocalOrCookieStorage
,
$
,
Base64
,
sjcl
,
hex_sha256
,
jIO
)
{
src/jio.storage/localstorage.js
View file @
eca94a2a
...
...
@@ -22,80 +22,10 @@ var newLocalStorage = function ( spec, my ) {
}
};
/**
* 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
hex_sha256
(
string
);
};
/**
* 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 {string} string String to help generate hash code
* @return {array} 0:The next revision number and 1:the hash code
*/
priv
.
generateNextRev
=
function
(
previous_revision
,
string
)
{
return
[
parseInt
(
previous_revision
.
split
(
'
-
'
)[
0
],
10
)
+
1
,
priv
.
hashCode
(
previous_revision
+
string
)];
};
/**
* Replace substrings to others substring following a [list_of_replacement].
* It will be executed recusively to replace substrings which are not
* replaced substrings.
* It starts from the last element of the list of replacement.
* @method replaceSubString
* @param {string} string The string to replace
* @param {array} list_of_replacement A list containing arrays with 2
* values:
* - {string} The substring to replace
* - {string} The new substring
* ex: [['b', 'abc'], ['abc', 'cba']]
* @return {string} The new string
*/
priv
.
replaceSubString
=
function
(
string
,
list_of_replacement
)
{
var
i
,
split_string
=
string
.
split
(
list_of_replacement
[
0
][
0
]);
if
(
list_of_replacement
[
1
])
{
for
(
i
=
0
;
i
<
split_string
.
length
;
i
+=
1
)
{
split_string
[
i
]
=
priv
.
replaceSubString
(
split_string
[
i
],
list_of_replacement
.
slice
(
1
)
);
}
}
return
split_string
.
join
(
list_of_replacement
[
0
][
1
]);
};
/**
* It secures the [string] replacing all '%' by '%%' and '/' by '%2F'.
* @method secureString
* @param {string} string The string to secure
* @return {string} The secured string
*/
priv
.
secureString
=
function
(
string
)
{
return
priv
.
replaceSubString
(
string
,
[[
'
/
'
,
'
%2F
'
],[
'
%
'
,
'
%%
'
]]);
};
/**
* It replaces all '%2F' by '/' and '%%' by '%'.
* @method unsecureString
* @param {string} string The string to convert
* @return {string} The converted string
*/
priv
.
unsecureString
=
function
(
string
)
{
return
priv
.
replaceSubString
(
string
,
[[
'
%%
'
,
'
%
'
],[
'
%2F
'
,
'
/
'
]]);
};
priv
.
username
=
spec
.
username
||
''
;
priv
.
secured_username
=
priv
.
secureString
(
priv
.
username
);
priv
.
secured_username
=
utilities
.
secureString
(
priv
.
username
);
priv
.
applicationname
=
spec
.
applicationname
||
'
untitled
'
;
priv
.
secured_applicationname
=
priv
.
secureString
(
priv
.
applicationname
);
priv
.
secured_applicationname
=
utilities
.
secureString
(
priv
.
applicationname
);
var
storage_user_array_name
=
'
jio/local_user_array
'
;
var
storage_file_array_name
=
'
jio/local_file_name_array/
'
+
...
...
@@ -234,177 +164,6 @@ var newLocalStorage = function ( spec, my ) {
}
};
/**
* @method throwError - Creates the error object for all errors
*
* @param {code} string - the error code.
* @param {reason} string - the error reason
*/
priv
.
throwError
=
function
(
code
,
reason
)
{
var
statusText
,
error
,
message
,
e
;
switch
(
code
){
case
409
:
statusText
=
'
Conflict
'
;
error
=
'
conflict
'
;
message
=
'
Document update conflict.
'
;
break
;
case
403
:
statusText
=
'
Forbidden
'
;
error
=
'
forbidden
'
;
message
=
'
Forbidden
'
;
break
;
case
404
:
statusText
=
'
Not found
'
;
error
=
'
not_found
'
;
message
=
'
Document not found.
'
;
break
;
}
// create object
e
=
({
status
:
code
,
statusText
:
statusText
,
error
:
error
,
message
:
message
,
reason
:
reason
});
return
e
;
};
/**
* @method createDocument - Creates a new document
*
* docid will be "" for POST and a string for PUT
*
* @param {docid} string - id for the new document
* @param {docpath} string - the path where to store the document
*
* @stored 'jio/local/USR/APP/FILE_NAME'
*/
priv
.
createDocument
=
function
(
docId
,
docPath
)
{
var
now
=
Date
.
now
(),
doc
=
{},
hash
=
priv
.
hashCode
(
''
+
doc
+
'
'
+
now
+
''
),
docPathRev
;
doc
.
_id
=
docId
;
doc
.
_rev
=
'
1-
'
+
hash
;
doc
.
_revisions
=
{
start
:
1
,
ids
:
[
hash
]
};
doc
.
_revs_info
=
[{
rev
:
'
1-
'
+
hash
,
status
:
'
available
'
}];
// allow to store multiple versions of a document by including rev
docPathRev
=
docPath
+
'
/
'
+
doc
.
_rev
;
// store
localstorage
.
setItem
(
docPathRev
,
doc
);
return
doc
;
};
/**
* @method updateDocument - updates a document
*
* called from PUT or PUTATTACHMENT (or REMOVE?)
*
* @param {docid} string - id for the new document
* @param {docpath} string - the path where to store the document
* @param {prev_rev} string- the previous revision
*
*/
priv
.
updateDocument
=
function
(
doc
,
docPath
,
prev_rev
)
{
var
now
=
Date
.
now
(),
rev
=
priv
.
generateNextRev
(
prev_rev
,
''
+
doc
+
'
'
+
now
+
''
);
// update document
doc
.
_rev
=
rev
.
join
(
'
-
'
);
doc
.
_revisions
.
ids
.
unshift
(
rev
[
1
]);
doc
.
_revisions
.
start
=
rev
[
0
];
doc
.
_revs_info
[
0
].
status
=
'
deleted
'
;
doc
.
_revs_info
.
unshift
({
"
rev
"
:
rev
.
join
(
'
-
'
),
"
status
"
:
"
available
"
});
// store
localstorage
.
setItem
(
docPath
,
doc
);
return
doc
;
};
/**
* @method createDocumentTree - Creates a new document.tree
*
* @param {docId} string - id for the new document
* @param {doc } object - the document object
* @param {docPath} string - the path where to store the document
*
* the tree will include
* @key {type} string - branch or leaf
* @key {status} string - available or deleted
* @key {rev} string - revision string of this node
* @key {spawns} object - child branches/leaves
*
* @stored 'jio/local/USR/APP/FILE_NAME/TREE_revision'.
*
* the tree is maintained as long as more than one leaf exists(!)
* a leaf set to status "deleted" implies a deleted document version
* When all leaves have been set to "deleted", the tree is also deleted.
*
*/
priv
.
createDocumentTree
=
function
(
doc
,
docId
,
docPath
){
var
tree
=
{
type
:
'
leaf
'
,
status
:
'
available
'
,
rev
:
doc
.
_rev
,
kids
:{}
},
treePath
=
docPath
+
'
/revision_tree
'
;
// store
localstorage
.
setItem
(
treePath
,
tree
);
};
/**
* @method updateDocumentTree - update a document tree
*
* @param {docId} string - id for the new document
* @param {doc } object - the document object
* @param {docPath} string - the path where to store the document
*
* a tree can be grown (update a document) or split (when creating
* a new version). Growing the tree means changing a leaf into
* a branch and creating a new leaf. This is done here.
*
*/
priv
.
updateDocumentTree
=
function
(
){
};
/**
* @method getLastTreeRevision - find a leaf
*
* @param {docTree} string - the tree for this document
*
* this method should get the last leaf on the tree.
* If there are multiple leaves that come into question
* we select same as COUCHDB, highest rev counter, than
* compare ASCII. We will return only a single document
*
*/
priv
.
getLastTreeRevision
=
function
(
docTree
){
};
/**
* @method post
*
...
...
@@ -420,39 +179,43 @@ var newLocalStorage = function ( spec, my ) {
setTimeout
(
function
()
{
var
docId
=
command
.
getDocId
(),
var
doc
,
docId
=
command
.
getDocId
(),
docPath
=
'
jio/local/
'
+
priv
.
secured_username
+
'
/
'
+
priv
.
secured_applicationname
+
'
/
'
+
docId
,
tree
,
treePath
=
docPath
+
'
/revision_tree
'
,
docTree
=
localstorage
.
getItem
(
treePath
),
doc
=
localstorage
.
getItem
(
docPath
),
reg
;
reg
=
utilities
.
isUUID
(
docId
);
// no attachments allowed
if
(
command
.
getAttachmentId
())
{
that
.
error
(
priv
.
throwError
(
403
,
that
.
error
(
utilities
.
throwError
(
403
,
'
Attachment cannot be added with a POST request
'
)
);
return
;
}
// check for UUID
reg
=
/^
[
0-9a-f
]{8}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{12}
$/i
.
test
(
docId
);
if
(
reg
!==
true
)
{
// id was supplied, use PUT
that
.
error
(
priv
.
throwError
(
403
,
that
.
error
(
utilities
.
throwError
(
403
,
'
ID cannot be supplied with a POST request. Please use PUT
'
)
);
return
;
}
else
{
// create and store new document
doc
=
priv
.
createDocument
(
docId
,
docPath
);
// create new document
doc
=
utilities
.
createDocument
(
docId
,
docPath
);
// store
localstorage
.
setItem
(
docPath
+
'
/
'
+
doc
.
_rev
,
doc
);
// create and store new document.tree
priv
.
createDocumentTree
(
doc
,
docId
,
docPath
);
tree
=
utilities
.
createDocumentTree
(
doc
);
// store
localstorage
.
setItem
(
treePath
,
tree
);
// add user
if
(
!
priv
.
doesUserExist
(
priv
.
secured_username
))
{
...
...
@@ -491,29 +254,16 @@ var newLocalStorage = function ( spec, my ) {
prev_rev
=
command
.
getDocInfo
(
'
_rev
'
),
docPath
=
'
jio/local/
'
+
priv
.
secured_username
+
'
/
'
+
priv
.
secured_applicationname
+
'
/
'
+
docId
,
docPathRev
=
docPath
+
'
/
'
+
prev_rev
,
treePath
=
docPath
+
'
/revision_tree
'
,
treePath
=
docPath
+
'
/revision_tree
'
,
tree
,
docTree
=
localstorage
.
getItem
(
treePath
),
doc
,
reg
;
doc
,
docPathRev
,
activeLeaves
,
reg
=
utilities
.
isUUID
(
docId
),
newDocTree
;
// no tree = create document or error
if
(
!
docTree
)
{
// check UUID
reg
=
/^
[
0-9a-f
]{8}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{4}
-
[
0-9a-f
]{12}
$/i
.
test
(
docId
);
// id/revision provided = update, revision must be incorrect
if
(
prev_rev
!==
undefined
&&
reg
===
false
){
that
.
error
(
priv
.
throwError
(
409
,
'
Incorrect Revision or ID
'
)
);
return
;
}
// revision provided = update, wrong revision or missing id
if
(
prev_rev
!==
undefined
){
that
.
error
(
priv
.
throwError
(
404
,
that
.
error
(
utilities
.
throwError
(
404
,
'
Document not found, please check revision and/or ID
'
)
);
return
;
...
...
@@ -521,7 +271,7 @@ var newLocalStorage = function ( spec, my ) {
// no revision and UUID = create, no id provided
if
(
prev_rev
===
undefined
&&
reg
===
true
){
that
.
error
(
priv
.
throwError
(
409
,
that
.
error
(
utilities
.
throwError
(
409
,
'
Missing Document ID and or Revision
'
)
);
return
;
...
...
@@ -534,10 +284,16 @@ var newLocalStorage = function ( spec, my ) {
// be the case.
// create and store new document
doc
=
priv
.
createDocument
(
docId
,
docPath
);
doc
=
utilities
.
createDocument
(
docId
,
docPath
);
// store
localstorage
.
setItem
(
docPath
+
'
/
'
+
doc
.
_rev
,
doc
);
// create and store new document.tree
priv
.
createDocumentTree
(
doc
,
docId
,
docPath
);
tree
=
utilities
.
createDocumentTree
(
doc
);
// store
localstorage
.
setItem
(
treePath
,
tree
);
// add user
if
(
!
priv
.
doesUserExist
(
priv
.
secured_username
))
{
...
...
@@ -556,61 +312,102 @@ var newLocalStorage = function ( spec, my ) {
);
}
else
{
// we found a tree
// found a tree - get active leaves
activeLeaves
=
utilities
.
getLeavesOnTree
(
docTree
);
// this should return an array of all active leaves
// or a single leaf, which needs to be put into an array
activeLeaves
=
typeof
activeLeaves
===
"
string
"
?
[
activeLeaves
]
:
activeLeaves
;
// check if revision is on doc-tree and is an active leaf
if
(
!
utilities
.
isInObject
(
prev_rev
,
activeLeaves
)
)
{
// check if it's a branch/dead leaf (deleted/updated version)
if
(
utilities
.
isDeadLeaf
(
prev_rev
,
docTree
)
){
that
.
error
(
utilities
.
throwError
(
409
,
'
Revision supplied is not the latest revision
'
)
);
return
;
}
// maybe a sync-PUT from another storage, we must
// have revs_info option, otherwise we cannot know
// where to put the file and update the storage tree
if
(
!
utilities
.
isDeadLeaf
(
prev_rev
,
docTree
)
&&
command
.
getDocInfo
(
'
_revs_info
'
)
===
undefined
){
that
.
error
(
utilities
.
throwError
(
409
,
'
Missing revs_info required for sync-put
'
)
);
return
;
}
else
{
// SYNC PUT
/*
console.log( docTree );
// revs_info is provided, this is a new version
// store this document and merge
console.log( prev_rev );
console.log( docId );
console.log( docPath );
console.log( docPathRev );
*/
doc
=
localstorage
.
getItem
(
docPathRev
);
// get the new document
doc
=
command
.
getDoc
();
console
.
log
(
doc
);
// check if rev_supplied is on the tree
// check if last node
// check if multiple leaves or one (do I need to???)
// if on tree and only leaf = expand
// if on tree and multiple leaves = ???
// if on tree, but not a leaf = error
// if not on tree, can be error or new version
// get revs_info from new document
// not available = error on PUT
// available = compare revs_info current and supplied
// start @root
// if nodes are the same and BRANCH, make sure they are branch and deleted if not the last nodes
// if nodes are not the same STOP
// add new kid to last branch
// continue with supplied revs_info to last version and add all the nodes on supplied revs_info
// this should "copy" the tree from supplied revs_info into the current document tree
// we have a tree, we know the last revision
//priv.getLastTreeRevision( docTree );
// we are not updating, this is a copy&paste sync
// therefore the path should have the revision of
// the current document. No new revision hash
// needs to be created
docPathRev
=
docPath
+
'
/
'
+
doc
.
_revs_info
[
0
].
rev
;
if
(
!
doc
)
{
// update ...???
priv
.
documentObjectUpdate
(
doc
,
command
.
cloneDoc
());
// store the new item.
localstorage
.
setItem
(
docPathRev
,
doc
);
// update tree and store
localstorage
.
setItem
(
treePath
,
utilities
.
updateDocumentTree
(
docTree
,
prev_rev
,
null
,
doc
.
_revs_info
)
);
that
.
success
(
priv
.
manageOptions
(
{
ok
:
true
,
id
:
docId
,
rev
:
prev_rev
},
command
,
doc
)
);
}
}
else
{
// revision matches a currently active leaf
// update of an existing document version
if
(
doc
.
_rev
!==
prev_rev
)
{
// get doc
docPathRev
=
docPath
+
'
/
'
+
prev_rev
;
doc
=
localstorage
.
getItem
(
docPathRev
);
that
.
error
(
priv
.
throwError
(
409
,
'
Revision supplied is not the latest revision
'
)
if
(
!
doc
){
// documen not available, should not happen!
that
.
error
(
utilities
.
throwError
(
404
,
'
Referenced document not found
'
)
);
return
;
}
}
else
{
// update ...?
priv
.
documentObjectUpdate
(
doc
,
command
.
cloneDoc
());
// update document (and get it back)
doc
=
priv
.
updateDocument
(
doc
,
docPath
,
prev_rev
);
// update document
doc
=
utilities
.
updateDocument
(
doc
,
docPath
,
prev_rev
);
// store new doc (.../DOCID/new_REVISION)
localstorage
.
setItem
(
docPath
+
'
/
'
+
doc
.
_rev
,
doc
);
// update document tree
priv
.
updateDocumentTree
();
// delete old doc (.../DOCID/old_REVISION)
localstorage
.
deleteItem
(
docPath
+
'
/
'
+
prev_rev
);
// update tree and store
localstorage
.
setItem
(
treePath
,
utilities
.
updateDocumentTree
(
docTree
,
prev_rev
,
doc
.
_rev
,
undefined
)
);
that
.
success
(
priv
.
manageOptions
(
...
...
@@ -619,8 +416,10 @@ var newLocalStorage = function ( spec, my ) {
doc
)
);
}
});
}
// found a doc to update
}
// updating existing document
}
// found a tree
});
// set timeout
};
// end put
/**
...
...
@@ -694,7 +493,7 @@ var newLocalStorage = function ( spec, my ) {
}
// rev = [number, hash]
rev
=
priv
.
generateNextRev
(
prev_rev
,
''
+
doc
+
'
'
+
now
+
''
);
rev
=
utilities
.
generateNextRevision
(
prev_rev
,
''
+
doc
+
'
'
+
now
+
''
);
doc
.
_rev
=
rev
.
join
(
'
-
'
);
doc
.
_revisions
.
ids
.
unshift
(
rev
[
1
]);
doc
.
_revisions
.
start
=
rev
[
0
];
...
...
src/jio/commands/command.js
View file @
eca94a2a
...
...
@@ -75,6 +75,13 @@ var command = function(spec, my) {
that
.
getContent
=
function
()
{
return
priv
.
content
;
};
/**
* @method getDoc returns the label of the command.
* @return {object} the document.
*/
that
.
getDoc
=
function
()
{
return
priv
.
doc
;
};
/**
* Returns an information about the document.
...
...
test/jiotests.js
View file @
eca94a2a
...
...
@@ -214,9 +214,47 @@ isEmptyObject = function( obj) {
}
}
return
true
;
}
;
}
,
//// end tools
//// test methods ////
checkReply
=
function
(
o
,
tick
,
fun
){
basic_tick_function
(
o
,
tick
,
fun
);
},
checkFile
=
function
(
response
,
o
,
tick
,
value
,
fun
)
{
o
.
tmp
=
localstorage
.
getItem
(
'
jio/local/
'
+
o
.
username
+
'
/jiotests/
'
+
response
.
id
+
'
/
'
+
response
.
rev
);
// remove everything not needed for basic response
o
.
tmp
.
ok
=
true
;
delete
o
.
tmp
.
_revisions
;
delete
o
.
tmp
.
_revs_info
;
delete
o
.
tmp
.
content
;
if
(
o
.
tmp
)
{
deepEqual
(
o
.
tmp
,{
"
ok
"
:
response
.
ok
,
"
_id
"
:
response
.
id
,
"
_rev
"
:
response
.
rev
,
},
'
document was created or updated
'
);
}
else
{
ok
(
false
,
'
document was not created or updated
'
);
}
},
checkTreeNode
=
function
(
response
,
o
,
tick
,
value
,
fun
)
{
o
.
tmp
=
localstorage
.
getItem
(
'
jio/local/
'
+
o
.
username
+
'
/jiotests/
'
+
response
.
id
+
'
/revision_tree
'
);
if
(
o
.
tmp
)
{
deepEqual
(
o
.
tmp
,
o
.
buildTestTree
,
'
tree node was created
'
);
}
else
{
ok
(
false
,
'
tree node was not created
'
);
}
};
//// QUnit Tests ////
module
(
'
Jio Global tests
'
);
...
...
@@ -290,7 +328,8 @@ test ('All tests', function () {
// post empty
o
.
jio
.
post
({},
function
(
err
,
response
)
{
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
},
'
dummyallok post/create empty object
'
);
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
},
'
dummyallok post/create empty object
'
);
o
.
f
(
response
);
});
o
.
tick
(
o
);
...
...
@@ -298,7 +337,8 @@ test ('All tests', function () {
// post
o
.
jio
.
post
({
"
content
"
:
"
basic_content
"
},
function
(
err
,
response
)
{
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
},
'
dummyallok post/create object
'
);
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
},
'
dummyallok post/create object
'
);
o
.
f
(
response
);
});
o
.
tick
(
o
);
...
...
@@ -306,7 +346,8 @@ test ('All tests', function () {
// put
o
.
jio
.
put
({
"
_id
"
:
"
file
"
,
"
content
"
:
"
basic_content
"
},
function
(
err
,
response
)
{
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
"
file
"
,
"
rev
"
:
response
.
rev
},
'
dummyallok put create object
'
);
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
"
file
"
,
"
rev
"
:
response
.
rev
},
'
dummyallok put create object
'
);
o
.
f
(
response
);
});
o
.
tick
(
o
);
...
...
@@ -594,11 +635,12 @@ module ( 'Jio LocalStorage' );
test
(
'
Post
'
,
function
(){
// runs following assertions
// a) POST with id - should be an error
// b) POST with attachment - should be an error
// c) POST with content
// d) check that document is created with UUID.revision
// e) check that document revision tree is created
// 1) POST with id - should be an error
// 2) POST with attachment - should be an error
// 3) POST CREATE with content
// 4) check that document is created with UUID.revision
// 5) check that document revision tree is created
// 6) POST UPDATE
var
o
=
{};
o
.
t
=
this
;
...
...
@@ -608,7 +650,7 @@ test ('Post', function(){
return
JSON
.
parse
(
localStorage
.
getItem
(
item
));
},
setItem
:
function
(
item
,
value
)
{
return
localStorage
.
setItem
(
item
,
JSON
.
stringify
(
value
));
return
localStorage
.
setItem
(
item
,
JSON
.
stringify
(
value
));
},
deleteItem
:
function
(
item
)
{
delete
localStorage
[
item
];
...
...
@@ -618,46 +660,12 @@ test ('Post', function(){
o
.
clock
.
tick
(
base_tick
);
o
.
spy
=
basic_spy_function
;
o
.
clean
=
clean_up_local_storage_function
();
// test functions
o
.
checkReply
=
function
(
o
,
tick
,
fun
){
basic_tick_function
(
o
,
tick
,
fun
);
};
o
.
checkFile
=
function
(
response
,
o
,
tick
,
value
,
fun
)
{
o
.
tmp
=
localstorage
.
getItem
(
'
jio/local/MrPost/jiotests/
'
+
response
.
id
+
'
/
'
+
response
.
rev
);
// fake it
o
.
tmp
.
ok
=
true
;
delete
o
.
tmp
.
_revisions
;
delete
o
.
tmp
.
_revs_info
;
if
(
o
.
tmp
)
{
deepEqual
(
o
.
tmp
,{
"
ok
"
:
response
.
ok
,
"
_id
"
:
response
.
id
,
"
_rev
"
:
response
.
rev
,
},
'
document was created
'
);
}
else
{
ok
(
false
,
'
document was not created
'
);
}
};
o
.
checkTree
=
function
(
response
,
o
,
tick
,
value
,
fun
)
{
o
.
tmp
=
localstorage
.
getItem
(
'
jio/local/MrPost/jiotests/
'
+
response
.
id
+
'
/revision_tree
'
);
if
(
o
.
tmp
)
{
deepEqual
(
o
.
tmp
,{
"
type
"
:
'
leaf
'
,
"
status
"
:
'
available
'
,
"
rev
"
:
response
.
rev
,
"
kids
"
:{}
},
'
document tree was created
'
);
}
else
{
ok
(
false
,
'
document tree was not created
'
);
}
};
o
.
username
=
'
MrPost
'
;
o
.
testBuildTree
=
[];
o
.
testRevisionStorage
=
[];
// let's go
o
.
jio
=
JIO
.
newJio
({
type
:
'
local
'
,
username
:
'
MrPost
'
,
o
.
jio
=
JIO
.
newJio
({
type
:
'
local
'
,
username
:
o
.
username
,
applicationname
:
'
jiotests
'
});
o
.
spy
(
o
,
'
value
'
,{
"
error
"
:
'
forbidden
'
,
...
...
@@ -666,9 +674,11 @@ test ('Post', function(){
"
status
"
:
403
,
"
statusText
"
:
'
Forbidden
'
},
'
POST with id = 403 forbidden
'
);
o
.
jio
.
post
({
"
_id
"
:
'
file
'
,
"
content
"
:
'
content
'
},
o
.
f
);
// TEST a) POST with id
o
.
checkReply
(
o
,
null
,
true
);
// TEST 1) POST with id
checkReply
(
o
,
null
,
true
);
o
.
spy
(
o
,
'
value
'
,{
"
error
"
:
'
forbidden
'
,
...
...
@@ -677,12 +687,14 @@ test ('Post', function(){
"
status
"
:
403
,
"
statusText
"
:
'
Forbidden
'
},
'
POST attachment = 403 forbidden
'
);
o
.
jio
.
post
({
"
_id
"
:
'
file/ABC
'
,
"
mimetype
"
:
'
text/html
'
,
"
content
"
:
'
<b>hello</b>
'
},
o
.
f
);
// TEST b) POST attachment
o
.
checkReply
(
o
,
null
,
true
);
// TEST 2) POST attachment
checkReply
(
o
,
null
,
true
);
o
.
jio
.
post
({
"
content
"
:
'
content
'
},
function
(
err
,
response
)
{
...
...
@@ -690,27 +702,49 @@ test ('Post', function(){
'
POST content = ok
'
);
o
.
f
(
response
);
// TEST d) check if document is created and correct
o
.
checkFile
(
response
,
o
,
null
,
true
);
// TEST e) check if document tree is created and correct
o
.
checkTree
(
response
,
o
,
null
,
true
);
// store falseRevision
o
.
falseRevision
=
response
.
rev
;
// build tree manually
o
.
testRevisionStorage
.
push
(
response
.
rev
);
o
.
buildTestTree
=
{
"
kids
"
:[],
"
rev
"
:
o
.
testRevisionStorage
[
0
],
"
status
"
:
'
available
'
,
"
type
"
:
'
leaf
'
};
// TEST 4) check if document is created and correct
checkFile
(
response
,
o
,
null
,
true
);
// TEST 5) check if document tree is created and correct
checkTreeNode
(
response
,
o
,
null
,
true
);
});
//
c
) TEST POST content
o
.
checkReply
(
o
,
null
,
true
);
//
3
) TEST POST content
checkReply
(
o
,
null
,
true
);
o
.
jio
.
stop
();
o
.
clean
;
});
// ============================== PUT ==========================
/*
test
(
'
Put
'
,
function
(){
// runs following assertions
// a)
// 1) PUT without ID = 409
// 2) PUT with wrong ID/rev = 404
// 3) PUT CREATE
// 4) check file was created
// 5) check tree was created
// 6) PUT UPDATE
// 7) check file was replaced
// 8) check tree was updated
// 9) PUT UPDATE 2
// 10) check file was replaced
// 11) check tree was updated
// 12) PUT UPDATE false revision = 409
// 13) SYNC-PUT no revs_info = 409
// 14) SYNC-PUT revs_info
var
o
=
{};
o
.
t
=
this
;
o
.
clock
=
o
.
t
.
sandbox
.
useFakeTimers
(),
o
.
falseRevision
,
localstorage
=
{
getItem
:
function
(
item
)
{
return
JSON
.
parse
(
localStorage
.
getItem
(
item
));
...
...
@@ -726,78 +760,219 @@ test ('Put', function(){
o
.
clock
.
tick
(
base_tick
);
o
.
spy
=
basic_spy_function
;
o
.
clean
=
clean_up_local_storage_function
();
o
.
username
=
'
MrPutt
'
;
o
.
testBuildTree
=
[];
o
.
testRevisionStorage
=
[];
// test functions
o.checkReply = function(o,tick,fun){
basic_tick_function(o,tick,fun);
};
o.checkFile = function (response, o, tick, value, fun) {
// let's go
o
.
jio
=
JIO
.
newJio
({
type
:
'
local
'
,
username
:
o
.
username
,
applicationname
:
'
jiotests
'
});
o.tmp =
localstorage.getItem('jio/local/MrSaveName/jiotests/'+ response.id+'/'+response.rev );
// TEST 1) PUT without ID
o
.
spy
(
o
,
'
value
'
,{
"
error
"
:
'
conflict
'
,
"
message
"
:
'
Document update conflict.
'
,
"
reason
"
:
'
Missing Document ID and or Revision
'
,
"
status
"
:
409
,
"
statusText
"
:
'
Conflict
'
},
'
PUT without id = 409 Conflict
'
);
o
.
jio
.
put
({
"
content
"
:
'
content
'
},
o
.
f
);
checkReply
(
o
,
null
,
true
);
// TEST 2) PUT wrong id/rev
o
.
spy
(
o
,
'
value
'
,{
"
error
"
:
'
not found
'
,
"
message
"
:
'
Document not found.
'
,
"
reason
"
:
'
Document not found, please check revision and/or ID
'
,
"
status
"
:
404
,
"
statusText
"
:
'
Not found
'
},
'
PUT with wrong id/revision = 404 Not found
'
);
o
.
jio
.
put
({
"
content
"
:
'
content
'
,
"
_id
"
:
'
myDoc
'
,
"
_rev
"
:
'
1-ABCDEFG
'
},
o
.
f
);
checkReply
(
o
,
null
,
true
);
// start adding content
o
.
jio
.
put
({
"
content
"
:
'
content
'
,
"
_id
"
:
'
myDoc
'
},
function
(
err
,
response
)
{
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
},
'
PUT content = ok
'
);
o
.
f
(
response
);
// fake it
o.tmp.ok = true;
delete o.tmp._revisions;
delete o.tmp._revs_info;
// store falseRevision
o
.
falseRevision
=
response
.
rev
;
if (o.tmp) {
deepEqual (o.tmp,{
"ok":response.ok,
"_id":response.id,
"_rev":response.rev,
},'document was created');
} else {
ok (false, 'document was not created');
}
// build tree manually
o
.
testRevisionStorage
.
unshift
(
response
.
rev
);
o
.
buildTestTree
=
{
"
kids
"
:[],
"
rev
"
:
o
.
testRevisionStorage
[
0
],
"
status
"
:
'
available
'
,
"
type
"
:
'
leaf
'
};
// TEST 4) check file was created
checkFile
(
response
,
o
,
null
,
true
);
// TEST 5) check tree was created
checkTreeNode
(
response
,
o
,
null
,
true
);
};
o.checkTree = function ( response, o, tick, value, fun) {
});
// 3) TEST PUT content
// no idea why this is working for 3/6/9/12, but it does...
checkReply
(
o
,
null
,
true
);
o.tmp =
localstorage.getItem('jio/local/MrPut/jiotests/'+ response.id+'/revision_tree' );
if (o.tmp) {
deepEqual (o.tmp,{
"type":'leaf',
"status":'available',
"rev":response.rev,
"kids":{}
},'document tree was created');
} else {
ok (false, 'document tree was not created');
}
// update document
o
.
jio
.
put
({
"
content
"
:
'
content_modified
'
,
"
_id
"
:
'
myDoc
'
,
"
_rev
"
:
o
.
testRevisionStorage
[
0
]},
function
(
err
,
response
)
{
};
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
},
'
PUT content = ok
'
);
o
.
f
(
response
);
o
.
testRevisionStorage
.
unshift
(
response
.
rev
);
o
.
buildTestTree
=
{
"
kids
"
:[{
"
kids
"
:[],
"
rev
"
:
o
.
testRevisionStorage
[
0
],
"
status
"
:
'
available
'
,
"
type
"
:
'
leaf
'
}],
"
rev
"
:
o
.
testRevisionStorage
[
1
],
"
status
"
:
'
deleted
'
,
"
type
"
:
'
branch
'
};
// let's go
o.jio = JIO.newJio({ type:'local', username:'MrPut',
applicationname:'jiotests' });
o.spy (o,'value',{ },'');
o.jio.post({"_id":'file',"content":'content'},o.f);
// a) TEST
o.checkReply(o,null,true);
// TEST 7) check document was replaced
checkFile
(
response
,
o
,
null
,
true
);
// TEST 8) check tree was updated
checkTreeNode
(
response
,
o
,
null
,
true
);
});
// 6) TEST PUT UPDATE
checkReply
(
o
,
null
,
true
);
// update document 2nd time
o
.
jio
.
put
({
"
content
"
:
'
content_modified_again
'
,
"
_id
"
:
'
myDoc
'
,
"
_rev
"
:
o
.
testRevisionStorage
[
0
]},
function
(
err
,
response
)
{
var
fake_rev_0
,
fake_rev_1
,
fake_id_0
,
fake_id_1
;
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
},
'
PUT content = ok
'
);
o
.
f
(
response
);
o
.
testRevisionStorage
.
unshift
(
response
.
rev
);
o
.
buildTestTree
=
{
"
kids
"
:[{
"
kids
"
:[{
"
kids
"
:[],
"
rev
"
:
o
.
testRevisionStorage
[
0
],
"
status
"
:
'
available
'
,
"
type
"
:
'
leaf
'
}],
"
rev
"
:
o
.
testRevisionStorage
[
1
],
"
status
"
:
'
deleted
'
,
"
type
"
:
'
branch
'
}],
"
rev
"
:
o
.
testRevisionStorage
[
2
],
"
status
"
:
'
deleted
'
,
"
type
"
:
'
branch
'
};
o.jio.post({"content":'content'},
// TEST 10) check document was replaced
checkFile
(
response
,
o
,
null
,
true
);
// TEST 11) check tree was updated
checkTreeNode
(
response
,
o
,
null
,
true
);
});
// 9) TEST PUT UPDATE
checkReply
(
o
,
null
,
true
);
// continue to work with this instance
//o.jio.stop();
//o.clean;
// try updating with false revision
o
.
spy
(
o
,
'
value
'
,{
"
error
"
:
'
conflict
'
,
"
message
"
:
'
Document update conflict.
'
,
"
reason
"
:
'
Revision supplied is not the latest revision
'
,
"
status
"
:
409
,
"
statusText
"
:
'
Conflict
'
},
'
PUT false revision = 409 Conflict
'
);
o
.
jio
.
put
({
"
content
"
:
'
content_modified_false
'
,
"
_id
"
:
'
myDoc
'
,
"
_rev
"
:
o
.
falseRevision
},
o
.
f
);
// TEST 12) PUT false revision
checkReply
(
o
,
null
,
true
);
// try updating without revs_info
o
.
spy
(
o
,
'
value
'
,{
"
error
"
:
'
conflict
'
,
"
message
"
:
'
Document update conflict.
'
,
"
reason
"
:
'
Missing revs_info required for sync-put
'
,
"
status
"
:
409
,
"
statusText
"
:
'
Conflict
'
},
'
PUT no sync info = 409 Conflict
'
);
o
.
jio
.
put
({
"
content
"
:
'
content_modified_false
'
,
"
_id
"
:
'
myDoc
'
,
"
_rev
"
:
'
1-abcdefg
'
},
o
.
f
);
// TEST 13) SYNC-PUT no revs_info
checkReply
(
o
,
null
,
true
);
// add a new document version with fake revs_info
// the new document has the same origin and first edit,
// then it was changed to a new version (3-a9d...),
// which was changed to a fourth version (4-b5bb...),
// the tree must merge on o.testRevisionStorage[1]
// and add the two new dummy revisions into the final
// tree. Also the new document should be stored
// in local storage.
fake_rev_1
=
o
.
testRevisionStorage
[
1
];
fake_rev_0
=
o
.
testRevisionStorage
[
0
];
fake_id_0
=
o
.
testRevisionStorage
[
0
].
split
(
'
-
'
)[
1
];
fake_id_1
=
o
.
testRevisionStorage
[
1
].
split
(
'
-
'
)[
1
];
// put a new document version
o
.
jio
.
put
({
"
content
"
:
'
a_new_version
'
,
"
_id
"
:
'
myDoc
'
,
"
_rev
"
:
"
4-b5bb2f1657ac5ac270c14b2335e51ef1ffccc0a7259e14bce46380d6c446eb89
"
,
"
_revs_info
"
:[
{
"
rev
"
:
"
4-b5bb2f1657ac5ac270c14b2335e51ef1ffccc0a7259e14bce46380d6c446eb89
"
,
"
status
"
:
"
available
"
},
{
"
rev
"
:
"
3-a9dac9ff5c8e1b2fce58e5397e9b6a8de729d5c6eff8f26a7b71df6348986123
"
,
"
status
"
:
"
deleted
"
},
{
"
rev
"
:
fake_rev_1
,
"
status
"
:
"
deleted
"
},
{
"
rev
"
:
fake_rev_0
,
"
status
"
:
"
deleted
"
}
],
"
_revisions
"
:{
"
start
"
:
4
,
"
ids
"
:[
"
b5bb2f1657ac5ac270c14b2335e51ef1ffccc0a7259e14bce46380d6c446eb89
"
,
"
a9dac9ff5c8e1b2fce58e5397e9b6a8de729d5c6eff8f26a7b71df6348986123
"
,
fake_id_1
,
fake_id_0
]}
},
function
(
err
,
response
)
{
o.spy(o,'value',{"ok":true,"id":response.id,"rev":response.rev},
'POST content = ok');
//o.testRevisionStorage.unshift(response.rev);
o
.
buildTestTree
=
{
"
kids
"
:[
{
"
kids
"
:[
{
"
kids
"
:[],
"
rev
"
:
o
.
testRevisionStorage
[
0
],
"
status
"
:
'
available
'
,
"
type
"
:
'
leaf
'
},
{
"
kids
"
:[{
"
kids
"
:[],
"
rev
"
:
"
4-b5bb2f1657ac5ac270c14b2335e51ef1ffccc0a7259e14bce46380d6c446eb89
"
,
"
status
"
:
'
available
'
,
"
type
"
:
'
leaf
'
}],
"
rev
"
:
"
3-a9dac9ff5c8e1b2fce58e5397e9b6a8de729d5c6eff8f26a7b71df6348986123
"
,
"
status
"
:
'
deleted
'
,
"
type
"
:
'
branch
'
}],
"
rev
"
:
o
.
testRevisionStorage
[
1
],
"
status
"
:
'
deleted
'
,
"
type
"
:
'
branch
'
}],
"
rev
"
:
o
.
testRevisionStorage
[
2
],
"
status
"
:
'
deleted
'
,
"
type
"
:
'
branch
'
};
o
.
spy
(
o
,
'
value
'
,{
"
ok
"
:
true
,
"
id
"
:
response
.
id
,
"
rev
"
:
response
.
rev
},
'
PUT SYNC = ok
'
);
o
.
f
(
response
);
// d) check document is created and correct
o.checkFile(response, o, null, true);
// e) check document tree is created and correct
o.checkTree(response, o, null, true);
// TEST 15) check document was stored
checkFile
(
response
,
o
,
null
,
true
);
// TEST 16) check tree was updated
checkTreeNode
(
response
,
o
,
null
,
true
);
});
//
c) POST content - o.spy must be in callback to access response
o.
checkReply(o,null,true);
//
14) TEST PUT UPDATE
checkReply
(
o
,
null
,
true
);
o.jio.stop();
o.clean;
});
*/
/*
...
...
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