diff --git a/CHANGELOG b/CHANGELOG index f8c0345638..78dc3d4420 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,12 @@ -v2.2.2-rc1 (XXXX-XX-XX) +v2.3.0 (XXXX-XX-XX) +------------------- + +* automatically call `toJSON` function of JavaScript objects (if present) + when serializing them into database documents. This change allows + storing JavaScript date objects in the database in a sensible manner. + + +v2.2.2 (2014-08-08) ------------------- * allow storing non-reserved attribute names starting with an underscore diff --git a/Documentation/Books/Users/Aql/Operators.mdpp b/Documentation/Books/Users/Aql/Operators.mdpp index 00f37c62e5..d2b93fd1a0 100644 --- a/Documentation/Books/Users/Aql/Operators.mdpp +++ b/Documentation/Books/Users/Aql/Operators.mdpp @@ -125,7 +125,7 @@ will produce the following result: !SUBSUBSECTION Operator precedence -The operator precedence in AQL is similar as in other familliar languages (lowest precedence first): +The operator precedence in AQL is similar as in other familiar languages (lowest precedence first): - *? :* ternary operator - *||* logical or @@ -135,9 +135,9 @@ The operator precedence in AQL is similar as in other familliar languages (lowes - *<*, *<=*, *>=*, *>* less than, less equal, greater equal, greater than - *+*, *-* addition, subtraction -- ***, */*, *%* multiplication, division, modulus +- \*, */*, *%* multiplication, division, modulus - *!*, *+*, *-* logical negation, unary plus, unary minus -- *[*]* expansion +- *[\]* expansion - *()* function call - *.* member access - *[]* indexed value access diff --git a/Documentation/Books/Users/Glossary/README.mdpp b/Documentation/Books/Users/Glossary/README.mdpp index 476b131691..7337fc7ef9 100644 --- a/Documentation/Books/Users/Glossary/README.mdpp +++ b/Documentation/Books/Users/Glossary/README.mdpp @@ -18,7 +18,7 @@ opaque strings when they store or use it locally. !SUBSECTION Collection Name -A collection name identifies a collection in a database. It is a string and is unique within the database. Unlike the collection identifier it is supplied by the creator of the collection. The collection name must consist of letters, digits, and the _ (underscore) and - (dash) characters only. Please refer to @ref NamingConventions for more information on valid collection names. +A collection name identifies a collection in a database. It is a string and is unique within the database. Unlike the collection identifier it is supplied by the creator of the collection. The collection name must consist of letters, digits, and the _ (underscore) and - (dash) characters only. Please refer to [NamingConventions](../NamingConventions/CollectionNames.html) for more information on valid collection names. !SUBSECTION Database @@ -28,7 +28,7 @@ A database contains its own collections (which cannot be accessed from other dat There will always be at least one database in ArangoDB. This is the default database, named _system. This database cannot be dropped, and provides special operations for creating, dropping, and enumerating databases. Users can create additional databases and give them unique names to access them later. Database management operations cannot be initiated from out of user-defined databases. -When ArangoDB is accessed via its HTTP REST API, the database name is read from the first part of the request URI path (e.g. /_db/_system/...). If the request URI does not contain a database name, the database name is automatically determined by the algorithm described in @ref HttpDatabaseMapping. +When ArangoDB is accessed via its HTTP REST API, the database name is read from the first part of the request URI path (e.g. /_db/_system/...). If the request URI does not contain a database name, the database name is automatically. !SUBSECTION Database Name diff --git a/arangod/Actions/RestActionHandler.cpp b/arangod/Actions/RestActionHandler.cpp index 8d8bd00fe8..7a6a622d5f 100644 --- a/arangod/Actions/RestActionHandler.cpp +++ b/arangod/Actions/RestActionHandler.cpp @@ -51,26 +51,11 @@ RestActionHandler::RestActionHandler (HttpRequest* request, : RestVocbaseBaseHandler(request), _action(0), _queue(), - _allowed(false), _dataLock(), _data(0) { _action = TRI_LookupActionVocBase(request); - // check if the action is allowed - if (_action != 0) { - for (set::const_iterator i = data->_contexts.begin(); i != data->_contexts.end(); ++i) { - if (_action->_contexts.find(*i) != _action->_contexts.end()) { - _allowed = true; - break; - } - } - - if (! _allowed) { - _action = 0; - } - } - // use the queue from options if an action is known if (_action != 0) { _queue = data->_queue; @@ -121,11 +106,6 @@ HttpHandler::status_t RestActionHandler::execute () { generateNotImplemented(_request->requestPath()); } - // need permission - else if (! _allowed) { - generateForbidden(); - } - // execute else { diff --git a/arangod/Actions/RestActionHandler.h b/arangod/Actions/RestActionHandler.h index a66537f781..66719d6b9b 100644 --- a/arangod/Actions/RestActionHandler.h +++ b/arangod/Actions/RestActionHandler.h @@ -68,7 +68,6 @@ namespace triagens { struct action_options_t { TRI_vocbase_t* _vocbase; std::string _queue; - std::set _contexts; }; // ----------------------------------------------------------------------------- @@ -145,12 +144,6 @@ namespace triagens { std::string _queue; -//////////////////////////////////////////////////////////////////////////////// -/// @brief action allowed in this context -//////////////////////////////////////////////////////////////////////////////// - - bool _allowed; - //////////////////////////////////////////////////////////////////////////////// /// @brief data lock //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Actions/actions.h b/arangod/Actions/actions.h index ea4d0d616d..0a165c9feb 100644 --- a/arangod/Actions/actions.h +++ b/arangod/Actions/actions.h @@ -84,8 +84,12 @@ class TRI_action_result_t { class TRI_action_t { public: - TRI_action_t (std::set const& context) - : _type(), _url(), _isPrefix(false), _urlParts(0), _contexts(context) { + TRI_action_t () + : _type(), + _url(), + _urlParts(0), + _isPrefix(false), + _allowUseDatabase(false) { } virtual ~TRI_action_t () {} @@ -100,10 +104,11 @@ class TRI_action_t { std::string _type; std::string _url; - bool _isPrefix; size_t _urlParts; - std::set _contexts; + + bool _isPrefix; + bool _allowUseDatabase; }; // ----------------------------------------------------------------------------- diff --git a/arangod/Ahuacatl/ahuacatl-grammar.h b/arangod/Ahuacatl/ahuacatl-grammar.h index 9057db4768..6e4fe00a4e 100644 --- a/arangod/Ahuacatl/ahuacatl-grammar.h +++ b/arangod/Ahuacatl/ahuacatl-grammar.h @@ -110,14 +110,14 @@ extern int Ahuacatldebug; typedef union YYSTYPE YYSTYPE; union YYSTYPE { -#line 26 "arangod/Ahuacatl/ahuacatl-grammar.y" /* yacc.c:1909 */ +#line 26 "arangod/Ahuacatl/ahuacatl-grammar.y" /* yacc.c:1915 */ TRI_aql_node_t* node; char* strval; bool boolval; int64_t intval; -#line 121 "arangod/Ahuacatl/ahuacatl-grammar.hpp" /* yacc.c:1909 */ +#line 121 "arangod/Ahuacatl/ahuacatl-grammar.hpp" /* yacc.c:1915 */ }; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 diff --git a/arangod/Aql/AqlValue.cpp b/arangod/Aql/AqlValue.cpp index d16e2bfcda..00ec4779f7 100644 --- a/arangod/Aql/AqlValue.cpp +++ b/arangod/Aql/AqlValue.cpp @@ -215,7 +215,8 @@ v8::Handle AqlValue::toV8 (AQL_TRANSACTION_V8* trx, /// @brief toString method //////////////////////////////////////////////////////////////////////////////// -std::string AqlValue::toString (TRI_document_collection_t const* document) const { +std::string AqlValue::toString (AQL_TRANSACTION_V8* trx, + TRI_document_collection_t const* document) const { switch (_type) { case JSON: { return _json->toString(); @@ -224,7 +225,7 @@ std::string AqlValue::toString (TRI_document_collection_t const* document) const case SHAPED: { // we're lazy and just stringify the json representation // this does not matter as this code is not performance-sensitive - return toJson(document).toString(); + return toJson(trx, document).toString(); } case DOCVEC: { @@ -252,7 +253,8 @@ std::string AqlValue::toString (TRI_document_collection_t const* document) const /// @brief toJson method //////////////////////////////////////////////////////////////////////////////// -Json AqlValue::toJson (TRI_document_collection_t const* document) const { +Json AqlValue::toJson (AQL_TRANSACTION_V8* trx, + TRI_document_collection_t const* document) const { switch (_type) { case JSON: { return _json->copy(); @@ -265,16 +267,32 @@ Json AqlValue::toJson (TRI_document_collection_t const* document) const { TRI_shaper_t* shaper = document->getShaper(); TRI_shaped_json_t shaped; TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, _marker); - triagens::basics::Json json(shaper->_memoryZone, TRI_JsonShapedJson(shaper, &shaped)); + Json json(shaper->_memoryZone, TRI_JsonShapedJson(shaper, &shaped)); + // append the internal attributes + + // _id, _key, _rev char const* key = TRI_EXTRACT_MARKER_KEY(_marker); - // TODO: use CollectionNameResolver - std::string id(document->_info._name); + std::string id(trx->resolver()->getCollectionName(document->_info._cid)); id.push_back('/'); - id += std::string(key); - json("_id", triagens::basics::Json(id)); - json("_rev", triagens::basics::Json(std::to_string(TRI_EXTRACT_MARKER_RID(_marker) ))); - json("_key", triagens::basics::Json(key)); + id.append(key); + json("_id", Json(id)); + json("_rev", Json(std::to_string(TRI_EXTRACT_MARKER_RID(_marker)))); + json("_key", Json(key)); + + if (TRI_IS_EDGE_MARKER(_marker)) { + // _from + std::string from(trx->resolver()->getCollectionName(TRI_EXTRACT_MARKER_FROM_CID(_marker))); + from.push_back('/'); + from.append(TRI_EXTRACT_MARKER_FROM_KEY(_marker)); + json("_from", Json(from)); + + // _to + std::string to(trx->resolver()->getCollectionName(TRI_EXTRACT_MARKER_TO_CID(_marker))); + to.push_back('/'); + to.append(TRI_EXTRACT_MARKER_TO_KEY(_marker)); + json("_to", Json(to)); + } // TODO: return _from and _to, and fix order of attributes! return json; @@ -297,7 +315,7 @@ Json AqlValue::toJson (TRI_document_collection_t const* document) const { size_t const n = current->size(); auto vecCollection = current->getDocumentCollection(0); for (size_t i = 0; i < n; ++i) { - json.add(current->getValue(i, 0).toJson(vecCollection)); + json.add(current->getValue(i, 0).toJson(trx, vecCollection)); } } @@ -331,7 +349,8 @@ Json AqlValue::toJson (TRI_document_collection_t const* document) const { /// @brief create an AqlValue from a vector of AqlItemBlock*s //////////////////////////////////////////////////////////////////////////////// -AqlValue AqlValue::CreateFromBlocks (std::vector const& src, +AqlValue AqlValue::CreateFromBlocks (AQL_TRANSACTION_V8* trx, + std::vector const& src, std::vector const& variableNames) { size_t totalSize = 0; @@ -352,7 +371,7 @@ AqlValue AqlValue::CreateFromBlocks (std::vector const& src, for (RegisterId j = 0; j < n; ++j) { if (variableNames[j][0] != '\0') { // temporaries don't have a name and won't be included - values.set(variableNames[j].c_str(), current->getValue(i, j).toJson(current->getDocumentCollection(j))); + values.set(variableNames[j].c_str(), current->getValue(i, j).toJson(trx, current->getDocumentCollection(j))); } } @@ -372,7 +391,8 @@ AqlValue AqlValue::CreateFromBlocks (std::vector const& src, /// @brief 3-way comparison for AqlValue objects //////////////////////////////////////////////////////////////////////////////// -int AqlValue::Compare (AqlValue const& left, +int AqlValue::Compare (AQL_TRANSACTION_V8* trx, + AqlValue const& left, TRI_document_collection_t const* leftcoll, AqlValue const& right, TRI_document_collection_t const* rightcoll) { @@ -386,12 +406,12 @@ int AqlValue::Compare (AqlValue const& left, } if (left._type == AqlValue::JSON && right._type == AqlValue::SHAPED) { - triagens::basics::Json rjson = right.toJson(rightcoll); + triagens::basics::Json rjson = right.toJson(trx, rightcoll); return TRI_CompareValuesJson(left._json->json(), rjson.json(), true); } if (left._type == AqlValue::SHAPED && right._type == AqlValue::JSON) { - triagens::basics::Json ljson = left.toJson(leftcoll); + triagens::basics::Json ljson = left.toJson(trx, leftcoll); return TRI_CompareValuesJson(ljson.json(), right._json->json(), true); } @@ -432,7 +452,8 @@ int AqlValue::Compare (AqlValue const& left, rblock < right._vector->size()) { AqlValue lval = left._vector->at(lblock)->getValue(litem, 0); AqlValue rval = right._vector->at(rblock)->getValue(ritem, 0); - int cmp = Compare(lval, + int cmp = Compare(trx, + lval, left._vector->at(lblock)->getDocumentCollection(0), rval, right._vector->at(rblock)->getDocumentCollection(0)); diff --git a/arangod/Aql/AqlValue.h b/arangod/Aql/AqlValue.h index 955a3b39d9..d621b9263d 100644 --- a/arangod/Aql/AqlValue.h +++ b/arangod/Aql/AqlValue.h @@ -154,36 +154,40 @@ namespace triagens { /// @brief construct a V8 value as input for the expression execution in V8 //////////////////////////////////////////////////////////////////////////////// - v8::Handle toV8 (AQL_TRANSACTION_V8* trx, + v8::Handle toV8 (AQL_TRANSACTION_V8*, TRI_document_collection_t const*) const; //////////////////////////////////////////////////////////////////////////////// /// @brief toString method //////////////////////////////////////////////////////////////////////////////// - std::string toString (TRI_document_collection_t const*) const; + std::string toString (AQL_TRANSACTION_V8*, + TRI_document_collection_t const*) const; //////////////////////////////////////////////////////////////////////////////// /// @brief toJson method //////////////////////////////////////////////////////////////////////////////// - triagens::basics::Json toJson (TRI_document_collection_t const*) const; + triagens::basics::Json toJson (AQL_TRANSACTION_V8*, + TRI_document_collection_t const*) const; //////////////////////////////////////////////////////////////////////////////// /// @brief create an AqlValue from a vector of AqlItemBlock*s //////////////////////////////////////////////////////////////////////////////// - static AqlValue CreateFromBlocks (std::vector const&, + static AqlValue CreateFromBlocks (AQL_TRANSACTION_V8*, + std::vector const&, std::vector const&); //////////////////////////////////////////////////////////////////////////////// /// @brief 3-way comparison for AqlValue objects //////////////////////////////////////////////////////////////////////////////// - static int Compare (AqlValue const& left, - TRI_document_collection_t const* leftcoll, - AqlValue const& right, - TRI_document_collection_t const* rightcoll); + static int Compare (AQL_TRANSACTION_V8*, + AqlValue const&, + TRI_document_collection_t const*, + AqlValue const&, + TRI_document_collection_t const*); // ----------------------------------------------------------------------------- // --SECTION-- public variables diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 4725529bdd..49fc460d6e 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -60,14 +60,18 @@ ExecutionBlock::~ExecutionBlock () { // --SECTION-- public methods // ----------------------------------------------------------------------------- -int ExecutionBlock::bind (AqlItemBlock* items, size_t pos) { - int res; +int ExecutionBlock::initCursor (AqlItemBlock* items, size_t pos) { for (auto d : _dependencies) { - res = d->bind(items, pos); + int res = d->initCursor(items, pos); if (res != TRI_ERROR_NO_ERROR) { return res; } } + for (auto x : _buffer) { + delete x; + } + _buffer.clear(); + _done = false; return TRI_ERROR_NO_ERROR; } diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index 071544c43f..21abfa9b67 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -392,26 +392,28 @@ namespace triagens { }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief Methods for execution +/// Lifecycle is: +/// CONSTRUCTOR +/// then the ExecutionEngine automatically calls +/// staticAnalysis() once, including subqueries +/// then the ExecutionEngine automatically calls +/// initialize() once, including subqueries +/// possibly repeat many times: +/// initCursor(...) (optionally with bind parameters) +/// // use cursor functionality +/// then the ExecutionEngine automatically calls +/// shutdown() +/// DESTRUCTOR +//////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// /// @brief static analysis //////////////////////////////////////////////////////////////////////////////// void staticAnalysis (ExecutionBlock* super = nullptr); -//////////////////////////////////////////////////////////////////////////////// -/// @brief Methods for execution -/// Lifecycle is: -/// staticAnalysis() -/// initialize() -/// possibly repeat many times: -/// bind(...) -/// execute(...) -/// // use cursor functionality -/// shutdown() -/// It should be possible to perform the sequence from initialize to shutdown -/// multiple times. -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief initialize //////////////////////////////////////////////////////////////////////////////// @@ -428,28 +430,13 @@ namespace triagens { } //////////////////////////////////////////////////////////////////////////////// -/// @brief bind +/// @brief initCursor, could be called multiple times //////////////////////////////////////////////////////////////////////////////// - virtual int bind (AqlItemBlock* items, size_t pos); + virtual int initCursor (AqlItemBlock* items, size_t pos); //////////////////////////////////////////////////////////////////////////////// -/// @brief execute -//////////////////////////////////////////////////////////////////////////////// - - virtual int execute () { - for (auto it = _dependencies.begin(); it != _dependencies.end(); ++it) { - int res = (*it)->execute(); - if (res != TRI_ERROR_NO_ERROR) { - return res; - } - } - _done = false; - return TRI_ERROR_NO_ERROR; - } - -//////////////////////////////////////////////////////////////////////////////// -/// @brief shutdown +/// @brief shutdown, will be called exactly once for the whole query //////////////////////////////////////////////////////////////////////////////// virtual int shutdown () { @@ -468,13 +455,13 @@ namespace triagens { return TRI_ERROR_NO_ERROR; } - protected: - //////////////////////////////////////////////////////////////////////////////// /// @brief copy register data from one block (src) into another (dst) /// register values are cloned //////////////////////////////////////////////////////////////////////////////// + protected: + void inheritRegisters (AqlItemBlock const* src, AqlItemBlock* dst, size_t row) { @@ -816,15 +803,18 @@ namespace triagens { } //////////////////////////////////////////////////////////////////////////////// -/// @brief bind, store a copy of the register values coming from above +/// @brief initCursor, store a copy of the register values coming from above //////////////////////////////////////////////////////////////////////////////// - int bind (AqlItemBlock* items, size_t pos) { + int initCursor (AqlItemBlock* items, size_t pos) { // Create a deep copy of the register values given to us: if (_inputRegisterValues != nullptr) { delete _inputRegisterValues; } - _inputRegisterValues = items->slice(pos, pos+1); + if (items != nullptr) { + _inputRegisterValues = items->slice(pos, pos+1); + } + _done = false; return TRI_ERROR_NO_ERROR; } @@ -1027,22 +1017,21 @@ namespace triagens { std::string collectionName(p->_collname); collectionName.push_back('/'); - initDocuments(); - return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// -/// @brief execute, here we release our docs from this collection +/// @brief initCursor, here we release our docs from this collection //////////////////////////////////////////////////////////////////////////////// - int execute () { - int res = ExecutionBlock::execute(); - + int initCursor (AqlItemBlock* items, size_t pos) { + int res = ExecutionBlock::initCursor(items, pos); if (res != TRI_ERROR_NO_ERROR) { return res; } + initDocuments(); + if (_totalCount == 0) { _done = true; } @@ -1050,16 +1039,6 @@ namespace triagens { return TRI_ERROR_NO_ERROR; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief shutdown, here we release our docs from this collection -//////////////////////////////////////////////////////////////////////////////// - - int shutdown () { - int res = ExecutionBlock::shutdown(); // Tell all dependencies - - return res; - } - //////////////////////////////////////////////////////////////////////////////// /// @brief getSome //////////////////////////////////////////////////////////////////////////////// @@ -1274,8 +1253,8 @@ namespace triagens { /// @brief execute, here we release our docs from this collection //////////////////////////////////////////////////////////////////////////////// - int execute () { - int res = ExecutionBlock::execute(); + int initCursor (AqlItemBlock* items, size_t pos) { + int res = ExecutionBlock::initCursor(items, pos); if (res != TRI_ERROR_NO_ERROR) { return res; @@ -1786,7 +1765,7 @@ namespace triagens { TRI_ASSERT(it3 != _varOverview->varInfo.end()); _outReg = it3->second.registerId; - return TRI_ERROR_NO_ERROR; + return getSubquery()->initialize(); } //////////////////////////////////////////////////////////////////////////////// @@ -1802,14 +1781,7 @@ namespace triagens { } for (size_t i = 0; i < res->size(); i++) { - int ret; - ret = _subquery->initialize(); - if (ret == TRI_ERROR_NO_ERROR) { - ret = _subquery->bind(res.get(), i); - } - if (ret == TRI_ERROR_NO_ERROR) { - ret = _subquery->execute(); - } + int ret = _subquery->initCursor(res.get(), i); if (ret != TRI_ERROR_NO_ERROR) { THROW_ARANGO_EXCEPTION(ret); } @@ -1834,12 +1806,6 @@ namespace triagens { delete results; throw; } - - ret = _subquery->shutdown(); - if (ret != TRI_ERROR_NO_ERROR) { - THROW_ARANGO_EXCEPTION(ret); - } - } return res.release(); } @@ -2241,7 +2207,8 @@ namespace triagens { size_t i = 0; for (auto it = _aggregateRegisters.begin(); it != _aggregateRegisters.end(); ++it) { - int cmp = AqlValue::Compare(_currentGroup.groupValues[i], + int cmp = AqlValue::Compare(_trx, + _currentGroup.groupValues[i], _currentGroup.collections[i], cur->getValue(_pos, (*it).second), cur->getDocumentCollection((*it).second)); @@ -2363,7 +2330,7 @@ namespace triagens { _currentGroup.addValues(cur, _groupRegister); res->setValue(row, _groupRegister, - AqlValue::CreateFromBlocks(_currentGroup.groupBlocks, _variableNames)); + AqlValue::CreateFromBlocks(_trx, _currentGroup.groupBlocks, _variableNames)); // FIXME: can throw: } @@ -2450,11 +2417,11 @@ namespace triagens { } //////////////////////////////////////////////////////////////////////////////// -/// @brief execute +/// @brief initCursor //////////////////////////////////////////////////////////////////////////////// - virtual int execute () { - int res = ExecutionBlock::execute(); + virtual int initCursor (AqlItemBlock* items, size_t pos) { + int res = ExecutionBlock::initCursor(items, pos); if (res != TRI_ERROR_NO_ERROR) { return res; } @@ -2467,6 +2434,21 @@ namespace triagens { return TRI_ERROR_NO_ERROR; } + doSorting(); + + _done = false; + _pos = 0; + + return TRI_ERROR_NO_ERROR; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief dosorting +//////////////////////////////////////////////////////////////////////////////// + + private: + + void doSorting () { // coords[i][j] is the th row of the th block std::vector> coords; @@ -2493,7 +2475,7 @@ namespace triagens { } // comparison function - OurLessThan ourLessThan(_buffer, _sortRegisters, colls); + OurLessThan ourLessThan(_trx, _buffer, _sortRegisters, colls); // sort coords if (_stable) { @@ -2612,25 +2594,21 @@ namespace triagens { for (auto x : newbuffer) { delete x; } - - _done = false; - _pos = 0; - return TRI_ERROR_NO_ERROR; } - private: - //////////////////////////////////////////////////////////////////////////////// /// @brief OurLessThan //////////////////////////////////////////////////////////////////////////////// class OurLessThan { public: - OurLessThan (std::deque& buffer, + OurLessThan (AQL_TRANSACTION_V8* trx, + std::deque& buffer, std::vector>& sortRegisters, std::vector& colls) - : _buffer(buffer), + : _trx(trx), + _buffer(buffer), _sortRegisters(sortRegisters), _colls(colls) { } @@ -2640,7 +2618,8 @@ namespace triagens { size_t i = 0; for (auto reg : _sortRegisters) { - int cmp = AqlValue::Compare(_buffer[a.first]->getValue(a.second, reg.first), + int cmp = AqlValue::Compare(_trx, + _buffer[a.first]->getValue(a.second, reg.first), _colls[i], _buffer[b.first]->getValue(b.second, reg.first), _colls[i]); @@ -2657,6 +2636,7 @@ namespace triagens { } private: + AQL_TRANSACTION_V8* _trx; std::deque& _buffer; std::vector>& _sortRegisters; std::vector& _colls; @@ -2710,18 +2690,33 @@ namespace triagens { if (res != TRI_ERROR_NO_ERROR) { return res; } + return TRI_ERROR_NO_ERROR; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initCursor +//////////////////////////////////////////////////////////////////////////////// + + int initCursor (AqlItemBlock* items, size_t pos) { + int res = ExecutionBlock::initCursor(items, pos); + if (res != TRI_ERROR_NO_ERROR) { + return res; + } _state = 0; _count = 0; return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// -/// @brief getSome +/// @brief getOrSkipSome //////////////////////////////////////////////////////////////////////////////// - virtual size_t skipSome (size_t atLeast, size_t atMost) { + virtual int getOrSkipSome (size_t atLeast, size_t atMost, bool skipping, + AqlItemBlock*& result, size_t& skipped) { + TRI_ASSERT(result == nullptr && skipped == 0); + if (_state == 2) { - return 0; + return TRI_ERROR_NO_ERROR; } if (_state == 0) { @@ -2732,7 +2727,7 @@ namespace triagens { _count = 0; if (_limit == 0) { _state = 2; - return 0; + return TRI_ERROR_NO_ERROR; } } @@ -2745,55 +2740,16 @@ namespace triagens { } } - size_t skipped = ExecutionBlock::skipSome(atLeast, atMost); + ExecutionBlock::getOrSkipSome(atLeast, atMost, skipping, result, skipped); if (skipped == 0) { - return 0; + return TRI_ERROR_NO_ERROR; } _count += skipped; if (_count >= _limit) { _state = 2; } - return skipped; - } - - virtual AqlItemBlock* getSome (size_t atLeast, - size_t atMost) { - if (_state == 2) { - return nullptr; - } - - if (_state == 0) { - if (_offset > 0) { - ExecutionBlock::_dependencies[0]->skip(_offset); - } - _state = 1; - _count = 0; - if (_limit == 0) { - _state = 2; - return nullptr; - } - } - - // If we get to here, _state == 1 and _count < _limit - - if (atMost > _limit - _count) { - atMost = _limit - _count; - if (atLeast > atMost) { - atLeast = atMost; - } - } - - auto res = ExecutionBlock::getSome(atLeast, atMost); - if (res == nullptr) { - return res; - } - _count += res->size(); - if (_count >= _limit) { - _state = 2; - } - - return res; + return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/ExecutionEngine.cpp b/arangod/Aql/ExecutionEngine.cpp index 9a5212081c..cf672ca58a 100644 --- a/arangod/Aql/ExecutionEngine.cpp +++ b/arangod/Aql/ExecutionEngine.cpp @@ -172,17 +172,18 @@ struct Instanciator : public WalkerWorker { //////////////////////////////////////////////////////////////////////////////// ExecutionEngine* ExecutionEngine::instanciateFromPlan (AQL_TRANSACTION_V8* trx, - ExecutionNode* plan) { + ExecutionPlan* plan) { auto engine = new ExecutionEngine(trx); try { auto inst = new Instanciator(engine); - plan->walk(inst); + plan->root()->walk(inst); auto root = inst->root; delete inst; root->staticAnalysis(); root->initialize(); + root->initCursor(nullptr, 0); engine->_root = root; diff --git a/arangod/Aql/ExecutionEngine.h b/arangod/Aql/ExecutionEngine.h index 821cc5acf1..019d487cdd 100644 --- a/arangod/Aql/ExecutionEngine.h +++ b/arangod/Aql/ExecutionEngine.h @@ -32,14 +32,14 @@ #include "Basics/Common.h" +#include "arangod/Aql/AqlItemBlock.h" +#include "arangod/Aql/ExecutionBlock.h" +#include "arangod/Aql/ExecutionPlan.h" #include "Utils/AqlTransaction.h" namespace triagens { namespace aql { - class ExecutionBlock; - class ExecutionNode; - // ----------------------------------------------------------------------------- // --SECTION-- class ExecutionEngine // ----------------------------------------------------------------------------- @@ -73,11 +73,11 @@ namespace triagens { public: //////////////////////////////////////////////////////////////////////////////// -/// @brief create an execution engine from a plan +// @brief create an execution engine from a plan //////////////////////////////////////////////////////////////////////////////// static ExecutionEngine* instanciateFromPlan (AQL_TRANSACTION_V8*, - ExecutionNode*); + ExecutionPlan*); //////////////////////////////////////////////////////////////////////////////// /// @brief get the root block @@ -96,6 +96,54 @@ namespace triagens { return _trx; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief getSome +//////////////////////////////////////////////////////////////////////////////// + + AqlItemBlock* getSome (size_t atLeast, size_t atMost) { + return _root->getSome(atLeast, atMost); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief getOne +//////////////////////////////////////////////////////////////////////////////// + + AqlItemBlock* getOne () { + return _root->getSome(1, 1); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief skip +//////////////////////////////////////////////////////////////////////////////// + + bool skip (size_t number) { + return _root->skip(number); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief hasMore +//////////////////////////////////////////////////////////////////////////////// + + bool hasMore () { + return _root->hasMore(); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief count +//////////////////////////////////////////////////////////////////////////////// + + int64_t count () { + return _root->count(); + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief remaining +//////////////////////////////////////////////////////////////////////////////// + + int64_t remaining () { + return _root->remaining(); + } + //////////////////////////////////////////////////////////////////////////////// /// @brief add a block to the engine //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index ab88852c10..3837414f1a 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -188,18 +188,33 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, auto shaper = myCollection->getShaper(); // look for the attribute name in the shape - if (strcmp(name, "_key") == 0) { - return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, TRI_EXTRACT_MARKER_KEY(result._marker))); - } - else if (strcmp(name, "_id") == 0) { - std::string id(myCollection->_info._name); - id.push_back('/'); - id.append(TRI_EXTRACT_MARKER_KEY(result._marker)); - return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, id)); - } - else if (strcmp(name, "_rev") == 0) { - TRI_voc_rid_t rid = TRI_EXTRACT_MARKER_RID(result._marker); - return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, JsonHelper::uint64String(TRI_UNKNOWN_MEM_ZONE, rid))); + if (*name == '_') { + if (strcmp(name, "_key") == 0) { + // _key value is copied into JSON + return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, TRI_EXTRACT_MARKER_KEY(result._marker))); + } + else if (strcmp(name, "_id") == 0) { + std::string id(trx->resolver()->getCollectionName(myCollection->_info._cid)); + id.push_back('/'); + id.append(TRI_EXTRACT_MARKER_KEY(result._marker)); + return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, id)); + } + else if (strcmp(name, "_rev") == 0) { + TRI_voc_rid_t rid = TRI_EXTRACT_MARKER_RID(result._marker); + return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, JsonHelper::uint64String(TRI_UNKNOWN_MEM_ZONE, rid))); + } + else if (strcmp(name, "_from") == 0) { + std::string from(trx->resolver()->getCollectionName(TRI_EXTRACT_MARKER_FROM_CID(result._marker))); + from.push_back('/'); + from.append(TRI_EXTRACT_MARKER_FROM_KEY(result._marker)); + return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, from)); + } + else if (strcmp(name, "_to") == 0) { + std::string to(trx->resolver()->getCollectionName(TRI_EXTRACT_MARKER_TO_CID(result._marker))); + to.push_back('/'); + to.append(TRI_EXTRACT_MARKER_TO_KEY(result._marker)); + return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, to)); + } } TRI_shape_pid_t pid = shaper->lookupAttributePathByName(shaper, name); @@ -247,7 +262,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, TRI_document_collection_t const* myCollection = nullptr; AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs); - list->add(result.toJson(myCollection)); + list->add(result.toJson(trx, myCollection)); } return AqlValue(list); } @@ -257,7 +272,6 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node, } } - else if (node->type == NODE_TYPE_REFERENCE) { auto v = static_cast(node->getData()); diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index 0c7e157fe7..f489dcad56 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -195,22 +195,19 @@ QueryResult Query::execute () { triagens::basics::Json json(triagens::basics::Json::List); try { - auto engine = ExecutionEngine::instanciateFromPlan(&trx, plan->root()); + auto engine = ExecutionEngine::instanciateFromPlan(&trx, plan); try { - auto root = engine->root(); - root->execute(); - AqlItemBlock* value; - while (nullptr != (value = root->getSome(1, ExecutionBlock::DefaultBatchSize))) { + while (nullptr != (value = engine->getSome(1, ExecutionBlock::DefaultBatchSize))) { auto doc = value->getDocumentCollection(0); size_t const n = value->size(); for (size_t i = 0; i < n; ++i) { AqlValue val = value->getValue(i, 0); if (! val.isEmpty()) { - json.add(val.toJson(doc)); + json.add(val.toJson(&trx, doc)); } } delete value; diff --git a/arangod/Cluster/ApplicationCluster.cpp b/arangod/Cluster/ApplicationCluster.cpp index 1e048f8679..fd8d3b2bdf 100644 --- a/arangod/Cluster/ApplicationCluster.cpp +++ b/arangod/Cluster/ApplicationCluster.cpp @@ -132,9 +132,26 @@ void ApplicationCluster::setupOptions (mapsetAuthentication(_username, _password); + + // overwrite memory area + _username = _password = "someotherusername"; + + ServerState::instance()->setDataPath(_dataPath); + ServerState::instance()->setLogPath(_logPath); + ServerState::instance()->setAgentPath(_agentPath); + ServerState::instance()->setArangodPath(_arangodPath); + ServerState::instance()->setDBserverConfig(_dbserverConfig); + ServerState::instance()->setCoordinatorConfig(_coordinatorConfig); + ServerState::instance()->setDisableDispatcherFrontend(_disableDispatcherFrontend); + ServerState::instance()->setDisableDispatcherKickstarter(_disableDispatcherKickstarter); + + // check the cluster state _enableCluster = (! _agencyEndpoints.empty() || ! _agencyPrefix.empty()); if (! enabled()) { @@ -196,22 +213,6 @@ bool ApplicationCluster::prepare () { //////////////////////////////////////////////////////////////////////////////// bool ApplicationCluster::start () { - - // set authentication data - ServerState::instance()->setAuthentication(_username, _password); - - // overwrite memory area - _username = _password = "someotherusername"; - - ServerState::instance()->setDataPath(_dataPath); - ServerState::instance()->setLogPath(_logPath); - ServerState::instance()->setAgentPath(_agentPath); - ServerState::instance()->setArangodPath(_arangodPath); - ServerState::instance()->setDBserverConfig(_dbserverConfig); - ServerState::instance()->setCoordinatorConfig(_coordinatorConfig); - ServerState::instance()->setDisableDispatcherFrontend(_disableDispatcherFrontend); - ServerState::instance()->setDisableDispatcherKickstarter(_disableDispatcherKickstarter); - if (! enabled()) { return true; } diff --git a/arangod/Cluster/ServerState.cpp b/arangod/Cluster/ServerState.cpp index 98bc0fd04a..85b25d27e2 100644 --- a/arangod/Cluster/ServerState.cpp +++ b/arangod/Cluster/ServerState.cpp @@ -48,6 +48,14 @@ using namespace triagens::arango; ServerState::ServerState () : _id(), + _dataPath(), + _logPath(), + _agentPath(), + _arangodPath(), + _dbserverConfig(), + _coordinatorConfig(), + _disableDispatcherFrontend(), + _disableDispatcherKickstarter(), _address(), _authentication(), _lock(), diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 4b8b08d501..84c43c9635 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -83,7 +83,7 @@ using namespace triagens::rest; using namespace triagens::admin; using namespace triagens::arango; -bool allowUseDatabaseInRESTActions; +bool ALLOW_USE_DATABASE_IN_REST_ACTIONS; // ----------------------------------------------------------------------------- // --SECTION-- private functions @@ -532,7 +532,7 @@ void ArangoServer::buildApplicationServer () { ("server.disable-authentication-unix-sockets", &_disableAuthenticationUnixSockets, "disable authentication for requests via UNIX domain sockets") #endif ("server.disable-replication-applier", &_disableReplicationApplier, "start with replication applier turned off") - ("server.allow-use-database", &allowUseDatabaseInRESTActions, "allow change of database in REST actions, only needed for unittests") + ("server.allow-use-database", &ALLOW_USE_DATABASE_IN_REST_ACTIONS, "allow change of database in REST actions, only needed for unittests") ; bool disableStatistics = false; @@ -799,7 +799,6 @@ int ArangoServer::startupServer () { _applicationV8->setVocbase(vocbase); _applicationV8->setConcurrency(concurrency); - // ............................................................................. // prepare everything // ............................................................................. @@ -809,7 +808,6 @@ int ArangoServer::startupServer () { _applicationDispatcher->disable(); _applicationEndpointServer->disable(); _applicationV8->disableActions(); - _applicationV8->setStartupFile(""); } // prepare scheduler and dispatcher @@ -823,20 +821,16 @@ int ArangoServer::startupServer () { // and finish prepare _applicationServer->prepare2(); - // run version check + // run version check (will exit!) if (checkVersion) { - _applicationV8->runUpgradeCheck(); + _applicationV8->versionCheck(); } - _applicationV8->runVersionCheck(skipUpgrade, performUpgrade); - - // finally flush the write-ahead log so all data in the WAL goes into the collections - - // WAL recovery done after here + _applicationV8->upgradeDatabase(skipUpgrade, performUpgrade); // setup the V8 actions if (startServer) { - _applicationV8->prepareActions(); + _applicationV8->prepareServer(); } // ............................................................................. @@ -850,11 +844,6 @@ int ArangoServer::startupServer () { if (startServer) { - // create the handlers - httpOptions._contexts.insert("user"); - httpOptions._contexts.insert("api"); - httpOptions._contexts.insert("admin"); - // create the server _applicationEndpointServer->buildServers(); @@ -876,10 +865,15 @@ int ArangoServer::startupServer () { _applicationServer->start(); - // if the authentication info could not be loaded, but authentication is turned on, - // then we refuse to start - if (! vocbase->_authInfoLoaded && ! _disableAuthentication) { - LOG_FATAL_AND_EXIT("could not load required authentication information"); + // for a cluster coordinator, the users are loaded at a later stage; + // the kickstarter will trigger a bootstrap process + if (ServerState::instance()->getRole() != ServerState::ROLE_COORDINATOR) { + + // if the authentication info could not be loaded, but authentication is turned on, + // then we refuse to start + if (! vocbase->_authInfoLoaded && ! _disableAuthentication) { + LOG_FATAL_AND_EXIT("could not load required authentication information"); + } } if (_disableAuthentication) { diff --git a/arangod/RestServer/VocbaseContext.h b/arangod/RestServer/VocbaseContext.h index 956f6bba6f..c1f5796659 100644 --- a/arangod/RestServer/VocbaseContext.h +++ b/arangod/RestServer/VocbaseContext.h @@ -36,20 +36,20 @@ #include "Rest/HttpResponse.h" #include "Rest/RequestContext.h" -struct TRI_server_s; -struct TRI_vocbase_s; - // ----------------------------------------------------------------------------- // --SECTION-- forward declarations // ----------------------------------------------------------------------------- -namespace triagens { - namespace arango { +struct TRI_server_s; +struct TRI_vocbase_s; // ----------------------------------------------------------------------------- // --SECTION-- class VocbaseContext // ----------------------------------------------------------------------------- +namespace triagens { + namespace arango { + //////////////////////////////////////////////////////////////////////////////// /// @brief ArangoDB VocbaseContext //////////////////////////////////////////////////////////////////////////////// @@ -76,6 +76,7 @@ namespace triagens { ~VocbaseContext (); +// ----------------------------------------------------------------------------- // --SECTION-- public methods // ----------------------------------------------------------------------------- diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index be67af548d..224d21a29d 100644 --- a/arangod/V8Server/ApplicationV8.cpp +++ b/arangod/V8Server/ApplicationV8.cpp @@ -73,19 +73,30 @@ using namespace std; /// @brief reload the routing cache //////////////////////////////////////////////////////////////////////////////// -std::string const GlobalContextMethods::CodeReloadRouting = "require(\"org/arangodb/actions\").reloadRouting()"; +std::string const GlobalContextMethods::CodeReloadRouting + = "require(\"org/arangodb/actions\").reloadRouting()"; //////////////////////////////////////////////////////////////////////////////// /// @brief flush the modules cache //////////////////////////////////////////////////////////////////////////////// -std::string const GlobalContextMethods::CodeFlushModuleCache = "require(\"internal\").flushModuleCache()"; +std::string const GlobalContextMethods::CodeFlushModuleCache + = "require(\"internal\").flushModuleCache()"; //////////////////////////////////////////////////////////////////////////////// /// @brief reload AQL functions //////////////////////////////////////////////////////////////////////////////// -std::string const GlobalContextMethods::CodeReloadAql = "try { require(\"org/arangodb/ahuacatl\").reload(); } catch (err) { }"; +std::string const GlobalContextMethods::CodeReloadAql + = "try { require(\"org/arangodb/ahuacatl\").reload(); } catch (err) { }"; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief bootstrap coordinator +//////////////////////////////////////////////////////////////////////////////// + +std::string const GlobalContextMethods::CodeBootstrapCoordinator + = "require('internal').loadStartup('server/bootstrap/autoload.js').startup();" + "require('internal').loadStartup('server/bootstrap/routing.js').startup();"; //////////////////////////////////////////////////////////////////////////////// /// @brief we'll store deprecated config option values in here @@ -194,6 +205,7 @@ void ApplicationV8::V8Context::handleGlobalContextMethods () { // lock while we execute them // this avoids potential deadlocks when one of the executed functions itself // registers a context method + MUTEX_LOCKER(_globalMethodsLock); copy = _globalMethods; _globalMethods.clear(); @@ -205,10 +217,16 @@ void ApplicationV8::V8Context::handleGlobalContextMethods () { LOG_DEBUG("executing global context methods '%s' for context %d", func.c_str(), (int) _id); + TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + bool allowUseDatabase = v8g->_allowUseDatabase; + v8g->_allowUseDatabase = true; + TRI_ExecuteJavaScriptString(_context, v8::String::New(func.c_str(), (int) func.size()), v8::String::New("global context method"), false); + + v8g->_allowUseDatabase = allowUseDatabase; } } @@ -245,8 +263,6 @@ ApplicationV8::ApplicationV8 (TRI_server_t* server, : ApplicationFeature("V8"), _server(server), _startupPath(), - _modulesPath(), - _actionPath(), _appPath(), _devAppPath(), _useActions(true), @@ -256,7 +272,6 @@ ApplicationV8::ApplicationV8 (TRI_server_t* server, _gcFrequency(10.0), _v8Options(""), _startupLoader(), - _actionLoader(), _vocbase(0), _nrInstances(0), _contexts(0), @@ -417,7 +432,6 @@ void ApplicationV8::exitContext (V8Context* context) { delete context->_locker; - // default is false bool performGarbageCollection = false; @@ -635,11 +649,11 @@ void ApplicationV8::enableDevelopmentMode () { } //////////////////////////////////////////////////////////////////////////////// -/// @brief runs the version check +/// @brief upgrades the database //////////////////////////////////////////////////////////////////////////////// -void ApplicationV8::runVersionCheck (bool skip, bool perform) { - LOG_TRACE("starting version check"); +void ApplicationV8::upgradeDatabase (bool skip, bool perform) { + LOG_TRACE("starting database init/upgrade"); // enter context and isolate V8Context* context = _contexts[0]; @@ -650,7 +664,7 @@ void ApplicationV8::runVersionCheck (bool skip, bool perform) { // run upgrade script if (! skip) { - LOG_DEBUG("running database version check"); + LOG_DEBUG("running database init/upgrade"); // can do this without a lock as this is the startup for (size_t j = 0; j < _server->_databases._nrAlloc; ++j) { @@ -666,18 +680,18 @@ void ApplicationV8::runVersionCheck (bool skip, bool perform) { context->_context->Global()->Set(v8::String::New("UPGRADE_ARGS"), args); - bool ok = TRI_V8RunVersionCheck(vocbase, &_startupLoader, context->_context); + bool ok = TRI_UpgradeDatabase(vocbase, &_startupLoader, context->_context); if (! ok) { if (context->_context->Global()->Has(v8::String::New("UPGRADE_STARTED"))) { if (perform) { LOG_FATAL_AND_EXIT( - "Database upgrade failed for '%s'. Please inspect the logs from the upgrade procedure", + "Database '%s' upgrade failed. Please inspect the logs from the upgrade procedure", vocbase->_name); } else { LOG_FATAL_AND_EXIT( - "Database version check failed for '%s'. Please start the server with the --upgrade option", + "Database '%s' needs upgrade. Please start the server with the --upgrade option", vocbase->_name); } } @@ -686,7 +700,7 @@ void ApplicationV8::runVersionCheck (bool skip, bool perform) { } } - LOG_DEBUG("database version check passed for '%s'", vocbase->_name); + LOG_DEBUG("database '%s' init/upgrade done", vocbase->_name); } } } @@ -729,15 +743,15 @@ void ApplicationV8::runVersionCheck (bool skip, bool perform) { context->_isolate->Exit(); delete context->_locker; - LOG_TRACE("finished version check"); + LOG_TRACE("finished database init/upgrade"); } //////////////////////////////////////////////////////////////////////////////// -/// @brief runs the upgrade check +/// @brief runs the version check //////////////////////////////////////////////////////////////////////////////// -void ApplicationV8::runUpgradeCheck () { - LOG_TRACE("starting upgrade check"); +void ApplicationV8::versionCheck () { + LOG_TRACE("starting version check"); // enter context and isolate V8Context* context = _contexts[0]; @@ -747,7 +761,7 @@ void ApplicationV8::runUpgradeCheck () { context->_context->Enter(); // run upgrade script - LOG_DEBUG("running database upgrade check"); + LOG_DEBUG("running database version check"); // can do this without a lock as this is the startup int result = 1; @@ -760,11 +774,11 @@ void ApplicationV8::runUpgradeCheck () { // but for all databases v8::HandleScope scope; - int status = TRI_V8RunUpgradeCheck(vocbase, &_startupLoader, context->_context); + int status = TRI_CheckDatabaseVersion(vocbase, &_startupLoader, context->_context); if (status < 0) { LOG_FATAL_AND_EXIT( - "Database upgrade check failed for '%s'. Please inspect the logs from any errors", + "Database version check failed for '%s'. Please inspect the logs from any errors", vocbase->_name); } else if (status == 3) { @@ -811,23 +825,15 @@ void ApplicationV8::runUpgradeCheck () { } //////////////////////////////////////////////////////////////////////////////// -/// @brief prepares the actions +/// @brief prepares the server //////////////////////////////////////////////////////////////////////////////// -void ApplicationV8::prepareActions () { +void ApplicationV8::prepareServer () { for (size_t i = 0; i < _nrInstances; ++i) { - prepareV8Actions(i); + prepareV8Server(i); } } -//////////////////////////////////////////////////////////////////////////////// -/// @brief sets an alternate init file -//////////////////////////////////////////////////////////////////////////////// - -void ApplicationV8::setStartupFile (const string& file) { - _startupFile = file; -} - // ----------------------------------------------------------------------------- // --SECTION-- ApplicationFeature methods // ----------------------------------------------------------------------------- @@ -844,16 +850,16 @@ void ApplicationV8::setupOptions (map ("javascript.dev-app-path", &_devAppPath, "directory for Foxx applications (development mode)") ("javascript.startup-directory", &_startupPath, "path to the directory containing JavaScript startup scripts") ("javascript.v8-options", &_v8Options, "options to pass to v8") + ; + + options[ApplicationServer::OPTIONS_HIDDEN] + ("javascript.frontend-development", &_frontendDevelopmentMode, "allows rebuild frontend assets") // deprecated options ("javascript.action-directory", &DeprecatedPath, "path to the JavaScript action directory (deprecated)") ("javascript.modules-path", &DeprecatedPath, "one or more directories separated by semi-colons (deprecated)") ("javascript.package-path", &DeprecatedPath, "one or more directories separated by semi-colons (deprecated)") ; - - options[ApplicationServer::OPTIONS_HIDDEN] - ("javascript.frontend-development", &_frontendDevelopmentMode, "allows rebuild frontend assets") - ; } //////////////////////////////////////////////////////////////////////////////// @@ -869,23 +875,11 @@ bool ApplicationV8::prepare () { // remove trailing / from path _startupPath = StringUtils::rTrim(_startupPath, TRI_DIR_SEPARATOR_STR); - // derive all other options from --javascript.startup-directory - _actionPath = _startupPath + TRI_DIR_SEPARATOR_STR + "actions"; - - _modulesPath = _startupPath + TRI_DIR_SEPARATOR_STR + "server" + TRI_DIR_SEPARATOR_STR + "modules;" + - _startupPath + TRI_DIR_SEPARATOR_STR + "common" + TRI_DIR_SEPARATOR_STR + "modules;" + - _startupPath + TRI_DIR_SEPARATOR_STR + "node"; - // dump paths { vector paths; paths.push_back(string("startup '" + _startupPath + "'")); - paths.push_back(string("modules '" + _modulesPath + "'")); - - if (_useActions) { - paths.push_back(string("actions '" + _actionPath + "'")); - } if (! _appPath.empty()) { paths.push_back(string("application '" + _appPath + "'")); @@ -911,11 +905,6 @@ bool ApplicationV8::prepare () { _developmentMode = true; } - // set up action loader - if (_useActions) { - _actionLoader.setDirectory(_actionPath); - } - // add v8 options if (_v8Options.size() > 0) { LOG_INFO("using V8 options '%s'", _v8Options.c_str()); @@ -1047,18 +1036,7 @@ void ApplicationV8::stop () { bool ApplicationV8::prepareV8Instance (const size_t i) { vector files; - files.push_back("common/bootstrap/modules.js"); - files.push_back("common/bootstrap/module-internal.js"); - files.push_back("common/bootstrap/module-fs.js"); - files.push_back("common/bootstrap/module-console.js"); // needs internal - files.push_back("common/bootstrap/errors.js"); - files.push_back("common/bootstrap/monkeypatches.js"); - - files.push_back("server/bootstrap/module-internal.js"); - - if (! _startupFile.empty()) { - files.push_back(_startupFile); // needs internal - } + files.push_back("server/initialise.js"); LOG_TRACE("initialising V8 context #%d", (int) i); @@ -1092,9 +1070,13 @@ bool ApplicationV8::prepareV8Instance (const size_t i) { TRI_InitV8Actions(context->_context, _vocbase, _scheduler, _dispatcher, this); } + string modulesPath = _startupPath + TRI_DIR_SEPARATOR_STR + "server" + TRI_DIR_SEPARATOR_STR + "modules;" + + _startupPath + TRI_DIR_SEPARATOR_STR + "common" + TRI_DIR_SEPARATOR_STR + "modules;" + + _startupPath + TRI_DIR_SEPARATOR_STR + "node"; + TRI_InitV8Buffer(context->_context); TRI_InitV8Conversions(context->_context); - TRI_InitV8Utils(context->_context, _startupPath, _modulesPath); + TRI_InitV8Utils(context->_context, _startupPath, modulesPath); TRI_InitV8Shell(context->_context); { @@ -1139,11 +1121,11 @@ bool ApplicationV8::prepareV8Instance (const size_t i) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief prepares the V8 actions +/// @brief prepares the V8 server //////////////////////////////////////////////////////////////////////////////// -void ApplicationV8::prepareV8Actions (const size_t i) { - LOG_TRACE("initialising V8 actions #%d", (int) i); +void ApplicationV8::prepareV8Server (const size_t i) { + LOG_TRACE("initialising V8 server #%d", (int) i); // enter context and isolate V8Context* context = _contexts[i]; @@ -1152,37 +1134,11 @@ void ApplicationV8::prepareV8Actions (const size_t i) { context->_isolate->Enter(); context->_context->Enter(); - // scan for foxx applications - if (i == 0) { - - // once again, we don't need the lock as this is the startup - for (size_t j = 0; j < _server->_databases._nrAlloc; ++j) { - TRI_vocbase_t* vocbase = (TRI_vocbase_t*) _server->_databases._table[j]; - - if (vocbase != 0) { - TRI_V8InitialiseFoxx(vocbase, context->_context); - } - } - } - - // load all actions - if (_useActions) { - v8::HandleScope scope; - - bool ok = _actionLoader.executeAllScripts(context->_context); - - if (! ok) { - LOG_FATAL_AND_EXIT("cannot load JavaScript actions from directory '%s'", _actionLoader.getDirectory().c_str()); - } - - { - v8::HandleScope scope; - TRI_ExecuteJavaScriptString(context->_context, - v8::String::New("require(\"internal\").actionLoaded()"), - v8::String::New("action loaded"), - false); - } + // load server startup file + bool ok = _startupLoader.loadScript(context->_context, _startupFile); + if (! ok) { + LOG_FATAL_AND_EXIT("cannot load JavaScript utilities from file '%s'", _startupFile.c_str()); } // and return from the context @@ -1191,7 +1147,7 @@ void ApplicationV8::prepareV8Actions (const size_t i) { delete context->_locker; // initialise garbage collection for context - LOG_TRACE("initialised V8 actions #%d", (int) i); + LOG_TRACE("initialised V8 server #%d", (int) i); } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/ApplicationV8.h b/arangod/V8Server/ApplicationV8.h index c7321faca9..4aef99ae6b 100644 --- a/arangod/V8Server/ApplicationV8.h +++ b/arangod/V8Server/ApplicationV8.h @@ -75,7 +75,8 @@ namespace triagens { TYPE_UNKNOWN = 0, TYPE_RELOAD_ROUTING, TYPE_FLUSH_MODULE_CACHE, - TYPE_RELOAD_AQL + TYPE_RELOAD_AQL, + TYPE_BOOTSTRAP_COORDINATOR }; //////////////////////////////////////////////////////////////////////////////// @@ -92,6 +93,9 @@ namespace triagens { if (type == "reloadAql") { return TYPE_RELOAD_AQL; } + if (type == "bootstrapCoordinator") { + return TYPE_BOOTSTRAP_COORDINATOR; + } return TYPE_UNKNOWN; } @@ -108,6 +112,8 @@ namespace triagens { return CodeFlushModuleCache; case TYPE_RELOAD_AQL: return CodeReloadAql; + case TYPE_BOOTSTRAP_COORDINATOR: + return CodeBootstrapCoordinator; case TYPE_UNKNOWN: default: return ""; @@ -121,6 +127,7 @@ namespace triagens { static std::string const CodeReloadRouting; static std::string const CodeFlushModuleCache; static std::string const CodeReloadAql; + static std::string const CodeBootstrapCoordinator; }; // ----------------------------------------------------------------------------- @@ -324,31 +331,23 @@ namespace triagens { _definedBooleans[name] = value; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief upgrades the database +//////////////////////////////////////////////////////////////////////////////// + + void upgradeDatabase (bool skip, bool perform); + //////////////////////////////////////////////////////////////////////////////// /// @brief runs the version check //////////////////////////////////////////////////////////////////////////////// - void runVersionCheck (bool skip, bool perform); + void versionCheck (); //////////////////////////////////////////////////////////////////////////////// -/// @brief runs the upgrade check +/// @brief prepares the server //////////////////////////////////////////////////////////////////////////////// - void runUpgradeCheck (); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief prepares the actions -//////////////////////////////////////////////////////////////////////////////// - - void prepareActions (); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief sets an alternate init file -/// -/// Normally "server.js" will be used. Pass empty string to disable. -//////////////////////////////////////////////////////////////////////////////// - - void setStartupFile (const string&); + void prepareServer (); // ----------------------------------------------------------------------------- // --SECTION-- ApplicationFeature methods @@ -411,10 +410,10 @@ namespace triagens { bool prepareV8Instance (size_t); //////////////////////////////////////////////////////////////////////////////// -/// @brief prepares the V8 actions +/// @brief prepares the V8 server //////////////////////////////////////////////////////////////////////////////// - void prepareV8Actions (size_t); + void prepareV8Server (size_t); //////////////////////////////////////////////////////////////////////////////// /// @brief shut downs a V8 instances @@ -443,24 +442,6 @@ namespace triagens { string _startupPath; -//////////////////////////////////////////////////////////////////////////////// -/// @brief semicolon separated list of module directories -/// -/// This variable is automatically set based on the value of -/// `--javascript.startup-directory`. -//////////////////////////////////////////////////////////////////////////////// - - string _modulesPath; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief path to the system action directory -/// -/// This variable is automatically set based on the value of -/// `--javascript.startup-directory`. -//////////////////////////////////////////////////////////////////////////////// - - string _actionPath; - //////////////////////////////////////////////////////////////////////////////// /// @brief semicolon separated list of application directories /// `--javascript.app-path directory` @@ -566,12 +547,6 @@ namespace triagens { JSLoader _startupLoader; -//////////////////////////////////////////////////////////////////////////////// -/// @brief V8 action loader -//////////////////////////////////////////////////////////////////////////////// - - JSLoader _actionLoader; - //////////////////////////////////////////////////////////////////////////////// /// @brief system database //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/v8-actions.cpp b/arangod/V8Server/v8-actions.cpp index 50f51396c6..9ba25c5fcf 100644 --- a/arangod/V8Server/v8-actions.cpp +++ b/arangod/V8Server/v8-actions.cpp @@ -111,8 +111,8 @@ class v8_action_t : public TRI_action_t { /// @brief constructor //////////////////////////////////////////////////////////////////////////////// - v8_action_t (set const& contexts) - : TRI_action_t(contexts), + v8_action_t () + : TRI_action_t(), _callbacks(), _callbacksLock() { _type = "JAVASCRIPT"; @@ -146,14 +146,19 @@ class v8_action_t : public TRI_action_t { TRI_action_result_t result; // determine whether we should force a re-initialistion of the engine in development mode - bool allowEngineReset; - extern bool allowUseDatabaseInRESTActions; + bool allowEngineReset = false; - allowEngineReset = false; + // allow use datase execution in rest calls + extern bool ALLOW_USE_DATABASE_IN_REST_ACTIONS; + bool allowUseDatabaseInRestActions = ALLOW_USE_DATABASE_IN_REST_ACTIONS; - string const& fullUrl = request->fullUrl(); + if (_allowUseDatabase) { + allowUseDatabaseInRestActions = true; + } // only URLs starting with /dev will trigger an engine reset + string const& fullUrl = request->fullUrl(); + if (fullUrl.find("/dev/") == 0) { allowEngineReset = true; } @@ -162,7 +167,7 @@ class v8_action_t : public TRI_action_t { vocbase, request, ! allowEngineReset, - allowUseDatabaseInRESTActions); + allowUseDatabaseInRestActions); // note: the context might be 0 in case of shut-down if (context == 0) { @@ -270,6 +275,14 @@ static void ParseActionOptions (TRI_v8_global_t* v8g, else { action->_isPrefix = false; } + + // check the "allowUseDatabase" field + if (options->Has(v8g->AllowUseDatabaseKey)) { + action->_allowUseDatabase = TRI_ObjectToBoolean(options->Get(v8g->AllowUseDatabaseKey)); + } + else { + action->_allowUseDatabase = false; + } } //////////////////////////////////////////////////////////////////////////////// @@ -762,8 +775,8 @@ static v8::Handle JS_DefineAction (v8::Arguments const& argv) { isolate = v8::Isolate::GetCurrent(); v8g = (TRI_v8_global_t*) isolate->GetData(); - if (argv.Length() != 4) { - TRI_V8_EXCEPTION_USAGE(scope, "defineAction(, , , )"); + if (argv.Length() != 3) { + TRI_V8_EXCEPTION_USAGE(scope, "defineAction(, , )"); } // extract the action name @@ -792,25 +805,8 @@ static v8::Handle JS_DefineAction (v8::Arguments const& argv) { options = v8::Object::New(); } - // extract the contexts - set contexts; - - if (! argv[3]->IsArray()) { - TRI_V8_TYPE_ERROR(scope, " must be a list of contexts"); - } - - v8::Handle array = v8::Handle::Cast(argv[3]); - - uint32_t n = array->Length(); - - for (uint32_t j = 0; j < n; ++j) { - v8::Handle item = array->Get(j); - - contexts.insert(TRI_ObjectToString(item)); - } - // create an action with the given options - v8_action_t* action = new v8_action_t(contexts); + v8_action_t* action = new v8_action_t(); ParseActionOptions(v8g, action, options); // store an action with the given name diff --git a/arangod/V8Server/v8-collection.cpp b/arangod/V8Server/v8-collection.cpp index 07507b8d52..61c25780c0 100644 --- a/arangod/V8Server/v8-collection.cpp +++ b/arangod/V8Server/v8-collection.cpp @@ -3747,11 +3747,10 @@ static v8::Handle JS_CompletionsVocbase (v8::Arguments const& argv) { return scope.Close(result); } - - // ----------------------------------------------------------------------------- // --SECTION-- javascript functions // ----------------------------------------------------------------------------- + //////////////////////////////////////////////////////////////////////////////// /// @brief removes a document /// @startDocuBlock documentsCollectionRemove diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 170e697e18..1cc2ff139f 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -1445,6 +1445,22 @@ static v8::Handle MapGetVocBase (v8::Local const name, return scope.Close(result); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the server version string +/// @startDocuBlock databaseVersion +/// `db._version()` +/// +/// Returns the server version string. Note that this is not the version of the +/// database. +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_VersionServer (v8::Arguments const& argv) { + v8::HandleScope scope; + + return scope.Close(v8::String::New(TRI_VERSION)); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief return the path to database files /// @startDocuBlock databasePath @@ -1798,14 +1814,14 @@ static v8::Handle CreateDatabaseCoordinator (v8::Arguments const& arg // database was created successfully in agency - TRI_v8_global_t* v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); // now wait for heartbeat thread to create the database object TRI_vocbase_t* vocbase = nullptr; int tries = 0; while (++tries <= 6000) { - vocbase = TRI_UseByIdCoordinatorDatabaseServer((TRI_server_t*) v8g->_server, id); + vocbase = TRI_UseByIdCoordinatorDatabaseServer(static_cast(v8g->_server), id); if (vocbase != nullptr) { break; @@ -1830,10 +1846,22 @@ static v8::Handle CreateDatabaseCoordinator (v8::Arguments const& arg v8::Context::GetCurrent()->Global()->Set(v8::String::New("UPGRADE_ARGS"), v8::Object::New()); } - if (TRI_V8RunVersionCheck(vocbase, static_cast(v8g->_loader), v8::Context::GetCurrent())) { - // version check ok - TRI_V8InitialiseFoxx(vocbase, v8::Context::GetCurrent()); - } + // switch databases + TRI_vocbase_t* orig = v8g->_vocbase; + TRI_ASSERT(orig != nullptr); + + v8g->_vocbase = vocbase; + + // initalise database + bool allowUseDatabase = v8g->_allowUseDatabase; + v8g->_allowUseDatabase = true; + + v8g->_loader->executeGlobalScript(v8::Context::GetCurrent(), "server/bootstrap/coordinator-database.js"); + + v8g->_allowUseDatabase = allowUseDatabase; + + // and switch back + v8g->_vocbase = orig; TRI_ReleaseVocBase(vocbase); @@ -1969,10 +1997,17 @@ static v8::Handle JS_CreateDatabase (v8::Arguments const& argv) { v8::Context::GetCurrent()->Global()->Set(v8::String::New("UPGRADE_ARGS"), v8::Object::New()); } - if (TRI_V8RunVersionCheck(database, static_cast(v8g->_loader), v8::Context::GetCurrent())) { - // version check ok - TRI_V8InitialiseFoxx(database, v8::Context::GetCurrent()); - } + // switch databases + TRI_vocbase_t* orig = v8g->_vocbase; + TRI_ASSERT(orig != nullptr); + + v8g->_vocbase = database; + + // initalise database + v8g->_loader->executeGlobalScript(v8::Context::GetCurrent(), "server/bootstrap/local-database.js"); + + // and switch back + v8g->_vocbase = orig; // populate the authentication cache. otherwise no one can access the new database TRI_ReloadAuthInfo(database); @@ -2317,17 +2352,17 @@ int32_t TRI_GetVocBaseColType () { /// @brief run version check //////////////////////////////////////////////////////////////////////////////// -bool TRI_V8RunVersionCheck (void* vocbase, - JSLoader* startupLoader, - v8::Handle context) { +bool TRI_UpgradeDatabase (TRI_vocbase_t* vocbase, + JSLoader* startupLoader, + v8::Handle context) { TRI_ASSERT(startupLoader != nullptr); v8::HandleScope scope; TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); - void* orig = v8g->_vocbase; + TRI_vocbase_t* orig = v8g->_vocbase; v8g->_vocbase = vocbase; - v8::Handle result = startupLoader->executeGlobalScript(context, "server/version-check.js"); + v8::Handle result = startupLoader->executeGlobalScript(context, "server/upgrade-database.js"); bool ok = TRI_ObjectToBoolean(result); if (! ok) { @@ -2343,17 +2378,17 @@ bool TRI_V8RunVersionCheck (void* vocbase, /// @brief run upgrade check //////////////////////////////////////////////////////////////////////////////// -int TRI_V8RunUpgradeCheck (void* vocbase, - JSLoader* startupLoader, - v8::Handle context) { +int TRI_CheckDatabaseVersion (TRI_vocbase_t* vocbase, + JSLoader* startupLoader, + v8::Handle context) { TRI_ASSERT(startupLoader != nullptr); v8::HandleScope scope; TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); - void* orig = v8g->_vocbase; + TRI_vocbase_t* orig = v8g->_vocbase; v8g->_vocbase = vocbase; - v8::Handle result = startupLoader->executeGlobalScript(context, "server/upgrade-check.js"); + v8::Handle result = startupLoader->executeGlobalScript(context, "server/check-version.js"); int code = (int) TRI_ObjectToInt64(result); v8g->_vocbase = orig; @@ -2361,33 +2396,6 @@ int TRI_V8RunUpgradeCheck (void* vocbase, return code; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief initialize foxx -//////////////////////////////////////////////////////////////////////////////// - -void TRI_V8InitialiseFoxx (void* vocbase, - v8::Handle context) { - void* orig = nullptr; - - { - v8::HandleScope scope; - TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); - orig = v8g->_vocbase; - v8g->_vocbase = vocbase; - } - - v8::HandleScope scope; - TRI_ExecuteJavaScriptString(context, - v8::String::New("require(\"internal\").initializeFoxx()"), - v8::String::New("initialize foxx"), - false); - { - v8::HandleScope scope; - TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); - v8g->_vocbase = orig; - } -} - //////////////////////////////////////////////////////////////////////////////// /// @brief reloads routing //////////////////////////////////////////////////////////////////////////////// @@ -2446,6 +2454,7 @@ void TRI_InitV8VocBridge (v8::Handle context, // for any database function added here, be sure to add it to in function // JS_CompletionsVocbase, too for the auto-completion + TRI_AddMethodVocbase(ArangoNS, "_version", JS_VersionServer); TRI_AddMethodVocbase(ArangoNS, "_id", JS_IdDatabase); TRI_AddMethodVocbase(ArangoNS, "_isSystem", JS_IsSystemDatabase); TRI_AddMethodVocbase(ArangoNS, "_name", JS_NameDatabase); diff --git a/arangod/V8Server/v8-vocbase.h b/arangod/V8Server/v8-vocbase.h index 3731e20fbd..678bc6651d 100644 --- a/arangod/V8Server/v8-vocbase.h +++ b/arangod/V8Server/v8-vocbase.h @@ -68,24 +68,17 @@ int32_t TRI_GetVocBaseColType (); /// @brief run version check //////////////////////////////////////////////////////////////////////////////// -bool TRI_V8RunVersionCheck (void*, - triagens::arango::JSLoader*, - v8::Handle); +bool TRI_UpgradeDatabase (TRI_vocbase_t*, + triagens::arango::JSLoader*, + v8::Handle); //////////////////////////////////////////////////////////////////////////////// /// @brief run upgrade check //////////////////////////////////////////////////////////////////////////////// -int TRI_V8RunUpgradeCheck (void* vocbase, - triagens::arango::JSLoader* startupLoader, - v8::Handle context); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief initialize foxx -//////////////////////////////////////////////////////////////////////////////// - -void TRI_V8InitialiseFoxx (void*, - v8::Handle); +int TRI_CheckDatabaseVersion (TRI_vocbase_t* vocbase, + triagens::arango::JSLoader* startupLoader, + v8::Handle context); //////////////////////////////////////////////////////////////////////////////// /// @brief reloads routing diff --git a/arangod/VocBase/FILES.md b/arangod/VocBase/FILES.md index dc1699fd7a..542540cdde 100644 --- a/arangod/VocBase/FILES.md +++ b/arangod/VocBase/FILES.md @@ -32,12 +32,12 @@ The file will contain a JSON array with a "version" attribute. The version attri will contain the version number that ArangoDB was last started with. It will also contain a "tasks" attribute with an array of all the tasks that are -or were already executed by the upgrade procedure in js/server/version-check.js. +or were already executed by the upgrade procedure in js/server/upgrade-database.js. Every successful upgrade task will be inserted into the "tasks" array with a value of true. Every failed upgrade task will be inserted into the "tasks" array with a value of false. Failed upgrade tasks will get re-executed on server startup if the task is still -present in the js/server/version-check.js file. +present in the js/server/upgrade-database.js file. The VERSION file will be created on the first start of ArangoDB if the database directory is still empty. diff --git a/arangod/VocBase/document-collection.h b/arangod/VocBase/document-collection.h index 4e75aeaa57..60dad4281f 100644 --- a/arangod/VocBase/document-collection.h +++ b/arangod/VocBase/document-collection.h @@ -508,6 +508,95 @@ size_t TRI_DocumentIteratorDocumentCollection (triagens::arango::TransactionBase #define TRI_UNLOCK_JOURNAL_ENTRIES_DOC_COLLECTION(a) \ TRI_UnlockCondition(&(a)->_journalsCondition) +//////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not the marker is an edge marker +//////////////////////////////////////////////////////////////////////////////// + +static inline bool TRI_IS_EDGE_MARKER (TRI_df_marker_t const* marker) { + return (marker->_type == TRI_DOC_MARKER_KEY_EDGE || + marker->_type == TRI_WAL_MARKER_EDGE); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts the pointer to the _from key from a marker +//////////////////////////////////////////////////////////////////////////////// + +static inline char const* TRI_EXTRACT_MARKER_FROM_KEY (TRI_df_marker_t const* marker) { + if (marker->_type == TRI_DOC_MARKER_KEY_EDGE) { + return ((char const*) marker) + ((TRI_doc_edge_key_marker_t const*) marker)->_offsetFromKey; + } + else if (marker->_type == TRI_WAL_MARKER_EDGE) { + return ((char const*) marker) + ((triagens::wal::edge_marker_t const*) marker)->_offsetFromKey; + } + +#ifdef TRI_ENABLE_MAINTAINER_MODE + // invalid marker type + TRI_ASSERT(false); +#endif + + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts the pointer to the _to key from a marker +//////////////////////////////////////////////////////////////////////////////// + +static inline char const* TRI_EXTRACT_MARKER_TO_KEY (TRI_df_marker_t const* marker) { + if (marker->_type == TRI_DOC_MARKER_KEY_EDGE) { + return ((char const*) marker) + ((TRI_doc_edge_key_marker_t const*) marker)->_offsetToKey; + } + else if (marker->_type == TRI_WAL_MARKER_EDGE) { + return ((char const*) marker) + ((triagens::wal::edge_marker_t const*) marker)->_offsetToKey; + } + +#ifdef TRI_ENABLE_MAINTAINER_MODE + // invalid marker type + TRI_ASSERT(false); +#endif + + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts the _from cid from a marker +//////////////////////////////////////////////////////////////////////////////// + +static inline TRI_voc_cid_t TRI_EXTRACT_MARKER_FROM_CID (TRI_df_marker_t const* marker) { + if (marker->_type == TRI_DOC_MARKER_KEY_EDGE) { + return ((TRI_doc_edge_key_marker_t const*) marker)->_fromCid; + } + else if (marker->_type == TRI_WAL_MARKER_EDGE) { + return ((triagens::wal::edge_marker_t const*) marker)->_fromCid; + } + +#ifdef TRI_ENABLE_MAINTAINER_MODE + // invalid marker type + TRI_ASSERT(false); +#endif + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts the _to cid from a marker +//////////////////////////////////////////////////////////////////////////////// + +static inline TRI_voc_cid_t TRI_EXTRACT_MARKER_TO_CID (TRI_df_marker_t const* marker) { + if (marker->_type == TRI_DOC_MARKER_KEY_EDGE) { + return ((TRI_doc_edge_key_marker_t const*) marker)->_toCid; + } + else if (marker->_type == TRI_WAL_MARKER_EDGE) { + return ((triagens::wal::edge_marker_t const*) marker)->_toCid; + } + +#ifdef TRI_ENABLE_MAINTAINER_MODE + // invalid marker type + TRI_ASSERT(false); +#endif + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief extracts the revision id from a marker //////////////////////////////////////////////////////////////////////////////// diff --git a/js/Makefile.files b/js/Makefile.files index db04a25e58..e8aa030420 100644 --- a/js/Makefile.files +++ b/js/Makefile.files @@ -71,7 +71,7 @@ JAVASCRIPT_JSLINT = \ \ @srcdir@/js/client/client.js \ @srcdir@/js/server/server.js \ - @srcdir@/js/server/version-check.js \ + @srcdir@/js/server/upgrade-database.js \ \ @srcdir@/js/apps/system/aardvark/frontend/js/shell/browser.js diff --git a/js/actions/api-aqlfunction.js b/js/actions/api-aqlfunction.js index 2b4691b141..ecd2047ada 100644 --- a/js/actions/api-aqlfunction.js +++ b/js/actions/api-aqlfunction.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -36,11 +37,6 @@ var aqlfunctions = require("org/arangodb/aql/functions"); // --SECTION-- private functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_get_api_aqlfunction /// @brief gets all reqistered AQL user functions @@ -113,9 +109,9 @@ function get_api_aqlfunction (req, res) { /// - *name*: the fully qualified name of the user functions. /// /// - *code*: a string representation of the function body. -/// +/// /// - *isDeterministic*: an optional boolean value to indicate that the function -/// results are fully deterministic (function return value solely depends on +/// results are fully deterministic (function return value solely depends on /// the input value and return value is the same for repeated calls with same /// input). The *isDeterministic* attribute is currently not used but may be /// used later for optimisations. @@ -145,7 +141,7 @@ function get_api_aqlfunction (req, res) { /// call, the server will respond with *HTTP 200*. /// /// @RESTRETURNCODE{201} -/// If the function can be registered by the server, the server will respond with +/// If the function can be registered by the server, the server will respond with /// *HTTP 201*. /// /// @RESTRETURNCODE{400} @@ -156,7 +152,7 @@ function get_api_aqlfunction (req, res) { /// /// @EXAMPLE_ARANGOSH_RUN{RestAqlfunctionCreate} /// var url = "/_api/aqlfunction"; -/// var body = '{ ' + +/// var body = '{ ' + /// '"name" : "myfunctions::temperature::celsiustofahrenheit", ' + /// '"code" : "function (celsius) { return celsius * 1.8 + 32; }" ' + /// '}'; @@ -167,7 +163,7 @@ function get_api_aqlfunction (req, res) { /// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN -/// @endDocuBlock +/// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// function post_api_aqlfunction (req, res) { @@ -198,12 +194,12 @@ function post_api_aqlfunction (req, res) { /// @RESTQUERYPARAM{group,string,optional} /// If set to *true*, then the function name provided in *name* is treated as /// a namespace prefix, and all functions in the specified namespace will be deleted. -/// If set to *false*, the function name provided in *name* must be fully +/// If set to *false*, the function name provided in *name* must be fully /// qualified, including any namespaces. /// /// @RESTDESCRIPTION /// -/// Removes an existing AQL user function, identified by *name*. +/// Removes an existing AQL user function, identified by *name*. /// /// In case of success, the returned JSON object has the following properties: /// @@ -226,7 +222,7 @@ function post_api_aqlfunction (req, res) { /// @RESTRETURNCODES /// /// @RESTRETURNCODE{200} -/// If the function can be removed by the server, the server will respond with +/// If the function can be removed by the server, the server will respond with /// *HTTP 200*. /// /// @RESTRETURNCODE{400} @@ -297,26 +293,25 @@ function delete_api_aqlfunction (req, res) { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief gateway +/// @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); + case actions.GET: + get_api_aqlfunction(req, res); break; - case actions.POST: - post_api_aqlfunction(req, res); + case actions.POST: + post_api_aqlfunction(req, res); break; - case actions.DELETE: - delete_api_aqlfunction(req, res); + case actions.DELETE: + delete_api_aqlfunction(req, res); break; default: @@ -329,11 +324,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-cluster.js b/js/actions/api-cluster.js index 07494b9628..80cb2826da 100644 --- a/js/actions/api-cluster.js +++ b/js/actions/api-cluster.js @@ -1,5 +1,6 @@ /*jslint indent: 2, nomen: true, maxlen: 140, sloppy: true, vars: true, white: true, plusplus: true, evil: true */ -/*global require, exports, module, SYS_CLUSTER_TEST, ArangoServerState, ArangoClusterComm, ArangoClusterInfo */ +/*global require, exports, module, SYS_CLUSTER_TEST, ArangoServerState, ArangoClusterComm, ArangoClusterInfo, + UPGRADE_ARGS: true */ //////////////////////////////////////////////////////////////////////////////// /// @brief cluster actions @@ -8,7 +9,7 @@ /// /// DISCLAIMER /// -/// Copyright 2014-2014 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,20 +23,19 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Max Neunhoeffer -/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany +/// @author Copyright 2013-2014, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// var actions = require("org/arangodb/actions"); var cluster = require("org/arangodb/cluster"); var internal = require("internal"); var console = require("console"); - -// ----------------------------------------------------------------------------- -// --SECTION-- private functions -// ----------------------------------------------------------------------------- +var fs = require("fs"); // ----------------------------------------------------------------------------- // --SECTION-- public functions @@ -167,9 +167,8 @@ var console = require("console"); //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_admin/cluster-test", - context : "admin", - prefix : true, + url: "_admin/cluster-test", + prefix: true, callback : function (req, res) { var path; @@ -298,9 +297,9 @@ function parseAuthorization (authorization) { //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_admin/clusterPlanner", - context : "admin", - prefix : "false", + url: "_admin/clusterPlanner", + prefix: false, + callback : function (req, res) { if (ArangoServerState.disableDispatcherKickstarter() === true) { actions.resultError(req, res, actions.HTTP_FORBIDDEN); @@ -389,9 +388,9 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_admin/clusterDispatch", - context : "admin", - prefix : "false", + url: "_admin/clusterDispatch", + prefix: false, + callback : function (req, res) { if (ArangoServerState.disableDispatcherKickstarter() === true) { actions.resultError(req, res, actions.HTTP_FORBIDDEN); @@ -540,9 +539,9 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_admin/clusterCheckPort", - context : "admin", - prefix : "false", + url: "_admin/clusterCheckPort", + prefix: false, + callback : function (req, res) { if (ArangoServerState.disableDispatcherKickstarter() === true) { actions.resultError(req, res, actions.HTTP_FORBIDDEN); @@ -604,9 +603,9 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_admin/clusterStatistics", - context : "admin", - prefix : "false", + url: "_admin/clusterStatistics", + prefix: false, + callback : function (req, res) { if (req.requestType !== actions.GET) { actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0, @@ -654,10 +653,14 @@ actions.defineHttp({ } }); +//////////////////////////////////////////////////////////////////////////////// +/// @brief allows to query the historic statistics of a DBserver in the cluster +//////////////////////////////////////////////////////////////////////////////// + actions.defineHttp({ - url : "_admin/history", - context : "admin", - prefix : "false", + url: "_admin/history", + prefix: false, + callback : function (req, res) { if (req.requestType !== actions.POST) { actions.resultError(req, res, actions.HTTP_FORBIDDEN, 0, @@ -714,7 +717,7 @@ actions.defineHttp({ } res.responseCode = actions.HTTP_OK; res.body = JSON.stringify({result : cursor.docs}); - } + } else { // query a remote statistics collection var coord = { coordTransactionID: ArangoClusterInfo.uniqid() }; @@ -749,14 +752,132 @@ actions.defineHttp({ }); //////////////////////////////////////////////////////////////////////////////// -/// @} +/// @brief bootstraps the all db servers //////////////////////////////////////////////////////////////////////////////// +actions.defineHttp({ + url: "_admin/cluster/bootstrapDbServers", + prefix: false, + + callback: function (req, res) { + var body = actions.getJsonBody(req, res); + + try { + var result = cluster.bootstrapDbServers(body.isRelaunch); + + if (result) { + actions.resultOk(req, res, actions.HTTP_OK); + } + else { + actions.resultBad(req, res); + } + } + catch(err) { + actions.resultException(req, res, err); + } + } +}); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief bootstraps one db server +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url: "_admin/cluster/bootstrapDbServer", + prefix: false, + + callback: function (req, res) { + var body = actions.getJsonBody(req, res); + + UPGRADE_ARGS = { + isCluster: true, + isDbServer: true, + isRelaunch: body.isRelaunch + }; + + try { + var func = internal.loadStartup("server/bootstrap/db-server.js"); + var result = func && func(); + + if (result) { + actions.resultOk(req, res, actions.HTTP_OK); + } + else { + actions.resultBad(req, res); + } + } + catch(err) { + actions.resultException(req, res, err); + } + } +}); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief upgrade cluster database +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url: "_admin/cluster/upgradeClusterDatabase", + prefix: false, + + callback: function (req, res) { + var body = actions.getJsonBody(req, res); + + UPGRADE_ARGS = { + isCluster: true, + isCoordinator: true, + isRelaunch: body.isRelaunch || false, + upgrade: body.upgrade || false + }; + + try { + var result = internal.loadStartup("server/upgrade-database.js"); + + if (result) { + actions.resultOk(req, res, actions.HTTP_OK); + } + else { + actions.resultBad(req, res); + } + } + catch(err) { + actions.resultException(req, res, err); + } + } +}); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief bootstraps the coordinator +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url: "_admin/cluster/bootstrapCoordinator", + allowUseDatabase: true, + prefix: false, + + callback: function (req, res) { + try { + var func = internal.loadStartup("server/bootstrap/coordinator.js"); + var result = func && func(); + + if (result) { + actions.resultOk(req, res, actions.HTTP_OK); + } + else { + actions.resultBad(req, res); + } + } + catch(err) { + actions.resultException(req, res, err); + } + } +}); + // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-collection.js b/js/actions/api-collection.js index b69781da9a..d61ac4a2e4 100644 --- a/js/actions/api-collection.js +++ b/js/actions/api-collection.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Achim Brandt +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -38,11 +39,6 @@ var API = "_api/collection"; // --SECTION-- private functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief return a prefixed URL //////////////////////////////////////////////////////////////////////////////// @@ -73,7 +69,7 @@ function collectionRepresentation (collection, showProperties, showCount, showFi result.doCompact = properties.doCompact; result.isVolatile = properties.isVolatile; - result.journalSize = properties.journalSize; + result.journalSize = properties.journalSize; result.keyOptions = properties.keyOptions; result.waitForSync = properties.waitForSync; @@ -101,19 +97,10 @@ function collectionRepresentation (collection, showProperties, showCount, showFi return result; } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief helper to parse arguments for creating collections //////////////////////////////////////////////////////////////////////////////// @@ -143,15 +130,15 @@ function parseBodyForCreateCollection (req, res) { if (body.hasOwnProperty("isSystem")) { r.parameter.isSystem = body.isSystem; } - + if (body.hasOwnProperty("isVolatile")) { r.parameter.isVolatile = body.isVolatile; } - + if (body.hasOwnProperty("journalSize")) { r.parameter.journalSize = body.journalSize; } - + if (body.hasOwnProperty("keyOptions")) { r.parameter.keyOptions = body.keyOptions; } @@ -159,15 +146,15 @@ function parseBodyForCreateCollection (req, res) { if (body.hasOwnProperty("type")) { r.type = body.type; } - + if (body.hasOwnProperty("waitForSync")) { r.parameter.waitForSync = body.waitForSync; } - + if (body.hasOwnProperty("shardKeys") && cluster.isCoordinator()) { r.parameter.shardKeys = body.shardKeys || { }; } - + if (body.hasOwnProperty("numberOfShards") && cluster.isCoordinator()) { r.parameter.numberOfShards = body.numberOfShards || 0; } @@ -178,7 +165,7 @@ function parseBodyForCreateCollection (req, res) { return r; } - + //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_post_api_collection /// @brief creates a collection @@ -203,7 +190,7 @@ function parseBodyForCreateCollection (req, res) { /// /// - *journalSize* (optional, default is a /// configuration parameter): The maximal size of -/// a journal or datafile. +/// a journal or datafile. /// **Note**: This also limits the maximal /// size of a single object. Must be at least 1MB. /// @@ -217,21 +204,21 @@ function parseBodyForCreateCollection (req, res) { /// collection data is kept in-memory only and not made persistent. Unloading /// the collection will cause the collection data to be discarded. Stopping /// or re-starting the server will also cause full loss of data in the -/// collection. Setting this option will make the resulting collection be +/// collection. Setting this option will make the resulting collection be /// slightly faster than regular collections because ArangoDB does not -/// enforce any synchronisation to disk and does not calculate any CRC +/// enforce any synchronisation to disk and does not calculate any CRC /// checksums for datafiles (as there are no datafiles). /// -/// This option should threrefore be used for cache-type collections only, +/// This option should threrefore be used for cache-type collections only, /// and not for data that cannot be re-created otherwise. /// /// - *keyOptions* (optional) additional options for key generation. If /// specified, then *keyOptions* should be a JSON array containing the /// following attributes (note: some of them are optional): -/// - *type*: specifies the type of the key generator. The currently +/// - *type*: specifies the type of the key generator. The currently /// available generators are *traditional* and *autoincrement*. /// - *allowUserKeys*: if set to *true*, then it is allowed to supply -/// own key values in the *_key* attribute of a document. If set to +/// own key values in the *_key* attribute of a document. If set to /// *false*, then the key generator will solely be responsible for /// generating keys and supplying own key values in the *_key* attribute /// of documents is considered an error. @@ -253,16 +240,16 @@ function parseBodyForCreateCollection (req, res) { /// attribute determines which document attributes are used to determine the /// target shard for documents. Documents are sent to shards based on the /// values of their shard key attributes. The values of all shard -/// key attributes in a document are hashed, and the hash value is used to -/// determine the target shard. +/// key attributes in a document are hashed, and the hash value is used to +/// determine the target shard. /// **Note**: Values of shard key attributes cannot be changed once set. /// This option is meaningless in a single server setup. /// @EXAMPLES /// /// @EXAMPLE_ARANGOSH_RUN{RestCollectionCreateCollection} /// var url = "/_api/collection"; -/// var body = { -/// name: "testCollectionBasics" +/// var body = { +/// name: "testCollectionBasics" /// }; /// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); @@ -270,9 +257,9 @@ function parseBodyForCreateCollection (req, res) { /// assert(response.code === 200); /// /// logJsonResponse(response); -/// body = { -/// name: "testCollectionEdges", -/// type : 3 +/// body = { +/// name: "testCollectionEdges", +/// type : 3 /// }; /// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); @@ -287,12 +274,12 @@ function parseBodyForCreateCollection (req, res) { /// /// @EXAMPLE_ARANGOSH_RUN{RestCollectionCreateKeyopt} /// var url = "/_api/collection"; -/// var body = { -/// name: "testCollectionUsers", -/// keyOptions : { -/// type : "autoincrement", -/// increment : 5, -/// allowUserKeys : true +/// var body = { +/// name: "testCollectionUsers", +/// keyOptions : { +/// type : "autoincrement", +/// increment : 5, +/// allowUserKeys : true /// } /// }; /// @@ -313,7 +300,7 @@ function post_api_collection (req, res) { if (r.bodyIsEmpty) { return; // error in JSON, is already reported } - + if (r.name === "") { actions.resultBad(req, res, arangodb.ERROR_ARANGO_ILLEGAL_NAME, "name must be non-empty"); @@ -350,7 +337,7 @@ function post_api_collection (req, res) { result.numberOfShards = collection.numberOfShards; result.distributeShardsLike = collection.distributeShardsLike || ""; } - + var headers = { location: databasePrefix(req, "/" + API + "/" + result.name) }; @@ -374,7 +361,7 @@ function post_api_collection (req, res) { /// Whether or not system collections should be excluded from the result. /// /// @RESTDESCRIPTION -/// Returns an object with an attribute *collections* containing a +/// Returns an object with an attribute *collections* containing a /// list of all collection descriptions. The same information is also /// available in the *names* as hash map with the collection names /// as keys. @@ -439,7 +426,7 @@ function get_api_collections (req, res) { /// /// @RESTURLPARAM{collection-name,string,required} /// The name of the collection. -/// +/// /// @RESTDESCRIPTION /// The result is an object describing the collection with the following /// attributes: @@ -467,9 +454,9 @@ function get_api_collections (req, res) { /// If the *collection-name* is unknown, then a *HTTP 404* is /// returned. /// @endDocuBlock -///////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_get_api_collection_properties /// /// @RESTHEADER{GET /_api/collection/{collection-name}/properties, Read properties of a collection} @@ -498,8 +485,8 @@ function get_api_collections (req, res) { /// In a cluster setup, the result will also contain the following attributes: /// - *numberOfShards*: the number of shards of the collection. /// -/// - *shardKeys*: contains the names of document attributes that are used to -/// determine the target shard for documents. +/// - *shardKeys*: contains the names of document attributes that are used to +/// determine the target shard for documents. /// @RESTRETURNCODES /// /// @RESTRETURNCODE{400} @@ -543,9 +530,9 @@ function get_api_collections (req, res) { /// db._drop(cn); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock -//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_get_api_collection_count /// /// @RESTHEADER{GET /_api/collection/{collection-name}/count, Return number of documents in a collection} @@ -592,9 +579,9 @@ function get_api_collections (req, res) { /// db._drop(cn); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock -//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_get_api_collection_figures /// /// @RESTHEADER{GET /_api/collection/{collection-name}/figures, Return statistics for a collection} @@ -606,16 +593,16 @@ function get_api_collections (req, res) { /// /// @RESTDESCRIPTION /// In addition to the above, the result also contains the number of documents -/// and additional statistical information about the collection. +/// and additional statistical information about the collection. /// **Note** : This will always load the collection into memory. /// /// - *count*: The number of documents currently present in the collection. /// -/// * *figures.alive.count*: The number of curretly active documents in all datafiles +/// * *figures.alive.count*: The number of curretly active documents in all datafiles /// and journals of the collection. Documents that are contained in the /// write-ahead log only are not reported in this figure. /// -/// * *figures.alive.size*: The total size in bytes used by all active documents of +/// * *figures.alive.size*: The total size in bytes used by all active documents of /// the collection. Documents that are contained in the write-ahead log only are /// not reported in this figure. /// @@ -658,7 +645,7 @@ function get_api_collections (req, res) { /// not reported in this figure. /// * *figures.attributes.size*: The total size of the attribute data (in bytes). /// Note: the value includes data of attributes that are not in use anymore. -/// Attributes that are contained in the write-ahead log only are not +/// Attributes that are contained in the write-ahead log only are not /// reported in this figure. /// /// * *figures.indexes.count*: The total number of indexes defined for the @@ -678,7 +665,7 @@ function get_api_collections (req, res) { /// /// **Note**: collection data that are stored in the write-ahead log only are /// not reported in the results. When the write-ahead log is collected, documents -/// might be added to journals and datafiles of the collection, which may modify +/// might be added to journals and datafiles of the collection, which may modify /// the figures of the collection. /// /// Additionally, the filesizes of collection and index parameter JSON files are @@ -691,8 +678,8 @@ function get_api_collections (req, res) { /// /// That means that the figures reported do not reflect the actual disk /// usage of the collection with 100% accuracy. The actual disk usage of -/// a collection is normally slightly higher than the sum of the reported -/// *fileSize* values. Still the sum of the *fileSize* values can still be +/// a collection is normally slightly higher than the sum of the reported +/// *fileSize* values. Still the sum of the *fileSize* values can still be /// used as a lower bound approximation of the disk usage. /// /// @RESTRETURNCODES @@ -725,9 +712,9 @@ function get_api_collections (req, res) { /// db._drop(cn); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_get_api_collection_revision /// /// @RESTHEADER{GET /_api/collection/{collection-name}/revision, Return collection revision id} @@ -775,7 +762,7 @@ function get_api_collections (req, res) { /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_get_api_collection_checksum /// /// @RESTHEADER{GET /_api/collection/{collection-name}/checksum, Return checksum for the collection} @@ -794,23 +781,23 @@ function get_api_collections (req, res) { /// Whether or not to include document body data in the checksum calculation. /// /// @RESTDESCRIPTION -/// Will calculate a checksum of the meta-data (keys and optionally revision ids) and +/// Will calculate a checksum of the meta-data (keys and optionally revision ids) and /// optionally the document data in the collection. /// /// The checksum can be used to compare if two collections on different ArangoDB -/// instances contain the same contents. The current revision of the collection is -/// returned too so one can make sure the checksums are calculated for the same +/// instances contain the same contents. The current revision of the collection is +/// returned too so one can make sure the checksums are calculated for the same /// state of data. /// /// By default, the checksum will only be calculated on the *_key* system attribute -/// of the documents contained in the collection. For edge collections, the system +/// of the documents contained in the collection. For edge collections, the system /// attributes *_from* and *_to* will also be included in the calculation. /// /// By setting the optional URL parameter *withRevisions* to *true*, then revision /// ids (*_rev* system attributes) are included in the checksumming. /// -/// By providing the optional URL parameter *withData* with a value of *true*, -/// the user-defined document attributes will be included in the calculation too. +/// By providing the optional URL parameter *withData* with a value of *true*, +/// the user-defined document attributes will be included in the calculation too. /// **Note**: Including user-defined attributes will make the checksumming slower. /// /// The response is a JSON object with the following attributes: @@ -878,25 +865,25 @@ function get_api_collection (req, res) { // ............................................................................. // /_api/collection // ............................................................................. - + if (req.suffix.length === 0 && req.parameters.id === undefined) { get_api_collections(req, res); return; } - + // ............................................................................. // /_api/collection/ // ............................................................................. name = decodeURIComponent(req.suffix[0]); - + var collection = arangodb.db._collection(name); if (collection === null) { actions.collectionNotFound(req, res, name); return; } - + var headers; // ............................................................................. @@ -905,8 +892,8 @@ function get_api_collection (req, res) { if (req.suffix.length === 1) { result = collectionRepresentation(collection, false, false, false); - headers = { - location : databasePrefix(req, "/" + API + "/" + collection.name()) + headers = { + location : databasePrefix(req, "/" + API + "/" + collection.name()) }; actions.resultOk(req, res, actions.HTTP_OK, result, headers); return; @@ -929,7 +916,7 @@ function get_api_collection (req, res) { withRevisions = true; } } - + if (req.parameters.hasOwnProperty('withData')) { value = req.parameters.withData.toLowerCase(); if (value === 'true' || value === 'yes' || value === 'on' || value === 'y' || value === '1') { @@ -943,15 +930,15 @@ function get_api_collection (req, res) { result.revision = checksum.revision; actions.resultOk(req, res, actions.HTTP_OK, result); } - + // ............................................................................. // /_api/collection//figures // ............................................................................. else if (sub === "figures") { result = collectionRepresentation(collection, true, true, true); - headers = { - location : databasePrefix(req, "/" + API + "/" + collection.name() + "/figures") + headers = { + location : databasePrefix(req, "/" + API + "/" + collection.name() + "/figures") }; actions.resultOk(req, res, actions.HTTP_OK, result, headers); } @@ -962,8 +949,8 @@ function get_api_collection (req, res) { else if (sub === "count") { result = collectionRepresentation(collection, true, true, false); - headers = { - location : databasePrefix(req, "/" + API + "/" + collection.name() + "/count") + headers = { + location : databasePrefix(req, "/" + API + "/" + collection.name() + "/count") }; actions.resultOk(req, res, actions.HTTP_OK, result, headers); } @@ -974,8 +961,8 @@ function get_api_collection (req, res) { else if (sub === "properties") { result = collectionRepresentation(collection, true, false, false); - headers = { - location : databasePrefix(req, "/" + API + "/" + collection.name() + "/properties") + headers = { + location : databasePrefix(req, "/" + API + "/" + collection.name() + "/properties") }; actions.resultOk(req, res, actions.HTTP_OK, result, headers); } @@ -1019,8 +1006,8 @@ function get_api_collection (req, res) { /// The request might optionally contain the following attribute: /// /// - *count*: If set, this controls whether the return value should include -/// the number of documents in the collection. Setting *count* to -/// *false* may speed up loading a collection. The default value for +/// the number of documents in the collection. Setting *count* to +/// *false* may speed up loading a collection. The default value for /// *count* is *true*. /// /// On success an object with the following attributes is returned: @@ -1236,7 +1223,7 @@ function put_api_collection_truncate (req, res, collection) { /// - 3: edges collection /// /// **Note**: some other collection properties, such as *type*, *isVolatile*, -/// *numberOfShards* or *shardKeys* cannot be changed once a collection is +/// *numberOfShards* or *shardKeys* cannot be changed once a collection is /// created. /// /// @EXAMPLES @@ -1361,12 +1348,12 @@ function put_api_collection_rename (req, res, collection) { /// The name of the collection. /// /// @RESTDESCRIPTION -/// Rotates the journal of a collection. The current journal of the collection will be closed +/// Rotates the journal of a collection. The current journal of the collection will be closed /// and made a read-only datafile. The purpose of the rotate method is to make the data in /// the file available for compaction (compaction is only performed for read-only datafiles, and /// not for journals). /// -/// Saving new data in the collection subsequently will create a new journal file +/// Saving new data in the collection subsequently will create a new journal file /// automatically if there is no current journal. /// /// If returns an object with the attributes @@ -1580,7 +1567,6 @@ function delete_api_collection (req, res) { actions.defineHttp({ url : API, - context : "api", callback : function (req, res) { try { @@ -1606,15 +1592,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-configuration.js b/js/actions/api-configuration.js index 53988a5233..7dde85b194 100644 --- a/js/actions/api-configuration.js +++ b/js/actions/api-configuration.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2014 triAGENS GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,7 +22,7 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann /// @author Copyright 2014, triAGENS GmbH, Cologne, Germany @@ -43,7 +43,6 @@ var configuration = require("org/arangodb/configuration"); actions.defineHttp({ url: "_admin/configuration/notifications/versions", prefix: false, - context: "api", callback : function (req, res) { var json; @@ -82,5 +81,5 @@ actions.defineHttp({ // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-cursor.js b/js/actions/api-cursor.js index 86dc1df6d2..ae702360a9 100644 --- a/js/actions/api-cursor.js +++ b/js/actions/api-cursor.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,10 +22,11 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Achim Brandt /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -61,34 +62,34 @@ var internal = require("internal"); /// /// - *count*: boolean flag that indicates whether the number of documents /// in the result set should be returned in the "count" attribute of the result (optional). -/// Calculating the "count" attribute might in the future have a performance -/// impact for some queries so this option is turned off by default, and "count" +/// Calculating the "count" attribute might in the future have a performance +/// impact for some queries so this option is turned off by default, and "count" /// is only returned when requested. /// /// - *batchSize*: maximum number of result documents to be transferred from /// the server to the client in one roundtrip (optional). If this attribute is /// not set, a server-controlled default value will be used. /// -/// - *ttl*: an optional time-to-live for the cursor (in seconds). The cursor will be +/// - *ttl*: an optional time-to-live for the cursor (in seconds). The cursor will be /// removed on the server automatically after the specified amount of time. This -/// is useful to ensure garbage collection of cursors that are not fully fetched +/// is useful to ensure garbage collection of cursors that are not fully fetched /// by clients. If not set, a server-defined value will be used. /// -/// - *bindVars*: key/value list of bind parameters (optional). +/// - *bindVars*: key/value list of bind parameters (optional). /// /// - *options*: key/value list of extra options for the query (optional). /// /// The following options are supported at the moment: -/// +/// /// - *fullCount*: if set to *true* and the query contains a *LIMIT* clause, then the -/// result will contain an extra attribute *extra* with a sub-attribute *fullCount*. +/// result will contain an extra attribute *extra* with a sub-attribute *fullCount*. /// This sub-attribute will contain the number of documents in the result before the /// last LIMIT in the query was applied. It can be used to count the number of documents that -/// match certain filter criteria, but only return a subset of them, in one go. -/// It is thus similar to MySQL's *SQL_CALC_FOUND_ROWS* hint. Note that setting the option +/// match certain filter criteria, but only return a subset of them, in one go. +/// It is thus similar to MySQL's *SQL_CALC_FOUND_ROWS* hint. Note that setting the option /// will disable a few LIMIT optimizations and may lead to more documents being processed, /// and thus make queries run longer. Note that the *fullCount* sub-attribute will only -/// be present in the result if the query has a LIMIT clause and the LIMIT clause is +/// be present in the result if the query has a LIMIT clause and the LIMIT clause is /// actually used in the query. /// /// If the result set can be created by the server, the server will respond with @@ -104,7 +105,7 @@ var internal = require("internal"); /// /// - *result*: an array of result documents (might be empty if query has no results) /// -/// - *hasMore*: a boolean indicator whether there are more results +/// - *hasMore*: a boolean indicator whether there are more results /// available for the cursor on the server /// /// - *count*: the total number of result documents available (only @@ -138,7 +139,7 @@ var internal = require("internal"); /// A list of query errors can be found (../ArangoErrors/README.md) here. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the result set can be created by the server. /// @@ -161,21 +162,21 @@ var internal = require("internal"); /// var cn = "products"; /// db._drop(cn); /// db._create(cn); -/// +/// /// db.products.save({"hello1":"world1"}); /// db.products.save({"hello2":"world1"}); /// /// var url = "/_api/cursor"; /// var body = { /// query: "FOR p IN products LIMIT 2 RETURN p", -/// count: true, +/// count: true, /// batchSize: 2 /// }; -/// +/// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 201); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -185,7 +186,7 @@ var internal = require("internal"); /// var cn = "products"; /// db._drop(cn); /// db._create(cn); -/// +/// /// db.products.save({"hello1":"world1"}); /// db.products.save({"hello2":"world1"}); /// db.products.save({"hello3":"world1"}); @@ -193,16 +194,16 @@ var internal = require("internal"); /// db.products.save({"hello5":"world1"}); /// /// var url = "/_api/cursor"; -/// var body = { +/// var body = { /// query: "FOR p IN products LIMIT 5 RETURN p", /// count: true, /// batchSize: 2 /// }; -/// +/// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 201); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -210,29 +211,29 @@ var internal = require("internal"); /// /// @EXAMPLE_ARANGOSH_RUN{RestCursorCreateCursorOption} /// var url = "/_api/cursor"; -/// var body = { +/// var body = { /// query: "FOR i IN 1..1000 FILTER i > 500 LIMIT 10 RETURN i", /// count: true, /// options: { /// fullCount: true /// } /// }; -/// +/// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 201); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// -/// Executes a data-modification query and retrieves the number of +/// Executes a data-modification query and retrieves the number of /// modified documents: /// /// @EXAMPLE_ARANGOSH_RUN{RestCursorDeleteQuery} /// var cn = "products"; /// db._drop(cn); /// db._create(cn); -/// +/// /// db.products.save({"hello1":"world1"}); /// db.products.save({"hello2":"world1"}); /// @@ -240,13 +241,13 @@ var internal = require("internal"); /// var body = { /// query: "FOR p IN products REMOVE p IN products" /// }; -/// +/// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 201); /// assert(JSON.parse(response.body).extra.operations.executed === 2); /// assert(JSON.parse(response.body).extra.operations.ignored === 0); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -256,20 +257,20 @@ var internal = require("internal"); /// var cn = "products"; /// db._drop(cn); /// db._create(cn); -/// +/// /// db.products.save({ _key: "foo" }); /// /// var url = "/_api/cursor"; /// var body = { /// query: "REMOVE 'bar' IN products OPTIONS { ignoreErrors: true }" /// }; -/// +/// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 201); /// assert(JSON.parse(response.body).extra.operations.executed === 0); /// assert(JSON.parse(response.body).extra.operations.ignored === 1); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -279,11 +280,11 @@ var internal = require("internal"); /// /// @EXAMPLE_ARANGOSH_RUN{RestCursorCreateCursorMissingBody} /// var url = "/_api/cursor"; -/// +/// /// var response = logCurlRequest('POST', url, ''); -/// +/// /// assert(response.code === 400); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -291,16 +292,16 @@ var internal = require("internal"); /// /// @EXAMPLE_ARANGOSH_RUN{RestCursorCreateCursorUnknownCollection} /// var url = "/_api/cursor"; -/// var body = { -/// query: "FOR u IN unknowncoll LIMIT 2 RETURN u", -/// count: true, -/// batchSize: 2 +/// var body = { +/// query: "FOR u IN unknowncoll LIMIT 2 RETURN u", +/// count: true, +/// batchSize: 2 /// }; -/// +/// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 404); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -311,18 +312,18 @@ var internal = require("internal"); /// var cn = "products"; /// db._drop(cn); /// db._create(cn); -/// +/// /// db.products.save({ _key: "bar" }); /// /// var url = "/_api/cursor"; /// var body = { /// query: "REMOVE 'foo' IN products" /// }; -/// +/// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 404); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -345,12 +346,12 @@ function post_api_cursor(req, res) { var cursor; if (json.query !== undefined) { - cursor = internal.AQL_QUERY(json.query, - json.bindVars, - { + cursor = internal.AQL_QUERY(json.query, + json.bindVars, + { count : json.count || false, batchSize: json.batchSize || 1000, - ttl: json.ttl + ttl: json.ttl }, json.options); } @@ -358,7 +359,7 @@ function post_api_cursor(req, res) { actions.resultBad(req, res, arangodb.ERROR_QUERY_EMPTY); return; } - + // error occurred if (cursor instanceof Error) { actions.resultException(req, res, cursor, undefined, false); @@ -366,11 +367,11 @@ function post_api_cursor(req, res) { } // this might dispose or persist the cursor - actions.resultCursor(req, - res, - cursor, - actions.HTTP_CREATED, - { + actions.resultCursor(req, + res, + cursor, + actions.HTTP_CREATED, + { countRequested: json.count ? true : false }); } @@ -402,9 +403,9 @@ function post_api_cursor(req, res) { /// *false*, the client can stop. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} -/// The server will respond with *HTTP 200* in case of success. +/// The server will respond with *HTTP 200* in case of success. /// /// @RESTRETURNCODE{400} /// If the cursor identifier is omitted, the server will respond with *HTTP 404*. @@ -422,7 +423,7 @@ function post_api_cursor(req, res) { /// var cn = "products"; /// db._drop(cn); /// db._create(cn); -/// +/// /// db.products.save({"hello1":"world1"}); /// db.products.save({"hello2":"world1"}); /// db.products.save({"hello3":"world1"}); @@ -431,9 +432,9 @@ function post_api_cursor(req, res) { /// /// var url = "/_api/cursor"; /// var body = { -/// query: "FOR p IN products LIMIT 5 RETURN p", -/// count: true, -/// batchSize: 2 +/// query: "FOR p IN products LIMIT 5 RETURN p", +/// count: true, +/// batchSize: 2 /// }; /// var response = logCurlRequest('POST', url, JSON.stringify(body)); /// @@ -441,7 +442,7 @@ function post_api_cursor(req, res) { /// var _id = JSON.parse(body).id; /// response = logCurlRequest('PUT', url + '/' + _id, ''); /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -449,11 +450,11 @@ function post_api_cursor(req, res) { /// /// @EXAMPLE_ARANGOSH_RUN{RestCursorMissingCursorIdentifier} /// var url = "/_api/cursor"; -/// +/// /// var response = logCurlRequest('PUT', url, ''); -/// +/// /// assert(response.code === 400); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -461,11 +462,11 @@ function post_api_cursor(req, res) { /// /// @EXAMPLE_ARANGOSH_RUN{RestCursorInvalidCursorIdentifier} /// var url = "/_api/cursor/123123"; -/// +/// /// var response = logCurlRequest('PUT', url, ''); -/// +/// /// assert(response.code === 404); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock @@ -477,15 +478,15 @@ function put_api_cursor (req, res) { return; } - var cursorId = decodeURIComponent(req.suffix[0]); + var cursorId = decodeURIComponent(req.suffix[0]); var cursor = CURSOR(cursorId); if (! (cursor instanceof arangodb.ArangoCursor)) { actions.resultBad(req, res, arangodb.ERROR_CURSOR_NOT_FOUND); return; } - - try { + + try { // note: this might dispose or persist the cursor actions.resultCursor(req, res, cursor, actions.HTTP_OK); } @@ -509,18 +510,18 @@ function put_api_cursor (req, res) { /// The name of the cursor /// /// @RESTDESCRIPTION -/// Deletes the cursor and frees the resources associated with it. +/// Deletes the cursor and frees the resources associated with it. /// /// The cursor will automatically be destroyed on the server when the client has /// retrieved all documents from it. The client can also explicitly destroy the /// cursor at any earlier time using an HTTP DELETE request. The cursor id must /// be included as part of the URL. -/// -/// Note: the server will also destroy abandoned cursors automatically after a +/// +/// Note: the server will also destroy abandoned cursors automatically after a /// certain server-controlled timeout to avoid resource leakage. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{202} /// is returned if the server is aware of the cursor. /// @@ -535,7 +536,7 @@ function put_api_cursor (req, res) { /// var cn = "products"; /// db._drop(cn); /// db._create(cn); -/// +/// /// db.products.save({"hello1":"world1"}); /// db.products.save({"hello2":"world1"}); /// db.products.save({"hello3":"world1"}); @@ -544,7 +545,7 @@ function put_api_cursor (req, res) { /// /// var url = "/_api/cursor"; /// var body = { -/// query: "FOR p IN products LIMIT 5 RETURN p", +/// query: "FOR p IN products LIMIT 5 RETURN p", /// count: true, /// batchSize: 2 /// }; @@ -553,7 +554,7 @@ function put_api_cursor (req, res) { /// var body = response.body.replace(/\\/g, ''); /// var _id = JSON.parse(body).id; /// response = logCurlRequest('DELETE', url + '/' + _id); -/// +/// /// assert(response.code === 202); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock @@ -582,26 +583,25 @@ function delete_api_cursor(req, res) { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief cursor actions gateway +/// @brief cursor actions gateway //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ url : "_api/cursor", - context : "api", callback : function (req, res) { try { switch (req.requestType) { - case actions.POST: - post_api_cursor(req, res); + case actions.POST: + post_api_cursor(req, res); break; - case actions.PUT: - put_api_cursor(req, res); + case actions.PUT: + put_api_cursor(req, res); break; - case actions.DELETE: - delete_api_cursor(req, res); + case actions.DELETE: + delete_api_cursor(req, res); break; default: @@ -614,7 +614,11 @@ actions.defineHttp({ } }); +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-database.js b/js/actions/api-database.js index 3a80a1a3b1..39e6d1b6ea 100644 --- a/js/actions/api-database.js +++ b/js/actions/api-database.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2013 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -38,11 +39,6 @@ var API = "_api/database"; // --SECTION-- public functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_get_api_database_list /// @brief retrieves a list of all existing databases @@ -55,7 +51,7 @@ var API = "_api/database"; /// **Note**: retrieving the list of databases is only possible from within the *_system* database. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the list of database was compiled successfully. /// @@ -70,9 +66,9 @@ var API = "_api/database"; /// @EXAMPLE_ARANGOSH_RUN{RestDatabaseGet} /// var url = "/_api/database"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock @@ -85,11 +81,11 @@ var API = "_api/database"; /// @RESTHEADER{GET /_api/database/user, List of accessible databases } /// /// @RESTDESCRIPTION -/// Retrieves the list of all databases the current user can access without +/// Retrieves the list of all databases the current user can access without /// specifying a different username or password. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the list of database was compiled successfully. /// @@ -101,9 +97,9 @@ var API = "_api/database"; /// @EXAMPLE_ARANGOSH_RUN{RestDatabaseGetUser} /// var url = "/_api/database/user"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock @@ -119,7 +115,7 @@ var API = "_api/database"; /// Retrieves information about the current database /// /// The response is a JSON object with the following attributes: -/// +/// /// - *name*: the name of the current database /// /// - *id*: the id of the current database @@ -129,7 +125,7 @@ var API = "_api/database"; /// - *isSystem*: whether or not the current database is the *_system* database /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the information was retrieved successfully. /// @@ -144,20 +140,20 @@ var API = "_api/database"; /// @EXAMPLE_ARANGOSH_RUN{RestDatabaseGetInfo} /// var url = "/_api/database/current"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// function get_api_database (req, res) { - if (req.suffix.length > 1) { + if (req.suffix.length > 1) { actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER); return; } - + var result; if (req.suffix.length === 0) { // list of all databases @@ -228,7 +224,7 @@ function get_api_database (req, res) { /// The request body must be a JSON object with the attribute *name*. *name* must /// contain a valid database name. /// -/// The request body can optionally contain an attribute *users*, which then +/// The request body can optionally contain an attribute *users*, which then /// must be a list of user objects to initially create for the new database. /// Each user object can contain the following attributes: /// @@ -253,12 +249,12 @@ function get_api_database (req, res) { /// **Note**: creating a new database is only possible from within the *_system* database. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the database was created successfully. /// /// @RESTRETURNCODE{400} -/// is returned if the request parameters are invalid or if a database with the +/// is returned if the request parameters are invalid or if a database with the /// specified name already exists. /// /// @RESTRETURNCODE{403} @@ -287,7 +283,7 @@ function get_api_database (req, res) { /// /// db._dropDatabase(name); /// assert(response.code === 201); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @@ -305,13 +301,13 @@ function get_api_database (req, res) { /// var data = { /// name: name, /// users: [ -/// { -/// username : "admin", +/// { +/// username : "admin", /// passwd : "secret", /// active: true /// }, /// { -/// username : "tester", +/// username : "tester", /// passwd : "test001", /// active: false /// } @@ -321,27 +317,27 @@ function get_api_database (req, res) { /// /// db._dropDatabase(name); /// assert(response.code === 201); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// function post_api_database (req, res) { - if (req.suffix.length !== 0) { + if (req.suffix.length !== 0) { actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER); return; } - + var json = actions.getJsonBody(req, res); - + if (json === undefined) { actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER); return; } var options = json.options; - + if (options === undefined) { options = { }; } @@ -352,7 +348,7 @@ function post_api_database (req, res) { } var users = json.users; - + if (users === undefined) { users = [ ]; } @@ -364,7 +360,7 @@ function post_api_database (req, res) { var i; for (i = 0; i < users.length; ++i) { var user = users[i]; - if (typeof user !== 'object' || + if (typeof user !== 'object' || ! user.hasOwnProperty('username') || typeof(user.username) !== 'string') { // bad username @@ -411,7 +407,7 @@ function post_api_database (req, res) { /// The *_system* database itself cannot be dropped. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the database was dropped successfully. /// @@ -430,22 +426,22 @@ function post_api_database (req, res) { /// var url = "/_api/database"; /// var name = "example"; /// -/// db._createDatabase(name); +/// db._createDatabase(name); /// var response = logCurlRequest('DELETE', url + '/' + name); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// function delete_api_database (req, res) { - if (req.suffix.length !== 1) { + if (req.suffix.length !== 1) { actions.resultBad(req, res, arangodb.ERROR_HTTP_BAD_PARAMETER); return; } - + var result = arangodb.db._dropDatabase(req.suffix[0]); actions.resultOk(req, res, actions.HTTP_OK, { result : result }); @@ -457,7 +453,6 @@ function delete_api_database (req, res) { actions.defineHttp({ url : API, - context : "api", callback : function (req, res) { try { @@ -480,15 +475,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-edges.js b/js/actions/api-edges.js index 113fb45e49..472a127af6 100644 --- a/js/actions/api-edges.js +++ b/js/actions/api-edges.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Achim Brandt +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -33,11 +34,6 @@ var actions = require("org/arangodb/actions"); var API = "/_api/edges"; -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock API_EDGE_READINOUTBOUND /// @brief get edges @@ -79,7 +75,7 @@ var API = "/_api/edges"; /// /// var url = "/_api/edges/edges?vertex=vertices/1"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -103,7 +99,7 @@ var API = "/_api/edges"; /// /// var url = "/_api/edges/edges?vertex=vertices/1&direction=in"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -127,7 +123,7 @@ var API = "/_api/edges"; /// /// var url = "/_api/edges/edges?vertex=vertices/1&direction=out"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -140,10 +136,10 @@ var API = "/_api/edges"; function get_edges (req, res) { if (req.suffix.length !== 1) { - actions.resultBad(req, - res, + actions.resultBad(req, + res, arangodb.ERROR_HTTP_BAD_PARAMETER, - "expect GET /" + API + + "expect GET /" + API + "/?vertex=&direction="); return; } @@ -174,7 +170,7 @@ function get_edges (req, res) { " must be any, in, or out, not: " + JSON.stringify(direction)); return; } - + actions.resultOk(req, res, actions.HTTP_OK, { edges: e }); } @@ -184,7 +180,6 @@ function get_edges (req, res) { actions.defineHttp({ url : API, - context : "api", callback : function (req, res) { try { @@ -201,11 +196,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-endpoint.js b/js/actions/api-endpoint.js index 2fdb7b5b80..de067625f8 100644 --- a/js/actions/api-endpoint.js +++ b/js/actions/api-endpoint.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -36,11 +37,6 @@ var internal = require("internal"); // --SECTION-- public functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_get_api_endpoint /// @brief returns a list of all endpoints @@ -58,8 +54,8 @@ var internal = require("internal"); /// accessed via the endpoint. If a list of mapped databases contains more than /// one database name, this means that any of the databases might be accessed /// via the endpoint, and the first database in the list will be treated as -/// the default database for the endpoint. The default database will be used -/// when an incoming request does not specify a database name in the request +/// the default database for the endpoint. The default database will be used +/// when an incoming request does not specify a database name in the request /// explicitly. /// /// **Note**: retrieving the list of all endpoints is allowed in the system database @@ -67,7 +63,7 @@ var internal = require("internal"); /// an error. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned when the list of endpoints can be determined successfully. /// @@ -89,9 +85,9 @@ var internal = require("internal"); /// curlRequest('POST', url, JSON.stringify(body)); /// /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// curlRequest('DELETE', url + '/' + encodeURIComponent(endpoint)); /// @END_EXAMPLE_ARANGOSH_RUN @@ -116,35 +112,35 @@ var internal = require("internal"); /// /// If *databases* is an empty list, all databases present in the server will /// become accessible via the endpoint, with the *_system* database being the -/// default database. +/// default database. /// -/// If *databases* is non-empty, only the specified databases will become -/// available via the endpoint. The first database name in the *databases* -/// list will also become the default database for the endpoint. The default -/// database will always be used if a request coming in on the endpoint does +/// If *databases* is non-empty, only the specified databases will become +/// available via the endpoint. The first database name in the *databases* +/// list will also become the default database for the endpoint. The default +/// database will always be used if a request coming in on the endpoint does /// not specify the database name explicitly. /// -/// **Note**: adding or reconfiguring endpoints is allowed in the system database -/// only. Calling this action in any other database will make the server +/// **Note**: adding or reconfiguring endpoints is allowed in the system database +/// only. Calling this action in any other database will make the server /// return an error. /// -/// Adding SSL endpoints at runtime is only supported if the server was started +/// Adding SSL endpoints at runtime is only supported if the server was started /// with SSL properly configured (e.g. *--server.keyfile* must have been set). /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned when the endpoint was added or changed successfully. /// /// @RESTRETURNCODE{400} -/// is returned if the request is malformed or if the action is not carried out +/// is returned if the request is malformed or if the action is not carried out /// in the system database. /// /// @RESTRETURNCODE{405} /// The server will respond with *HTTP 405* if an unsupported HTTP method is used. /// /// @EXAMPLES -/// Adding an endpoint *tcp://127.0.0.1:8532* with two mapped databases +/// Adding an endpoint *tcp://127.0.0.1:8532* with two mapped databases /// (*mydb1* and *mydb2*). *mydb1* will become the default database for the /// endpoint. /// @@ -156,7 +152,7 @@ var internal = require("internal"); /// databases: [ "mydb1", "mydb2" ] /// }; /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -176,7 +172,7 @@ var internal = require("internal"); /// databases: [ ] /// }; /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -186,7 +182,7 @@ var internal = require("internal"); /// Adding an endpoint *tcp://127.0.0.1:8533* without any databases first, /// and then updating the databases for the endpoint to *testdb1*, *testdb2*, and /// *testdb3*. -/// +/// /// @EXAMPLE_ARANGOSH_RUN{RestEndpointPostChange} /// var url = "/_api/endpoint"; /// var endpoint = "tcp://127.0.0.1:8533"; @@ -195,14 +191,14 @@ var internal = require("internal"); /// databases: [ ] /// }; /// var response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); /// /// body.database = [ "testdb1", "testdb2", "testdb3" ]; /// response = logCurlRequest('POST', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -226,20 +222,20 @@ var internal = require("internal"); /// This operation deletes an existing endpoint from the list of all endpoints, /// and makes the server stop listening on the endpoint. /// -/// **Note**: deleting and disconnecting an endpoint is allowed in the system -/// database only. Calling this action in any other database will make the server +/// **Note**: deleting and disconnecting an endpoint is allowed in the system +/// database only. Calling this action in any other database will make the server /// return an error. /// /// Futhermore, the last remaining endpoint cannot be deleted as this would make /// the server kaputt. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned when the endpoint was deleted and disconnected successfully. /// /// @RESTRETURNCODE{400} -/// is returned if the request is malformed or if the action is not carried out +/// is returned if the request is malformed or if the action is not carried out /// in the system database. /// /// @RESTRETURNCODE{404} @@ -261,7 +257,7 @@ var internal = require("internal"); /// }; /// curlRequest('POST', url, JSON.stringify(body)); /// var response = logCurlRequest('DELETE', url + '/' + encodeURIComponent(endpoint)); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -273,7 +269,7 @@ var internal = require("internal"); /// var url = "/_api/endpoint"; /// var endpoint = "tcp://127.0.0.1:8532"; /// var response = logCurlRequest('DELETE', url + '/' + encodeURIComponent(endpoint)); -/// +/// /// assert(response.code === 404); /// /// logJsonResponse(response); @@ -283,7 +279,6 @@ var internal = require("internal"); actions.defineHttp({ url : "_api/endpoint", - context : "admin", prefix : true, callback : function (req, res) { @@ -304,7 +299,7 @@ actions.defineHttp({ } result = internal.configureEndpoint(body.endpoint, body.databases || [ ]); - actions.resultOk(req, res, actions.HTTP_OK, { result: result }); + actions.resultOk(req, res, actions.HTTP_OK, { result: result }); } else if (req.requestType === actions.DELETE) { @@ -328,15 +323,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-explain.js b/js/actions/api-explain.js index 5fc15fd28c..e4b76baf67 100644 --- a/js/actions/api-explain.js +++ b/js/actions/api-explain.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -37,11 +38,6 @@ var EXPLAIN = require("internal").AQL_EXPLAIN; // --SECTION-- global variables // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_post_api_explain /// @brief explain a query and return information about it @@ -50,22 +46,22 @@ var EXPLAIN = require("internal").AQL_EXPLAIN; /// /// @RESTBODYPARAM{body,json,required} /// The query string needs to be passed in the attribute *query* of a JSON -/// object as the body of the POST request. If the query references any bind +/// object as the body of the POST request. If the query references any bind /// variables, these must also be passed in the attribute *bindVars*. /// /// @RESTDESCRIPTION /// -/// To explain how an AQL query would be executed on the server, the query string +/// To explain how an AQL query would be executed on the server, the query string /// can be sent to the server via an HTTP POST request. The server will then validate /// the query and create an execution plan for it, but will not execute it. /// /// The execution plan that is returned by the server can be used to estimate the /// probable performance of an AQL query. Though the actual performance will depend /// on many different factors, the execution plan normally can give some good hint -/// on the amount of work the server needs to do in order to actually run the query. +/// on the amount of work the server needs to do in order to actually run the query. /// /// The top-level statements will appear in the result in the same order in which -/// they have been used in the original query. Each result element has at most the +/// they have been used in the original query. Each result element has at most the /// following attributes: /// - *id*: the row number of the top-level statement, starting at 1 /// - *type*: the type of the top-level statement (e.g. *for*, *return* ...) @@ -75,13 +71,13 @@ var EXPLAIN = require("internal").AQL_EXPLAIN; /// used. /// Many top-level statements will provide an *expression* attribute that /// contains data about the expression they operate on. This is true for *FOR*, -/// *FILTER*, *SORT*, *COLLECT*, and *RETURN* statements. The +/// *FILTER*, *SORT*, *COLLECT*, and *RETURN* statements. The /// *expression* attribute has the following sub-attributes: /// - *type*: the type of the expression. Some possible values are: -/// - *collection*: an iteration over documents from a collection. The +/// - *collection*: an iteration over documents from a collection. The /// *value* attribute will then contain the collection name. The *extra* /// attribute will contain information about if and which index is used when -/// accessing the documents from the collection. If no index is used, the +/// accessing the documents from the collection. If no index is used, the /// *accessType* sub-attribute of the *extra* attribute will have the /// value *all*, otherwise it will be *index*. /// - *list*: a list of dynamic values. The *value* attribute will contain the @@ -93,7 +89,7 @@ var EXPLAIN = require("internal").AQL_EXPLAIN; /// /// Please note that the structure of the explain result data might change in future /// versions of ArangoDB without further notice and without maintaining backwards -/// compatibility. +/// compatibility. /// /// @RESTRETURNCODES /// @@ -151,7 +147,7 @@ var EXPLAIN = require("internal").AQL_EXPLAIN; /// @END_EXAMPLE_ARANGOSH_RUN /// /// The data returned in the *plan* attribute of the result contains one -/// element per AQL top-level statement (i.e. *FOR*, *RETURN*, +/// element per AQL top-level statement (i.e. *FOR*, *RETURN*, /// *FILTER* etc.). If the query optimiser removed some unnecessary statements, /// the result might also contain less elements than there were top-level /// statements in the AQL query. @@ -176,9 +172,9 @@ var EXPLAIN = require("internal").AQL_EXPLAIN; function post_api_explain (req, res) { if (req.suffix.length !== 0) { - actions.resultNotFound(req, - res, - ERRORS.errors.ERROR_HTTP_NOT_FOUND.code, + actions.resultNotFound(req, + res, + ERRORS.errors.ERROR_HTTP_NOT_FOUND.code, ERRORS.errors.ERROR_HTTP_NOT_FOUND.message); return; } @@ -205,18 +201,17 @@ function post_api_explain (req, res) { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief explain gateway +/// @brief explain gateway //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ url : "_api/explain", - context : "api", callback : function (req, res) { try { switch (req.requestType) { - case actions.POST: - post_api_explain(req, res); + case actions.POST: + post_api_explain(req, res); break; default: @@ -229,11 +224,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-foxx.js b/js/actions/api-foxx.js index a8001f7455..cb9dcabed3 100644 --- a/js/actions/api-foxx.js +++ b/js/actions/api-foxx.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -48,7 +49,6 @@ var easyPostCallback = actions.easyPostCallback; actions.defineHttp({ url : "_admin/foxx/fetch", - context : "admin", prefix : false, callback : function (req, res) { @@ -62,13 +62,13 @@ actions.defineHttp({ } var json = actions.getJsonBody(req, res, actions.HTTP_BAD); - + if (json === undefined) { return; } var serverFile = json.filename; - var realFile = fs.join(fs.getTempPath(), serverFile); + var realFile = fs.join(fs.getTempPath(), serverFile); if (! fs.isFile(realFile)) { actions.resultNotFound(req, res, arangodb.errors.ERROR_FILE_NOT_FOUND.code); @@ -116,7 +116,7 @@ actions.defineHttp({ if (found !== null) { found = found.app; } - + actions.resultOk(req, res, actions.HTTP_OK, { path: path, app: found }); } catch (err) { @@ -131,7 +131,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/foxx/mount", - context : "admin", prefix : false, callback: easyPostCallback({ @@ -145,14 +144,13 @@ actions.defineHttp({ } }) }); - + //////////////////////////////////////////////////////////////////////////////// /// @brief rescans the FOXX application directory //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ url : "_admin/foxx/rescan", - context : "admin", prefix : false, callback: easyPostCallback({ @@ -170,7 +168,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/foxx/setup", - context : "admin", prefix : false, callback: easyPostCallback({ @@ -189,7 +186,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/foxx/teardown", - context : "admin", prefix : false, callback: easyPostCallback({ @@ -208,7 +204,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/foxx/unmount", - context : "admin", prefix : false, callback: easyPostCallback({ @@ -227,7 +222,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/foxx/dev-setup", - context : "admin", prefix : false, callback: easyPostCallback({ @@ -246,7 +240,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/foxx/dev-teardown", - context : "admin", prefix : false, callback: easyPostCallback({ @@ -265,7 +258,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/foxx/purge", - context : "admin", prefix : false, callback: easyPostCallback({ @@ -284,7 +276,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/foxx/config", - context : "admin", prefix : false, callback : function (req, res) { @@ -305,5 +296,5 @@ actions.defineHttp({ // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-graph.js b/js/actions/api-graph.js index 1bacce2f9f..1dc86fef4a 100644 --- a/js/actions/api-graph.js +++ b/js/actions/api-graph.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,10 +22,11 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Achim Brandt /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -38,36 +39,16 @@ var arangodb = require("org/arangodb"); // --SECTION-- global variables // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief url prefix //////////////////////////////////////////////////////////////////////////////// var GRAPH_URL_PREFIX = "_api/graph"; -//////////////////////////////////////////////////////////////////////////////// -/// @brief context -//////////////////////////////////////////////////////////////////////////////// - -var GRAPH_CONTEXT = "api"; - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief get graph by request parameter (throws exception) //////////////////////////////////////////////////////////////////////////////// @@ -134,69 +115,60 @@ function edge_by_request (req, g) { /// @brief returns true if a "if-match" or "if-none-match" error happens //////////////////////////////////////////////////////////////////////////////// -function matchError (req, res, doc, errorCode) { +function matchError (req, res, doc, errorCode) { if (req.headers["if-none-match"] !== undefined) { if (doc._rev === req.headers["if-none-match"].replace(/(^["']|["']$)/g, '')) { - // error + // error res.responseCode = actions.HTTP_NOT_MODIFIED; res.contentType = "application/json; charset=utf-8"; res.body = ''; - res.headers = {}; + res.headers = {}; return true; } - } - + } + if (req.headers["if-match"] !== undefined) { if (doc._rev !== req.headers["if-match"].replace(/(^["']|["']$)/g, '')) { // error - actions.resultError(req, - res, - actions.HTTP_PRECONDITION_FAILED, - errorCode, - "wrong revision", + actions.resultError(req, + res, + actions.HTTP_PRECONDITION_FAILED, + errorCode, + "wrong revision", {}); return true; } - } - + } + var rev = req.parameters.rev; if (rev !== undefined) { if (doc._rev !== rev) { // error - actions.resultError(req, - res, - actions.HTTP_PRECONDITION_FAILED, - errorCode, - "wrong revision", + actions.resultError(req, + res, + actions.HTTP_PRECONDITION_FAILED, + errorCode, + "wrong revision", {}); return true; } - } - + } + return false; } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- graph functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief create a graph /// /// @RESTHEADER{POST /_api/graph,create graph} /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until document has been sync to disk. /// @@ -213,7 +185,7 @@ function matchError (req, res, doc, errorCode) { /// list of all graph properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the graph was created successfully and `waitForSync` was /// `true`. @@ -230,10 +202,10 @@ function matchError (req, res, doc, errorCode) { /// /// @EXAMPLE_ARANGOSH_RUN{RestGraphPostGraph} /// var url = "/_api/graph/"; -/// var response = logCurlRequest('POST', -/// url, +/// var response = logCurlRequest('POST', +/// url, /// {_key : "graph", vertices : "vertices", edges : "edges"}); -/// +/// /// assert(response.code === 201); /// /// logJsonResponse(response); @@ -292,7 +264,7 @@ function post_graph_graph (req, res) { /// @RESTHEADERPARAMETERS /// /// @RESTHEADERPARAM{If-None-Match,string,optional} -/// If `graph-name` is specified, then this header can be used to check +/// If `graph-name` is specified, then this header can be used to check /// whether a specific graph has changed or not. /// /// If the "If-None-Match" header is given, then it must contain exactly one @@ -300,7 +272,7 @@ function post_graph_graph (req, res) { /// given etag. Otherwise a `HTTP 304` is returned. /// /// @RESTHEADERPARAM{If-Match,string,optional} -/// If `graph-name` is specified, then this header can be used to check +/// If `graph-name` is specified, then this header can be used to check /// whether a specific graph has changed or not. /// /// If the "If-Match" header is given, then it must contain exactly one @@ -310,16 +282,16 @@ function post_graph_graph (req, res) { /// /// @RESTDESCRIPTION /// -/// If `graph-name` is specified, returns an object with an attribute `graph` +/// If `graph-name` is specified, returns an object with an attribute `graph` /// containing a JSON hash with all properties of the specified graph. /// /// If `graph-name` is not specified, returns a list of graph objects. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the graph was found (in case `graph-name` was specified) -/// or the list of graphs was assembled successfully (in case `graph-name` +/// or the list of graphs was assembled successfully (in case `graph-name` /// was not specified). /// /// @RESTRETURNCODE{404} @@ -328,12 +300,12 @@ function post_graph_graph (req, res) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{304} -/// "If-None-Match" header is given and the current graph has not a different +/// "If-None-Match" header is given and the current graph has not a different /// version. This response code may only be returned if `graph-name` is /// specified in the request. /// /// @RESTRETURNCODE{412} -/// "If-Match" header or `rev` is given and the current graph has +/// "If-Match" header or `rev` is given and the current graph has /// a different version. This response code may only be returned if `graph-name` /// is specified in the request. /// @@ -346,7 +318,7 @@ function post_graph_graph (req, res) { /// var g = new Graph("graph", "vertices", "edges"); /// var url = "/_api/graph/graph"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -363,7 +335,7 @@ function post_graph_graph (req, res) { /// new Graph("graph2", "vertices2", "edges2"); /// var url = "/_api/graph"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -379,11 +351,11 @@ function post_graph_graph (req, res) { function get_graph_graph (req, res) { try { var g = graph_by_request(req); - + if (matchError(req, res, g._properties, arangodb.ERROR_GRAPH_INVALID_GRAPH)) { return; - } - + } + var headers = { "Etag" : g._properties._rev }; @@ -418,7 +390,7 @@ function get_graph_graph (req, res) { /// Deletes graph, edges and vertices /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the graph was deleted and `waitForSync` was /// `true`. @@ -432,7 +404,7 @@ function get_graph_graph (req, res) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-Match" header or `rev` is given and the current graph has +/// "If-Match" header or `rev` is given and the current graph has /// a different version /// /// *Examples* @@ -444,7 +416,7 @@ function get_graph_graph (req, res) { /// var g = new Graph("graph", "vertices", "edges"); /// var url = "/_api/graph/graph"; /// var response = logCurlRequest('DELETE', url); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -475,7 +447,7 @@ function delete_graph_graph (req, res) { actions.resultNotFound(req, res, arangodb.ERROR_GRAPH_INVALID_GRAPH, err); return; } - + if (matchError(req, res, g._properties, arangodb.ERROR_GRAPH_INVALID_GRAPH)) { return; } @@ -484,7 +456,7 @@ function delete_graph_graph (req, res) { if (req.parameters.waitForSync) { waitForSync = true; } - + g.drop(waitForSync); waitForSync = waitForSync || g._gdb.properties().waitForSync; @@ -493,19 +465,10 @@ function delete_graph_graph (req, res) { actions.resultOk(req, res, returnCode, { "deleted" : true }); } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- vertex functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief creates a graph vertex /// @@ -517,7 +480,7 @@ function delete_graph_graph (req, res) { /// The name of the graph /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until document has been sync to disk. /// @@ -533,7 +496,7 @@ function delete_graph_graph (req, res) { /// list of all vertex properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the graph was created successfully and `waitForSync` was /// `true`. @@ -549,7 +512,7 @@ function delete_graph_graph (req, res) { /// var g = new Graph("graph", "vertices", "edges"); /// var url = "/_api/graph/graph/vertex"; /// var response = logCurlRequest('POST', url, {"_key" : "v1", "optional1" : "val1" }); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); @@ -572,7 +535,7 @@ function post_graph_vertex (req, res, g) { if (req.parameters.waitForSync) { waitForSync = true; } - + var v = g.addVertex(id, json, waitForSync); if (v === null || v._properties === undefined) { @@ -582,9 +545,9 @@ function post_graph_vertex (req, res, g) { var headers = { "Etag" : v._properties._rev }; - + var returnCode = waitForSync ? actions.HTTP_CREATED : actions.HTTP_ACCEPTED; - + actions.resultOk(req, res, returnCode, { "vertex" : v._properties }, headers ); } catch (err) { @@ -606,7 +569,7 @@ function post_graph_vertex (req, res, g) { /// The name of the vertex /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{rev,string,optional} /// Revision of a vertex /// @@ -628,12 +591,12 @@ function post_graph_vertex (req, res, g) { /// list of all vertex properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the graph was found /// /// @RESTRETURNCODE{304} -/// "If-Match" header is given and the current graph has not a different +/// "If-Match" header is given and the current graph has not a different /// version /// /// @RESTRETURNCODE{404} @@ -641,7 +604,7 @@ function post_graph_vertex (req, res, g) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-None-Match" header or `rev` is given and the current graph has +/// "If-None-Match" header or `rev` is given and the current graph has /// a different version /// /// *Examples* @@ -654,7 +617,7 @@ function post_graph_vertex (req, res, g) { /// g.addVertex("v1", {"optional1" : "val1" }); /// var url = "/_api/graph/graph/vertex/v1"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -674,15 +637,15 @@ function get_graph_vertex (req, res, g) { actions.resultNotFound(req, res, arangodb.ERROR_GRAPH_INVALID_VERTEX, err); return; } - + if (matchError(req, res, v._properties, arangodb.ERROR_GRAPH_INVALID_VERTEX)) { return; - } - + } + var headers = { "Etag" : v._properties._rev }; - + actions.resultOk(req, res, actions.HTTP_OK, { "vertex" : v._properties}, headers); } @@ -700,7 +663,7 @@ function get_graph_vertex (req, res, g) { /// The name of the vertex /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until document has been sync to disk. /// @@ -719,7 +682,7 @@ function get_graph_vertex (req, res, g) { /// Deletes vertex and all in and out edges of the vertex /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the vertex was deleted and `waitForSync` was /// `true`. @@ -733,7 +696,7 @@ function get_graph_vertex (req, res, g) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-Match" header or `rev` is given and the current vertex has +/// "If-Match" header or `rev` is given and the current vertex has /// a different version /// /// *Examples* @@ -744,7 +707,7 @@ function get_graph_vertex (req, res, g) { /// g.addVertex("v1", {"optional1" : "val1" }); /// var url = "/_api/graph/graph/vertex/v1"; /// var response = logCurlRequest('DELETE', url); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); @@ -764,10 +727,10 @@ function delete_graph_vertex (req, res, g) { actions.resultNotFound(req, res, arangodb.ERROR_GRAPH_INVALID_VERTEX, err); return; } - + if (matchError(req, res, v._properties, arangodb.ERROR_GRAPH_INVALID_VERTEX)) { return; - } + } var waitForSync = g._vertices.properties().waitForSync; if (req.parameters.waitForSync) { @@ -798,15 +761,15 @@ function update_graph_vertex (req, res, g, isPatch) { if (matchError(req, res, v._properties, arangodb.ERROR_GRAPH_INVALID_VERTEX)) { return; - } + } try { var json = actions.getJsonBody(req, res, arangodb.ERROR_GRAPH_COULD_NOT_CHANGE_VERTEX); if (json === undefined) { - actions.resultBad(req, - res, - arangodb.ERROR_GRAPH_COULD_NOT_CHANGE_VERTEX, + actions.resultBad(req, + res, + arangodb.ERROR_GRAPH_COULD_NOT_CHANGE_VERTEX, "error in request body"); return; } @@ -815,7 +778,7 @@ function update_graph_vertex (req, res, g, isPatch) { if (req.parameters.waitForSync) { waitForSync = true; } - + var shallow = json._shallowCopy; var id2 = null; @@ -828,10 +791,10 @@ function update_graph_vertex (req, res, g, isPatch) { keepNull = true; } - id2 = g._vertices.update(v._properties, json, true, keepNull, waitForSync); + id2 = g._vertices.update(v._properties, json, true, keepNull, waitForSync); } else { - id2 = g._vertices.replace(v._properties, shallow, true, waitForSync); + id2 = g._vertices.replace(v._properties, shallow, true, waitForSync); } var result = g._vertices.document(id2); @@ -841,7 +804,7 @@ function update_graph_vertex (req, res, g, isPatch) { }; var returnCode = waitForSync ? actions.HTTP_CREATED : actions.HTTP_ACCEPTED; - + actions.resultOk(req, res, returnCode, { "vertex" : result }, headers ); } catch (err2) { @@ -863,7 +826,7 @@ function update_graph_vertex (req, res, g, isPatch) { /// The name of the vertex /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until vertex has been sync to disk. /// @@ -888,7 +851,7 @@ function update_graph_vertex (req, res, g, isPatch) { /// list of all vertex properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the vertex was updated successfully and `waitForSync` was /// `true`. @@ -902,7 +865,7 @@ function update_graph_vertex (req, res, g, isPatch) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-Match" header or `rev` is given and the current vertex has +/// "If-Match" header or `rev` is given and the current vertex has /// a different version /// /// *Examples* @@ -913,7 +876,7 @@ function update_graph_vertex (req, res, g, isPatch) { /// g.addVertex("v1", {"optional1" : "val1" }); /// var url = "/_api/graph/graph/vertex/v1"; /// var response = logCurlRequest('PUT', url, { "optional1" : "val2" }); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); @@ -941,7 +904,7 @@ function put_graph_vertex (req, res, g) { /// The name of the vertex /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until vertex has been sync to disk. /// @@ -965,19 +928,19 @@ function put_graph_vertex (req, res, g) { /// @RESTDESCRIPTION /// Partially updates the vertex properties. /// -/// Setting an attribute value to `null` in the patch document will cause a value -/// of `null` be saved for the attribute by default. If the intention is to -/// delete existing attributes with the patch command, the URL parameter -/// `keepNull` can be used with a value of `false`. -/// This will modify the behavior of the patch command to remove any attributes -/// from the existing document that are contained in the patch document +/// Setting an attribute value to `null` in the patch document will cause a value +/// of `null` be saved for the attribute by default. If the intention is to +/// delete existing attributes with the patch command, the URL parameter +/// `keepNull` can be used with a value of `false`. +/// This will modify the behavior of the patch command to remove any attributes +/// from the existing document that are contained in the patch document /// with an attribute value of `null`. /// /// Returns an object with an attribute `vertex` containing a /// list of all vertex properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the vertex was updated successfully and `waitForSync` was /// `true`. @@ -991,7 +954,7 @@ function put_graph_vertex (req, res, g) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-Match" header or `rev` is given and the current vertex has +/// "If-Match" header or `rev` is given and the current vertex has /// a different version /// /// *Examples* @@ -1002,12 +965,12 @@ function put_graph_vertex (req, res, g) { /// g.addVertex("v1", { "optional1" : "val1" }); /// var url = "/_api/graph/graph/vertex/v1"; /// var response = logCurlRequest('PATCH', url, { "optional1" : "vertexPatch" }); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); /// var response = logCurlRequest('PATCH', url, { "optional1" : null }); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); @@ -1062,13 +1025,13 @@ function process_property_filter (data, num, property, collname) { if (data.filter === "") { data.filter = " FILTER"; } else { data.filter += " &&";} data.filter += " HAS(" + collname + ", @key" + num.toString() + ") "; data.bindVars["key" + num.toString()] = property.key; - return; + return; } if (property.key !== undefined && property.compare === "HAS_NOT") { if (data.filter === "") { data.filter = " FILTER"; } else { data.filter += " &&";} data.filter += " !HAS(" + collname + ", @key" + num.toString() + ") "; data.bindVars["key" + num.toString()] = property.key; - return; + return; } if (property.key !== undefined && property.value !== undefined) { if (data.filter === "") { data.filter = " FILTER"; } else { data.filter += " &&";} @@ -1145,7 +1108,7 @@ function process_labels_filter (data, labels, collname) { /// - `compare`: a compare operator /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the cursor was created /// @@ -1163,7 +1126,7 @@ function process_labels_filter (data, labels, collname) { /// g.addVertex("v5", { "optional1" : "val1" }); /// var url = "/_api/graph/graph/vertices"; /// var response = logCurlRequest('POST', url, {"batchSize" : 100 }); -/// +/// /// assert(response.code === 201); /// /// logJsonResponse(response); @@ -1200,7 +1163,7 @@ function post_graph_all_vertices (req, res, g) { var cursor = internal.AQL_QUERY(query, data.bindVars, - { + { count: json.count, batchSize: json.batchSize || 1000 }); @@ -1258,7 +1221,7 @@ function post_graph_all_vertices (req, res, g) { /// - `compare`: a compare operator /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the cursor was created /// @@ -1282,7 +1245,7 @@ function post_graph_all_vertices (req, res, g) { /// var body = '{"batchSize" : 100, "filter" : {"direction" : "any", "properties":'; /// body += '[] }}'; /// var response = logCurlRequest('POST', url, body); -/// +/// /// assert(response.code === 201); /// logJsonResponse(response); /// db._drop("edges"); @@ -1308,7 +1271,7 @@ function post_graph_all_vertices (req, res, g) { /// var body = '{"batchSize" : 100, "filter" : {"direction" : "out", "properties":'; /// body += '[ { "key": "optional1", "value": "val2", "compare" : "==" }, ] }}'; /// var response = logCurlRequest('POST', url, body); -/// +/// /// assert(response.code === 201); /// /// logJsonResponse(response); @@ -1351,7 +1314,7 @@ function post_graph_vertex_vertices (req, res, g) { direction = "outbound"; } } - + if (json.filter !== undefined && json.filter.properties !== undefined) { process_properties_filter(data, json.filter.properties, "n.edge"); } @@ -1361,9 +1324,9 @@ function post_graph_vertex_vertices (req, res, g) { } // build aql query - var query = 'FOR n IN NEIGHBORS( @@vertexColl, @@edgeColl, @id, "' + direction + '") ' + + var query = 'FOR n IN NEIGHBORS( @@vertexColl, @@edgeColl, @id, "' + direction + '") ' + data.filter + limit + " RETURN n.vertex "; - + var cursor = internal.AQL_QUERY(query, data.bindVars, { @@ -1400,7 +1363,7 @@ function post_graph_vertex_vertices (req, res, g) { /// The name of the graph /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until edge has been sync to disk. /// @@ -1422,7 +1385,7 @@ function post_graph_vertex_vertices (req, res, g) { /// list of all edge properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the edge was created successfully and `waitForSync` was /// `true`. @@ -1441,7 +1404,7 @@ function post_graph_vertex_vertices (req, res, g) { /// g.addVertex("vert2"); /// var body = {"_key" : "edge1", "_from" : "vert2", "_to" : "vert1", "optional1" : "val1"}; /// var response = logCurlRequest('POST', url, body); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); @@ -1456,9 +1419,9 @@ function post_graph_edge (req, res, g) { var json = actions.getJsonBody(req, res, arangodb.ERROR_GRAPH_COULD_NOT_CREATE_EDGE); if (json === undefined) { - actions.resultBad(req, - res, - arangodb.ERROR_GRAPH_COULD_NOT_CREATE_EDGE, + actions.resultBad(req, + res, + arangodb.ERROR_GRAPH_COULD_NOT_CREATE_EDGE, "error in request body"); return; } @@ -1467,7 +1430,7 @@ function post_graph_edge (req, res, g) { if (req.parameters.waitForSync) { waitForSync = true; } - + var id = json._key; var out = g.getVertex(json._from); var ine = g.getVertex(json._to); @@ -1482,7 +1445,7 @@ function post_graph_edge (req, res, g) { var headers = { "Etag" : e._properties._rev }; - + var returnCode = waitForSync ? actions.HTTP_CREATED : actions.HTTP_ACCEPTED; actions.resultOk(req, res, returnCode, { "edge" : e._properties }, headers); @@ -1506,7 +1469,7 @@ function post_graph_edge (req, res, g) { /// The name of the edge /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{rev,string,optional} /// Revision of an edge /// @@ -1528,12 +1491,12 @@ function post_graph_edge (req, res, g) { /// list of all edge properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the edge was found /// /// @RESTRETURNCODE{304} -/// "If-Match" header is given and the current edge has not a different +/// "If-Match" header is given and the current edge has not a different /// version /// /// @RESTRETURNCODE{404} @@ -1541,7 +1504,7 @@ function post_graph_edge (req, res, g) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-None-Match" header or `rev` is given and the current edge has +/// "If-None-Match" header or `rev` is given and the current edge has /// a different version /// /// *Examples* @@ -1552,9 +1515,9 @@ function post_graph_edge (req, res, g) { /// var url = "/_api/graph/graph/edge/edge1"; /// var v1 = g.addVertex("vert1"); /// var v2 = g.addVertex("vert2"); -/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); +/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); /// /// logJsonResponse(response); @@ -1572,12 +1535,12 @@ function get_graph_edge (req, res, g) { if (matchError(req, res, e._properties, arangodb.ERROR_GRAPH_INVALID_EDGE)) { return; - } - + } + var headers = { "Etag" : e._properties._rev }; - + actions.resultOk(req, res, actions.HTTP_OK, { "edge" : e._properties}, headers); } catch (err) { @@ -1599,7 +1562,7 @@ function get_graph_edge (req, res, g) { /// The name of the edge /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until edge has been sync to disk. /// @@ -1618,7 +1581,7 @@ function get_graph_edge (req, res, g) { /// Deletes an edge of the graph /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the edge was deletd successfully and `waitForSync` was /// `true`. @@ -1632,7 +1595,7 @@ function get_graph_edge (req, res, g) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-Match" header or `rev` is given and the current edge has +/// "If-Match" header or `rev` is given and the current edge has /// a different version /// /// *Examples* @@ -1642,10 +1605,10 @@ function get_graph_edge (req, res, g) { /// var g = new Graph("graph", "vertices", "edges"); /// var v1 = g.addVertex("vert1"); /// var v2 = g.addVertex("vert2"); -/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); +/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); /// var url = "/_api/graph/graph/edge/edge1"; /// var response = logCurlRequest('DELETE', url); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); @@ -1668,13 +1631,13 @@ function delete_graph_edge (req, res, g) { if (matchError(req, res, e._properties, arangodb.ERROR_GRAPH_INVALID_EDGE)) { return; - } - + } + var waitForSync = g._edges.properties().waitForSync; if (req.parameters.waitForSync) { waitForSync = true; } - + g.removeEdge(e, waitForSync); var returnCode = waitForSync ? actions.HTTP_OK : actions.HTTP_ACCEPTED; @@ -1699,15 +1662,15 @@ function update_graph_edge (req, res, g, isPatch) { if (matchError(req, res, e._properties, arangodb.ERROR_GRAPH_INVALID_EDGE)) { return; - } - + } + try { var json = actions.getJsonBody(req, res, arangodb.ERROR_GRAPH_COULD_NOT_CHANGE_EDGE); if (json === undefined) { - actions.resultBad(req, - res, - arangodb.ERROR_GRAPH_COULD_NOT_CHANGE_EDGE, + actions.resultBad(req, + res, + arangodb.ERROR_GRAPH_COULD_NOT_CHANGE_EDGE, "error in request body"); return; } @@ -1716,10 +1679,10 @@ function update_graph_edge (req, res, g, isPatch) { if (req.parameters.waitForSync) { waitForSync = true; } - + var shallow = json._shallowCopy; shallow.$label = e._properties.$label; - + var id2 = null; if (isPatch) { var keepNull = req.parameters.keepNull; @@ -1729,11 +1692,11 @@ function update_graph_edge (req, res, g, isPatch) { else { keepNull = true; } - - id2 = g._edges.update(e._properties, shallow, true, keepNull, waitForSync); + + id2 = g._edges.update(e._properties, shallow, true, keepNull, waitForSync); } else { - id2 = g._edges.replace(e._properties, shallow, true, waitForSync); + id2 = g._edges.replace(e._properties, shallow, true, waitForSync); } var result = g._edges.document(id2); @@ -1741,14 +1704,14 @@ function update_graph_edge (req, res, g, isPatch) { var headers = { "Etag" : result._rev }; - + var returnCode = waitForSync ? actions.HTTP_CREATED : actions.HTTP_ACCEPTED; - + actions.resultOk(req, res, returnCode, { "edge" : result}, headers ); } catch (err2) { actions.resultBad(req, res, arangodb.ERROR_GRAPH_COULD_NOT_CHANGE_EDGE, err2); - } + } } //////////////////////////////////////////////////////////////////////////////// @@ -1765,7 +1728,7 @@ function update_graph_edge (req, res, g, isPatch) { /// The name of the edge /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until edge has been sync to disk. /// @@ -1792,7 +1755,7 @@ function update_graph_edge (req, res, g, isPatch) { /// list of all edge properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the edge was updated successfully and `waitForSync` was /// `true`. @@ -1806,7 +1769,7 @@ function update_graph_edge (req, res, g, isPatch) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-Match" header or `rev` is given and the current edge has +/// "If-Match" header or `rev` is given and the current edge has /// a different version /// /// *Examples* @@ -1816,10 +1779,10 @@ function update_graph_edge (req, res, g, isPatch) { /// var g = new Graph("graph", "vertices", "edges"); /// var v1 = g.addVertex("vert1"); /// var v2 = g.addVertex("vert2"); -/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); +/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); /// var url = "/_api/graph/graph/edge/edge1"; /// var response = logCurlRequest('PUT', url, { "optional1" : "val2" }); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); @@ -1847,7 +1810,7 @@ function put_graph_edge (req, res, g) { /// The name of the edge /// /// @RESTQUERYPARAMETERS -/// +/// /// @RESTQUERYPARAM{waitForSync,boolean,optional} /// Wait until edge has been sync to disk. /// @@ -1871,19 +1834,19 @@ function put_graph_edge (req, res, g) { /// @RESTDESCRIPTION /// Partially updates the edge properties. /// -/// Setting an attribute value to `null` in the patch document will cause a value -/// of `null` be saved for the attribute by default. If the intention is to -/// delete existing attributes with the patch command, the URL parameter -/// `keepNull` can be used with a value of `false`. -/// This will modify the behavior of the patch command to remove any attributes -/// from the existing document that are contained in the patch document +/// Setting an attribute value to `null` in the patch document will cause a value +/// of `null` be saved for the attribute by default. If the intention is to +/// delete existing attributes with the patch command, the URL parameter +/// `keepNull` can be used with a value of `false`. +/// This will modify the behavior of the patch command to remove any attributes +/// from the existing document that are contained in the patch document /// with an attribute value of `null`. /// /// Returns an object with an attribute `edge` containing a /// list of all edge properties. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the edge was updated successfully and `waitForSync` was /// `true`. @@ -1897,7 +1860,7 @@ function put_graph_edge (req, res, g) { /// The response body contains an error document in this case. /// /// @RESTRETURNCODE{412} -/// "If-Match" header or `rev` is given and the current edge has +/// "If-Match" header or `rev` is given and the current edge has /// a different version /// /// *Examples* @@ -1907,10 +1870,10 @@ function put_graph_edge (req, res, g) { /// var g = new Graph("graph", "vertices", "edges"); /// var v1 = g.addVertex("vert1"); /// var v2 = g.addVertex("vert2"); -/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); +/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); /// var url = "/_api/graph/graph/edge/edge1"; /// var response = logCurlRequest('PATCH', url, { "optional3" : "val3" }); -/// +/// /// assert(response.code === 202); /// /// logJsonResponse(response); @@ -1957,7 +1920,7 @@ function patch_graph_edge (req, res, g) { /// - `compare`: a compare operator /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the cursor was created /// @@ -1973,13 +1936,13 @@ function patch_graph_edge (req, res, g) { /// var v3 = g.addVertex("v3", { "optional1" : "val1" }); /// var v4 = g.addVertex("v4", { "optional1" : "val1" }); /// var v5 = g.addVertex("v5", { "optional1" : "val1" }); -/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); -/// g.addEdge(v1, v3, "edge2", { "optional1" : "val1" }); -/// g.addEdge(v2, v4, "edge3", { "optional1" : "val1" }); -/// g.addEdge(v1, v5, "edge4", { "optional1" : "val1" }); +/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); +/// g.addEdge(v1, v3, "edge2", { "optional1" : "val1" }); +/// g.addEdge(v2, v4, "edge3", { "optional1" : "val1" }); +/// g.addEdge(v1, v5, "edge4", { "optional1" : "val1" }); /// var url = "/_api/graph/graph/edges"; /// var response = logCurlRequest('POST', url, {"batchSize" : 100 }); -/// +/// /// assert(response.code === 201); /// /// logJsonResponse(response); @@ -2024,7 +1987,7 @@ function post_graph_all_edges (req, res, g) { data.bindVars, { count: json.count, - batchSize: json.batchSize || 1000 + batchSize: json.batchSize || 1000 }); // error occurred @@ -2084,7 +2047,7 @@ function post_graph_all_edges (req, res, g) { /// - `compare`: a compare operator /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// is returned if the cursor was created /// @@ -2100,14 +2063,14 @@ function post_graph_all_edges (req, res, g) { /// var v3 = g.addVertex("v3", { "optional1" : "val1" }); /// var v4 = g.addVertex("v4", { "optional1" : "val1" }); /// var v5 = g.addVertex("v5", { "optional1" : "val1" }); -/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); -/// g.addEdge(v1, v3, "edge2", { "optional1" : "val1" }); -/// g.addEdge(v2, v4, "edge3", { "optional1" : "val1" }); -/// g.addEdge(v1, v5, "edge4", { "optional1" : "val1" }); +/// g.addEdge(v1, v2, "edge1", { "optional1" : "val1" }); +/// g.addEdge(v1, v3, "edge2", { "optional1" : "val1" }); +/// g.addEdge(v2, v4, "edge3", { "optional1" : "val1" }); +/// g.addEdge(v1, v5, "edge4", { "optional1" : "val1" }); /// var url = "/_api/graph/graph/edges/v2"; /// var body = '{"batchSize" : 100, "filter" : { "direction" : "any" }}'; /// var response = logCurlRequest('POST', url, body); -/// +/// /// assert(response.code === 201); /// /// logJsonResponse(response); @@ -2158,7 +2121,7 @@ function post_graph_vertex_edges (req, res, g) { process_labels_filter(data, json.filter.labels, "e"); } - var query = 'FOR e in EDGES( @@edgeColl , @id , "' + direction + '") ' + var query = 'FOR e in EDGES( @@edgeColl , @id , "' + direction + '") ' + data.filter + limit + " RETURN e"; var cursor = internal.AQL_QUERY(query, @@ -2420,7 +2383,6 @@ function delete_graph (req, res) { actions.defineHttp({ url : GRAPH_URL_PREFIX, - context : GRAPH_CONTEXT, callback : function (req, res) { try { @@ -2455,15 +2417,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-index.js b/js/actions/api-index.js index 902b9bb635..176f195a5f 100644 --- a/js/actions/api-index.js +++ b/js/actions/api-index.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Achim Brandt +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -37,11 +38,6 @@ var API = "_api/index"; // --SECTION-- public functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_get_api_index /// @brief returns all indexes of a collection @@ -83,7 +79,7 @@ var API = "_api/index"; function get_api_indexes (req, res) { var name = req.parameters.collection; var collection = arangodb.db._collection(name); - + if (collection === null) { actions.collectionNotFound(req, res, name); return; @@ -93,13 +89,13 @@ function get_api_indexes (req, res) { for (i = 0; i < indexes.length; ++i) { var index = indexes[i]; - + list.push(index); ids[index.id] = index; } var result = { indexes : list, identifiers : ids }; - + actions.resultOk(req, res, actions.HTTP_OK, result); } @@ -167,7 +163,7 @@ function get_api_index (req, res) { else if (req.suffix.length === 2) { var name = decodeURIComponent(req.suffix[0]); var collection = arangodb.db._collection(name); - + if (collection === null) { actions.collectionNotFound(req, res, name); return; @@ -218,7 +214,7 @@ function get_api_index (req, res) { /// /// - *size*: The maximal number of documents for the collection. If specified, /// the value must be greater than zero. -/// +/// /// - *byteSize*: The maximal size of the active document data in the collection /// (in bytes). If specified, the value must be at least 16384. /// @@ -258,9 +254,9 @@ function get_api_index (req, res) { /// db._create(cn, { waitForSync: true }); /// /// var url = "/_api/index?collection=" + cn; -/// var body = { -/// type: "cap", -/// size : 10 +/// var body = { +/// type: "cap", +/// size : 10 /// }; /// /// var response = logCurlRequest('POST', url, JSON.stringify(body)); @@ -292,7 +288,7 @@ function get_api_index (req, res) { /// /// - *type*: must be equal to *"geo"*. /// -/// - *fields*: A list with one or two attribute paths. +/// - *fields*: A list with one or two attribute paths. /// /// If it is a list with one attribute path *location*, then a geo-spatial /// index on all documents is created using *location* as path to the @@ -310,12 +306,12 @@ function get_api_index (req, res) { /// /// - *geoJson*: If a geo-spatial index on a *location* is constructed /// and *geoJson* is *true*, then the order within the list is longitude -/// followed by latitude. This corresponds to the format described in +/// followed by latitude. This corresponds to the format described in /// http://geojson.org/geojson-spec.html#positions /// /// - *constraint*: If *constraint* is *true*, then a geo-spatial -/// constraint is created. The constraint is a non-unique variant of the index. -/// **Note**: It is also possible to set the *unique* attribute instead of +/// constraint is created. The constraint is a non-unique variant of the index. +/// **Note**: It is also possible to set the *unique* attribute instead of /// the *constraint* attribute. /// /// - *ignoreNull*: If a geo-spatial constraint is created and @@ -332,7 +328,7 @@ function get_api_index (req, res) { /// /// @RESTRETURNCODE{201} /// If the index does not already exist and could be created, then a *HTTP 201* -/// is returned. +/// is returned. /// /// @RESTRETURNCODE{404} /// If the *collection-name* is unknown, then a *HTTP 404* is returned. @@ -410,7 +406,7 @@ function get_api_index (req, res) { /// /// @RESTRETURNCODE{201} /// If the index does not already exist and could be created, then a *HTTP 201* -/// is returned. +/// is returned. /// /// @RESTRETURNCODE{400} /// If the collection already contains documents and you try to create a unique @@ -545,7 +541,7 @@ function get_api_index (req, res) { /// /// - *type*: must be equal to *"fulltext"*. /// -/// - *fields*: A list of attribute names. Currently, the list is limited +/// - *fields*: A list of attribute names. Currently, the list is limited /// to exactly one attribute, so the value of *fields* should look like /// this for example: *[ "text" ]*. /// @@ -635,9 +631,9 @@ function get_api_index (req, res) { /// db._create(cn, { waitForSync: true }); /// /// var url = "/_api/index?collection=" + cn; -/// var body = '{ ' + -/// '"type" : "bitarray", ' + -/// '"unique" : false, ' + +/// var body = '{ ' + +/// '"type" : "bitarray", ' + +/// '"unique" : false, ' + /// '"fields" : [ "x", [0,1,[]], "y", ["a","b",[]] ] ' + /// '}'; /// @@ -675,26 +671,26 @@ function get_api_index (req, res) { /// /// Most indexes (a notable exception being the cap constraint) require the /// list of attributes to be indexed in the *fields* attribute of the index -/// details. Depending on the index type, a single attribute or multiple -/// attributes may be indexed. -/// +/// details. Depending on the index type, a single attribute or multiple +/// attributes may be indexed. +/// /// Indexing system attributes such as *_id*, *_key*, *_from*, and *_to* -/// is not supported by any index type. Manually creating an index that +/// is not supported by any index type. Manually creating an index that /// relies on any of these attributes is unsupported. /// /// Some indexes can be created as unique or non-unique variants. Uniqueness /// can be controlled for most indexes by specifying the *unique* in the -/// index details. Setting it to *true* will create a unique index. +/// index details. Setting it to *true* will create a unique index. /// Setting it to *false* or omitting the *unique* attribute will /// create a non-unique index. /// -/// **Note**: The following index types do not support uniqueness, and using +/// **Note**: The following index types do not support uniqueness, and using /// the *unique* attribute with these types may lead to an error: /// - cap constraints /// - fulltext indexes /// - bitarray indexes /// -/// **Note**: Unique indexes on non-shard keys are not supported in a +/// **Note**: Unique indexes on non-shard keys are not supported in a /// cluster. /// /// @RESTRETURNCODES @@ -741,7 +737,7 @@ function post_api_index (req, res) { body.collection = name; } - // fill "unique" attribute from "constraint" attribute to be downward-compatible + // fill "unique" attribute from "constraint" attribute to be downward-compatible // with old geo index API if (body.hasOwnProperty("constraint") && ! body.hasOwnProperty("unique")) { body.unique = body.constraint; @@ -749,7 +745,7 @@ function post_api_index (req, res) { // rewrite bitarray fields if (body.type === "bitarray") { - if (typeof body.fields === "object" && + if (typeof body.fields === "object" && Array.isArray(body.fields) && body.fields.length > 0) { if (! Array.isArray(body.fields[0])) { @@ -762,7 +758,7 @@ function post_api_index (req, res) { } } - // create the index + // create the index var index = collection.ensureIndex(body); if (index.isNewlyCreated) { actions.resultOk(req, res, actions.HTTP_CREATED, index); @@ -845,7 +841,6 @@ function delete_api_index (req, res) { actions.defineHttp({ url : API, - context : "api", callback : function (req, res) { try { @@ -868,11 +863,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-open.js b/js/actions/api-open.js index 7cb647dc73..3dd5e2a4dc 100644 --- a/js/actions/api-open.js +++ b/js/actions/api-open.js @@ -10,7 +10,7 @@ /// /// DISCLAIMER /// -/// Copyright 2014 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -24,7 +24,7 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Copyright 2014, triAGENS GmbH, Cologne, Germany @@ -42,8 +42,7 @@ var console = require("console"); //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_open/cerberus", - context : "admin", + url: "_open/cerberus", prefix : true, callback : function (req, res) { @@ -55,7 +54,7 @@ actions.defineHttp({ suffix = suffix.concat(req.suffix); req.suffix = suffix; - + actions.routeRequest(req, res); } }); @@ -66,5 +65,5 @@ actions.defineHttp({ // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-query.js b/js/actions/api-query.js index 600299a481..352cda5719 100644 --- a/js/actions/api-query.js +++ b/js/actions/api-query.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -38,11 +39,6 @@ var PARSE = require("internal").AQL_PARSE; // --SECTION-- global variables // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSF_post_api_query /// @brief parse a query and return information about it @@ -100,9 +96,9 @@ var PARSE = require("internal").AQL_PARSE; function post_api_query (req, res) { if (req.suffix.length !== 0) { - actions.resultNotFound(req, - res, - arangodb.ERROR_HTTP_NOT_FOUND, + actions.resultNotFound(req, + res, + arangodb.ERROR_HTTP_NOT_FOUND, arangodb.errors.ERROR_HTTP_NOT_FOUND.message); return; } @@ -130,18 +126,17 @@ function post_api_query (req, res) { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief query actions gateway +/// @brief query actions gateway //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_api/query", - context : "api", + url: "_api/query", callback : function (req, res) { try { switch (req.requestType) { - case actions.POST: - post_api_query(req, res); + case actions.POST: + post_api_query(req, res); break; default: @@ -154,11 +149,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-simple.js b/js/actions/api-simple.js index 20b44eabd8..0d6ce2a710 100644 --- a/js/actions/api-simple.js +++ b/js/actions/api-simple.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Achim Brandt +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -34,18 +35,13 @@ var ERRORS = require("internal").errors; var API = "_api/simple/"; -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_put_api_simple_by_example_hash -/// @brief returns all documents of a collection matching a given example, +/// @brief returns all documents of a collection matching a given example, /// using a specific hash index /// /// @RESTHEADER{PUT /_api/simple/by-example-hash, Hash index} @@ -92,7 +88,7 @@ var API = "_api/simple/"; //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_put_api_simple_by_example_skiplist -/// @brief returns all documents of a collection matching a given example, +/// @brief returns all documents of a collection matching a given example, /// using a specific skiplist index /// /// @RESTHEADER{PUT /_api/simple/by-example-skiplist, Skiplist index} @@ -109,7 +105,7 @@ var API = "_api/simple/"; /// /// - *collection*: The name of the collection to query. /// -/// - *index*: The id of the index to be used for the query. The index must +/// - *index*: The id of the index to be used for the query. The index must /// exist and must be of type *skiplist*. /// /// - *example*: an example document. The example must contain a value for each @@ -139,7 +135,7 @@ var API = "_api/simple/"; //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_put_api_simple_by_example_bitarray -/// @brief returns all documents of a collection matching a given example, +/// @brief returns all documents of a collection matching a given example, /// using a specific bitarray index /// /// @RESTHEADER{PUT /_api/simple/by-example-bitarray, Bitarray index} @@ -156,7 +152,7 @@ var API = "_api/simple/"; /// /// - *collection*: The name of the collection to query. /// -/// - *index*: The id of the index to be used for the query. The index must +/// - *index*: The id of the index to be used for the query. The index must /// exist and must be of type *bitarray*. /// /// - *example*: an example document. The example must contain a value for each @@ -186,7 +182,7 @@ var API = "_api/simple/"; //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_put_api_simple_by_condition_skiplist -/// @brief returns all documents of a collection matching a given condition, +/// @brief returns all documents of a collection matching a given condition, /// using a specific skiplist index /// /// @RESTHEADER{PUT /_api/simple/by-condition-skiplist,Query by-condition using Skiplist index} @@ -203,11 +199,11 @@ var API = "_api/simple/"; /// /// - *collection*: The name of the collection to query. /// -/// - *index*: The id of the index to be used for the query. The index must +/// - *index*: The id of the index to be used for the query. The index must /// exist and must be of type *skiplist*. /// -/// - *condition*: the condition which all returned documents shall satisfy. -/// Conditions must be specified for all indexed attributes. +/// - *condition*: the condition which all returned documents shall satisfy. +/// Conditions must be specified for all indexed attributes. /// /// - *skip*: The number of documents to skip in the query. (optional) /// @@ -233,7 +229,7 @@ var API = "_api/simple/"; //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_put_api_simple_by_condition_bitarray -/// @brief returns all documents of a collection matching a given condition, +/// @brief returns all documents of a collection matching a given condition, /// using a specific bitarray index /// /// @RESTHEADER{PUT /_api/simple/by-condition-bitarray, Query by-condition using bitarray index} @@ -250,11 +246,11 @@ var API = "_api/simple/"; /// /// - *collection*: The name of the collection to query. /// -/// - *index*: The id of the index to be used for the query. The index must +/// - *index*: The id of the index to be used for the query. The index must /// exist and must be of type *bitarray*. /// -/// - *condition*: the condition which all returned documents shall satisfy. -/// Conditions must be specified for all indexed attributes. +/// - *condition*: the condition which all returned documents shall satisfy. +/// Conditions must be specified for all indexed attributes. /// /// - *skip*: The number of documents to skip in the query. (optional) /// @@ -282,7 +278,7 @@ var API = "_api/simple/"; //////////////////////////////////////////////////////////////////////////////// /// @brief create a cursor response //////////////////////////////////////////////////////////////////////////////// - + function createCursorResponse (req, res, cursor) { actions.resultCursor(req, res, cursor, undefined, { countRequested: true }); } @@ -293,10 +289,9 @@ function createCursorResponse (req, res, cursor) { function setupIndexQuery (name, func, isExampleQuery) { actions.defineHttp({ - url : API + name, - context : "api", + url: API + name, - callback : function (req, res) { + callback: function (req, res) { try { var body = actions.getJsonBody(req, res); @@ -326,7 +321,7 @@ function setupIndexQuery (name, func, isExampleQuery) { actions.badParameter(req, res, "example"); return; } - + result = collection[func](index, body.example); } else { @@ -334,23 +329,23 @@ function setupIndexQuery (name, func, isExampleQuery) { actions.badParameter(req, res, "condition"); return; } - + result = collection[func](index, body.condition); } - + if (skip > 0) { result.skip(skip); } if (limit !== undefined && limit !== null) { result.limit(limit); } - + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } catch (err) { actions.resultException(req, res, err, undefined, false); - } + } } }); } @@ -442,7 +437,7 @@ setupIndexQueries(); /// logJsonResponse(response); /// db._drop(cn); /// @END_EXAMPLE_ARANGOSH_RUN -/// +/// /// Using a *batchSize* value /// /// @EXAMPLE_ARANGOSH_RUN{RestSimpleAllBatch} @@ -469,8 +464,7 @@ setupIndexQueries(); //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "all", - context : "api", + url: API + "all", callback : function (req, res) { try { @@ -572,10 +566,9 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "any", - context : "api", + url: API + "any", - callback : function (req, res) { + callback: function (req, res) { try { var body = actions.getJsonBody(req, res); @@ -620,7 +613,7 @@ actions.defineHttp({ /// /// The default will find at most 100 documents near the given coordinate. The /// returned list is sorted according to the distance, with the nearest document -/// being first in the list. If there are near documents of equal distance, documents +/// being first in the list. If there are near documents of equal distance, documents /// are chosen randomly from this set until the limit is reached. /// /// In order to use the *near* operator, a geo index must be defined for the @@ -675,7 +668,7 @@ actions.defineHttp({ /// products.save({ name : "Name/" + i + "/",loc: [ i, 0 ] }); /// } /// var url = "/_api/simple/near"; -/// var body = '{ ' + +/// var body = '{ ' + /// '"collection": "products", ' + /// '"latitude" : 0, ' + /// '"longitude" : 0, ' + @@ -703,8 +696,8 @@ actions.defineHttp({ /// products.save({ name : "Name/" + i + "/",loc: [ i, 0 ] }); /// } /// var url = "/_api/simple/near"; -/// var body = '{ ' + -/// '"collection": "products", ' + +/// var body = '{ ' + +/// '"collection": "products", ' + /// '"latitude" : 0, ' + /// '"longitude" : 0, ' + /// '"skip" : 1, ' + @@ -723,8 +716,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "near", - context : "api", + url: API + "near", callback : function (req, res) { try { @@ -900,8 +892,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "within", - context : "api", + url: API + "within", callback : function (req, res) { try { @@ -943,19 +934,19 @@ actions.defineHttp({ else { result = collection.geo({ id : geo }).within(latitude, longitude, radius); } - + if (skip !== null && skip !== undefined) { result = result.skip(skip); } - + if (limit !== null && limit !== undefined) { result = result.limit(limit); } - + if (distance !== null && distance !== undefined) { result = result.distance(distance); } - + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } @@ -980,7 +971,7 @@ actions.defineHttp({ /// This will find all documents from the collection that match the fulltext /// query specified in *query*. /// -/// In order to use the *fulltext* operator, a fulltext index must be defined +/// In order to use the *fulltext* operator, a fulltext index must be defined /// for the collection and the specified attribute. /// /// The call expects a JSON object as body with the following attributes: @@ -1037,8 +1028,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "fulltext", - context : "api", + url: API + "fulltext", callback : function (req, res) { try { @@ -1056,7 +1046,7 @@ actions.defineHttp({ var skip = body.skip; var attribute = body.attribute; var query = body.query; - var iid = body.index || undefined; + var iid = body.index || undefined; var name = body.collection; var collection = db._collection(name); @@ -1071,15 +1061,15 @@ actions.defineHttp({ } else { var result = collection.fulltext(attribute, query, iid); - + if (skip !== null && skip !== undefined) { result = result.skip(skip); } - + if (limit !== null && limit !== undefined) { result = result.limit(limit); } - + createCursorResponse(req, res, CREATE_CURSOR(result.toArray(), true, body.batchSize, body.ttl)); } } @@ -1197,8 +1187,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "by-example", - context : "api", + url: API + "by-example", callback : function (req, res) { try { @@ -1265,7 +1254,7 @@ actions.defineHttp({ /// - *example*: The example document. /// /// Returns a result containing the document or *HTTP 404* if no -/// document matched the example. +/// document matched the example. /// /// If more than one document in the collection matches the specified example, only /// one of these documents will be returned, and it is undefined which of the matching @@ -1331,13 +1320,12 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "first-example", - context : "api", + url: API + "first-example", callback : function (req, res) { try { var body = actions.getJsonBody(req, res); - + if (body === undefined) { return; } @@ -1358,7 +1346,7 @@ actions.defineHttp({ } else { var result = collection.byExample(example).limit(1); - + if (result.hasNext()) { actions.resultOk(req, res, actions.HTTP_OK, { document : result.next() }); } @@ -1394,13 +1382,13 @@ actions.defineHttp({ /// /// The request body must be a JSON object with the following attributes: /// - *collection*: the name of the collection -/// -/// - *count*: the number of documents to return at most. Specifiying count is +/// +/// - *count*: the number of documents to return at most. Specifiying count is /// optional. If it is not specified, it defaults to 1. /// -/// Note: this method is not supported for sharded collections with more than +/// Note: this method is not supported for sharded collections with more than /// one shard. -/// +/// /// @RESTRETURNCODES /// /// @RESTRETURNCODE{200} @@ -1461,8 +1449,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "first", - context : "api", + url: API + "first", callback : function (req, res) { try { @@ -1511,8 +1498,8 @@ actions.defineHttp({ /// /// The request body must be a JSON object with the following attributes: /// - *collection*: the name of the collection -/// -/// - *count*: the number of documents to return at most. Specifiying count is +/// +/// - *count*: the number of documents to return at most. Specifiying count is /// optional. If it is not specified, it defaults to 1. /// /// If the *count* argument is not supplied, the result is the "latest" document @@ -1520,7 +1507,7 @@ actions.defineHttp({ /// /// Note: this method is not supported for sharded collections with more than /// one shard. -/// +/// /// @RESTRETURNCODES /// /// @RESTRETURNCODE{200} @@ -1581,8 +1568,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "last", - context : "api", + url: API + "last", callback : function (req, res) { try { @@ -1685,8 +1671,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "range", - context : "api", + url: API + "range", callback : function (req, res) { try { @@ -1751,8 +1736,8 @@ actions.defineHttp({ /// /// @RESTDESCRIPTION /// -/// This will find all documents in the collection that match the specified -/// example object. +/// This will find all documents in the collection that match the specified +/// example object. /// /// The call expects a JSON object as body with the following attributes: /// @@ -1763,20 +1748,20 @@ actions.defineHttp({ /// /// - options: an json object which can contains following attributes: /// -/// - *waitForSync*: if set to true, then all removal operations will +/// - *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. /// -/// - *limit*: an optional value that determines how many documents to +/// - *limit*: an optional value that determines how many documents to /// delete at most. If *limit* is specified but is less than the number -/// of documents in the collection, it is undefined which of the documents +/// of documents in the collection, it is undefined which of the documents /// will be deleted. /// -/// Note: the *limit* attribute is not supported on sharded collections. +/// Note: the *limit* attribute is not supported on sharded collections. /// Using it will result in an error. -/// The options attributes waitForSync and limit can given yet without -/// an ecapsulation into a json object. but this may be deprecated in future -/// versions of arango +/// The options attributes waitForSync and limit can given yet without +/// an ecapsulation into a json object. but this may be deprecated in future +/// versions of arango /// /// Returns the number of documents that were deleted. /// @@ -1823,7 +1808,7 @@ actions.defineHttp({ /// products.save({ "i": 1}); /// products.save({ "a": { "k": 2, "j": 2 }, "i": 1}); /// var url = "/_api/simple/remove-by-example"; -/// var body = '{ "collection": "products", "example" : { "a" : { "j" : 1 } },' + +/// var body = '{ "collection": "products", "example" : { "a" : { "j" : 1 } },' + /// '"waitForSync": true, "limit": 2 }'; /// /// var response = logCurlRequest('PUT', url, body); @@ -1860,13 +1845,12 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "remove-by-example", - context : "api", + url: API + "remove-by-example", callback : function (req, res) { try { var body = actions.getJsonBody(req, res); - + if (body === undefined) { return; } @@ -1905,7 +1889,7 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// /// @startDocuBlock JSA_put_api_simple_replace_by_example -/// @brief replaces the body of all documents of a collection that match an +/// @brief replaces the body of all documents of a collection that match an /// example /// /// @RESTHEADER{PUT /_api/simple/replace-by-example, Replace documents by example} @@ -1915,10 +1899,10 @@ actions.defineHttp({ /// /// @RESTDESCRIPTION /// -/// This will find all documents in the collection that match the specified +/// 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 *_id*, *_key*, -/// *_from*, *_to* etc. cannot be replaced. +/// *_from*, *_to* etc. cannot be replaced. /// /// The call expects a JSON object as body with the following attributes: /// @@ -1932,20 +1916,20 @@ actions.defineHttp({ /// /// - *options*: an json object which can contain following attributes /// -/// - *waitForSync*: if set to true, then all removal operations will +/// - *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. /// -/// - *limit*: an optional value that determines how many documents to +/// - *limit*: an optional value that determines how many documents to /// replace at most. If *limit* is specified but is less than the number -/// of documents in the collection, it is undefined which of the documents +/// of documents in the collection, it is undefined which of the documents /// will be replaced. /// -/// Note: the *limit* attribute is not supported on sharded collections. +/// Note: the *limit* attribute is not supported on sharded collections. /// Using it will result in an error. -/// The options attributes waitForSync and limit can given yet without -/// an ecapsulation into a json object. but this may be deprecated in future -/// versions of arango +/// The options attributes waitForSync and limit can given yet without +/// an ecapsulation into a json object. but this may be deprecated in future +/// versions of arango /// /// Returns the number of documents that were replaced. /// @@ -2015,13 +1999,12 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "replace-by-example", - context : "api", + url: API + "replace-by-example", callback : function (req, res) { try { var body = actions.getJsonBody(req, res); - + if (body === undefined) { return; } @@ -2074,10 +2057,10 @@ actions.defineHttp({ /// /// @RESTDESCRIPTION /// -/// This will find all documents in the collection that match the specified +/// 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 *_id*, *_key*, -/// *_from*, *_to* etc. cannot be replaced. +/// *_from*, *_to* etc. cannot be replaced. /// /// The call expects a JSON object as body with the following attributes: /// @@ -2093,20 +2076,20 @@ actions.defineHttp({ /// /// - *keepNull*: This parameter can be used to modify the behavior when /// handling *null* values. Normally, *null* values are stored in the -/// database. By setting the *keepNull* parameter to *false*, this -/// behavior can be changed so that all attributes in *data* with *null* +/// database. By setting the *keepNull* parameter to *false*, this +/// behavior can be changed so that all attributes in *data* with *null* /// values will be removed from the updated document. /// -/// - *waitForSync*: if set to true, then all removal operations will +/// - *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. /// -/// - *limit*: an optional value that determines how many documents to +/// - *limit*: an optional value that determines how many documents to /// update at most. If *limit* is specified but is less than the number -/// of documents in the collection, it is undefined which of the documents +/// of documents in the collection, it is undefined which of the documents /// will be updated. /// -/// Note: the *limit* attribute is not supported on sharded collections. +/// Note: the *limit* attribute is not supported on sharded collections. /// Using it will result in an error. /// /// Returns the number of documents that were updated. @@ -2137,7 +2120,7 @@ actions.defineHttp({ /// products.save({ "i": 1}); /// products.save({ "a": { "k": 2, "j": 2 }, "i": 1}); /// var url = "/_api/simple/update-by-example"; -/// var body = '{ ' + +/// var body = '{ ' + /// '"collection": "products", ' + /// '"example" : { "a" : { "j" : 1 } }, ' + /// '"newValue" : { "a" : { "j" : 22 } }, ' + @@ -2161,11 +2144,11 @@ actions.defineHttp({ /// products.save({ "i": 1}); /// products.save({ "a": { "k": 2, "j": 2 }, "i": 1}); /// var url = "/_api/simple/update-by-example"; -/// var body = '{ ' + +/// var body = '{ ' + /// '"collection": "products", ' + /// '"example" : { "a" : { "j" : 1 } }, ' + /// '"newValue" : { "a" : { "j" : 22 } }, ' + -/// '"options" : { "limit" : 3, "waitForSync": true } ' + +/// '"options" : { "limit" : 3, "waitForSync": true } ' + /// '}'; /// /// var response = logCurlRequest('PUT', url, body); @@ -2179,13 +2162,12 @@ actions.defineHttp({ //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : API + "update-by-example", - context : "api", + url: API + "update-by-example", callback : function (req, res) { try { var body = actions.getJsonBody(req, res); - + if (body === undefined) { return; } @@ -2217,8 +2199,8 @@ actions.defineHttp({ limit = body.limit || undefined; options = {waitForSync: waitForSync, keepNull: keepNull, limit: limit}; } - var result = collection.updateByExample(example, - newValue, + var result = collection.updateByExample(example, + newValue, options); actions.resultOk(req, res, actions.HTTP_OK, { updated: result }); } @@ -2230,11 +2212,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-structure.js b/js/actions/api-structure.js index 45a1c48587..01cbce5032 100644 --- a/js/actions/api-structure.js +++ b/js/actions/api-structure.js @@ -15,7 +15,7 @@ /// /// DISCLAIMER /// -/// Copyright 2013 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -29,9 +29,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -49,10 +50,6 @@ var checkedIndex = false; // --SECTION-- private functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// /* Configuration example document: @@ -184,7 +181,7 @@ Configuration example document: "arrayTypes": { <- Array type definitions "number_list_type": { <- Name of type - "type": "number", + "type": "number", "formatter": { "default": { "args": { @@ -232,7 +229,7 @@ Configuration example document: "objectTypes": { <- Object type definitions "complex_type1": { <- Name of type "attributes": { <- Attributes of the object type - "aNumber": { + "aNumber": { "type": "number" }, "aList": { @@ -273,7 +270,7 @@ function stringToBoolean (string){ if (undefined === string || null === string) { return false; } - + switch(string.toLowerCase()){ case "true": case "yes": case "1": return true; case "false": case "no": case "0": case null: return false; @@ -282,36 +279,36 @@ function stringToBoolean (string){ } //////////////////////////////////////////////////////////////////////////////// -/// @brief returns a (OK) result +/// @brief returns a (OK) result //////////////////////////////////////////////////////////////////////////////// -function resultOk (req, res, httpReturnCode, keyvals, headers) { +function resultOk (req, res, httpReturnCode, keyvals, headers) { 'use strict'; res.responseCode = httpReturnCode; res.contentType = "application/json; charset=utf-8"; - + if (undefined !== keyvals) { res.body = JSON.stringify(keyvals); } if (headers !== undefined && headers !== null) { - res.headers = headers; + res.headers = headers; } } //////////////////////////////////////////////////////////////////////////////// -/// @brief returns a (error) result +/// @brief returns a (error) result //////////////////////////////////////////////////////////////////////////////// -function resultError (req, res, httpReturnCode, errorNum, errorMessage, keyvals, headers) { +function resultError (req, res, httpReturnCode, errorNum, errorMessage, keyvals, headers) { 'use strict'; var i; res.responseCode = httpReturnCode; res.contentType = "application/json; charset=utf-8"; - + var result = {}; if (keyvals !== undefined) { @@ -328,20 +325,20 @@ function resultError (req, res, httpReturnCode, errorNum, errorMessage, keyvals, result.errorNum = errorMessage.errorNum; } else { - result.errorNum = errorNum; + result.errorNum = errorNum; } if (undefined !== errorMessage.errorMessage) { result.errorMessage = errorMessage.errorMessage; } else { - result.errorMessage = errorMessage; + result.errorMessage = errorMessage; } - - + + res.body = JSON.stringify(result); if (headers !== undefined && headers !== null) { - res.headers = headers; + res.headers = headers; } } @@ -350,39 +347,39 @@ function resultError (req, res, httpReturnCode, errorNum, errorMessage, keyvals, /// @brief returns true if a "if-match" or "if-none-match" errer happens //////////////////////////////////////////////////////////////////////////////// -function matchError (req, res, doc) { +function matchError (req, res, doc) { if (req.headers["if-none-match"] !== undefined) { if (doc._rev === req.headers["if-none-match"]) { - // error + // error res.responseCode = actions.HTTP_NOT_MODIFIED; res.contentType = "application/json; charset=utf-8"; res.body = ''; - res.headers = {}; + res.headers = {}; return true; } - } + } if (req.headers["if-match"] !== undefined) { if (doc._rev !== req.headers["if-match"]) { // error - resultError(req, res, actions.HTTP_PRECONDITION_FAILED, - arangodb.ERROR_ARANGO_CONFLICT, "wrong revision", + resultError(req, res, actions.HTTP_PRECONDITION_FAILED, + arangodb.ERROR_ARANGO_CONFLICT, "wrong revision", {'_id': doc._id, '_rev': doc._rev, '_key': doc._key}); return true; } - } + } var rev = req.parameters.rev; if (rev !== undefined) { if (doc._rev !== rev) { // error - resultError(req, res, actions.HTTP_PRECONDITION_FAILED, - arangodb.ERROR_ARANGO_CONFLICT, "wrong revision", + resultError(req, res, actions.HTTP_PRECONDITION_FAILED, + arangodb.ERROR_ARANGO_CONFLICT, "wrong revision", {'_id': doc._id, '_rev': doc._rev, '_key': doc._key}); return true; } - } + } return false; } @@ -392,10 +389,10 @@ function matchError (req, res, doc) { //////////////////////////////////////////////////////////////////////////////// function getCollectionByRequest(req, res) { - + if (req.suffix.length === 0) { // GET /_api/structure (missing collection) - resultError(req, res, actions.HTTP_BAD, + resultError(req, res, actions.HTTP_BAD, arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND, "collection not found"); return; } @@ -410,13 +407,13 @@ function getCollectionByRequest(req, res) { //////////////////////////////////////////////////////////////////////////////// function getOverwritePolicy(req) { - + var policy = req.parameters.policy; - + if (undefined !== policy && "error" === policy) { return false; } - + return true; } @@ -424,7 +421,7 @@ function getOverwritePolicy(req) { /// @brief returns the overwite policy //////////////////////////////////////////////////////////////////////////////// -function getKeepNull(req) { +function getKeepNull(req) { return stringToBoolean(req.parameters.keepNull); } @@ -432,11 +429,11 @@ function getKeepNull(req) { /// @brief returns wait for sync //////////////////////////////////////////////////////////////////////////////// -function getWaitForSync(req, collection) { +function getWaitForSync(req, collection) { if (collection.properties().waitForSync) { return true; } - + return stringToBoolean(req.parameters.waitForSync); } @@ -447,13 +444,13 @@ function getWaitForSync(req, collection) { function saveDocument(req, res, collection, document) { var doc; var waitForSync = getWaitForSync(req, collection); - + try { doc = collection.save(document, waitForSync); } catch(err) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, err); return; } @@ -462,16 +459,16 @@ function saveDocument(req, res, collection, document) { "Etag" : doc._rev, "location" : "/_api/structure/" + doc._id }; - + if (req.hasOwnProperty('compatibility') && req.compatibility >= 10400) { // 1.4+ style location header headers.location = "/_db/" + encodeURIComponent(arangodb.db._name()) + headers.location; } var returnCode = waitForSync ? actions.HTTP_CREATED : actions.HTTP_ACCEPTED; - + doc.error = false; - + resultOk(req, res, returnCode, doc, headers); } @@ -483,22 +480,22 @@ function replaceDocument(req, res, collection, oldDocument, newDocument) { var doc; var waitForSync = getWaitForSync(req, collection); var overwrite = getOverwritePolicy(req); - - if (! overwrite && - undefined !== newDocument._rev && + + if (! overwrite && + undefined !== newDocument._rev && oldDocument._rev !== newDocument._rev) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, "wrong version"); - return; + return; } try { doc = collection.replace(oldDocument, newDocument, true, waitForSync); } catch(err) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, err); return; } @@ -508,7 +505,7 @@ function replaceDocument(req, res, collection, oldDocument, newDocument) { }; var returnCode = waitForSync ? actions.HTTP_CREATED : actions.HTTP_ACCEPTED; - + resultOk(req, res, returnCode, doc, headers); } @@ -521,22 +518,22 @@ function patchDocument(req, res, collection, oldDocument, newDocument) { var waitForSync = getWaitForSync(req, collection); var overwrite = getOverwritePolicy(req); var keepNull = getKeepNull(req); - - if (!overwrite && - undefined !== newDocument._rev && + + if (!overwrite && + undefined !== newDocument._rev && oldDocument._rev !== newDocument._rev) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, "wrong version"); - return; + return; } - + try { doc = collection.update(oldDocument, newDocument, true, keepNull, waitForSync); } catch(err) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, err); return; } @@ -546,7 +543,7 @@ function patchDocument(req, res, collection, oldDocument, newDocument) { }; var returnCode = waitForSync ? actions.HTTP_CREATED : actions.HTTP_ACCEPTED; - + resultOk(req, res, returnCode, doc, headers); } @@ -555,10 +552,10 @@ function patchDocument(req, res, collection, oldDocument, newDocument) { //////////////////////////////////////////////////////////////////////////////// function getDocumentByRequest(req, res, collection) { - + if (req.suffix.length < 2) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_ARANGO_DOCUMENT_HANDLE_BAD, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_ARANGO_DOCUMENT_HANDLE_BAD, "expecting GET /_api/structure/"); return; } @@ -567,11 +564,11 @@ function getDocumentByRequest(req, res, collection) { return collection.document(req.suffix[1]); } catch (err) { - resultError(req, res, actions.HTTP_NOT_FOUND, - arangodb.ERROR_ARANGO_DOCUMENT_NOT_FOUND, - "document /_api/structure/" + req.suffix[0] + "/" + req.suffix[1] + - " not found"); - } + resultError(req, res, actions.HTTP_NOT_FOUND, + arangodb.ERROR_ARANGO_DOCUMENT_NOT_FOUND, + "document /_api/structure/" + req.suffix[0] + "/" + req.suffix[1] + + " not found"); + } } //////////////////////////////////////////////////////////////////////////////// @@ -595,11 +592,11 @@ function getTypes (structure) { "arrayTypes" : {}, "objectTypes" : {} }; - + if (undefined !== structure.arrayTypes) { types.arrayTypes = structure.arrayTypes; } - + if (undefined !== structure.objectTypes) { types.objectTypes = structure.objectTypes; } @@ -614,7 +611,7 @@ function getLang (req) { if (undefined !== req.parameters.lang) { return req.parameters.lang; } - + return null; } @@ -622,7 +619,7 @@ function getLang (req) { /// @brief returns formatter //////////////////////////////////////////////////////////////////////////////// -function selectFormatter (formatter1, formatter2, lang) { +function selectFormatter (formatter1, formatter2, lang) { var formatter = formatter1; if (undefined === formatter1 || JSON.stringify(formatter1) === "{}") { formatter = formatter2; @@ -632,10 +629,10 @@ function selectFormatter (formatter1, formatter2, lang) { if (undefined === lang) { return formatter[DEFAULT_KEY]; } - + if (undefined === formatter[lang]) { return formatter[DEFAULT_KEY]; - } + } return formatter[lang]; } } @@ -644,7 +641,7 @@ function selectFormatter (formatter1, formatter2, lang) { /// @brief returns the parser //////////////////////////////////////////////////////////////////////////////// -function selectParser (parser1, parser2, lang) { +function selectParser (parser1, parser2, lang) { var parser = parser1; if (undefined === parser1 || JSON.stringify(parser1) === "{}") { parser = parser2; @@ -654,10 +651,10 @@ function selectParser (parser1, parser2, lang) { if (undefined === lang) { return parser[DEFAULT_KEY]; } - + if (undefined === parser[lang]) { return parser[DEFAULT_KEY]; - } + } return parser[lang]; } } @@ -666,18 +663,18 @@ function selectParser (parser1, parser2, lang) { /// @brief call a module function //////////////////////////////////////////////////////////////////////////////// -function callModuleFunction(value, moduleName, functionName, functionArgs) { +function callModuleFunction(value, moduleName, functionName, functionArgs) { if (undefined === moduleName) { return value; } - + try { var formatModule = require(moduleName); if (formatModule.hasOwnProperty(functionName)) { // call the function - return formatModule[functionName].call(null, value, functionArgs); + return formatModule[functionName].call(null, value, functionArgs); } - } + } catch (err) { // could not load module console.warn("module error for module: " + moduleName + " error: " + err); @@ -685,10 +682,10 @@ function callModuleFunction(value, moduleName, functionName, functionArgs) { } // function not found - console.warn("module function '" + functionName + "' of module '" + moduleName + console.warn("module function '" + functionName + "' of module '" + moduleName + "' not found."); - - return value; + + return value; } //////////////////////////////////////////////////////////////////////////////// @@ -703,38 +700,38 @@ function formatValue (value, structure, types, lang) { try { var type = types.predefinedTypes[structure.type]; if (type) { - //console.warn("predefined type found: " + structure.type); - - section = selectFormatter(structure.formatter, + //console.warn("predefined type found: " + structure.type); + + section = selectFormatter(structure.formatter, types.predefinedTypes[structure.type].formatter, lang); - + if (undefined === section) { return value; } - - return callModuleFunction(value, - section.module, - section['do'], + + return callModuleFunction(value, + section.module, + section['do'], section.args); } - + // array types type = types.arrayTypes[structure.type]; if (type) { //console.warn("array type found: " + structure.type); - + // check for array formatter - section = selectFormatter(structure.formatter, undefined, lang); + section = selectFormatter(structure.formatter, undefined, lang); if (undefined !== section) { - return callModuleFunction(value, - section.module, - section['do'], + return callModuleFunction(value, + section.module, + section['do'], section.args); } - + // format each element result = []; - + if(value instanceof Array) { for (key = 0; key < value.length; ++key) { result[key] = formatValue(value[key], type, types, lang); @@ -742,8 +739,8 @@ function formatValue (value, structure, types, lang) { } return result; } - - // object types + + // object types type = types.objectTypes[structure.type]; if (type) { //console.warn("object type found: " + structure.type); @@ -751,11 +748,11 @@ function formatValue (value, structure, types, lang) { // TODO check type of value // check for object formatter - section = selectFormatter(structure.formatter, undefined, lang); + section = selectFormatter(structure.formatter, undefined, lang); if (undefined !== section) { - return callModuleFunction(value, - section.module, - section['do'], + return callModuleFunction(value, + section.module, + section['do'], section.args); } @@ -764,14 +761,14 @@ function formatValue (value, structure, types, lang) { // no attributes return null; } - + // TODO check type of attribute - + // format each property result = {}; for (key in attributes) { if (attributes.hasOwnProperty(key)) { - if (value.hasOwnProperty(key)) { + if (value.hasOwnProperty(key)) { var subStructure = attributes[key]; if (undefined === subStructure) { result[key] = value[key]; @@ -792,7 +789,7 @@ function formatValue (value, structure, types, lang) { //console.warn("error = " + err); } - return value; + return value; } //////////////////////////////////////////////////////////////////////////////// @@ -804,16 +801,16 @@ function parseValue (value, structure, types, lang) { var key; var section; - // console.warn("in parseValue"); + // console.warn("in parseValue"); try { var type = types.predefinedTypes[structure.type]; if (type) { - // console.warn("predefined type found: " + structure.type); - + // console.warn("predefined type found: " + structure.type); + // TODO check type of value - - section = selectParser(structure.parser, + + section = selectParser(structure.parser, types.predefinedTypes[structure.type].parser, lang); if (undefined === section) { @@ -821,30 +818,30 @@ function parseValue (value, structure, types, lang) { return value; } - var x = callModuleFunction(value, - section.module, - section['do'], + var x = callModuleFunction(value, + section.module, + section['do'], section.args); - + // console.warn("parsing " + value + " to " + x ); - + return x; } - + // array types type = types.arrayTypes[structure.type]; if (type) { //console.warn("array type found: " + structure.type); - + // check for array formatter - section = selectParser(structure.parser, undefined, lang); + section = selectParser(structure.parser, undefined, lang); if (undefined !== section) { - return callModuleFunction(value, - section.module, - section['do'], + return callModuleFunction(value, + section.module, + section['do'], section.args); } - + // parse each element result = []; if(value instanceof Array) { @@ -854,8 +851,8 @@ function parseValue (value, structure, types, lang) { } return result; } - - // object types + + // object types type = types.objectTypes[structure.type]; if (type) { //console.warn("object type found: " + structure.type); @@ -863,11 +860,11 @@ function parseValue (value, structure, types, lang) { // TODO check type of value // check for object parser - section = selectParser(structure.parser, undefined, lang); + section = selectParser(structure.parser, undefined, lang); if (undefined !== section) { - return callModuleFunction(value, - section.module, - section['do'], + return callModuleFunction(value, + section.module, + section['do'], section.args); } @@ -876,15 +873,15 @@ function parseValue (value, structure, types, lang) { // no attributes return null; } - + // TODO check type of attribute - + // parse each property result = {}; for (key in attributes) { if (attributes.hasOwnProperty(key)) { if (value.hasOwnProperty(key)) { - + var subStructure = attributes[key]; if (undefined === subStructure) { result[key] = value[key]; @@ -906,7 +903,7 @@ function parseValue (value, structure, types, lang) { //console.warn("error = " + err); } - return value; + return value; } //////////////////////////////////////////////////////////////////////////////// @@ -918,70 +915,70 @@ function validateValue (value, structure, types, lang) { var key; var validators; var v; - - //console.warn("in validateValue(): " + structure.type); + + //console.warn("in validateValue(): " + structure.type); try { var type = types.predefinedTypes[structure.type]; if (type) { - //console.warn("predefined type found: " + structure.type); - + //console.warn("predefined type found: " + structure.type); + // TODO check type of value - - validators = structure.validators; - if (undefined !== validators) { + + validators = structure.validators; + if (undefined !== validators) { for (key = 0; key < validators.length; ++key) { - //console.warn("call function: " + validators[key]['do']); - - result = callModuleFunction(value, - validators[key].module, - validators[key]['do'], + //console.warn("call function: " + validators[key]['do']); + + result = callModuleFunction(value, + validators[key].module, + validators[key]['do'], validators[key].args); - + if (!result) { return false; } - } + } } validators = types.predefinedTypes[structure.type].validators; - if (undefined !== validators) { + if (undefined !== validators) { for (key = 0; key < validators.length; ++key) { - //console.warn("call function: " + validators[key]['do']); - - result = callModuleFunction(value, - validators[key].module, - validators[key]['do'], + //console.warn("call function: " + validators[key]['do']); + + result = callModuleFunction(value, + validators[key].module, + validators[key]['do'], validators[key].args); - + if (!result) { return false; } - } + } } - return true; + return true; } - + // array types type = types.arrayTypes[structure.type]; if (type) { //console.warn("array type found: " + structure.type); - + // TODO check type of value - + // check for array validator - validators = structure.validators; - if (undefined !== validators) { + validators = structure.validators; + if (undefined !== validators) { for (key = 0; key < validators.length; ++key) { - - result = callModuleFunction(value, - validators[key].module, - validators[key]['do'], + + result = callModuleFunction(value, + validators[key].module, + validators[key]['do'], validators[key].args); - + if (!result) { return false; } @@ -998,8 +995,8 @@ function validateValue (value, structure, types, lang) { } return true; } - - // object types + + // object types type = types.objectTypes[structure.type]; if (type) { //console.warn("object type found: " + structure.type); @@ -1007,15 +1004,15 @@ function validateValue (value, structure, types, lang) { // TODO check type of value // check for object validator - validators = structure.validators; - if (undefined !== validators) { + validators = structure.validators; + if (undefined !== validators) { for (key = 0; key < validators.length; ++key) { - - result = callModuleFunction(value, - validators[key].module, - validators[key]['do'], + + result = callModuleFunction(value, + validators[key].module, + validators[key]['do'], validators[key].args); - + if (!result) { return false; } @@ -1027,20 +1024,20 @@ function validateValue (value, structure, types, lang) { // no attributes return true; } - + // validate each property for (key in attributes) { if (attributes.hasOwnProperty(key)) { - + if (value.hasOwnProperty(key)) { v = value[key]; } else { v = null; } - + var subStructure = attributes[key]; - + if (undefined !== subStructure) { if (!validateValue(v, subStructure, types, lang)) { return false; @@ -1048,7 +1045,7 @@ function validateValue (value, structure, types, lang) { } } } - + return true; } } @@ -1056,7 +1053,7 @@ function validateValue (value, structure, types, lang) { //console.warn("error = " + err); } - return false; + return false; } //////////////////////////////////////////////////////////////////////////////// @@ -1074,12 +1071,12 @@ function resultStructure (req, res, doc, structure, headers) { if (undefined !== req.parameters.format) { format = stringToBoolean(req.parameters.format); } - + if (structure.attributes !== undefined) { for (key in structure.attributes) { if (structure.attributes.hasOwnProperty(key)) { var value = doc[key]; - + // format value if (format) { result[key] = formatValue(value, structure.attributes[key], types, lang); @@ -1087,14 +1084,14 @@ function resultStructure (req, res, doc, structure, headers) { else { result[key] = value; } - } + } } } result._id = doc._id; result._rev = doc._rev; result._key = doc._key; - + resultOk(req, res, actions.HTTP_OK, result, headers); } @@ -1115,7 +1112,7 @@ function parseDocumentByStructure(req, res, structure, body, isPatch) { if (undefined !== req.parameters.format) { format = stringToBoolean(req.parameters.format); } - + for (key in structure.attributes) { if (structure.attributes.hasOwnProperty(key)) { value = body[key]; @@ -1144,8 +1141,8 @@ function parseDocumentByStructure(req, res, structure, body, isPatch) { } if (undefined !== body._key) { document._key = body._key; - } - + } + return document; } @@ -1159,8 +1156,8 @@ function saveDocumentByStructure(req, res, collection, structure, body) { saveDocument(req, res, collection, document); } catch(err) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, err); return; } @@ -1176,8 +1173,8 @@ function replaceDocumentByStructure(req, res, collection, structure, oldDocument replaceDocument(req, res, collection, oldDocument, document); } catch(err) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, err); return; } @@ -1193,26 +1190,17 @@ function patchDocumentByStructure(req, res, collection, structure, oldDocument, patchDocument(req, res, collection, oldDocument, document); } catch(err) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, err); return; } } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief reads a single document /// @@ -1226,7 +1214,7 @@ function patchDocumentByStructure(req, res, collection, structure, oldDocument, /// @RESTQUERYPARAM{rev,string,optional} /// You can conditionally select a document based on a target revision id by /// using the `rev` URL parameter. -/// +/// /// @RESTQUERYPARAM{lang,string,optional} /// Language of the data. /// @@ -1252,7 +1240,7 @@ function patchDocumentByStructure(req, res, collection, structure, oldDocument, /// handle and `_rev` containing the revision. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the document was found /// @@ -1271,7 +1259,7 @@ function patchDocumentByStructure(req, res, collection, structure, oldDocument, function get_api_structure(req, res) { var structure; - + var collection = getCollectionByRequest(req, res); if (undefined === collection) { return; @@ -1317,20 +1305,20 @@ function get_api_structure(req, res) { /// @RESTQUERYPARAM{rev,string,optional} /// You can conditionally select a document based on a target revision id by /// using the `rev` URL parameter. -/// +/// /// @RESTHEADERPARAMETERS /// /// @RESTHEADERPARAM{If-Match,string,optional} /// You can conditionally get a document based on a target revision id by /// using the `if-match` HTTP header. -/// +/// /// @RESTDESCRIPTION /// Like `GET`, but only returns the header fields and not the body. You /// can use this call to get the current revision of a document or check if /// the document was deleted. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// is returned if the document was found /// @@ -1377,14 +1365,14 @@ function head_api_structure(req, res) { /// @RESTURLPARAMETERS /// /// @RESTURLPARAM{document-handle,string,required} -/// Deletes the document identified by `document-handle`. -/// +/// Deletes the document identified by `document-handle`. +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{rev,string,optional} /// You can conditionally delete a document based on a target revision id by /// using the `rev` URL parameter. -/// +/// /// @RESTQUERYPARAM{policy,string,optional} /// To control the update behavior in case there is a revision mismatch, you /// can use the `policy` parameter. This is the same as when replacing @@ -1398,7 +1386,7 @@ function head_api_structure(req, res) { /// @RESTHEADERPARAM{If-Match,string,optional} /// You can conditionally delete a document based on a target revision id by /// using the `if-match` HTTP header. -/// +/// /// @RESTDESCRIPTION /// The body of the response contains a JSON object with the information about /// the handle and the revision. The attribute `_id` contains the known @@ -1432,7 +1420,7 @@ function head_api_structure(req, res) { //////////////////////////////////////////////////////////////////////////////// function delete_api_structure (req, res) { - + var collection = getCollectionByRequest(req, res); if (undefined === collection) { return; @@ -1451,13 +1439,13 @@ function delete_api_structure (req, res) { try { collection.remove( doc, true, waitForSync); - resultOk(req, res, - waitForSync ? actions.HTTP_OK : actions.HTTP_ACCEPTED, - { "deleted" : true }); + resultOk(req, res, + waitForSync ? actions.HTTP_OK : actions.HTTP_ACCEPTED, + { "deleted" : true }); } catch(err) { - resultError(req, res, actions.HTTP_BAD, - arangodb.ERROR_FAILED, + resultError(req, res, actions.HTTP_BAD, + arangodb.ERROR_FAILED, err); return; } @@ -1476,7 +1464,7 @@ function delete_api_structure (req, res) { /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{keepNull,boolean,optional} -/// If the intention is to delete existing attributes with the patch command, +/// If the intention is to delete existing attributes with the patch command, /// the URL query parameter `keepNull` can be used with a value of `false`. /// This will modify the behavior of the patch command to remove any attributes /// from the existing document that are contained in the patch document with an @@ -1488,7 +1476,7 @@ function delete_api_structure (req, res) { /// @RESTQUERYPARAM{rev,string,optional} /// You can conditionally patch a document based on a target revision id by /// using the `rev` URL parameter. -/// +/// /// @RESTQUERYPARAM{policy,string,optional} /// To control the update behavior in case there is a revision mismatch, you /// can use the `policy` parameter. @@ -1504,7 +1492,7 @@ function delete_api_structure (req, res) { /// @RESTHEADERPARAM{If-Match,string,optional} /// You can conditionally delete a document based on a target revision id by /// using the `if-match` HTTP header. -/// +/// /// @RESTDESCRIPTION /// Partially updates the document identified by `document-handle`. /// The body of the request must contain a JSON document with the attributes @@ -1513,7 +1501,7 @@ function delete_api_structure (req, res) { /// in the existing document if they do exist there. /// /// Setting an attribute value to `null` in the patch document will cause a -/// value of `null` be saved for the attribute by default. +/// value of `null` be saved for the attribute by default. /// /// Optionally, the URL parameter `waitForSync` can be used to force /// synchronisation of the document update operation to disk even in case @@ -1566,7 +1554,7 @@ function delete_api_structure (req, res) { function patch_api_structure (req, res) { var body; var structure; - + var collection = getCollectionByRequest(req, res); if (undefined === collection) { return; @@ -1584,7 +1572,7 @@ function patch_api_structure (req, res) { body = actions.getJsonBody(req, res); if (body === undefined) { - resultError(req, res, actions.HTTP_BAD, + resultError(req, res, actions.HTTP_BAD, arangodb.ERROR_FAILED, "no body data"); return; } @@ -1607,7 +1595,7 @@ function patch_api_structure (req, res) { /// /// @RESTURLPARAM{document-handle,string,required} /// The Handle of the Document. -/// +/// /// @RESTQUERYPARAMETERS /// /// @RESTQUERYPARAM{waitForSync,boolean,optional} @@ -1616,7 +1604,7 @@ function patch_api_structure (req, res) { /// @RESTQUERYPARAM{rev,string,optional} /// You can conditionally replace a document based on a target revision id by /// using the `rev` URL parameter. -/// +/// /// @RESTQUERYPARAM{policy,string,optional} /// To control the update behavior in case there is a revision mismatch, you /// can use the `policy` parameter. This is the same as when replacing @@ -1633,7 +1621,7 @@ function patch_api_structure (req, res) { /// @RESTHEADERPARAM{If-Match,string,optional} /// You can conditionally replace a document based on a target revision id by /// using the `if-match` HTTP header. -/// +/// /// @RESTDESCRIPTION /// Completely updates (i.e. replaces) the document identified by `document-handle`. /// If the document exists and can be updated, then a `HTTP 201` is returned @@ -1679,7 +1667,7 @@ function patch_api_structure (req, res) { /// /// For example, to conditionally replace a document based on a specific revision /// id, you the following request: -/// +/// /// - PUT /_api/document/`document-handle`?rev=`etag` /// /// If a target revision id is provided in the request (e.g. via the `etag` value @@ -1727,7 +1715,7 @@ function patch_api_structure (req, res) { function put_api_structure (req, res) { var body; var structure; - + var collection = getCollectionByRequest(req, res); if (undefined === collection) { return; @@ -1745,7 +1733,7 @@ function put_api_structure (req, res) { body = actions.getJsonBody(req, res); if (body === undefined) { - resultError(req, res, actions.HTTP_BAD, + resultError(req, res, actions.HTTP_BAD, arangodb.ERROR_FAILED, "no body data"); return; } @@ -1843,9 +1831,9 @@ function post_api_structure (req, res) { var collectionName = req.parameters.collection; if (undefined === collectionName) { - resultError(req, res, actions.HTTP_NOT_FOUND, + resultError(req, res, actions.HTTP_NOT_FOUND, arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND, "collection not found"); - return; + return; } try { @@ -1853,7 +1841,7 @@ function post_api_structure (req, res) { } catch (err) { } - + if (null === collection) { var createCollection = stringToBoolean(req.parameters.createCollection); if (createCollection) { @@ -1861,24 +1849,24 @@ function post_api_structure (req, res) { db._create(collectionName); collection = db._collection(collectionName); } - catch(err2) { - resultError(req, res, actions.HTTP_NOT_FOUND, + catch(err2) { + resultError(req, res, actions.HTTP_NOT_FOUND, arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND, err2); - return; + return; } - } - } - - if (undefined === collection || null === collection) { - resultError(req, res, actions.HTTP_NOT_FOUND, - arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND, "collection not found"); - return; + } } - + + if (undefined === collection || null === collection) { + resultError(req, res, actions.HTTP_NOT_FOUND, + arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND, "collection not found"); + return; + } + body = actions.getJsonBody(req, res); if (body === undefined) { - resultError(req, res, actions.HTTP_BAD, + resultError(req, res, actions.HTTP_BAD, arangodb.ERROR_FAILED, "no body data"); return; } @@ -1898,7 +1886,6 @@ function post_api_structure (req, res) { actions.defineHttp({ url : API, - context : "api", callback : function (req, res) { try { @@ -1930,15 +1917,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-system.js b/js/actions/api-system.js index 45b4fc7403..bebf8d2b20 100644 --- a/js/actions/api-system.js +++ b/js/actions/api-system.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Dr. Frank Celler +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -34,6 +35,8 @@ var internal = require("internal"); var console = require("console"); var users = require("org/arangodb/users"); +var targetDatabaseVersion = require("org/arangodb/database-version").CURRENT_VERSION; + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -45,7 +48,6 @@ var users = require("org/arangodb/users"); actions.defineHttp({ url : "", prefix : true, - context : "admin", callback : function (req, res) { try { @@ -69,6 +71,33 @@ actions.defineHttp({ } }); +//////////////////////////////////////////////////////////////////////////////// +/// @brief _admin/database/version +/// @startDocuBlock JSF_get_admin_database_version +/// +/// @RESTHEADER{GET /_admin/database/target-version, Return the required version of the database} +/// +/// @RESTDESCRIPTION +/// +/// Returns the database-version that this server requires. +/// The version is returned in the *version* attribute of the result. +/// +/// @RESTRETURNCODES +/// +/// @RESTRETURNCODE{200} +/// Is returned in all cases. +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url : "_admin/database/target-version", + prefix : false, + + callback : function (req, res) { + actions.resultOk(req, res, actions.HTTP_OK, { version: String(targetDatabaseVersion) }); + } +}); + //////////////////////////////////////////////////////////////////////////////// /// @brief returns the role of a server in a cluster /// @startDocuBlock JSF_get_admin_server_role @@ -83,7 +112,7 @@ actions.defineHttp({ /// - *COORDINATOR*: the server is a coordinator in a cluster /// - *PRIMARY*: the server is a primary database server in a cluster /// - *SECONDARY*: the server is a secondary database server in a cluster -/// - *UNDEFINED*: in a cluster, *UNDEFINED* is returned if the server role cannot be +/// - *UNDEFINED*: in a cluster, *UNDEFINED* is returned if the server role cannot be /// determined. On a single server, *UNDEFINED* is the only possible return /// value. /// @@ -96,7 +125,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/server/role", - context : "admin", prefix : false, callback : function (req, res) { @@ -143,7 +171,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/wal/flush", - context : "admin", prefix : false, callback : function (req, res) { @@ -153,7 +180,7 @@ actions.defineHttp({ } /*jslint node: true, stupid: true */ - internal.wal.flush(req.parameters.waitForSync === "true", + internal.wal.flush(req.parameters.waitForSync === "true", req.parameters.waitForCollector === "true"); actions.resultOk(req, res, actions.HTTP_OK); } @@ -169,7 +196,7 @@ actions.defineHttp({ /// /// Configures the behavior of the write-ahead log. The body of the request /// must be a JSON object with the following attributes: -/// - *allowOversizeEntries*: whether or not operations that are bigger than a +/// - *allowOversizeEntries*: whether or not operations that are bigger than a /// single logfile can be executed and stored /// - *logfileSize*: the size of each write-ahead logfile /// - *historicLogfiles*: the maximum number of historic logfiles to keep @@ -177,7 +204,7 @@ actions.defineHttp({ /// allocates in the background /// - *throttleWait*: the maximum wait time that operations will wait before /// they get aborted if case of write-throttling (in milliseconds) -/// - *throttleWhenPending*: the number of unprocessed garbage-collection +/// - *throttleWhenPending*: the number of unprocessed garbage-collection /// operations that, when reached, will activate write-throttling. A value of /// *0* means that write-throttling will not be triggered. /// @@ -194,7 +221,7 @@ actions.defineHttp({ /// @endDocuBlock /// /// @EXAMPLES -/// +/// /// @EXAMPLE_ARANGOSH_RUN{RestWalPropertiesPut} /// var url = "/_admin/wal/properties"; /// var body = { @@ -202,9 +229,9 @@ actions.defineHttp({ /// allowOversizeEntries: true /// }; /// var response = logCurlRequest('PUT', url, JSON.stringify(body)); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock @@ -230,7 +257,7 @@ actions.defineHttp({ /// synchronized write-ahead log data (in milliseconds) /// - *throttleWait*: the maximum wait time that operations will wait before /// they get aborted if case of write-throttling (in milliseconds) -/// - *throttleWhenPending*: the number of unprocessed garbage-collection +/// - *throttleWhenPending*: the number of unprocessed garbage-collection /// operations that, when reached, will activate write-throttling. A value of /// *0* means that write-throttling will not be triggered. /// @@ -244,13 +271,13 @@ actions.defineHttp({ /// @endDocuBlock /// /// @EXAMPLES -/// +/// /// @EXAMPLE_ARANGOSH_RUN{RestWalPropertiesGet} /// var url = "/_admin/wal/properties"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock @@ -258,7 +285,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/wal/properties", - context : "admin", prefix : false, callback : function (req, res) { @@ -289,7 +315,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/auth/reload", - context : "admin", prefix : false, callback : function (req, res) { @@ -304,7 +329,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/aql/reload", - context : "admin", prefix : false, callback : function (req, res) { @@ -332,7 +356,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/routing/reload", - context : "admin", prefix : false, callback : function (req, res) { @@ -343,12 +366,11 @@ actions.defineHttp({ }); //////////////////////////////////////////////////////////////////////////////// -/// @brief returns the current routing information +/// @brief returns the current routing information //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ url : "_admin/routing/routes", - context : "admin", prefix : false, callback : function (req, res) { @@ -376,7 +398,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/modules/flush", - context : "admin", prefix : false, callback : function (req, res) { @@ -406,7 +427,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/time", - context : "admin", prefix : false, callback : function (req, res) { @@ -434,7 +454,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/sleep", - context : "admin", prefix : false, callback : function (req, res) { @@ -472,7 +491,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/echo", - context : "admin", prefix : true, callback : function (req, res) { @@ -500,20 +518,20 @@ actions.defineHttp({ /// In case of a distribution, the returned object contains the total count in /// *count* and the distribution list in *counts*. The sum (or total) of the /// individual values is returned in *sum*. -/// +/// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// Statistics were returned successfully. -/// +/// /// @EXAMPLES -/// +/// /// @EXAMPLE_ARANGOSH_RUN{RestAdminStatistics1} /// var url = "/_admin/statistics"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock @@ -521,7 +539,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/statistics", - context : "admin", prefix : false, callback : function (req, res) { @@ -548,7 +565,7 @@ actions.defineHttp({ /// @startDocuBlock JSF_get_admin_statistics_description /// /// @RESTHEADER{GET /_admin/statistics-description, Statistics description} -/// +/// /// @RESTDESCRIPTION /// /// Returns a description of the statistics returned by */_admin/statistics*. @@ -572,18 +589,18 @@ actions.defineHttp({ /// - *units*: Units in which the figure is measured. /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// Description was returned successfully. -/// +/// /// @EXAMPLES -/// +/// /// @EXAMPLE_ARANGOSH_RUN{RestAdminStatisticsDescription1} /// var url = "/_admin/statistics-description"; /// var response = logCurlRequest('GET', url); -/// +/// /// assert(response.code === 200); -/// +/// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN /// @endDocuBlock @@ -591,7 +608,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/statistics-description", - context : "admin", prefix : false, callback : function (req, res) { @@ -611,13 +627,13 @@ actions.defineHttp({ name: "Client Connection Statistics", description: "Statistics about the connections." }, - + { group: "http", name: "HTTP Request Statistics", description: "Statistics about the HTTP requests." }, - + { group: "server", name: "Server Statistics", @@ -636,7 +652,7 @@ actions.defineHttp({ group: "system", identifier: "userTime", name: "User Time", - description: "Amount of time that this process has been scheduled in user mode, " + + description: "Amount of time that this process has been scheduled in user mode, " + "measured in seconds.", type: "accumulated", units: "seconds" @@ -665,7 +681,7 @@ actions.defineHttp({ group: "system", identifier: "residentSize", name: "Resident Set Size", - description: "The total size of the number of pages the process has in real memory. " + + description: "The total size of the number of pages the process has in real memory. " + "This is just the pages which count toward text, data, or stack space. " + "This does not include pages which have not been demand-loaded in, " + "or which are swapped out. The resident set size is reported in bytes.", @@ -720,7 +736,7 @@ actions.defineHttp({ // ............................................................................. // client statistics // ............................................................................. - + { group: "client", identifier: "httpConnections", @@ -759,7 +775,7 @@ actions.defineHttp({ cuts: internal.requestTimeDistribution, units: "seconds" }, - + { group: "client", identifier: "bytesSent", @@ -789,7 +805,7 @@ actions.defineHttp({ cuts: internal.connectionTimeDistribution, units: "seconds" }, - + { group: "http", identifier: "requestsTotal", @@ -798,7 +814,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsAsync", @@ -807,7 +823,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsGet", @@ -816,7 +832,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsHead", @@ -825,7 +841,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsPost", @@ -834,7 +850,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsPut", @@ -843,7 +859,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsPatch", @@ -852,7 +868,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsDelete", @@ -861,7 +877,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsOptions", @@ -870,7 +886,7 @@ actions.defineHttp({ type: "accumulated", units: "number" }, - + { group: "http", identifier: "requestsOther", @@ -921,21 +937,20 @@ actions.defineHttp({ /// @RESTHEADER{POST /_admin/test, Runs tests on server} /// /// @RESTBODYPARAM{body,javascript,required} -/// A JSON body containing an attribute "tests" which lists the files +/// A JSON body containing an attribute "tests" which lists the files /// containing the test suites. /// /// @RESTDESCRIPTION /// /// Executes the specified tests on the server and returns an object with the -/// test results. The object has an attribute "error" which states whether -/// any error occurred. The object also has an attribute "passed" which +/// test results. The object has an attribute "error" which states whether +/// any error occurred. The object also has an attribute "passed" which /// indicates which tests passed and which did not. /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ url : "_admin/test", - context : "admin", prefix : false, callback : function (req, res) { @@ -944,7 +959,7 @@ actions.defineHttp({ if (body === undefined) { return; } - + var tests = body.tests; if (! Array.isArray(tests)) { actions.resultError(req, res, @@ -955,7 +970,7 @@ actions.defineHttp({ var jsUnity = require("jsunity"); var testResults = { passed: { }, error: false }; - + tests.forEach (function (test) { var result = false; try { @@ -980,7 +995,7 @@ actions.defineHttp({ /// @RESTHEADER{POST /_admin/execute, Execute program} /// /// @RESTBODYPARAM{body,javascript,required} -/// The body to be executed. +/// The body to be executed. /// /// @RESTDESCRIPTION /// @@ -996,7 +1011,6 @@ actions.defineHttp({ actions.defineHttp({ url : "_admin/execute", - context : "admin", prefix : false, callback : function (req, res) { @@ -1025,5 +1039,5 @@ actions.defineHttp({ // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-transaction.js b/js/actions/api-transaction.js index fe75d23fba..0888173583 100644 --- a/js/actions/api-transaction.js +++ b/js/actions/api-transaction.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -54,33 +55,33 @@ var actions = require("org/arangodb/actions"); /// transaction (mandatory). *collections* must be a JSON array that can /// have the optional sub-attributes *read* and *write*. *read* /// and *write* must each be either lists of collections names or strings -/// with a single collection name. +/// with a single collection name. /// /// - *action*: the actual transaction operations to be executed, in the /// form of stringified Javascript code. The code will be executed on server /// side, with late binding. It is thus critical that the code specified in -/// *action* properly sets up all the variables it needs. +/// *action* properly sets up all the variables it needs. /// If the code specified in *action* ends with a return statement, the /// value returned will also be returned by the REST API in the *result* /// attribute if the transaction committed successfully. /// /// The following optional attributes may also be specified in the request: /// -/// - *waitForSync*: an optional boolean flag that, if set, will force the +/// - *waitForSync*: an optional boolean flag that, if set, will force the /// transaction to write all data to disk before returning. /// /// - *lockTimeout*: an optional numeric value that can be used to set a -/// timeout for waiting on collection locks. If not specified, a default -/// value will be used. Setting *lockTimeout* to *0* will make ArangoDB +/// timeout for waiting on collection locks. If not specified, a default +/// value will be used. Setting *lockTimeout* to *0* will make ArangoDB /// not time out waiting for a lock. /// /// - *params*: optional arguments passed to *action*. /// -/// If the transaction is fully executed and committed on the server, -/// *HTTP 200* will be returned. Additionally, the return value of the +/// If the transaction is fully executed and committed on the server, +/// *HTTP 200* will be returned. Additionally, the return value of the /// code defined in *action* will be returned in the *result* attribute. -/// -/// For successfully committed transactions, the returned JSON object has the +/// +/// For successfully committed transactions, the returned JSON object has the /// following properties: /// /// - *error*: boolean flag to indicate if an error occurred (*false* @@ -104,16 +105,16 @@ var actions = require("org/arangodb/actions"); /// /// - *errorMessage*: a descriptive error message /// -/// If a transaction fails to commit, either by an exception thrown in the -/// *action* code, or by an internal error, the server will respond with -/// an error. +/// If a transaction fails to commit, either by an exception thrown in the +/// *action* code, or by an internal error, the server will respond with +/// an error. /// Any other errors will be returned with any of the return codes /// *HTTP 400*, *HTTP 409*, or *HTTP 500*. /// /// @RESTRETURNCODES /// /// @RESTRETURNCODE{200} -/// If the transaction is fully executed and committed on the server, +/// If the transaction is fully executed and committed on the server, /// *HTTP 200* will be returned. /// /// @RESTRETURNCODE{400} @@ -125,8 +126,8 @@ var actions = require("org/arangodb/actions"); /// will respond with *HTTP 404*. /// /// @RESTRETURNCODE{500} -/// Exceptions thrown by users will make the server respond with a return code of -/// *HTTP 500* +/// Exceptions thrown by users will make the server respond with a return code of +/// *HTTP 500* /// /// @EXAMPLES /// @@ -137,10 +138,10 @@ var actions = require("org/arangodb/actions"); /// db._drop(cn); /// var products = db._create(cn); /// var url = "/_api/transaction"; -/// var body = { -/// collections: { -/// write : "products" -/// }, +/// var body = { +/// collections: { +/// write : "products" +/// }, /// action: "function () { var db = require('internal').db; db.products.save({}); return db.products.count(); }" /// }; /// @@ -163,9 +164,9 @@ var actions = require("org/arangodb/actions"); /// products.save({ "a": 1}); /// materials.save({ "b": 1}); /// var url = "/_api/transaction"; -/// var body = { -/// collections: { -/// write : [ "products", "materials" ] +/// var body = { +/// collections: { +/// write : [ "products", "materials" ] /// }, /// action: "function () { var db = require('internal').db; db.products.save({}); db.materials.save({}); return 'worked!'; }" /// }; @@ -185,9 +186,9 @@ var actions = require("org/arangodb/actions"); /// db._drop(cn); /// var products = db._create(cn); /// var url = "/_api/transaction"; -/// var body = { -/// collections: { -/// write : "products" +/// var body = { +/// collections: { +/// write : "products" /// }, /// action : "function () { var db = require('internal').db; db.products.save({ _key: 'abc'}); db.products.save({ _key: 'abc'}); }" /// }; @@ -207,10 +208,10 @@ var actions = require("org/arangodb/actions"); /// var products = db._create(cn, { waitForSync: true }); /// products.save({ "a": 1 }); /// var url = "/_api/transaction"; -/// var body = { +/// var body = { /// collections: { -/// read : "products" -/// }, +/// read : "products" +/// }, /// action : "function () { throw 'doh!'; }" /// }; /// @@ -227,10 +228,10 @@ var actions = require("org/arangodb/actions"); /// var cn = "products"; /// db._drop(cn); /// var url = "/_api/transaction"; -/// var body = { +/// var body = { /// collections: { -/// read : "products" -/// }, +/// read : "products" +/// }, /// action : "function () { return true; }" /// }; /// @@ -239,7 +240,7 @@ var actions = require("org/arangodb/actions"); /// /// logJsonResponse(response); /// @END_EXAMPLE_ARANGOSH_RUN -/// @endDocuBlock +/// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// function post_api_transaction(req, res) { @@ -260,18 +261,17 @@ function post_api_transaction(req, res) { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief gateway +/// @brief gateway //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ url : "_api/transaction", - context : "api", callback : function (req, res) { try { switch (req.requestType) { - case actions.POST: - post_api_transaction(req, res); + case actions.POST: + post_api_transaction(req, res); break; default: @@ -284,7 +284,11 @@ actions.defineHttp({ } }); +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-traversal.js b/js/actions/api-traversal.js index 8669215ed5..214dc2aecc 100644 --- a/js/actions/api-traversal.js +++ b/js/actions/api-traversal.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -39,11 +40,6 @@ var graph = require("org/arangodb/general-graph"); // --SECTION-- private functions // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// /// @brief create a "bad parameter" error //////////////////////////////////////////////////////////////////////////////// @@ -390,14 +386,14 @@ function notFound (req, res, code, message) { /// expander: "var connections = [ ];" + /// "if (vertex.name === \"Alice\") {" + /// "config.datasource.getInEdges(vertex).forEach(function (e) {" + -/// "connections.push({ " + -/// "vertex: require(\"internal\").db._document(e._from), " + +/// "connections.push({ " + +/// "vertex: require(\"internal\").db._document(e._from), " + /// "edge: e" + /// "});" + /// "});" + /// "}" + /// "if (vertex.name === \"Eve\") {" + -/// "config.datasource.getOutEdges(vertex).forEach(function (e) {" + +/// "config.datasource.getOutEdges(vertex).forEach(function (e) {" + /// "connections.push({" + /// "vertex: require(\"internal\").db._document(e._to), " + /// "edge: e" + @@ -737,10 +733,6 @@ function post_api_traversal(req, res) { } } -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- initialiser // ----------------------------------------------------------------------------- @@ -751,7 +743,6 @@ function post_api_traversal(req, res) { actions.defineHttp({ url : "_api/traversal", - context : "api", callback : function (req, res) { try { @@ -770,11 +761,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/api-user.js b/js/actions/api-user.js index 60e62c525c..c87b548ffa 100644 --- a/js/actions/api-user.js +++ b/js/actions/api-user.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,9 +22,10 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012-2014, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// @@ -46,27 +47,27 @@ var users = require("org/arangodb/users"); /// @startDocuBlock JSF_api_user_fetch /// /// @RESTHEADER{GET /_api/user/{user}, Fetch User} -/// +/// /// @RESTDESCRIPTION /// /// Fetches data about the specified user. -/// +/// /// The call will return a JSON document with at least the following attributes on success: -/// +/// /// * *user*: The name of the user as a string. /// * *active*: An optional flag that specifies whether the user is active. /// * *extra*: An optional JSON object with arbitrary extra data about the user. -/// * *changePassword*: An optional flag that specifies whether the user must +/// * *changePassword*: An optional flag that specifies whether the user must /// change the password or not. -/// +/// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// The user was found -/// +/// /// @RESTRETURNCODE{404} /// The user with user does not exist -/// +/// /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// @@ -106,22 +107,22 @@ function get_api_user (req, res) { /// @startDocuBlock JSF_api_user_create /// /// @RESTHEADER{POST /_api/user, Create User} -/// +/// /// @RESTDESCRIPTION /// -/// The following data need to be passed in a JSON representation in the body +/// The following data need to be passed in a JSON representation in the body /// of the POST request: /// /// * *user*: The name of the user as a string. This is mandatory -/// * *passwd*: The user password as a string. If no password is specified, +/// * *passwd*: The user password as a string. If no password is specified, /// the empty string will be used /// * *active*: An optional flag that specifies whether the user is active. /// If not specified, this will default to true /// * *extra*: An optional JSON object with arbitrary extra data about the user -/// * *changePassword*: An optional flag that specifies whethers the user must +/// * *changePassword*: An optional flag that specifies whethers the user must /// change the password or not. If not specified, this will default to false /// -/// If set to true, the only operations allowed are PUT /_api/user or PATCH /_api/user. +/// If set to true, the only operations allowed are PUT /_api/user or PATCH /_api/user. /// All other operations will result in a HTTP 403. /// If the user can be added by the server, the server will respond with HTTP 201. /// In case of success, the returned JSON object has the following properties: @@ -129,10 +130,10 @@ function get_api_user (req, res) { /// * *error*: Boolean flag to indicate that an error occurred (false in this case) /// * *code*: The HTTP status code /// -/// If the JSON representation is malformed or mandatory data is missing from the request, +/// If the JSON representation is malformed or mandatory data is missing from the request, /// the server will respond with HTTP 400. /// -/// The body of the response will contain a JSON object with additional error details. +/// The body of the response will contain a JSON object with additional error details. /// The object has the following attributes: /// /// * *error*: Boolean flag to indicate that an error occurred (true in this case) @@ -141,13 +142,13 @@ function get_api_user (req, res) { /// * *errorMessage*: A descriptive error message /// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{201} /// Returned if the user can be added by the server -/// +/// /// @RESTRETURNCODE{400} /// If the JSON representation is malformed or mandatory data is missing from the request. -/// +/// /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// @@ -205,51 +206,51 @@ function post_api_user (req, res) { /// @startDocuBlock JSF_api_user_replace /// /// @RESTHEADER{PUT /_api/user/{user}, Replace User} -/// +/// /// @RESTDESCRIPTION /// /// Replaces the data of an existing user. The name of an existing user must be specified in user. -/// +/// /// The following data can to be passed in a JSON representation in the body of the POST request: -/// -/// * *passwd*: The user password as a string. Specifying a password is mandatory, +/// +/// * *passwd*: The user password as a string. Specifying a password is mandatory, /// but the empty string is allowed for passwords -/// * *active*: An optional flag that specifies whether the user is active. +/// * *active*: An optional flag that specifies whether the user is active. /// If not specified, this will default to true /// * *extra*: An optional JSON object with arbitrary extra data about the user -/// * *changePassword*: An optional flag that specifies whether the user must change +/// * *changePassword*: An optional flag that specifies whether the user must change /// the password or not. If not specified, this will default to false -/// +/// /// If the user can be replaced by the server, the server will respond with HTTP 200. -/// +/// /// In case of success, the returned JSON object has the following properties: -/// +/// /// * *error*: Boolean flag to indicate that an error occurred (false in this case) /// * *code*: The HTTP status code -/// -/// If the JSON representation is malformed or mandatory data is missing from the request, -/// the server will respond with HTTP 400. If the specified user does not exist, +/// +/// If the JSON representation is malformed or mandatory data is missing from the request, +/// the server will respond with HTTP 400. If the specified user does not exist, /// the server will respond with HTTP 404. -/// -/// The body of the response will contain a JSON object with additional +/// +/// The body of the response will contain a JSON object with additional /// error details. The object has the following attributes: -/// +/// /// * *error*: Boolean flag to indicate that an error occurred (true in this case) /// * *code*: The HTTP status code /// * *errorNum*: The server error number /// * *errorMessage*: A descriptive error message -/// +/// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// Is returned if the user data can be replaced by the server -/// +/// /// @RESTRETURNCODE{400} /// The JSON representation is malformed or mandatory data is missing from the request /// /// @RESTRETURNCODE{404} /// The specified user does not exist -/// +/// /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// @@ -293,54 +294,54 @@ function put_api_user (req, res) { /// @startDocuBlock JSF_api_user_update /// /// @RESTHEADER{PATCH /_api/user/{user}, Update User} -/// +/// /// @RESTDESCRIPTION /// -/// Partially updates the data of an existing user. The name of an existing +/// Partially updates the data of an existing user. The name of an existing /// user must be specified in user. -/// -/// The following data can be passed in a JSON representation in the body of the +/// +/// The following data can be passed in a JSON representation in the body of the /// POST request: -/// -/// * *passwd*: The user password as a string. Specifying a password is optional. +/// +/// * *passwd*: The user password as a string. Specifying a password is optional. /// If not specified, the previously existing value will not be modified. -/// * *active*: An optional flag that specifies whether the user is active. +/// * *active*: An optional flag that specifies whether the user is active. /// If not specified, the previously existing value will not be modified. -/// * *extra*: An optional JSON object with arbitrary extra data about the user. +/// * *extra*: An optional JSON object with arbitrary extra data about the user. /// If not specified, the previously existing value will not be modified. -/// * *changePassword*: An optional flag that specifies whether the user must change +/// * *changePassword*: An optional flag that specifies whether the user must change /// the password or not. 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 HTTP 200. -/// +/// /// In case of success, the returned JSON object has the following properties: -/// +/// /// * *error*: Boolean flag to indicate that an error occurred (false in this case) /// * *code*: The HTTP status code -/// -/// If the JSON representation is malformed or mandatory data is missing from the request, -/// the server will respond with HTTP 400. If the specified user does not exist, +/// +/// If the JSON representation is malformed or mandatory data is missing from the request, +/// the server will respond with HTTP 400. If the specified user does not exist, /// the server will respond with HTTP 404. -/// -/// The body of the response will contain a JSON object with additional error details. +/// +/// The body of the response will contain a JSON object with additional error details. /// The object has the following attributes: -/// +/// /// * *error*: Boolean flag to indicate that an error occurred (true in this case) /// * *code*: The HTTP status code /// * *errorNum*: The server error number /// * *errorMessage*: A descriptive error message -/// +/// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{200} /// Is returned if the user data can be replaced by the server -/// +/// /// @RESTRETURNCODE{400} /// The JSON representation is malformed or mandatory data is missing from the request /// /// @RESTRETURNCODE{404} /// The specified user does not exist -/// +/// /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// @@ -381,35 +382,35 @@ function patch_api_user (req, res) { /// @startDocuBlock JSF_api_user_delete /// /// @RESTHEADER{DELETE /_api/user/{user}, Remove User} -/// +/// /// @RESTDESCRIPTION /// /// Removes an existing user, identified by user. -/// +/// /// If the user can be removed, the server will respond with HTTP 202. /// In case of success, the returned JSON object has the following properties: -/// +/// /// * *error*: Boolean flag to indicate that an error occurred (false in this case) /// * *code*: The HTTP status code -/// +/// /// If the specified user does not exist, the server will respond with HTTP 404. -/// -/// The body of the response will contain a JSON object with additional error details. +/// +/// The body of the response will contain a JSON object with additional error details. /// The object has the following attributes: -/// +/// /// * *error*: Boolean flag to indicate that an error occurred (true in this case) /// * *code*: The HTTP status code /// * *errorNum*: The server error number /// * *errorMessage*: A descriptive error message -/// +/// /// @RESTRETURNCODES -/// +/// /// @RESTRETURNCODE{202} /// Is returned if the user was removed by the server -/// +/// /// @RESTRETURNCODE{404} /// The specified user does not exist -/// +/// /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// @@ -446,7 +447,6 @@ function delete_api_user (req, res) { actions.defineHttp({ url : "_api/user", - context : "api", callback : function (req, res) { try { @@ -487,5 +487,5 @@ actions.defineHttp({ // Local Variables: // mode: outline-minor -// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/actions/key-value.js b/js/actions/key-value.js index e6258c797f..f85d13d51a 100644 --- a/js/actions/key-value.js +++ b/js/actions/key-value.js @@ -8,7 +8,7 @@ /// /// DISCLAIMER /// -/// Copyright 2012 triagens GmbH, Cologne, Germany +/// Copyright 2014 ArangoDB 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. @@ -22,18 +22,14 @@ /// See the License for the specific language governing permissions and /// limitations under the License. /// -/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Achim Brandt /// @author Jan Steemann +/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- global variables // ----------------------------------------------------------------------------- @@ -47,9 +43,9 @@ var arangodb = require("org/arangodb"); // --SECTION-- private functions // ----------------------------------------------------------------------------- -function formatTimeStamp (timestamp) { +function formatTimeStamp (timestamp) { var d = new Date(timestamp * 1000); - + var year = d.getUTCFullYear(); var month = d.getUTCMonth() + 1; var date = d.getUTCDate(); @@ -74,12 +70,12 @@ function formatTimeStamp (timestamp) { if (seconds < 10) { seconds = "0" + seconds; } - - return year + "-" + month + "-" + date + "T" + hour + ":" + minutes + ":" + seconds + "Z"; + + return year + "-" + month + "-" + date + "T" + hour + ":" + minutes + ":" + seconds + "Z"; } function buildDocumentFromReq(req) { - + // Example requests: // Header: // POST /_api/key/example_collection/example_key1 HTTP/1.1 @@ -89,35 +85,35 @@ function buildDocumentFromReq(req) { // Body: // 12 // - // - + // + var key = req.suffix[1], i; - + for (i = 2; i < req.suffix.length; ++i) { key += "/" + req.suffix[i]; } var doc = { "$key" : key, - "$value" : req.requestBody + "$value" : req.requestBody }; - + if (req.headers["x-voc-expires"] !== undefined) { var d = new Date(req.headers["x-voc-expires"]); // store time stamp as double - doc.$expires = d.getTime() / 1000; + doc.$expires = d.getTime() / 1000; } - + if (req.headers["x-voc-extended"] !== undefined) { var json = JSON.parse(req.headers["x-voc-extended"]); if (json !== undefined) { doc.$extended = json; } } - + // store time stamp as double doc.$created = internal.time(); - + return doc; } @@ -138,39 +134,39 @@ function postKeyValue(req, res) { } var collection = req.suffix[0]; - + if (db._collection(collection) === null) { actions.collectionNotFound(req, res, collection); - return; + return; } if (req.requestBody === "" || req.requestBody === undefined) { - actions.resultError(req, - res, - actions.HTTP_BAD, - arangodb.ERROR_KEYVALUE_NO_VALUE, + actions.resultError(req, + res, + actions.HTTP_BAD, + arangodb.ERROR_KEYVALUE_NO_VALUE, actions.getErrorMessage(arangodb.ERROR_KEYVALUE_NO_VALUE)); return; } - + var doc = buildDocumentFromReq(req); var oldDoc = db._collection(collection).firstExample("$key", doc.$key); - + if (oldDoc !== undefined) { - actions.resultError(req, - res, - actions.HTTP_BAD, - arangodb.ERROR_KEYVALUE_KEY_EXISTS, + actions.resultError(req, + res, + actions.HTTP_BAD, + arangodb.ERROR_KEYVALUE_KEY_EXISTS, actions.getErrorMessage(arangodb.ERROR_KEYVALUE_KEY_EXISTS)); } else { - var id = db[collection].save(doc); + var id = db[collection].save(doc); var result = { "saved" : true, "_id" : id }; - actions.resultOk(req, res, actions.HTTP_CREATED, result); + actions.resultOk(req, res, actions.HTTP_CREATED, result); } } @@ -189,51 +185,51 @@ function putKeyValue(req, res) { } var collection = req.suffix[0]; - + if (db._collection(collection) === null) { actions.collectionNotFound(req, res); - return; + return; } - + var doc = buildDocumentFromReq(req); var oldDoc = db._collection(collection).firstExample("$key", doc.$key); var id; - + if (oldDoc === undefined) { if (req.parameters.create === 1 || req.parameters.create === "1") { - id = db[collection].save(doc); + id = db[collection].save(doc); var result = { "saved" : true, "_id" : id }; - actions.resultOk(req, res, actions.HTTP_CREATED, result); + actions.resultOk(req, res, actions.HTTP_CREATED, result); return; } - actions.resultError(req, - res, - actions.HTTP_NOT_FOUND, - arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND, + actions.resultError(req, + res, + actions.HTTP_NOT_FOUND, + arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND, actions.getErrorMessage(arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND)); } else { // get _id id = oldDoc._id; - + // save x-voc-created - var created = oldDoc.$created; + var created = oldDoc.$created; if (created !== undefined) { - doc["x-voc-created"] = created; + doc["x-voc-created"] = created; } - + // replace the document - if (db[collection].replace(id, doc)) { - actions.resultOk(req, res, actions.HTTP_ACCEPTED, {"changed" : true}); + if (db[collection].replace(id, doc)) { + actions.resultOk(req, res, actions.HTTP_ACCEPTED, {"changed" : true}); } else { - actions.resultError(req, - res, - actions.HTTP_BAD, - arangodb.ERROR_KEYVALUE_KEY_NOT_CHANGED, + actions.resultError(req, + res, + actions.HTTP_BAD, + arangodb.ERROR_KEYVALUE_KEY_NOT_CHANGED, actions.getErrorMessage(arangodb.ERROR_KEYVALUE_KEY_NOT_CHANGED)); } } @@ -253,37 +249,37 @@ function deleteKeyValue(req, res) { } var collection = req.suffix[0]; - + if (db._collection(collection) === null) { actions.collectionNotFound(req, res); - return; + return; } - + var key = req.suffix[1], i; - + for (i = 2; i < req.suffix.length; ++i) { key += "/" + req.suffix[i]; } var doc = db._collection(collection).firstExample("$key", key); - + if (doc === undefined) { - actions.resultError(req, - res, - actions.HTTP_NOT_FOUND, - arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND, + actions.resultError(req, + res, + actions.HTTP_NOT_FOUND, + arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND, actions.getErrorMessage(arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND)); } else { var id = doc._id; - if (db[collection].remove(id)) { - actions.resultOk(req, res, actions.HTTP_ACCEPTED, {"removed" : true}); + if (db[collection].remove(id)) { + actions.resultOk(req, res, actions.HTTP_ACCEPTED, {"removed" : true}); } else { - actions.resultError(req, - res, - actions.HTTP_BAD, - arangodb.ERROR_KEYVALUE_KEY_NOT_REMOVED, + actions.resultError(req, + res, + actions.HTTP_BAD, + arangodb.ERROR_KEYVALUE_KEY_NOT_REMOVED, actions.getErrorMessage(arangodb.ERROR_KEYVALUE_KEY_NOT_REMOVED)); } } @@ -295,7 +291,7 @@ function deleteKeyValue(req, res) { /// @REST{GET /_api/key/@FA{collection-name}/@FA{key}} /// /// returned headers: "x-voc-expires", "x-voc-extended" and "x-voc-created" -/// +/// //////////////////////////////////////////////////////////////////////////////// function getKeyValue(req, res) { @@ -305,30 +301,30 @@ function getKeyValue(req, res) { } var collection = req.suffix[0]; - + if (db._collection(collection) === null) { actions.collectionNotFound(req, res); - return; + return; } - + var key = req.suffix[1], i; - + for (i = 2; i < req.suffix.length; ++i) { key += "/" + req.suffix[i]; } - - var doc = db._collection(collection).firstExample("$key", key); - + + var doc = db._collection(collection).firstExample("$key", key); + if (doc === undefined) { - actions.resultError(req, - res, - actions.HTTP_NOT_FOUND, - arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND, + actions.resultError(req, + res, + actions.HTTP_NOT_FOUND, + arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND, actions.getErrorMessage(arangodb.ERROR_KEYVALUE_KEY_NOT_FOUND)); } else { var headers = {}; - + if (doc.$expires !== undefined) { // format timestamp headers["x-voc-expires"] = formatTimeStamp(doc.$expires); @@ -341,8 +337,8 @@ function getKeyValue(req, res) { // format timestamp headers["x-voc-created"] = formatTimeStamp(doc.$created); } - - actions.resultOk(req, res, actions.HTTP_OK, doc.$value, headers); + + actions.resultOk(req, res, actions.HTTP_OK, doc.$value, headers); } } @@ -351,30 +347,29 @@ function getKeyValue(req, res) { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief key value pair actions gateway +/// @brief key value pair actions gateway //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ url : "_api/key", - context : "api", callback : function (req, res) { try { switch (req.requestType) { case (actions.POST) : - postKeyValue(req, res); + postKeyValue(req, res); break; case (actions.GET) : - getKeyValue(req, res); + getKeyValue(req, res); break; case (actions.PUT) : - putKeyValue(req, res); + putKeyValue(req, res); break; case (actions.DELETE) : - deleteKeyValue(req, res); + deleteKeyValue(req, res); break; default: @@ -388,15 +383,6 @@ actions.defineHttp({ }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup ArangoAPI -/// @{ -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- @@ -415,24 +401,24 @@ function searchKeyValue(req, res) { } var collection = req.suffix[0]; - + if (db._collection(collection) === null) { actions.collectionNotFound(req, res); - return; + return; } - + var prefix = req.suffix[1], i; - + for (i = 2; i < req.suffix.length; ++i) { prefix += "/" + req.suffix[i]; } - + // // TODO: build a query which selects the keys // - + var cursor = db._collection(collection).all(), result = [ ]; - + while (cursor.hasNext() ) { var doc = cursor.next(); if (doc.$key !== undefined && doc.$key.indexOf(prefix) === 0) { @@ -448,20 +434,19 @@ function searchKeyValue(req, res) { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief key value pair actions gateway +/// @brief key value pair actions gateway //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ url : "_api/keys", - context : "api", callback : function (req, res) { try { switch (req.requestType) { case (actions.GET) : - searchKeyValue(req, res); + searchKeyValue(req, res); break; - + default: actions.resultUnsupported(req, res); } @@ -472,11 +457,11 @@ actions.defineHttp({ } }); -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- // Local Variables: // mode: outline-minor -// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: diff --git a/js/apps/system/aardvark/cluster.js b/js/apps/system/aardvark/cluster.js index a5d68e5732..1b9d0856fa 100644 --- a/js/apps/system/aardvark/cluster.js +++ b/js/apps/system/aardvark/cluster.js @@ -29,7 +29,6 @@ //////////////////////////////////////////////////////////////////////////////// (function() { - "use strict"; // Initialise a new FoxxController called controller under the urlPrefix: "cluster". @@ -51,7 +50,7 @@ res.json(!cluster.dispatcherDisabled()); }); - if (!cluster.dispatcherDisabled()) { + if (! cluster.dispatcherDisabled()) { var Plans = require("./repositories/plans.js"), plans = new Plans.Repository( require("internal").db._collection( @@ -162,6 +161,7 @@ p = body.passwd; plans.saveCredentials(u, p); }); + controller.get("/plan", function(req, res) { res.json(plans.loadConfig()); }); @@ -229,7 +229,8 @@ controller.get("/relaunch", function(req, res) { var k = getStarter(); - var r = k.relaunch(); + var u = plans.getCredentials(); + var r = k.relaunch(u.name, u.passwd); if (r.error) { res.json("Unable to relaunch cluster"); res.status(409); diff --git a/js/apps/system/aardvark/clusterFrontend/js/views/clusterDownView.js b/js/apps/system/aardvark/clusterFrontend/js/views/clusterDownView.js index e32aea42ba..aff3f01af9 100644 --- a/js/apps/system/aardvark/clusterFrontend/js/views/clusterDownView.js +++ b/js/apps/system/aardvark/clusterFrontend/js/views/clusterDownView.js @@ -24,20 +24,18 @@ var planVersion = window.versionHelper.fromString( window.App.clusterPlan.getVersion() ); - var currentVersion = window.App.footerView.system.version; - if (!currentVersion) { - $.ajax({ - type: "GET", - cache: false, - url: "/_api/version", - contentType: "application/json", - processData: false, - async: false, - success: function(data) { - currentVersion = data.version; - } - }); - } + var currentVersion; + $.ajax({ + type: "GET", + cache: false, + url: "/_admin/database/target-version", + contentType: "application/json", + processData: false, + async: false, + success: function(data) { + currentVersion = data.version; + } + }); currentVersion = window.versionHelper.fromString( currentVersion ); diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/module-console.js b/js/apps/system/aardvark/frontend/js/bootstrap/module-console.js index c31023e380..c7ed6eef06 100644 --- a/js/apps/system/aardvark/frontend/js/bootstrap/module-console.js +++ b/js/apps/system/aardvark/frontend/js/bootstrap/module-console.js @@ -1,4 +1,4 @@ -/*jslint indent: 2, maxlen: 120, vars: true, white: true, plusplus: true, nonpropdel: true, sloppy: true */ +/*jslint indent: 2, maxlen: 120, vars: true, white: true, plusplus: true, nonpropdel: true, sloppy: true, proto: true */ /*global require, SYS_GETLINE, SYS_LOG, jqconsole */ //////////////////////////////////////////////////////////////////////////////// @@ -90,6 +90,40 @@ log(level, groupLevel + msg); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief try to prettify +//////////////////////////////////////////////////////////////////////////////// + + function prepareArgs (args) { + var ShapedJson = require("internal").ShapedJson; + var result = []; + var i; + + if (0 < args.length && typeof args[0] !== "string") { + result.push("%s"); + } + + for (i = 0; i < args.length; ++i) { + var arg = args[i]; + + if (typeof arg === "object") { + if (ShapedJson !== undefined && arg instanceof ShapedJson) { + arg = inspect(arg, {prettyPrint: false}); + } + else if (arg === null) { + arg = "null"; + } + else if (arg.__proto__ === Object.prototype || Array.isArray(arg)) { + arg = inspect(arg, {prettyPrint: false}); + } + } + + result.push(arg); + } + + return result; + } + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -109,7 +143,7 @@ var msg; try { - msg = sprintf.apply(sprintf, args); + msg = sprintf.apply(sprintf, prepareArgs(args)); } catch (err) { msg = err + ": " + args; @@ -130,7 +164,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -149,7 +183,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -183,7 +217,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -202,7 +236,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -235,7 +269,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -255,7 +289,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -285,7 +319,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -304,7 +338,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -371,7 +405,7 @@ exports.trace = function () { var err = new Error(); err.name = 'trace'; - err.message = sprintf.apply(sprintf, arguments); + err.message = sprintf.apply(sprintf, prepareArgs(arguments)); Error.captureStackTrace(err, exports.trace); logGroup("info", err.stack); }; @@ -386,7 +420,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; @@ -405,7 +439,7 @@ var msg; try { - msg = sprintf.apply(sprintf, arguments); + msg = sprintf.apply(sprintf, prepareArgs(arguments)); } catch (err) { msg = err + ": " + arguments; diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js index 8aa8935e03..af23711b86 100644 --- a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js +++ b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js @@ -1,7 +1,7 @@ /*jslint indent: 2, nomen: true, maxlen: 120, vars: true, white: true, plusplus: true, nonpropdel: true, proto: true */ /*jslint sloppy: true, regexp: true */ /*global require, module, Module, ArangoError, SleepAndRequeue, - CONFIGURE_ENDPOINT, REMOVE_ENDPOINT, LIST_ENDPOINTS, + CONFIGURE_ENDPOINT, REMOVE_ENDPOINT, LIST_ENDPOINTS, STARTUP_PATH, SYS_BASE64DECODE, SYS_BASE64ENCODE, SYS_DEBUG_SEGFAULT, SYS_DEBUG_CAN_USE_FAILAT, SYS_DEBUG_SET_FAILAT, SYS_DEBUG_REMOVE_FAILAT, SYS_DEBUG_CLEAR_FAILAT, SYS_DOWNLOAD, SYS_EXECUTE, SYS_GET_CURRENT_REQUEST, SYS_GET_CURRENT_RESPONSE, @@ -258,6 +258,21 @@ delete REQUEST_TIME_DISTRIBUTION; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief startupPath +//////////////////////////////////////////////////////////////////////////////// + + exports.startupPath = ""; + + if (typeof STARTUP_PATH !== "undefined") { + exports.startupPath = STARTUP_PATH; + delete STARTUP_PATH; + } + + if (exports.startupPath === "") { + exports.startupPath = "."; + } + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -1427,14 +1442,18 @@ limitString: false, names: [], output: "", - path: "~", prettyPrint: true, + path: "~", seen: [], showFunction: true, useColor: false, useToString: false }; + if (options && options.hasOwnProperty("prettyPrint")) { + context.prettyPrint = options.prettyPrint; + } + printRecursive(object, context); return context.output; diff --git a/js/apps/system/aardvark/frontend/js/collections/arangoQueries.js b/js/apps/system/aardvark/frontend/js/collections/arangoQueries.js index bc976f0233..eafc0ed004 100644 --- a/js/apps/system/aardvark/frontend/js/collections/arangoQueries.js +++ b/js/apps/system/aardvark/frontend/js/collections/arangoQueries.js @@ -1,32 +1,39 @@ /*jslint indent: 2, nomen: true, maxlen: 100, vars: true, white: true, plusplus: true, stupid: true*/ -/*global require, exports, Backbone, window, ArangoQuery, $, data, _ */ +/*global require, exports, Backbone, activeUser, window, ArangoQuery, $, data, _ */ (function() { "use strict"; window.ArangoQueries = Backbone.Collection.extend({ initialize: function(models, options) { - this.fetch(); - - if (options.activeUser) { - this.activeUser = options.activeUser; + var result; + $.ajax("whoAmI", {async:false}).done( + function(data) { + result = data.name; } - else { + ); + this.activeUser = result; + + if (this.activeUser === 0 || this.activeUser === undefined || this.activeUser === null) { this.activeUser = "root"; } + }, - url: '/_api/user', + url: '/_api/user/', model: ArangoQuery, activeUser: 0, + currentExtra: {}, + parse: function(response) { var self = this, toReturn; _.each(response.result, function(val) { if (val.user === self.activeUser) { + self.currentExtra = val.extra; try { if (val.extra.queries) { toReturn = val.extra.queries; @@ -39,30 +46,167 @@ return toReturn; }, - saveQueries: function(data) { - var returnValue = false; + saveCollectionQueries: function() { + if (this.activeUser === 0) { + return false; + } + + var queries = [], + returnValue1 = false, + returnValue2 = false, + returnValue3 = false, + extraBackup = null, + self = this; + + this.each(function(query) { + queries.push({ + value: query.attributes.value, + name: query.attributes.name + }); + }); + + extraBackup = self.currentExtra; + extraBackup.queries = []; + + $.ajax({ + cache: false, + type: "PUT", + async: false, + url: "/_api/user/" + this.activeUser, + data: JSON.stringify({ + extra: extraBackup + }), + contentType: "application/json", + processData: false, + success: function() { + returnValue1 = true; + }, + error: function() { + returnValue1 = false; + } + }); + + //save current collection + $.ajax({ + cache: false, + type: "PATCH", + async: false, + url: "/_api/user/" + this.activeUser, + data: JSON.stringify({ + extra: { + queries: queries + } + }), + contentType: "application/json", + processData: false, + success: function() { + returnValue2 = true; + }, + error: function() { + returnValue2 = false; + } + }); + + if (returnValue1 === true && returnValue2 === true) { + returnValue3 = true; + } + else { + returnValue3 = false; + } + return returnValue3; + }, + + saveImportQueries: function(file) { + + if (this.activeUser === 0) { + return false; + } + + var queries = []; + this.each(function(query) { + queries.push({ + value: query.attributes.value, + name: query.attributes.name + }); + }); + + var self = this, + returnValue1 = false, + returnValue2 = false, + returnValue3 = false; + + var oldQueries = this.currentExtra; $.ajax({ cache: false, type: "PATCH", async: false, url: "/_api/user/" + this.activeUser, - data: { - extra: { - queries: data - } - }, + data: file, contentType: "application/json", processData: false, success: function() { - returnValue = true; + returnValue1 = true; }, error: function() { - returnValue = false; + returnValue1 = false; } }); - return returnValue; + this.fetch({ + async: false, + success: function() { + }}); + + var newQueries = self.currentExtra; + + var nameTaken = false; + + _.each(oldQueries.queries, function(query) { + + _.each(newQueries.queries, function(newQuery) { + if (newQuery.name === query.name) { + nameTaken = true; + } + }); + + if (nameTaken === false) { + newQueries.queries.push({ + name: query.name, + value: query.value + }); + } + }); + + $.ajax({ + cache: false, + type: "PATCH", + async: false, + url: "/_api/user/" + this.activeUser, + data: JSON.stringify({ + extra: newQueries + }), + contentType: "application/json", + processData: false, + success: function() { + returnValue2 = true; + }, + error: function() { + returnValue2 = false; + } + }); + + this.fetch({ + async: false + }); + + if (returnValue1 === true && returnValue2 === true) { + returnValue3 = true; + } + else { + returnValue3 = false; + } + return returnValue3; } }); diff --git a/js/apps/system/aardvark/frontend/js/lib/joi.browser.js b/js/apps/system/aardvark/frontend/js/lib/joi.browser.js new file mode 100644 index 0000000000..9c446249b8 --- /dev/null +++ b/js/apps/system/aardvark/frontend/js/lib/joi.browser.js @@ -0,0 +1,7178 @@ +!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Joi=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0, 'limit must be a positive integer'); + + return this._test('min', limit, function (value, state, options) { + + if (value.length >= limit) { + return null; + } + + return Errors.create('array.min', { limit: limit }, state, options); + }); +}; + + +internals.Array.prototype.max = function (limit) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + + return this._test('max', limit, function (value, state, options) { + + if (value.length <= limit) { + return null; + } + + return Errors.create('array.max', { limit: limit }, state, options); + }); +}; + + +internals.Array.prototype.length = function (limit) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + + return this._test('length', limit, function (value, state, options) { + + if (value.length === limit) { + return null; + } + + return Errors.create('array.length', { limit: limit }, state, options); + }); +}; + + +module.exports = new internals.Array(); + +},{"./any":2,"./cast":6,"./errors":8,"hoek":16}],4:[function(require,module,exports){ +(function (Buffer){ +// Load modules + +var Any = require('./any'); +var Errors = require('./errors'); +var Hoek = require('hoek'); + + +// Declare internals + +var internals = {}; + + +internals.Binary = function () { + + Any.call(this); + this._type = 'binary'; +}; + +Hoek.inherits(internals.Binary, Any); + + +internals.Binary.prototype._base = function (value, state, options) { + + var result = { + value: value + }; + + if (typeof value === 'string' && + options.convert) { + + try { + var converted = new Buffer(value, this._flags.encoding); + result.value = converted; + } + catch (e) { } + } + + result.errors = Buffer.isBuffer(result.value) ? null : Errors.create('binary.base', null, state, options); + return result; +}; + + +internals.Binary.prototype.encoding = function (encoding) { + + Hoek.assert(Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); + + var obj = this.clone(); + obj._flags.encoding = encoding; + return obj; +}; + + +internals.Binary.prototype.min = function (limit) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + + return this._test('min', limit, function (value, state, options) { + + if (value.length >= limit) { + return null; + } + + return Errors.create('binary.min', { limit: limit }, state, options); + }); +}; + + +internals.Binary.prototype.max = function (limit) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + + return this._test('max', limit, function (value, state, options) { + + if (value.length <= limit) { + return null; + } + + return Errors.create('binary.max', { limit: limit }, state, options); + }); +}; + + +internals.Binary.prototype.length = function (limit) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + + return this._test('length', limit, function (value, state, options) { + + if (value.length === limit) { + return null; + } + + return Errors.create('binary.length', { limit: limit }, state, options); + }); +}; + + +module.exports = new internals.Binary(); +}).call(this,require("buffer").Buffer) +},{"./any":2,"./errors":8,"buffer":24,"hoek":16}],5:[function(require,module,exports){ +// Load modules + +var Any = require('./any'); +var Errors = require('./errors'); +var Hoek = require('hoek'); + + +// Declare internals + +var internals = {}; + + +internals.Boolean = function () { + + Any.call(this); + this._type = 'boolean'; +}; + +Hoek.inherits(internals.Boolean, Any); + + +internals.Boolean.prototype._base = function (value, state, options) { + + var result = { + value: value + }; + + if (typeof value === 'string' && + options.convert) { + + var lower = value.toLowerCase(); + result.value = (lower === 'true' || lower === 'yes' || lower === 'on' ? true + : (lower === 'false' || lower === 'no' || lower === 'off' ? false : value)); + } + + result.errors = (typeof result.value === 'boolean') ? null : Errors.create('boolean.base', null, state, options); + return result; +}; + + +module.exports = new internals.Boolean(); +},{"./any":2,"./errors":8,"hoek":16}],6:[function(require,module,exports){ +// Load modules + +var Hoek = require('hoek'); +var Ref = require('./ref'); +// Type modules are delay-loaded to prevent circular dependencies + + +// Declare internals + +var internals = { + any: null, + date: require('./date'), + string: require('./string'), + number: require('./number'), + boolean: require('./boolean'), + alt: null, + object: null +}; + + +exports.schema = function (config) { + + internals.any = internals.any || new (require('./any'))(); + internals.alt = internals.alt || require('./alternatives'); + internals.object = internals.object || require('./object'); + + if (config && + typeof config === 'object') { + + if (config.isJoi) { + return config; + } + + if (Array.isArray(config)) { + return internals.alt.try(config); + } + + if (config instanceof RegExp) { + return internals.string.regex(config); + } + + if (config instanceof Date) { + return internals.date.valid(config); + } + + return internals.object.keys(config); + } + + if (typeof config === 'string') { + return internals.string.valid(config); + } + + if (typeof config === 'number') { + return internals.number.valid(config); + } + + if (typeof config === 'boolean') { + return internals.boolean.valid(config); + } + + if (Ref.isRef(config)) { + return internals.any.valid(config); + } + + Hoek.assert(config === null, 'Invalid schema content:', config); + + return internals.any.valid(null); +}; + + +exports.ref = function (id) { + + return Ref.isRef(id) ? id : Ref.create(id); +}; +},{"./alternatives":1,"./any":2,"./boolean":5,"./date":7,"./number":12,"./object":13,"./ref":14,"./string":15,"hoek":16}],7:[function(require,module,exports){ +// Load modules + +var Any = require('./any'); +var Errors = require('./errors'); +var Hoek = require('hoek'); + + +// Declare internals + +var internals = {}; + + +internals.Date = function () { + + Any.call(this); + this._type = 'date'; +}; + +Hoek.inherits(internals.Date, Any); + + +internals.Date.prototype._base = function (value, state, options) { + + var result = { + value: (options.convert && internals.toDate(value)) || value + }; + + result.errors = (result.value instanceof Date && !isNaN(result.value.getTime())) ? null : Errors.create('date.base', null, state, options); + return result; +}; + + +internals.toDate = function (value) { + + if (value instanceof Date) { + return value; + } + + if (typeof value === 'string' || + Hoek.isInteger(value)) { + + if (typeof value === 'string' && + /^\d+$/.test(value)) { + + value = parseInt(value, 10); + } + + var date = new Date(value); + if (!isNaN(date.getTime())) { + return date; + } + } + + return null; +}; + + +internals.Date.prototype.min = function (date) { + + date = internals.toDate(date); + Hoek.assert(date, 'Invalid date format'); + + return this._test('min', date, function (value, state, options) { + + if (value.getTime() >= date.getTime()) { + return null; + } + + return Errors.create('date.min', { limit: date }, state, options); + }); +}; + + +internals.Date.prototype.max = function (date) { + + date = internals.toDate(date); + Hoek.assert(date, 'Invalid date format'); + + return this._test('max', date, function (value, state, options) { + + if (value.getTime() <= date.getTime()) { + return null; + } + + return Errors.create('date.max', { limit: date }, state, options); + }); +}; + + +module.exports = new internals.Date(); + +},{"./any":2,"./errors":8,"hoek":16}],8:[function(require,module,exports){ +// Load modules + +var Hoek = require('hoek'); +var Language = require('./language'); + + +// Declare internals + +var internals = {}; + + +internals.Err = function (type, context, state, options) { + + this.type = type; + this.context = context || {}; + this.context.key = state.key; + this.path = state.path; + this.options = options; +}; + + +internals.Err.prototype.toString = function () { + + var self = this; + + var localized = this.options.language; + this.context.key = this.context.key || localized.root || Language.errors.root; + + var format = Hoek.reach(localized, this.type) || Hoek.reach(Language.errors, this.type); + var hasKey = /\{\{\!?key\}\}/.test(format); + format = (hasKey ? format : '{{!key}} ' + format); + var message = format.replace(/\{\{(\!?)([^}]+)\}\}/g, function ($0, isSecure, name) { + + var value = Hoek.reach(self.context, name); + var normalized = Array.isArray(value) ? value.join(', ') : value.toString(); + return (isSecure ? Hoek.escapeHtml(normalized) : normalized); + }); + + return message; +}; + + +exports.create = function (type, context, state, options) { + + return new internals.Err(type, context, state, options); +}; + + +exports.process = function (errors, object) { + + if (!errors || !errors.length) { + return null; + } + + var details = []; + for (var i = 0, il = errors.length; i < il; ++i) { + var item = errors[i]; + details.push({ + message: item.toString(), + path: item.path || item.context.key, + type: item.type + }); + } + + // Construct error + + var message = ''; + details.forEach(function (error) { + + message += (message ? '. ' : '') + error.message; + }); + + var error = new Error(message); + error.name = 'ValidationError'; + error.details = details; + error._object = object; + error.annotate = internals.annotate; + return error; +}; + + +internals.annotate = function () { + + var obj = Hoek.clone(this._object || {}); + + var lookup = {}; + var el = this.details.length; + for (var e = el - 1; e >= 0; --e) { // Reverse order to process deepest child first + var pos = el - e; + var error = this.details[e]; + var path = error.path.split('.'); + var ref = obj; + for (var i = 0, il = path.length; i < il && ref; ++i) { + var seg = path[i]; + if (i + 1 < il) { + ref = ref[seg]; + } + else { + var value = ref[seg]; + if (value !== undefined) { + delete ref[seg]; + var label = seg + '_$key$_' + pos + '_$end$_'; + ref[label] = value; + lookup[error.path] = label; + } + else if (lookup[error.path]) { + var replacement = lookup[error.path]; + var appended = replacement.replace('_$end$_', ', ' + pos + '_$end$_'); + ref[appended] = ref[replacement]; + lookup[error.path] = appended; + delete ref[replacement]; + } + else { + ref['_$miss$_' + seg + '|' + pos + '_$end$_'] = '__missing__'; + } + } + } + } + + var annotated = JSON.stringify(obj, null, 2); + + annotated = annotated.replace(/_\$key\$_([, \d]+)_\$end\$_\"/g, function ($0, $1) { + + return '" \u001b[31m[' + $1 + ']\u001b[0m'; + }); + + var message = annotated.replace(/\"_\$miss\$_([^\|]+)\|(\d+)_\$end\$_\"\: \"__missing__\"/g, function ($0, $1, $2) { + + return '\u001b[41m"' + $1 + '"\u001b[0m\u001b[31m [' + $2 + ']: -- missing --\u001b[0m'; + }); + + message += '\n\u001b[31m'; + + for (e = 0; e < el; ++e) { + message += '\n[' + (e + 1) + '] ' + this.details[e].message; + } + + message += '\u001b[0m'; + + return message; +}; + + +},{"./language":11,"hoek":16}],9:[function(require,module,exports){ +// Load modules + +var Any = require('./any'); +var Errors = require('./errors'); +var Hoek = require('hoek'); + + +// Declare internals + +var internals = {}; + + +internals.Function = function () { + + Any.call(this); + this._type = 'func'; +}; + +Hoek.inherits(internals.Function, Any); + + +internals.Function.prototype._base = function (value, state, options) { + + return { + value: value, + errors: (typeof value === 'function') ? null : Errors.create('function.base', null, state, options) + }; +}; + + +module.exports = new internals.Function(); +},{"./any":2,"./errors":8,"hoek":16}],10:[function(require,module,exports){ +// Load modules + +var Hoek = require('hoek'); +var Any = require('./any'); +var Cast = require('./cast'); +var Ref = require('./ref'); + + +// Declare internals + +var internals = { + alternatives: require('./alternatives'), + array: require('./array'), + boolean: require('./boolean'), + binary: require('./binary'), + date: require('./date'), + func: require('./function'), + number: require('./number'), + object: require('./object'), + string: require('./string') +}; + + +internals.root = function () { + + var any = new Any(); + + var root = any.clone(); + root.any = function () { + + return any; + }; + + root.alternatives = root.alt = function () { + + return arguments.length ? internals.alternatives.try.apply(internals.alternatives, arguments) : internals.alternatives; + }; + + root.array = function () { + + return internals.array; + }; + + root.boolean = root.bool = function () { + + return internals.boolean; + }; + + root.binary = function () { + + return internals.binary; + }; + + root.date = function () { + + return internals.date; + }; + + root.func = function () { + + return internals.func; + }; + + root.number = function () { + + return internals.number; + }; + + root.object = function () { + + return arguments.length ? internals.object.keys.apply(internals.object, arguments) : internals.object; + }; + + root.string = function () { + + return internals.string; + }; + + root.ref = function () { + + return Ref.create.apply(null, arguments); + }; + + root.isRef = function (ref) { + + return Ref.isRef(ref); + }; + + root.validate = function (value /*, [schema], [options], callback */) { + + var last = arguments[arguments.length - 1]; + var callback = typeof last === 'function' ? last : null; + + var count = arguments.length - (callback ? 1 : 0); + if (count === 1) { + return any.validate(value, callback); + } + + var options = count === 3 ? arguments[2] : {}; + var schema = Cast.schema(arguments[1]); + + return schema._validateWithOptions(value, options, callback); + }; + + root.describe = function () { + + var schema = arguments.length ? Cast.schema(arguments[0]) : any; + return schema.describe(); + }; + + root.compile = function (schema) { + + return Cast.schema(schema); + }; + + root.assert = function (value, schema) { + + var error = root.validate(value, schema).error; + if (error) { + throw new Error(error.annotate()); + } + }; + + return root; +}; + + +module.exports = internals.root(); + +},{"./alternatives":1,"./any":2,"./array":3,"./binary":4,"./boolean":5,"./cast":6,"./date":7,"./function":9,"./number":12,"./object":13,"./ref":14,"./string":15,"hoek":16}],11:[function(require,module,exports){ +// Load modules + + +// Declare internals + +var internals = {}; + + +exports.errors = { + root: 'value', + any: { + unknown: 'is not allowed', + invalid: 'contains an invalid value', + empty: 'is not allowed to be empty', + required: 'is required', + allowOnly: 'must be one of {{valids}}' + }, + alternatives: { + base: 'not matching any of the allowed alternatives' + }, + array: { + base: 'must be an array', + includes: 'position {{pos}} does not match any of the allowed types', + includesOne: 'position {{pos}} fails because {{reason}}', + excludes: 'position {{pos}} contains an excluded value', + min: 'must contain at least {{limit}} items', + max: 'must contain less than or equal to {{limit}} items', + length: 'must contain {{limit}} items' + }, + boolean: { + base: 'must be a boolean' + }, + binary: { + base: 'must be a buffer or a string', + min: 'must be at least {{limit}} bytes', + max: 'must be less than or equal to {{limit}} bytes', + length: 'must be {{limit}} bytes' + }, + date: { + base: 'must be a number of milliseconds or valid date string', + min: 'must be larger than or equal to {{limit}}', + max: 'must be less than or equal to {{limit}}' + }, + function: { + base: 'must be a Function' + }, + object: { + base: 'must be an object', + min: 'must have at least {{limit}} children', + max: 'must have less than or equal to {{limit}} children', + length: 'must have {{limit}} children', + allowUnknown: 'is not allowed', + with: 'missing required peer {{peer}}', + without: 'conflict with forbidden peer {{peer}}', + missing: 'must contain at least one of {{peers}}', + xor: 'contains a conflict between exclusive peers {{peers}}', + or: 'must contain at least one of {{peers}}', + and: 'contains {{present}} without its required peers {{missing}}', + assert: 'validation failed because {{ref}} failed to {{message}}', + rename: { + multiple: 'cannot rename child {{from}} because multiple renames are disabled and another key was already renamed to {{to}}', + override: 'cannot rename child {{from}} because override is disabled and target {{to}} exists' + } + }, + number: { + base: 'must be a number', + min: 'must be larger than or equal to {{limit}}', + max: 'must be less than or equal to {{limit}}', + float: 'must be a float or double', + integer: 'must be an integer', + negative: 'must be a negative number', + positive: 'must be a positive number' + }, + string: { + base: 'must be a string', + min: 'length must be at least {{limit}} characters long', + max: 'length must be less than or equal to {{limit}} characters long', + length: 'length must be {{limit}} characters long', + alphanum: 'must only contain alpha-numeric characters', + token: 'must only contain alpha-numeric and underscore characters', + regex: 'fails to match the required pattern', + email: 'must be a valid email', + isoDate: 'must be a valid ISO 8601 date', + guid: 'must be a valid GUID', + hostname: 'must be a valid hostname', + lowercase: 'must only contain lowercase characters', + uppercase: 'must only contain uppercase characters', + trim: 'must not have leading or trailing whitespace' + } +}; + +},{}],12:[function(require,module,exports){ +// Load modules + +var Any = require('./any'); +var Errors = require('./errors'); +var Hoek = require('hoek'); + + +// Declare internals + +var internals = {}; + + +internals.Number = function () { + + Any.call(this); + this._type = 'number'; +}; + +Hoek.inherits(internals.Number, Any); + + +internals.Number.prototype._base = function (value, state, options) { + + var result = { + errors: null, + value: value + }; + + if (typeof value === 'string' && + options.convert) { + + var number = parseFloat(value); + result.value = (isNaN(number) || !isFinite(value)) ? NaN : number; + } + + result.errors = (typeof result.value === 'number' && !isNaN(result.value)) ? null : Errors.create('number.base', null, state, options); + return result; +}; + + +internals.Number.prototype.min = function (limit) { + + Hoek.assert(Hoek.isInteger(limit), 'limit must be an integer'); + + return this._test('min', limit, function (value, state, options) { + + if (value >= limit) { + return null; + } + + return Errors.create('number.min', { limit: limit }, state, options); + }); +}; + + +internals.Number.prototype.max = function (limit) { + + Hoek.assert(Hoek.isInteger(limit), 'limit must be an integer'); + + return this._test('max', limit, function (value, state, options) { + + if (value <= limit) { + return null; + } + + return Errors.create('number.max', { limit: limit }, state, options); + }); +}; + + +internals.Number.prototype.integer = function () { + + return this._test('integer', undefined, function (value, state, options) { + + return Hoek.isInteger(value) ? null : Errors.create('number.integer', null, state, options); + }); +}; + + +internals.Number.prototype.negative = function () { + + return this._test('negative', undefined, function (value, state, options) { + + if (value < 0) { + return null; + } + + return Errors.create('number.negative', null, state, options); + }); +}; + + +internals.Number.prototype.positive = function () { + + return this._test('positive', undefined, function (value, state, options) { + + if (value > 0) { + return null; + } + + return Errors.create('number.positive', null, state, options); + }); +}; + + +module.exports = new internals.Number(); + +},{"./any":2,"./errors":8,"hoek":16}],13:[function(require,module,exports){ +// Load modules + +var Hoek = require('hoek'); +var Topo = require('topo'); +var Any = require('./any'); +var Cast = require('./cast'); +var Ref = require('./ref'); +var Errors = require('./errors'); + + +// Declare internals + +var internals = {}; + + +internals.Object = function () { + + Any.call(this); + this._type = 'object'; + this._inner.children = null; + this._inner.renames = []; + this._inner.dependencies = []; + this._inner.patterns = []; +}; + +Hoek.inherits(internals.Object, Any); + + +internals.Object.prototype._base = function (value, state, options) { + + var target = value; + var errors = []; + var finish = function () { + + return { + value: target, + errors: errors.length ? errors : null + }; + }; + + if (typeof value === 'string' && + options.convert) { + + try { + value = JSON.parse(value); + } + catch (err) { } + } + + if (!value || + typeof value !== 'object' || + Array.isArray(value)) { + + errors.push(Errors.create('object.base', null, state, options)); + return finish(); + } + + // Ensure target is a local copy (parsed) or shallow copy + + if (target === value) { + target = {}; + target.__proto__ = Object.getPrototypeOf(value); + var valueKeys = Object.keys(value); + for (var t = 0, tl = valueKeys.length; t < tl; ++t) { + target[valueKeys[t]] = value[valueKeys[t]]; + } + } + else { + target = value; + } + + // Rename keys + + var renamed = {}; + for (var r = 0, rl = this._inner.renames.length; r < rl; ++r) { + var item = this._inner.renames[r]; + + if (target[item.from] === undefined) { + continue; + } + + if (!item.options.multiple && + renamed[item.to]) { + + errors.push(Errors.create('object.rename.multiple', { from: item.from, to: item.to }, state, options)); + if (options.abortEarly) { + return finish(); + } + } + + if (target.hasOwnProperty(item.to) && + !item.options.override && + !renamed[item.to]) { + + errors.push(Errors.create('object.rename.override', { from: item.from, to: item.to }, state, options)); + if (options.abortEarly) { + return finish(); + } + } + + target[item.to] = target[item.from]; + renamed[item.to] = true; + + if (!item.options.alias) { + delete target[item.from]; + } + } + + // Validate dependencies + + for (var d = 0, dl = this._inner.dependencies.length; d < dl; ++d) { + var dep = this._inner.dependencies[d]; + var err = internals[dep.type](dep.key !== null && value[dep.key], dep.peers, target, { key: dep.key, path: (state.path ? state.path + '.' : '') + dep.key }, options); + if (err) { + errors.push(err); + if (options.abortEarly) { + return finish(); + } + } + } + + // Validate schema + + if (!this._inner.children && // null allows any keys + !this._inner.patterns.length) { + + return finish(); + } + + var unprocessed = Hoek.mapToObject(Object.keys(target)); + var key; + + if (this._inner.children) { + for (var i = 0, il = this._inner.children.length; i < il; ++i) { + var child = this._inner.children[i]; + var key = child.key; + var item = target[key]; + + delete unprocessed[key]; + + var localState = { key: key, path: (state.path ? state.path + '.' : '') + key, parent: target, reference: state.reference }; + var result = child.schema._validate(item, localState, options); + if (result.errors) { + errors = errors.concat(result.errors); + if (options.abortEarly) { + return finish(); + } + } + + if (result.value !== undefined) { + target[key] = result.value; + } + } + } + + // Unknown keys + + var unprocessedKeys = Object.keys(unprocessed); + if (unprocessedKeys.length && + this._inner.patterns.length) { + + for (i = 0, il = unprocessedKeys.length; i < il; ++i) { + var key = unprocessedKeys[i]; + + for (var p = 0, pl = this._inner.patterns.length; p < pl; ++p) { + var pattern = this._inner.patterns[p]; + + if (pattern.regex.test(key)) { + delete unprocessed[key]; + + var item = target[key]; + var localState = { key: key, path: (state.path ? state.path + '.' : '') + key, parent: target, reference: state.reference }; + var result = pattern.rule._validate(item, localState, options); + if (result.errors) { + errors = errors.concat(result.errors); + if (options.abortEarly) { + return finish(); + } + } + + if (result.value !== undefined) { + target[key] = result.value; + } + } + } + } + + unprocessedKeys = Object.keys(unprocessed); + } + + if (unprocessedKeys.length) { + if (options.stripUnknown || + options.skipFunctions) { + + var hasFunctions = false; + for (var k = 0, kl = unprocessedKeys.length; k < kl; ++k) { + key = unprocessedKeys[k]; + + if (options.stripUnknown) { + delete target[key]; + } + else if (typeof target[key] === 'function') { + delete unprocessed[key]; + hasFunctions = true; + } + } + + if (options.stripUnknown) { + return finish(); + } + + if (hasFunctions) { + unprocessedKeys = Object.keys(unprocessed); + } + } + + if (unprocessedKeys.length && + (this._flags.allowUnknown !== undefined ? !this._flags.allowUnknown : !options.allowUnknown)) { + + for (var e = 0, el = unprocessedKeys.length; e < el; ++e) { + errors.push(Errors.create('object.allowUnknown', null, { key: unprocessedKeys[e], path: state.path }, options)); + } + } + } + + return finish(); +}; + + +internals.Object.prototype.keys = function (schema) { + + Hoek.assert(schema === null || schema === undefined || typeof schema === 'object', 'Object schema must be a valid object'); + Hoek.assert(!schema || !schema.isJoi, 'Object schema cannot be a joi schema'); + + var obj = this.clone(); + + if (!schema) { + obj._inner.children = null; + return obj; + } + + var children = Object.keys(schema); + + if (!children.length) { + obj._inner.children = []; + return obj; + } + + var topo = new Topo(); + if (obj._inner.children) { + for (var i = 0, il = obj._inner.children.length; i < il; ++i) { + var child = obj._inner.children[i]; + topo.add(child, { after: child._refs, group: child.key }); + } + } + + for (var c = 0, cl = children.length; c < cl; ++c) { + var key = children[c]; + var child = schema[key]; + var cast = Cast.schema(child); + topo.add({ key: key, schema: cast }, { after: cast._refs, group: key }); + } + + obj._inner.children = topo.nodes; + + return obj; +}; + + +internals.Object.prototype.unknown = function (allow) { + + var obj = this.clone(); + obj._flags.allowUnknown = (allow !== false); + return obj; +}; + + +internals.Object.prototype.length = function (limit) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + + return this._test('length', limit, function (value, state, options) { + + if (Object.keys(value).length === limit) { + return null; + } + + return Errors.create('object.length', { limit: limit }, state, options); + }); +}; + + +internals.Object.prototype.min = function (limit) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + + return this._test('min', limit, function (value, state, options) { + + if (Object.keys(value).length >= limit) { + return null; + } + + return Errors.create('object.min', { limit: limit }, state, options); + }); +}; + + +internals.Object.prototype.max = function (limit) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + + return this._test('max', limit, function (value, state, options) { + + if (Object.keys(value).length <= limit) { + return null; + } + + return Errors.create('object.max', { limit: limit }, state, options); + }); +}; + + +internals.Object.prototype.pattern = function (regex, schema) { + + Hoek.assert(regex instanceof RegExp, 'Invalid regular expression'); + Hoek.assert(schema !== undefined, 'Invalid rule'); + + var obj = this.clone(); + obj._inner.patterns.push({ regex: regex, rule: Cast.schema(schema) }); + return obj; +}; + + +internals.Object.prototype.with = function (key, peers) { + + return this._dependency('with', key, peers); +}; + + +internals.Object.prototype.without = function (key, peers) { + + return this._dependency('without', key, peers); +}; + + +internals.Object.prototype.xor = function () { + + var peers = Hoek.flatten(Array.prototype.slice.call(arguments)); + return this._dependency('xor', null, peers); +}; + + +internals.Object.prototype.or = function () { + + var peers = Hoek.flatten(Array.prototype.slice.call(arguments)); + return this._dependency('or', null, peers); +}; + + +internals.Object.prototype.and = function () { + + var peers = Hoek.flatten(Array.prototype.slice.call(arguments)); + return this._dependency('and', null, peers); +}; + + +internals.renameDefaults = { + alias: false, // Keep old value in place + multiple: false, // Allow renaming multiple keys into the same target + override: false // Overrides an existing key +}; + + +internals.Object.prototype.rename = function (from, to, options) { + + Hoek.assert(typeof from === 'string', 'Rename missing the from argument'); + Hoek.assert(typeof to === 'string', 'Rename missing the to argument'); + Hoek.assert(to !== from, 'Cannot rename key to same name:', from); + + for (var i = 0, il = this._inner.renames.length; i < il; ++i) { + Hoek.assert(this._inner.renames[i].from !== from, 'Cannot rename the same key multiple times'); + } + + var obj = this.clone(); + + obj._inner.renames.push({ + from: from, + to: to, + options: Hoek.applyToDefaults(internals.renameDefaults, options || {}) + }); + + return obj; +}; + + +internals.Object.prototype._dependency = function (type, key, peers) { + + peers = [].concat(peers); + for (var i = 0, li = peers.length; i < li; i++) { + Hoek.assert(typeof peers[i] === 'string', type, 'peers must be a string or array of strings'); + } + + var obj = this.clone(); + obj._inner.dependencies.push({ type: type, key: key, peers: peers }); + return obj; +}; + + +internals.with = function (value, peers, parent, state, options) { + + if (value === undefined) { + return null; + } + + for (var i = 0, il = peers.length; i < il; ++i) { + var peer = peers[i]; + if (!parent.hasOwnProperty(peer) || + parent[peer] === undefined) { + + return Errors.create('object.with', { peer: peer }, state, options); + } + } + + return null; +}; + + +internals.without = function (value, peers, parent, state, options) { + + if (value === undefined) { + return null; + } + + for (var i = 0, il = peers.length; i < il; ++i) { + var peer = peers[i]; + if (parent.hasOwnProperty(peer) && + parent[peer] !== undefined) { + + return Errors.create('object.without', { peer: peer }, state, options); + } + } + + return null; +}; + + +internals.xor = function (value, peers, parent, state, options) { + + var present = []; + for (var i = 0, il = peers.length; i < il; ++i) { + var peer = peers[i]; + if (parent.hasOwnProperty(peer) && + parent[peer] !== undefined) { + + present.push(peer); + } + } + + if (present.length === 1) { + return null; + } + + if (present.length === 0) { + return Errors.create('object.missing', { peers: peers }, state, options); + } + + return Errors.create('object.xor', { peers: peers }, state, options); +}; + + +internals.or = function (value, peers, parent, state, options) { + + for (var i = 0, il = peers.length; i < il; ++i) { + var peer = peers[i]; + if (parent.hasOwnProperty(peer) && + parent[peer] !== undefined) { + return null; + } + } + + return Errors.create('object.missing', { peers: peers }, state, options); +}; + + +internals.and = function (value, peers, parent, state, options) { + + var missing = []; + var present = []; + var count = peers.length; + for (var i = 0; i < count; ++i) { + var peer = peers[i]; + if (!parent.hasOwnProperty(peer) || + parent[peer] === undefined) { + + missing.push(peer); + } + else { + present.push(peer); + } + } + + var aon = (missing.length === count || present.length === count); + return !aon ? Errors.create('object.and', { present: present, missing: missing }, state, options) : null; +}; + + +internals.Object.prototype.describe = function (shallow) { + + var description = Any.prototype.describe.call(this); + + if (this._inner.children && + !shallow) { + + description.children = {}; + for (var i = 0, il = this._inner.children.length; i < il; ++i) { + var child = this._inner.children[i]; + description.children[child.key] = child.schema.describe(); + } + } + + if (this._inner.dependencies.length) { + description.dependencies = Hoek.clone(this._inner.dependencies); + } + + return description; +}; + + +internals.Object.prototype.assert = function (ref, schema, message) { + + ref = Cast.ref(ref); + Hoek.assert(ref.isContext || ref.depth > 1, 'Cannot use assertions for root level references - use direct key rules instead'); + + var cast = Cast.schema(schema); + + return this._test('assert', { cast: cast, ref: ref }, function (value, state, options) { + + var result = cast._validate(ref(value), null, options, value); + if (!result.errors) { + return null; + } + + return Errors.create('object.assert', { ref: ref.path.join('.'), message: message }, state, options); + }); +}; + + +module.exports = new internals.Object(); + +},{"./any":2,"./cast":6,"./errors":8,"./ref":14,"hoek":16,"topo":21}],14:[function(require,module,exports){ +// Load modules + +var Hoek = require('hoek'); + + +// Declare internals + +var internals = {}; + + +exports.create = function (key, options) { + + Hoek.assert(typeof key === 'string', 'Invalid reference key:', key); + + var settings = Hoek.clone(options); // options can be reused and modified + + var ref = function (value, validationOptions) { + + return Hoek.reach(ref.isContext ? validationOptions.context : value, ref.key, settings); + }; + + ref.isContext = (key[0] === ((settings && settings.contextPrefix) || '$')); + ref.key = (ref.isContext ? key.slice(1) : key); + ref.path = ref.key.split((settings && settings.separator) || '.'); + ref.depth = ref.path.length; + ref.root = ref.path[0]; + ref.isJoi = true; + + ref.toString = function () { + + return (ref.isContext ? 'context:' : 'ref:') + ref.key; + }; + + return ref; +}; + + +exports.isRef = function (ref) { + + return typeof ref === 'function' && ref.isJoi; +}; + + +exports.push = function (array, ref) { + + if (exports.isRef(ref) && + !ref.isContext) { + + array.push(ref.root); + } +}; +},{"hoek":16}],15:[function(require,module,exports){ +(function (Buffer){ +// Load modules + +var Net = require('net'); +var Hoek = require('hoek'); +var Isemail = require('isemail'); +var Any = require('./any'); +var Errors = require('./errors'); + + +// Declare internals + +var internals = {}; + + +internals.String = function () { + + Any.call(this); + this._type = 'string'; + this._invalids.add(''); +}; + +Hoek.inherits(internals.String, Any); + + +internals.String.prototype._base = function (value, state, options) { + + if (typeof value === 'string' && + options.convert) { + + if (this._flags.case) { + value = (this._flags.case === 'upper' ? value.toLocaleUpperCase() : value.toLocaleLowerCase()); + } + + if (this._flags.trim) { + value = value.trim(); + } + } + + return { + value: value, + errors: (typeof value === 'string') ? null : Errors.create('string.base', null, state, options) + }; +}; + + +internals.String.prototype.insensitive = function () { + + var obj = this.clone(); + obj._flags.insensitive = true; + return obj; +}; + + +internals.String.prototype.min = function (limit, encoding) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); + + return this._test('min', limit, function (value, state, options) { + + var length = encoding ? Buffer.byteLength(value, encoding) : value.length; + if (length >= limit) { + return null; + } + + return Errors.create('string.min', { limit: limit }, state, options); + }); +}; + + +internals.String.prototype.max = function (limit, encoding) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); + + return this._test('max', limit, function (value, state, options) { + + var length = encoding ? Buffer.byteLength(value, encoding) : value.length; + if (length <= limit) { + return null; + } + + return Errors.create('string.max', { limit: limit }, state, options); + }); +}; + + +internals.String.prototype.length = function (limit, encoding) { + + Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer'); + Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); + + return this._test('length', limit, function (value, state, options) { + + var length = encoding ? Buffer.byteLength(value, encoding) : value.length; + if (length === limit) { + return null; + } + + return Errors.create('string.length', { limit: limit }, state, options); + }); +}; + + +internals.String.prototype.regex = function (pattern) { + + Hoek.assert(pattern instanceof RegExp, 'pattern must be a RegExp'); + + return this._test('regex', pattern, function (value, state, options) { + + if (pattern.test(value)) { + return null; + } + + return Errors.create('string.regex', null, state, options); + }); +}; + + +internals.String.prototype.alphanum = function () { + + return this._test('alphanum', undefined, function (value, state, options) { + + if (/^[a-zA-Z0-9]+$/.test(value)) { + return null; + } + + return Errors.create('string.alphanum', null, state, options); + }); +}; + + +internals.String.prototype.token = function () { + + return this._test('token', undefined, function (value, state, options) { + + if (/^\w+$/.test(value)) { + return null; + } + + return Errors.create('string.token', null, state, options); + }); +}; + + +internals.String.prototype.email = function () { + + return this._test('email', undefined, function (value, state, options) { + + if (Isemail(value)) { + return null; + } + + return Errors.create('string.email', null, state, options); + }); +}; + + +internals.String.prototype.isoDate = function () { + + var regex = /^(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))$/; + + return this._test('isoDate', undefined, function (value, state, options) { + + if (regex.test(value)) { + return null; + } + + return Errors.create('string.isoDate', null, state, options); + }); +}; + + +internals.String.prototype.guid = function () { + + var regex = /^[A-F0-9]{8}(?:-?[A-F0-9]{4}){3}-?[A-F0-9]{12}$/i; + var regex2 = /^\{[A-F0-9]{8}(?:-?[A-F0-9]{4}){3}-?[A-F0-9]{12}\}$/i; + + return this._test('guid', undefined, function (value, state, options) { + + if (regex.test(value) || regex2.test(value)) { + return null; + } + + return Errors.create('string.guid', null, state, options); + }); +}; + + +internals.String.prototype.hostname = function () { + + var regex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/; + + return this._test('hostname', undefined, function (value, state, options) { + + if ((value.length <= 255 && regex.test(value)) || + Net.isIPv6(value)) { + + return null; + } + + return Errors.create("string.hostname", null, state, options); + }); +}; + + +internals.String.prototype.lowercase = function () { + + var obj = this._test('lowercase', undefined, function (value, state, options) { + + if (options.convert || + value === value.toLocaleLowerCase()) { + + return null; + } + + return Errors.create('string.lowercase', null, state, options); + }); + + obj._flags.case = 'lower'; + return obj; +}; + + +internals.String.prototype.uppercase = function (options) { + + var obj = this._test('uppercase', undefined, function (value, state, options) { + + if (options.convert || + value === value.toLocaleUpperCase()) { + + return null; + } + + return Errors.create('string.uppercase', null, state, options); + }); + + obj._flags.case = 'upper'; + return obj; +}; + + +internals.String.prototype.trim = function () { + + var obj = this._test('trim', undefined, function (value, state, options) { + + if (options.convert || + value === value.trim()) { + + return null; + } + + return Errors.create('string.trim', null, state, options); + }); + + obj._flags.trim = true; + return obj; +}; + + +module.exports = new internals.String(); + +}).call(this,require("buffer").Buffer) +},{"./any":2,"./errors":8,"buffer":24,"hoek":16,"isemail":19,"net":23}],16:[function(require,module,exports){ +module.exports = require('./lib'); +},{"./lib":18}],17:[function(require,module,exports){ +(function (Buffer){ +// Declare internals + +var internals = {}; + + +exports.escapeJavaScript = function (input) { + + if (!input) { + return ''; + } + + var escaped = ''; + + for (var i = 0, il = input.length; i < il; ++i) { + + var charCode = input.charCodeAt(i); + + if (internals.isSafe(charCode)) { + escaped += input[i]; + } + else { + escaped += internals.escapeJavaScriptChar(charCode); + } + } + + return escaped; +}; + + +exports.escapeHtml = function (input) { + + if (!input) { + return ''; + } + + var escaped = ''; + + for (var i = 0, il = input.length; i < il; ++i) { + + var charCode = input.charCodeAt(i); + + if (internals.isSafe(charCode)) { + escaped += input[i]; + } + else { + escaped += internals.escapeHtmlChar(charCode); + } + } + + return escaped; +}; + + +internals.escapeJavaScriptChar = function (charCode) { + + if (charCode >= 256) { + return '\\u' + internals.padLeft('' + charCode, 4); + } + + var hexValue = new Buffer(String.fromCharCode(charCode), 'ascii').toString('hex'); + return '\\x' + internals.padLeft(hexValue, 2); +}; + + +internals.escapeHtmlChar = function (charCode) { + + var namedEscape = internals.namedHtml[charCode]; + if (typeof namedEscape !== 'undefined') { + return namedEscape; + } + + if (charCode >= 256) { + return '&#' + charCode + ';'; + } + + var hexValue = new Buffer(String.fromCharCode(charCode), 'ascii').toString('hex'); + return '&#x' + internals.padLeft(hexValue, 2) + ';'; +}; + + +internals.padLeft = function (str, len) { + + while (str.length < len) { + str = '0' + str; + } + + return str; +}; + + +internals.isSafe = function (charCode) { + + return (typeof internals.safeCharCodes[charCode] !== 'undefined'); +}; + + +internals.namedHtml = { + '38': '&', + '60': '<', + '62': '>', + '34': '"', + '160': ' ', + '162': '¢', + '163': '£', + '164': '¤', + '169': '©', + '174': '®' +}; + + +internals.safeCharCodes = (function () { + + var safe = {}; + + for (var i = 32; i < 123; ++i) { + + if ((i >= 97) || // a-z + (i >= 65 && i <= 90) || // A-Z + (i >= 48 && i <= 57) || // 0-9 + i === 32 || // space + i === 46 || // . + i === 44 || // , + i === 45 || // - + i === 58 || // : + i === 95) { // _ + + safe[i] = null; + } + } + + return safe; +}()); +}).call(this,require("buffer").Buffer) +},{"buffer":24}],18:[function(require,module,exports){ +(function (process,Buffer){ +// Load modules + +var Path = require('path'); +var Util = require('util'); +var Escape = require('./escape'); + + +// Declare internals + +var internals = {}; + + +// Clone object or array + +exports.clone = function (obj, seen) { + + if (typeof obj !== 'object' || + obj === null) { + + return obj; + } + + seen = seen || { orig: [], copy: [] }; + + var lookup = seen.orig.indexOf(obj); + if (lookup !== -1) { + return seen.copy[lookup]; + } + + var newObj; + var cloneDeep = false; + + if (!Array.isArray(obj)) { + if (Buffer.isBuffer(obj)) { + newObj = new Buffer(obj); + } + else if (obj instanceof Date) { + newObj = new Date(obj.getTime()); + } + else if (obj instanceof RegExp) { + newObj = new RegExp(obj); + } + else { + var proto = Object.getPrototypeOf(obj); + if (!proto || proto.isImmutable) { + newObj = obj; + } + else { + newObj = {}; + newObj.__proto__ = proto; + cloneDeep = true; + } + } + } + else { + newObj = []; + cloneDeep = true; + } + + seen.orig.push(obj); + seen.copy.push(newObj); + + if (cloneDeep) { + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + newObj[i] = exports.clone(obj[i], seen); + } + } + } + + return newObj; +}; + + +// Merge all the properties of source into target, source wins in conflict, and by default null and undefined from source are applied + +exports.merge = function (target, source, isNullOverride /* = true */, isMergeArrays /* = true */) { + + exports.assert(target && typeof target === 'object', 'Invalid target value: must be an object'); + exports.assert(source === null || source === undefined || typeof source === 'object', 'Invalid source value: must be null, undefined, or an object'); + + if (!source) { + return target; + } + + if (Array.isArray(source)) { + exports.assert(Array.isArray(target), 'Cannot merge array onto an object'); + if (isMergeArrays === false) { // isMergeArrays defaults to true + target.length = 0; // Must not change target assignment + } + + for (var i = 0, il = source.length; i < il; ++i) { + target.push(exports.clone(source[i])); + } + + return target; + } + + var keys = Object.keys(source); + for (var k = 0, kl = keys.length; k < kl; ++k) { + var key = keys[k]; + var value = source[key]; + if (value && + typeof value === 'object') { + + if (!target[key] || + typeof target[key] !== 'object' || + (Array.isArray(target[key]) ^ Array.isArray(value)) || + value instanceof Date || + Buffer.isBuffer(value) || + value instanceof RegExp) { + + target[key] = exports.clone(value); + } + else { + exports.merge(target[key], value, isNullOverride, isMergeArrays); + } + } + else { + if (value !== null && + value !== undefined) { // Explicit to preserve empty strings + + target[key] = value; + } + else if (isNullOverride !== false) { // Defaults to true + target[key] = value; + } + } + } + + return target; +}; + + +// Apply options to a copy of the defaults + +exports.applyToDefaults = function (defaults, options) { + + exports.assert(defaults && typeof defaults === 'object', 'Invalid defaults value: must be an object'); + exports.assert(!options || options === true || typeof options === 'object', 'Invalid options value: must be true, falsy or an object'); + + if (!options) { // If no options, return null + return null; + } + + var copy = exports.clone(defaults); + + if (options === true) { // If options is set to true, use defaults + return copy; + } + + return exports.merge(copy, options, false, false); +}; + + +// Clone an object except for the listed keys which are shallow copied + +exports.cloneWithShallow = function (source, keys) { + + if (!source || + typeof source !== 'object') { + + return source; + } + + return internals.shallow(source, keys, function () { + + return exports.clone(source); + }); +}; + + +// Apply options to defaults except for the listed keys which are shallow copied from option without merging + +exports.applyToDefaultsWithShallow = function (defaults, options, keys) { + + return internals.shallow(options, keys, function () { + + return exports.applyToDefaults(defaults, options); + }); +}; + + +internals.shallow = function (source, keys, clone) { + + // Move shallow copy items to storage + + var storage = {}; + for (var i = 0, il = keys.length; i < il; ++i) { + var key = keys[i]; + if (source.hasOwnProperty(key)) { + storage[key] = source[key]; + source[key] = undefined; + } + } + + // Deep copy the rest + + var copy = clone(); + + // Shallow copy the stored items + + for (i = 0; i < il; ++i) { + var key = keys[i]; + if (storage.hasOwnProperty(key)) { + source[key] = storage[key]; + copy[key] = storage[key]; + } + } + + return copy; +}; + + +// Remove duplicate items from array + +exports.unique = function (array, key) { + + var index = {}; + var result = []; + + for (var i = 0, il = array.length; i < il; ++i) { + var id = (key ? array[i][key] : array[i]); + if (index[id] !== true) { + + result.push(array[i]); + index[id] = true; + } + } + + return result; +}; + + +// Convert array into object + +exports.mapToObject = function (array, key) { + + if (!array) { + return null; + } + + var obj = {}; + for (var i = 0, il = array.length; i < il; ++i) { + if (key) { + if (array[i][key]) { + obj[array[i][key]] = true; + } + } + else { + obj[array[i]] = true; + } + } + + return obj; +}; + + +// Find the common unique items in two arrays + +exports.intersect = function (array1, array2, justFirst) { + + if (!array1 || !array2) { + return []; + } + + var common = []; + var hash = (Array.isArray(array1) ? exports.mapToObject(array1) : array1); + var found = {}; + for (var i = 0, il = array2.length; i < il; ++i) { + if (hash[array2[i]] && !found[array2[i]]) { + if (justFirst) { + return array2[i]; + } + + common.push(array2[i]); + found[array2[i]] = true; + } + } + + return (justFirst ? null : common); +}; + + +// Flatten array + +exports.flatten = function (array, target) { + + var result = target || []; + + for (var i = 0, il = array.length; i < il; ++i) { + if (Array.isArray(array[i])) { + exports.flatten(array[i], result); + } + else { + result.push(array[i]); + } + } + + return result; +}; + + +// Convert an object key chain string ('a.b.c') to reference (object[a][b][c]) + +exports.reach = function (obj, chain, options) { + + options = options || {}; + if (typeof options === 'string') { + options = { separator: options }; + } + + var path = chain.split(options.separator || '.'); + var ref = obj; + for (var i = 0, il = path.length; i < il; ++i) { + if (!ref || + !ref.hasOwnProperty(path[i]) || + (typeof ref !== 'object' && options.functions === false)) { // Only object and function can have properties + + exports.assert(!options.strict || i + 1 === il, 'Missing segment', path[i], 'in reach path ', chain); + exports.assert(typeof ref === 'object' || options.functions === true || typeof ref !== 'function', 'Invalid segment', path[i], 'in reach path ', chain); + ref = options.default || undefined; + break; + } + + ref = ref[path[i]]; + } + + return ref; +}; + + +exports.formatStack = function (stack) { + + var trace = []; + for (var i = 0, il = stack.length; i < il; ++i) { + var item = stack[i]; + trace.push([item.getFileName(), item.getLineNumber(), item.getColumnNumber(), item.getFunctionName(), item.isConstructor()]); + } + + return trace; +}; + + +exports.formatTrace = function (trace) { + + var display = []; + + for (var i = 0, il = trace.length; i < il; ++i) { + var row = trace[i]; + display.push((row[4] ? 'new ' : '') + row[3] + ' (' + row[0] + ':' + row[1] + ':' + row[2] + ')'); + } + + return display; +}; + + +exports.callStack = function (slice) { + + // http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi + + var v8 = Error.prepareStackTrace; + Error.prepareStackTrace = function (err, stack) { + + return stack; + }; + + var capture = {}; + Error.captureStackTrace(capture, arguments.callee); + var stack = capture.stack; + + Error.prepareStackTrace = v8; + + var trace = exports.formatStack(stack); + + if (slice) { + return trace.slice(slice); + } + + return trace; +}; + + +exports.displayStack = function (slice) { + + var trace = exports.callStack(slice === undefined ? 1 : slice + 1); + + return exports.formatTrace(trace); +}; + + +exports.abortThrow = false; + + +exports.abort = function (message, hideStack) { + + if (process.env.NODE_ENV === 'test' || exports.abortThrow === true) { + throw new Error(message || 'Unknown error'); + } + + var stack = ''; + if (!hideStack) { + stack = exports.displayStack(1).join('\n\t'); + } + console.log('ABORT: ' + message + '\n\t' + stack); + process.exit(1); +}; + + +exports.assert = function (condition /*, msg1, msg2, msg3 */) { + + if (condition) { + return; + } + + var msgs = []; + for (var i = 1, il = arguments.length; i < il; ++i) { + msgs.push(arguments[i]); // Avoids Array.slice arguments leak, allowing for V8 optimizations + } + + msgs = msgs.map(function (msg) { + + return typeof msg === 'string' ? msg : msg instanceof Error ? msg.message : JSON.stringify(msg); + }); + throw new Error(msgs.join(' ') || 'Unknown error'); +}; + + +exports.Timer = function () { + + this.ts = 0; + this.reset(); +}; + + +exports.Timer.prototype.reset = function () { + + this.ts = Date.now(); +}; + + +exports.Timer.prototype.elapsed = function () { + + return Date.now() - this.ts; +}; + + +exports.Bench = function () { + + this.ts = 0; + this.reset(); +}; + + +exports.Bench.prototype.reset = function () { + + this.ts = exports.Bench.now(); +}; + + +exports.Bench.prototype.elapsed = function () { + + return exports.Bench.now() - this.ts; +}; + + +exports.Bench.now = function () { + + var ts = process.hrtime(); + return (ts[0] * 1e3) + (ts[1] / 1e6); +}; + + +// Escape string for Regex construction + +exports.escapeRegex = function (string) { + + // Escape ^$.*+-?=!:|\/()[]{}, + return string.replace(/[\^\$\.\*\+\-\?\=\!\:\|\\\/\(\)\[\]\{\}\,]/g, '\\$&'); +}; + + +// Base64url (RFC 4648) encode + +exports.base64urlEncode = function (value, encoding) { + + var buf = (Buffer.isBuffer(value) ? value : new Buffer(value, encoding || 'binary')); + return buf.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, ''); +}; + + +// Base64url (RFC 4648) decode + +exports.base64urlDecode = function (value, encoding) { + + if (value && + !/^[\w\-]*$/.test(value)) { + + return new Error('Invalid character'); + } + + try { + var buf = new Buffer(value, 'base64'); + return (encoding === 'buffer' ? buf : buf.toString(encoding || 'binary')); + } + catch (err) { + return err; + } +}; + + +// Escape attribute value for use in HTTP header + +exports.escapeHeaderAttribute = function (attribute) { + + // Allowed value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9, \, " + + exports.assert(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~\"\\]*$/.test(attribute), 'Bad attribute value (' + attribute + ')'); + + return attribute.replace(/\\/g, '\\\\').replace(/\"/g, '\\"'); // Escape quotes and slash +}; + + +exports.escapeHtml = function (string) { + + return Escape.escapeHtml(string); +}; + + +exports.escapeJavaScript = function (string) { + + return Escape.escapeJavaScript(string); +}; + + +exports.nextTick = function (callback) { + + return function () { + + var args = arguments; + process.nextTick(function () { + + callback.apply(null, args); + }); + }; +}; + + +exports.once = function (method) { + + if (method._hoekOnce) { + return method; + } + + var once = false; + var wrapped = function () { + + if (!once) { + once = true; + method.apply(null, arguments); + } + }; + + wrapped._hoekOnce = true; + + return wrapped; +}; + + +exports.isAbsolutePath = function (path, platform) { + + if (!path) { + return false; + } + + if (Path.isAbsolute) { // node >= 0.11 + return Path.isAbsolute(path); + } + + platform = platform || process.platform; + + // Unix + + if (platform !== 'win32') { + return path[0] === '/'; + } + + // Windows + + return !!/^(?:[a-zA-Z]:[\\\/])|(?:[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/])/.test(path); // C:\ or \\something\something +}; + + +exports.isInteger = function (value) { + + return (typeof value === 'number' && + parseFloat(value) === parseInt(value, 10) && + !isNaN(value)); +}; + + +exports.ignore = function () { }; + + +exports.inherits = Util.inherits; + + +exports.transform = function (source, transform, options) { + + exports.assert(source == null || typeof source === 'object', 'Invalid source object: must be null, undefined, or an object'); + + var result = {}; + var keys = Object.keys(transform); + + for (var k = 0, kl = keys.length; k < kl; ++k) { + var key = keys[k]; + var path = key.split('.'); + var sourcePath = transform[key]; + + exports.assert(typeof sourcePath === 'string', 'All mappings must be "." delineated strings'); + + var segment; + var res = result; + + while (path.length > 1) { + segment = path.shift(); + if (!res[segment]) { + res[segment] = {}; + } + res = res[segment]; + } + segment = path.shift(); + res[segment] = exports.reach(source, sourcePath, options); + } + + return result; +}; + +}).call(this,require('_process'),require("buffer").Buffer) +},{"./escape":17,"_process":29,"buffer":24,"path":28,"util":31}],19:[function(require,module,exports){ +module.exports = require('./lib/isemail'); + +},{"./lib/isemail":20}],20:[function(require,module,exports){ +(function (process){ +/** + * To validate an email address according to RFCs 5321, 5322 and others + * + * Copyright © 2008-2011, Dominic Sayers + * Test schema documentation Copyright © 2011, Daniel Marschall + * Port for Node.js Copyright © 2013, GlobeSherpa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of Dominic Sayers nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Dominic Sayers + * @author Eli Skeggs + * @copyright 2008-2011 Dominic Sayers + * @copyright 2013-2014 GlobeSherpa + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @link http://www.dominicsayers.com/isemail + * @link https://github.com/globesherpa/isemail + * @version 1.0.1 - Increase test coverage, minor style fixes. + */ + +// lazy-loaded +var dns, HAS_REQUIRE = typeof require !== 'undefined'; + +// categories +var ISEMAIL_VALID_CATEGORY = 1; +var ISEMAIL_DNSWARN = 7; +var ISEMAIL_RFC5321 = 15; +var ISEMAIL_CFWS = 31; +var ISEMAIL_DEPREC = 63; +var ISEMAIL_RFC5322 = 127; +var ISEMAIL_ERR = 255; + +// diagnoses +// address is valid +var ISEMAIL_VALID = 0; +// address is valid but a DNS check was not successful +var ISEMAIL_DNSWARN_NO_MX_RECORD = 5; +var ISEMAIL_DNSWARN_NO_RECORD = 6; +// address is valid for SMTP but has unusual elements +var ISEMAIL_RFC5321_TLD = 9; +var ISEMAIL_RFC5321_TLDNUMERIC = 10; +var ISEMAIL_RFC5321_QUOTEDSTRING = 11; +var ISEMAIL_RFC5321_ADDRESSLITERAL = 12; +var ISEMAIL_RFC5321_IPV6DEPRECATED = 13; +// address is valid within the message but cannot be used unmodified for the envelope +var ISEMAIL_CFWS_COMMENT = 17; +var ISEMAIL_CFWS_FWS = 18; +// address contains deprecated elements but may still be valid in restricted contexts +var ISEMAIL_DEPREC_LOCALPART = 33; +var ISEMAIL_DEPREC_FWS = 34; +var ISEMAIL_DEPREC_QTEXT = 35; +var ISEMAIL_DEPREC_QP = 36; +var ISEMAIL_DEPREC_COMMENT = 37; +var ISEMAIL_DEPREC_CTEXT = 38; +var ISEMAIL_DEPREC_CFWS_NEAR_AT = 49; +// the address is only valid according to the broad definition of RFC 5322, but otherwise invalid +var ISEMAIL_RFC5322_DOMAIN = 65; +var ISEMAIL_RFC5322_TOOLONG = 66; +var ISEMAIL_RFC5322_LOCAL_TOOLONG = 67; +var ISEMAIL_RFC5322_DOMAIN_TOOLONG = 68; +var ISEMAIL_RFC5322_LABEL_TOOLONG = 69; +var ISEMAIL_RFC5322_DOMAINLITERAL = 70; +var ISEMAIL_RFC5322_DOMLIT_OBSDTEXT = 71; +var ISEMAIL_RFC5322_IPV6_GRPCOUNT = 72; +var ISEMAIL_RFC5322_IPV6_2X2XCOLON = 73; +var ISEMAIL_RFC5322_IPV6_BADCHAR = 74; +var ISEMAIL_RFC5322_IPV6_MAXGRPS = 75; +var ISEMAIL_RFC5322_IPV6_COLONSTRT = 76; +var ISEMAIL_RFC5322_IPV6_COLONEND = 77; +// address is invalid for any purpose +var ISEMAIL_ERR_EXPECTING_DTEXT = 129; +var ISEMAIL_ERR_NOLOCALPART = 130; +var ISEMAIL_ERR_NODOMAIN = 131; +var ISEMAIL_ERR_CONSECUTIVEDOTS = 132; +var ISEMAIL_ERR_ATEXT_AFTER_CFWS = 133; +var ISEMAIL_ERR_ATEXT_AFTER_QS = 134; +var ISEMAIL_ERR_ATEXT_AFTER_DOMLIT = 135; +var ISEMAIL_ERR_EXPECTING_QPAIR = 136; +var ISEMAIL_ERR_EXPECTING_ATEXT = 137; +var ISEMAIL_ERR_EXPECTING_QTEXT = 138; +var ISEMAIL_ERR_EXPECTING_CTEXT = 139; +var ISEMAIL_ERR_BACKSLASHEND = 140; +var ISEMAIL_ERR_DOT_START = 141; +var ISEMAIL_ERR_DOT_END = 142; +var ISEMAIL_ERR_DOMAINHYPHENSTART = 143; +var ISEMAIL_ERR_DOMAINHYPHENEND = 144; +var ISEMAIL_ERR_UNCLOSEDQUOTEDSTR = 145; +var ISEMAIL_ERR_UNCLOSEDCOMMENT = 146; +var ISEMAIL_ERR_UNCLOSEDDOMLIT = 147; +var ISEMAIL_ERR_FWS_CRLF_X2 = 148; +var ISEMAIL_ERR_FWS_CRLF_END = 149; +var ISEMAIL_ERR_CR_NO_LF = 150; + +// function control +var ISEMAIL_THRESHOLD = 16; +// email parts +var ISEMAIL_COMPONENT_LOCALPART = 0; +var ISEMAIL_COMPONENT_DOMAIN = 1; +var ISEMAIL_COMPONENT_LITERAL = 2; +var ISEMAIL_CONTEXT_COMMENT = 3; +var ISEMAIL_CONTEXT_FWS = 4; +var ISEMAIL_CONTEXT_QUOTEDSTRING = 5; +var ISEMAIL_CONTEXT_QUOTEDPAIR = 6; +// US-ASCII visible characters not valid for atext (http://tools.ietf.org/html/rfc5322#section-3.2.3) +var SPECIALS = '()<>[]:;@\\,."'; + +// matches valid IPv4 addresses from the end of a string +var IPv4_REGEX = /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/; +var IPv6_REGEX = /^[a-fA-F\d]{0,4}$/, IPv6_REGEX_TEST = IPv6_REGEX.test.bind(IPv6_REGEX); + +/** + * Get the largest number in the array. + * + * Returns -Infinity if the array is empty. + * + * @param {Array.} array The array to scan. + * @return {number} The largest number contained. + */ +function maxValue(array) { + var v = -Infinity, i = 0, n = array.length; + + for (; i < n; i++) { + if (array[i] > v) { + v = array[i]; + } + } + + return v; +} + +/** + * Check that an email address conforms to RFCs 5321, 5322 and others + * + * As of Version 3.0, we are now distinguishing clearly between a Mailbox + * as defined by RFC 5321 and an addr-spec as defined by RFC 5322. Depending + * on the context, either can be regarded as a valid email address. The + * RFC 5321 Mailbox specification is more restrictive (comments, white space + * and obsolete forms are not allowed). + * + * @param {string} email The email address to check. + * @param {boolean} checkDNS If true then will check DNS for MX records. If true + * this isEmail _will_ be asynchronous. + * @param {*} errorLevel Determines the boundary between valid and invalid + * addresses. Status codes above this number will be returned as-is, status + * codes below will be returned as ISEMAIL_VALID. Thus the calling program can + * simply look for ISEMAIL_VALID if it is only interested in whether an + * address is valid or not. The errorLevel will determine how "picky" + * isEmail() is about the address. If omitted or passed as false then + * isEmail() will return true or false rather than an integer error or + * warning. NB Note the difference between errorLevel = false and + * errorLevel = 0. + * @return {*} + */ +function isEmail(email, options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + options || (options = {}); + + var threshold, diagnose; + if (typeof options.errorLevel === 'number') { + diagnose = true; + threshold = options.errorLevel; + } else { + diagnose = !!options.errorLevel; + threshold = ISEMAIL_VALID; + } + + var result = [ISEMAIL_VALID]; + + var context = { + now: ISEMAIL_COMPONENT_LOCALPART, + prev: ISEMAIL_COMPONENT_LOCALPART, + stack: [ISEMAIL_COMPONENT_LOCALPART] + }; + + var token = '', prevToken = '', charCode = 0, parseData = {}, atomList = {}; + parseData[ISEMAIL_COMPONENT_LOCALPART] = ''; + parseData[ISEMAIL_COMPONENT_DOMAIN] = ''; + atomList[ISEMAIL_COMPONENT_LOCALPART] = ['']; + atomList[ISEMAIL_COMPONENT_DOMAIN] = ['']; + + var elementCount = 0, elementLength = 0, hyphenFlag = false, assertEnd = false; + var crlfCount = 0; + + for (var i = 0; i < email.length; i++) { + token = email[i]; + + switch (context.now) { + // local-part + case ISEMAIL_COMPONENT_LOCALPART: + // http://tools.ietf.org/html/rfc5322#section-3.4.1 + // local-part = dot-atom / quoted-string / obs-local-part + // + // dot-atom = [CFWS] dot-atom-text [CFWS] + // + // dot-atom-text = 1*atext *("." 1*atext) + // + // quoted-string = [CFWS] + // DQUOTE *([FWS] qcontent) [FWS] DQUOTE + // [CFWS] + // + // obs-local-part = word *("." word) + // + // word = atom / quoted-string + // + // atom = [CFWS] 1*atext [CFWS] + switch (token) { + // comment + case '(': + if (elementLength === 0) { + // comments are OK at the beginning of an element + result.push(elementCount === 0 ? ISEMAIL_CFWS_COMMENT : ISEMAIL_DEPREC_COMMENT); + } else { + result.push(ISEMAIL_CFWS_COMMENT); + assertEnd = true; // can't start a comment in an element, should be end + } + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_COMMENT; + break; + // next dot-atom element + case '.': + if (elementLength === 0) { + // another dot, already? + result.push(elementCount === 0 ? ISEMAIL_ERR_DOT_START : ISEMAIL_ERR_CONSECUTIVEDOTS); + } else { + // the entire local-part can be a quoted string for RFC 5321 + // if it's just one atom that is quoted then it's an RFC 5322 obsolete form + if (assertEnd) { + result.push(ISEMAIL_DEPREC_LOCALPART); + } + + // CFWS & quoted strings are OK again now we're at the beginning of an element (although they are obsolete forms) + assertEnd = false; + elementLength = 0; + elementCount++; + parseData[ISEMAIL_COMPONENT_LOCALPART] += token; + atomList[ISEMAIL_COMPONENT_LOCALPART][elementCount] = ''; // TODO: push? + } + break; + // quoted string + case '"': + if (elementLength === 0) { + // the entire local-part can be a quoted string for RFC 5321 + // if it's just one atom that is quoted then it's an RFC 5322 obsolete form + result.push(elementCount === 0 ? ISEMAIL_RFC5321_QUOTEDSTRING : ISEMAIL_DEPREC_LOCALPART); + + parseData[ISEMAIL_COMPONENT_LOCALPART] += token; + atomList[ISEMAIL_COMPONENT_LOCALPART][elementCount] += token; + elementLength++; + assertEnd = true; // quoted string must be the entire element + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_QUOTEDSTRING; + } else { + result.push(ISEMAIL_ERR_EXPECTING_ATEXT); + } + break; + // folding white space + case '\r': + case ' ': + case '\t': + if (token === '\r' && ((++i === email.length) || email[i] !== '\n')) { + // fatal error + result.push(ISEMAIL_ERR_CR_NO_LF); + break; + } + if (elementLength === 0) { + result.push(elementCount === 0 ? ISEMAIL_CFWS_FWS : ISEMAIL_DEPREC_FWS); + } else { + // we can't start FWS in the middle of an element, better be end + assertEnd = true; + } + + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_FWS; + prevToken = token; + break; + // @ + case '@': + // at this point we should have a valid local-part + /* istanbul ignore next: logically unreachable */ + if (context.stack.length !== 1) { + throw new Error('unexpected item on context stack'); + } + + if (parseData[ISEMAIL_COMPONENT_LOCALPART].length === 0) { + // fatal error + result.push(ISEMAIL_ERR_NOLOCALPART); + } else if (elementLength === 0) { + // fatal error + result.push(ISEMAIL_ERR_DOT_END); + // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1 + // the maximum total length of a user name or other local-part is 64 + // octets + } else if (parseData[ISEMAIL_COMPONENT_LOCALPART].length > 64) { + result.push(ISEMAIL_RFC5322_LOCAL_TOOLONG); + // http://tools.ietf.org/html/rfc5322#section-3.4.1 + // comments and folding white space + // SHOULD NOT be used around the "@" in the addr-spec + // + // http://tools.ietf.org/html/rfc2119 + // 4. SHOULD NOT this phrase, or the phrase "NOT RECOMMENDED" mean that + // there may exist valid reasons in particular circumstances when the + // particular behavior is acceptable or even useful, but the full + // implications should be understood and the case carefully weighed + // before implementing any behavior described with this label + } else if ((context.prev === ISEMAIL_CONTEXT_COMMENT) || (context.prev === ISEMAIL_CONTEXT_FWS)) { + result.push(ISEMAIL_DEPREC_CFWS_NEAR_AT); + } + + // clear everything down for the domain parsing + context.now = ISEMAIL_COMPONENT_DOMAIN; // where we are + context.stack = [ISEMAIL_COMPONENT_DOMAIN]; // where we have been + elementCount = 0; + elementLength = 0; + assertEnd = false; // CFWS can only appear at the end of the element + break; + // atext + default: + // http://tools.ietf.org/html/rfc5322#section-3.2.3 + // atext = ALPHA / DIGIT / ; Printable US-ASCII + // "!" / "#" / ; characters not including + // "$" / "%" / ; specials. Used for atoms. + // "&" / "'" / + // "*" / "+" / + // "-" / "/" / + // "=" / "?" / + // "^" / "_" / + // "`" / "{" / + // "|" / "}" / + // "~" + if (assertEnd) { + // we have encountered atext where it is no longer valid + switch (context.prev) { + case ISEMAIL_CONTEXT_COMMENT: + case ISEMAIL_CONTEXT_FWS: + result.push(ISEMAIL_ERR_ATEXT_AFTER_CFWS); + break; + case ISEMAIL_CONTEXT_QUOTEDSTRING: + result.push(ISEMAIL_ERR_ATEXT_AFTER_QS); + break; + /* istanbul ignore next: logically unreachable */ + default: + throw new Error('more atext found where none is allowed, but unrecognized prev context: ' + context.prev); + } + } else { + context.prev = context.now; + charCode = token.charCodeAt(0); + + if (charCode < 33 || charCode > 126 || charCode === 10 || ~SPECIALS.indexOf(token)) { + // fatal error + result.push(ISEMAIL_ERR_EXPECTING_ATEXT); + } + + parseData[ISEMAIL_COMPONENT_LOCALPART] += token; + atomList[ISEMAIL_COMPONENT_LOCALPART][elementCount] += token; + elementLength++; + } + } + break; + case ISEMAIL_COMPONENT_DOMAIN: + // http://tools.ietf.org/html/rfc5322#section-3.4.1 + // domain = dot-atom / domain-literal / obs-domain + // + // dot-atom = [CFWS] dot-atom-text [CFWS] + // + // dot-atom-text = 1*atext *("." 1*atext) + // + // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS] + // + // dtext = %d33-90 / ; Printable US-ASCII + // %d94-126 / ; characters not including + // obs-dtext ; "[", "]", or "\" + // + // obs-domain = atom *("." atom) + // + // atom = [CFWS] 1*atext [CFWS] + + // http://tools.ietf.org/html/rfc5321#section-4.1.2 + // Mailbox = Local-part "@" ( Domain / address-literal ) + // + // Domain = sub-domain *("." sub-domain) + // + // address-literal = "[" ( IPv4-address-literal / + // IPv6-address-literal / + // General-address-literal ) "]" + // ; See Section 4.1.3 + + // http://tools.ietf.org/html/rfc5322#section-3.4.1 + // Note: A liberal syntax for the domain portion of addr-spec is + // given here. However, the domain portion contains addressing + // information specified by and used in other protocols (e.g., + // [RFC1034], [RFC1035], [RFC1123], [RFC5321]). It is therefore + // incumbent upon implementations to conform to the syntax of + // addresses for the context in which they are used. + // is_email() author's note: it's not clear how to interpret this in + // the context of a general email address validator. The conclusion I + // have reached is this: "addressing information" must comply with + // RFC 5321 (and in turn RFC 1035), anything that is "semantically + // invisible" must comply only with RFC 5322. + switch (token) { + // comment + case '(': + if (elementLength === 0) { + // comments at the start of the domain are deprecated in the text + // comments at the start of a subdomain are obs-domain + // (http://tools.ietf.org/html/rfc5322#section-3.4.1) + result.push(elementCount === 0 ? ISEMAIL_DEPREC_CFWS_NEAR_AT : ISEMAIL_DEPREC_COMMENT); + } else { + result.push(ISEMAIL_CFWS_COMMENT); + assertEnd = true; // can't start a comment mid-element, better be end + } + + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_COMMENT; + break; + // next dot-atom element + case '.': + if (elementLength === 0) { + // another dot, already? + result.push(elementCount === 0 ? ISEMAIL_ERR_DOT_START : ISEMAIL_ERR_CONSECUTIVEDOTS); // fatal error + } else if (hyphenFlag) { + // previous subdomain ended in a hyphen + result.push(ISEMAIL_ERR_DOMAINHYPHENEND); // fatal error + } else if (elementLength > 63) { + // Nowhere in RFC 5321 does it say explicitly that the + // domain part of a Mailbox must be a valid domain according + // to the DNS standards set out in RFC 1035, but this *is* + // implied in several places. For instance, wherever the idea + // of host routing is discussed the RFC says that the domain + // must be looked up in the DNS. This would be nonsense unless + // the domain was designed to be a valid DNS domain. Hence we + // must conclude that the RFC 1035 restriction on label length + // also applies to RFC 5321 domains. + // + // http://tools.ietf.org/html/rfc1035#section-2.3.4 + // labels 63 octets or less + + result.push(ISEMAIL_RFC5322_LABEL_TOOLONG); + } + + // CFWS is OK again now we're at the beginning of an element (although it may be obsolete CFWS) + assertEnd = false; + elementLength = 0; + elementCount++; + atomList[ISEMAIL_COMPONENT_DOMAIN][elementCount] = ''; + parseData[ISEMAIL_COMPONENT_DOMAIN] += token; + + break; + // domain literal + case '[': + if (parseData[ISEMAIL_COMPONENT_DOMAIN].length === 0) { + // domain literal must be the only component + assertEnd = true; + elementLength++; + context.stack.push(context.now); + context.now = ISEMAIL_COMPONENT_LITERAL; + parseData[ISEMAIL_COMPONENT_DOMAIN] += token; + atomList[ISEMAIL_COMPONENT_DOMAIN][elementCount] += token; + parseData[ISEMAIL_COMPONENT_LITERAL] = ''; + } else { + // fatal error + result.push(ISEMAIL_ERR_EXPECTING_ATEXT); + } + break; + // folding white space + case '\r': + case ' ': + case '\t': + if (token === '\r' && ((++i === email.length) || email[i] !== '\n')) { + // fatal error + result.push(ISEMAIL_ERR_CR_NO_LF); + break; + } + + if (elementLength === 0) { + result.push(elementCount === 0 ? ISEMAIL_DEPREC_CFWS_NEAR_AT : ISEMAIL_DEPREC_FWS); + } else { + // We can't start FWS in the middle of an element, so this better be the end + result.push(ISEMAIL_CFWS_FWS); + assertEnd = true; + } + + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_FWS; + prevToken = token; + break; + // atext + default: + // RFC 5322 allows any atext... + // http://tools.ietf.org/html/rfc5322#section-3.2.3 + // atext = ALPHA / DIGIT / ; Printable US-ASCII + // "!" / "#" / ; characters not including + // "$" / "%" / ; specials. Used for atoms. + // "&" / "'" / + // "*" / "+" / + // "-" / "/" / + // "=" / "?" / + // "^" / "_" / + // "`" / "{" / + // "|" / "}" / + // "~" + + // But RFC 5321 only allows letter-digit-hyphen to comply with DNS rules (RFCs 1034 & 1123) + // http://tools.ietf.org/html/rfc5321#section-4.1.2 + // sub-domain = Let-dig [Ldh-str] + // + // Let-dig = ALPHA / DIGIT + // + // Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig + // + if (assertEnd) { + // we have encountered atext where it is no longer valid + switch (context.prev) { + case ISEMAIL_CONTEXT_COMMENT: + case ISEMAIL_CONTEXT_FWS: + result.push(ISEMAIL_ERR_ATEXT_AFTER_CFWS); + break; + case ISEMAIL_COMPONENT_LITERAL: + result.push(ISEMAIL_ERR_ATEXT_AFTER_DOMLIT); + break; + /* istanbul ignore next: logically unreachable */ + default: + throw new Error('more atext found where none is allowed, but unrecognized prev context: ' + context.prev); + } + } + + charCode = token.charCodeAt(0); + hyphenFlag = false; // assume this token isn't a hyphen unless we discover it is + + if (charCode < 33 || charCode > 126 || ~SPECIALS.indexOf(token)) { + // fatal error + result.push(ISEMAIL_ERR_EXPECTING_ATEXT); + } else if (token === '-') { + if (elementLength === 0) { + // hyphens can't be at the beginning of a subdomain + result.push(ISEMAIL_ERR_DOMAINHYPHENSTART); // fatal error + } + + hyphenFlag = true; + } else if (!((charCode > 47 && charCode < 58) || (charCode > 64 && charCode < 91) || (charCode > 96 && charCode < 123))) { + // not an RFC 5321 subdomain, but still OK by RFC 5322 + result.push(ISEMAIL_RFC5322_DOMAIN); + } + + parseData[ISEMAIL_COMPONENT_DOMAIN] += token; + atomList[ISEMAIL_COMPONENT_DOMAIN][elementCount] += token; + elementLength++; + } + break; + // domain literal + case ISEMAIL_COMPONENT_LITERAL: + // http://tools.ietf.org/html/rfc5322#section-3.4.1 + // domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS] + // + // dtext = %d33-90 / ; Printable US-ASCII + // %d94-126 / ; characters not including + // obs-dtext ; "[", "]", or "\" + // + // obs-dtext = obs-NO-WS-CTL / quoted-pair + switch (token) { + // end of domain literal + case ']': + if (maxValue(result) < ISEMAIL_DEPREC) { + // Could be a valid RFC 5321 address literal, so let's check + + // http://tools.ietf.org/html/rfc5321#section-4.1.2 + // address-literal = "[" ( IPv4-address-literal / + // IPv6-address-literal / + // General-address-literal ) "]" + // ; See Section 4.1.3 + // + // http://tools.ietf.org/html/rfc5321#section-4.1.3 + // IPv4-address-literal = Snum 3("." Snum) + // + // IPv6-address-literal = "IPv6:" IPv6-addr + // + // General-address-literal = Standardized-tag ":" 1*dcontent + // + // Standardized-tag = Ldh-str + // ; Standardized-tag MUST be specified in a + // ; Standards-Track RFC and registered with IANA + // + // dcontent = %d33-90 / ; Printable US-ASCII + // %d94-126 ; excl. "[", "\", "]" + // + // Snum = 1*3DIGIT + // ; representing a decimal integer + // ; value in the range 0 through 255 + // + // IPv6-addr = IPv6-full / IPv6-comp / IPv6v4-full / IPv6v4-comp + // + // IPv6-hex = 1*4HEXDIG + // + // IPv6-full = IPv6-hex 7(":" IPv6-hex) + // + // IPv6-comp = [IPv6-hex *5(":" IPv6-hex)] "::" + // [IPv6-hex *5(":" IPv6-hex)] + // ; The "::" represents at least 2 16-bit groups of + // ; zeros. No more than 6 groups in addition to the + // ; "::" may be present. + // + // IPv6v4-full = IPv6-hex 5(":" IPv6-hex) ":" IPv4-address-literal + // + // IPv6v4-comp = [IPv6-hex *3(":" IPv6-hex)] "::" + // [IPv6-hex *3(":" IPv6-hex) ":"] + // IPv4-address-literal + // ; The "::" represents at least 2 16-bit groups of + // ; zeros. No more than 4 groups in addition to the + // ; "::" and IPv4-address-literal may be present. + // + // is_email() author's note: We can't use ip2long() to validate + // IPv4 addresses because it accepts abbreviated addresses + // (xxx.xxx.xxx), expanding the last group to complete the address. + // filter_var() validates IPv6 address inconsistently (up to PHP 5.3.3 + // at least) -- see http://bugs.php.net/bug.php?id=53236 for example + + // TODO: var here? + var maxGroups = 8, matchesIP, index = false; + var addressLiteral = parseData[ISEMAIL_COMPONENT_LITERAL]; + + // extract IPv4 part from the end of the address-literal (if applicable) + if (matchesIP = IPv4_REGEX.exec(addressLiteral)) { + if ((index = matchesIP.index) !== 0) { + // convert IPv4 part to IPv6 format for futher testing + addressLiteral = addressLiteral.substr(0, matchesIP.index) + '0:0'; + } + } + + if (index === 0) { + // nothing there except a valid IPv4 address, so... + result.push(ISEMAIL_RFC5321_ADDRESSLITERAL); + } else if (addressLiteral.slice(0, 5).toLowerCase() !== 'ipv6:') { + result.push(ISEMAIL_RFC5322_DOMAINLITERAL); + } else { + var match = addressLiteral.substr(5); + matchesIP = match.split(':'); + index = match.indexOf('::'); + + if (!~index) { + // need exactly the right number of groups + if (matchesIP.length !== maxGroups) { + result.push(ISEMAIL_RFC5322_IPV6_GRPCOUNT); + } + } else if (index !== match.lastIndexOf('::')) { + result.push(ISEMAIL_RFC5322_IPV6_2X2XCOLON); + } else { + if (index === 0 || index === match.length - 2) { + // RFC 4291 allows :: at the start or end of an address with 7 other groups in addition + maxGroups++; + } + + if (matchesIP.length > maxGroups) { + result.push(ISEMAIL_RFC5322_IPV6_MAXGRPS); + } else if (matchesIP.length === maxGroups) { + // eliding a single "::" + result.push(ISEMAIL_RFC5321_IPV6DEPRECATED); + } + } + + // IPv6 testing strategy + if (match[0] === ':' && match[1] !== ':') { + result.push(ISEMAIL_RFC5322_IPV6_COLONSTRT); + } else if (match[match.length - 1] === ':' && match[match.length - 2] !== ':') { + result.push(ISEMAIL_RFC5322_IPV6_COLONEND); + } else if (matchesIP.every(IPv6_REGEX_TEST)) { + result.push(ISEMAIL_RFC5321_ADDRESSLITERAL); + } else { + result.push(ISEMAIL_RFC5322_IPV6_BADCHAR); + } + } + } else { + result.push(ISEMAIL_RFC5322_DOMAINLITERAL); + } + + parseData[ISEMAIL_COMPONENT_DOMAIN] += token; + atomList[ISEMAIL_COMPONENT_DOMAIN][elementCount] += token; + elementLength++; + context.prev = context.now; + context.now = context.stack.pop(); + break; + case '\\': + result.push(ISEMAIL_RFC5322_DOMLIT_OBSDTEXT); + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_QUOTEDPAIR; + break; + // folding white space + case '\r': + case ' ': + case '\t': + if (token === '\r' && ((++i === email.length) || email[i] !== '\n')) { + // fatal error + result.push(ISEMAIL_ERR_CR_NO_LF); + break; + } + + result.push(ISEMAIL_CFWS_FWS); + + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_FWS; + prevToken = token; + break; + // dtext + default: + // http://tools.ietf.org/html/rfc5322#section-3.4.1 + // dtext = %d33-90 / ; Printable US-ASCII + // %d94-126 / ; characters not including + // obs-dtext ; "[", "]", or "\" + // + // obs-dtext = obs-NO-WS-CTL / quoted-pair + // + // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control + // %d11 / ; characters that do not + // %d12 / ; include the carriage + // %d14-31 / ; return, line feed, and + // %d127 ; white space characters + charCode = token.charCodeAt(0); + + // CR, LF, SP & HTAB have already been parsed above + if (charCode > 127 || charCode === 0 || token === '[') { + // fatal error + result.push(ISEMAIL_ERR_EXPECTING_DTEXT); + break; + } else if (charCode < 33 || charCode === 127) { + result.push(ISEMAIL_RFC5322_DOMLIT_OBSDTEXT); + } + + parseData[ISEMAIL_COMPONENT_LITERAL] += token; + parseData[ISEMAIL_COMPONENT_DOMAIN] += token; + atomList[ISEMAIL_COMPONENT_DOMAIN][elementCount] += token; + elementLength++; + } + break; + // quoted string + case ISEMAIL_CONTEXT_QUOTEDSTRING: + // http://tools.ietf.org/html/rfc5322#section-3.2.4 + // quoted-string = [CFWS] + // DQUOTE *([FWS] qcontent) [FWS] DQUOTE + // [CFWS] + // + // qcontent = qtext / quoted-pair + switch (token) { + // quoted pair + case '\\': + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_QUOTEDPAIR; + break; + // folding white space + // inside a quoted string, spaces are allowed as regular characters + // it's only FWS if we include HTAB or CRLF + case '\r': + case '\t': + if (token === '\r' && ((++i === email.length) || email[i] !== '\n')) { + // fatal error + result.push(ISEMAIL_ERR_CR_NO_LF); + break; + } + + // http://tools.ietf.org/html/rfc5322#section-3.2.2 + // Runs of FWS, comment, or CFWS that occur between lexical tokens in a + // structured header field are semantically interpreted as a single + // space character. + + // http://tools.ietf.org/html/rfc5322#section-3.2.4 + // the CRLF in any FWS/CFWS that appears within the quoted-string [is] + // semantically "invisible" and therefore not part of the quoted-string + + parseData[ISEMAIL_COMPONENT_LOCALPART] += ' '; + atomList[ISEMAIL_COMPONENT_LOCALPART][elementCount] += ' '; + elementLength++; + + result.push(ISEMAIL_CFWS_FWS); + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_FWS; + prevToken = token; + break; + // end of quoted string + case '"': + parseData[ISEMAIL_COMPONENT_LOCALPART] += token; + atomList[ISEMAIL_COMPONENT_LOCALPART][elementCount] += token; + elementLength++; + context.prev = context.now; + context.now = context.stack.pop(); + break; + // qtext + default: + // http://tools.ietf.org/html/rfc5322#section-3.2.4 + // qtext = %d33 / ; Printable US-ASCII + // %d35-91 / ; characters not including + // %d93-126 / ; "\" or the quote character + // obs-qtext + // + // obs-qtext = obs-NO-WS-CTL + // + // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control + // %d11 / ; characters that do not + // %d12 / ; include the carriage + // %d14-31 / ; return, line feed, and + // %d127 ; white space characters + charCode = token.charCodeAt(0); + + if (charCode > 127 || charCode === 0 || charCode === 10) { + result.push(ISEMAIL_ERR_EXPECTING_QTEXT); + } else if (charCode < 32 || charCode === 127) { + result.push(ISEMAIL_DEPREC_QTEXT); + } + + parseData[ISEMAIL_COMPONENT_LOCALPART] += token; + atomList[ISEMAIL_COMPONENT_LOCALPART][elementCount] += token; + elementLength++; + } + + // http://tools.ietf.org/html/rfc5322#section-3.4.1 + // If the string can be represented as a dot-atom (that is, it contains + // no characters other than atext characters or "." surrounded by atext + // characters), then the dot-atom form SHOULD be used and the quoted- + // string form SHOULD NOT be used. + + break; + // quoted pair + case ISEMAIL_CONTEXT_QUOTEDPAIR: + // http://tools.ietf.org/html/rfc5322#section-3.2.1 + // quoted-pair = ("\" (VCHAR / WSP)) / obs-qp + // + // VCHAR = %d33-126 ; visible (printing) characters + // WSP = SP / HTAB ; white space + // + // obs-qp = "\" (%d0 / obs-NO-WS-CTL / LF / CR) + // + // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control + // %d11 / ; characters that do not + // %d12 / ; include the carriage + // %d14-31 / ; return, line feed, and + // %d127 ; white space characters + // + // i.e. obs-qp = "\" (%d0-8, %d10-31 / %d127) + charCode = token.charCodeAt(0); + + if (charCode > 127) { + // fatal error + result.push(ISEMAIL_ERR_EXPECTING_QPAIR); + } else if ((charCode < 31 && charCode !== 9) || charCode === 127) { + // SP & HTAB are allowed + result.push(ISEMAIL_DEPREC_QP); + } + + // At this point we know where this qpair occurred so + // we could check to see if the character actually + // needed to be quoted at all. + // http://tools.ietf.org/html/rfc5321#section-4.1.2 + // the sending system SHOULD transmit the + // form that uses the minimum quoting possible. + + // To do: check whether the character needs to be quoted (escaped) in this context + + context.prev = context.now; + context.now = context.stack.pop(); // end of qpair + token = '\\' + token; + + switch (context.now) { + case ISEMAIL_CONTEXT_COMMENT: break; + case ISEMAIL_CONTEXT_QUOTEDSTRING: + parseData[ISEMAIL_COMPONENT_LOCALPART] += token; + atomList[ISEMAIL_COMPONENT_LOCALPART][elementCount] += token; + elementLength += 2; // the maximum sizes specified by RFC 5321 are octet counts, so we must include the backslash + break; + case ISEMAIL_COMPONENT_LITERAL: + parseData[ISEMAIL_COMPONENT_DOMAIN] += token; + atomList[ISEMAIL_COMPONENT_DOMAIN][elementCount] += token; + elementLength += 2; // the maximum sizes specified by RFC 5321 are octet counts, so we must include the backslash + break; + /* istanbul ignore next: logically unreachable */ + default: + throw new Error('quoted pair logic invoked in an invalid context: ' + context.now); + } + break; + // comment + case ISEMAIL_CONTEXT_COMMENT: + // http://tools.ietf.org/html/rfc5322#section-3.2.2 + // comment = "(" *([FWS] ccontent) [FWS] ")" + // + // ccontent = ctext / quoted-pair / comment + switch (token) { + // nested comment + case '(': + // nested comments are ok + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_COMMENT; + break; + // end of comment + case ')': + context.prev = context.now; + context.now = context.stack.pop(); + + // http://tools.ietf.org/html/rfc5322#section-3.2.2 + // Runs of FWS, comment, or CFWS that occur between lexical tokens in a + // structured header field are semantically interpreted as a single + // space character. + // + // isEmail() author's note: This *cannot* mean that we must add a + // space to the address wherever CFWS appears. This would result in + // any addr-spec that had CFWS outside a quoted string being invalid + // for RFC 5321. +// if (context.now === ISEMAIL_COMPONENT_LOCALPART || context.now === ISEMAIL_COMPONENT_DOMAIN) { +// parseData[context.now] += ' '; +// atomList[context.now][elementCount] += ' '; +// elementLength++; +// } + + break; + // quoted pair + case '\\': + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_QUOTEDPAIR; + break; + // folding white space + case '\r': + case ' ': + case '\t': + if (token === '\r' && ((++i === email.length) || email[i] !== '\n')) { + // fatal error + result.push(ISEMAIL_ERR_CR_NO_LF); + break; + } + + result.push(ISEMAIL_CFWS_FWS); + + context.stack.push(context.now); + context.now = ISEMAIL_CONTEXT_FWS; + prevToken = token; + break; + // ctext + default: + // http://tools.ietf.org/html/rfc5322#section-3.2.3 + // ctext = %d33-39 / ; Printable US-ASCII + // %d42-91 / ; characters not including + // %d93-126 / ; "(", ")", or "\" + // obs-ctext + // + // obs-ctext = obs-NO-WS-CTL + // + // obs-NO-WS-CTL = %d1-8 / ; US-ASCII control + // %d11 / ; characters that do not + // %d12 / ; include the carriage + // %d14-31 / ; return, line feed, and + // %d127 ; white space characters + charCode = token.charCodeAt(0); + + if (charCode > 127 || charCode === 0 || charCode === 10) { + // fatal error + result.push(ISEMAIL_ERR_EXPECTING_CTEXT); + break; + } else if (charCode < 32 || charCode === 127) { + result.push(ISEMAIL_DEPREC_CTEXT); + } + } + break; + // folding white space + case ISEMAIL_CONTEXT_FWS: + // http://tools.ietf.org/html/rfc5322#section-3.2.2 + // FWS = ([*WSP CRLF] 1*WSP) / obs-FWS + // ; Folding white space + + // But note the erratum: + // http://www.rfc-editor.org/errata_search.php?rfc=5322&eid=1908: + // In the obsolete syntax, any amount of folding white space MAY be + // inserted where the obs-FWS rule is allowed. This creates the + // possibility of having two consecutive "folds" in a line, and + // therefore the possibility that a line which makes up a folded header + // field could be composed entirely of white space. + // + // obs-FWS = 1*([CRLF] WSP) + + if (prevToken === '\r') { + if (token === '\r') { + // fatal error + result.push(ISEMAIL_ERR_FWS_CRLF_X2); + break; + } + + if (++crlfCount > 1) { + // multiple folds = obsolete FWS + result.push(ISEMAIL_DEPREC_FWS); + } else { + crlfCount = 1; + } + } + + switch (token) { + case '\r': + if ((++i === email.length) || email[i] !== '\n') { + // fatal error + result.push(ISEMAIL_ERR_CR_NO_LF); + } + break; + case ' ': + case '\t': + break; + default: + if (prevToken === '\r') { + // fatal error + result.push(ISEMAIL_ERR_FWS_CRLF_END); + } + + crlfCount = 0; + + context.prev = context.now; + context.now = context.stack.pop(); // end of FWS + + // http://tools.ietf.org/html/rfc5322#section-3.2.2 + // Runs of FWS, comment, or CFWS that occur between lexical tokens in a + // structured header field are semantically interpreted as a single + // space character. + // + // isEmail() author's note: This *cannot* mean that we must add a + // space to the address wherever CFWS appears. This would result in + // any addr-spec that had CFWS outside a quoted string being invalid + // for RFC 5321. +// if ((context.now === ISEMAIL_COMPONENT_LOCALPART) || (context.now === ISEMAIL_COMPONENT_DOMAIN)) { +// parseData[context.now] += ' '; +// atomList[context.now][elementCount] += ' '; +// elementLength++; +// } + + i--; // look at this token again in the parent context + } + prevToken = token; + break; + // unexpected context + /* istanbul ignore next: logically unreachable */ + default: + throw new Error('unknown context: ' + context.now); + } // primary state machine + + if (maxValue(result) > ISEMAIL_RFC5322) { + // fatal error, no point continuing + break; + } + } // token loop + + // check for errors + if (maxValue(result) < ISEMAIL_RFC5322) { + // fatal errors + if (context.now === ISEMAIL_CONTEXT_QUOTEDSTRING) result.push(ISEMAIL_ERR_UNCLOSEDQUOTEDSTR); + else if (context.now === ISEMAIL_CONTEXT_QUOTEDPAIR) result.push(ISEMAIL_ERR_BACKSLASHEND); + else if (context.now === ISEMAIL_CONTEXT_COMMENT) result.push(ISEMAIL_ERR_UNCLOSEDCOMMENT); + else if (context.now === ISEMAIL_COMPONENT_LITERAL) result.push(ISEMAIL_ERR_UNCLOSEDDOMLIT); + else if (token === '\r') result.push(ISEMAIL_ERR_FWS_CRLF_END); + else if (parseData[ISEMAIL_COMPONENT_DOMAIN].length === 0) result.push(ISEMAIL_ERR_NODOMAIN); + else if (elementLength === 0) result.push(ISEMAIL_ERR_DOT_END); + else if (hyphenFlag) result.push(ISEMAIL_ERR_DOMAINHYPHENEND); + // other errors + // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.2 + // The maximum total length of a domain name or number is 255 octets. + else if (parseData[ISEMAIL_COMPONENT_DOMAIN].length > 255) { + result.push(ISEMAIL_RFC5322_DOMAIN_TOOLONG); + // http://tools.ietf.org/html/rfc5321#section-4.1.2 + // Forward-path = Path + // + // Path = "<" [ A-d-l ":" ] Mailbox ">" + // + // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3 + // The maximum total length of a reverse-path or forward-path is 256 + // octets (including the punctuation and element separators). + // + // Thus, even without (obsolete) routing information, the Mailbox can + // only be 254 characters long. This is confirmed by this verified + // erratum to RFC 3696: + // + // http://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690 + // However, there is a restriction in RFC 2821 on the length of an + // address in MAIL and RCPT commands of 254 characters. Since addresses + // that do not fit in those fields are not normally useful, the upper + // limit on address lengths should normally be considered to be 254. + } else if (parseData[ISEMAIL_COMPONENT_LOCALPART].length + + parseData[ISEMAIL_COMPONENT_DOMAIN].length + + 1 /* '@' symbol */ > 254) { + result.push(ISEMAIL_RFC5322_TOOLONG); + // http://tools.ietf.org/html/rfc1035#section-2.3.4 + // labels 63 octets or less + } else if (elementLength > 63) { + result.push(ISEMAIL_RFC5322_LABEL_TOOLONG); + } + } // check for errors + + var dnsPositive = false; + + if (options.checkDNS && (maxValue(result) < ISEMAIL_DNSWARN) && HAS_REQUIRE) { + dns || (dns = require('dns')); + // http://tools.ietf.org/html/rfc5321#section-2.3.5 + // Names that can + // be resolved to MX RRs or address (i.e., A or AAAA) RRs (as discussed + // in Section 5) are permitted, as are CNAME RRs whose targets can be + // resolved, in turn, to MX or address RRs. + // + // http://tools.ietf.org/html/rfc5321#section-5.1 + // The lookup first attempts to locate an MX record associated with the + // name. If a CNAME record is found, the resulting name is processed as + // if it were the initial name. ... If an empty list of MXs is returned, + // the address is treated as if it was associated with an implicit MX + // RR, with a preference of 0, pointing to that host. + // + // isEmail() author's note: We will regard the existence of a CNAME to be + // sufficient evidence of the domain's existence. For performance reasons + // we will not repeat the DNS lookup for the CNAME's target, but we will + // raise a warning because we didn't immediately find an MX record. + if (elementCount === 0) { + // checking TLD DNS seems to work only if you explicitly check from the root + parseData[ISEMAIL_COMPONENT_DOMAIN] += '.'; + } + + var dnsDomain = parseData[ISEMAIL_COMPONENT_DOMAIN]; + dns.resolveMx(dnsDomain, function(err, records) { + if ((err && err.code !== dns.NODATA) || (!err && !records)) { + result.push(ISEMAIL_DNSWARN_NO_RECORD); + return finish(); + } + if (records && records.length) { + dnsPositive = true; + return finish(); + } + var done = false, count = 3; + result.push(ISEMAIL_DNSWARN_NO_MX_RECORD); + dns.resolveCname(dnsDomain, handleRecords); + dns.resolve4(dnsDomain, handleRecords); + dns.resolve6(dnsDomain, handleRecords); + function handleRecords(err, records) { + if (done) return; + count--; + if (!err && records && records.length) { + done = true; + return finish(); + } + if (count === 0) { + // no usable records for the domain can be found + result.push(ISEMAIL_DNSWARN_NO_RECORD); + done = true; + finish(); + } + } + }); + } else if (options.checkDNS) { + // guarantee asynchronicity + process.nextTick(finish); + } else { + return finish(); + } // checkDNS + + function finish() { + if (!dnsPositive && (maxValue(result) < ISEMAIL_DNSWARN)) { + if (elementCount === 0) { + result.push(ISEMAIL_RFC5321_TLD); + } else { + var charCode = atomList[ISEMAIL_COMPONENT_DOMAIN][elementCount].charCodeAt(0); + if (charCode >= 48 && charCode <= 57) { + result.push(ISEMAIL_RFC5321_TLDNUMERIC); + } + } + } + + // make unique (TODO: how efficient is this?) + var has = {}, index = 0; + while (index < result.length) { + if (has[result[index]]) { + result.splice(index, 1); + } else { + has[result[index]] = true; + index++; + } + } + // TODO: optimize all these Math.max calls! + var finalStatus = maxValue(result); + + if (result.length !== 1) { + // remove redundant ISEMAIL_VALID + result.shift(); + } + + if (finalStatus < threshold) { + finalStatus = ISEMAIL_VALID; + } + + finalStatus = diagnose ? finalStatus : finalStatus < ISEMAIL_THRESHOLD; + callback && callback(finalStatus); + + return finalStatus; + } // finish +} // isEmail + +module.exports = isEmail; + +}).call(this,require('_process')) +},{"_process":29,"dns":23}],21:[function(require,module,exports){ +arguments[4][16][0].apply(exports,arguments) +},{"./lib":22}],22:[function(require,module,exports){ +// Load modules + +var Hoek = require('hoek'); + + +// Declare internals + +var internals = {}; + + +exports = module.exports = internals.Topo = function () { + + this._items = []; + this.nodes = []; +}; + + +internals.Topo.prototype.add = function (nodes, options) { + + var self = this; + + options = options || {}; + + // Validate rules + + var before = [].concat(options.before || []); + var after = [].concat(options.after || []); + var group = options.group || '?'; + + Hoek.assert(before.indexOf(group) === -1, 'Item cannot come before itself:', group); + Hoek.assert(before.indexOf('?') === -1, 'Item cannot come before unassociated items'); + Hoek.assert(after.indexOf(group) === -1, 'Item cannot come after itself:', group); + Hoek.assert(after.indexOf('?') === -1, 'Item cannot come after unassociated items'); + + ([].concat(nodes)).forEach(function (node, i) { + + var item = { + seq: self._items.length, + before: before, + after: after, + group: group, + node: node + }; + + self._items.push(item); + }); + + // Insert event + + var error = this._sort(); + Hoek.assert(!error, 'item', (group !== '?' ? 'added into group ' + group : ''), 'created a dependencies error'); + + return this.nodes; +}; + + +internals.Topo.prototype._sort = function () { + + // Construct graph + + var groups = {}; + var graph = {}; + var graphAfters = {}; + + for (var i = 0, il = this._items.length; i < il; ++i) { + var item = this._items[i]; + var seq = item.seq; // Unique across all items + var group = item.group; + + // Determine Groups + + groups[group] = groups[group] || []; + groups[group].push(seq); + + // Build intermediary graph using 'before' + + graph[seq] = [item.before]; + + // Build second intermediary graph with 'after' + + var after = item.after; + for (var j = 0, jl = after.length; j < jl; ++j) { + graphAfters[after[j]] = (graphAfters[after[j]] || []).concat(seq); + } + } + + // Expand intermediary graph + + var graphNodes = Object.keys(graph); + for (i = 0, il = graphNodes.length; i < il; ++i) { + var node = graphNodes[i]; + var expandedGroups = []; + + var graphNodeItems = Object.keys(graph[node]); + for (j = 0, jl = graphNodeItems.length; j < jl; ++j) { + var group = graph[node][graphNodeItems[j]]; + groups[group] = groups[group] || []; + groups[group].forEach(function (d) { + + expandedGroups.push(d); + }); + } + graph[node] = expandedGroups; + } + + // Merge intermediary graph using graphAfters into final graph + + var afterNodes = Object.keys(graphAfters); + for (i = 0, il = afterNodes.length; i < il; ++i) { + var group = afterNodes[i]; + + if (groups[group]) { + for (j = 0, jl = groups[group].length; j < jl; ++j) { + var node = groups[group][j]; + graph[node] = graph[node].concat(graphAfters[group]); + } + } + } + + // Compile ancestors + + var ancestors = {}; + graphNodes = Object.keys(graph); + for (i = 0, il = graphNodes.length; i < il; ++i) { + var node = graphNodes[i]; + var children = graph[node]; + + for (j = 0, jl = children.length; j < jl; ++j) { + ancestors[children[j]] = (ancestors[children[j]] || []).concat(node); + } + } + + // Topo sort + + var visited = {}; + var sorted = []; + + for (i = 0, il = this._items.length; i < il; ++i) { + var next = i; + + if (ancestors[i]) { + next = null; + for (j = 0, jl = this._items.length; j < jl; ++j) { + if (visited[j] === true) { + continue; + } + + if (!ancestors[j]) { + ancestors[j] = []; + } + + var shouldSeeCount = ancestors[j].length; + var seenCount = 0; + for (var l = 0, ll = shouldSeeCount; l < ll; ++l) { + if (sorted.indexOf(ancestors[j][l]) >= 0) { + ++seenCount; + } + } + + if (seenCount === shouldSeeCount) { + next = j; + break; + } + } + } + + if (next !== null) { + next = next.toString(); // Normalize to string TODO: replace with seq + visited[next] = true; + sorted.push(next); + } + } + + if (sorted.length !== this._items.length) { + return new Error('Invalid dependencies'); + } + + var seqIndex = {}; + this._items.forEach(function (item) { + + seqIndex[item.seq] = item; + }); + + var sortedNodes = []; + this._items = sorted.map(function (value) { + + var item = seqIndex[value]; + sortedNodes.push(item.node); + return item; + }); + + this.nodes = sortedNodes; +}; + +},{"hoek":16}],23:[function(require,module,exports){ + +},{}],24:[function(require,module,exports){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ + +var base64 = require('base64-js') +var ieee754 = require('ieee754') + +exports.Buffer = Buffer +exports.SlowBuffer = Buffer +exports.INSPECT_MAX_BYTES = 50 +Buffer.poolSize = 8192 + +/** + * If `TYPED_ARRAY_SUPPORT`: + * === true Use Uint8Array implementation (fastest) + * === false Use Object implementation (most compatible, even IE6) + * + * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, + * Opera 11.6+, iOS 4.2+. + * + * Note: + * + * - Implementation must support adding new properties to `Uint8Array` instances. + * Firefox 4-29 lacked support, fixed in Firefox 30+. + * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. + * + * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. + * + * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of + * incorrect length in some situations. + * + * We detect these buggy browsers and set `TYPED_ARRAY_SUPPORT` to `false` so they will + * get the Object implementation, which is slower but will work correctly. + */ +var TYPED_ARRAY_SUPPORT = (function () { + try { + var buf = new ArrayBuffer(0) + var arr = new Uint8Array(buf) + arr.foo = function () { return 42 } + return 42 === arr.foo() && // typed array instances can be augmented + typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` + new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` + } catch (e) { + return false + } +})() + +/** + * Class: Buffer + * ============= + * + * The Buffer constructor returns instances of `Uint8Array` that are augmented + * with function properties for all the node `Buffer` API functions. We use + * `Uint8Array` so that square bracket notation works as expected -- it returns + * a single octet. + * + * By augmenting the instances, we can avoid modifying the `Uint8Array` + * prototype. + */ +function Buffer (subject, encoding, noZero) { + if (!(this instanceof Buffer)) + return new Buffer(subject, encoding, noZero) + + var type = typeof subject + + // Find the length + var length + if (type === 'number') + length = subject > 0 ? subject >>> 0 : 0 + else if (type === 'string') { + if (encoding === 'base64') + subject = base64clean(subject) + length = Buffer.byteLength(subject, encoding) + } else if (type === 'object' && subject !== null) { // assume object is array-like + if (subject.type === 'Buffer' && isArray(subject.data)) + subject = subject.data + length = +subject.length > 0 ? Math.floor(+subject.length) : 0 + } else + throw new Error('First argument needs to be a number, array or string.') + + var buf + if (TYPED_ARRAY_SUPPORT) { + // Preferred: Return an augmented `Uint8Array` instance for best performance + buf = Buffer._augment(new Uint8Array(length)) + } else { + // Fallback: Return THIS instance of Buffer (created by `new`) + buf = this + buf.length = length + buf._isBuffer = true + } + + var i + if (TYPED_ARRAY_SUPPORT && typeof subject.byteLength === 'number') { + // Speed optimization -- use set if we're copying from a typed array + buf._set(subject) + } else if (isArrayish(subject)) { + // Treat array-ish objects as a byte array + if (Buffer.isBuffer(subject)) { + for (i = 0; i < length; i++) + buf[i] = subject.readUInt8(i) + } else { + for (i = 0; i < length; i++) + buf[i] = ((subject[i] % 256) + 256) % 256 + } + } else if (type === 'string') { + buf.write(subject, 0, encoding) + } else if (type === 'number' && !TYPED_ARRAY_SUPPORT && !noZero) { + for (i = 0; i < length; i++) { + buf[i] = 0 + } + } + + return buf +} + +// STATIC METHODS +// ============== + +Buffer.isEncoding = function (encoding) { + switch (String(encoding).toLowerCase()) { + case 'hex': + case 'utf8': + case 'utf-8': + case 'ascii': + case 'binary': + case 'base64': + case 'raw': + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return true + default: + return false + } +} + +Buffer.isBuffer = function (b) { + return !!(b != null && b._isBuffer) +} + +Buffer.byteLength = function (str, encoding) { + var ret + str = str.toString() + switch (encoding || 'utf8') { + case 'hex': + ret = str.length / 2 + break + case 'utf8': + case 'utf-8': + ret = utf8ToBytes(str).length + break + case 'ascii': + case 'binary': + case 'raw': + ret = str.length + break + case 'base64': + ret = base64ToBytes(str).length + break + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + ret = str.length * 2 + break + default: + throw new Error('Unknown encoding') + } + return ret +} + +Buffer.concat = function (list, totalLength) { + assert(isArray(list), 'Usage: Buffer.concat(list[, length])') + + if (list.length === 0) { + return new Buffer(0) + } else if (list.length === 1) { + return list[0] + } + + var i + if (totalLength === undefined) { + totalLength = 0 + for (i = 0; i < list.length; i++) { + totalLength += list[i].length + } + } + + var buf = new Buffer(totalLength) + var pos = 0 + for (i = 0; i < list.length; i++) { + var item = list[i] + item.copy(buf, pos) + pos += item.length + } + return buf +} + +Buffer.compare = function (a, b) { + assert(Buffer.isBuffer(a) && Buffer.isBuffer(b), 'Arguments must be Buffers') + var x = a.length + var y = b.length + for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {} + if (i !== len) { + x = a[i] + y = b[i] + } + if (x < y) { + return -1 + } + if (y < x) { + return 1 + } + return 0 +} + +// BUFFER INSTANCE METHODS +// ======================= + +function hexWrite (buf, string, offset, length) { + offset = Number(offset) || 0 + var remaining = buf.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + + // must be an even number of digits + var strLen = string.length + assert(strLen % 2 === 0, 'Invalid hex string') + + if (length > strLen / 2) { + length = strLen / 2 + } + for (var i = 0; i < length; i++) { + var byte = parseInt(string.substr(i * 2, 2), 16) + assert(!isNaN(byte), 'Invalid hex string') + buf[offset + i] = byte + } + return i +} + +function utf8Write (buf, string, offset, length) { + var charsWritten = blitBuffer(utf8ToBytes(string), buf, offset, length) + return charsWritten +} + +function asciiWrite (buf, string, offset, length) { + var charsWritten = blitBuffer(asciiToBytes(string), buf, offset, length) + return charsWritten +} + +function binaryWrite (buf, string, offset, length) { + return asciiWrite(buf, string, offset, length) +} + +function base64Write (buf, string, offset, length) { + var charsWritten = blitBuffer(base64ToBytes(string), buf, offset, length) + return charsWritten +} + +function utf16leWrite (buf, string, offset, length) { + var charsWritten = blitBuffer(utf16leToBytes(string), buf, offset, length) + return charsWritten +} + +Buffer.prototype.write = function (string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length + length = undefined + } + } else { // legacy + var swap = encoding + encoding = offset + offset = length + length = swap + } + + offset = Number(offset) || 0 + var remaining = this.length - offset + if (!length) { + length = remaining + } else { + length = Number(length) + if (length > remaining) { + length = remaining + } + } + encoding = String(encoding || 'utf8').toLowerCase() + + var ret + switch (encoding) { + case 'hex': + ret = hexWrite(this, string, offset, length) + break + case 'utf8': + case 'utf-8': + ret = utf8Write(this, string, offset, length) + break + case 'ascii': + ret = asciiWrite(this, string, offset, length) + break + case 'binary': + ret = binaryWrite(this, string, offset, length) + break + case 'base64': + ret = base64Write(this, string, offset, length) + break + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + ret = utf16leWrite(this, string, offset, length) + break + default: + throw new Error('Unknown encoding') + } + return ret +} + +Buffer.prototype.toString = function (encoding, start, end) { + var self = this + + encoding = String(encoding || 'utf8').toLowerCase() + start = Number(start) || 0 + end = (end === undefined) ? self.length : Number(end) + + // Fastpath empty strings + if (end === start) + return '' + + var ret + switch (encoding) { + case 'hex': + ret = hexSlice(self, start, end) + break + case 'utf8': + case 'utf-8': + ret = utf8Slice(self, start, end) + break + case 'ascii': + ret = asciiSlice(self, start, end) + break + case 'binary': + ret = binarySlice(self, start, end) + break + case 'base64': + ret = base64Slice(self, start, end) + break + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + ret = utf16leSlice(self, start, end) + break + default: + throw new Error('Unknown encoding') + } + return ret +} + +Buffer.prototype.toJSON = function () { + return { + type: 'Buffer', + data: Array.prototype.slice.call(this._arr || this, 0) + } +} + +Buffer.prototype.equals = function (b) { + assert(Buffer.isBuffer(b), 'Argument must be a Buffer') + return Buffer.compare(this, b) === 0 +} + +Buffer.prototype.compare = function (b) { + assert(Buffer.isBuffer(b), 'Argument must be a Buffer') + return Buffer.compare(this, b) +} + +// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) +Buffer.prototype.copy = function (target, target_start, start, end) { + var source = this + + if (!start) start = 0 + if (!end && end !== 0) end = this.length + if (!target_start) target_start = 0 + + // Copy 0 bytes; we're done + if (end === start) return + if (target.length === 0 || source.length === 0) return + + // Fatal error conditions + assert(end >= start, 'sourceEnd < sourceStart') + assert(target_start >= 0 && target_start < target.length, + 'targetStart out of bounds') + assert(start >= 0 && start < source.length, 'sourceStart out of bounds') + assert(end >= 0 && end <= source.length, 'sourceEnd out of bounds') + + // Are we oob? + if (end > this.length) + end = this.length + if (target.length - target_start < end - start) + end = target.length - target_start + start + + var len = end - start + + if (len < 100 || !TYPED_ARRAY_SUPPORT) { + for (var i = 0; i < len; i++) { + target[i + target_start] = this[i + start] + } + } else { + target._set(this.subarray(start, start + len), target_start) + } +} + +function base64Slice (buf, start, end) { + if (start === 0 && end === buf.length) { + return base64.fromByteArray(buf) + } else { + return base64.fromByteArray(buf.slice(start, end)) + } +} + +function utf8Slice (buf, start, end) { + var res = '' + var tmp = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; i++) { + if (buf[i] <= 0x7F) { + res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]) + tmp = '' + } else { + tmp += '%' + buf[i].toString(16) + } + } + + return res + decodeUtf8Char(tmp) +} + +function asciiSlice (buf, start, end) { + var ret = '' + end = Math.min(buf.length, end) + + for (var i = start; i < end; i++) { + ret += String.fromCharCode(buf[i]) + } + return ret +} + +function binarySlice (buf, start, end) { + return asciiSlice(buf, start, end) +} + +function hexSlice (buf, start, end) { + var len = buf.length + + if (!start || start < 0) start = 0 + if (!end || end < 0 || end > len) end = len + + var out = '' + for (var i = start; i < end; i++) { + out += toHex(buf[i]) + } + return out +} + +function utf16leSlice (buf, start, end) { + var bytes = buf.slice(start, end) + var res = '' + for (var i = 0; i < bytes.length; i += 2) { + res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) + } + return res +} + +Buffer.prototype.slice = function (start, end) { + var len = this.length + start = ~~start + end = end === undefined ? len : ~~end + + if (start < 0) { + start += len; + if (start < 0) + start = 0 + } else if (start > len) { + start = len + } + + if (end < 0) { + end += len + if (end < 0) + end = 0 + } else if (end > len) { + end = len + } + + if (end < start) + end = start + + if (TYPED_ARRAY_SUPPORT) { + return Buffer._augment(this.subarray(start, end)) + } else { + var sliceLen = end - start + var newBuf = new Buffer(sliceLen, undefined, true) + for (var i = 0; i < sliceLen; i++) { + newBuf[i] = this[i + start] + } + return newBuf + } +} + +// `get` will be removed in Node 0.13+ +Buffer.prototype.get = function (offset) { + console.log('.get() is deprecated. Access using array indexes instead.') + return this.readUInt8(offset) +} + +// `set` will be removed in Node 0.13+ +Buffer.prototype.set = function (v, offset) { + console.log('.set() is deprecated. Access using array indexes instead.') + return this.writeUInt8(v, offset) +} + +Buffer.prototype.readUInt8 = function (offset, noAssert) { + if (!noAssert) { + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset < this.length, 'Trying to read beyond buffer length') + } + + if (offset >= this.length) + return + + return this[offset] +} + +function readUInt16 (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 1 < buf.length, 'Trying to read beyond buffer length') + } + + var len = buf.length + if (offset >= len) + return + + var val + if (littleEndian) { + val = buf[offset] + if (offset + 1 < len) + val |= buf[offset + 1] << 8 + } else { + val = buf[offset] << 8 + if (offset + 1 < len) + val |= buf[offset + 1] + } + return val +} + +Buffer.prototype.readUInt16LE = function (offset, noAssert) { + return readUInt16(this, offset, true, noAssert) +} + +Buffer.prototype.readUInt16BE = function (offset, noAssert) { + return readUInt16(this, offset, false, noAssert) +} + +function readUInt32 (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') + } + + var len = buf.length + if (offset >= len) + return + + var val + if (littleEndian) { + if (offset + 2 < len) + val = buf[offset + 2] << 16 + if (offset + 1 < len) + val |= buf[offset + 1] << 8 + val |= buf[offset] + if (offset + 3 < len) + val = val + (buf[offset + 3] << 24 >>> 0) + } else { + if (offset + 1 < len) + val = buf[offset + 1] << 16 + if (offset + 2 < len) + val |= buf[offset + 2] << 8 + if (offset + 3 < len) + val |= buf[offset + 3] + val = val + (buf[offset] << 24 >>> 0) + } + return val +} + +Buffer.prototype.readUInt32LE = function (offset, noAssert) { + return readUInt32(this, offset, true, noAssert) +} + +Buffer.prototype.readUInt32BE = function (offset, noAssert) { + return readUInt32(this, offset, false, noAssert) +} + +Buffer.prototype.readInt8 = function (offset, noAssert) { + if (!noAssert) { + assert(offset !== undefined && offset !== null, + 'missing offset') + assert(offset < this.length, 'Trying to read beyond buffer length') + } + + if (offset >= this.length) + return + + var neg = this[offset] & 0x80 + if (neg) + return (0xff - this[offset] + 1) * -1 + else + return this[offset] +} + +function readInt16 (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 1 < buf.length, 'Trying to read beyond buffer length') + } + + var len = buf.length + if (offset >= len) + return + + var val = readUInt16(buf, offset, littleEndian, true) + var neg = val & 0x8000 + if (neg) + return (0xffff - val + 1) * -1 + else + return val +} + +Buffer.prototype.readInt16LE = function (offset, noAssert) { + return readInt16(this, offset, true, noAssert) +} + +Buffer.prototype.readInt16BE = function (offset, noAssert) { + return readInt16(this, offset, false, noAssert) +} + +function readInt32 (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') + } + + var len = buf.length + if (offset >= len) + return + + var val = readUInt32(buf, offset, littleEndian, true) + var neg = val & 0x80000000 + if (neg) + return (0xffffffff - val + 1) * -1 + else + return val +} + +Buffer.prototype.readInt32LE = function (offset, noAssert) { + return readInt32(this, offset, true, noAssert) +} + +Buffer.prototype.readInt32BE = function (offset, noAssert) { + return readInt32(this, offset, false, noAssert) +} + +function readFloat (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset + 3 < buf.length, 'Trying to read beyond buffer length') + } + + return ieee754.read(buf, offset, littleEndian, 23, 4) +} + +Buffer.prototype.readFloatLE = function (offset, noAssert) { + return readFloat(this, offset, true, noAssert) +} + +Buffer.prototype.readFloatBE = function (offset, noAssert) { + return readFloat(this, offset, false, noAssert) +} + +function readDouble (buf, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset + 7 < buf.length, 'Trying to read beyond buffer length') + } + + return ieee754.read(buf, offset, littleEndian, 52, 8) +} + +Buffer.prototype.readDoubleLE = function (offset, noAssert) { + return readDouble(this, offset, true, noAssert) +} + +Buffer.prototype.readDoubleBE = function (offset, noAssert) { + return readDouble(this, offset, false, noAssert) +} + +Buffer.prototype.writeUInt8 = function (value, offset, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset < this.length, 'trying to write beyond buffer length') + verifuint(value, 0xff) + } + + if (offset >= this.length) return + + this[offset] = value + return offset + 1 +} + +function writeUInt16 (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 1 < buf.length, 'trying to write beyond buffer length') + verifuint(value, 0xffff) + } + + var len = buf.length + if (offset >= len) + return + + for (var i = 0, j = Math.min(len - offset, 2); i < j; i++) { + buf[offset + i] = + (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> + (littleEndian ? i : 1 - i) * 8 + } + return offset + 2 +} + +Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) { + return writeUInt16(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) { + return writeUInt16(this, value, offset, false, noAssert) +} + +function writeUInt32 (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'trying to write beyond buffer length') + verifuint(value, 0xffffffff) + } + + var len = buf.length + if (offset >= len) + return + + for (var i = 0, j = Math.min(len - offset, 4); i < j; i++) { + buf[offset + i] = + (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff + } + return offset + 4 +} + +Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) { + return writeUInt32(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) { + return writeUInt32(this, value, offset, false, noAssert) +} + +Buffer.prototype.writeInt8 = function (value, offset, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset < this.length, 'Trying to write beyond buffer length') + verifsint(value, 0x7f, -0x80) + } + + if (offset >= this.length) + return + + if (value >= 0) + this.writeUInt8(value, offset, noAssert) + else + this.writeUInt8(0xff + value + 1, offset, noAssert) + return offset + 1 +} + +function writeInt16 (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 1 < buf.length, 'Trying to write beyond buffer length') + verifsint(value, 0x7fff, -0x8000) + } + + var len = buf.length + if (offset >= len) + return + + if (value >= 0) + writeUInt16(buf, value, offset, littleEndian, noAssert) + else + writeUInt16(buf, 0xffff + value + 1, offset, littleEndian, noAssert) + return offset + 2 +} + +Buffer.prototype.writeInt16LE = function (value, offset, noAssert) { + return writeInt16(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeInt16BE = function (value, offset, noAssert) { + return writeInt16(this, value, offset, false, noAssert) +} + +function writeInt32 (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'Trying to write beyond buffer length') + verifsint(value, 0x7fffffff, -0x80000000) + } + + var len = buf.length + if (offset >= len) + return + + if (value >= 0) + writeUInt32(buf, value, offset, littleEndian, noAssert) + else + writeUInt32(buf, 0xffffffff + value + 1, offset, littleEndian, noAssert) + return offset + 4 +} + +Buffer.prototype.writeInt32LE = function (value, offset, noAssert) { + return writeInt32(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeInt32BE = function (value, offset, noAssert) { + return writeInt32(this, value, offset, false, noAssert) +} + +function writeFloat (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 3 < buf.length, 'Trying to write beyond buffer length') + verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38) + } + + var len = buf.length + if (offset >= len) + return + + ieee754.write(buf, value, offset, littleEndian, 23, 4) + return offset + 4 +} + +Buffer.prototype.writeFloatLE = function (value, offset, noAssert) { + return writeFloat(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeFloatBE = function (value, offset, noAssert) { + return writeFloat(this, value, offset, false, noAssert) +} + +function writeDouble (buf, value, offset, littleEndian, noAssert) { + if (!noAssert) { + assert(value !== undefined && value !== null, 'missing value') + assert(typeof littleEndian === 'boolean', 'missing or invalid endian') + assert(offset !== undefined && offset !== null, 'missing offset') + assert(offset + 7 < buf.length, + 'Trying to write beyond buffer length') + verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308) + } + + var len = buf.length + if (offset >= len) + return + + ieee754.write(buf, value, offset, littleEndian, 52, 8) + return offset + 8 +} + +Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) { + return writeDouble(this, value, offset, true, noAssert) +} + +Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) { + return writeDouble(this, value, offset, false, noAssert) +} + +// fill(value, start=0, end=buffer.length) +Buffer.prototype.fill = function (value, start, end) { + if (!value) value = 0 + if (!start) start = 0 + if (!end) end = this.length + + assert(end >= start, 'end < start') + + // Fill 0 bytes; we're done + if (end === start) return + if (this.length === 0) return + + assert(start >= 0 && start < this.length, 'start out of bounds') + assert(end >= 0 && end <= this.length, 'end out of bounds') + + var i + if (typeof value === 'number') { + for (i = start; i < end; i++) { + this[i] = value + } + } else { + var bytes = utf8ToBytes(value.toString()) + var len = bytes.length + for (i = start; i < end; i++) { + this[i] = bytes[i % len] + } + } + + return this +} + +Buffer.prototype.inspect = function () { + var out = [] + var len = this.length + for (var i = 0; i < len; i++) { + out[i] = toHex(this[i]) + if (i === exports.INSPECT_MAX_BYTES) { + out[i + 1] = '...' + break + } + } + return '' +} + +/** + * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance. + * Added in Node 0.12. Only available in browsers that support ArrayBuffer. + */ +Buffer.prototype.toArrayBuffer = function () { + if (typeof Uint8Array !== 'undefined') { + if (TYPED_ARRAY_SUPPORT) { + return (new Buffer(this)).buffer + } else { + var buf = new Uint8Array(this.length) + for (var i = 0, len = buf.length; i < len; i += 1) { + buf[i] = this[i] + } + return buf.buffer + } + } else { + throw new Error('Buffer.toArrayBuffer not supported in this browser') + } +} + +// HELPER FUNCTIONS +// ================ + +var BP = Buffer.prototype + +/** + * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods + */ +Buffer._augment = function (arr) { + arr._isBuffer = true + + // save reference to original Uint8Array get/set methods before overwriting + arr._get = arr.get + arr._set = arr.set + + // deprecated, will be removed in node 0.13+ + arr.get = BP.get + arr.set = BP.set + + arr.write = BP.write + arr.toString = BP.toString + arr.toLocaleString = BP.toString + arr.toJSON = BP.toJSON + arr.equals = BP.equals + arr.compare = BP.compare + arr.copy = BP.copy + arr.slice = BP.slice + arr.readUInt8 = BP.readUInt8 + arr.readUInt16LE = BP.readUInt16LE + arr.readUInt16BE = BP.readUInt16BE + arr.readUInt32LE = BP.readUInt32LE + arr.readUInt32BE = BP.readUInt32BE + arr.readInt8 = BP.readInt8 + arr.readInt16LE = BP.readInt16LE + arr.readInt16BE = BP.readInt16BE + arr.readInt32LE = BP.readInt32LE + arr.readInt32BE = BP.readInt32BE + arr.readFloatLE = BP.readFloatLE + arr.readFloatBE = BP.readFloatBE + arr.readDoubleLE = BP.readDoubleLE + arr.readDoubleBE = BP.readDoubleBE + arr.writeUInt8 = BP.writeUInt8 + arr.writeUInt16LE = BP.writeUInt16LE + arr.writeUInt16BE = BP.writeUInt16BE + arr.writeUInt32LE = BP.writeUInt32LE + arr.writeUInt32BE = BP.writeUInt32BE + arr.writeInt8 = BP.writeInt8 + arr.writeInt16LE = BP.writeInt16LE + arr.writeInt16BE = BP.writeInt16BE + arr.writeInt32LE = BP.writeInt32LE + arr.writeInt32BE = BP.writeInt32BE + arr.writeFloatLE = BP.writeFloatLE + arr.writeFloatBE = BP.writeFloatBE + arr.writeDoubleLE = BP.writeDoubleLE + arr.writeDoubleBE = BP.writeDoubleBE + arr.fill = BP.fill + arr.inspect = BP.inspect + arr.toArrayBuffer = BP.toArrayBuffer + + return arr +} + +var INVALID_BASE64_RE = /[^+\/0-9A-z]/g + +function base64clean (str) { + // Node strips out invalid characters like \n and \t from the string, base64-js does not + str = stringtrim(str).replace(INVALID_BASE64_RE, '') + // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not + while (str.length % 4 !== 0) { + str = str + '=' + } + return str +} + +function stringtrim (str) { + if (str.trim) return str.trim() + return str.replace(/^\s+|\s+$/g, '') +} + +function isArray (subject) { + return (Array.isArray || function (subject) { + return Object.prototype.toString.call(subject) === '[object Array]' + })(subject) +} + +function isArrayish (subject) { + return isArray(subject) || Buffer.isBuffer(subject) || + subject && typeof subject === 'object' && + typeof subject.length === 'number' +} + +function toHex (n) { + if (n < 16) return '0' + n.toString(16) + return n.toString(16) +} + +function utf8ToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; i++) { + var b = str.charCodeAt(i) + if (b <= 0x7F) { + byteArray.push(b) + } else { + var start = i + if (b >= 0xD800 && b <= 0xDFFF) i++ + var h = encodeURIComponent(str.slice(start, i+1)).substr(1).split('%') + for (var j = 0; j < h.length; j++) { + byteArray.push(parseInt(h[j], 16)) + } + } + } + return byteArray +} + +function asciiToBytes (str) { + var byteArray = [] + for (var i = 0; i < str.length; i++) { + // Node's code seems to be doing this and not & 0x7F.. + byteArray.push(str.charCodeAt(i) & 0xFF) + } + return byteArray +} + +function utf16leToBytes (str) { + var c, hi, lo + var byteArray = [] + for (var i = 0; i < str.length; i++) { + c = str.charCodeAt(i) + hi = c >> 8 + lo = c % 256 + byteArray.push(lo) + byteArray.push(hi) + } + + return byteArray +} + +function base64ToBytes (str) { + return base64.toByteArray(str) +} + +function blitBuffer (src, dst, offset, length) { + for (var i = 0; i < length; i++) { + if ((i + offset >= dst.length) || (i >= src.length)) + break + dst[i + offset] = src[i] + } + return i +} + +function decodeUtf8Char (str) { + try { + return decodeURIComponent(str) + } catch (err) { + return String.fromCharCode(0xFFFD) // UTF 8 invalid char + } +} + +/* + * We have to make sure that the value is a valid integer. This means that it + * is non-negative. It has no fractional component and that it does not + * exceed the maximum allowed value. + */ +function verifuint (value, max) { + assert(typeof value === 'number', 'cannot write a non-number as a number') + assert(value >= 0, 'specified a negative value for writing an unsigned value') + assert(value <= max, 'value is larger than maximum value for type') + assert(Math.floor(value) === value, 'value has a fractional component') +} + +function verifsint (value, max, min) { + assert(typeof value === 'number', 'cannot write a non-number as a number') + assert(value <= max, 'value larger than maximum allowed value') + assert(value >= min, 'value smaller than minimum allowed value') + assert(Math.floor(value) === value, 'value has a fractional component') +} + +function verifIEEE754 (value, max, min) { + assert(typeof value === 'number', 'cannot write a non-number as a number') + assert(value <= max, 'value larger than maximum allowed value') + assert(value >= min, 'value smaller than minimum allowed value') +} + +function assert (test, message) { + if (!test) throw new Error(message || 'Failed assertion') +} + +},{"base64-js":25,"ieee754":26}],25:[function(require,module,exports){ +var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +;(function (exports) { + 'use strict'; + + var Arr = (typeof Uint8Array !== 'undefined') + ? Uint8Array + : Array + + var PLUS = '+'.charCodeAt(0) + var SLASH = '/'.charCodeAt(0) + var NUMBER = '0'.charCodeAt(0) + var LOWER = 'a'.charCodeAt(0) + var UPPER = 'A'.charCodeAt(0) + + function decode (elt) { + var code = elt.charCodeAt(0) + if (code === PLUS) + return 62 // '+' + if (code === SLASH) + return 63 // '/' + if (code < NUMBER) + return -1 //no match + if (code < NUMBER + 10) + return code - NUMBER + 26 + 26 + if (code < UPPER + 26) + return code - UPPER + if (code < LOWER + 26) + return code - LOWER + 26 + } + + function b64ToByteArray (b64) { + var i, j, l, tmp, placeHolders, arr + + if (b64.length % 4 > 0) { + throw new Error('Invalid string. Length must be a multiple of 4') + } + + // the number of equal signs (place holders) + // if there are two placeholders, than the two characters before it + // represent one byte + // if there is only one, then the three characters before it represent 2 bytes + // this is just a cheap hack to not do indexOf twice + var len = b64.length + placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0 + + // base64 is 4/3 + up to two characters of the original data + arr = new Arr(b64.length * 3 / 4 - placeHolders) + + // if there are placeholders, only get up to the last complete 4 chars + l = placeHolders > 0 ? b64.length - 4 : b64.length + + var L = 0 + + function push (v) { + arr[L++] = v + } + + for (i = 0, j = 0; i < l; i += 4, j += 3) { + tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3)) + push((tmp & 0xFF0000) >> 16) + push((tmp & 0xFF00) >> 8) + push(tmp & 0xFF) + } + + if (placeHolders === 2) { + tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4) + push(tmp & 0xFF) + } else if (placeHolders === 1) { + tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2) + push((tmp >> 8) & 0xFF) + push(tmp & 0xFF) + } + + return arr + } + + function uint8ToBase64 (uint8) { + var i, + extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes + output = "", + temp, length + + function encode (num) { + return lookup.charAt(num) + } + + function tripletToBase64 (num) { + return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F) + } + + // go through the array every three bytes, we'll deal with trailing stuff later + for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) { + temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2]) + output += tripletToBase64(temp) + } + + // pad the end with zeros, but make sure to not forget the extra bytes + switch (extraBytes) { + case 1: + temp = uint8[uint8.length - 1] + output += encode(temp >> 2) + output += encode((temp << 4) & 0x3F) + output += '==' + break + case 2: + temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1]) + output += encode(temp >> 10) + output += encode((temp >> 4) & 0x3F) + output += encode((temp << 2) & 0x3F) + output += '=' + break + } + + return output + } + + exports.toByteArray = b64ToByteArray + exports.fromByteArray = uint8ToBase64 +}(typeof exports === 'undefined' ? (this.base64js = {}) : exports)) + +},{}],26:[function(require,module,exports){ +exports.read = function(buffer, offset, isLE, mLen, nBytes) { + var e, m, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = isLE ? (nBytes - 1) : 0, + d = isLE ? -1 : 1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); +}; + +exports.write = function(buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = isLE ? 0 : (nBytes - 1), + d = isLE ? 1 : -1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); + + buffer[offset + i - d] |= s * 128; +}; + +},{}],27:[function(require,module,exports){ +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} + +},{}],28:[function(require,module,exports){ +(function (process){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + +// posix version +exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; +}; + +// posix version +exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(filter(paths, function(p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/')); +}; + + +// path.relative(from, to) +// posix version +exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +}; + +exports.sep = '/'; +exports.delimiter = ':'; + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + +function filter (xs, f) { + if (xs.filter) return xs.filter(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + if (f(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// String.prototype.substr - negative index don't work in IE8 +var substr = 'ab'.substr(-1) === 'b' + ? function (str, start, len) { return str.substr(start, len) } + : function (str, start, len) { + if (start < 0) start = str.length + start; + return str.substr(start, len); + } +; + +}).call(this,require('_process')) +},{"_process":29}],29:[function(require,module,exports){ +// shim for using process in browser + +var process = module.exports = {}; + +process.nextTick = (function () { + var canSetImmediate = typeof window !== 'undefined' + && window.setImmediate; + var canPost = typeof window !== 'undefined' + && window.postMessage && window.addEventListener + ; + + if (canSetImmediate) { + return function (f) { return window.setImmediate(f) }; + } + + if (canPost) { + var queue = []; + window.addEventListener('message', function (ev) { + var source = ev.source; + if ((source === window || source === null) && ev.data === 'process-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + + return function nextTick(fn) { + queue.push(fn); + window.postMessage('process-tick', '*'); + }; + } + + return function nextTick(fn) { + setTimeout(fn, 0); + }; +})(); + +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +} + +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; + +},{}],30:[function(require,module,exports){ +module.exports = function isBuffer(arg) { + return arg && typeof arg === 'object' + && typeof arg.copy === 'function' + && typeof arg.fill === 'function' + && typeof arg.readUInt8 === 'function'; +} +},{}],31:[function(require,module,exports){ +(function (process,global){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var formatRegExp = /%[sdj%]/g; +exports.format = function(f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(' '); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function(x) { + if (x === '%%') return '%'; + if (i >= len) return x; + switch (x) { + case '%s': return String(args[i++]); + case '%d': return Number(args[i++]); + case '%j': + try { + return JSON.stringify(args[i++]); + } catch (_) { + return '[Circular]'; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += ' ' + x; + } else { + str += ' ' + inspect(x); + } + } + return str; +}; + + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +exports.deprecate = function(fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function() { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; +}; + + +var debugs = {}; +var debugEnviron; +exports.debuglog = function(set) { + if (isUndefined(debugEnviron)) + debugEnviron = process.env.NODE_DEBUG || ''; + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function() { + var msg = exports.format.apply(exports, arguments); + console.error('%s %d: %s', set, pid, msg); + }; + } else { + debugs[set] = function() {}; + } + } + return debugs[set]; +}; + + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ +/* legacy: obj, showHidden, depth, colors*/ +function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); +} +exports.inspect = inspect; + + +// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics +inspect.colors = { + 'bold' : [1, 22], + 'italic' : [3, 23], + 'underline' : [4, 24], + 'inverse' : [7, 27], + 'white' : [37, 39], + 'grey' : [90, 39], + 'black' : [30, 39], + 'blue' : [34, 39], + 'cyan' : [36, 39], + 'green' : [32, 39], + 'magenta' : [35, 39], + 'red' : [31, 39], + 'yellow' : [33, 39] +}; + +// Don't use 'blue' not visible on cmd.exe +inspect.styles = { + 'special': 'cyan', + 'number': 'yellow', + 'boolean': 'yellow', + 'undefined': 'grey', + 'null': 'bold', + 'string': 'green', + 'date': 'magenta', + // "name": intentionally not styling + 'regexp': 'red' +}; + + +function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return '\u001b[' + inspect.colors[style][0] + 'm' + str + + '\u001b[' + inspect.colors[style][1] + 'm'; + } else { + return str; + } +} + + +function stylizeNoColor(str, styleType) { + return str; +} + + +function arrayToHash(array) { + var hash = {}; + + array.forEach(function(val, idx) { + hash[val] = true; + }); + + return hash; +} + + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if (isError(value) + && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ': ' + value.name : ''; + return ctx.stylize('[Function' + name + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ': ' + value.name : ''; + base = ' [Function' + n + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = ' ' + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + if (isUndefined(value)) + return ctx.stylize('undefined', 'undefined'); + if (isString(value)) { + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + } + if (isNumber(value)) + return ctx.stylize('' + value, 'number'); + if (isBoolean(value)) + return ctx.stylize('' + value, 'boolean'); + // For some reason typeof null is "object", so special case here. + if (isNull(value)) + return ctx.stylize('null', 'null'); +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (desc.set) { + str = ctx.stylize('[Setter]', 'special'); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + + +// NOTE: These type checking functions intentionally don't use `instanceof` +// because it is fragile and can be easily faked with `Object.create()`. +function isArray(ar) { + return Array.isArray(ar); +} +exports.isArray = isArray; + +function isBoolean(arg) { + return typeof arg === 'boolean'; +} +exports.isBoolean = isBoolean; + +function isNull(arg) { + return arg === null; +} +exports.isNull = isNull; + +function isNullOrUndefined(arg) { + return arg == null; +} +exports.isNullOrUndefined = isNullOrUndefined; + +function isNumber(arg) { + return typeof arg === 'number'; +} +exports.isNumber = isNumber; + +function isString(arg) { + return typeof arg === 'string'; +} +exports.isString = isString; + +function isSymbol(arg) { + return typeof arg === 'symbol'; +} +exports.isSymbol = isSymbol; + +function isUndefined(arg) { + return arg === void 0; +} +exports.isUndefined = isUndefined; + +function isRegExp(re) { + return isObject(re) && objectToString(re) === '[object RegExp]'; +} +exports.isRegExp = isRegExp; + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} +exports.isObject = isObject; + +function isDate(d) { + return isObject(d) && objectToString(d) === '[object Date]'; +} +exports.isDate = isDate; + +function isError(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); +} +exports.isError = isError; + +function isFunction(arg) { + return typeof arg === 'function'; +} +exports.isFunction = isFunction; + +function isPrimitive(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; +} +exports.isPrimitive = isPrimitive; + +exports.isBuffer = require('./support/isBuffer'); + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + + +function pad(n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +} + + +var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', + 'Oct', 'Nov', 'Dec']; + +// 26 Feb 16:19:34 +function timestamp() { + var d = new Date(); + var time = [pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds())].join(':'); + return [d.getDate(), months[d.getMonth()], time].join(' '); +} + + +// log is just a thin wrapper to console.log that prepends a timestamp +exports.log = function() { + console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ +exports.inherits = require('inherits'); + +exports._extend = function(origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; +}; + +function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); +} + +}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./support/isBuffer":30,"_process":29,"inherits":27}],"joi":[function(require,module,exports){ +arguments[4][16][0].apply(exports,arguments) +},{"./lib":10}]},{},[])("joi") +}); \ No newline at end of file diff --git a/js/apps/system/aardvark/frontend/js/models/arangoQuery.js b/js/apps/system/aardvark/frontend/js/models/arangoQuery.js index 19c7792e9c..465b1f2304 100644 --- a/js/apps/system/aardvark/frontend/js/models/arangoQuery.js +++ b/js/apps/system/aardvark/frontend/js/models/arangoQuery.js @@ -9,7 +9,7 @@ defaults: { name: "", type: "custom", - content: "" + value: "" } }); diff --git a/js/apps/system/aardvark/frontend/js/routers/router.js b/js/apps/system/aardvark/frontend/js/routers/router.js index d76812cfec..648b2bf7c9 100644 --- a/js/apps/system/aardvark/frontend/js/routers/router.js +++ b/js/apps/system/aardvark/frontend/js/routers/router.js @@ -86,9 +86,7 @@ userCollection: this.userCollection }); - this.queryCollection = new window.ArangoQueries([],{ - activeUser: self.userCollection.activeUser - }); + this.queryCollection = new window.ArangoQueries(); this.footerView.render(); this.naviView.render(); diff --git a/js/apps/system/aardvark/frontend/js/templates/documentView.ejs b/js/apps/system/aardvark/frontend/js/templates/documentView.ejs index 9eb03e38fa..02911ccb53 100644 --- a/js/apps/system/aardvark/frontend/js/templates/documentView.ejs +++ b/js/apps/system/aardvark/frontend/js/templates/documentView.ejs @@ -5,8 +5,42 @@
+
+ +
+
+
+
+ +
+ +
+
+
_rev:
+
+
+
+
_key:
+
+
+
+ +
+
+
_from:
+ +
+
+
_to:
+ +
+
+ +
+
- + +
diff --git a/js/apps/system/aardvark/frontend/js/templates/queryView.ejs b/js/apps/system/aardvark/frontend/js/templates/queryView.ejs index 900a0d79e0..928c2d3c27 100644 --- a/js/apps/system/aardvark/frontend/js/templates/queryView.ejs +++ b/js/apps/system/aardvark/frontend/js/templates/queryView.ejs @@ -1,6 +1,25 @@