Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
Conflicts: Documentation/DbaManual/Authentication.md Documentation/DbaManual/UserManagementTOC.md js/common/modules/org/arangodb/users.js
|
@ -19,7 +19,7 @@ BUILT_SOURCES += @V8_LIBS@
|
|||
@echo "--------------------------------------------------------------------------------"
|
||||
@echo
|
||||
|
||||
cd @top_srcdir@/3rdParty/V8 && $(MAKE) library=static strictaliasing=off snapshot=off @V8_TARGET@
|
||||
cd @top_srcdir@/3rdParty/V8 && $(MAKE) library=static strictaliasing=off snapshot=off werror=no @V8_TARGET@
|
||||
|
||||
touch @srcdir@/.v8-build-@TRI_BITS@
|
||||
|
||||
|
|
13
CHANGELOG
|
@ -1,6 +1,19 @@
|
|||
v1.2.beta3 (XXXX-XX-XX)
|
||||
-----------------------
|
||||
|
||||
* issue #393: added REST API for user management at /_api/user
|
||||
|
||||
* issue #393, #128: added simple cryptographic functions for user actions in module "crypto":
|
||||
* require("org/arangodb/crypto").md5()
|
||||
* require("org/arangodb/crypto").sha256()
|
||||
* require("org/arangodb/crypto").rand()
|
||||
|
||||
* added replaceByExample() Javascript and REST API method
|
||||
|
||||
* added updateByExample() Javascript and REST API method
|
||||
|
||||
* added optional "limit" parameter for removeByExample() Javascript and REST API method
|
||||
|
||||
* fixed issue #413
|
||||
|
||||
* updated bundled V8 version from 3.9.4 to 3.16.14.1
|
||||
|
|
|
@ -13,8 +13,8 @@ will allow the administrator to restrict access to collections and queries to
|
|||
certain users, given them either read or write access.
|
||||
|
||||
Currently, you can only secure the access to ArangoDB in an all-or-nothing
|
||||
fashion. The collection `_users` contains all user and the SHA256 of their
|
||||
passwords. A user can be active or inactive. A typical document of this
|
||||
fashion. The collection `_users` contains all users and a salted SHA256 hash
|
||||
of their passwords. A user can be active or inactive. A typical document of this
|
||||
collection is
|
||||
|
||||
@EXAMPLE_ARANGOSH_OUTPUT{AuthenticationExample1}
|
||||
|
@ -40,10 +40,18 @@ web interface.
|
|||
@anchor UserManagementSave
|
||||
@copydetails JSF_saveUser
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementDocument
|
||||
@copydetails JSF_documentUser
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementReplace
|
||||
@copydetails JSF_replaceUser
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementUpdate
|
||||
@copydetails JSF_updateUser
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor UserManagementRemove
|
||||
@copydetails JSF_removeUser
|
||||
|
|
|
@ -6,6 +6,7 @@ TOC {#DbaManualAuthenticationTOC}
|
|||
- @ref DbaManualAuthenticationCommandLine
|
||||
- @ref UserManagementIntro
|
||||
- @ref UserManagementSave "users.save"
|
||||
- @ref UserManagementDocument "users.document"
|
||||
- @ref UserManagementReplace "users.replace"
|
||||
- @ref UserManagementRemove "users.remove"
|
||||
- @ref UserManagementReload "users.reload"
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
> curl --data @- -X PUT --dump - http://localhost:8529/_api/simple/replace-by-example
|
||||
{ "collection" : "test", "example" : { "age" : 37, "likes" : "tennis" }, "newValue" : { "foo" : "bar" }, "limit" : 3 }
|
||||
|
||||
HTTP/1.1 200 Ok
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"code": 200,
|
||||
"replaced": 1,
|
||||
"error": false
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
> curl --data @- -X PUT --dump - http://localhost:8529/_api/simple/update-by-example
|
||||
{ "collection" : "test", "example" : { "age" : 37, "likes" : "tennis" }, "newValue" : { "age" : null, "likes" : "foo" }, "keepNull" : false }
|
||||
|
||||
HTTP/1.1 200 Ok
|
||||
content-type: application/json
|
||||
|
||||
{
|
||||
"code": 200,
|
||||
"updated": 1,
|
||||
"error": false
|
||||
}
|
|
@ -65,3 +65,11 @@ dispose the server-side cursor afterwards.
|
|||
@CLEARPAGE
|
||||
@anchor HttpSimpleRemoveByExample
|
||||
@copydetails JSA_PUT_api_simple_remove_by_example
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HttpSimpleReplaceByExample
|
||||
@copydetails JSA_PUT_api_simple_replace_by_example
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HttpSimpleUpdateByExample
|
||||
@copydetails JSA_PUT_api_simple_update_by_example
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
HTTP Interface for User Management {#HttpUser}
|
||||
==============================================
|
||||
|
||||
@NAVIGATE_HttpUser
|
||||
@EMBEDTOC{HttpUserTOC}
|
||||
|
||||
User Management {#HttpUserIntro}
|
||||
================================
|
||||
|
||||
This is an introduction to ArangoDB's Http interface for managing users.
|
||||
|
||||
The interface provides a simple means to add, update, and remove users.
|
||||
All users managed through this interface will be stored in the system
|
||||
collection `_users`.
|
||||
|
||||
This specialised interface intentionally does not provide all functionality
|
||||
that is available in the regular document REST API.
|
||||
|
||||
Operations on users may become more restricted than regular document operations,
|
||||
and extra privilege and security security checks may be introduced in the
|
||||
future for this interface.
|
||||
|
||||
@anchor HttpUserSave
|
||||
@copydetails JSF_POST_api_user
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HttpUserReplace
|
||||
@copydetails JSF_PUT_api_user
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HttpUserUpdate
|
||||
@copydetails JSF_PATCH_api_user
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HttpUserRemove
|
||||
@copydetails JSF_DELETE_api_user
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HttpUserDocument
|
||||
@copydetails JSF_GET_api_user
|
|
@ -0,0 +1,10 @@
|
|||
TOC {#HttpUserTOC}
|
||||
===================
|
||||
|
||||
- @ref HttpUser
|
||||
- @ref HttpUserIntro
|
||||
- @ref HttpUserSave "POST /_api/user"
|
||||
- @ref HttpUserReplace "PUT /_api/user/username"
|
||||
- @ref HttpUserUpdate "PATCH /_api/user/username"
|
||||
- @ref HttpUserRemove "DELETE /_api/user/username"
|
||||
- @ref HttpUserDocument "GET /_api/user/username"
|
|
@ -19,6 +19,7 @@ ArangoDB for API Implementors (@VERSION) {#ImplementorManual}
|
|||
@CHAPTER_REF{HttpImport}
|
||||
@CHAPTER_REF{HttpBatch}
|
||||
@CHAPTER_REF{HttpSystem}
|
||||
@CHAPTER_REF{HttpUser}
|
||||
@CHAPTER_REF{HttpMisc}
|
||||
@CHAPTER_REF{Communication}
|
||||
@CHAPTER_REF{NamingConventions}
|
||||
|
|
|
@ -49,6 +49,7 @@ DOXYGEN = \
|
|||
Doxygen/js/actions/system/api-query.c \
|
||||
Doxygen/js/actions/system/api-simple.c \
|
||||
Doxygen/js/actions/system/api-system.c \
|
||||
Doxygen/js/actions/system/api-user.c \
|
||||
Doxygen/js/common/bootstrap/module-console.c \
|
||||
Doxygen/js/common/bootstrap/module-fs.c \
|
||||
Doxygen/js/common/bootstrap/modules.c \
|
||||
|
@ -98,6 +99,7 @@ WIKI = \
|
|||
HttpQuery \
|
||||
HttpSimple \
|
||||
HttpSystem \
|
||||
HttpUser \
|
||||
ImpManual \
|
||||
ImpManualBasics \
|
||||
ImplementorManual \
|
||||
|
|
|
@ -320,8 +320,22 @@ The `removeByExample` method returns the number of documents removed:
|
|||
arangosh> db.mycollection.removeByExample({ value: 1 });
|
||||
2
|
||||
|
||||
The number of documents to remove can optionally be limited.
|
||||
|
||||
The method is also available via the REST API (@ref HttpSimple).
|
||||
|
||||
### Replace / Update by Example
|
||||
|
||||
ArangoDB 1.2 also provides "Replace by Example" and "Update by Example" methods that
|
||||
can be used to fully or partially update documents from a collection that match the
|
||||
specified example. This can be used to replace document values easily with a single
|
||||
operation.
|
||||
|
||||
The `replaceByExample` and `updateByExaple` methods return the number of documents
|
||||
modified. Both operations can be limited to a specific number of documents if required.
|
||||
|
||||
Both methods are also available via the REST API (@ref HttpSimple).
|
||||
|
||||
### Collection revision id
|
||||
|
||||
ArangoDB 1.2 collections have a revision property. The current revision of a collection
|
||||
|
|
|
@ -89,6 +89,14 @@ Collection Methods {#HandlingDocumentsCollectionMethods}
|
|||
@anchor HandlingDocumentsRemoveByExample
|
||||
@copydetails JSF_ArangoCollection_prototype_removeByExample
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HandlingDocumentsReplaceByExample
|
||||
@copydetails JSF_ArangoCollection_prototype_replaceByExample
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HandlingDocumentsUpdateByExample
|
||||
@copydetails JSF_ArangoCollection_prototype_updateByExample
|
||||
|
||||
@CLEARPAGE
|
||||
Database Methods {#HandlingDocumentsDatabaseMethods}
|
||||
----------------------------------------------------
|
||||
|
|
|
@ -13,6 +13,8 @@ TOC {#HandlingDocumentsTOC}
|
|||
- @ref HandlingDocumentsUpdate "collection.update"
|
||||
- @ref HandlingDocumentsRemove "collection.remove"
|
||||
- @ref HandlingDocumentsRemoveByExample "collection.removeByExample"
|
||||
- @ref HandlingDocumentsReplaceByExample "collection.replaceByExample"
|
||||
- @ref HandlingDocumentsUpdateByExample "collection.updateByExample"
|
||||
- @ref HandlingDocumentsDatabaseMethods
|
||||
- @ref HandlingDocumentsDbRead "db._document"
|
||||
- @ref HandlingDocumentsDbReplace "db._replace"
|
||||
|
|
|
@ -221,12 +221,23 @@ Sequential Access and Cursors {#SimpleQueriesCursor}
|
|||
Modification Queries {#SimpleQueriesModify}
|
||||
===========================================
|
||||
|
||||
ArangoDB also allows removing documents based on an example document. Every
|
||||
document in the collection will be compared against the specified example
|
||||
document and be deleted if all attributes match.
|
||||
ArangoDB also allows removing, replacing, and updating documents based
|
||||
on an example document. Every document in the collection will be
|
||||
compared against the specified example document and be deleted/replaced/
|
||||
updated if all attributes match.
|
||||
|
||||
This method should be used with caution as it intended to remove lots of
|
||||
documents from a collection.
|
||||
These method should be used with caution as they are intended to remove or
|
||||
modify lots of documents in a collection.
|
||||
|
||||
All methods can optionally be restricted to a specific number of operations.
|
||||
However, if a limit is specific but is less than the number of matches, it
|
||||
will be undefined which of the matching documents will get removed/modified.
|
||||
|
||||
@anchor SimpleQueryRemoveByExample
|
||||
@copydetails JSF_ArangoCollection_prototype_removeByExample
|
||||
|
||||
@anchor SimpleQueryReplaceByExample
|
||||
@copydetails JSF_ArangoCollection_prototype_replaceByExample
|
||||
|
||||
@anchor SimpleQueryUpdateByExample
|
||||
@copydetails JSF_ArangoCollection_prototype_updateByExample
|
||||
|
|
|
@ -29,3 +29,5 @@ TOC {#SimpleQueriesTOC}
|
|||
- @ref SimpleQueryCount "query.count"
|
||||
- @ref SimpleQueriesModify
|
||||
- @ref SimpleQueryRemoveByExample "collection.removeByExample"
|
||||
- @ref SimpleQueryReplaceByExample "collection.replaceByExample"
|
||||
- @ref SimpleQueryUpdateByExample "collection.updateByExample"
|
||||
|
|
|
@ -62,35 +62,30 @@ case $TRI_OS_LONG in
|
|||
echo "Using configuration for Arch Linux"
|
||||
OPTIONS="$OPTIONS --disable-mruby"
|
||||
LDD_INFO="yes"
|
||||
RESULTS="$RESULTS arangoirb"
|
||||
;;
|
||||
|
||||
Linux-LinuxMint-13*)
|
||||
echo "Using configuration for LinuxMint 13"
|
||||
OPTIONS="$OPTIONS --disable-mruby"
|
||||
LDD_INFO="yes"
|
||||
RESULTS="$RESULTS arangoirb"
|
||||
;;
|
||||
|
||||
Linux-openSUSE-12*)
|
||||
echo "Using configuration for openSuSE 12.X"
|
||||
OPTIONS="$OPTIONS --disable-mruby "
|
||||
LDD_INFO="yes"
|
||||
RESULTS="$RESULTS arangoirb"
|
||||
;;
|
||||
|
||||
Linux-openSUSE-11*)
|
||||
echo "Using configuration for openSuSE 11.X"
|
||||
OPTIONS="$OPTIONS --disable-mruby"
|
||||
LDD_INFO="yes"
|
||||
RESULTS="$RESULTS arangoirb"
|
||||
;;
|
||||
|
||||
Linux-Debian-6*)
|
||||
echo "Using configuration for Debian"
|
||||
OPTIONS="$OPTIONS --enable-all-in-one-libev --enable-all-in-one-v8 --disable-mruby"
|
||||
LDD_INFO="yes"
|
||||
RESULTS="$RESULTS arangoirb"
|
||||
;;
|
||||
|
||||
Linux-Debian*)
|
||||
|
@ -117,19 +112,17 @@ case $TRI_OS_LONG in
|
|||
echo "Using configuration for Ubuntu"
|
||||
OPTIONS="$OPTIONS --enable-all-in-one-libev --enable-all-in-one-v8 --disable-mruby"
|
||||
LDD_INFO="yes"
|
||||
RESULTS="$RESULTS arangoirb"
|
||||
;;
|
||||
|
||||
Darwin*)
|
||||
echo "Using configuration for DARWIN"
|
||||
CPPFLAGS='-isystem /usr/include -isystem /opt/local/include -Wno-deprecated-declarations'
|
||||
LDFLAGS='-L/usr/lib -L/opt/local/lib' # need to use OpenSSL from system
|
||||
LDFLAGS='-L/usr/lib '
|
||||
OPTIONS="$OPTIONS --enable-all-in-one-libev --enable-all-in-one-v8 --enable-all-in-one-icu --disable-mruby"
|
||||
RESULTS="$RESULTS arangoirb"
|
||||
if [ "${TRI_MACH}" == "x86_64" ]; then
|
||||
X=$(uname -r)
|
||||
OPTIONS="$OPTIONS --build x86_64-apple-darwin${X}"
|
||||
fi
|
||||
# we need 64 bits
|
||||
TRI_MACH="x86_64"
|
||||
X=$(uname -r)
|
||||
OPTIONS="$OPTIONS --build x86_64-apple-darwin${X}"
|
||||
;;
|
||||
|
||||
*)
|
||||
|
|
|
@ -310,13 +310,8 @@ export systemddir
|
|||
echo
|
||||
echo "########################################################"
|
||||
echo "Call EPM to build the package."
|
||||
if [ "$TRI_MACH" == "amd64" ] ; then
|
||||
echo " sudo -E epm -a ${EPM_ARCH} -f ${package_type} ${product_name} ${sfolder_name}/${LIST}"
|
||||
sudo -E epm -a ${TRI_MACH} -f ${package_type} ${product_name} ${sfolder_name}/${LIST} || exit 1
|
||||
else
|
||||
echo " sudo -E epm -f ${package_type} ${product_name} ${sfolder_name}/${LIST}"
|
||||
sudo -E epm -f ${package_type} ${product_name} ${sfolder_name}/${LIST} || exit 1
|
||||
fi
|
||||
echo " sudo -E epm -a ${EPM_MACH} -f ${package_type} ${product_name} ${sfolder_name}/${LIST}"
|
||||
sudo -E epm -a ${TRI_MACH} -f ${package_type} ${product_name} ${sfolder_name}/${LIST} || exit 1
|
||||
echo "########################################################"
|
||||
echo
|
||||
|
||||
|
|
|
@ -405,7 +405,7 @@ describe ArangoDB do
|
|||
@cid = ArangoDB.create_collection(@cn, false)
|
||||
|
||||
(0...20).each{|i|
|
||||
ArangoDB.post("/_api/document?collection=#{@cn}", :body => "{ \"value\" : #{i} }")
|
||||
ArangoDB.post("/_api/document?collection=#{@cn}", :body => "{ \"value\" : #{i}, \"value2\" : 99 }")
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -418,15 +418,15 @@ describe ArangoDB do
|
|||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 1 } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-remove-by-example", cmd, :body => body)
|
||||
|
||||
# remove first
|
||||
# remove first
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['deleted'].should eq(1)
|
||||
|
||||
# remove again
|
||||
doc = ArangoDB.log_put("#{prefix}-remove-by-example", cmd, :body => body)
|
||||
# remove again
|
||||
doc = ArangoDB.log_put("#{prefix}-remove-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
|
@ -532,6 +532,238 @@ describe ArangoDB do
|
|||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['deleted'].should eq(0)
|
||||
end
|
||||
|
||||
it "removes the examples, with limit" do
|
||||
cmd = api + "/remove-by-example"
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"limit\" : 5 }"
|
||||
doc = ArangoDB.log_put("#{prefix}-remove-by-example-limit", cmd, :body => body)
|
||||
|
||||
# remove some
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['deleted'].should eq(5)
|
||||
|
||||
# remove some more
|
||||
doc = ArangoDB.log_put("#{prefix}-remove-by-example-limit", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['deleted'].should eq(5)
|
||||
|
||||
# remove the rest
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"limit\" : 50 }"
|
||||
doc = ArangoDB.log_put("#{prefix}-remove-by-example-limit", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['deleted'].should eq(10)
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## replace-by-example query
|
||||
################################################################################
|
||||
|
||||
context "replace-by-example query:" do
|
||||
before do
|
||||
@cn = "UnitTestsCollectionByExample"
|
||||
ArangoDB.drop_collection(@cn)
|
||||
@cid = ArangoDB.create_collection(@cn, false)
|
||||
|
||||
(0...20).each{|i|
|
||||
ArangoDB.post("/_api/document?collection=#{@cn}", :body => "{ \"value\" : #{i}, \"value2\" : 99 }")
|
||||
}
|
||||
end
|
||||
|
||||
after do
|
||||
ArangoDB.drop_collection(@cn)
|
||||
end
|
||||
|
||||
it "replaces the examples" do
|
||||
cmd = api + "/replace-by-example"
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 1 }, \"newValue\" : { \"foo\" : \"bar\" } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace-by-example", cmd, :body => body)
|
||||
|
||||
# replace one
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['replaced'].should eq(1)
|
||||
|
||||
# replace other
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 2 }, \"newValue\" : { \"foo\" : \"baz\" } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['replaced'].should eq(1)
|
||||
|
||||
# replace all others
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"moo\" : \"fox\" } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['replaced'].should eq(18)
|
||||
|
||||
# remove non-existing values
|
||||
[ 21, 22, 100, 101, 99, "\"meow\"", "\"\"", "\"null\"" ].each{|value|
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : " + value.to_s + " }, \"newValue\" : { } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['replaced'].should eq(0)
|
||||
}
|
||||
end
|
||||
|
||||
it "replaces the examples, with limit" do
|
||||
cmd = api + "/replace-by-example"
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"foo\" : \"bar\" }, \"limit\" : 5 }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace-by-example-limit", cmd, :body => body)
|
||||
|
||||
# replace some
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['replaced'].should eq(5)
|
||||
|
||||
# replace some more
|
||||
doc = ArangoDB.log_put("#{prefix}-replace-by-example-limit", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['replaced'].should eq(5)
|
||||
|
||||
# replace the rest
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"fox\" : \"box\" }, \"limit\" : 50 }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace-by-example-limit", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['replaced'].should eq(10)
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## update-by-example query
|
||||
################################################################################
|
||||
|
||||
context "update-by-example query:" do
|
||||
before do
|
||||
@cn = "UnitTestsCollectionByExample"
|
||||
ArangoDB.drop_collection(@cn)
|
||||
@cid = ArangoDB.create_collection(@cn, false)
|
||||
|
||||
(0...20).each{|i|
|
||||
ArangoDB.post("/_api/document?collection=#{@cn}", :body => "{ \"value\" : #{i}, \"value2\" : 99 }")
|
||||
}
|
||||
end
|
||||
|
||||
after do
|
||||
ArangoDB.drop_collection(@cn)
|
||||
end
|
||||
|
||||
it "updates the examples" do
|
||||
cmd = api + "/update-by-example"
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 1 }, \"newValue\" : { \"foo\" : \"bar\" } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
|
||||
|
||||
# update one
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(1)
|
||||
|
||||
# update other
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 2 }, \"newValue\" : { \"foo\" : \"baz\" } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(1)
|
||||
|
||||
# update other, overwrite
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 3 }, \"newValue\" : { \"foo\" : \"baz\", \"value\" : 12 } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(1)
|
||||
|
||||
# update other, remove
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : 12 }, \"newValue\" : { \"value2\" : null }, \"keepNull\" : false }"
|
||||
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(2)
|
||||
|
||||
# update all but the 2 from before
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"moo\" : \"fox\" } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(18)
|
||||
|
||||
# update non-existing values
|
||||
[ 100, 101, 99, "\"meow\"", "\"\"", "\"null\"" ].each{|value|
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value\" : " + value.to_s + " }, \"newValue\" : { } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-update-by-example", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(0)
|
||||
}
|
||||
end
|
||||
|
||||
it "updates the examples, with limit" do
|
||||
cmd = api + "/update-by-example"
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"foo\" : \"bar\", \"value2\" : 17 }, \"limit\" : 5 }"
|
||||
doc = ArangoDB.log_put("#{prefix}-update-by-example-limit", cmd, :body => body)
|
||||
|
||||
# update some
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(5)
|
||||
|
||||
# update some more
|
||||
doc = ArangoDB.log_put("#{prefix}-update-by-example-limit", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(5)
|
||||
|
||||
# update the rest
|
||||
body = "{ \"collection\" : \"#{@cn}\", \"example\" : { \"value2\" : 99 }, \"newValue\" : { \"fox\" : \"box\" }, \"limit\" : 50 }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace-by-example-limit", cmd, :body => body)
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['updated'].should eq(10)
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
|
|
|
@ -0,0 +1,420 @@
|
|||
# coding: utf-8
|
||||
|
||||
require 'rspec'
|
||||
require './arangodb.rb'
|
||||
|
||||
describe ArangoDB do
|
||||
api = "/_api/user"
|
||||
prefix = "api-users"
|
||||
|
||||
context "user management:" do
|
||||
|
||||
before do
|
||||
(0...10).each{|i|
|
||||
ArangoDB.delete("/_api/user/users-" + i.to_s);
|
||||
}
|
||||
end
|
||||
|
||||
after do
|
||||
(0...10).each{|i|
|
||||
ArangoDB.delete("/_api/user/users-" + i.to_s);
|
||||
}
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## adding users
|
||||
################################################################################
|
||||
|
||||
context "adding users" do
|
||||
|
||||
it "add user, no username" do
|
||||
body = "{ \"passwd\" : \"fox\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['errorNum'].should eq(1700)
|
||||
end
|
||||
|
||||
it "add user, empty username" do
|
||||
body = "{ \"username\" : \"\", \"passwd\" : \"fox\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['errorNum'].should eq(1700)
|
||||
end
|
||||
|
||||
it "add user, no passwd" do
|
||||
body = "{ \"username\" : \"users-1\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(201)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-1")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-1")
|
||||
doc.parsed_response['active'].should eq(true)
|
||||
end
|
||||
|
||||
it "add user, username and passwd" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(201)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-1")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-1")
|
||||
doc.parsed_response['active'].should eq(true)
|
||||
end
|
||||
|
||||
it "add user, username passwd, active, extra" do
|
||||
body = "{ \"username\" : \"users-2\", \"passwd\" : \"fox\", \"active\" : false, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(201)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-2")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-2")
|
||||
doc.parsed_response['active'].should eq(false)
|
||||
doc.parsed_response['extra'].should eq({ "foo" => true })
|
||||
end
|
||||
|
||||
it "add user, duplicate username" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(201)
|
||||
|
||||
doc = ArangoDB.log_post("#{prefix}-add", api, :body => body)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['errorNum'].should eq(1702)
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## replacing users
|
||||
################################################################################
|
||||
|
||||
context "replacing users" do
|
||||
it "replace, no user" do
|
||||
doc = ArangoDB.log_put("#{prefix}-replace", api)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
end
|
||||
|
||||
it "replace non-existing user" do
|
||||
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
end
|
||||
|
||||
it "replace already removed user" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-replace", api, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# remove
|
||||
doc = ArangoDB.log_delete("#{prefix}-replace", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(202)
|
||||
|
||||
# now replace
|
||||
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
end
|
||||
|
||||
it "replace, empty body" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-replace", api, :body => body)
|
||||
|
||||
# replace
|
||||
body = "{ }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1", :body => body)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-1")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-1")
|
||||
doc.parsed_response['active'].should eq(true)
|
||||
end
|
||||
|
||||
it "replace existing user, no passwd" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-replace", api, :body => body)
|
||||
|
||||
# replace
|
||||
body = "{ \"active\" : false, \"extra\" : { \"foo\" : false } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1", :body => body)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-1")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-1")
|
||||
doc.parsed_response['active'].should eq(false)
|
||||
doc.parsed_response['extra'].should eq({ "foo" => false })
|
||||
end
|
||||
|
||||
it "replace existing user" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-replace", api, :body => body)
|
||||
|
||||
# replace
|
||||
body = "{ \"passwd\" : \"fox2\", \"active\" : false, \"extra\" : { \"foo\" : false } }"
|
||||
doc = ArangoDB.log_put("#{prefix}-replace", api + "/users-1", :body => body)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-1")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-1")
|
||||
doc.parsed_response['active'].should eq(false)
|
||||
doc.parsed_response['extra'].should eq({ "foo" => false })
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## updating users
|
||||
################################################################################
|
||||
|
||||
context "updating users" do
|
||||
it "update, no user" do
|
||||
doc = ArangoDB.log_patch("#{prefix}-update", api)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
end
|
||||
|
||||
it "update non-existing user" do
|
||||
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
end
|
||||
|
||||
it "update already removed user" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update", api, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# remove
|
||||
doc = ArangoDB.log_delete("#{prefix}-update", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(202)
|
||||
|
||||
# now update
|
||||
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
end
|
||||
|
||||
it "update, empty body" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update", api, :body => body)
|
||||
|
||||
# update
|
||||
body = "{ }"
|
||||
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1", :body => body)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-1")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-1")
|
||||
doc.parsed_response['active'].should eq(true)
|
||||
doc.parsed_response['extra'].should eq({ "foo" => true })
|
||||
end
|
||||
|
||||
it "update existing user, no passwd" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update", api, :body => body)
|
||||
|
||||
# update
|
||||
body = "{ \"active\" : false, \"extra\" : { \"foo\" : false } }"
|
||||
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1", :body => body)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-1")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-1")
|
||||
doc.parsed_response['active'].should eq(false)
|
||||
doc.parsed_response['extra'].should eq({ "foo" => false })
|
||||
end
|
||||
|
||||
it "update existing user" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-update", api, :body => body)
|
||||
|
||||
# update
|
||||
body = "{ \"passwd\" : \"fox2\", \"active\" : false }"
|
||||
doc = ArangoDB.log_patch("#{prefix}-update", api + "/users-1", :body => body)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-1")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-1")
|
||||
doc.parsed_response['active'].should eq(false)
|
||||
doc.parsed_response['extra'].should eq({ "foo" => true })
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## removing users
|
||||
################################################################################
|
||||
|
||||
context "removing users" do
|
||||
it "remove, no user" do
|
||||
doc = ArangoDB.log_delete("#{prefix}-remove", api)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
end
|
||||
|
||||
it "remove non-existing user" do
|
||||
doc = ArangoDB.log_delete("#{prefix}-remove", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
end
|
||||
|
||||
it "remove already removed user" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-delete", api, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
|
||||
# remove for the first time
|
||||
doc = ArangoDB.log_delete("#{prefix}-remove", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(202)
|
||||
|
||||
# remove again
|
||||
doc = ArangoDB.log_delete("#{prefix}-remove", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
end
|
||||
|
||||
it "remove existing user" do
|
||||
body = "{ \"username\" : \"users-1\", \"passwd\" : \"fox\", \"active\" : true, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-delete", api, :body => body)
|
||||
|
||||
# remove
|
||||
doc = ArangoDB.log_delete("#{prefix}-remove", api + "/users-1")
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(202)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## fetching users
|
||||
################################################################################
|
||||
|
||||
context "fetching users" do
|
||||
|
||||
it "no user specified" do
|
||||
doc = ArangoDB.log_get("#{prefix}-fetch", api)
|
||||
|
||||
doc.code.should eq(400)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(400)
|
||||
doc.parsed_response['errorNum'].should eq(400)
|
||||
end
|
||||
|
||||
it "fetch non-existing user" do
|
||||
doc = ArangoDB.log_get("#{prefix}-fetch", api + "/users-16")
|
||||
|
||||
doc.code.should eq(404)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
doc.parsed_response['errorNum'].should eq(1703)
|
||||
end
|
||||
|
||||
it "fetch user" do
|
||||
body = "{ \"username\" : \"users-2\", \"passwd\" : \"fox\", \"active\" : false, \"extra\" : { \"foo\" : true } }"
|
||||
doc = ArangoDB.log_post("#{prefix}-fetch", api, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(201)
|
||||
|
||||
doc = ArangoDB.get(api + "/users-2")
|
||||
doc.code.should eq(200)
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(200)
|
||||
doc.parsed_response['user'].should eq("users-2")
|
||||
doc.parsed_response['active'].should eq(false)
|
||||
doc.parsed_response['extra'].should eq({ "foo" => true })
|
||||
doc.parsed_response.should_not have_key("passwd")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -21,4 +21,5 @@ rspec --color --format d \
|
|||
api-explain-spec.rb \
|
||||
api-cursor-spec.rb \
|
||||
api-statistics-spec.rb \
|
||||
api-simple-spec.rb
|
||||
api-simple-spec.rb \
|
||||
api-users-spec.rb
|
||||
|
|
|
@ -214,6 +214,7 @@ SHELL_COMMON = @top_srcdir@/js/common/tests/shell-document.js \
|
|||
@top_srcdir@/js/common/tests/shell-compactor.js \
|
||||
@top_srcdir@/js/common/tests/shell-simple-query.js \
|
||||
@top_srcdir@/js/common/tests/shell-statement.js \
|
||||
@top_srcdir@/js/common/tests/shell-crypto.js \
|
||||
@top_srcdir@/js/common/tests/shell-users.js \
|
||||
@top_srcdir@/js/common/tests/shell-index.js \
|
||||
@top_srcdir@/js/common/tests/shell-index-geo.js \
|
||||
|
|
|
@ -336,7 +336,7 @@ bool RestDocumentHandler::createDocument () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool RestDocumentHandler::readDocument () {
|
||||
size_t len = _request->suffix().size();
|
||||
const size_t len = _request->suffix().size();
|
||||
|
||||
switch (len) {
|
||||
case 0:
|
||||
|
@ -403,10 +403,6 @@ bool RestDocumentHandler::readDocument () {
|
|||
bool RestDocumentHandler::readSingleDocument (bool generateBody) {
|
||||
vector<string> const& suffix = _request->suffix();
|
||||
|
||||
/// check for an etag
|
||||
TRI_voc_rid_t ifNoneRid = extractRevision("if-none-match", 0);
|
||||
TRI_voc_rid_t ifRid = extractRevision("if-match", "rev");
|
||||
|
||||
// split the document reference
|
||||
const string& collection = suffix[0];
|
||||
const string& key = suffix[1];
|
||||
|
@ -451,7 +447,10 @@ bool RestDocumentHandler::readSingleDocument (bool generateBody) {
|
|||
assert(document);
|
||||
assert(document->_key);
|
||||
|
||||
TRI_voc_rid_t rid = document->_rid;
|
||||
const TRI_voc_rid_t rid = document->_rid;
|
||||
// check for an etag
|
||||
const TRI_voc_rid_t ifNoneRid = extractRevision("if-none-match", 0);
|
||||
const TRI_voc_rid_t ifRid = extractRevision("if-match", "rev");
|
||||
|
||||
if (ifNoneRid == 0) {
|
||||
if (ifRid == 0 || ifRid == rid) {
|
||||
|
@ -772,10 +771,11 @@ bool RestDocumentHandler::modifyDocument (bool isPatch) {
|
|||
}
|
||||
|
||||
// extract the revision
|
||||
TRI_voc_rid_t revision = extractRevision("if-match", "rev");
|
||||
const TRI_voc_rid_t revision = extractRevision("if-match", "rev");
|
||||
|
||||
// extract or chose the update policy
|
||||
TRI_doc_update_policy_e policy = extractUpdatePolicy();
|
||||
const TRI_doc_update_policy_e policy = extractUpdatePolicy();
|
||||
const bool waitForSync = extractWaitForSync();
|
||||
|
||||
TRI_doc_mptr_t* document = 0;
|
||||
|
||||
|
@ -839,13 +839,13 @@ bool RestDocumentHandler::modifyDocument (bool isPatch) {
|
|||
|
||||
if (holder.registerJson(TRI_UNKNOWN_MEM_ZONE, patchedJson)) {
|
||||
// do not acquire an extra lock
|
||||
res = trx.updateDocument(key, &document, patchedJson, policy, extractWaitForSync(), revision, &rid, false);
|
||||
res = trx.updateDocument(key, &document, patchedJson, policy, waitForSync, revision, &rid, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// replacing an existing document, using a lock
|
||||
res = trx.updateDocument(key, &document, json, policy, extractWaitForSync(), revision, &rid, true);
|
||||
res = trx.updateDocument(key, &document, json, policy, waitForSync, revision, &rid, true);
|
||||
}
|
||||
|
||||
res = trx.finish(res);
|
||||
|
@ -934,10 +934,11 @@ bool RestDocumentHandler::deleteDocument () {
|
|||
const string& key = suffix[1];
|
||||
|
||||
// extract the revision
|
||||
TRI_voc_rid_t revision = extractRevision("if-match", "rev");
|
||||
const TRI_voc_rid_t revision = extractRevision("if-match", "rev");
|
||||
|
||||
// extract or choose the update policy
|
||||
TRI_doc_update_policy_e policy = extractUpdatePolicy();
|
||||
const TRI_doc_update_policy_e policy = extractUpdatePolicy();
|
||||
const bool waitForSync = extractWaitForSync();
|
||||
|
||||
if (policy == TRI_DOC_UPDATE_ILLEGAL) {
|
||||
generateError(HttpResponse::BAD,
|
||||
|
@ -961,7 +962,7 @@ bool RestDocumentHandler::deleteDocument () {
|
|||
const TRI_voc_cid_t cid = trx.cid();
|
||||
|
||||
TRI_voc_rid_t rid = 0;
|
||||
res = trx.deleteDocument(key, policy, extractWaitForSync(), revision, &rid);
|
||||
res = trx.deleteDocument(key, policy, waitForSync, revision, &rid);
|
||||
if (res == TRI_ERROR_NO_ERROR) {
|
||||
res = trx.commit();
|
||||
}
|
||||
|
|
|
@ -207,9 +207,9 @@ void RestVocbaseBaseHandler::generate20x (const HttpResponse::HttpResponseCode r
|
|||
if (responseCode != HttpResponse::OK) {
|
||||
// 200 OK is sent is case of delete or update.
|
||||
// in these cases we do not return etag nor location
|
||||
_response->setHeader("ETag", "\"" + rev + "\"");
|
||||
_response->setHeader("etag", 4, "\"" + rev + "\"");
|
||||
// handle does not need to be RFC 2047-encoded
|
||||
_response->setHeader("location", DOCUMENT_PATH + "/" + handle);
|
||||
_response->setHeader("location", 8, DOCUMENT_PATH + "/" + handle);
|
||||
}
|
||||
|
||||
// _id and _key are safe and do not need to be JSON-encoded
|
||||
|
@ -297,7 +297,7 @@ void RestVocbaseBaseHandler::generateNotModified (const TRI_voc_rid_t rid) {
|
|||
const string rev = StringUtils::itoa(rid);
|
||||
|
||||
_response = createResponse(HttpResponse::NOT_MODIFIED);
|
||||
_response->setHeader("ETag", "\"" + rev + "\"");
|
||||
_response->setHeader("etag", 4, "\"" + rev + "\"");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -315,13 +315,10 @@ void RestVocbaseBaseHandler::generateDocument (const TRI_voc_cid_t cid,
|
|||
return;
|
||||
}
|
||||
|
||||
// add document identifier to buffer
|
||||
TRI_string_buffer_t buffer;
|
||||
|
||||
string id = DocumentHelper::assembleDocumentId(_resolver.getCollectionName(cid), document->_key);
|
||||
const string id = DocumentHelper::assembleDocumentId(_resolver.getCollectionName(cid), document->_key);
|
||||
|
||||
TRI_json_t augmented;
|
||||
TRI_InitArrayJson(TRI_UNKNOWN_MEM_ZONE, &augmented);
|
||||
TRI_Init2ArrayJson(TRI_UNKNOWN_MEM_ZONE, &augmented, 8);
|
||||
|
||||
TRI_json_t* _id = TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, id.c_str());
|
||||
|
||||
|
@ -330,7 +327,7 @@ void RestVocbaseBaseHandler::generateDocument (const TRI_voc_cid_t cid,
|
|||
}
|
||||
|
||||
// convert rid from uint64_t to string
|
||||
string rid = StringUtils::itoa(document->_rid);
|
||||
const string rid = StringUtils::itoa(document->_rid);
|
||||
TRI_json_t* _rev = TRI_CreateString2CopyJson(TRI_UNKNOWN_MEM_ZONE, rid.c_str(), rid.size());
|
||||
|
||||
if (_rev) {
|
||||
|
@ -354,6 +351,9 @@ void RestVocbaseBaseHandler::generateDocument (const TRI_voc_cid_t cid,
|
|||
TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, &augmented, "_to", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, to.c_str()));
|
||||
}
|
||||
|
||||
// add document identifier to buffer
|
||||
TRI_string_buffer_t buffer;
|
||||
|
||||
// convert object to string
|
||||
TRI_InitStringBuffer(&buffer, TRI_UNKNOWN_MEM_ZONE);
|
||||
|
||||
|
@ -378,7 +378,7 @@ void RestVocbaseBaseHandler::generateDocument (const TRI_voc_cid_t cid,
|
|||
// and generate a response
|
||||
_response = createResponse(HttpResponse::OK);
|
||||
_response->setContentType("application/json; charset=utf-8");
|
||||
_response->setHeader("ETag", "\"" + StringUtils::itoa(document->_rid) + "\"");
|
||||
_response->setHeader("etag", 4, "\"" + rid + "\"");
|
||||
|
||||
if (generateBody) {
|
||||
_response->body().appendText(TRI_BeginStringBuffer(&buffer), TRI_LengthStringBuffer(&buffer));
|
||||
|
@ -447,7 +447,8 @@ void RestVocbaseBaseHandler::generateTransactionError (const string& collectionN
|
|||
/// @brief extracts the revision
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TRI_voc_rid_t RestVocbaseBaseHandler::extractRevision (char const* header, char const* parameter) {
|
||||
TRI_voc_rid_t RestVocbaseBaseHandler::extractRevision (char const* header,
|
||||
char const* parameter) {
|
||||
bool found;
|
||||
char const* etag = _request->header(header, found);
|
||||
|
||||
|
|
|
@ -292,7 +292,7 @@ namespace triagens {
|
|||
/// @note @FA{header} must be lowercase.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TRI_voc_rid_t extractRevision (char const* header, char const* parameter);
|
||||
TRI_voc_rid_t extractRevision (char const*, char const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extracts the update policy
|
||||
|
|
|
@ -486,7 +486,7 @@ void ArangoServer::buildApplicationServer () {
|
|||
|
||||
if (absoluteFile != 0) {
|
||||
_pidFile = string(absoluteFile);
|
||||
TRI_Free(TRI_CORE_MEM_ZONE, absoluteFile);
|
||||
TRI_Free(TRI_UNKNOWN_MEM_ZONE, absoluteFile);
|
||||
|
||||
LOGGER_DEBUG("using absolute pid file '" << _pidFile << "'");
|
||||
}
|
||||
|
|
|
@ -101,10 +101,12 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const TRI_vocbase_col_t* getCollectionStruct (const string& name) const {
|
||||
map<string, const TRI_vocbase_col_t*>::iterator it = _resolvedNames.find(name);
|
||||
|
||||
if (it != _resolvedNames.end()) {
|
||||
return (*it).second;
|
||||
if (_resolvedNames.size() > 0) {
|
||||
map<string, const TRI_vocbase_col_t*>::const_iterator it = _resolvedNames.find(name);
|
||||
|
||||
if (it != _resolvedNames.end()) {
|
||||
return (*it).second;
|
||||
}
|
||||
}
|
||||
|
||||
const TRI_vocbase_col_t* collection = TRI_LookupCollectionByNameVocBase(_vocbase, name.c_str());
|
||||
|
@ -120,11 +122,13 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string getCollectionName (const TRI_voc_cid_t cid) const {
|
||||
map<TRI_voc_cid_t, string>::iterator it = _resolvedIds.find(cid);
|
||||
if (_resolvedIds.size() > 0) {
|
||||
map<TRI_voc_cid_t, string>::const_iterator it = _resolvedIds.find(cid);
|
||||
|
||||
if (it != _resolvedIds.end()) {
|
||||
return (*it).second;
|
||||
}
|
||||
if (it != _resolvedIds.end()) {
|
||||
return (*it).second;
|
||||
}
|
||||
}
|
||||
|
||||
char* n = TRI_GetCollectionNameByIdVocBase(_vocbase, cid);
|
||||
if (n == 0) {
|
||||
|
|
|
@ -120,7 +120,7 @@ namespace triagens {
|
|||
/// @brief get the underlying collection's id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline TRI_voc_cid_t cid () {
|
||||
inline TRI_voc_cid_t cid () const {
|
||||
return this->_cid;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
#newCollection {
|
||||
margin-right: 22px;
|
||||
margin-top: 11px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
#newCollection:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.thumbnail, .navbar-inner, .btn, .btn-primary {
|
||||
border-radius:0;
|
||||
box-shadow: 0;
|
||||
|
@ -34,9 +44,10 @@
|
|||
height: 18px;
|
||||
line-height: 18px;
|
||||
margin-top: 3px;
|
||||
margin-left: 20px;
|
||||
margin-left: 18px;
|
||||
padding: 2px;
|
||||
vertical-align: middle;
|
||||
width: 435px;
|
||||
}
|
||||
|
||||
#searchSubmit {
|
||||
|
|
|
@ -124,13 +124,13 @@ table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; }
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.paginate_enabled_previous { background: url('../images/back_enabled.png') no-repeat top left; }
|
||||
.paginate_enabled_previous:hover { background: url('../images/back_enabled_hover.png') no-repeat top left; }
|
||||
.paginate_disabled_previous { background: url('../images/back_disabled.png') no-repeat top left; }
|
||||
.paginate_enabled_previous { background: url('../img/back_enabled.png') no-repeat top left; }
|
||||
.paginate_enabled_previous:hover { background: url('../img/back_enabled_hover.png') no-repeat top left; }
|
||||
.paginate_disabled_previous { background: url('../img/back_disabled.png') no-repeat top left; }
|
||||
|
||||
.paginate_enabled_next { background: url('../images/forward_enabled.png') no-repeat top right; }
|
||||
.paginate_enabled_next:hover { background: url('../images/forward_enabled_hover.png') no-repeat top right; }
|
||||
.paginate_disabled_next { background: url('../images/forward_disabled.png') no-repeat top right; }
|
||||
.paginate_enabled_next { background: url('../img/forward_enabled.png') no-repeat top right; }
|
||||
.paginate_enabled_next:hover { background: url('../img/forward_enabled_hover.png') no-repeat top right; }
|
||||
.paginate_disabled_next { background: url('../img/forward_disabled.png') no-repeat top right; }
|
||||
|
||||
/* Full number pagination */
|
||||
.paging_full_numbers {
|
||||
|
@ -194,12 +194,12 @@ table.dataTable tr.even td.sorting_3 { background-color: #F9F9FF; }
|
|||
/*
|
||||
* Sorting
|
||||
*/
|
||||
.sorting { background: url('../images/sort_both.png') no-repeat center right; }
|
||||
.sorting_asc { background: url('../images/sort_asc.png') no-repeat center right; }
|
||||
.sorting_desc { background: url('../images/sort_desc.png') no-repeat center right; }
|
||||
.sorting { background: url('../img/sort_both.png') no-repeat center right; }
|
||||
.sorting_asc { background: url('../img/sort_asc.png') no-repeat center right; }
|
||||
.sorting_desc { background: url('../img/sort_desc.png') no-repeat center right; }
|
||||
|
||||
.sorting_asc_disabled { background: url('../images/sort_asc_disabled.png') no-repeat center right; }
|
||||
.sorting_desc_disabled { background: url('../images/sort_desc_disabled.png') no-repeat center right; }
|
||||
.sorting_asc_disabled { background: url('../img/sort_asc_disabled.png') no-repeat center right; }
|
||||
.sorting_desc_disabled { background: url('../img/sort_desc_disabled.png') no-repeat center right; }
|
||||
|
||||
table.dataTable thead th:active,
|
||||
table.dataTable thead td:active {
|
||||
|
|
|
@ -27,14 +27,6 @@
|
|||
#logNav {
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.00);
|
||||
}
|
||||
|
||||
#logToolbar {
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.odd {
|
||||
|
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 894 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
|
@ -35,7 +35,6 @@ $(document).ready(function() {
|
|||
window.arangoLogsStore.fetch({
|
||||
success: function () {
|
||||
if (!window.logsView) {
|
||||
console.log("not existing");
|
||||
}
|
||||
window.logsView = new window.logsView({
|
||||
collection: window.arangoLogsStore
|
||||
|
@ -127,7 +126,6 @@ $(document).ready(function() {
|
|||
window.arangoLogsStore.fetch({
|
||||
success: function () {
|
||||
if (!window.logsView) {
|
||||
console.log("not exits");
|
||||
}
|
||||
window.logsView.render();
|
||||
$('#logNav a[href="#all"]').tab('show');
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<input type="text" id="searchInput" class="searchInput" placeholder="Search..."><img id="searchSubmit" width="16" height="16" src="/_admin/html/img/enter_icon.png">
|
||||
|
||||
<a href="#new">
|
||||
<img id="plusIcon" src="/_admin/html/img/plus_icon.png"class="pull-right"></img>
|
||||
<img id="newCollection" src="/_admin/html/img/plus_icon.png"class="pull-right"></img>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
<table cellpadding="0" cellspacing="0" border="0" class="display" id="documentsTableID">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:10px"></th>
|
||||
<th style="width: 100px">_id</th>
|
||||
<th style="width: 490px">Content</th>
|
||||
<th style="width:10px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<div id="add-collection" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display:none">
|
||||
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>New Collection</h3>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<table>
|
||||
<tr>
|
||||
|
@ -10,31 +12,52 @@
|
|||
<th><input type="text" id="new-collection-name" name="name" value=""/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="collectionTh">Size:</th>
|
||||
<th><input type="text" id="new-collection-size" name="size" value=""/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="collectionTh">Sync:</th>
|
||||
<th class="collectionTh">Type:</th>
|
||||
<th>
|
||||
<select id="new-collection-sync">
|
||||
<option value="false">No</option>
|
||||
<option value="true">Yes</option>
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="collectionTh">Type:</th>
|
||||
<th>
|
||||
<select id="new-collection-type">
|
||||
<option value="3">Document</option>
|
||||
<option value="2">Edge</option>
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Abort</button>
|
||||
<button id="save-new-collection" class="btn btn-primary">Create Collection</button>
|
||||
</div>
|
||||
<select id="new-collection-type">
|
||||
<option value="2">Document</option>
|
||||
<option value="3">Edge</option>
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="accordion" id="accordion2">
|
||||
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapseOne">
|
||||
Advanced
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="collapseOne" class="accordion-body collapse out">
|
||||
<div class="accordion-inner">
|
||||
<table>
|
||||
<tr>
|
||||
<th class="collectionTh">Size:</th>
|
||||
<th><input type="text" id="new-collection-size" name="size" value=""/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="collectionTh">Sync:</th>
|
||||
<th>
|
||||
<select id="new-collection-sync">
|
||||
<option value="false">No</option>
|
||||
<option value="true">Yes</option>
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Abort</button>
|
||||
<button id="save-new-collection" class="btn btn-primary">Create Collection</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -72,7 +72,6 @@ var collectionView = Backbone.View.extend({
|
|||
else if (status === 'unloaded') {
|
||||
var newname = $('#change-collection-name').val();
|
||||
if (this.myCollection.name !== newname) {
|
||||
console.log("different name");
|
||||
window.arangoCollectionsStore.renameCollection(collid, newname );
|
||||
this.hideModal();
|
||||
}
|
||||
|
|
|
@ -11,8 +11,19 @@ var documentSourceView = Backbone.View.extend({
|
|||
|
||||
render: function() {
|
||||
$(this.el).html(this.template.text);
|
||||
this.breadcrumb();
|
||||
return this;
|
||||
},
|
||||
breadcrumb: function () {
|
||||
var name = window.location.hash.split("/");
|
||||
$('#transparentHeader').append(
|
||||
'<a href="#" class="activeBread">Collections</a>'+
|
||||
' > '+
|
||||
'<a class="activeBread" href="#collection/'+name[1]+'/documents/1">'+name[1]+'</a>'+
|
||||
' > '+
|
||||
'<a class="disabledBread">'+name[2]+'</a>'
|
||||
);
|
||||
},
|
||||
saveSourceDoc: function() {
|
||||
window.arangoDocumentStore.saveDocument("source");
|
||||
},
|
||||
|
|
|
@ -64,7 +64,7 @@ var documentView = Backbone.View.extend({
|
|||
|
||||
initTable: function () {
|
||||
var documentTable = $(this.table).dataTable({
|
||||
"aaSorting": [[ 1, "desc" ]],
|
||||
"aaSorting": [[ 1, "asc" ]],
|
||||
"bAutoWidth": false,
|
||||
"bFilter": false,
|
||||
"bPaginate":false,
|
||||
|
|
|
@ -38,7 +38,7 @@ var documentsView = Backbone.View.extend({
|
|||
remove: function (a) {
|
||||
this.target = a.currentTarget;
|
||||
var thiselement = a.currentTarget.parentElement;
|
||||
this.idelement = $(thiselement).next().text();
|
||||
this.idelement = $(thiselement).prev().prev();
|
||||
this.alreadyClicked = true;
|
||||
|
||||
$('#docDeleteModal').modal('show');
|
||||
|
@ -49,11 +49,12 @@ var documentsView = Backbone.View.extend({
|
|||
},
|
||||
reallyDelete: function () {
|
||||
var self = this;
|
||||
var todelete = $(self.idelement).text();
|
||||
try {
|
||||
$.ajax({
|
||||
type: 'DELETE',
|
||||
contentType: "application/json",
|
||||
url: "/_api/document/" + self.idelement,
|
||||
url: "/_api/document/" + todelete,
|
||||
success: function () {
|
||||
var row = $(self.target).closest("tr").get(0);
|
||||
$('#documentsTableID').dataTable().fnDeleteRow($('#documentsTableID').dataTable().fnGetPosition(row));
|
||||
|
@ -80,7 +81,7 @@ var documentsView = Backbone.View.extend({
|
|||
var self = a.currentTarget;
|
||||
var aPos = $(this.table).dataTable().fnGetPosition(self);
|
||||
var rowContent = $(this.table).dataTable().fnGetData(aPos);
|
||||
window.location.hash = "#collection/" + rowContent[1];
|
||||
window.location.hash = "#collection/" + rowContent[0];
|
||||
},
|
||||
|
||||
initTable: function (colid, pageid) {
|
||||
|
@ -111,11 +112,11 @@ var documentsView = Backbone.View.extend({
|
|||
var self = this;
|
||||
$.each(window.arangoDocumentsStore.models, function(key, value) {
|
||||
$(self.table).dataTable().fnAddData([
|
||||
'<button class="enabled" id="deleteDoc"><img src="/_admin/html/img/doc_delete_icon16.png" width="16" height="16"></button>',
|
||||
value.attributes.id,
|
||||
//value.attributes.key,
|
||||
//value.attributes.rev,
|
||||
'<pre class=prettify>' + self.cutByResolution(JSON.stringify(value.attributes.content)) + '</pre>'
|
||||
'<pre class=prettify>' + self.cutByResolution(JSON.stringify(value.attributes.content)) + '</pre>',
|
||||
'<button class="enabled" id="deleteDoc"><img src="/_admin/html/img/doc_delete_icon16.png" width="16" height="16"></button>'
|
||||
]);
|
||||
});
|
||||
$(".prettify").snippet("javascript", {style: "nedit", menu: false, startText: false, transparent: true, showNum: false});
|
||||
|
|
|
@ -49,6 +49,7 @@ var newCollectionView = Backbone.View.extend({
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/_api/collection",
|
||||
|
|
|
@ -18,7 +18,6 @@ var queryView = Backbone.View.extend({
|
|||
return this;
|
||||
},
|
||||
submitQuery: function() {
|
||||
console.log("submit");
|
||||
var self = this;
|
||||
var data = {query:$('#queryInput').val()};
|
||||
var formattedJSON;
|
||||
|
|
|
@ -66,7 +66,6 @@ var shellView = Backbone.View.extend({
|
|||
var client = "arangosh> " + escapeHTML(data) + "<br>";
|
||||
$(varContent).append('<b class="shellClient">' + client + '</b>');
|
||||
this.evaloutput(command);
|
||||
console.log(command);
|
||||
$(varContent).animate({scrollTop:$(varContent)[0].scrollHeight}, 1);
|
||||
$(varInput).val('');
|
||||
return false;
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
<div style="background-color: rgba(0, 0, 0, 0.15); border-radius: 0;">
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<img class="about-icon" src="img/download.png" width="50">
|
||||
<h3>Download the source code</h3>
|
||||
<p>The source code for this application is available in this repository on GitHub.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<img class="about-icon" src="img/discuss.png" width="50" style="float: left;">
|
||||
|
||||
<h3>Comments and questions</h3>
|
||||
|
||||
<p>We love to hear your feedback. Post your questions and comments on the blog post associated with this
|
||||
application.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<img class="about-icon" src="img/twitter.png" width="50" style="float: left;">
|
||||
|
||||
<h3>Follow us on Twitter</h3>
|
||||
|
||||
<p><a href="http://twitter.com/arangodb">@arangodb</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<img class="about-icon" src="img/blog.png" width="50" style="float: left;">
|
||||
|
||||
<h3>Check out our blog</h3>
|
||||
|
||||
<p><a href="http://arangodb.org">http://arangodb.org</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,10 +0,0 @@
|
|||
<a href="#collections,<%= name %>" class="thumbnail plain">
|
||||
<img src="pics/<%= picture %>" height="48" width="48" alt="" style="float:left; padding-right:20px; padding-left: 5px; padding-top: 5px; ">
|
||||
<h5 class="collectionName"><%= name %></h5>
|
||||
<p><br/>
|
||||
<%= status %>
|
||||
</p></a>
|
||||
|
||||
<a role="button" data-toggle="modal" class="thumbnail plain pull-right" style="width:14px; height:14px;">
|
||||
<i class="icon-info-sign" id="<%= name %>"></i>
|
||||
</a>
|
|
@ -1,108 +0,0 @@
|
|||
<ul class="thumbnails2">
|
||||
<div id="transparentHeader">
|
||||
<div id="transparentPlaceholder">
|
||||
<input type="text" class="searchInput"><img id="searchSubmit" width="16" height="16" src="/_admin/html/pics/enter_icon.png">
|
||||
<a href="#add-collection" role="button" data-toggle="modal">
|
||||
<img id="plusIcon" src="/_admin/html/pics/plus_icon.png"class="pull-right"></img></a>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
</ul><ul class="thumbnails">
|
||||
</ul>
|
||||
|
||||
<div id="add-collection" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display:none">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>New Collection</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table>
|
||||
<tr>
|
||||
<th class="collectionTh">Name:</th>
|
||||
<th><input type="text" id="new-collection-name" name="name" value=""/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="collectionTh">Size:</th>
|
||||
<th><input type="text" id="new-collection-size" name="size" value=""/></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="collectionTh">Sync:</th>
|
||||
<th>
|
||||
<select id="new-collection-sync">
|
||||
<option value="false">No</option>
|
||||
<option value="true">Yes</option>
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="collectionTh">Type:</th>
|
||||
<th>
|
||||
<select id="new-collection-type">
|
||||
<option value="3">Document</option>
|
||||
<option value="2">Edge</option>
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Abort</button>
|
||||
<button id="save-new-collection" class="btn btn-primary">Create Collection</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="change-collection" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display:none">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Modify Collection</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table>
|
||||
<tr>
|
||||
<th class="collectionTh">Name:</th>
|
||||
<th><input type="text" id="change-collection-name" name="name" value=""/></th>
|
||||
</tr>
|
||||
<tr id="collectionSizeBox" style="display:none">
|
||||
<th class="collectionTh">Size:</th>
|
||||
<th><input type="text" id="change-collection-size" name="size" value=""/></th>
|
||||
</tr>
|
||||
<tr id="collectionSyncBox" style="display:none">
|
||||
<th class="collectionTh">Sync:</th>
|
||||
<th>
|
||||
<select id="change-collection-sync">
|
||||
<option value="false">No</option>
|
||||
<option value="true">Yes</option>
|
||||
</select>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="collectionTh">ID:</th>
|
||||
<th>
|
||||
<input id="change-collection-id" name="id" type="text" class="span1" disabled/>
|
||||
<th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="collectionTh">Type:</th>
|
||||
<th>
|
||||
<input type="text" id="change-collection-type" name="size" value="" disabled/>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="collectionTh">Status:</th>
|
||||
<th>
|
||||
<input id="change-collection-status" name="id" type="text" class="span1" disabled/>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
|
||||
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Abort</button>
|
||||
<button id="save-modified-collection" class="btn btn-primary">Modify Collection</button>
|
||||
</div>
|
||||
</div>
|
|
@ -1,99 +0,0 @@
|
|||
<div class="row blackbackground collectionDiv">
|
||||
|
||||
<form class="form-horizontal span12 collectionDiv2">
|
||||
|
||||
<fieldset class="">
|
||||
|
||||
<legend> Collection Details</legend>
|
||||
<br/>
|
||||
|
||||
<div class="row">
|
||||
<div class="span9">
|
||||
|
||||
<div class="control-group">
|
||||
<label for="name" class="control-label">Name:</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" id="collectionName" name="name" value="<%= name %>"/>
|
||||
<span class="help-inline"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" id="collectionSizeBox" style="display:none">
|
||||
<label for="size" class="control-label">Size:</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" id="update-collection-size" name="size" value=""/>
|
||||
<span class="help-inline"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" id="collectionSyncBox" style="display:none">
|
||||
<label for="sync" class="control-label">Sync:</label>
|
||||
|
||||
<div class="controls">
|
||||
<select id="update-collection-sync">
|
||||
<option value="false">No</option>
|
||||
<option value="true">Yes</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="id" class="control-label">Id:</label>
|
||||
|
||||
<div class="controls">
|
||||
<input id="collectionId" name="id" type="text" value="<%= id === null ? '' : id %>" class="span1"
|
||||
disabled/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="status" class="control-label">Status:</label>
|
||||
|
||||
<div class="controls">
|
||||
<input id="status" name="status" type="text" value="<%= status === null ? '' : status %>" class="span1"
|
||||
disabled/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="type" class="control-label">Type:</label>
|
||||
|
||||
<div class="controls">
|
||||
<input id="type" name="type" type="text" value="<%= type === null ? '' : type %>" class="span1"
|
||||
disabled/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="span3">
|
||||
<div class="well" id="collectionBox">
|
||||
<img id="picture" width="48" src="<%= picture === null ? 'pics/generic.jpg' : 'pics/' + picture %>"/>
|
||||
<a href="#" class="btn load">Load</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="#" class="btn btn-primary save pull-right collectionViewBtn">Save</a>
|
||||
<a href="#" class="btn delete btn-delete pull-right">Delete</a>
|
||||
<a href="#" class="btn">Cancel</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row status-bar">
|
||||
<div class="span12">
|
||||
<div class="alert alert-success" style="display: none">
|
||||
<b>Success!</b> Collection saved successfully
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<button class="minimal enabled" id="addDocumentButton">
|
||||
<img src="/_admin/html/pics/doc_plus_icon16.png" width="16" height="16">
|
||||
<a>Add Document</a>
|
||||
</button>
|
||||
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="display" id="documentsTableID" width="100%" style="table-layout:fixed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>_id</th>
|
||||
<th>_key</th>
|
||||
<th>_rev</th>
|
||||
<th>Content</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
<div id="documentsToolbar">
|
||||
|
||||
<div class="pull-left">
|
||||
</div>
|
||||
|
||||
<div class="pull-right">
|
||||
<form id="docPageForm">
|
||||
Jump to: <input type="text" id="docPageInput" placeholder="Page"></input>
|
||||
<button id="submitDocPageInput">Ok</button>
|
||||
</form>
|
||||
<button class="enabled" id="documents_first"><img src="/_admin/html/pics/rnd_br_last_icon16.png"></button>
|
||||
<button class="enabled" id="documents_prev"><img src="/_admin/html/pics/rnd_br_next_icon16.png"></button>
|
||||
<button class="enabled" id="documents_next"><img src="/_admin/html/pics/rnd_br_prev_icon16.png"></button>
|
||||
<button class="enabled" id="documents_last"><img src="/_admin/html/pics/rnd_br_first_icon16.png"></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
<div class="navbar navbar-fixed-top" xmlns="http://www.w3.org/1999/html">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
|
||||
<a class="brand navlogo" href="#"><img src="pics/logo_arangob_white.gif"/></a>
|
||||
|
||||
<div class="nav-collapse">
|
||||
<ul class="nav">
|
||||
</ul>
|
||||
|
||||
<ul class="nav pull-right">
|
||||
<li class="dashboard-menu"><a href="#dashboard">Dashboard</a></li>
|
||||
<li class="collections-menu"><a href="#">Collections</a></li>
|
||||
<!-- <li class="statistics-menu"><a href="#statistics">Statistics</a></li> -->
|
||||
<li class="query-menu"><a href="#query">Query</a></li>
|
||||
<li class="shell-menu"><a href="#shell">Shell</a></li>
|
||||
<li class="logs-menu"><a href="#logs">Logs</a></li>
|
||||
<li class="about-menu"><a href="#about">About</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Links <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="nav-header">Documentation</li>
|
||||
<li><a href="http://www.arangodb.org/manuals/current/UserManualArangosh.html" target="_blank">Shell Documentation</a></li>
|
||||
<li><a href="http://www.arangodb.org/manuals/current/Aql.html" target="_blank">Query Documentation</a></li>
|
||||
<li><a href="http://www.arangodb.org/manuals/current/" target="_blank">General Documentation</a></li>
|
||||
<li class="divider"></li>
|
||||
<li class="nav-header">This App</li>
|
||||
<li><a href="https://github.com/triAGENS/ArangoDB" target="_blank">GitHub Repository</a></li>
|
||||
<li><a href="http://www.arangodb.org" target="_blank">ArangoDB.org</a></li>
|
||||
<li class="divider"></li>
|
||||
<li class="nav-header">Social</li>
|
||||
<li><a href="https://twitter.com/arangodb" target="_blank">Twitter</a></li>
|
||||
<li><a href="http://www.arangodb.org/connect" target="_blank">Announcement List</a></li>
|
||||
<li><a href="https://groups.google.com/group/arangodb" target="_blank">Google Groups</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,110 +0,0 @@
|
|||
<div id="logTabs">
|
||||
<ul>
|
||||
<li><a class="hoverClass" id="logAllTab" href="#All">All</a><li>
|
||||
<li><a class="hoverClass" id="logCritTab" href="#Critical">Critical</a><li>
|
||||
<li><a class="hoverClass" id="logWarnTab" href="#Warning">Warning</a><li>
|
||||
<li><a class="hoverClass" id="logInfoTab" href="#Info">Info</a><li>
|
||||
<li><a class="hoverClass" id="logDebuTab" href="#Debug">Debug</a><li>
|
||||
</ul>
|
||||
|
||||
<div id="All">
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="display" id="logTableID" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Loglevel</th>
|
||||
<th>Info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div id="logToolbar">
|
||||
<div id="logToolbarAll">
|
||||
<button class="enabled" id="logTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
|
||||
<button class="enabled" id="logTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
|
||||
<button class="enabled" id="logTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
|
||||
<button class="enabled" id="logTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
|
||||
</div>
|
||||
<a id="logTableID_status">Showing</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="Critical">
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="display" id="critLogTableID" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Loglevel</th>
|
||||
<th>Info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div id="logToolbar">
|
||||
<div id="logToolbarCrit">
|
||||
<button class="enabled" id="critLogTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
|
||||
<button class="enabled" id="critLogTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
|
||||
<button class="enabled" id="critLogTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
|
||||
<button class="enabled" id="critLogTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
|
||||
<a id="critLogTableID_status">Showing</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="Warning">
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="display" id="warnLogTableID" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Loglevel</th>
|
||||
<th>Info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div id="logToolbar">
|
||||
<div id="logToolbarWarn">
|
||||
<button class="enabled" id="warnLogTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
|
||||
<button class="enabled" id="warnLogTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
|
||||
<button class="enabled" id="warnLogTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
|
||||
<button class="enabled" id="warnLogTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
|
||||
</div>
|
||||
<a id="warnLogTableID_status">Showing</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="Info">
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="display" id="infoLogTableID" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Loglevel</th>
|
||||
<th>Info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div id="logToolbar">
|
||||
<div id="logToolbarInfo">
|
||||
<button class="enabled" id="infoLogTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
|
||||
<button class="enabled" id="infoLogTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
|
||||
<button class="enabled" id="infoLogTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
|
||||
<button class="enabled" id="infoLogTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
|
||||
</div>
|
||||
<a id="infoLogTableID_status">Showing</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="Debug">
|
||||
<table cellpadding="0" cellspacing="0" border="0" class="display" id="debugLogTableID" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Loglevel</th>
|
||||
<th>Info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div id="logToolbar">
|
||||
<div id="logToolbarDebu">
|
||||
<button class="enabled" id="debugLogTableID_first"><img src="/_admin/html/media/icons/rnd_br_first_icon16.png"></button>
|
||||
<button class="enabled" id="debugLogTableID_prev"><img src="/_admin/html/media/icons/rnd_br_prev_icon16.png"></button>
|
||||
<button class="enabled" id="debugLogTableID_next"><img src="/_admin/html/media/icons/rnd_br_next_icon16.png"></button>
|
||||
<button class="enabled" id="debugLogTableID_last"><img src="/_admin/html/media/icons/rnd_br_last_icon16.png"></button>
|
||||
</div>
|
||||
<a id="debugLogTableID_status">Showing</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,9 +0,0 @@
|
|||
<!-- <div class="querynavbar navbar-inner navbar-inner-custom"><h3>Arango Shell</h3></div> -->
|
||||
<div id="queryDiv" class="form-actions">
|
||||
<div id="queryOutput" class="thumbnail"></div>
|
||||
<form method="post" onsubmit="submitQuery()">
|
||||
<i class="icon-trash clearicon"></i>
|
||||
<textarea placeholder="Type in your query..." class="editBox" id="queryInput"></textarea>
|
||||
</form>
|
||||
<button id="submitQueryButton" class="btn btn-primary">Submit</button>
|
||||
</div>
|
|
@ -1,11 +0,0 @@
|
|||
<!-- <div class="shellnavbar navbar-inner navbar-inner-custom"><h3>Arango Shell</h3></div> -->
|
||||
|
||||
<div id="shellFooter" class="form-actions">
|
||||
<i class="icon-trash clearicon shelltrashicon"></i>
|
||||
<i class="icon-refresh reloadicon"></i>
|
||||
<div id="shellContent" class="thumbnail"></div>
|
||||
<form id="shellForm" onsubmit="submitShell(); return false;">
|
||||
<input id="shellInput" type="text" autocomplete="off" role="textbox" aria-autocomplete="list" aria-haspopup="true">
|
||||
</form>
|
||||
<img id="submitShellButton"src="/_admin/html/pics/enter_icon.png" width="16" height="16">
|
||||
</div>
|
|
@ -673,6 +673,13 @@ ArangoCollection.prototype.iterate = function (iterator, options) {
|
|||
/// synchronisation for collections that have a default @LIT{waitForSync} value
|
||||
/// of @LIT{true}.
|
||||
///
|
||||
/// @FUN{@FA{collection}.removeByExample(@FA{document}, @FA{waitForSync}, @FA{limit})}
|
||||
///
|
||||
/// The optional @FA{limit} parameter can be used to restrict the number of
|
||||
/// removals to the specified value. If @FA{limit} is specified but less than the
|
||||
/// number of documents in the collection, it is undefined which documents are
|
||||
/// removed.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
|
@ -680,10 +687,99 @@ ArangoCollection.prototype.iterate = function (iterator, options) {
|
|||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
|
||||
ArangoCollection.prototype.removeByExample = function (example, waitForSync, limit) {
|
||||
throw "cannot call abstract removeByExample function";
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replaces documents matching an example
|
||||
///
|
||||
/// @FUN{@FA{collection}.replaceByExample(@FA{example}, @FA{newValue})}
|
||||
///
|
||||
/// Replaces all documents matching an example with a new document body.
|
||||
/// The entire document body of each document matching the @FA{example} will be
|
||||
/// replaced with @FA{newValue}. The document meta-attributes such as @LIT{_id},
|
||||
/// @LIT{_key}, @LIT{_from}, @LIT{_to} will not be replaced.
|
||||
///
|
||||
/// @FUN{@FA{collection}.replaceByExample(@FA{document}, @FA{newValue}, @FA{waitForSync})}
|
||||
///
|
||||
/// The optional @FA{waitForSync} parameter can be used to force synchronisation
|
||||
/// of the document replacement operation to disk even in case that the
|
||||
/// @LIT{waitForSync} flag had been disabled for the entire collection. Thus,
|
||||
/// the @FA{waitForSync} parameter can be used to force synchronisation of just
|
||||
/// specific operations. To use this, set the @FA{waitForSync} parameter to
|
||||
/// @LIT{true}. If the @FA{waitForSync} parameter is not specified or set to
|
||||
/// @LIT{false}, then the collection's default @LIT{waitForSync} behavior is
|
||||
/// applied. The @FA{waitForSync} parameter cannot be used to disable
|
||||
/// synchronisation for collections that have a default @LIT{waitForSync} value
|
||||
/// of @LIT{true}.
|
||||
///
|
||||
/// @FUN{@FA{collection}.replaceByExample(@FA{document}, @FA{newValue}, @FA{waitForSync}, @FA{limit})}
|
||||
///
|
||||
/// The optional @FA{limit} parameter can be used to restrict the number of
|
||||
/// replacements to the specified value. If @FA{limit} is specified but less than
|
||||
/// the number of documents in the collection, it is undefined which documents are
|
||||
/// replaced.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangod> db.content.replaceByExample({ "domain": "de.celler" }, { "foo": "someValue }, false, 5)
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.replaceByExample = function (example, newValue, waitForSync, limit) {
|
||||
throw "cannot call abstract replaceByExample function";
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief partially updates documents matching an example
|
||||
///
|
||||
/// @FUN{@FA{collection}.updateByExample(@FA{example}, @FA{newValue})}
|
||||
///
|
||||
/// Partially updates all documents matching an example with a new document body.
|
||||
/// Specific attributes in the document body of each document matching the
|
||||
/// @FA{example} will be updated with the values from @FA{newValue}.
|
||||
/// The document meta-attributes such as @LIT{_id}, @LIT{_key}, @LIT{_from},
|
||||
/// @LIT{_to} cannot be updated.
|
||||
///
|
||||
/// @FUN{@FA{collection}.updateByExample(@FA{document}, @FA{newValue}, @FA{keepNull}, @FA{waitForSync})}
|
||||
///
|
||||
/// The optional @FA{keepNull} parameter can be used to modify the behavior when
|
||||
/// handling @LIT{null} values. Normally, @LIT{null} values are stored in the
|
||||
/// database. By setting the @FA{keepNull} parameter to @LIT{false}, this behavior
|
||||
/// can be changed so that all attributes in @FA{data} with @LIT{null} values will
|
||||
/// be removed from the target document.
|
||||
///
|
||||
/// The optional @FA{waitForSync} parameter can be used to force synchronisation
|
||||
/// of the document replacement operation to disk even in case that the
|
||||
/// @LIT{waitForSync} flag had been disabled for the entire collection. Thus,
|
||||
/// the @FA{waitForSync} parameter can be used to force synchronisation of just
|
||||
/// specific operations. To use this, set the @FA{waitForSync} parameter to
|
||||
/// @LIT{true}. If the @FA{waitForSync} parameter is not specified or set to
|
||||
/// @LIT{false}, then the collection's default @LIT{waitForSync} behavior is
|
||||
/// applied. The @FA{waitForSync} parameter cannot be used to disable
|
||||
/// synchronisation for collections that have a default @LIT{waitForSync} value
|
||||
/// of @LIT{true}.
|
||||
///
|
||||
/// @FUN{@FA{collection}.updateByExample(@FA{document}, @FA{newValue}, @FA{keepNull}, @FA{waitForSync}, @FA{limit})}
|
||||
///
|
||||
/// The optional @FA{limit} parameter can be used to restrict the number of
|
||||
/// updates to the specified value. If @FA{limit} is specified but less than
|
||||
/// the number of documents in the collection, it is undefined which documents are
|
||||
/// updated.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangod> db.content.updateByExample({ "domain": "de.celler" }, { "foo": "someValue, "domain": null }, false)
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.updateByExample = function (example, newValue, keepNull, waitForSync, limit) {
|
||||
throw "cannot call abstract updateExample function";
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1117,11 +1117,14 @@ ArangoCollection.prototype.outEdges = function (vertex) {
|
|||
/// @brief removes documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
|
||||
ArangoCollection.prototype.removeByExample = function (example,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var data = {
|
||||
collection: this._name,
|
||||
example: example,
|
||||
waitForSync: waitForSync
|
||||
waitForSync: waitForSync,
|
||||
limit: limit
|
||||
};
|
||||
|
||||
var requestResult = this._database._connection.PUT(
|
||||
|
@ -1133,6 +1136,58 @@ ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
|
|||
return requestResult.deleted;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replaces documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.replaceByExample = function (example,
|
||||
newValue,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var data = {
|
||||
collection: this._name,
|
||||
example: example,
|
||||
newValue: newValue,
|
||||
waitForSync: waitForSync,
|
||||
limit: limit
|
||||
};
|
||||
|
||||
var requestResult = this._database._connection.PUT(
|
||||
"/_api/simple/replace-by-example",
|
||||
JSON.stringify(data));
|
||||
|
||||
arangosh.checkRequestResult(requestResult);
|
||||
|
||||
return requestResult.replaced;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief updates documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.updateByExample = function (example,
|
||||
newValue,
|
||||
keepNull,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var data = {
|
||||
collection: this._name,
|
||||
example: example,
|
||||
newValue: newValue,
|
||||
keepNull: keepNull,
|
||||
waitForSync: waitForSync,
|
||||
limit: limit
|
||||
};
|
||||
|
||||
var requestResult = this._database._connection.PUT(
|
||||
"/_api/simple/update-by-example",
|
||||
JSON.stringify(data));
|
||||
|
||||
arangosh.checkRequestResult(requestResult);
|
||||
|
||||
return requestResult.updated;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -831,6 +831,11 @@ actions.defineHttp({
|
|||
/// instantly be synchronised to disk. If this is not specified, then the
|
||||
/// collection's default sync behavior will be applied.
|
||||
///
|
||||
/// - @LIT{limit}: an optional value that determines how many documents to
|
||||
/// delete at most. If @LIT{limit} is specified but is less than the number
|
||||
/// of documents in the collection, it is undefined which of the documents
|
||||
/// will be deleted.
|
||||
///
|
||||
/// Returns the number of documents that were deleted
|
||||
///
|
||||
/// @EXAMPLES
|
||||
|
@ -857,6 +862,7 @@ actions.defineHttp({
|
|||
var example = body.example;
|
||||
var name = body.collection;
|
||||
var collection = db._collection(name);
|
||||
var limit = body.limit || null;
|
||||
|
||||
if (collection === null) {
|
||||
actions.collectionNotFound(req, res, name);
|
||||
|
@ -865,7 +871,7 @@ actions.defineHttp({
|
|||
actions.badParameter(req, res, "example");
|
||||
}
|
||||
else {
|
||||
var result = collection.removeByExample(example, body.waitForSync);
|
||||
var result = collection.removeByExample(example, body.waitForSync, limit);
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { deleted: result });
|
||||
}
|
||||
}
|
||||
|
@ -876,6 +882,179 @@ actions.defineHttp({
|
|||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSA_PUT_api_simple_replace_by_example
|
||||
/// @brief replaces the body of all documents of a collection that match an
|
||||
/// example
|
||||
///
|
||||
/// @RESTHEADER{PUT /_api/simple/replace-by-example,replaces documents by example}
|
||||
///
|
||||
/// @REST{PUT /_api/simple/replace-by-example}
|
||||
///
|
||||
/// This will find all documents in the collection that match the specified
|
||||
/// example object, and replace the entire document body with the new value
|
||||
/// specified. Note that document meta-attributes such as @LIT{_id}, @LIT{_key},
|
||||
/// @LIT{_from}, @LIT{_to} etc. cannot be replaced.
|
||||
///
|
||||
/// The call expects a JSON hash array as body with the following attributes:
|
||||
///
|
||||
/// - @LIT{collection}: The name of the collection to replace within.
|
||||
///
|
||||
/// - @LIT{example}: An example object that all collection objects are compared
|
||||
/// against.
|
||||
///
|
||||
/// - @LIT{newValue}: The replacement document that will get inserted in place
|
||||
/// of the "old" documents.
|
||||
///
|
||||
/// - @LIT{waitForSync}: if set to true, then all removal operations will
|
||||
/// instantly be synchronised to disk. If this is not specified, then the
|
||||
/// collection's default sync behavior will be applied.
|
||||
///
|
||||
/// - @LIT{limit}: an optional value that determines how many documents to
|
||||
/// replace at most. If @LIT{limit} is specified but is less than the number
|
||||
/// of documents in the collection, it is undefined which of the documents
|
||||
/// will be replaced.
|
||||
///
|
||||
/// Returns the number of documents that were replaced
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @verbinclude api-simple-replace-by-example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
actions.defineHttp({
|
||||
url : API + "replace-by-example",
|
||||
context : "api",
|
||||
|
||||
callback : function (req, res) {
|
||||
try {
|
||||
var body = actions.getJsonBody(req, res);
|
||||
|
||||
if (body === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.requestType != actions.PUT) {
|
||||
actions.resultUnsupported(req, res);
|
||||
}
|
||||
else {
|
||||
var example = body.example;
|
||||
var newValue = body.newValue;
|
||||
var name = body.collection;
|
||||
var collection = db._collection(name);
|
||||
var limit = body.limit || null;
|
||||
|
||||
if (collection === null) {
|
||||
actions.collectionNotFound(req, res, name);
|
||||
}
|
||||
else if (typeof example !== "object") {
|
||||
actions.badParameter(req, res, "example");
|
||||
}
|
||||
else if (typeof newValue !== "object") {
|
||||
actions.badParameter(req, res, "newValue");
|
||||
}
|
||||
else {
|
||||
var result = collection.replaceByExample(example, newValue, body.waitForSync, limit);
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { replaced: result });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
actions.resultException(req, res, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSA_PUT_api_simple_update_by_example
|
||||
/// @brief partially updates the body of all documents of a collection that
|
||||
/// match an example
|
||||
///
|
||||
/// @RESTHEADER{PUT /_api/simple/update-by-example,updates documents by example}
|
||||
///
|
||||
/// @REST{PUT /_api/simple/update-by-example}
|
||||
///
|
||||
/// This will find all documents in the collection that match the specified
|
||||
/// example object, and partially update the document body with the new value
|
||||
/// specified. Note that document meta-attributes such as @LIT{_id}, @LIT{_key},
|
||||
/// @LIT{_from}, @LIT{_to} etc. cannot be replaced.
|
||||
///
|
||||
/// The call expects a JSON hash array as body with the following attributes:
|
||||
///
|
||||
/// - @LIT{collection}: The name of the collection to update within.
|
||||
///
|
||||
/// - @LIT{example}: An example object that all collection objects are compared
|
||||
/// against.
|
||||
///
|
||||
/// - @LIT{newValue}: The update value that will get inserted in place
|
||||
/// of the "old" version of the found documents.
|
||||
///
|
||||
/// - @LIT{keepNull}: This parameter can be used to modify the behavior when
|
||||
/// handling @LIT{null} values. Normally, @LIT{null} values are stored in the
|
||||
/// database. By setting the @FA{keepNull} parameter to @LIT{false}, this
|
||||
/// behavior can be changed so that all attributes in @FA{data} with @LIT{null}
|
||||
/// values will be removed from the updated document.
|
||||
///
|
||||
/// - @LIT{waitForSync}: if set to true, then all removal operations will
|
||||
/// instantly be synchronised to disk. If this is not specified, then the
|
||||
/// collection's default sync behavior will be applied.
|
||||
///
|
||||
/// - @LIT{limit}: an optional value that determines how many documents to
|
||||
/// update at most. If @LIT{limit} is specified but is less than the number
|
||||
/// of documents in the collection, it is undefined which of the documents
|
||||
/// will be updated.
|
||||
///
|
||||
/// Returns the number of documents that were updated
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @verbinclude api-simple-update-by-example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
actions.defineHttp({
|
||||
url : API + "update-by-example",
|
||||
context : "api",
|
||||
|
||||
callback : function (req, res) {
|
||||
try {
|
||||
var body = actions.getJsonBody(req, res);
|
||||
|
||||
if (body === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.requestType != actions.PUT) {
|
||||
actions.resultUnsupported(req, res);
|
||||
}
|
||||
else {
|
||||
var example = body.example;
|
||||
var newValue = body.newValue;
|
||||
var name = body.collection;
|
||||
var collection = db._collection(name);
|
||||
var limit = body.limit || null;
|
||||
var keepNull = body.keepNull || null;
|
||||
|
||||
if (collection === null) {
|
||||
actions.collectionNotFound(req, res, name);
|
||||
}
|
||||
else if (typeof example !== "object") {
|
||||
actions.badParameter(req, res, "example");
|
||||
}
|
||||
else if (typeof newValue !== "object") {
|
||||
actions.badParameter(req, res, "newValue");
|
||||
}
|
||||
else {
|
||||
var result = collection.updateByExample(example, newValue, keepNull, body.waitForSync, limit);
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { updated: result });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
actions.resultException(req, res, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief user management
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Achim Brandt
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var arangodb = require("org/arangodb");
|
||||
var actions = require("org/arangodb/actions");
|
||||
var users = require("org/arangodb/users");
|
||||
var ArangoError = require("org/arangodb/arango-error").ArangoError;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoAPI
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief fetch a user
|
||||
///
|
||||
/// @RESTHEADER{GET /_api/user,fetches a user}
|
||||
///
|
||||
/// @REST{GET /_api/user/@FA{username}}
|
||||
///
|
||||
/// Fetches data about the specified user.
|
||||
///
|
||||
/// The call will return a JSON document with at least the following attributes
|
||||
/// on success:
|
||||
///
|
||||
/// - @LIT{username}: The name of the user as a string.
|
||||
///
|
||||
/// - @LIT{active}: an optional flag that specifies whether the user is active.
|
||||
///
|
||||
/// - @LIT{extra}: an optional JSON object with arbitrary extra data about the
|
||||
/// user.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function GET_api_user (req, res) {
|
||||
if (req.suffix.length != 1) {
|
||||
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
var username = decodeURIComponent(req.suffix[0]);
|
||||
try {
|
||||
var result = users.document(username);
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_OK, result)
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
actions.resultNotFound(req, res, arangodb.errors.ERROR_USER_NOT_FOUND.code);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a new user
|
||||
///
|
||||
/// @RESTHEADER{POST /_api/user,creates user}
|
||||
///
|
||||
/// @REST{POST /_api/user}
|
||||
///
|
||||
/// The following data need to be passed in a JSON representation in the body of
|
||||
/// the POST request:
|
||||
///
|
||||
/// - @LIT{username}: The name of the user as a string. This is mandatory.
|
||||
///
|
||||
/// - @LIT{passwd}: The user password as a string. If no password is specified,
|
||||
/// the empty string will be used.
|
||||
///
|
||||
/// - @LIT{active}: an optional flag that specifies whether the user is active.
|
||||
/// If not specified, this will default to @LIT{true}.
|
||||
///
|
||||
/// - @LIT{extra}: an optional JSON object with arbitrary extra data about the
|
||||
/// user.
|
||||
///
|
||||
/// If the user can be added by the server, the server will respond with
|
||||
/// @LIT{HTTP 201}.
|
||||
///
|
||||
/// In case of success, the returned JSON object has the following properties:
|
||||
///
|
||||
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{false}
|
||||
/// in this case)
|
||||
///
|
||||
/// - @LIT{code}: the HTTP status code
|
||||
///
|
||||
/// If the JSON representation is malformed or mandatory data is missing from the
|
||||
/// request, the server will respond with @LIT{HTTP 400}.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{true} in this case)
|
||||
///
|
||||
/// - @LIT{code}: the HTTP status code
|
||||
///
|
||||
/// - @LIT{errorNum}: the server error number
|
||||
///
|
||||
/// - @LIT{errorMessage}: a descriptive error message
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function POST_api_user (req, res) {
|
||||
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
|
||||
|
||||
if (json === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = users.save(json.username, json.passwd, json.active, json.extra);
|
||||
users.reload();
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_CREATED, { });
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replace an existing user
|
||||
///
|
||||
/// @RESTHEADER{PUT /_api/user,replaces user}
|
||||
///
|
||||
/// @REST{PUT /_api/user/@FA{username}}
|
||||
///
|
||||
/// Replaces the data of an existing user. The name of an existing user must
|
||||
/// be specified in @FA{username}.
|
||||
///
|
||||
/// The following data can to be passed in a JSON representation in the body of
|
||||
/// the POST request:
|
||||
///
|
||||
/// - @LIT{passwd}: The user password as a string. Specifying a password is
|
||||
/// mandatory, but the empty string is allowed for passwords.
|
||||
///
|
||||
/// - @LIT{active}: an optional flag that specifies whether the user is active.
|
||||
/// If not specified, this will default to @LIT{true}.
|
||||
///
|
||||
/// - @LIT{extra}: an optional JSON object with arbitrary extra data about the
|
||||
/// user.
|
||||
///
|
||||
/// If the user can be replaced by the server, the server will respond with
|
||||
/// @LIT{HTTP 200}.
|
||||
///
|
||||
/// In case of success, the returned JSON object has the following properties:
|
||||
///
|
||||
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{false}
|
||||
/// in this case)
|
||||
///
|
||||
/// - @LIT{code}: the HTTP status code
|
||||
///
|
||||
/// If the JSON representation is malformed or mandatory data is missing from the
|
||||
/// request, the server will respond with @LIT{HTTP 400}. If the specified user
|
||||
/// does not exist, the server will respond with @LIT{HTTP 404}.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{true} in this case)
|
||||
///
|
||||
/// - @LIT{code}: the HTTP status code
|
||||
///
|
||||
/// - @LIT{errorNum}: the server error number
|
||||
///
|
||||
/// - @LIT{errorMessage}: a descriptive error message
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function PUT_api_user (req, res) {
|
||||
if (req.suffix.length != 1) {
|
||||
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
var username = decodeURIComponent(req.suffix[0]);
|
||||
|
||||
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
|
||||
|
||||
if (json === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var result = users.replace(username, json.passwd, json.active, json.extra);
|
||||
users.reload();
|
||||
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { });
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
actions.resultNotFound(req, res, arangodb.errors.ERROR_USER_NOT_FOUND.code);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief partially update an existing user
|
||||
///
|
||||
/// @RESTHEADER{PATCH /_api/user,updates user}
|
||||
///
|
||||
/// @REST{PATCH /_api/user/@FA{username}}
|
||||
///
|
||||
/// Partially updates the data of an existing user. The name of an existing user
|
||||
/// must be specified in @FA{username}.
|
||||
///
|
||||
/// The following data can be passed in a JSON representation in the body of
|
||||
/// the POST request:
|
||||
///
|
||||
/// - @LIT{passwd}: The user password as a string. Specifying a password is
|
||||
/// optional. If not specified, the previously existing value will not be
|
||||
/// modified.
|
||||
///
|
||||
/// - @LIT{active}: an optional flag that specifies whether the user is active.
|
||||
/// If not specified, the previously existing value will not be modified.
|
||||
///
|
||||
/// - @LIT{extra}: an optional JSON object with arbitrary extra data about the
|
||||
/// user. If not specified, the previously existing value will not be modified.
|
||||
///
|
||||
/// If the user can be updated by the server, the server will respond with
|
||||
/// @LIT{HTTP 200}.
|
||||
///
|
||||
/// In case of success, the returned JSON object has the following properties:
|
||||
///
|
||||
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{false}
|
||||
/// in this case)
|
||||
///
|
||||
/// - @LIT{code}: the HTTP status code
|
||||
///
|
||||
/// If the JSON representation is malformed or mandatory data is missing from the
|
||||
/// request, the server will respond with @LIT{HTTP 400}. If the specified user
|
||||
/// does not exist, the server will respond with @LIT{HTTP 404}.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{true} in this case)
|
||||
///
|
||||
/// - @LIT{code}: the HTTP status code
|
||||
///
|
||||
/// - @LIT{errorNum}: the server error number
|
||||
///
|
||||
/// - @LIT{errorMessage}: a descriptive error message
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function PATCH_api_user (req, res) {
|
||||
if (req.suffix.length != 1) {
|
||||
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
var username = decodeURIComponent(req.suffix[0]);
|
||||
|
||||
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
|
||||
|
||||
if (json === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var result = users.update(username, json.passwd, json.active, json.extra);
|
||||
users.reload();
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { });
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
actions.resultNotFound(req, res, arangodb.errors.ERROR_USER_NOT_FOUND.code);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove an existing user
|
||||
///
|
||||
/// @RESTHEADER{DELETE /_api/user,removes a user}
|
||||
///
|
||||
/// @REST{DELETE /_api/user/@FA{username}}
|
||||
///
|
||||
/// Removes an existing user, identified by @FA{username}.
|
||||
///
|
||||
/// If the user can be removed by the server, the server will respond with
|
||||
/// @LIT{HTTP 202}.
|
||||
///
|
||||
/// In case of success, the returned JSON object has the following properties:
|
||||
///
|
||||
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{false}
|
||||
/// in this case)
|
||||
///
|
||||
/// - @LIT{code}: the HTTP status code
|
||||
///
|
||||
/// If the JSON representation is malformed or mandatory data is missing from the
|
||||
/// request, the server will respond with @LIT{HTTP 400}. If the specified user
|
||||
/// does not exist, the server will respond with @LIT{HTTP 404}.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - @LIT{error}: boolean flag to indicate that an error occurred (@LIT{true} in this case)
|
||||
///
|
||||
/// - @LIT{code}: the HTTP status code
|
||||
///
|
||||
/// - @LIT{errorNum}: the server error number
|
||||
///
|
||||
/// - @LIT{errorMessage}: a descriptive error message
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function DELETE_api_user (req, res) {
|
||||
if (req.suffix.length != 1) {
|
||||
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
var username = decodeURIComponent(req.suffix[0]);
|
||||
try {
|
||||
var result = users.remove(username);
|
||||
users.reload();
|
||||
actions.resultOk(req, res, actions.HTTP_ACCEPTED, { });
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_USER_NOT_FOUND.code) {
|
||||
actions.resultNotFound(req, res, arangodb.errors.ERROR_USER_NOT_FOUND.code);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- initialiser
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief user actions gateway
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
actions.defineHttp({
|
||||
url : "_api/user",
|
||||
context : "api",
|
||||
|
||||
callback : function (req, res) {
|
||||
try {
|
||||
switch (req.requestType) {
|
||||
case actions.GET:
|
||||
GET_api_user(req, res);
|
||||
break;
|
||||
|
||||
case actions.POST:
|
||||
POST_api_user(req, res);
|
||||
break;
|
||||
|
||||
case actions.PUT:
|
||||
PUT_api_user(req, res);
|
||||
break;
|
||||
|
||||
case actions.PATCH:
|
||||
PATCH_api_user(req, res);
|
||||
break;
|
||||
|
||||
case actions.DELETE:
|
||||
DELETE_api_user(req, res);
|
||||
break;
|
||||
|
||||
default:
|
||||
actions.resultUnsupported(req, res);
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
actions.resultException(req, res, err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
|
||||
// End:
|
|
@ -1116,11 +1116,14 @@ ArangoCollection.prototype.outEdges = function (vertex) {
|
|||
/// @brief removes documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
|
||||
ArangoCollection.prototype.removeByExample = function (example,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var data = {
|
||||
collection: this._name,
|
||||
example: example,
|
||||
waitForSync: waitForSync
|
||||
waitForSync: waitForSync,
|
||||
limit: limit
|
||||
};
|
||||
|
||||
var requestResult = this._database._connection.PUT(
|
||||
|
@ -1132,6 +1135,58 @@ ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
|
|||
return requestResult.deleted;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replaces documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.replaceByExample = function (example,
|
||||
newValue,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var data = {
|
||||
collection: this._name,
|
||||
example: example,
|
||||
newValue: newValue,
|
||||
waitForSync: waitForSync,
|
||||
limit: limit
|
||||
};
|
||||
|
||||
var requestResult = this._database._connection.PUT(
|
||||
"/_api/simple/replace-by-example",
|
||||
JSON.stringify(data));
|
||||
|
||||
arangosh.checkRequestResult(requestResult);
|
||||
|
||||
return requestResult.replaced;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief updates documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.updateByExample = function (example,
|
||||
newValue,
|
||||
keepNull,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var data = {
|
||||
collection: this._name,
|
||||
example: example,
|
||||
newValue: newValue,
|
||||
keepNull: keepNull,
|
||||
waitForSync: waitForSync,
|
||||
limit: limit
|
||||
};
|
||||
|
||||
var requestResult = this._database._connection.PUT(
|
||||
"/_api/simple/update-by-example",
|
||||
JSON.stringify(data));
|
||||
|
||||
arangosh.checkRequestResult(requestResult);
|
||||
|
||||
return requestResult.updated;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true, nonpropdel: true, proto: true */
|
||||
/*global require, module, Module, FS_MOVE, FS_REMOVE, FS_EXISTS, FS_IS_DIRECTORY, FS_LIST_TREE,
|
||||
SYS_EXECUTE, SYS_LOAD, SYS_LOG, SYS_LOG_LEVEL, SYS_OUTPUT, SYS_PROCESS_STAT, SYS_READ,
|
||||
SYS_SPRINTF, SYS_TIME, SYS_START_PAGER, SYS_STOP_PAGER, SYS_SHA256, SYS_WAIT, SYS_GETLINE,
|
||||
SYS_PARSE, SYS_SAVE, SYS_IMPORT_CSV_FILE, SYS_IMPORT_JSON_FILE, SYS_PROCESS_CSV_FILE,
|
||||
SYS_PROCESS_JSON_FILE, ARANGO_QUIET, MODULES_PATH, COLORS, COLOR_OUTPUT, COLOR_OUTPUT_RESET,
|
||||
COLOR_BRIGHT, COLOR_BLACK, COLOR_BOLD_BLACK, COLOR_BLINK, COLOR_BLUE, COLOR_BOLD_BLUE,
|
||||
COLOR_BOLD_GREEN, COLOR_RED, COLOR_BOLD_RED, COLOR_GREEN, COLOR_WHITE, COLOR_BOLD_WHITE,
|
||||
COLOR_YELLOW, COLOR_BOLD_YELLOW, PRETTY_PRINT, VALGRIND, HAS_ICU, VERSION, UPGRADE */
|
||||
SYS_EXECUTE, SYS_LOAD, SYS_LOG, SYS_LOG_LEVEL, SYS_MD5, SYS_OUTPUT, SYS_PROCESS_STAT, SYS_RAND,
|
||||
SYS_READ, SYS_SPRINTF, SYS_TIME, SYS_START_PAGER, SYS_STOP_PAGER, SYS_SHA256, SYS_WAIT,
|
||||
SYS_GETLINE, SYS_PARSE, SYS_SAVE, SYS_IMPORT_CSV_FILE, SYS_IMPORT_JSON_FILE,
|
||||
SYS_PROCESS_CSV_FILE, SYS_PROCESS_JSON_FILE, ARANGO_QUIET, MODULES_PATH, COLORS, COLOR_OUTPUT,
|
||||
COLOR_OUTPUT_RESET, COLOR_BRIGHT, COLOR_BLACK, COLOR_BOLD_BLACK, COLOR_BLINK, COLOR_BLUE,
|
||||
COLOR_BOLD_BLUE, COLOR_BOLD_GREEN, COLOR_RED, COLOR_BOLD_RED, COLOR_GREEN, COLOR_WHITE,
|
||||
COLOR_BOLD_WHITE, COLOR_YELLOW, COLOR_BOLD_YELLOW, PRETTY_PRINT, VALGRIND, HAS_ICU, VERSION,
|
||||
UPGRADE */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief module "internal"
|
||||
|
@ -76,6 +77,11 @@
|
|||
internal.logLevel = SYS_LOG_LEVEL;
|
||||
delete SYS_LOG_LEVEL;
|
||||
}
|
||||
|
||||
if (typeof SYS_MD5 !== "undefined") {
|
||||
internal.md5 = SYS_MD5;
|
||||
delete SYS_MD5;
|
||||
}
|
||||
|
||||
if (typeof SYS_OUTPUT !== "undefined") {
|
||||
internal.stdOutput = SYS_OUTPUT;
|
||||
|
@ -92,6 +98,11 @@
|
|||
internal.processStat = SYS_PROCESS_STAT;
|
||||
delete SYS_PROCESS_STAT;
|
||||
}
|
||||
|
||||
if (typeof SYS_RAND !== "undefined") {
|
||||
internal.rand = SYS_RAND;
|
||||
delete SYS_RAND;
|
||||
}
|
||||
|
||||
if (typeof SYS_READ !== "undefined") {
|
||||
internal.read = SYS_READ;
|
||||
|
@ -798,22 +809,6 @@
|
|||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief encode password using SHA256
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
internal.encodePassword = function (password) {
|
||||
var salt;
|
||||
var encoded;
|
||||
|
||||
salt = internal.sha256("time:" + internal.time());
|
||||
salt = salt.substr(0,8);
|
||||
|
||||
encoded = "$1$" + salt + "$" + internal.sha256(salt + password);
|
||||
|
||||
return encoded;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief extends a prototype
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -672,6 +672,13 @@ ArangoCollection.prototype.iterate = function (iterator, options) {
|
|||
/// synchronisation for collections that have a default @LIT{waitForSync} value
|
||||
/// of @LIT{true}.
|
||||
///
|
||||
/// @FUN{@FA{collection}.removeByExample(@FA{document}, @FA{waitForSync}, @FA{limit})}
|
||||
///
|
||||
/// The optional @FA{limit} parameter can be used to restrict the number of
|
||||
/// removals to the specified value. If @FA{limit} is specified but less than the
|
||||
/// number of documents in the collection, it is undefined which documents are
|
||||
/// removed.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
|
@ -679,10 +686,99 @@ ArangoCollection.prototype.iterate = function (iterator, options) {
|
|||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
|
||||
ArangoCollection.prototype.removeByExample = function (example, waitForSync, limit) {
|
||||
throw "cannot call abstract removeByExample function";
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replaces documents matching an example
|
||||
///
|
||||
/// @FUN{@FA{collection}.replaceByExample(@FA{example}, @FA{newValue})}
|
||||
///
|
||||
/// Replaces all documents matching an example with a new document body.
|
||||
/// The entire document body of each document matching the @FA{example} will be
|
||||
/// replaced with @FA{newValue}. The document meta-attributes such as @LIT{_id},
|
||||
/// @LIT{_key}, @LIT{_from}, @LIT{_to} will not be replaced.
|
||||
///
|
||||
/// @FUN{@FA{collection}.replaceByExample(@FA{document}, @FA{newValue}, @FA{waitForSync})}
|
||||
///
|
||||
/// The optional @FA{waitForSync} parameter can be used to force synchronisation
|
||||
/// of the document replacement operation to disk even in case that the
|
||||
/// @LIT{waitForSync} flag had been disabled for the entire collection. Thus,
|
||||
/// the @FA{waitForSync} parameter can be used to force synchronisation of just
|
||||
/// specific operations. To use this, set the @FA{waitForSync} parameter to
|
||||
/// @LIT{true}. If the @FA{waitForSync} parameter is not specified or set to
|
||||
/// @LIT{false}, then the collection's default @LIT{waitForSync} behavior is
|
||||
/// applied. The @FA{waitForSync} parameter cannot be used to disable
|
||||
/// synchronisation for collections that have a default @LIT{waitForSync} value
|
||||
/// of @LIT{true}.
|
||||
///
|
||||
/// @FUN{@FA{collection}.replaceByExample(@FA{document}, @FA{newValue}, @FA{waitForSync}, @FA{limit})}
|
||||
///
|
||||
/// The optional @FA{limit} parameter can be used to restrict the number of
|
||||
/// replacements to the specified value. If @FA{limit} is specified but less than
|
||||
/// the number of documents in the collection, it is undefined which documents are
|
||||
/// replaced.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangod> db.content.replaceByExample({ "domain": "de.celler" }, { "foo": "someValue }, false, 5)
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.replaceByExample = function (example, newValue, waitForSync, limit) {
|
||||
throw "cannot call abstract replaceByExample function";
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief partially updates documents matching an example
|
||||
///
|
||||
/// @FUN{@FA{collection}.updateByExample(@FA{example}, @FA{newValue})}
|
||||
///
|
||||
/// Partially updates all documents matching an example with a new document body.
|
||||
/// Specific attributes in the document body of each document matching the
|
||||
/// @FA{example} will be updated with the values from @FA{newValue}.
|
||||
/// The document meta-attributes such as @LIT{_id}, @LIT{_key}, @LIT{_from},
|
||||
/// @LIT{_to} cannot be updated.
|
||||
///
|
||||
/// @FUN{@FA{collection}.updateByExample(@FA{document}, @FA{newValue}, @FA{keepNull}, @FA{waitForSync})}
|
||||
///
|
||||
/// The optional @FA{keepNull} parameter can be used to modify the behavior when
|
||||
/// handling @LIT{null} values. Normally, @LIT{null} values are stored in the
|
||||
/// database. By setting the @FA{keepNull} parameter to @LIT{false}, this behavior
|
||||
/// can be changed so that all attributes in @FA{data} with @LIT{null} values will
|
||||
/// be removed from the target document.
|
||||
///
|
||||
/// The optional @FA{waitForSync} parameter can be used to force synchronisation
|
||||
/// of the document replacement operation to disk even in case that the
|
||||
/// @LIT{waitForSync} flag had been disabled for the entire collection. Thus,
|
||||
/// the @FA{waitForSync} parameter can be used to force synchronisation of just
|
||||
/// specific operations. To use this, set the @FA{waitForSync} parameter to
|
||||
/// @LIT{true}. If the @FA{waitForSync} parameter is not specified or set to
|
||||
/// @LIT{false}, then the collection's default @LIT{waitForSync} behavior is
|
||||
/// applied. The @FA{waitForSync} parameter cannot be used to disable
|
||||
/// synchronisation for collections that have a default @LIT{waitForSync} value
|
||||
/// of @LIT{true}.
|
||||
///
|
||||
/// @FUN{@FA{collection}.updateByExample(@FA{document}, @FA{newValue}, @FA{keepNull}, @FA{waitForSync}, @FA{limit})}
|
||||
///
|
||||
/// The optional @FA{limit} parameter can be used to restrict the number of
|
||||
/// updates to the specified value. If @FA{limit} is specified but less than
|
||||
/// the number of documents in the collection, it is undefined which documents are
|
||||
/// updated.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangod> db.content.updateByExample({ "domain": "de.celler" }, { "foo": "someValue, "domain": null }, false)
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.updateByExample = function (example, newValue, keepNull, waitForSync, limit) {
|
||||
throw "cannot call abstract updateExample function";
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, sloppy: true, vars: true, white: true, plusplus: true */
|
||||
/*global require, exports */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Some crypto functions
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2012 triagens GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var internal = require("internal");
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- RANDOM
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup Random
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a random number
|
||||
///
|
||||
/// the value returned might be positive or negative or even 0.
|
||||
/// if random number generation fails, then undefined is returned
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.rand = function (value) {
|
||||
return internal.rand();
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- HASHES
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup Hashes
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief apply an MD5 hash
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.md5 = function (value) {
|
||||
return internal.md5(value);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief apply an SHA 256 hash
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.sha256 = function (value) {
|
||||
return internal.sha256(value);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @}\\|/\\*jslint"
|
||||
// End:
|
|
@ -28,11 +28,11 @@
|
|||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var internal = require("internal"); // OK: encodePassword, reloadAuth
|
||||
var internal = require("internal"); // OK: reloadAuth, time
|
||||
|
||||
var encodePassword = internal.encodePassword;
|
||||
var reloadAuth = internal.reloadAuth;
|
||||
var arangodb = require("org/arangodb");
|
||||
var crypto = require("org/arangodb/crypto");
|
||||
var db = arangodb.db;
|
||||
var ArangoError = require("org/arangodb/arango-error").ArangoError;
|
||||
|
||||
|
@ -49,6 +49,30 @@ var ArangoError = require("org/arangodb/arango-error").ArangoError;
|
|||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief encode password using SHA256
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var encodePassword = function (password) {
|
||||
var salt;
|
||||
var encoded;
|
||||
|
||||
var random = crypto.rand();
|
||||
if (random === undefined) {
|
||||
random = "time:" + internal.time();
|
||||
}
|
||||
else {
|
||||
random = "random:" + random;
|
||||
}
|
||||
|
||||
salt = crypto.sha256(random);
|
||||
salt = salt.substr(0,8);
|
||||
|
||||
encoded = "$1$" + salt + "$" + crypto.sha256(salt + password);
|
||||
|
||||
return encoded;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief validate a username
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -112,14 +136,16 @@ var getStorage = function () {
|
|||
/// @fn JSF_saveUser
|
||||
/// @brief create a new user
|
||||
///
|
||||
/// @FUN{users.save(@FA{username}, @FA{passwd})}
|
||||
/// @FUN{users.save(@FA{username}, @FA{passwd}, @FA{active}, @FA{extra})}
|
||||
///
|
||||
/// This will create a new ArangoDB user. The username must be a string and
|
||||
/// contain only the letters a-z (lower or upper case), the digits 0-9, the dash
|
||||
/// or the underscore symbol.
|
||||
/// This will create a new ArangoDB user. The username must be specified and
|
||||
/// must not be empty.
|
||||
///
|
||||
/// The password must be given as a string, too, but can be left empty if
|
||||
/// required.
|
||||
///
|
||||
/// If the @FA{active} attribute is not specified, it defaults to @LIT{true}.
|
||||
/// The @FA{extra} attribute can be used to save custom data with the user.
|
||||
///
|
||||
/// This method will fail if either the username or the passwords are not
|
||||
/// specified or given in a wrong format, or there already exists a user with
|
||||
|
@ -138,7 +164,7 @@ var getStorage = function () {
|
|||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.save = function (username, passwd) {
|
||||
exports.save = function (username, passwd, active, extra) {
|
||||
if (passwd === null || passwd === undefined) {
|
||||
passwd = "";
|
||||
}
|
||||
|
@ -150,9 +176,24 @@ exports.save = function (username, passwd) {
|
|||
var users = getStorage();
|
||||
var user = users.firstExample({ user: username });
|
||||
|
||||
if (active === undefined || active === null) {
|
||||
// this is the default value for active
|
||||
active = true;
|
||||
}
|
||||
|
||||
if (user === null) {
|
||||
var hash = encodePassword(passwd);
|
||||
return users.save({ user: username, password: hash, active: true });
|
||||
var data = {
|
||||
user: username,
|
||||
password: hash,
|
||||
active: active
|
||||
};
|
||||
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
return users.save(data);
|
||||
}
|
||||
|
||||
var err = new ArangoError();
|
||||
|
@ -164,25 +205,27 @@ exports.save = function (username, passwd) {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_replaceUser
|
||||
/// @brief update an existing user
|
||||
/// @brief replace an existing user
|
||||
///
|
||||
/// @FUN{users.replace(@FA{username}, @FA{passwd})}
|
||||
/// @FUN{users.replace(@FA{username}, @FA{passwd}, @FA{active}, @FA{extra})}
|
||||
///
|
||||
/// This will update an existing ArangoDB user with a new password.
|
||||
/// This will look up an existing ArangoDB user and replace its user data.
|
||||
///
|
||||
/// The username must be a string and contain only the letters a-z (lower or
|
||||
/// upper case), the digits 0-9, the dash or the underscore symbol. The user
|
||||
/// must already exist in the database.
|
||||
/// The username must be specified, and a user with the specified name must
|
||||
/// already exist in the database.
|
||||
///
|
||||
/// The password must be given as a string, too, but can be left empty if
|
||||
/// required.
|
||||
///
|
||||
/// If the @FA{active} attribute is not specified, it defaults to @LIT{true}.
|
||||
/// The @FA{extra} attribute can be used to save custom data with the user.
|
||||
///
|
||||
/// This method will fail if either the username or the passwords are not
|
||||
/// specified or given in a wrong format, or if the specified user cannot be
|
||||
/// found in the database.
|
||||
///
|
||||
/// The update is effective only after the server is either restarted
|
||||
/// or the server authentication cache is @ref UserManagementReload "reloaded".
|
||||
/// The replace is effective only after the server is either restarted
|
||||
/// or the server authentication cache is reloaded (see @ref JSF_reloadUsers).
|
||||
///
|
||||
/// Note: this function will not work from within the web interface
|
||||
///
|
||||
|
@ -194,8 +237,7 @@ exports.save = function (username, passwd) {
|
|||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.replace =
|
||||
exports.update = function (username, passwd) {
|
||||
exports.replace = function (username, passwd, active, extra) {
|
||||
if (passwd === null || passwd === undefined) {
|
||||
passwd = "";
|
||||
}
|
||||
|
@ -207,6 +249,11 @@ exports.update = function (username, passwd) {
|
|||
var users = getStorage();
|
||||
var user = users.firstExample({ user: username });
|
||||
|
||||
if (active === undefined || active === null) {
|
||||
// this is the default
|
||||
active = true;
|
||||
}
|
||||
|
||||
if (user === null) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_NOT_FOUND.code;
|
||||
|
@ -216,11 +263,84 @@ exports.update = function (username, passwd) {
|
|||
}
|
||||
|
||||
var hash = encodePassword(passwd);
|
||||
var data = user.shallowCopy;
|
||||
data.password = hash;
|
||||
var data = {
|
||||
user: username,
|
||||
password: hash,
|
||||
active: active
|
||||
};
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
return users.replace(user, data);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_updateUser
|
||||
/// @brief update an existing user
|
||||
///
|
||||
/// @FUN{@FA{users}.update(@FA{username}, @FA{passwd}, @FA{active}, @FA{extra})}
|
||||
///
|
||||
/// This will update an existing ArangoDB user with a new password and other
|
||||
/// data.
|
||||
///
|
||||
/// The username must be specified and the user must already exist in the
|
||||
/// database.
|
||||
///
|
||||
/// The password must be given as a string, too, but can be left empty if
|
||||
/// required.
|
||||
///
|
||||
/// If the @FA{active} attribute is not specified, the current value saved for
|
||||
/// the user will not be changed. The same is true for the @FA{extra} attribute.
|
||||
///
|
||||
/// This method will fail if either the username or the passwords are not
|
||||
/// specified or given in a wrong format, or if the specified user cannot be
|
||||
/// found in the database.
|
||||
///
|
||||
/// The update is effective only after the server is either restarted
|
||||
/// or the server authentication cache is reloaded (see @ref JSF_reloadUsers).
|
||||
///
|
||||
/// Note: this function will not work from within the web interface
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @TINYEXAMPLE{user-replace,replacing an existing user}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.update = function (username, passwd, active, extra) {
|
||||
// validate input
|
||||
validateName(username);
|
||||
if (passwd !== undefined) {
|
||||
validatePassword(passwd);
|
||||
}
|
||||
|
||||
var users = getStorage();
|
||||
var user = users.firstExample({ user: username });
|
||||
|
||||
if (user === null) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_NOT_FOUND.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_USER_NOT_FOUND.message;
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
var data = user.shallowCopy;
|
||||
|
||||
if (passwd !== undefined) {
|
||||
var hash = encodePassword(passwd);
|
||||
data.password = hash;
|
||||
}
|
||||
if (active !== undefined && active !== null) {
|
||||
data.active = active;
|
||||
}
|
||||
if (extra !== undefined) {
|
||||
data.extra = extra;
|
||||
}
|
||||
|
||||
return users.update(user, data);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_removeUser
|
||||
/// @brief delete an existing user
|
||||
|
@ -229,9 +349,8 @@ exports.update = function (username, passwd) {
|
|||
///
|
||||
/// Removes an existing ArangoDB user from the database.
|
||||
///
|
||||
/// The username must be a string and contain only the letters a-z (lower or
|
||||
/// upper case), the digits 0-9, the dash or the underscore symbol. The user
|
||||
/// must already exist in the database.
|
||||
/// The username must be a string and the specified user must exist in the
|
||||
/// database.
|
||||
///
|
||||
/// This method will fail if the user cannot be found in the database.
|
||||
///
|
||||
|
@ -266,6 +385,39 @@ exports.remove = function (username) {
|
|||
return users.remove(user._id);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_documentUser
|
||||
/// @brief get an existing user
|
||||
///
|
||||
/// @FUN{users.document(@FA{username})}
|
||||
///
|
||||
/// Fetches an existing ArangoDB user from the database.
|
||||
///
|
||||
/// This method will fail if the user cannot be found in the database.
|
||||
///
|
||||
/// Note: this function will not work from within the web interface
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.document = function (username) {
|
||||
// validate name
|
||||
validateName(username);
|
||||
|
||||
var users = getStorage();
|
||||
var user = users.firstExample({ user: username });
|
||||
|
||||
if (user === null) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_USER_NOT_FOUND.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_USER_NOT_FOUND.message;
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
delete user.passwd;
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_reloadUsers
|
||||
/// @brief reloads the user authentication data
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test the crypto interface
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
///
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var jsunity = require("jsunity");
|
||||
var crypto = require("org/arangodb/crypto");
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- crypto methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test suite
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function CryptoSuite () {
|
||||
return {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test md5, invalid values
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testMd5Invalid : function () {
|
||||
[ undefined, null, true, false, 0, 1, -1, 32.5, [ ], { } ].forEach(function (value) {
|
||||
try {
|
||||
crypto.md5(value);
|
||||
fail();
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test md5
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testMd5 : function () {
|
||||
var data = [
|
||||
[ "", "d41d8cd98f00b204e9800998ecf8427e" ],
|
||||
[ " ", "7215ee9c7d9dc229d2921a40e899ec5f" ],
|
||||
[ "arangodb", "335889e69e03cefd041babef1b02a0b8" ],
|
||||
[ "Arangodb", "0862bc79ec789143f75e3282df98c8f4" ],
|
||||
[ "ArangoDB is a database", "b88ddc26cfa3a652fdd8bf8e8c069540" ]
|
||||
];
|
||||
|
||||
data.forEach(function (value) {
|
||||
assertEqual(value[1], crypto.md5(value[0]));
|
||||
});
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test sha256, invalid values
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testSha256Invalid : function () {
|
||||
[ undefined, null, true, false, 0, 1, -1, 32.5, [ ], { } ].forEach(function (value) {
|
||||
try {
|
||||
crypto.sha256(value);
|
||||
fail();
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test sha256
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testSha256 : function () {
|
||||
var data = [
|
||||
[ "", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ],
|
||||
[ " ", "36a9e7f1c95b82ffb99743e0c5c4ce95d83c9a430aac59f84ef3cbfab6145068"],
|
||||
[ "arangodb", "d0a274654772fa104df32ff457ff0a432f2dfad18e7c3146fab3c807f2ed86e5" ],
|
||||
[ "Arangodb", "38579e73fd7435de7db93028a1b340be77445b46c94dff013c05c696ccae259c" ],
|
||||
[ "ArangoDB is a database", "00231e8f9c0a617426ae51e4e230a1b25f6d5b82c10fccc835b514142f235f31" ]
|
||||
];
|
||||
|
||||
data.forEach(function (value) {
|
||||
assertEqual(value[1], crypto.sha256(value[0]));
|
||||
});
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test random
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRandom : function () {
|
||||
var i;
|
||||
|
||||
for (i = 0; i < 100; ++i) {
|
||||
assertTrue(crypto.rand() != 0);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- main
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief executes the test suite
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
jsunity.run(CryptoSuite);
|
||||
|
||||
return jsunity.done();
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
|
||||
// End:
|
||||
|
|
@ -886,19 +886,22 @@ function CollectionDocumentSuite () {
|
|||
assertEqual(3, figures.alive.count);
|
||||
assertEqual(0, figures.dead.count);
|
||||
|
||||
// now remove some
|
||||
// now remove some documents
|
||||
collection.remove("a2");
|
||||
collection.remove("a3");
|
||||
|
||||
// we should see two live docs less
|
||||
figures = collection.figures();
|
||||
assertEqual(1, figures.alive.count);
|
||||
assertEqual(2, figures.dead.count);
|
||||
|
||||
// replacing one document does not change alive, but increases dead!
|
||||
collection.replace("a1", { });
|
||||
figures = collection.figures();
|
||||
assertEqual(1, figures.alive.count);
|
||||
assertEqual(3, figures.dead.count);
|
||||
|
||||
// this doc does not exist. should not change the figures
|
||||
try {
|
||||
collection.replace("a2", { });
|
||||
fail();
|
||||
|
|
|
@ -468,7 +468,249 @@ function SimpleQueryByExampleSuite () {
|
|||
collection.truncate();
|
||||
deleted = collection.removeByExample({ value1: 3 });
|
||||
assertEqual(0, deleted);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test: removeByExample
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRemoveByExampleLimit : function () {
|
||||
var deleted;
|
||||
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
collection.save({ value : 1 });
|
||||
}
|
||||
|
||||
deleted = collection.removeByExample({ value : 1 }, false, 10);
|
||||
assertEqual(10, deleted);
|
||||
|
||||
assertEqual(40, collection.count());
|
||||
|
||||
deleted = collection.removeByExample({ value : 1 }, false, 20);
|
||||
assertEqual(20, deleted);
|
||||
|
||||
assertEqual(20, collection.count());
|
||||
|
||||
deleted = collection.removeByExample({ value : 1 }, false, 20);
|
||||
assertEqual(20, deleted);
|
||||
|
||||
assertEqual(0, collection.count());
|
||||
|
||||
deleted = collection.removeByExample({ value : 1 }, false, 20);
|
||||
assertEqual(0, deleted);
|
||||
|
||||
assertEqual(0, collection.count());
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test: replaceByExample
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testReplaceByExample : function () {
|
||||
var replaced;
|
||||
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
collection.save({ value : i });
|
||||
}
|
||||
|
||||
replaced = collection.replaceByExample({ value : 2 }, { foo : "bar", bar : "baz" });
|
||||
assertEqual(1, replaced);
|
||||
|
||||
assertEqual(50, collection.count());
|
||||
|
||||
var doc = collection.firstExample({ foo : "bar", bar : "baz" });
|
||||
assertEqual("bar", doc.foo);
|
||||
assertEqual("baz", doc.bar);
|
||||
assertEqual(undefined, doc.value);
|
||||
|
||||
// not existing documents
|
||||
replaced = collection.replaceByExample({ meow : true }, { });
|
||||
assertEqual(0, replaced);
|
||||
|
||||
replaced = collection.replaceByExample({ value : null }, { });
|
||||
assertEqual(0, replaced);
|
||||
|
||||
collection.truncate();
|
||||
replaced = collection.replaceByExample({ value : 6 }, { });
|
||||
assertEqual(0, replaced);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test: replaceByExample
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testReplaceByExampleLimit : function () {
|
||||
var replaced, docs;
|
||||
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
collection.save({ value : 1 });
|
||||
}
|
||||
|
||||
replaced = collection.replaceByExample({ value : 1 }, { foo : "bar", bar : "baz" }, false, 10);
|
||||
assertEqual(10, replaced);
|
||||
|
||||
assertEqual(50, collection.count());
|
||||
|
||||
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
|
||||
assertEqual(10, docs.length);
|
||||
docs = collection.byExample({ value : 1 }).toArray();
|
||||
assertEqual(40, docs.length);
|
||||
|
||||
replaced = collection.replaceByExample({ value : 1 }, { meow : false }, false, 15);
|
||||
assertEqual(15, replaced);
|
||||
|
||||
assertEqual(50, collection.count());
|
||||
|
||||
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
|
||||
assertEqual(10, docs.length);
|
||||
docs = collection.byExample({ meow : false }).toArray();
|
||||
assertEqual(15, docs.length);
|
||||
docs = collection.byExample({ value : 1 }).toArray();
|
||||
assertEqual(25, docs.length);
|
||||
|
||||
// not existing documents
|
||||
replaced = collection.replaceByExample({ meow : true }, { }, false, 99);
|
||||
assertEqual(0, replaced);
|
||||
|
||||
replaced = collection.replaceByExample({ value : null }, { }, false, 99);
|
||||
assertEqual(0, replaced);
|
||||
|
||||
// check counts
|
||||
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
|
||||
assertEqual(10, docs.length);
|
||||
docs = collection.byExample({ meow : false }).toArray();
|
||||
assertEqual(15, docs.length);
|
||||
docs = collection.byExample({ value : 1 }).toArray();
|
||||
assertEqual(25, docs.length);
|
||||
|
||||
collection.truncate();
|
||||
replaced = collection.replaceByExample({ value : 1 }, { }, false);
|
||||
assertEqual(0, replaced);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test: updateByExample
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testUpdateByExample : function () {
|
||||
var updated;
|
||||
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
collection.save({ value : i });
|
||||
}
|
||||
|
||||
// update and keep old values
|
||||
updated = collection.updateByExample({ value : 2 }, { foo : "bar", bar : "baz" });
|
||||
assertEqual(1, updated);
|
||||
|
||||
assertEqual(50, collection.count());
|
||||
|
||||
var doc = collection.firstExample({ foo : "bar", bar : "baz" });
|
||||
assertEqual("bar", doc.foo);
|
||||
assertEqual("baz", doc.bar);
|
||||
assertEqual(2, doc.value);
|
||||
|
||||
// update and remove old values
|
||||
updated = collection.updateByExample({ value : 5 }, { foo : "bart", bar : "baz", value : null }, false);
|
||||
assertEqual(1, updated);
|
||||
|
||||
var doc = collection.firstExample({ foo : "bart", bar : "baz" });
|
||||
assertEqual("bart", doc.foo);
|
||||
assertEqual("baz", doc.bar);
|
||||
assertEqual(undefined, doc.value);
|
||||
|
||||
// update and overwrite old values
|
||||
updated = collection.updateByExample({ value : 17 }, { foo : "barw", bar : "baz", value : 9 }, false);
|
||||
assertEqual(1, updated);
|
||||
|
||||
var doc = collection.firstExample({ foo : "barw", bar : "baz" });
|
||||
assertEqual("barw", doc.foo);
|
||||
assertEqual("baz", doc.bar);
|
||||
assertEqual(9, doc.value);
|
||||
|
||||
// not existing documents
|
||||
updated = collection.updateByExample({ meow : true }, { });
|
||||
assertEqual(0, updated);
|
||||
|
||||
updated = collection.updateByExample({ value : null }, { });
|
||||
assertEqual(0, updated);
|
||||
|
||||
collection.truncate();
|
||||
updated = collection.updateByExample({ value : 6 }, { });
|
||||
assertEqual(0, updated);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test: updateByExample
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testUpdateByExampleLimit : function () {
|
||||
var updated, docs;
|
||||
|
||||
for (var i = 0; i < 50; ++i) {
|
||||
collection.save({ value : 1 });
|
||||
}
|
||||
|
||||
// update some, keep old values
|
||||
updated = collection.updateByExample({ value : 1 }, { foo : "bar", bar : "baz" }, false, false, 10);
|
||||
assertEqual(10, updated);
|
||||
|
||||
assertEqual(50, collection.count());
|
||||
|
||||
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
|
||||
assertEqual(10, docs.length);
|
||||
docs = collection.byExample({ value : 1 }).toArray();
|
||||
assertEqual(50, docs.length);
|
||||
|
||||
// update some others
|
||||
updated = collection.updateByExample({ value : 1 }, { meow : false }, false, false, 15);
|
||||
assertEqual(15, updated);
|
||||
|
||||
assertEqual(50, collection.count());
|
||||
|
||||
docs = collection.byExample({ foo : "bar", bar : "baz" }).toArray();
|
||||
assertEqual(10, docs.length);
|
||||
docs = collection.byExample({ meow : false }).toArray();
|
||||
assertEqual(15, docs.length);
|
||||
docs = collection.byExample({ value : 1 }).toArray();
|
||||
assertEqual(50, docs.length);
|
||||
|
||||
// update some, remove old values
|
||||
updated = collection.updateByExample({ value : 1 }, { value : null, fox : true }, false, false, 5);
|
||||
assertEqual(5, updated);
|
||||
|
||||
assertEqual(50, collection.count());
|
||||
|
||||
docs = collection.byExample({ fox : true }).toArray();
|
||||
assertEqual(5, docs.length);
|
||||
docs = collection.byExample({ value : 1 }).toArray();
|
||||
assertEqual(45, docs.length);
|
||||
|
||||
// update some, overwrite old values
|
||||
updated = collection.updateByExample({ value : 1 }, { value : 16 }, false, false, 10);
|
||||
assertEqual(10, updated);
|
||||
|
||||
assertEqual(50, collection.count());
|
||||
|
||||
docs = collection.byExample({ value : 16 }).toArray();
|
||||
assertEqual(10, docs.length);
|
||||
docs = collection.byExample({ fox : true }).toArray();
|
||||
assertEqual(5, docs.length);
|
||||
docs = collection.byExample({ value : 1 }).toArray();
|
||||
assertEqual(35, docs.length);
|
||||
|
||||
// not existing documents
|
||||
updated = collection.updateByExample({ meow : true }, { }, false, false, 99);
|
||||
assertEqual(0, updated);
|
||||
|
||||
updated = collection.updateByExample({ value : null }, { }, false, false, 99);
|
||||
assertEqual(0, updated);
|
||||
|
||||
collection.truncate();
|
||||
updated = collection.updateByExample({ value : 1 }, { });
|
||||
assertEqual(0, updated);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -252,11 +252,20 @@ ArangoCollection.prototype.firstExample = function (example) {
|
|||
/// @brief removes documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
|
||||
ArangoCollection.prototype.removeByExample = function (example,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var deleted = 0;
|
||||
var documents;
|
||||
|
||||
if (limit === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
documents = this.byExample(example);
|
||||
if (limit > 0) {
|
||||
documents = documents.limit(limit);
|
||||
}
|
||||
|
||||
while (documents.hasNext()) {
|
||||
var document = documents.next();
|
||||
|
@ -269,6 +278,85 @@ ArangoCollection.prototype.removeByExample = function (example, waitForSync) {
|
|||
return deleted;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replaces documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.replaceByExample = function (example,
|
||||
newValue,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var replaced = 0;
|
||||
var documents;
|
||||
|
||||
if (limit === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (typeof newValue !== "object") {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid value for parameter 'newValue'";
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
documents = this.byExample(example);
|
||||
if (limit > 0) {
|
||||
documents = documents.limit(limit);
|
||||
}
|
||||
|
||||
while (documents.hasNext()) {
|
||||
var document = documents.next();
|
||||
|
||||
if (this.replace(document, newValue, true, waitForSync)) {
|
||||
replaced++;
|
||||
}
|
||||
}
|
||||
|
||||
return replaced;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief partially updates documents matching an example
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ArangoCollection.prototype.updateByExample = function (example,
|
||||
newValue,
|
||||
keepNull,
|
||||
waitForSync,
|
||||
limit) {
|
||||
var updated = 0;
|
||||
var documents;
|
||||
|
||||
if (limit === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (typeof newValue !== "object") {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = internal.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = "invalid value for parameter 'newValue'";
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
documents = this.byExample(example);
|
||||
if (limit > 0) {
|
||||
documents = documents.limit(limit);
|
||||
}
|
||||
|
||||
while (documents.hasNext()) {
|
||||
var document = documents.next();
|
||||
|
||||
if (this.update(document, newValue, true, keepNull, waitForSync)) {
|
||||
updated++;
|
||||
}
|
||||
}
|
||||
|
||||
return updated;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
(function() {
|
||||
var internal = require("internal");
|
||||
var console = require("console");
|
||||
var userManager = require("org/arangodb/users");
|
||||
var db = internal.db;
|
||||
|
||||
// path to the VERSION file
|
||||
|
@ -147,7 +148,7 @@
|
|||
|
||||
if (users.count() === 0) {
|
||||
// only add account if user has not created his/her own accounts already
|
||||
users.save({ user: "root", password: internal.encodePassword(""), active: true });
|
||||
userManager.save("root", "", true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -452,6 +452,17 @@ void TRI_InitArrayJson (TRI_memory_zone_t* zone, TRI_json_t* result) {
|
|||
TRI_InitVector(&result->_value._objects, zone, sizeof(TRI_json_t));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initialises an array, using a specific initial size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_Init2ArrayJson (TRI_memory_zone_t* zone,
|
||||
TRI_json_t* result,
|
||||
size_t initialSize) {
|
||||
result->_type = TRI_JSON_ARRAY;
|
||||
TRI_InitVector2(&result->_value._objects, zone, sizeof(TRI_json_t), initialSize);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroys a json object, but does not free the pointer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -173,6 +173,12 @@ TRI_json_t* TRI_CreateArrayJson (TRI_memory_zone_t*);
|
|||
|
||||
void TRI_InitArrayJson (TRI_memory_zone_t*, TRI_json_t*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initialises an array, using a specific initial size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TRI_Init2ArrayJson (TRI_memory_zone_t*, TRI_json_t*, size_t);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroys a json object, but does not free the pointer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -71,7 +71,6 @@ void TRI_InitVector (TRI_vector_t* vector, TRI_memory_zone_t* zone, size_t eleme
|
|||
vector->_buffer = NULL;
|
||||
vector->_length = 0;
|
||||
vector->_capacity = 0;
|
||||
vector->_growthFactor = GROW_FACTOR;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -81,15 +80,10 @@ void TRI_InitVector (TRI_vector_t* vector, TRI_memory_zone_t* zone, size_t eleme
|
|||
int TRI_InitVector2 (TRI_vector_t* vector,
|
||||
TRI_memory_zone_t* zone,
|
||||
size_t elementSize,
|
||||
size_t initialCapacity,
|
||||
double growthFactor) {
|
||||
size_t initialCapacity) {
|
||||
// init vector as usual
|
||||
TRI_InitVector(vector, zone, elementSize);
|
||||
|
||||
if (growthFactor > 1.0) {
|
||||
vector->_growthFactor = growthFactor;
|
||||
}
|
||||
|
||||
if (initialCapacity != 0) {
|
||||
vector->_buffer = (char*) TRI_Allocate(vector->_memoryZone, (initialCapacity * vector->_elementSize), false);
|
||||
if (vector->_buffer == NULL) {
|
||||
|
@ -252,7 +246,7 @@ int TRI_ResizeVector (TRI_vector_t* vector, size_t n) {
|
|||
int TRI_PushBackVector (TRI_vector_t* vector, void const* element) {
|
||||
if (vector->_length == vector->_capacity) {
|
||||
char* newBuffer;
|
||||
size_t newSize = (size_t) (1 + (vector->_growthFactor * vector->_capacity));
|
||||
size_t newSize = (size_t) (1 + (GROW_FACTOR * vector->_capacity));
|
||||
|
||||
newBuffer = (char*) TRI_Reallocate(vector->_memoryZone, vector->_buffer, newSize * vector->_elementSize);
|
||||
|
||||
|
@ -312,7 +306,7 @@ void TRI_InsertVector (TRI_vector_t* vector, void const* element, size_t positio
|
|||
// ...........................................................................
|
||||
|
||||
if (vector->_length >= vector->_capacity || position >= vector->_length) {
|
||||
size_t newSize = (size_t) (1 + (vector->_growthFactor * vector->_capacity));
|
||||
size_t newSize = (size_t) (1 + (GROW_FACTOR * vector->_capacity));
|
||||
|
||||
if (position >= newSize) {
|
||||
newSize = position + 1;
|
||||
|
|
|
@ -57,7 +57,6 @@ typedef struct TRI_vector_s {
|
|||
char * _buffer;
|
||||
size_t _length;
|
||||
size_t _capacity;
|
||||
double _growthFactor;
|
||||
}
|
||||
TRI_vector_t;
|
||||
|
||||
|
@ -87,8 +86,7 @@ void TRI_InitVector (TRI_vector_t*, TRI_memory_zone_t*, size_t elementSize);
|
|||
int TRI_InitVector2 (TRI_vector_t*,
|
||||
TRI_memory_zone_t*,
|
||||
size_t elementSize,
|
||||
size_t initialCapacity,
|
||||
double growthFactor);
|
||||
size_t initialCapacity);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroys a vector, but does not free the pointer
|
||||
|
|
|
@ -89,7 +89,6 @@ typedef int16_t flex_int16_t;
|
|||
typedef uint16_t flex_uint16_t;
|
||||
typedef int32_t flex_int32_t;
|
||||
typedef uint32_t flex_uint32_t;
|
||||
typedef uint64_t flex_uint64_t;
|
||||
#else
|
||||
typedef signed char flex_int8_t;
|
||||
typedef short int flex_int16_t;
|
||||
|
@ -213,11 +212,6 @@ typedef void* yyscan_t;
|
|||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
#endif
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_SIZE_T
|
||||
#define YY_TYPEDEF_YY_SIZE_T
|
||||
typedef size_t yy_size_t;
|
||||
#endif
|
||||
|
||||
#define EOB_ACT_CONTINUE_SCAN 0
|
||||
#define EOB_ACT_END_OF_FILE 1
|
||||
#define EOB_ACT_LAST_MATCH 2
|
||||
|
@ -240,6 +234,11 @@ typedef size_t yy_size_t;
|
|||
|
||||
#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_SIZE_T
|
||||
#define YY_TYPEDEF_YY_SIZE_T
|
||||
typedef size_t yy_size_t;
|
||||
#endif
|
||||
|
||||
#ifndef YY_STRUCT_YY_BUFFER_STATE
|
||||
#define YY_STRUCT_YY_BUFFER_STATE
|
||||
struct yy_buffer_state
|
||||
|
@ -257,7 +256,7 @@ struct yy_buffer_state
|
|||
/* Number of characters read into yy_ch_buf, not including EOB
|
||||
* characters.
|
||||
*/
|
||||
yy_size_t yy_n_chars;
|
||||
int yy_n_chars;
|
||||
|
||||
/* Whether we "own" the buffer - i.e., we know we created it,
|
||||
* and can realloc() it to grow it, and should free() it to
|
||||
|
@ -336,7 +335,7 @@ static void tri_jsp__init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscann
|
|||
|
||||
YY_BUFFER_STATE tri_jsp__scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE tri_jsp__scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
|
||||
|
||||
void *tri_jsp_alloc (yy_size_t ,yyscan_t yyscanner );
|
||||
void *tri_jsp_realloc (void *,yy_size_t ,yyscan_t yyscanner );
|
||||
|
@ -387,7 +386,7 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
|
|||
*/
|
||||
#define YY_DO_BEFORE_ACTION \
|
||||
yyg->yytext_ptr = yy_bp; \
|
||||
yyleng = (yy_size_t) (yy_cp - yy_bp); \
|
||||
yyleng = (size_t) (yy_cp - yy_bp); \
|
||||
yyg->yy_hold_char = *yy_cp; \
|
||||
*yy_cp = '\0'; \
|
||||
yyg->yy_c_buf_p = yy_cp;
|
||||
|
@ -534,8 +533,8 @@ static yyconst flex_int16_t yy_chk[152] =
|
|||
#define STRING_CONSTANT_ASCII 13
|
||||
|
||||
struct jsonData {
|
||||
char const* _message;
|
||||
TRI_memory_zone_t* _memoryZone;
|
||||
char const* _message;
|
||||
};
|
||||
|
||||
#define YY_FATAL_ERROR(a) \
|
||||
|
@ -566,8 +565,8 @@ struct yyguts_t
|
|||
size_t yy_buffer_stack_max; /**< capacity of stack. */
|
||||
YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
|
||||
char yy_hold_char;
|
||||
yy_size_t yy_n_chars;
|
||||
yy_size_t yyleng_r;
|
||||
int yy_n_chars;
|
||||
int yyleng_r;
|
||||
char *yy_c_buf_p;
|
||||
int yy_init;
|
||||
int yy_start;
|
||||
|
@ -614,7 +613,7 @@ FILE *tri_jsp_get_out (yyscan_t yyscanner );
|
|||
|
||||
void tri_jsp_set_out (FILE * out_str ,yyscan_t yyscanner );
|
||||
|
||||
yy_size_t tri_jsp_get_leng (yyscan_t yyscanner );
|
||||
int tri_jsp_get_leng (yyscan_t yyscanner );
|
||||
|
||||
char *tri_jsp_get_text (yyscan_t yyscanner );
|
||||
|
||||
|
@ -673,7 +672,7 @@ static int input (yyscan_t yyscanner );
|
|||
if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
|
||||
{ \
|
||||
int c = '*'; \
|
||||
yy_size_t n; \
|
||||
int n; \
|
||||
for ( n = 0; n < max_size && \
|
||||
(c = getc( yyin )) != EOF && c != '\n'; ++n ) \
|
||||
buf[n] = (char) c; \
|
||||
|
@ -1129,7 +1128,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
|||
|
||||
else
|
||||
{
|
||||
yy_size_t num_to_read =
|
||||
int num_to_read =
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
|
||||
|
||||
while ( num_to_read <= 0 )
|
||||
|
@ -1143,7 +1142,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
|||
|
||||
if ( b->yy_is_our_buffer )
|
||||
{
|
||||
yy_size_t new_size = b->yy_buf_size * 2;
|
||||
int new_size = b->yy_buf_size * 2;
|
||||
|
||||
if ( new_size <= 0 )
|
||||
b->yy_buf_size += b->yy_buf_size / 8;
|
||||
|
@ -1174,7 +1173,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
|||
|
||||
/* Read in more data. */
|
||||
YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
|
||||
yyg->yy_n_chars, num_to_read );
|
||||
yyg->yy_n_chars, (int) num_to_read );
|
||||
|
||||
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
|
||||
}
|
||||
|
@ -1299,7 +1298,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
|||
|
||||
else
|
||||
{ /* need more input */
|
||||
yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
|
||||
int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
|
||||
++yyg->yy_c_buf_p;
|
||||
|
||||
switch ( yy_get_next_buffer( yyscanner ) )
|
||||
|
@ -1323,7 +1322,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
|
|||
case EOB_ACT_END_OF_FILE:
|
||||
{
|
||||
if ( tri_jsp_wrap(yyscanner ) )
|
||||
return 0;
|
||||
return EOF;
|
||||
|
||||
if ( ! yyg->yy_did_buffer_switch_on_eof )
|
||||
YY_NEW_FILE;
|
||||
|
@ -1585,7 +1584,7 @@ void tri_jsp_pop_buffer_state (yyscan_t yyscanner)
|
|||
*/
|
||||
static void tri_jsp_ensure_buffer_stack (yyscan_t yyscanner)
|
||||
{
|
||||
yy_size_t num_to_alloc;
|
||||
int num_to_alloc;
|
||||
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
|
||||
|
||||
if (!yyg->yy_buffer_stack) {
|
||||
|
@ -1683,11 +1682,12 @@ YY_BUFFER_STATE tri_jsp__scan_string (yyconst char * yystr , yyscan_t yyscanner)
|
|||
* @param yyscanner The scanner object.
|
||||
* @return the newly allocated buffer state object.
|
||||
*/
|
||||
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner)
|
||||
YY_BUFFER_STATE tri_jsp__scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
|
||||
{
|
||||
YY_BUFFER_STATE b;
|
||||
char *buf;
|
||||
yy_size_t n, i;
|
||||
yy_size_t n;
|
||||
int i;
|
||||
|
||||
/* Get memory for full buffer, including space for trailing EOB's. */
|
||||
n = _yybytes_len + 2;
|
||||
|
@ -1797,7 +1797,7 @@ FILE *tri_jsp_get_out (yyscan_t yyscanner)
|
|||
/** Get the length of the current token.
|
||||
* @param yyscanner The scanner object.
|
||||
*/
|
||||
yy_size_t tri_jsp_get_leng (yyscan_t yyscanner)
|
||||
int tri_jsp_get_leng (yyscan_t yyscanner)
|
||||
{
|
||||
struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
|
||||
return yyleng;
|
||||
|
|
|
@ -70,8 +70,8 @@ PLUS [+]
|
|||
#define STRING_CONSTANT_ASCII 13
|
||||
|
||||
struct jsonData {
|
||||
char const* _message;
|
||||
TRI_memory_zone_t* _memoryZone;
|
||||
char const* _message;
|
||||
};
|
||||
|
||||
#define YY_FATAL_ERROR(a) \
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include <Basics/RandomGenerator.h>
|
||||
#include <Basics/StringUtils.h>
|
||||
|
@ -130,7 +131,6 @@ namespace triagens {
|
|||
}
|
||||
|
||||
|
||||
|
||||
void sslSHA256 (char const* inputStr, char*& outputStr, size_t& outputLen) {
|
||||
sslSHA256(inputStr, strlen(inputStr), outputStr, outputLen);
|
||||
}
|
||||
|
@ -180,9 +180,8 @@ namespace triagens {
|
|||
void sslBASE64 (char const* inputStr, char*& outputStr, size_t& outputLen) {
|
||||
sslBASE64(inputStr, strlen(inputStr), outputStr, outputLen);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
string sslHMAC (char const* key, char const* message, size_t messageLen) {
|
||||
const EVP_MD * evp_md = EVP_sha256();
|
||||
unsigned char* md = (unsigned char*) TRI_SystemAllocate(EVP_MAX_MD_SIZE + 1, false);
|
||||
|
@ -212,7 +211,30 @@ namespace triagens {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
int sslRand (uint64_t* value) {
|
||||
if (! RAND_pseudo_bytes((unsigned char *) value, sizeof(uint64_t))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sslRand (int64_t* value) {
|
||||
if (! RAND_pseudo_bytes((unsigned char *) value, sizeof(int64_t))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sslRand (int32_t* value) {
|
||||
if (! RAND_pseudo_bytes((unsigned char *) value, sizeof(int32_t))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void salt64 (uint64_t& result) {
|
||||
string salt = SaltGenerator.random(8);
|
||||
|
|
|
@ -99,12 +99,12 @@ namespace triagens {
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void sslHEX (char const* inputStr, char*& outputStr, size_t& outputLen);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// @brief BASE64
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void sslBAE64 (char const* inputStr, const size_t length, char*& outputStr, size_t& outputLen);
|
||||
void sslBASE64 (char const* inputStr, const size_t length, char*& outputStr, size_t& outputLen);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// @brief BASE64
|
||||
|
@ -123,12 +123,36 @@ namespace triagens {
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool verifyHMAC (char const* challenge, char const* secret, size_t secretLen, char const* response, size_t responseLen);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a random number using OpenSsl
|
||||
///
|
||||
/// will return 0 on success, and != 0 on error
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int sslRand (uint64_t*);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a random number using OpenSsl
|
||||
///
|
||||
/// will return 0 on success, and != 0 on error
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int sslRand (int64_t*);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a random number using OpenSsl
|
||||
///
|
||||
/// will return 0 on success, and != 0 on error
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int sslRand (int32_t*);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// @brief salt
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void salt64 (uint64_t& saltInt);
|
||||
void salt64 (uint64_t& saltInt);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// @brief salt
|
||||
|
|
|
@ -619,6 +619,46 @@ static v8::Handle<v8::Value> JS_LogLevel (v8::Arguments const& argv) {
|
|||
return scope.Close(v8::String::New(TRI_LogLevelLogging()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief md5 sum
|
||||
///
|
||||
/// @FUN{internal.md5(@FA{text})}
|
||||
///
|
||||
/// Computes an md5 for the @FA{text}.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static v8::Handle<v8::Value> JS_Md5 (v8::Arguments const& argv) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
// extract arguments
|
||||
if (argv.Length() != 1 || ! argv[0]->IsString()) {
|
||||
return scope.Close(v8::ThrowException(v8::String::New("usage: md5(<text>)")));
|
||||
}
|
||||
|
||||
string key = TRI_ObjectToString(argv[0]);
|
||||
|
||||
// create md5
|
||||
char* hash = 0;
|
||||
size_t hashLen;
|
||||
|
||||
SslInterface::sslMD5(key.c_str(), key.size(), hash, hashLen);
|
||||
|
||||
// as hex
|
||||
char* hex = 0;
|
||||
size_t hexLen;
|
||||
|
||||
SslInterface::sslHEX(hash, hashLen, hex, hexLen);
|
||||
|
||||
delete[] hash;
|
||||
|
||||
// and return
|
||||
v8::Handle<v8::String> hashStr = v8::String::New(hex, hexLen);
|
||||
|
||||
delete[] hex;
|
||||
|
||||
return scope.Close(hashStr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief renames a file
|
||||
///
|
||||
|
@ -742,6 +782,46 @@ static v8::Handle<v8::Value> JS_ProcessStat (v8::Arguments const& argv) {
|
|||
return scope.Close(result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief generate a random number using OpenSSL
|
||||
///
|
||||
/// @FUN{internal.rand()}
|
||||
///
|
||||
/// Generates a random number
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static v8::Handle<v8::Value> JS_Rand (v8::Arguments const& argv) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
// check arguments
|
||||
if (argv.Length() != 0) {
|
||||
return scope.Close(v8::ThrowException(v8::String::New("usage: rand()")));
|
||||
}
|
||||
|
||||
int iterations = 0;
|
||||
while (iterations++ < 5) {
|
||||
int32_t value;
|
||||
int result = SslInterface::sslRand(&value);
|
||||
|
||||
if (result != 0) {
|
||||
// error
|
||||
break;
|
||||
}
|
||||
|
||||
// no error, now check what random number was produced
|
||||
|
||||
if (value != 0) {
|
||||
// a number != 0 was produced. that is sufficient
|
||||
return scope.Close(v8::Number::New(value));
|
||||
}
|
||||
|
||||
// we don't want to return 0 as the result, so we try again
|
||||
}
|
||||
|
||||
// we failed to produce a valid random number
|
||||
return scope.Close(v8::Undefined());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief reads in a file
|
||||
///
|
||||
|
@ -962,14 +1042,14 @@ static v8::Handle<v8::Value> JS_SPrintF (v8::Arguments const& argv) {
|
|||
///
|
||||
/// @FUN{internal.sha256(@FA{text})}
|
||||
///
|
||||
/// Computes a sha256 for the @FA{text}.
|
||||
/// Computes an sha256 for the @FA{text}.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static v8::Handle<v8::Value> JS_Sha256 (v8::Arguments const& argv) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
// extract arguments
|
||||
if (argv.Length() != 1) {
|
||||
if (argv.Length() != 1 || ! argv[0]->IsString()) {
|
||||
return scope.Close(v8::ThrowException(v8::String::New("usage: sha256(<text>)")));
|
||||
}
|
||||
|
||||
|
@ -1398,9 +1478,11 @@ void TRI_InitV8Utils (v8::Handle<v8::Context> context, string const& path) {
|
|||
TRI_AddGlobalFunctionVocbase(context, "SYS_LOAD", JS_Load);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_LOG", JS_Log);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_LOG_LEVEL", JS_LogLevel);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_MD5", JS_Md5);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_OUTPUT", JS_Output);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_PARSE", JS_Parse);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_PROCESS_STAT", JS_ProcessStat);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_RAND", JS_Rand);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_READ", JS_Read);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_SAVE", JS_Save);
|
||||
TRI_AddGlobalFunctionVocbase(context, "SYS_SHA256", JS_Sha256);
|
||||
|
|