mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into devel
This commit is contained in:
commit
a3da7b9897
|
@ -1,6 +1,8 @@
|
|||
v1.3.0-alpha1 (XXXX-XX-XX)
|
||||
--------------------------
|
||||
|
||||
* added API for user-defined AQL functions
|
||||
|
||||
* issue #475: A better error message for deleting a non-existent graph
|
||||
|
||||
* issue #474: Web interface problems with the JS Shell
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
> curl --data @- -X POST --dump - http://localhost:8529/_api/aqlfunction
|
||||
{ "name" : "myfunctions:temperature:celsiustofahrenheit, "code" : "function (celsius) { return celsius * 1.8 + 32; }" }
|
||||
|
||||
HTTP/1.1 201 Created
|
||||
content-type: application/json; charset=utf-8
|
||||
|
||||
{
|
||||
"error" : false,
|
||||
"code" : 201
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
HTTP Interface for AQL User Functions Management {#HttpAqlFunctions}
|
||||
====================================================================
|
||||
|
||||
@NAVIGATE_HttpAqlFunctions
|
||||
@EMBEDTOC{HttpAqlFunctionsTOC}
|
||||
|
||||
AQL User Functions Management {#HttpAqlFunctionsIntro}
|
||||
======================================================
|
||||
|
||||
This is an introduction to ArangoDB's Http interface for managing AQL
|
||||
user functions. AQL user functions are a means to extend the functionality
|
||||
of ArangoDB's query language (AQL) with user-defined Javascript code.
|
||||
|
||||
For an overview of how AQL user functions work, please refer to @ref
|
||||
ExtendingAql.
|
||||
|
||||
The Http interface provides an API for adding, deleting, and listing
|
||||
previously registered AQL user functions.
|
||||
|
||||
All user functions managed through this interface will be stored in the
|
||||
system collection `_aqlfunctions`. Documents in this collection should not
|
||||
be accessed directly, but only via the dedicated interfaces.
|
||||
|
||||
@anchor HttpAqlFunctionsSave
|
||||
@copydetails JSF_POST_api_aqlfunction
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HttpAqlFunctionsRemove
|
||||
@copydetails JSF_DELETE_api_aqlfunction
|
||||
|
||||
@CLEARPAGE
|
||||
@anchor HttpAqlFunctionsList
|
||||
@copydetails JSF_GET_api_aqlfunction
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
TOC {#HttpAqlFunctionsTOC}
|
||||
==========================
|
||||
|
||||
- @ref HttpAqlFunctions
|
||||
- @ref HttpAqlFunctionsIntro
|
||||
- @ref HttpAqlFunctionsSave "POST /_api/aqlfunction"
|
||||
- @ref HttpAqlFunctionsRemove "DELETE /_api/aqlfunction/functionname"
|
||||
- @ref HttpAqlFunctionsList "GET /_api/aqlfunction"
|
|
@ -7,6 +7,7 @@ ArangoDB for API Implementors (@VERSION) {#ImplementorManual}
|
|||
@CHAPTER_REF{RestEdge}
|
||||
@CHAPTER_REF{HttpCursor}
|
||||
@CHAPTER_REF{HttpQuery}
|
||||
@CHAPTER_REF{HttpAqlFunctions}
|
||||
@CHAPTER_REF{HttpSimple}
|
||||
@CHAPTER_REF{HttpCollection}
|
||||
@CHAPTER_REF{HttpIndex}
|
||||
|
|
|
@ -33,6 +33,7 @@ dist_man_MANS += \
|
|||
################################################################################
|
||||
|
||||
DOXYGEN = \
|
||||
Doxygen/js/actions/api-aqlfunction.c \
|
||||
Doxygen/js/actions/api-collection.c \
|
||||
Doxygen/js/actions/api-cursor.c \
|
||||
Doxygen/js/actions/api-edges.c \
|
||||
|
@ -49,6 +50,7 @@ DOXYGEN = \
|
|||
Doxygen/js/common/bootstrap/module-fs.c \
|
||||
Doxygen/js/common/bootstrap/modules.c \
|
||||
Doxygen/js/common/modules/jsunity.c \
|
||||
Doxygen/js/common/modules/org/arangodb/aql/functions.c \
|
||||
Doxygen/js/common/modules/org/arangodb/arango-collection-common.c \
|
||||
Doxygen/js/common/modules/org/arangodb/arango-statement-common.c \
|
||||
Doxygen/js/common/modules/org/arangodb/graph-common.c \
|
||||
|
@ -78,6 +80,7 @@ WIKI = \
|
|||
DbaManualBasics \
|
||||
DbaManualDatafileDebugger \
|
||||
DbaManualEmergencyConsole \
|
||||
ExtendingAql \
|
||||
FirstStepsArangoDB \
|
||||
Glossary \
|
||||
HandlingCollections \
|
||||
|
@ -85,6 +88,7 @@ WIKI = \
|
|||
HandlingEdges \
|
||||
HandlingIndexes \
|
||||
Home \
|
||||
HttpAqlFunctions \
|
||||
HttpBatch \
|
||||
HttpCollection \
|
||||
HttpCursor \
|
||||
|
@ -155,6 +159,7 @@ Doxygen/.setup-directories:
|
|||
@test -d Doxygen/website/images || mkdir -p Doxygen/website/images
|
||||
@test -d Doxygen/js/actions || mkdir -p Doxygen/js/actions
|
||||
@test -d Doxygen/js/common/bootstrap || mkdir -p Doxygen/js/common/bootstrap
|
||||
@test -d Doxygen/js/common/modules/org/arangodb/aql || mkdir -p Doxygen/js/common/modules/org/arangodb/aql
|
||||
@test -d Doxygen/js/common/modules/org/arangodb || mkdir -p Doxygen/js/common/modules/org/arangodb
|
||||
@test -d Doxygen/js/server/modules/org/arangodb || mkdir -p Doxygen/js/server/modules/org/arangodb
|
||||
@test -d Doxygen/js/client || mkdir -p Doxygen/js/client
|
||||
|
|
|
@ -779,7 +779,7 @@ AQL supports functions to allow more complex computations. Functions can be
|
|||
called at any query position where an expression is allowed. The general
|
||||
function call syntax is:
|
||||
|
||||
FUNCTIONAME(arguments)
|
||||
FUNCTIONNAME(arguments)
|
||||
|
||||
where `FUNCTIONNAME` is the name of the function to be called, and `arguments`
|
||||
is a comma-separated list of function arguments. If a function does not need any
|
||||
|
@ -793,7 +793,30 @@ Some example function calls:
|
|||
LENGTH(friends)
|
||||
COLLECTIONS()
|
||||
|
||||
Function names are not case-sensitive.
|
||||
In contrast to collection and variable names, function names are case-insensitive,
|
||||
i.e. `LENGTH(foo)` and `length(foo)` are equivalent.
|
||||
|
||||
@subsubsection AqlFunctionsExtending Extending AQL
|
||||
|
||||
Since ArangoDB 1.3, it is possible to extend AQL with user-defined functions.
|
||||
These functions need to be written in Javascript, and be registered before usage
|
||||
in a query.
|
||||
|
||||
Please refer to @ref ExtendingAql for more details on this.
|
||||
|
||||
By default, any function used in an AQL query will be sought in the built-in
|
||||
function namespace `_aql`. This is the default namespace that contains all AQL
|
||||
functions that are shipped with ArangoDB.
|
||||
To refer to a user-defined AQL function, the function name must be fully qualified
|
||||
to also include the user-defined namespace. The `:` symbol is used as the namespace
|
||||
separator:
|
||||
|
||||
MYGROUP:MYFUNC()
|
||||
|
||||
MYFUNCTIONS.MATH.RANDOM()
|
||||
|
||||
As all AQL function names, user function names are also case-insensitive.
|
||||
|
||||
|
||||
@subsubsection AqlFunctionsCasting Type cast functions
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
Extending AQL with User Functions {#ExtendingAql}
|
||||
=================================================
|
||||
|
||||
@NAVIGATE_ExtendingAql
|
||||
@EMBEDTOC{ExtendingAqlTOC}
|
||||
|
||||
Conventions {#ExtendingAqlConventions}
|
||||
======================================
|
||||
|
||||
AQL comes with a built-in set of functions, but it is no
|
||||
full-feature programming language.
|
||||
|
||||
To add missing functionality or to simplifiy queries, users
|
||||
may add own functions to AQL. These functions can be written
|
||||
in Javascript, and must be registered via an API.
|
||||
|
||||
In order to avoid conflicts with existing or future built-in
|
||||
function names, all user functions must be put into separate
|
||||
namespaces. Invoking a user functions is then possible by referring
|
||||
to the fully-qualified function name, which includes the namespace,
|
||||
too.
|
||||
|
||||
The `:` symbol is used inside AQL as the namespace separator. Using
|
||||
the namespace separator, users can create a multi-level hierarchy of
|
||||
function groups if required.
|
||||
|
||||
Examples:
|
||||
|
||||
RETURN myfunctions:myfunc()
|
||||
|
||||
RETURN myfunctions:math:random()
|
||||
|
||||
Note: as all function names in AQL, user function names are also
|
||||
case-insensitive.
|
||||
|
||||
Built-in AQL functions reside in the namespace `_aql`, which is also
|
||||
the default namespace to look in if an unqualified function name is
|
||||
found. Adding user functions to the `_aql` namespace is disallowed and
|
||||
will fail.
|
||||
|
||||
User functions can take any number of input arguments and should
|
||||
provide one result. They should be kept purely functional and thus free of
|
||||
side effects and state.
|
||||
|
||||
User function code is late-bound, and may thus not rely on any variables
|
||||
that existed at the time of declaration. If user function code requires
|
||||
access to any external data, it must take care to set up the data by
|
||||
itself.
|
||||
|
||||
User functions must only return primitive types (i.e. `null`, boolean
|
||||
values, numeric values, string values) or aggregate types (lists or
|
||||
documents) composed of these types.
|
||||
Returning any other Javascript object type from a user function may lead
|
||||
to undefined behavior and should be avoided.
|
||||
|
||||
|
||||
Registering and Unregistering User Functions {#ExtendingAqlHowTo}
|
||||
=================================================================
|
||||
|
||||
AQL user functions can be registered using the `aqlfunctions` object as
|
||||
follows:
|
||||
|
||||
var aqlfunctions = require("org/arangodb/aql/functions");
|
||||
|
||||
To register a function, the fully qualified function name plus the
|
||||
function code must be specified.
|
||||
|
||||
@copydetails JSF_aqlfunctions_register
|
||||
|
||||
It is possible to unregister a single user function, or a whole group of
|
||||
user functions (identified by their namespace) in one go:
|
||||
|
||||
@copydetails JSF_aqlfunctions_unregister
|
||||
|
||||
@copydetails JSF_aqlfunctions_unregister_group
|
||||
|
||||
To get an overview of which functions are currently registered, the
|
||||
`toArray` function can be used:
|
||||
|
||||
@copydetails JSF_aqlfunctions_toArray
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
TOC {#ExtendingAqlTOC}
|
||||
======================
|
||||
|
||||
- @ref ExtendingAql
|
||||
- @ref ExtendingAqlConventions
|
||||
- @ref ExtendingAqlHowTo
|
|
@ -11,6 +11,7 @@ ArangoDB's User Manual (@VERSION) {#UserManual}
|
|||
@CHAPTER_REF{HandlingEdges}
|
||||
@CHAPTER_REF{SimpleQueries}
|
||||
@CHAPTER_REF{Aql}
|
||||
@CHAPTER_REF{ExtendingAql}
|
||||
@CHAPTER_REF{AqlExamples}
|
||||
@CHAPTER_REF{UserManualActions}
|
||||
@CHAPTER_REF{Transactions}
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
# coding: utf-8
|
||||
|
||||
require 'rspec'
|
||||
require './arangodb.rb'
|
||||
|
||||
describe ArangoDB do
|
||||
api = "/_api/aqlfunction"
|
||||
prefix = "api-aqlfunction"
|
||||
|
||||
context "AQL user functions:" do
|
||||
|
||||
################################################################################
|
||||
## error handling
|
||||
################################################################################
|
||||
|
||||
context "error handling" do
|
||||
|
||||
it "add function, without name" do
|
||||
body = "{ \"code\" : \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-no-name", 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(1580)
|
||||
end
|
||||
|
||||
it "add function, invalid name" do
|
||||
body = "{ \"name\" : \"\", \"code\" : \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-invalid1", 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(1580)
|
||||
end
|
||||
|
||||
it "add function, invalid name" do
|
||||
body = "{ \"name\" : \"_aql:foobar\", \"code\" : \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-invalid2", 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(1580)
|
||||
end
|
||||
|
||||
it "add function, invalid name" do
|
||||
body = "{ \"name\" : \"foobar\", \"code\" : \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-invalid3", 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(1580)
|
||||
end
|
||||
|
||||
it "add function, no code" do
|
||||
body = "{ \"name\" : \"myfunc:mytest\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-no-code", 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(1581)
|
||||
end
|
||||
|
||||
it "add function, invalid code" do
|
||||
body = "{ \"name\" : \"myfunc:mytest\", \"code\" : \"function ()\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-invalid-code", 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(1581)
|
||||
end
|
||||
|
||||
it "deleting non-existing function" do
|
||||
doc = ArangoDB.log_delete("#{prefix}-delete", api + "/mytest%3Amynonfunc")
|
||||
|
||||
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(1582)
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## adding and deleting functions
|
||||
################################################################################
|
||||
|
||||
context "adding functions" do
|
||||
before do
|
||||
ArangoDB.delete("/_api/aqlfunction/UnitTests%3Amytest")
|
||||
end
|
||||
|
||||
after do
|
||||
ArangoDB.delete("/_api/aqlfunction/UnitTests%3Amytest")
|
||||
end
|
||||
|
||||
it "add function, valid code" do
|
||||
body = "{ \"name\" : \"UnitTests:mytest\", \"code\": \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-function1", 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)
|
||||
end
|
||||
|
||||
it "add function, update" do
|
||||
body = "{ \"name\" : \"UnitTests:mytest\", \"code\": \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-function2", 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-function2", api, :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)
|
||||
end
|
||||
|
||||
it "add function, delete" do
|
||||
body = "{ \"name\" : \"UnitTests:mytest\", \"code\": \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-add-function3", 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_delete("#{prefix}-add-function3", api + "/UnitTests%3Amytest")
|
||||
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 = ArangoDB.log_delete("#{prefix}-add-function3", api + "/UnitTests%3Amytest")
|
||||
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(1582)
|
||||
end
|
||||
end
|
||||
|
||||
################################################################################
|
||||
## retrieving the list of functions
|
||||
################################################################################
|
||||
|
||||
context "adding functions" do
|
||||
before do
|
||||
ArangoDB.delete("/_api/aqlfunction/UnitTests%3Amytest")
|
||||
end
|
||||
|
||||
after do
|
||||
ArangoDB.delete("/_api/aqlfunction/UnitTests%3Amytest")
|
||||
end
|
||||
|
||||
it "add function and retrieve the list" do
|
||||
body = "{ \"name\" : \"UnitTests:mytest\", \"code\": \"function () { return 1; }\" }"
|
||||
doc = ArangoDB.log_post("#{prefix}-list-functions", 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_get("#{prefix}-list-functions", api + "?prefix=UnitTests")
|
||||
doc.code.should eq(200)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response.length.should eq(1)
|
||||
doc.parsed_response[0]['name'].should eq("UnitTests:mytest")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -4,6 +4,7 @@ test -d logs || mkdir logs
|
|||
rspec --color --format d \
|
||||
api-http-spec.rb \
|
||||
api-admin-spec.rb \
|
||||
api-aqlfunction-spec.rb \
|
||||
api-attributes-spec.rb \
|
||||
api-batch-spec.rb \
|
||||
api-collection-spec.rb \
|
||||
|
|
|
@ -134,6 +134,15 @@ start-server:
|
|||
unittests-make:
|
||||
@(ctags --version > /dev/null 2> /dev/null && make tags > /dev/null || test "x$(FORCE)" == "x1") || true
|
||||
|
||||
################################################################################
|
||||
### @brief static codebase tests (a.k.a. Visual Studio cries)
|
||||
################################################################################
|
||||
|
||||
unittests-codebase-static:
|
||||
@rm -f duplicates
|
||||
@(find lib arangosh arangod arangoirb -regex ".*/.*\.\(c\|h\|cpp\)" -printf "%f\n" | sort | uniq -c | grep -v "^ \+1 \+" > duplicates) || true
|
||||
@if [ "`grep " " duplicates`" != "" ]; then echo ; echo "Duplicate filenames found. These should be fixed to allow compilation with Visual Studio:"; cat duplicates; false; fi
|
||||
|
||||
################################################################################
|
||||
### @brief BOOST TESTS
|
||||
################################################################################
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief AQL user functions 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 Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var arangodb = require("org/arangodb");
|
||||
var actions = require("org/arangodb/actions");
|
||||
var aqlfunctions = require("org/arangodb/aql/functions");
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @addtogroup ArangoAPI
|
||||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief fetch a user
|
||||
///
|
||||
/// @RESTHEADER{GET /_api/aqlfunction,returns registered AQL user functions}
|
||||
///
|
||||
/// @REST{GET /_api/aqlfunction}
|
||||
///
|
||||
/// Returns all registered AQL user functions.
|
||||
///
|
||||
/// @REST{GET /_api/aqlfunction?namespace=@FA{namespace}}
|
||||
///
|
||||
/// Returns all registered AQL user functions from namespace @FA{namespace}.
|
||||
///
|
||||
/// The call will return a JSON list with all user functions found. Each user
|
||||
/// function will at least have the following attributes:
|
||||
///
|
||||
/// - @LIT{name}: The fully qualified name of the user function
|
||||
///
|
||||
/// - @LIT{code}: A string representation of the function body
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function GET_api_aqlfunction (req, res) {
|
||||
if (req.suffix.length != 0) {
|
||||
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
var namespace = undefined;
|
||||
if (req.parameters.hasOwnProperty('namespace')) {
|
||||
namespace = req.parameters.namespace;
|
||||
}
|
||||
|
||||
var result = aqlfunctions.toArray(namespace);
|
||||
actions.resultOk(req, res, actions.HTTP_OK, result)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a new AQL user function
|
||||
///
|
||||
/// @RESTHEADER{POST /_api/aqlfunction,creates or replaces an AQL user function}
|
||||
///
|
||||
/// @REST{POST /_api/aqlfunction}
|
||||
///
|
||||
/// The following data need to be passed in a JSON representation in the body of
|
||||
/// the POST request:
|
||||
///
|
||||
/// - @LIT{name}: the fully qualified name of the user functions.
|
||||
///
|
||||
/// - @LIT{code}: a string representation of the function body.
|
||||
///
|
||||
/// If the function can be registered by the server, the server will respond with
|
||||
/// @LIT{HTTP 201}. If the function already existed and was replaced by the
|
||||
/// call, 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}.
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @verbinclude api-aqlfunction-create
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function POST_api_aqlfunction (req, res) {
|
||||
var json = actions.getJsonBody(req, res, actions.HTTP_BAD);
|
||||
|
||||
if (json === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = aqlfunctions.register(json.name, json.code);
|
||||
|
||||
actions.resultOk(req, res, result ? actions.HTTP_OK : actions.HTTP_CREATED, { });
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove an existing AQL user function
|
||||
///
|
||||
/// @RESTHEADER{DELETE /_api/aqlfunction,remove an existing AQL user function}
|
||||
///
|
||||
/// @REST{DELETE /_api/aqlfunction/@FA{name}}
|
||||
///
|
||||
/// Removes an existing AQL user function, identified by @FA{name}.
|
||||
/// The function name provided in @FA{name} must be fully qualified, including
|
||||
/// any namespaces.
|
||||
///
|
||||
/// If the function can be removed 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 DELETE_api_aqlfunction (req, res) {
|
||||
if (req.suffix.length !== 1) {
|
||||
actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER);
|
||||
return;
|
||||
}
|
||||
|
||||
var name = decodeURIComponent(req.suffix[0]);
|
||||
try {
|
||||
aqlfunctions.unregister(name);
|
||||
actions.resultOk(req, res, actions.HTTP_OK, { });
|
||||
}
|
||||
catch (err) {
|
||||
if (err.errorNum === arangodb.errors.ERROR_QUERY_FUNCTION_NOT_FOUND.code) {
|
||||
actions.resultNotFound(req, res, arangodb.errors.ERROR_QUERY_FUNCTION_NOT_FOUND.code);
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- initialiser
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief gateway
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
actions.defineHttp({
|
||||
url : "_api/aqlfunction",
|
||||
context : "api",
|
||||
|
||||
callback : function (req, res) {
|
||||
try {
|
||||
switch (req.requestType) {
|
||||
case actions.GET:
|
||||
GET_api_aqlfunction(req, res);
|
||||
break;
|
||||
|
||||
case actions.POST:
|
||||
POST_api_aqlfunction(req, res);
|
||||
break;
|
||||
|
||||
case actions.DELETE:
|
||||
DELETE_api_aqlfunction(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:
|
|
@ -21,7 +21,6 @@
|
|||
///
|
||||
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Achim Brandt
|
||||
/// @author Jan Steemann
|
||||
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -316,9 +315,8 @@ function PATCH_api_user (req, res) {
|
|||
///
|
||||
/// - @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}.
|
||||
/// 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:
|
||||
|
|
|
@ -158,13 +158,13 @@
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief plattform
|
||||
/// @brief platform
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.plattform = "unknown";
|
||||
exports.platform = "unknown";
|
||||
|
||||
if (typeof SYS_PLATFORM !== "undefined") {
|
||||
exports.plattform = SYS_PLATFORM;
|
||||
exports.platform = SYS_PLATFORM;
|
||||
delete SYS_PLATFORM;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,33 @@ var ArangoError = arangodb.ArangoError;
|
|||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief apply a prefix filter on the functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var getFiltered = function (group) {
|
||||
var result = [ ];
|
||||
|
||||
if (group !== null && group !== undefined && group.length > 0) {
|
||||
var prefix = group.toUpperCase();
|
||||
|
||||
if (group.substr(group.length - 1, 1) !== ':') {
|
||||
prefix += ':';
|
||||
}
|
||||
|
||||
getStorage().toArray().forEach(function (f) {
|
||||
if (f.name.toUpperCase().substr(0, prefix.length) === prefix) {
|
||||
result.push(f);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
result = getStorage().toArray();
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief validate a function name
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -109,7 +136,7 @@ var getStorage = function () {
|
|||
if (functions === null) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code;
|
||||
err.errorMessage = "collection _aqlfunctions not found";
|
||||
err.errorMessage = "collection '_aqlfunctions' not found";
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
@ -131,7 +158,22 @@ var getStorage = function () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_aqlfunctions_unregister
|
||||
/// @brief delete an existing AQL user function
|
||||
///
|
||||
/// @FUN{aqlfunctions.unregister(@FA{name})}
|
||||
///
|
||||
/// Unregisters an existing AQL user function, identified by the fully qualified
|
||||
/// function name.
|
||||
///
|
||||
/// Trying to unregister a function that does not exist will result in an
|
||||
/// exception.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/aql/functions").unregister("myfunctions:temperature:celsiustofahrenheit");
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var unregisterFunction = function (name) {
|
||||
|
@ -148,7 +190,7 @@ var unregisterFunction = function (name) {
|
|||
if (func === null) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_QUERY_FUNCTION_NOT_FOUND.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_QUERY_FUNCTION_NOT_FOUND.message;
|
||||
err.errorMessage = internal.sprintf(arangodb.errors.ERROR_QUERY_FUNCTION_NOT_FOUND.message, name);
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
@ -160,16 +202,79 @@ var unregisterFunction = function (name) {
|
|||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_aqlfunctions_unregister_group
|
||||
/// @brief delete a group of AQL user functions
|
||||
///
|
||||
/// @FUN{aqlfunctions.unregisterGroup(@FA{prefix})}
|
||||
///
|
||||
/// Unregisters a group of AQL user function, identified by a common function
|
||||
/// group prefix.
|
||||
///
|
||||
/// This will return the number of functions unregistered.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/aql/functions").unregisterGroup("myfunctions:temperature");
|
||||
///
|
||||
/// arangosh> require("org/arangodb/aql/functions").unregisterGroup("myfunctions");
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var unregisterFunctionsGroup = function (group) {
|
||||
if (group.length === 0) {
|
||||
var err = new ArangoError();
|
||||
err.errorNum = arangodb.errors.ERROR_BAD_PARAMETER.code;
|
||||
err.errorMessage = arangodb.errors.ERROR_BAD_PARAMETER.message;
|
||||
}
|
||||
|
||||
var deleted = 0;
|
||||
|
||||
getFiltered(group).forEach(function (f) {
|
||||
getStorage().remove(f._id);
|
||||
deleted++;
|
||||
});
|
||||
|
||||
if (deleted > 0) {
|
||||
internal.reloadAqlFunctions();
|
||||
}
|
||||
|
||||
return deleted;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @fn JSF_aqlfunctions_register
|
||||
/// @brief register an AQL user function
|
||||
///
|
||||
/// @FUN{aqlfunctions.register(@FA{name}, @FA{code})}
|
||||
///
|
||||
/// Registers an AQL user function, identified by a fully qualified function
|
||||
/// name. The function code in @FA{code} must be specified as a Javascript
|
||||
/// function or a string representation of a Javascript function.
|
||||
///
|
||||
/// If a function identified by @FA{name} already exists, the previous function
|
||||
/// definition will be updated.
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/aql/functions").register("myfunctions:temperature:celsiustofahrenheit",
|
||||
/// function (celsius) {
|
||||
/// return celsius * 1.8 + 32;
|
||||
/// });
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var registerFunction = function (name, code, isDeterministic) {
|
||||
// validate input
|
||||
validateName(name);
|
||||
code = stringifyFunction(code, name);
|
||||
|
||||
var exists = false;
|
||||
|
||||
try {
|
||||
unregisterFunction(name);
|
||||
exists = true;
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
|
@ -184,15 +289,52 @@ var registerFunction = function (name, code, isDeterministic) {
|
|||
getStorage().save(data);
|
||||
internal.reloadAqlFunctions();
|
||||
|
||||
return true;
|
||||
return exists;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief reloads the AQL user functons
|
||||
/// @fn JSF_aqlfunctions_toArray
|
||||
/// @brief list all AQL user functions
|
||||
///
|
||||
/// @FUN{aqlfunctions.toArray()}
|
||||
///
|
||||
/// Returns all previously registered AQL user functions, with their fully
|
||||
/// qualified names and function code.
|
||||
///
|
||||
/// The result may optionally be restricted to a specified group of functions
|
||||
/// by specifying a group prefix:
|
||||
///
|
||||
/// @FUN{aqlfunctions.toArray(@FA{prefix})}
|
||||
///
|
||||
/// @EXAMPLES
|
||||
///
|
||||
/// To list all available user functions:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/aql/functions").toArray();
|
||||
/// @endcode
|
||||
///
|
||||
/// To list all available user functions in the `myfunctions` namespace:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/aql/functions").toArray("myfunctions");
|
||||
/// @endcode
|
||||
///
|
||||
/// To list all available user functions in the `myfunctions:temperature` namespace:
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("org/arangodb/aql/functions").toArray("myfunctions:temperature");
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var toArrayFunctions = function (group) {
|
||||
var result = [ ];
|
||||
|
||||
var reloadFunctions = function () {
|
||||
throw "cannot use abstract reload function";
|
||||
getFiltered(group).forEach(function (f) {
|
||||
result.push({ name: f.name, code: f.code });
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -208,9 +350,10 @@ var reloadFunctions = function () {
|
|||
/// @{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
exports.register = registerFunction;
|
||||
exports.unregister = unregisterFunction;
|
||||
exports.reload = reloadFunctions;
|
||||
exports.unregister = unregisterFunction;
|
||||
exports.unregisterGroup = unregisterFunctionsGroup;
|
||||
exports.register = registerFunction;
|
||||
exports.toArray = toArrayFunctions;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @}
|
||||
|
|
|
@ -158,8 +158,8 @@ var getStorage = function () {
|
|||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("users").save("my-user", "my-secret-password");
|
||||
/// arangosh> require("users").reload();
|
||||
/// arangosh> require("org/arangodb/users").save("my-user", "my-secret-password");
|
||||
/// arangosh> require("org/arangodb/users").reload();
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -231,8 +231,8 @@ exports.save = function (username, passwd, active, extra) {
|
|||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("users").replace("my-user", "my-changed-password");
|
||||
/// arangosh> require("users").reload();
|
||||
/// arangosh> require("org/arangodb/users").replace("my-user", "my-changed-password");
|
||||
/// arangosh> require("org/arangodb/users").reload();
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -304,8 +304,8 @@ exports.replace = function (username, passwd, active, extra) {
|
|||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("users").replace("my-user", "my-secret-password");
|
||||
/// arangosh> require("users").reload();
|
||||
/// arangosh> require("org/arangodb/users").replace("my-user", "my-secret-password");
|
||||
/// arangosh> require("org/arangodb/users").reload();
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -364,8 +364,8 @@ exports.update = function (username, passwd, active, extra) {
|
|||
/// @EXAMPLES
|
||||
///
|
||||
/// @code
|
||||
/// arangosh> require("users").remove("my-user");
|
||||
/// arangosh> require("users").reload();
|
||||
/// arangosh> require("org/arangodb/users").remove("my-user");
|
||||
/// arangosh> require("org/arangodb/users").reload();
|
||||
/// @endcode
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ function AqlFunctionsSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
setUp : function () {
|
||||
aqlfunctions.unregisterGroup("UnitTests:");
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -67,6 +68,43 @@ function AqlFunctionsSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
tearDown : function () {
|
||||
aqlfunctions.unregisterGroup("UnitTests:");
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief toArray
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testToArray1 : function () {
|
||||
aqlfunctions.register("UnitTests:tryme:foo", function (what) { return what * 2; }, true);
|
||||
aqlfunctions.register("UnitTests:tryme:bar", function (what) { return what * 2; }, true);
|
||||
|
||||
assertEqual([ "UnitTests:tryme:bar", "UnitTests:tryme:foo" ], aqlfunctions.toArray("UnitTests").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests:tryme:bar", "UnitTests:tryme:foo" ], aqlfunctions.toArray("UnitTests:").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests:tryme:bar", "UnitTests:tryme:foo" ], aqlfunctions.toArray("UnitTests:tryme").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests:tryme:bar", "UnitTests:tryme:foo" ], aqlfunctions.toArray("UnitTests:tryme:").sort().map(function (f) { return f.name; }));
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief toArray
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testToArray2 : function () {
|
||||
aqlfunctions.register("UnitTests:tryme:foo", function (what) { return what * 2; }, true);
|
||||
aqlfunctions.register("UnitTests:tryme:bar", function (what) { return what * 2; }, true);
|
||||
aqlfunctions.register("UnitTests58:tryme:bar", function (what) { return what * 2; }, true);
|
||||
aqlfunctions.register("UnitTests58:whyme:bar", function (what) { return what * 2; }, true);
|
||||
|
||||
assertEqual([ "UnitTests:tryme:bar", "UnitTests:tryme:foo" ], aqlfunctions.toArray("UnitTests").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests:tryme:bar", "UnitTests:tryme:foo" ], aqlfunctions.toArray("UnitTests:").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests:tryme:bar", "UnitTests:tryme:foo" ], aqlfunctions.toArray("UnitTests:tryme").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests58:tryme:bar", "UnitTests58:whyme:bar" ], aqlfunctions.toArray("UnitTests58").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests58:tryme:bar", "UnitTests58:whyme:bar" ], aqlfunctions.toArray("UnitTests58:").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests58:tryme:bar" ], aqlfunctions.toArray("UnitTests58:tryme").sort().map(function (f) { return f.name; }));
|
||||
assertEqual([ "UnitTests58:tryme:bar" ], aqlfunctions.toArray("UnitTests58:tryme:").sort().map(function (f) { return f.name; }));
|
||||
|
||||
aqlfunctions.unregister("UnitTests58:tryme:bar", function (what) { return what * 2; }, true);
|
||||
aqlfunctions.unregister("UnitTests58:whyme:bar", function (what) { return what * 2; }, true);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -49,9 +49,14 @@ function FileSystemSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
setUp : function () {
|
||||
// create a name for a temporary directory we will run all tests in
|
||||
// as we are creating a new name, this shouldn't collide with any existing
|
||||
// directories, and we can also remove our directory when the tests are
|
||||
// over
|
||||
tempDir = fs.join(fs.getTempFile('', false));
|
||||
|
||||
try {
|
||||
// create this directory before any tests
|
||||
fs.makeDirectoryRecursive(tempDir);
|
||||
}
|
||||
catch (err) {
|
||||
|
@ -63,38 +68,49 @@ function FileSystemSuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
tearDown : function () {
|
||||
// some sanity check as we don't want to unintentionally remove "." or "/"
|
||||
if (tempDir.length > 5) {
|
||||
// remove our temporary directory with all its subdirectories
|
||||
// we created it, so we don't care what's in it
|
||||
fs.removeDirectoryRecursive(tempDir);
|
||||
}
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief exists()
|
||||
/// @brief exists() - test if a file / directory exists
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testExists : function () {
|
||||
var tempName;
|
||||
|
||||
// existing directory
|
||||
// create the name for a new directory
|
||||
tempName = fs.join(tempDir, 'foo');
|
||||
try {
|
||||
// remove it if it exists
|
||||
fs.removeDirectory(tempName);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
|
||||
// create the directory with all paths to it
|
||||
fs.makeDirectoryRecursive(tempName);
|
||||
|
||||
// now the directory should exist
|
||||
assertTrue(fs.exists(tempName));
|
||||
|
||||
// remove it again
|
||||
fs.removeDirectory(tempName);
|
||||
|
||||
// non-existing directory/file
|
||||
tempName = fs.join(tempDir, 'meow');
|
||||
// this file should not exist
|
||||
assertFalse(fs.exists(tempName));
|
||||
|
||||
// file
|
||||
// create a new file
|
||||
tempName = fs.getTempFile('', true);
|
||||
// the file should now exist
|
||||
assertTrue(fs.exists(tempName));
|
||||
// clean it up
|
||||
fs.remove(tempName);
|
||||
},
|
||||
|
||||
|
@ -105,30 +121,40 @@ function FileSystemSuite () {
|
|||
testGetTempFile : function () {
|
||||
var tempName;
|
||||
|
||||
// creating a new file
|
||||
// creating a new empty file with a temporary name
|
||||
tempName = fs.getTempFile('', true);
|
||||
|
||||
assertTrue(tempName !== '');
|
||||
// the file should be located inside the tempPath
|
||||
assertEqual(fs.getTempPath(), tempName.substr(0, fs.getTempPath().length));
|
||||
// the file should exist
|
||||
assertTrue(fs.exists(tempName));
|
||||
|
||||
// clean up
|
||||
fs.remove(tempName);
|
||||
|
||||
// without creating
|
||||
// create a filename only, without creating the file itself
|
||||
tempName = fs.getTempFile('', false);
|
||||
assertTrue(tempName !== '');
|
||||
// filename should be located underneath tempPath
|
||||
assertEqual(fs.getTempPath(), tempName.substr(0, fs.getTempPath().length));
|
||||
// the file should not exist
|
||||
assertFalse(fs.exists(tempName));
|
||||
|
||||
// in a subdirectory
|
||||
// create a temporary filename for a file in a subdirectory, without creating the file
|
||||
tempName = fs.getTempFile('tests', false);
|
||||
assertTrue(tempName !== '');
|
||||
// filename should be located underneath tempPath
|
||||
assertEqual(fs.join(fs.getTempPath(), 'test'), tempName.substr(0, fs.join(fs.getTempPath(), 'test').length));
|
||||
// file should not yet exist
|
||||
assertFalse(fs.exists(tempName));
|
||||
|
||||
// clean up
|
||||
fs.removeDirectory(fs.join(fs.getTempPath(), 'tests'));
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief getTempPath()
|
||||
/// @brief getTempPath() - test the system's temp path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testGetTempPath : function () {
|
||||
|
@ -137,76 +163,102 @@ function FileSystemSuite () {
|
|||
tempName = fs.getTempPath();
|
||||
|
||||
assertTrue(tempName !== '');
|
||||
// the temp path should also be an existing directory
|
||||
assertTrue(fs.isDirectory(tempName));
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief isDirectory()
|
||||
/// @brief isDirectory() - test if a directory exists
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testIsDirectory : function () {
|
||||
var tempName;
|
||||
|
||||
// existing directory
|
||||
// create a new directory name
|
||||
tempName = fs.join(tempDir, 'foo');
|
||||
try {
|
||||
// remove any existing directory first
|
||||
fs.removeDirectory(tempName);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
|
||||
// create the directory with the full path to it
|
||||
fs.makeDirectoryRecursive(tempName);
|
||||
|
||||
// check if it does exist
|
||||
assertTrue(fs.exists(tempName));
|
||||
// should also be a directory
|
||||
assertTrue(fs.isDirectory(tempName));
|
||||
// remove the directory
|
||||
fs.removeDirectory(tempName);
|
||||
|
||||
// non-existing directory/file
|
||||
tempName = fs.join(tempDir, 'meow');
|
||||
// this file shouldn't exist
|
||||
assertFalse(fs.exists(tempName));
|
||||
// and thus shouldn't be a directory
|
||||
assertFalse(fs.isDirectory(tempName));
|
||||
|
||||
// file
|
||||
// create a new file
|
||||
tempName = fs.getTempFile('', true);
|
||||
// should exist
|
||||
assertTrue(fs.exists(tempName));
|
||||
// but shouldn't be a directory
|
||||
assertFalse(fs.isDirectory(tempName));
|
||||
|
||||
// clean up
|
||||
fs.remove(tempName);
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief isFile()
|
||||
/// @brief isFile() - test if a file exists
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testIsFile : function () {
|
||||
var tempName;
|
||||
|
||||
// existing directory
|
||||
// create a new directory name
|
||||
tempName = fs.join(tempDir, 'foo');
|
||||
try {
|
||||
// remove any existing directory if it exists
|
||||
fs.removeDirectory(tempName);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
|
||||
try {
|
||||
// remove any existing file if it exists
|
||||
fs.remove(tempName);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
|
||||
// now create the whole directory with all paths to it
|
||||
fs.makeDirectoryRecursive(tempName);
|
||||
|
||||
// directory should now exist
|
||||
assertTrue(fs.exists(tempName));
|
||||
// but should not be a file
|
||||
assertFalse(fs.isFile(tempName));
|
||||
// remove it
|
||||
fs.removeDirectory(tempName);
|
||||
|
||||
// non-existing directory/file
|
||||
tempName = fs.join(tempDir, 'meow');
|
||||
// this file shouldn't exist
|
||||
assertFalse(fs.exists(tempName));
|
||||
// and shouldn't be a file
|
||||
assertFalse(fs.isFile(tempName));
|
||||
|
||||
// file
|
||||
// now create a new file
|
||||
tempName = fs.getTempFile('', true);
|
||||
// should exist
|
||||
assertTrue(fs.exists(tempName));
|
||||
// and should be a file
|
||||
assertTrue(fs.isFile(tempName));
|
||||
|
||||
// clean up and remove the file
|
||||
fs.remove(tempName);
|
||||
},
|
||||
|
||||
|
@ -218,37 +270,53 @@ function FileSystemSuite () {
|
|||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief makeDirectory()
|
||||
/// @brief makeDirectory() - create a directory
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testMakeDirectory : function () {
|
||||
var tempName;
|
||||
|
||||
// create the name for a subdirectory
|
||||
tempName = fs.join(tempDir, 'bar');
|
||||
try {
|
||||
// remove an existing directory if it already exists
|
||||
fs.removeDirectoryRecursive(tempName);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
|
||||
// the directory shouldn't exist. we just deleted it if it was there
|
||||
assertFalse(fs.exists(tempName));
|
||||
|
||||
// now create the directory
|
||||
fs.makeDirectory(tempName);
|
||||
|
||||
// directory should be there after creation
|
||||
assertTrue(fs.exists(tempName));
|
||||
|
||||
// now remove it
|
||||
fs.removeDirectory(tempName);
|
||||
|
||||
// create the name for another subdirectory
|
||||
tempName = fs.join(tempDir, 'baz');
|
||||
try {
|
||||
// remove it if it does exist
|
||||
fs.removeDirectoryRecursive(tempName);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
|
||||
// make with an existing directory
|
||||
// the directory shouldn't be there...
|
||||
assertFalse(fs.exists(tempName));
|
||||
|
||||
// now create it
|
||||
fs.makeDirectory(tempName);
|
||||
|
||||
// should have succeeded
|
||||
assertTrue(fs.exists(tempName));
|
||||
|
||||
try {
|
||||
// try to create the same directory again. this should fail
|
||||
fs.makeDirectory(tempName);
|
||||
fail();
|
||||
}
|
||||
|
@ -256,16 +324,21 @@ function FileSystemSuite () {
|
|||
assertEqual(ERRORS.ERROR_SYS_ERROR.code, err.errorNum);
|
||||
}
|
||||
|
||||
// make subdirectory
|
||||
// create a subdirectory in the directory we created
|
||||
tempName = fs.join(tempDir, 'baz', 'foo');
|
||||
fs.makeDirectory(tempName);
|
||||
// the subdirectory should now be there
|
||||
assertTrue(fs.exists(tempName));
|
||||
|
||||
// create a file in the subdirecory
|
||||
tempName = fs.join(tempDir, 'baz', 'foo', 'test');
|
||||
// write something to the file
|
||||
fs.write(tempName, "this is a test");
|
||||
// the file should exist after writing to it
|
||||
assertTrue(fs.exists(tempName));
|
||||
|
||||
try {
|
||||
// create a directory with the name of an already existing file. this should fail.
|
||||
fs.makeDirectory(tempName);
|
||||
fail();
|
||||
}
|
||||
|
@ -273,73 +346,83 @@ function FileSystemSuite () {
|
|||
assertEqual(ERRORS.ERROR_SYS_ERROR.code, err.errorNum);
|
||||
}
|
||||
|
||||
// remove all stuff we created for testing
|
||||
fs.removeDirectoryRecursive(fs.join(tempDir, 'baz'));
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief makeDirectoryRecursive()
|
||||
/// @brief makeDirectoryRecursive() - create a directory will all paths to it
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testMakeDirectoryRecursive : function () {
|
||||
var tempName;
|
||||
|
||||
// create the name for a new subdirectory
|
||||
tempName = fs.join(tempDir, 'bar');
|
||||
try {
|
||||
// make sure it does not yet exist
|
||||
fs.removeDirectoryRecursive(tempName);
|
||||
}
|
||||
catch (err) {
|
||||
}
|
||||
|
||||
// create
|
||||
// create the subdirectory
|
||||
fs.makeDirectoryRecursive(tempName);
|
||||
// check if it is there
|
||||
assertTrue(fs.isDirectory(tempName));
|
||||
|
||||
// create again
|
||||
// create the subdirectory again. this should not fail
|
||||
fs.makeDirectoryRecursive(tempName);
|
||||
// should still be a directory
|
||||
assertTrue(fs.isDirectory(tempName));
|
||||
|
||||
// create subdirectories
|
||||
// create subdirectory in subdirectory of subdirectory
|
||||
tempName = fs.join(tempDir, 'bar', 'baz', 'test');
|
||||
fs.makeDirectoryRecursive(tempName);
|
||||
assertTrue(fs.isDirectory(tempName));
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief move()
|
||||
/// @brief move() - TODO
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testMove : function () {
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief read()
|
||||
/// @brief read() - TODO
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRead : function () {
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove()
|
||||
/// @brief remove() - TODO
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testRemove : function () {
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief size()
|
||||
/// @brief test size() - the filesize of a file
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testSize : function () {
|
||||
var tempName;
|
||||
|
||||
// existing file
|
||||
// create a new file with a specific content
|
||||
tempName = fs.join(tempDir, 'foo');
|
||||
fs.write(tempName, "this is a test file");
|
||||
|
||||
// test the size of the new file
|
||||
assertEqual(19, fs.size(tempName));
|
||||
|
||||
// remove the new file
|
||||
fs.remove(tempName);
|
||||
|
||||
// non-existing file
|
||||
// now the file does not exist
|
||||
try {
|
||||
// try to read filesize. this should fail
|
||||
fs.size(tempName);
|
||||
fail();
|
||||
}
|
||||
|
@ -350,12 +433,15 @@ function FileSystemSuite () {
|
|||
// directory
|
||||
fs.makeDirectory(tempName);
|
||||
try {
|
||||
// try to read the filesize of a directory. this should fail
|
||||
fs.size(tempName);
|
||||
fail();
|
||||
}
|
||||
catch (err) {
|
||||
assertEqual(ERRORS.ERROR_FILE_NOT_FOUND.code, err.errorNum);
|
||||
}
|
||||
|
||||
// remove the directory
|
||||
fs.removeDirectory(tempName);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue