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
2ae42bb4
Commit
2ae42bb4
authored
Apr 05, 2013
by
Tristan Cavelier
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dav: post, put, putAttachment and get updated + test
parent
6e6a56d0
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
695 additions
and
466 deletions
+695
-466
src/jio.storage/davstorage.js
src/jio.storage/davstorage.js
+450
-346
test/jiotests.js
test/jiotests.js
+245
-120
No files found.
src/jio.storage/davstorage.js
View file @
2ae42bb4
...
...
@@ -3,147 +3,259 @@
* Released under the LGPL license.
* http://www.gnu.org/licenses/lgpl.html
*/
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global jIO: true, $: true, btoa: true */
jIO
.
addStorageType
(
'
dav
'
,
function
(
spec
,
my
)
{
spec
=
spec
||
{};
var
that
,
priv
,
super_serialized
;
that
=
my
.
basicStorage
(
spec
,
my
);
priv
=
{};
super_serialized
=
that
.
serialized
;
priv
.
secureDocId
=
function
(
string
)
{
var
split
=
string
.
split
(
'
/
'
),
i
;
if
(
split
[
0
]
===
''
)
{
split
=
split
.
slice
(
1
);
}
for
(
i
=
0
;
i
<
split
.
length
;
i
+=
1
)
{
if
(
split
[
i
]
===
''
)
{
return
''
;
// JIO Dav Storage Description :
// {
// type: "dav",
// url: {string}
// }
// {
// type: "dav",
// url: {string},
// auth_type: {string}, (optional)
// - "auto" (default) (not implemented)
// - "basic"
// - "digest" (not implemented)
// realm: {string}, (optional)
// - undefined (default) (not implemented)
// - "<string>" realm name (not implemented)
// username: {string},
// password: {string} (optional)
// }
// {
// type: "dav",
// url: {string},
// encoded_login: {string}
// }
// {
// type: "dav",
// url: {string},
// secured_login: {string} (not implemented)
// }
// NOTE: to get the authentication type ->
// curl --verbose -X OPTION http://domain/
// In the headers: "WWW-Authenticate: Basic realm="DAV-upload"
// URL Characters convertion:
// If I want to retrieve the file which id is -> http://100%.json
// http://domain/collection/http://100%.json cannot be applied
// - '/' is col separator,
// - '%' is special char
// - '.' document and attachment separator
// http://100%.json will become
// - http:%2F%2F100%25.json to avoid bad request ('/', '%' -> '%2F', '%25')
// - http:%2F%2F100%25_.json to avoid ids conflicts ('.' -> '_.')
// - http:%252F%252F100%2525_.json to avoid request bad interpretation
// ('%', '%25')
// The file will be saved as http:%2F%2F100%25_.json
jIO
.
addStorageType
(
"
dav
"
,
function
(
spec
,
my
)
{
var
priv
=
{},
that
=
my
.
basicStorage
(
spec
,
my
),
dav
=
{};
// ATTRIBUTES //
priv
.
url
=
null
;
priv
.
username
=
null
;
priv
.
password
=
null
;
priv
.
encoded_login
=
null
;
// CONSTRUCTOR //
/**
* Init the dav storage connector thanks to the description
* @method __init__
* @param {object} description The description object
*/
priv
.
__init__
=
function
(
description
)
{
priv
.
url
=
description
.
url
||
""
;
priv
.
url
=
priv
.
removeSlashIfLast
(
priv
.
url
);
// if (description.secured_login) {
// not implemented
// } else
if
(
description
.
encoded_login
)
{
priv
.
encoded_login
=
description
.
encoded_login
;
}
else
if
(
description
.
auth_type
)
{
if
(
description
.
auth_type
===
"
basic
"
)
{
priv
.
encoded_login
=
"
Basic
"
+
btoa
((
description
.
username
||
""
)
+
"
:
"
+
(
description
.
password
||
""
));
}
}
else
{
priv
.
encoded_login
=
""
;
}
return
split
.
join
(
'
%2F
'
);
};
priv
.
convertSlashes
=
function
(
string
)
{
return
string
.
split
(
'
/
'
).
join
(
'
%2F
'
);
// OVERRIDES //
that
.
specToStore
=
function
()
{
// TODO: secured password
// The encoded_login can be seen by anyone, we must find a way to secure it!
// secured_login = encrypt(encoded_login)
// encoded_login = decrypt(secured_login)
return
{
"
url
"
:
priv
.
url
,
"
encoded_login
"
:
priv
.
encoded_login
};
};
priv
.
restoreSlashes
=
function
(
string
)
{
return
string
.
split
(
'
%2F
'
).
join
(
'
/
'
);
that
.
validateState
=
function
()
{
if
(
typeof
priv
.
url
!==
"
string
"
||
priv
.
url
===
""
)
{
return
"
The webDav server URL is not provided
"
;
}
if
(
priv
.
encoded_login
===
null
)
{
return
"
Impossible to create the authorization
"
;
}
return
""
;
};
// TOOLS //
/**
* 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
();
};
// /**
// * Clones an object in deep
// * @method clone
// * @param {object} object The object to clone
// * @return {object} The cloned object
// */
// priv.clone = function (object) {
// var tmp = JSON.stringify(object);
// if (tmp === undefined) {
// return undefined;
// }
// return JSON.parse(tmp);
// };
/**
* Checks if an object has no enumerable keys
* @method objectIsEmpty
* @param {object} obj The object
* @return {boolean} true if empty, else false
* Replace substrings to another strings
* @method recursiveReplace
* @param {string} string The string to do replacement
* @param {array} list_of_replacement An array of couple
* ["substring to select", "selected substring replaced by this string"].
* @return {string} The replaced string
*/
priv
.
objectIsEmpty
=
function
(
obj
)
{
var
k
;
for
(
k
in
obj
)
{
if
(
obj
.
hasOwnProperty
(
k
))
{
return
false
;
priv
.
recursiveReplace
=
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
.
recursiveReplace
(
split_string
[
i
],
list_of_replacement
.
slice
(
1
)
);
}
}
return
true
;
return
split_string
.
join
(
list_of_replacement
[
0
][
1
])
;
};
priv
.
username
=
spec
.
username
||
''
;
priv
.
secured_username
=
priv
.
convertSlashes
(
priv
.
username
);
priv
.
password
=
spec
.
password
||
''
;
priv
.
url
=
spec
.
url
||
''
;
that
.
serialized
=
function
()
{
var
o
=
super_serialized
();
o
.
username
=
priv
.
username
;
o
.
url
=
priv
.
url
;
o
.
password
=
priv
.
password
;
return
o
;
/**
* Changes / to %2F, % to %25 and . to _.
* @method secureName
* @param {string} name The name to secure
* @return {string} The secured name
*/
priv
.
secureName
=
function
(
name
)
{
return
priv
.
recursiveReplace
(
name
,
[[
"
/
"
,
"
%2F
"
],
[
"
%
"
,
"
%25
"
]]);
};
priv
.
newAsyncModule
=
function
()
{
var
async
=
{};
async
.
call
=
function
(
obj
,
function_name
,
arglist
)
{
obj
.
_wait
=
obj
.
_wait
||
{};
if
(
obj
.
_wait
[
function_name
])
{
obj
.
_wait
[
function_name
]
-=
1
;
return
function
()
{};
}
// ok if undef or 0
arglist
=
arglist
||
[];
return
obj
[
function_name
].
apply
(
obj
[
function_name
],
arglist
);
};
async
.
neverCall
=
function
(
obj
,
function_name
)
{
obj
.
_wait
=
obj
.
_wait
||
{};
obj
.
_wait
[
function_name
]
=
-
1
;
};
async
.
wait
=
function
(
obj
,
function_name
,
times
)
{
obj
.
_wait
=
obj
.
_wait
||
{};
obj
.
_wait
[
function_name
]
=
times
;
};
async
.
end
=
function
()
{
async
.
call
=
function
()
{};
};
return
async
;
/**
* Restores the original name from a secured name
* @method restoreName
* @param {string} secured_name The secured name to restore
* @return {string} The original name
*/
priv
.
restoreName
=
function
(
secured_name
)
{
return
priv
.
recursiveReplace
(
secured_name
,
[[
"
%2F
"
,
"
/
"
],
[
"
%25
"
,
"
%
"
]]);
};
/**
* Checks if a browser supports cors (cross domain ajax requests)
* @method checkCors
* @return {boolean} true if supported, else false
* Convert document id and attachment id to a file name
* @method idsToFileName
* @param {string} doc_id The document id
* @param {string} attachment_id The attachment id (optional)
* @return {string} The file name
*/
priv
.
checkCors
=
function
()
{
return
$
.
support
.
cors
;
priv
.
idsToFileName
=
function
(
doc_id
,
attachment_id
)
{
doc_id
=
priv
.
secureName
(
doc_id
).
split
(
"
.
"
).
join
(
"
_.
"
);
if
(
typeof
attachment_id
===
"
string
"
)
{
attachment_id
=
priv
.
secureName
(
attachment_id
).
split
(
"
.
"
).
join
(
"
_.
"
);
return
doc_id
+
"
.
"
+
attachment_id
;
}
return
doc_id
;
};
/**
* Re
places last "." with "_." in document filenames
* @method
underscoreFileExtenisons
* @param
{string} url url to clean up
* @return {string}
clean_url cleaned up URL
* Re
moves the last character if it is a "/". "/a/b/c/" become "/a/b/c"
* @method
removeSlashIfLast
* @param
{string} string The string to modify
* @return {string}
The modified string
*/
priv
.
underscoreFileExtenisons
=
function
(
url
)
{
var
clean_url
=
url
.
replace
(
/,
\s(\w
+
)
$/
,
"
_.$1
"
);
return
clean_url
;
priv
.
removeSlashIfLast
=
function
(
string
)
{
if
(
string
[
string
.
length
-
1
]
===
"
/
"
)
{
return
string
.
slice
(
0
,
-
1
);
}
return
string
;
};
/**
* Replaces "_." with "." in document filenames
* @method restoreDots
* @param {string} url url to clean up
* @return {string} clean_url cleaned up URL
* Modify an ajax object to add default values
* @method makeAjaxObject
* @param {string} file_name The file name to add to the url
* @param {object} ajax_object The ajax object to override
* @return {object} A new ajax object with default values
*/
priv
.
restoreDots
=
function
(
url
)
{
var
clean_url
=
url
.
replace
(
/_
\.
/g
,
'
.
'
);
return
clean_url
;
priv
.
makeAjaxObject
=
function
(
file_name
,
method
,
ajax_object
)
{
ajax_object
.
type
=
method
||
ajax_object
.
type
||
"
GET
"
;
ajax_object
.
url
=
priv
.
url
+
"
/
"
+
priv
.
secureName
(
file_name
)
+
"
?_=
"
+
Date
.
now
();
ajax_object
.
async
=
ajax_object
.
async
===
false
?
false
:
true
;
ajax_object
.
crossdomain
=
ajax_object
.
crossdomain
===
false
?
false
:
true
;
ajax_object
.
headers
=
ajax_object
.
headers
||
{};
ajax_object
.
headers
.
Authorization
=
ajax_object
.
headers
.
Authorization
||
priv
.
encoded_login
;
return
ajax_object
;
};
/**
* Runs all ajax requests for davStorage
* @method ajax
* @param {object} ajax_object The request parameters
* @param {string} doc_id The document id
* @param {string} attachment_id The attachment id, can be undefined
* @param {string} method The request method
* @param {object} ajax_object The request parameters (optional)
*/
priv
.
ajax
=
function
(
ajax_object
)
{
$
.
ajax
({
url
:
ajax_object
.
url
,
type
:
ajax_object
.
type
,
async
:
true
,
dataType
:
ajax_object
.
dataType
||
null
,
data
:
ajax_object
.
data
||
null
,
crossdomain
:
true
,
headers
:
{
Authorization
:
'
Basic
'
+
btoa
(
priv
.
username
+
'
:
'
+
priv
.
password
),
Depth
:
ajax_object
.
headers
===
undefined
?
null
:
ajax_object
.
headers
.
depth
},
// xhrFields: {withCredentials: 'true'},
success
:
ajax_object
.
success
,
error
:
ajax_object
.
error
});
priv
.
ajax
=
function
(
doc_id
,
attachment_id
,
method
,
ajax_object
)
{
var
new_ajax_object
=
JSON
.
parse
(
JSON
.
stringify
(
ajax_object
)
||
"
{}
"
);
console
.
log
(
priv
.
makeAjaxObject
(
priv
.
idsToFileName
(
doc_id
,
attachment_id
),
method
,
new_ajax_object
));
return
$
.
ajax
(
priv
.
makeAjaxObject
(
priv
.
idsToFileName
(
doc_id
,
attachment_id
),
method
,
new_ajax_object
));
//.always(then || function () {});
};
/**
...
...
@@ -153,57 +265,123 @@ jIO.addStorageType('dav', function (spec, my) {
* @return {object} error The error object
*/
priv
.
createError
=
function
(
status
,
message
,
reason
)
{
var
error
=
{};
var
error
=
{
"
status
"
:
status
,
"
message
"
:
message
,
"
reason
"
:
reason
};
switch
(
status
)
{
case
404
:
error
.
status
=
status
;
error
.
statusText
=
"
Not found
"
;
error
.
error
=
"
not_found
"
;
error
.
message
=
message
;
error
.
reason
=
reason
;
break
;
case
405
:
error
.
status
=
status
;
error
.
statusText
=
"
Method Not Allowed
"
;
error
.
error
=
"
method_not_allowed
"
;
error
.
message
=
message
;
error
.
reason
=
reason
;
break
;
case
409
:
error
.
status
=
status
;
error
.
statusText
=
"
Conflicts
"
;
error
.
error
=
"
conflicts
"
;
error
.
message
=
message
;
error
.
reason
=
reason
;
break
;
case
24
:
error
.
statusText
=
"
Broken Document
"
;
break
;
}
error
.
error
=
error
.
statusText
.
toLowerCase
().
split
(
"
"
).
join
(
"
_
"
);
return
error
;
};
/**
* Check if method can be run on browser
* @method support
* Converts ajax error object to a JIO error object
* @method ajaxErrorToJioError
* @param {object} ajax_error_object The ajax error object
* @param {string} message The error message
* @param {string} reason The error reason
* @return {object} The JIO error object
*/
priv
.
support
=
function
(
docid
)
{
// no docId
if
(
!
(
typeof
docid
===
"
string
"
&&
docid
!==
""
))
{
that
.
error
(
priv
.
createError
(
405
,
"
Can't create document without id
"
,
"
Document id is undefined
"
));
return
true
;
priv
.
ajaxErrorToJioError
=
function
(
ajax_error_object
,
message
,
reason
)
{
var
jio_error_object
=
{};
jio_error_object
.
status
=
ajax_error_object
.
status
;
jio_error_object
.
statusText
=
ajax_error_object
.
statusText
;
jio_error_object
.
error
=
ajax_error_object
.
statusText
.
toLowerCase
().
split
(
"
"
).
join
(
"
_
"
);
jio_error_object
.
message
=
message
;
jio_error_object
.
reason
=
reason
;
return
jio_error_object
;
};
/**
* Function that create an object containing jQuery like callbacks
* @method makeJQLikeCallback
* @return {object} jQuery like callback methods
*/
priv
.
makeJQLikeCallback
=
function
()
{
var
result
=
null
,
emptyFun
=
function
()
{},
jql
=
{
"
respond
"
:
function
()
{
result
=
arguments
;
},
"
to_return
"
:
{
"
always
"
:
function
(
func
)
{
if
(
result
)
{
func
.
apply
(
func
,
result
);
jql
.
to_return
.
always
=
emptyFun
;
}
else
{
jql
.
respond
=
func
;
}
// no cross domain ajax
if
(
priv
.
checkCors
===
false
)
{
that
.
error
(
priv
.
createError
(
405
,
"
Browser does not support cross domain ajax
"
,
"
CORS is undefined
"
));
return
true
;
return
jql
.
to_return
;
},
"
then
"
:
function
(
func
)
{
if
(
result
)
{
func
(
result
[
1
]);
jql
.
to_return
.
then
=
emptyFun
;
}
else
{
jql
.
respond
=
function
(
err
,
response
)
{
func
(
response
);
};
}
return
jql
.
to_return
;
}
}
};
return
jql
;
};
// DAV REQUESTS //
/**
* Retrieve a document
* @method dav.getDocument
* @param {string} doc_id The document id
* @param {function} then The callback(err, response)
*/
dav
.
getDocument
=
function
(
doc_id
)
{
var
doc
,
jql
=
priv
.
makeJQLikeCallback
();
priv
.
ajax
(
doc_id
,
undefined
,
"
GET
"
).
always
(
function
(
one
,
state
,
three
)
{
if
(
state
!==
"
success
"
)
{
jql
.
respond
(
priv
.
ajaxErrorToJioError
(
one
,
"
Cannot retrieve document
"
,
"
Unknown error
"
),
undefined
);
}
else
{
try
{
doc
=
JSON
.
parse
(
one
);
}
catch
(
e
)
{
return
jql
.
respond
(
priv
.
createError
(
24
,
"
Cannot parse document
"
,
"
Document is broken
"
),
undefined
);
}
// document health is good
jql
.
respond
(
undefined
,
doc
);
}
});
return
jql
.
to_return
;
};
dav
.
putDocument
=
function
(
doc
)
{
// TODO
};
// JIO COMMANDS //
// wedDav methods rfc4918 (short summary)
// COPY Reproduces single resources (files) and collections (directory
// trees). Will overwrite files (if specified by request) but will
...
...
@@ -229,89 +407,54 @@ jIO.addStorageType('dav', function (spec, my) {
// adding custom headers triggers preflight OPTIONS request!
// http://remysharp.com/2011/04/21/getting-cors-working/
priv
.
putOrPost
=
function
(
command
,
type
)
{
var
docid
=
command
.
getDocId
(),
secured_docid
,
url
,
ajax_object
;
if
(
priv
.
support
(
docid
))
{
return
;
}
secured_docid
=
priv
.
secureDocId
(
command
.
getDocId
());
url
=
priv
.
url
+
'
/
'
+
priv
.
underscoreFileExtenisons
(
secured_docid
);
ajax_object
=
{
url
:
url
+
'
?_=
'
+
Date
.
now
(),
type
:
"
GET
"
,
dataType
:
"
text
"
,
success
:
function
()
{
if
(
type
===
'
POST
'
)
{
// POST the document already exists
that
.
error
(
priv
.
createError
(
409
,
"
Cannot create a new document
"
,
"
Document already exists
"
/**
* Creates a new document
* @method post
* @param {object} command The JIO command
*/
that
.
post
=
function
(
command
)
{
var
doc_id
=
command
.
getDocId
()
||
priv
.
generateUuid
();
priv
.
ajax
(
doc_id
,
undefined
,
"
GET
"
).
always
(
function
(
one
,
state
,
three
)
{
if
(
state
!==
"
success
"
)
{
if
(
one
.
status
===
404
)
{
// the document does not already exist
// updating document
priv
.
ajax
(
doc_id
,
undefined
,
"
PUT
"
,
{
"
dataType
"
:
"
text
"
,
"
data
"
:
JSON
.
stringify
(
command
.
cloneDoc
())
}).
always
(
function
(
one
,
state
,
three
)
{
if
(
state
!==
"
success
"
)
{
// an error occured
that
.
retry
(
priv
.
ajaxErrorToJioError
(
one
,
"
An error occured when trying to PUT data
"
,
"
Unknown
"
));
return
;
}
ajax_object
=
{
url
:
url
,
type
:
type
,
data
:
JSON
.
stringify
(
command
.
getDoc
()),
success
:
function
()
{
}
else
{
// document updated
that
.
success
({
ok
:
true
,
id
:
command
.
getDocId
()
"
ok
"
:
true
,
"
id
"
:
doc_id
});
},
error
:
function
()
{
that
.
error
(
priv
.
createError
(
409
,
"
Cannot modify document
"
,
"
Error writing to remote storage
"
));
}
};
priv
.
ajax
(
ajax_object
);
},
error
:
function
(
err
)
{
// Firefox returns 0 instead of 404 on CORS?
if
(
err
.
status
===
404
||
err
.
status
===
0
)
{
ajax_object
=
{
url
:
url
,
type
:
"
PUT
"
,
data
:
JSON
.
stringify
(
command
.
getDoc
()),
success
:
function
()
{
that
.
success
({
ok
:
true
,
id
:
command
.
getDocId
()
});
},
error
:
function
()
{
that
.
error
(
priv
.
createError
(
409
,
"
Cannot modify document
"
,
"
Error writing to remote storage
"
}
else
{
// an error occured
that
.
retry
(
priv
.
ajaxErrorToJioError
(
one
,
"
An error occured when trying to GET data
"
,
"
Unknown
"
));
}
};
priv
.
ajax
(
ajax_object
);
}
else
{
// error accessing remote storage
that
.
error
({
"
status
"
:
err
.
status
,
"
statusText
"
:
err
.
statusText
,
"
error
"
:
"
error
"
,
"
message
"
:
err
.
message
,
"
reason
"
:
"
Failed to access remote storage
"
});
}
// the document already exists
that
.
error
(
priv
.
createError
(
405
,
"
Cannot create document
"
,
"
Document already exists
"
));
}
};
priv
.
ajax
(
ajax_object
);
};
/**
* Creates a new document
* @method post
* @param {object} command The JIO command
*/
that
.
post
=
function
(
command
)
{
priv
.
putOrPost
(
command
,
'
POST
'
);
});
};
/**
...
...
@@ -320,7 +463,26 @@ jIO.addStorageType('dav', function (spec, my) {
* @param {object} command The JIO command
*/
that
.
put
=
function
(
command
)
{
priv
.
putOrPost
(
command
,
'
PUT
'
);
var
doc_id
=
command
.
getDocId
();
priv
.
ajax
(
doc_id
,
undefined
,
"
PUT
"
,
{
"
dataType
"
:
"
text
"
,
"
data
"
:
JSON
.
stringify
(
command
.
cloneDoc
())
}).
always
(
function
(
one
,
state
,
three
)
{
if
(
state
!==
"
success
"
)
{
// an error occured
that
.
retry
(
priv
.
ajaxErrorToJioError
(
one
,
"
Cannot update document
"
,
"
Unknown error
"
));
}
else
{
// document updated
that
.
success
({
"
ok
"
:
true
,
"
id
"
:
doc_id
});
}
});
};
/**
...
...
@@ -329,149 +491,90 @@ jIO.addStorageType('dav', function (spec, my) {
* @param {object} command The JIO command
*/
that
.
putAttachment
=
function
(
command
)
{
var
docid
=
command
.
getDocId
(),
doc
,
url
,
secured_docid
,
secured_attachmentid
,
attachment_url
,
ajax_object
;
priv
.
support
(
docid
);
secured_docid
=
priv
.
secureDocId
(
docid
);
url
=
priv
.
url
+
'
/
'
+
priv
.
underscoreFileExtenisons
(
secured_docid
);
ajax_object
=
{
url
:
url
+
'
?_=
'
+
Date
.
now
(),
type
:
'
GET
'
,
dataType
:
'
text
'
,
success
:
function
(
response
)
{
doc
=
JSON
.
parse
(
response
);
// the document exists - update document
var
doc
=
null
,
doc_id
=
command
.
getDocId
(),
attachment_id
,
tmp
;
attachment_id
=
command
.
getAttachmentId
();
priv
.
ajax
(
doc_id
,
undefined
,
"
GET
"
).
always
(
function
(
one
,
state
,
three
)
{
if
(
state
!==
"
success
"
)
{
// document not found or error
tmp
=
that
.
retry
;
if
(
one
.
status
===
404
)
{
tmp
=
that
.
error
;
}
tmp
(
priv
.
ajaxErrorToJioError
(
one
,
"
Cannot update document
"
,
"
Unknown error
"
));
}
else
{
try
{
doc
=
JSON
.
parse
(
one
);
}
catch
(
e
)
{
return
that
.
error
(
priv
.
createError
(
24
,
"
Cannot upload attachment
"
,
"
Document is broken
"
));
}
// document health is good
doc
.
_attachments
=
doc
.
_attachments
||
{};
doc
.
_attachments
[
command
.
getAttachmentId
()
]
=
{
"
content_type
"
:
command
.
getAttachmentMimeType
(),
doc
.
_attachments
[
attachment_id
]
=
{
"
length
"
:
command
.
getAttachmentLength
(),
"
digest
"
:
"
md5-
"
+
command
.
md5SumAttachmentData
(),
"
length
"
:
command
.
getAttachmentLength
()
};
// put updated document data
ajax_object
=
{
url
:
url
+
'
?_=
'
+
Date
.
now
(),
type
:
'
PUT
'
,
data
:
JSON
.
stringify
(
doc
),
success
:
function
()
{
secured_attachmentid
=
priv
.
secureDocId
(
command
.
getAttachmentId
());
attachment_url
=
url
+
'
.
'
+
priv
.
underscoreFileExtenisons
(
secured_attachmentid
);
ajax_object
=
{
url
:
attachment_url
+
'
?_=
'
+
Date
.
now
(),
type
:
'
PUT
'
,
data
:
JSON
.
stringify
(
command
.
getDoc
()),
success
:
function
()
{
"
content_type
"
:
command
.
getAttachmentMimeType
()
};
// put the attachment
priv
.
ajax
(
doc_id
,
attachment_id
,
"
PUT
"
,
{
"
dataType
"
:
"
text
"
,
"
data
"
:
command
.
getAttachmentData
()
}).
always
(
function
(
one
,
state
,
three
)
{
if
(
state
!==
"
success
"
)
{
// an error occured
that
.
retry
(
priv
.
ajaxErrorToJioError
(
one
,
"
An error occured when trying to PUT data
"
,
"
Unknown
"
));
}
else
{
// update the document
priv
.
ajax
(
doc_id
,
undefined
,
"
PUT
"
,
{
"
dataType
"
:
"
text
"
,
"
data
"
:
JSON
.
stringify
(
doc
)
}).
always
(
function
(
one
,
state
,
three
)
{
if
(
state
!==
"
success
"
)
{
that
.
retry
(
priv
.
ajaxErrorToJioError
(
one
,
"
An error occured when trying to PUT data
"
,
"
Unknown
"
));
}
else
{
that
.
success
({
ok
:
true
,
id
:
command
.
getDocId
()
+
'
/
'
+
command
.
getAttachmentId
()
"
ok
"
:
true
,
"
id
"
:
doc_id
,
"
attachment
"
:
attachment_id
});
},
error
:
function
()
{
that
.
error
(
priv
.
createError
(
409
,
"
Cannot modify document
"
,
"
Error when saving attachment
"
));
return
;
}
};
priv
.
ajax
(
ajax_object
);
},
error
:
function
()
{
that
.
error
(
priv
.
createError
(
409
,
"
Cannot modify document
"
,
"
Error writing to remote storage
"
));
return
;
});
}
};
priv
.
ajax
(
ajax_object
);
},
error
:
function
()
{
// the document does not exist
that
.
error
(
priv
.
createError
(
404
,
"
Impossible to add attachment
"
,
"
Document not found
"
));
return
;
});
}
};
// see if the underlying document exists
priv
.
ajax
(
ajax_object
);
});
};
/**
* Get a document or attachment from distant storage
* Options:
* - {boolean} revs Add simple revision history (false by default).
* - {boolean} revs_info Add revs info (false by default).
* - {boolean} conflicts Add conflict object (false by default).
* Get a document
* @method get
* @param {object} command The JIO command
*/
that
.
get
=
function
(
command
)
{
var
docid
=
command
.
getDocId
(),
doc
,
url
,
secured_docid
,
secured_attachmentid
,
attachment_url
,
ajax_object
;
if
(
priv
.
support
(
docid
))
{
return
;
dav
.
getDocument
(
command
.
getDocId
()).
always
(
function
(
err
,
response
)
{
if
(
err
)
{
if
(
err
.
status
===
404
)
{
return
that
.
error
(
err
);
}
secured_docid
=
priv
.
secureDocId
(
command
.
getDocId
());
url
=
priv
.
url
+
'
/
'
+
priv
.
underscoreFileExtenisons
(
secured_docid
);
if
(
typeof
command
.
getAttachmentId
()
===
"
string
"
)
{
secured_attachmentid
=
priv
.
secureDocId
(
command
.
getAttachmentId
());
attachment_url
=
url
+
'
.
'
+
priv
.
underscoreFileExtenisons
(
secured_attachmentid
);
// get attachment
ajax_object
=
{
url
:
attachment_url
+
'
?_=
'
+
Date
.
now
(),
type
:
"
GET
"
,
dataType
:
"
text
"
,
success
:
function
(
response
)
{
doc
=
JSON
.
parse
(
response
);
that
.
success
(
doc
);
},
error
:
function
()
{
that
.
error
(
priv
.
createError
(
404
,
"
Cannot find the attachment
"
,
"
Attachment does not exist
"
));
}
};
priv
.
ajax
(
ajax_object
);
}
else
{
// get document
ajax_object
=
{
url
:
url
+
'
?_=
'
+
Date
.
now
(),
type
:
"
GET
"
,
dataType
:
"
text
"
,
success
:
function
(
response
)
{
// metadata_only should not be handled by jIO, as it is a
// webDav only option, shouldn't it?
// ditto for content_only
doc
=
JSON
.
parse
(
response
);
that
.
success
(
doc
);
},
error
:
function
()
{
that
.
error
(
priv
.
createError
(
404
,
"
Cannot find the document
"
,
"
Document does not exist
"
));
}
};
priv
.
ajax
(
ajax_object
);
return
that
.
retry
(
err
);
}
return
that
.
success
(
response
);
});
};
/**
...
...
@@ -479,7 +582,7 @@ jIO.addStorageType('dav', function (spec, my) {
* @method remove
* @param {object} command The JIO command
*/
that
.
remove
=
function
(
command
)
{
that
.
_
remove
=
function
(
command
)
{
var
docid
=
command
.
getDocId
(),
doc
,
url
,
secured_docid
,
secured_attachmentid
,
attachment_url
,
attachment_list
=
[],
i
,
j
,
k
=
1
,
deleteAttachment
,
ajax_object
;
...
...
@@ -666,7 +769,7 @@ jIO.addStorageType('dav', function (spec, my) {
// },{...}
// ]
//}
that
.
allDocs
=
function
(
command
)
{
that
.
_
allDocs
=
function
(
command
)
{
var
rows
=
[],
url
,
am
=
priv
.
newAsyncModule
(),
o
=
{},
...
...
@@ -762,5 +865,6 @@ jIO.addStorageType('dav', function (spec, my) {
am
.
call
(
o
,
'
getDocumentList
'
);
};
priv
.
__init__
(
spec
);
return
that
;
});
test/jiotests.js
View file @
2ae42bb4
...
...
@@ -164,7 +164,6 @@ generateTools = function (test_namespace) {
var
o
=
{};
o
.
t
=
test_namespace
;
o
.
server
=
o
.
t
.
sandbox
.
useFakeServer
();
o
.
clock
=
sinon
.
useFakeTimers
();
o
.
clock
.
tick
(
base_tick
);
o
.
spy
=
basicSpyFunction
;
...
...
@@ -3471,7 +3470,7 @@ module ("JIO Replicate Revision Storage");
}]
},
"
2
"
);
});
/*
module
(
"
Jio DAVStorage
"
);
test
(
"
Post
"
,
function
()
{
...
...
@@ -3480,31 +3479,96 @@ test ("Post", function () {
o
.
jio
=
JIO
.
newJio
({
"
type
"
:
"
dav
"
,
"username": "davpost",
"password": "checkpwd",
"url": "https://ca-davstorage:8080"
"
url
"
:
"
https://ca-davstorage:8080
"
,
"
auth_type
"
:
"
basic
"
,
"
username
"
:
"
admin
"
,
"
password
"
:
"
pwd
"
});
// post without id
o.spy (o, "status", 405, "Post without id");
o.jio.post({}, o.f);
o.clock.tick(5000);
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
GET
"
,
/https:
\/\/
ca-davstorage:8080
\/[
0-9a-fA-F
]{4}
/
,
[
404
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>Document not found</h1>
"
]
);
o
.
server
.
respondWith
(
"
PUT
"
,
/https:
\/\/
ca-davstorage:8080
\/[
0-9a-fA-F
]{4}
/
,
[
200
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>Document updated!</h1>
"
]
);
o
.
spy
(
o
,
"
jobstatus
"
,
"
done
"
,
"
Post without id
"
);
o
.
jio
.
post
({},
{
"
max_retry
"
:
1
},
function
(
err
,
response
)
{
o
.
f
.
apply
(
arguments
);
if
(
response
)
{
ok
(
isUuid
(
response
.
id
),
"
Uuid should look like
"
+
"
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx :
"
+
response
.
id
);
}
});
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
o
.
tick
(
o
);
o
.
server
.
restore
();
// post non empty document
o.addFakeServerResponse("dav", "PUT", "myFile", 201, "HTML RESPONSE");
o.spy(o, "value", {"id": "myFile", "ok": true},
"Create = POST non empty document");
o.jio.post({"_id": "myFile", "title": "hello there"}, o.f);
o.clock.tick(5000);
// post document with id
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
GET
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
404
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>Document not found</h1>
"
]
);
o
.
server
.
respondWith
(
"
PUT
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
200
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>Document updated!</h1>
"
]
);
o
.
spy
(
o
,
"
value
"
,
{
"
id
"
:
"
http://100%.json
"
,
"
ok
"
:
true
},
"
Create document with an id
"
);
o
.
jio
.
post
({
"
_id
"
:
"
http://100%.json
"
,
"
title
"
:
"
Hello There
"
},
{
"
max_retry
"
:
1
},
o
.
f
);
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
o
.
tick
(
o
);
o
.
server
.
restore
();
// post but document already exists (post = error!, put = ok)
o.answer = JSON.stringify({"_id": "myFile", "title": "hello there"});
o.addFakeServerResponse("dav", "GET", "myFile", 200, o.answer);
o.spy (o, "status", 409, "Post but document already exists");
o.jio.post({"_id": "myFile", "title": "hello again"}, o.f);
o.clock.tick(5000);
// post already existant file
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
GET
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
200
,
{
"
Content-Type
"
:
"
text/plain
"
},
'
{"_id":"doc1","title":"Hello There"}
'
]
);
o
.
spy
(
o
,
"
status
"
,
405
,
"
Update document previous -> 405
"
);
o
.
jio
.
post
({
"
_id
"
:
"
http://100%.json
"
,
"
title
"
:
"
Hello There Again
"
},
{
"
max_retry
"
:
1
},
o
.
f
);
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
o
.
tick
(
o
);
o
.
server
.
restore
();
o
.
jio
.
stop
();
});
...
...
@@ -3515,35 +3579,60 @@ test ("Put", function(){
o
.
jio
=
JIO
.
newJio
({
"
type
"
:
"
dav
"
,
"username": "davput",
"password": "checkpwd",
"url": "https://ca-davstorage:8080"
"
url
"
:
"
https://ca-davstorage:8080
"
,
"
auth_type
"
:
"
basic
"
,
"
username
"
:
"
admin
"
,
"
password
"
:
"
pwd
"
});
// put without id =>
id r
equired
o.spy (o, "status", 20, "Put without id");
// put without id =>
20 Id R
equired
o
.
spy
(
o
,
"
status
"
,
20
,
"
Put without id
-> 20
"
);
o
.
jio
.
put
({},
o
.
f
);
o.
clock.tick(5000
);
o
.
tick
(
o
);
// put non empty document
o.addFakeServerResponse("dav", "PUT", "put1", 201, "HTML RESPONSE");
o.spy (o, "value", {"ok": true, "id": "put1"},
"Create = PUT non empty document");
o.jio.put({"_id": "put1", "title": "myPut1"}, o.f);
o.clock.tick(5000);
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
PUT
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
200
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>OK1</h1>
"
]
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
http://100%.json
"
},
"
Create document
"
);
o
.
jio
.
put
({
"
_id
"
:
"
http://100%.json
"
,
"
title
"
:
"
Hi There
"
},
{
"
max_retry
"
:
1
},
o
.
f
);
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
//console.log( o.server );
//console.log( o.server.requests[0].requestHeaders );
//console.log( o.server.requests[0].responseHeaders );
// put but document already exists = update
o.answer = JSON.stringify({"_id": "put1", "title": "myPut1"});
o.addFakeServerResponse("dav", "GET", "put1", 200, o.answer);
o.addFakeServerResponse("dav", "PUT", "put1", 201, "HTML RESPONSE");
o.spy (o, "value", {"ok": true, "id": "put1"}, "Updated the document");
o.jio.put({"_id": "put1", "title": "myPut2abcdedg"}, o.f);
o.clock.tick(5000);
o
.
tick
(
o
);
o
.
server
.
restore
();
// update document
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
PUT
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
200
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>OK!</h1>
"
]
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
http://100%.json
"
},
"
Update document
"
);
o
.
jio
.
put
({
"
_id
"
:
"
http://100%.json
"
,
"
title
"
:
"
Hi There Again
"
},
{
"
max_retry
"
:
1
},
o
.
f
);
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
o
.
tick
(
o
);
o
.
server
.
restore
();
o
.
jio
.
stop
();
});
...
...
@@ -3554,51 +3643,87 @@ test ("PutAttachment", function(){
o
.
jio
=
JIO
.
newJio
({
"
type
"
:
"
dav
"
,
"username": "davputattm",
"password": "checkpwd",
"url": "https://ca-davstorage:8080"
"
url
"
:
"
https://ca-davstorage:8080
"
,
"
auth_type
"
:
"
basic
"
,
"
username
"
:
"
admin
"
,
"
password
"
:
"
pwd
"
});
// putAttachment without doc id => id required
o.spy(o, "status", 20, "PutAttachment without doc id");
o.jio.putAttachment({}, o.f);
o.clock.tick(5000);
// putAttachment without attachment id => attachment id required
o.spy(o, "status", 22, "PutAttachment without attachment id");
o.jio.putAttachment({"id": "putattmt1"}, o.f);
o.clock.tick(5000);
// putAttachment without document id => 20 Id Required
o
.
spy
(
o
,
"
status
"
,
20
,
"
PutAttachment without doc id -> 20
"
);
o
.
jio
.
putAttachment
({
"
_attachment
"
:
"
body.html
"
},
o
.
f
);
o
.
tick
(
o
);
// putAttachment without underlying document => not found
o.addFakeServerResponse("dav", "GET", "putattmtx", 22, "HTML RESPONSE");
o.spy(o, "status", 22, "PutAttachment without document");
o.jio.putAttachment({"id": "putattmtx.putattmt2"}, o.f);
o.clock.tick(5000);
o.server.respond();
// putAttachment without attachment id => 22 Attachment Id Required
o
.
spy
(
o
,
"
status
"
,
22
,
"
PutAttachment without attachment id -> 22
"
);
o
.
jio
.
putAttachment
({
"
_id
"
:
"
http://100%.json
"
},
o
.
f
);
o
.
tick
(
o
);
// putAttachment with document without data
o.answer = JSON.stringify({"_id": "putattmt1", "title": "myPutAttm1"});
o.addFakeServerResponse("dav", "GET", "putattmt1", 200, o.answer);
o.addFakeServerResponse("dav", "PUT", "putattmt1", 201, "HTML RESPONSE");
o.addFakeServerResponse("dav", "PUT", "putattmt1.putattmt2", 201,"HTML"+
+ "RESPONSE");
o.spy(o, "value", {"ok": true, "id": "putattmt1/putattmt2"},
"PutAttachment with document, without data");
o.jio.putAttachment({"id": "putattmt1/putattmt2"}, o.f);
o.clock.tick(5000);
// putAttachment without underlying document => 404 Not Found
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
GET
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
404
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>Not Found</h1>
"
]
);
o
.
spy
(
o
,
"
status
"
,
404
,
"
PutAttachment without document -> 404
"
);
o
.
jio
.
putAttachment
({
"
_id
"
:
"
http://100%.json
"
,
"
_attachment
"
:
"
putattmt2
"
},
{
"
max_retry
"
:
1
},
o
.
f
);
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
o
.
tick
(
o
);
o
.
server
.
restore
();
// update attachment
o.answer = JSON.stringify({"_id": "putattmt1", "title": "myPutAttm1"});
o.addFakeServerResponse("dav", "GET", "putattmt1", 200, o.answer);
o.addFakeServerResponse("dav", "PUT", "putattmt1", 201, "HTML RESPONSE");
o.addFakeServerResponse("dav", "PUT", "putattmt1.putattmt2", 201,"HTML"+
"RESPONSE");
o.spy(o, "value", {"ok": true, "id": "putattmt1/putattmt2"},
"Update Attachment, with data");
o.jio.putAttachment({"id": "putattmt1/putattmt2", "data": "abc"}, o.f);
o.clock.tick(5000);
// upload attachment
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
GET
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
200
,
{
"
Content-Type
"
:
"
text/plain
"
},
'
{"_id":"http://100%.json","title":"Hi There!"}
'
]
);
o
.
server
.
respondWith
(
"
PUT
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json.body_
\.
html/
,
[
200
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>OK!</h1>
"
]
);
o
.
server
.
respondWith
(
"
PUT
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
200
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>OK!</h1>
"
]
);
o
.
spy
(
o
,
"
value
"
,
{
"
ok
"
:
true
,
"
id
"
:
"
http://100%.json
"
,
"
attachment
"
:
"
body.html
"
},
"
Upload attachment
"
);
o
.
jio
.
putAttachment
({
"
_id
"
:
"
http://100%.json
"
,
"
_attachment
"
:
"
body.html
"
,
"
_mimetype
"
:
"
text/html
"
,
"
_data
"
:
"
<h1>Hi There!!</h1><p>How are you?</p>
"
},
{
"
max_retry
"
:
1
},
o
.
f
);
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
o
.
tick
(
o
);
o
.
server
.
restore
();
o
.
jio
.
stop
();
});
...
...
@@ -3609,52 +3734,52 @@ test ("Get", function(){
o
.
jio
=
JIO
.
newJio
({
"
type
"
:
"
dav
"
,
"username": "davget",
"password": "checkpwd",
"url": "https://ca-davstorage:8080"
"
url
"
:
"
https://ca-davstorage:8080
"
,
"
auth_type
"
:
"
basic
"
,
"
username
"
:
"
admin
"
,
"
password
"
:
"
pwd
"
});
// get inexistent document
o.addFakeServerResponse("dav", "GET", "get1", 404, "HTML RESPONSE");
o.spy(o, "status", 404, "Get non existing document");
o.jio.get("get1", o.f);
o.clock.tick(5000);
o.server.respond();
// get inexistent attachment
o.addFakeServerResponse("dav", "GET", "get1.get2", 404, "HTML RESPONSE");
o.spy(o, "status", 404, "Get non existing attachment");
o.jio.get("get1/get2", o.f);
o.clock.tick(5000);
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
GET
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
404
,
{
"
Content-Type
"
:
"
text/html
"
},
"
<h1>Not Found</h1>
"
]
);
o
.
spy
(
o
,
"
status
"
,
404
,
"
Get non existing document -> 404
"
);
o
.
jio
.
get
({
"
_id
"
:
"
http://100%.json
"
},
{
"
max_retry
"
:
1
},
o
.
f
);
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
o
.
tick
(
o
);
o
.
server
.
restore
();
// get document
o.answer = JSON.stringify({"_id": "get3", "title": "some title"});
o.addFakeServerResponse("dav", "GET", "get3", 200, o.answer);
o.spy(o, "value", {"_id": "get3", "title": "some title"}, "Get document");
o.jio.get("get3", o.f);
o.clock.tick(5000);
o.server.respond();
// get inexistent attachment (document exists)
o.addFakeServerResponse("dav", "GET", "get3.getx", 404, "HTML RESPONSE");
o.spy(o, "status", 404, "Get non existing attachment (doc exists)");
o.jio.get("get3/getx", o.f);
o.clock.tick(5000);
o.server.respond();
// get attachment
o.answer = JSON.stringify({"_id": "get4", "title": "some attachment"});
o.addFakeServerResponse("dav", "GET", "get3.get4", 200, o.answer);
o.spy(o, "value", {"_id": "get4", "title": "some attachment"},
"Get attachment");
o.jio.get("get3/get4", o.f);
o.clock.tick(5000);
o
.
server
=
sinon
.
fakeServer
.
create
();
o
.
server
.
respondWith
(
"
GET
"
,
/https:
\/\/
ca-davstorage:8080
\/
http:%252F%252F100%2525_
\.
json/
,
[
200
,
{
"
Content-Type
"
:
"
text/html
"
},
'
{"_id":"http://100%.json","title":"Hi There!"}
'
]
);
o
.
spy
(
o
,
"
value
"
,
{
"
_id
"
:
"
http://100%.json
"
,
"
title
"
:
"
Hi There!
"
},
"
Get document
"
);
o
.
jio
.
get
({
"
_id
"
:
"
http://100%.json
"
},
{
"
max_retry
"
:
1
},
o
.
f
);
o
.
clock
.
tick
(
1000
);
o
.
server
.
respond
();
o
.
tick
(
o
);
o
.
server
.
restore
();
o
.
jio
.
stop
();
});
/*
test ("Remove", function(){
var o = generateTools(this);
...
...
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