diff --git a/UnitTests/HttpInterface/api-admin-spec.rb b/UnitTests/HttpInterface/api-admin-spec.rb index 48cf0d049a..a8d538a748 100644 --- a/UnitTests/HttpInterface/api-admin-spec.rb +++ b/UnitTests/HttpInterface/api-admin-spec.rb @@ -10,7 +10,7 @@ describe ArangoDB do before do # load the most current routing information cmd = "/_admin/routing/reload" - doc = ArangoDB.get(cmd) + ArangoDB.get(cmd) end ################################################################################ @@ -121,10 +121,10 @@ describe ArangoDB do it "checks whether the admin interface is available at /_admin/aardvark/index.html" do cmd = "/_admin/aardvark/index.html" begin - doc = ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) + ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) rescue HTTParty::RedirectionTooDeep => e # check response code - e.response.code.should eq("301") + e.response.code.should eq("302") end end @@ -149,7 +149,7 @@ describe ArangoDB do it "checks whether the admin interface is available at /" do cmd = "/" begin - doc = ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) + ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) rescue HTTParty::RedirectionTooDeep => e # check response code e.response.code.should eq("301") @@ -160,7 +160,7 @@ describe ArangoDB do it "checks whether the admin interface is available at /_admin/html" do cmd = "/_admin/html" begin - doc = ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) + ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) rescue HTTParty::RedirectionTooDeep => e # check response code e.response.code.should eq("301") @@ -171,7 +171,7 @@ describe ArangoDB do it "checks whether the admin interface is available at /_admin/html/" do cmd = "/_admin/html/" begin - doc = ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) + ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) rescue HTTParty::RedirectionTooDeep => e # check response code e.response.code.should eq("301") @@ -182,7 +182,7 @@ describe ArangoDB do it "checks whether the admin interface is available at /_admin/aardvark/" do cmd = "/_admin/aardvark/" begin - doc = ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) + ArangoDB.log_get("admin-interface-get", cmd, :format => :plain, :no_follow => true) rescue HTTParty::RedirectionTooDeep => e # check response code e.response.code.should eq("301") diff --git a/arangod/Aql/Executor.cpp b/arangod/Aql/Executor.cpp index 3c3f28d701..e657f11cc1 100644 --- a/arangod/Aql/Executor.cpp +++ b/arangod/Aql/Executor.cpp @@ -178,7 +178,7 @@ std::unordered_map const Executor::FunctionNames{ // document functions { "HAS", Function("HAS", "AQL_HAS", "az,s", true, false, true, &Functions::Has) }, { "ATTRIBUTES", Function("ATTRIBUTES", "AQL_ATTRIBUTES", "a|b,b", true, false, true, &Functions::Attributes) }, - { "VALUES", Function("VALUES", "AQL_VALUES", "a|b", true, false, true) }, + { "VALUES", Function("VALUES", "AQL_VALUES", "a|b", true, false, true, &Functions::Values) }, { "MERGE", Function("MERGE", "AQL_MERGE", "a,a|+", true, false, true, &Functions::Merge) }, { "MERGE_RECURSIVE", Function("MERGE_RECURSIVE", "AQL_MERGE_RECURSIVE", "a,a|+", true, false, true) }, { "DOCUMENT", Function("DOCUMENT", "AQL_DOCUMENT", "h.|.", false, true, false) }, diff --git a/arangod/Aql/Functions.cpp b/arangod/Aql/Functions.cpp index 78da598553..3ebfe5f031 100644 --- a/arangod/Aql/Functions.cpp +++ b/arangod/Aql/Functions.cpp @@ -679,6 +679,65 @@ AqlValue Functions::Attributes (triagens::aql::Query* query, return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, result.steal())); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief function VALUES +//////////////////////////////////////////////////////////////////////////////// + +AqlValue Functions::Values (triagens::aql::Query* query, + triagens::arango::AqlTransaction* trx, + TRI_document_collection_t const* collection, + AqlValue const parameters) { + size_t const n = parameters.arraySize(); + + if (n < 1) { + // no parameters + return AqlValue(new Json(Json::Null)); + } + + Json value(parameters.extractArrayMember(trx, collection, 0, false)); + + if (! value.isObject()) { + // not an object + RegisterWarning(query, "ATTRIBUTES", TRI_ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH); + return AqlValue(new Json(Json::Null)); + } + + bool const removeInternal = GetBooleanParameter(trx, collection, parameters, 1, false); + + auto const valueJson = value.json(); + TRI_ASSERT(TRI_IsObjectJson(valueJson)); + + size_t const numValues = TRI_LengthVectorJson(valueJson); + + if (numValues == 0) { + // empty object + return AqlValue(new Json(Json::Object)); + } + + // create the output + Json result(Json::Array, numValues); + + // create a vector with positions into the object + for (size_t i = 0; i < numValues; i += 2) { + auto key = static_cast(TRI_AddressVector(&valueJson->_value._objects, i)); + + if (! TRI_IsStringJson(key)) { + // somehow invalid + continue; + } + + if (removeInternal && *key->_value._string.data == '_') { + // skip attribute + continue; + } + + auto value = static_cast(TRI_AddressVector(&valueJson->_value._objects, i + 1)); + result.add(Json(TRI_UNKNOWN_MEM_ZONE, TRI_CopyJson(TRI_UNKNOWN_MEM_ZONE, value))); + } + + return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, result.steal())); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief function MIN //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Functions.h b/arangod/Aql/Functions.h index 92feff964d..d45d816d7d 100644 --- a/arangod/Aql/Functions.h +++ b/arangod/Aql/Functions.h @@ -67,6 +67,7 @@ namespace triagens { static AqlValue Merge (triagens::aql::Query*, triagens::arango::AqlTransaction*, TRI_document_collection_t const*, AqlValue const); static AqlValue Has (triagens::aql::Query*, triagens::arango::AqlTransaction*, TRI_document_collection_t const*, AqlValue const); static AqlValue Attributes (triagens::aql::Query*, triagens::arango::AqlTransaction*, TRI_document_collection_t const*, AqlValue const); + static AqlValue Values (triagens::aql::Query*, triagens::arango::AqlTransaction*, TRI_document_collection_t const*, AqlValue const); static AqlValue Min (triagens::aql::Query*, triagens::arango::AqlTransaction*, TRI_document_collection_t const*, AqlValue const); static AqlValue Max (triagens::aql::Query*, triagens::arango::AqlTransaction*, TRI_document_collection_t const*, AqlValue const); static AqlValue Sum (triagens::aql::Query*, triagens::arango::AqlTransaction*, TRI_document_collection_t const*, AqlValue const); diff --git a/js/server/modules/org/arangodb/foxx/arangoApp.js b/js/server/modules/org/arangodb/foxx/arangoApp.js index f7f218500d..9f28817c85 100644 --- a/js/server/modules/org/arangodb/foxx/arangoApp.js +++ b/js/server/modules/org/arangodb/foxx/arangoApp.js @@ -161,6 +161,12 @@ function computeRootAppPath(mount, isValidation) { name: "unknown", version: "error" }; + if (!this._manifest.configuration) { + this._manifest.configuration = {}; + } + if (!this._manifest.dependencies) { + this._manifest.dependencies = {}; + } this._name = this._manifest.name; this._version = this._manifest.version; this._root = computeRootAppPath(config.mount, config.id === "__internal"); diff --git a/js/server/tests/aql-functions.js b/js/server/tests/aql-functions.js index 942cc37833..443913b1bd 100644 --- a/js/server/tests/aql-functions.js +++ b/js/server/tests/aql-functions.js @@ -2486,6 +2486,46 @@ function ahuacatlFunctionsTestSuite () { assertEqual(expected, actual); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test attributes function +//////////////////////////////////////////////////////////////////////////////// + + testAttributesCxx1 : function () { + var expected = [ [ "foo", "bar", "meow", "_id" ], [ "foo" ] ]; + var actual = getQueryResults("FOR u IN [ { foo: \"bar\", bar: \"baz\", meow: true, _id: \"123/456\" }, { foo: \"bar\" } ] RETURN NOOPT(ATTRIBUTES(u))"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test attributes function +//////////////////////////////////////////////////////////////////////////////// + + testAttributesCxx2 : function () { + var expected = [ [ "foo", "bar", "meow" ], [ "foo" ] ]; + var actual = getQueryResults("FOR u IN [ { foo: \"bar\", bar: \"baz\", meow: true, _id: \"123/456\" }, { foo: \"bar\" } ] RETURN NOOPT(ATTRIBUTES(u, true))"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test attributes function +//////////////////////////////////////////////////////////////////////////////// + + testAttributesCxx3 : function () { + var expected = [ [ "_id", "bar", "foo", "meow" ], [ "foo" ] ]; + var actual = getQueryResults("FOR u IN [ { foo: \"bar\", bar: \"baz\", meow: true, _id: \"123/456\" }, { foo: \"bar\" } ] RETURN NOOPT(ATTRIBUTES(u, false, true))"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test attributes function +//////////////////////////////////////////////////////////////////////////////// + + testAttributesCxx4 : function () { + var expected = [ [ "bar", "foo", "meow" ], [ "foo" ] ]; + var actual = getQueryResults("FOR u IN [ { foo: \"bar\", bar: \"baz\", meow: true, _id: \"123/456\" }, { foo: \"bar\" } ] RETURN NOOPT(ATTRIBUTES(u, true, true))"); + assertEqual(expected, actual); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test values function //////////////////////////////////////////////////////////////////////////////// @@ -2516,6 +2556,36 @@ function ahuacatlFunctionsTestSuite () { assertEqual(expected, actual); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test values function +//////////////////////////////////////////////////////////////////////////////// + + testValuesCxx1 : function () { + var expected = [ [ "bar", "baz", true, "123/456" ], [ "bar" ] ]; + var actual = getQueryResults("FOR u IN [ { foo: \"bar\", bar: \"baz\", meow: true, _id: \"123/456\" }, { foo: \"bar\" } ] RETURN NOOPT(VALUES(u))"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test values function +//////////////////////////////////////////////////////////////////////////////// + + testValuesCxx2 : function () { + var expected = [ [ "bar", "baz", true ], [ "bar" ] ]; + var actual = getQueryResults("FOR u IN [ { foo: \"bar\", bar: \"baz\", meow: true, _id: \"123/456\" }, { foo: \"bar\" } ] RETURN NOOPT(VALUES(u, true))"); + assertEqual(expected, actual); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test values function +//////////////////////////////////////////////////////////////////////////////// + + testValuesCxx3 : function () { + var expected = [ [ "test/1123", "test/abc", "1234", "test", "", [ 1, 2, 3, 4, [ true ] ], null, { d: 42, e: null, f: [ "test!" ] } ] ]; + var actual = getQueryResults("RETURN NOOPT(VALUES({ _from: \"test/1123\", _to: \"test/abc\", _rev: \"1234\", _key: \"test\", void: \"\", a: [ 1, 2, 3, 4, [ true ] ], b : null, c: { d: 42, e: null, f: [ \"test!\" ] } }))"); + assertEqual(expected, actual); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test zip function ////////////////////////////////////////////////////////////////////////////////