1
0
Fork 0

Porting fix for issue #9652 (#9727)

* Porting fix for issue #9652

* Update CHANGELOG
This commit is contained in:
Dronplane 2019-08-16 13:27:51 +03:00 committed by KVS85
parent b753c895e4
commit 5effbfd35a
19 changed files with 1641 additions and 351 deletions

View File

@ -658,10 +658,19 @@ bool make_json_config(
if(!options.explicit_stopwords.empty() || options.explicit_stopwords_set) {
// explicit_stopwords_set marks that even empty stopwords list is valid
rapidjson::Value stopwordsArray(rapidjson::kArrayType);
for (const auto& stopword : options.explicit_stopwords) {
stopwordsArray.PushBack(
rapidjson::StringRef(stopword.c_str(), stopword.size()),
allocator);
if (!options.explicit_stopwords.empty()) {
// for simplifying comparsion between properties we need deterministic order of stopwords
std::vector<irs::string_ref> sortedWords;
sortedWords.reserve(options.explicit_stopwords.size());
for (const auto& stopword : options.explicit_stopwords) {
sortedWords.emplace_back(stopword);
}
std::sort(sortedWords.begin(), sortedWords.end());
for (const auto& stopword : sortedWords) {
stopwordsArray.PushBack(
rapidjson::StringRef(stopword.c_str(), stopword.size()),
allocator);
}
}
json.AddMember(

View File

@ -1,6 +1,10 @@
v3.5.1 (XXXX-XX-XX)
-------------------
* Fixed issue #9652: fix ArangoSearch wrong name collision and raising
"Name collision detected" error during creation of a custom analyzer with
stopwords.
* Fixed an agency bug found in Windows tests.
* Added initial support for wgs84 reference ellipsoid in GEO_DISTANCE through

View File

@ -522,11 +522,34 @@ bool equalAnalyzer(
return false;
}
return type == pool.type() // same type
&& 0 == arangodb::basics::VelocyPackHelper::compare(
arangodb::iresearch::slice(normalizedProperties),
pool.properties(), false) // same properties
&& features == pool.features(); // same features
// first check non-normalizeable portion of analyzer definition
// to rule out need to check normalization of properties
if (type != pool.type() || features != pool.features()) {
return false;
}
// this check is not final as old-normalized definition may be present in database!
if (arangodb::basics::VelocyPackHelper::equal(arangodb::iresearch::slice(normalizedProperties),
pool.properties(), false)) {
return true;
}
// Here could be analyzer definition with old-normalized properties (see Issue #9652)
// To make sure properties really differ, let`s re-normalize and re-check
std::string reNormalizedProperties;
if (ADB_UNLIKELY(!::normalize(reNormalizedProperties, pool.type(), pool.properties()))) {
// failed to re-normalize definition - strange. It was already normalized once.
// Some bug in load/store?
TRI_ASSERT(FALSE);
LOG_TOPIC("a4073", WARN, arangodb::iresearch::TOPIC)
<< "failed to re-normalize properties for analyzer type '"
<< pool.type()
<< "' properties '" << pool.properties().toString() << "'";
return false;
}
return arangodb::basics::VelocyPackHelper::equal(
arangodb::iresearch::slice(normalizedProperties),
arangodb::iresearch::slice(reNormalizedProperties), false);
}
////////////////////////////////////////////////////////////////////////////////
@ -670,34 +693,6 @@ void registerUpgradeTasks() {
});
}
////////////////////////////////////////////////////////////////////////////////
/// @brief split the analyzer name into the vocbase part and analyzer part
/// @param name analyzer name
/// @return pair of first == vocbase name, second == analyzer name
/// EMPTY == system vocbase
/// NIL == unprefixed analyzer name, i.e. active vocbase
////////////////////////////////////////////////////////////////////////////////
std::pair<irs::string_ref, irs::string_ref> splitAnalyzerName( // split name
irs::string_ref const& analyzer // analyzer name
) noexcept {
// search for vocbase prefix ending with '::'
for (size_t i = 1, count = analyzer.size(); i < count; ++i) {
if (analyzer[i] == ANALYZER_PREFIX_DELIM // current is delim
&& analyzer[i - 1] == ANALYZER_PREFIX_DELIM) { // previous is also delim
auto vocbase = i > 1 // non-empty prefix, +1 for first delimiter char
? irs::string_ref(analyzer.c_str(), i - 1) // -1 for the first ':' delimiter
: irs::string_ref::EMPTY;
auto name = i < count - 1 // have suffix
? irs::string_ref(analyzer.c_str() + i + 1, count - i - 1) // +-1 for the suffix after '::'
: irs::string_ref::EMPTY; // do not point after end of buffer
return std::make_pair(vocbase, name); // prefixed analyzer name
}
}
return std::make_pair(irs::string_ref::NIL, analyzer); // unprefixed analyzer name
}
////////////////////////////////////////////////////////////////////////////////
/// @brief read analyzers from vocbase
/// @return visitation completed fully
@ -1971,6 +1966,49 @@ arangodb::Result IResearchAnalyzerFeature::loadAnalyzers(
return FEATURE_NAME;
}
/*static*/ irs::string_ref IResearchAnalyzerFeature::extractVocbaseName(
irs::string_ref const& name) noexcept {// analyzer name (normalized)
auto split = splitAnalyzerName(name);
return split.first;
}
/*static*/ bool IResearchAnalyzerFeature::analyzerReachableFromDb(
irs::string_ref const& dbNameFromAnalyzer,
irs::string_ref const& currentDbName, bool forGetters) noexcept {
TRI_ASSERT(!currentDbName.empty());
if (dbNameFromAnalyzer.null()) { // NULL means local db name = always reachable
return true;
}
if (dbNameFromAnalyzer.empty()) { // empty name with :: means always system
if (forGetters) {
return true; // system database is always accessible for reading analyzer from any other db
}
return currentDbName == arangodb::StaticStrings::SystemDatabase;
}
return currentDbName == dbNameFromAnalyzer ||
(forGetters && dbNameFromAnalyzer == arangodb::StaticStrings::SystemDatabase);
}
/*static*/ std::pair<irs::string_ref, irs::string_ref> IResearchAnalyzerFeature::splitAnalyzerName(
irs::string_ref const& analyzer) noexcept {
// search for vocbase prefix ending with '::'
for (size_t i = 1, count = analyzer.size(); i < count; ++i) {
if (analyzer[i] == ANALYZER_PREFIX_DELIM // current is delim
&& analyzer[i - 1] == ANALYZER_PREFIX_DELIM) { // previous is also delim
auto vocbase = i > 1 // non-empty prefix, +1 for first delimiter char
? irs::string_ref(analyzer.c_str(), i - 1) // -1 for the first ':' delimiter
: irs::string_ref::EMPTY;
auto name = i < count - 1 // have suffix
? irs::string_ref(analyzer.c_str() + i + 1, count - i - 1) // +-1 for the suffix after '::'
: irs::string_ref::EMPTY; // do not point after end of buffer
return std::make_pair(vocbase, name); // prefixed analyzer name
}
}
return std::make_pair(irs::string_ref::NIL, analyzer); // unprefixed analyzer name
}
/*static*/ std::string IResearchAnalyzerFeature::normalize( // normalize name
irs::string_ref const& name, // analyzer name
TRI_vocbase_t const& activeVocbase, // fallback vocbase if not part of name
@ -2055,7 +2093,6 @@ arangodb::Result IResearchAnalyzerFeature::remove( // remove analyzer
// will make the state consistent again
if (!pool) {
_analyzers.erase(itr);
return arangodb::Result( // result
TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND, // code
std::string("failure to find a valid analyzer while removing arangosearch analyzer '") + std::string(name)+ "'"
@ -2195,7 +2232,18 @@ void IResearchAnalyzerFeature::start() {
if (!isEnabled()) {
return;
}
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
// sanity check: we rely on this condition is true internally
{
auto sysDbFeature =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::SystemDatabaseFeature>(
"SystemDatabase");
if (sysDbFeature && sysDbFeature->use()) { // feature/db may be absent in some unit-test enviroment
TRI_ASSERT(sysDbFeature->use()->name() == arangodb::StaticStrings::SystemDatabase);
}
}
#endif
// register analyzer functions
{
auto* functions =

View File

@ -200,6 +200,36 @@ class IResearchAnalyzerFeature final : public arangodb::application_features::Ap
bool expandVocbasePrefix = true // use full vocbase name as prefix for active/system v.s. EMPTY/'::'
);
//////////////////////////////////////////////////////////////////////////////
/// @param name analyzer name (normalized)
/// @return vocbase prefix extracted from normalized analyzer name
/// EMPTY == system vocbase
/// NIL == analyzer name have had no db name prefix
/// @see analyzerReachableFromDb
//////////////////////////////////////////////////////////////////////////////
static irs::string_ref extractVocbaseName(irs::string_ref const& name) noexcept;
//////////////////////////////////////////////////////////////////////////////
/// Checks if analyzer db (identified by db name prefix extracted from analyzer
/// name) could be reached from specified db.
/// Properly handles special cases (e.g. NIL and EMPTY)
/// @param dbNameFromAnalyzer database name extracted from analyzer name
/// @param currentDbName database name to check against (should not be empty!)
/// @param forGetters check special case for getting analyzer (not creating/removing)
/// @return true if analyzer is reachable
static bool analyzerReachableFromDb(irs::string_ref const& dbNameFromAnalyzer,
irs::string_ref const& currentDbName,
bool forGetters = false) noexcept;
////////////////////////////////////////////////////////////////////////////////
/// @brief split the analyzer name into the vocbase part and analyzer part
/// @param name analyzer name
/// @return pair of first == vocbase name, second == analyzer name
/// EMPTY == system vocbase
/// NIL == unprefixed analyzer name, i.e. active vocbase
////////////////////////////////////////////////////////////////////////////////
static std::pair<irs::string_ref, irs::string_ref> splitAnalyzerName(irs::string_ref const& analyzer) noexcept;
void prepare() override;
//////////////////////////////////////////////////////////////////////////////

View File

@ -776,14 +776,33 @@ namespace iresearch {
IResearchLinkMeta meta;
std::string errorField;
if (!linkDefinition.isNull() // have link definition
&& !meta.init(linkDefinition, false, errorField, &vocbase)) { // for db-server analyzer validation should have already applied on coordinator
return arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
errorField.empty()
? (std::string("while validating arangosearch link definition, error: invalid link definition for collection '") + collectionName.copyString() + "': " + linkDefinition.toString())
: (std::string("while validating arangosearch link definition, error: invalid link definition for collection '") + collectionName.copyString() + "' error in attribute: " + errorField)
);
if (!linkDefinition.isNull()) { // have link definition
if (!meta.init(linkDefinition, false, errorField, &vocbase)) { // for db-server analyzer validation should have already applied on coordinator
return arangodb::Result( // result
TRI_ERROR_BAD_PARAMETER, // code
errorField.empty()
? (std::string("while validating arangosearch link definition, error: invalid link definition for collection '") + collectionName.copyString() + "': " + linkDefinition.toString())
: (std::string("while validating arangosearch link definition, error: invalid link definition for collection '") + collectionName.copyString() + "' error in attribute: " + errorField)
);
}
// validate analyzers origin
// analyzer should be either from same database as view (and collection) or from system database
{
const auto& currentVocbase = vocbase.name();
for (const auto& analyzer : meta._analyzers) {
TRI_ASSERT(analyzer._pool); // should be checked in meta init
if (ADB_UNLIKELY(!analyzer._pool)) {
continue;
}
auto analyzerVocbase = IResearchAnalyzerFeature::extractVocbaseName(analyzer._pool->name());
if (!IResearchAnalyzerFeature::analyzerReachableFromDb(analyzerVocbase, currentVocbase, true)) {
return arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("Analyzer '").append(analyzer._pool->name())
.append("' is not accessible from database '").append(currentVocbase).append("'"));
}
}
}
}
}

View File

@ -28,14 +28,12 @@
#include "analysis/token_attributes.hpp"
#include "utils/hash_utils.hpp"
#include "utils/locale_utils.hpp"
#include "Basics/StringUtils.h"
#include "Cluster/ServerState.h"
#include "RestServer/SystemDatabaseFeature.h"
#include "VelocyPackHelper.h"
#include "velocypack/Builder.h"
#include "velocypack/Iterator.h"
#include "IResearchLinkMeta.h"
#include "Misc.h"
@ -318,7 +316,6 @@ bool IResearchLinkMeta::init( // initialize meta
}
}
}
// get analyzer potentially creating it (e.g. on cluster)
// @note do not use emplace(...) since it'll trigger loadAnalyzers(...)
if (!analyzers->get(name, type, properties, features)) {

View File

@ -35,23 +35,21 @@
namespace arangodb {
namespace iresearch {
RestAnalyzerHandler::RestAnalyzerHandler( // constructor
arangodb::GeneralRequest* request, // request
arangodb::GeneralResponse* response // response
RestAnalyzerHandler::RestAnalyzerHandler(
arangodb::GeneralRequest* request,
arangodb::GeneralResponse* response
): RestVocbaseBaseHandler(request, response) {
}
void RestAnalyzerHandler::createAnalyzer( // create
IResearchAnalyzerFeature& analyzers, // analyzer feature
TRI_vocbase_t const* sysVocbase // system vocbase
IResearchAnalyzerFeature& analyzers
) {
TRI_ASSERT(_request); // ensured by execute()
if (_request->payload().isEmptyObject()) {
generateError( // generate error
arangodb::rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON // args
generateError(
arangodb::rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON
);
return;
}
@ -63,8 +61,8 @@ void RestAnalyzerHandler::createAnalyzer( // create
}
if (!body.isObject()) {
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
"expecting body to be of the form { name: <string>, type: <string>[, properties: <object|string>[, features: <string-array>]] }"
));
@ -75,26 +73,35 @@ void RestAnalyzerHandler::createAnalyzer( // create
auto nameSlice = body.get(StaticStrings::AnalyzerNameField);
if (!nameSlice.isString()) {
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
"invalid 'name', expecting body to be of the form { name: <string>, type: <string>[, properties: <object|string>[, features: <string-array>]] }"
));
return;
}
auto splittedAnalyzerName = IResearchAnalyzerFeature::splitAnalyzerName(getStringRef(nameSlice));
if (!IResearchAnalyzerFeature::analyzerReachableFromDb(splittedAnalyzerName.first,
_vocbase.name())) {
generateError(arangodb::Result(
TRI_ERROR_FORBIDDEN,
"Database in analyzer name does not match current database"));
return;
}
name = getStringRef(nameSlice);
name = splittedAnalyzerName.second;
if (!TRI_vocbase_t::IsAllowedName(false, velocypack::StringRef(name.c_str(), name.size()))) {
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
"invalid characters in analyzer name '" + static_cast<std::string>(name) + "'"
));
return;
}
std::string nameBuf;
auto* sysDatabase =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::SystemDatabaseFeature>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
if (sysVocbase) {
nameBuf = IResearchAnalyzerFeature::normalize(name, _vocbase, *sysVocbase); // normalize
name = nameBuf;
@ -104,8 +111,8 @@ void RestAnalyzerHandler::createAnalyzer( // create
auto typeSlice = body.get(StaticStrings::AnalyzerTypeField);
if (!typeSlice.isString()) {
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
"invalid 'type', expecting body to be of the form { name: <string>, type: <string>[, properties: <object|string>[, features: <string-array>]] }"
));
@ -123,8 +130,8 @@ void RestAnalyzerHandler::createAnalyzer( // create
}
if (!properties.isNone() && !properties.isObject()) { // optional parameter
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
"invalid 'properties', expecting body to be of the form { name: <string>, type: <string>[, properties: <object>[, features: <string-array>]] }"
));
return;
@ -143,8 +150,8 @@ void RestAnalyzerHandler::createAnalyzer( // create
auto value = *itr;
if (!value.isString()) {
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
"invalid value in 'features', expecting body to be of the form { name: <string>, type: <string>[, properties: <object>[, features: <string-array>]] }"
));
@ -154,8 +161,8 @@ void RestAnalyzerHandler::createAnalyzer( // create
auto* feature = irs::attribute::type_id::get(getStringRef(value));
if (!feature) {
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
"unknown value in 'features', expecting body to be of the form { name: <string>, type: <string>[, properties: <object>[, features: <string-array>]] }"
));
@ -165,8 +172,8 @@ void RestAnalyzerHandler::createAnalyzer( // create
features.add(*feature);
}
} else {
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
"invalid 'features', expecting body to be of the form { name: <string>, type: <string>[, properties: <object>[, features: <string-array>]] }"
));
@ -179,12 +186,11 @@ void RestAnalyzerHandler::createAnalyzer( // create
// ...........................................................................
if (!IResearchAnalyzerFeature::canUse(name, auth::Level::RW)) {
generateError( // generate error
arangodb::rest::ResponseCode::FORBIDDEN, // HTTP code
TRI_ERROR_FORBIDDEN, // code
std::string("insufficient rights while creating analyzer: ") + body.toString() // message
generateError(
arangodb::rest::ResponseCode::FORBIDDEN,
TRI_ERROR_FORBIDDEN,
std::string("insufficient rights while creating analyzer: ") + body.toString()
);
return;
}
@ -198,12 +204,11 @@ void RestAnalyzerHandler::createAnalyzer( // create
}
if (!result.first) {
generateError( // generate error
arangodb::rest::ResponseCode::BAD, // HTTP code
TRI_errno(), // code
std::string("failure while creating analyzer: ") + body.toString() // message
generateError(
arangodb::rest::ResponseCode::BAD,
TRI_errno(),
std::string("failure while creating analyzer: ") + body.toString()
);
return;
}
@ -212,7 +217,7 @@ void RestAnalyzerHandler::createAnalyzer( // create
pool->toVelocyPack(builder);
generateResult( // generate result
generateResult(
result.second // new analyzer v.s. existing analyzer
? arangodb::rest::ResponseCode::CREATED : arangodb::rest::ResponseCode::OK,
builder.slice() // analyzer definition
@ -220,146 +225,116 @@ void RestAnalyzerHandler::createAnalyzer( // create
}
arangodb::RestStatus RestAnalyzerHandler::execute() {
auto* analyzers = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
IResearchAnalyzerFeature // feature type
>();
if (!analyzers) {
generateError( // generate error
arangodb::rest::ResponseCode::BAD, // HTTP code
TRI_ERROR_INTERNAL, // code
std::string("failure to find feature '") + IResearchAnalyzerFeature::name() + "' while executing REST request for: " + ANALYZER_PATH
);
return arangodb::RestStatus::DONE;
}
auto* sysDatabase = arangodb::application_features::ApplicationServer::lookupFeature< // find feature
arangodb::SystemDatabaseFeature // featue type
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
if (!_request) {
generateError( // generate error
arangodb::rest::ResponseCode::METHOD_NOT_ALLOWED, // HTTP code
TRI_ERROR_HTTP_BAD_PARAMETER // error code
generateError(
arangodb::rest::ResponseCode::METHOD_NOT_ALLOWED,
TRI_ERROR_HTTP_BAD_PARAMETER
);
return arangodb::RestStatus::DONE;
}
auto* analyzers =
arangodb::application_features::ApplicationServer::getFeature<IResearchAnalyzerFeature>();
auto& suffixes = _request->suffixes();
switch (_request->requestType()) {
case arangodb::rest::RequestType::DELETE_REQ:
if (suffixes.size() == 1) {
auto name = arangodb::basics::StringUtils::urlDecode(suffixes[0]);
if (!sysVocbase) {
removeAnalyzer(*analyzers, name); // verbatim (assume already normalized)
return arangodb::RestStatus::DONE;
}
removeAnalyzer( // remove
*analyzers, // feature
IResearchAnalyzerFeature::normalize(name, _vocbase, *sysVocbase) // normalize
);
auto force = _request->parsedValue("force", false);
removeAnalyzer(*analyzers, name, force);
return arangodb::RestStatus::DONE;
}
generateError( // generate error
arangodb::rest::ResponseCode::BAD, // HTTP code
TRI_ERROR_BAD_PARAMETER, // code
std::string("expecting DELETE ") + ANALYZER_PATH + "/<analyzer-name>[?force=true]" // mesage
generateError(
arangodb::rest::ResponseCode::BAD,
TRI_ERROR_BAD_PARAMETER,
std::string("expecting DELETE ").append(ANALYZER_PATH).append("/<analyzer-name>[?force=true]")
);
return arangodb::RestStatus::DONE;
case arangodb::rest::RequestType::GET:
if (suffixes.empty()) {
getAnalyzers(*analyzers);
return arangodb::RestStatus::DONE;
}
if (suffixes.size() == 1) {
auto name = arangodb::basics::StringUtils::urlDecode(suffixes[0]);
if (!sysVocbase) {
getAnalyzer(*analyzers, name);
return arangodb::RestStatus::DONE;
}
getAnalyzer( // get
*analyzers, // feature
IResearchAnalyzerFeature::normalize(name, _vocbase, *sysVocbase) // normalize
);
getAnalyzer(*analyzers, name);
return arangodb::RestStatus::DONE;
}
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
std::string("expecting GET ") + ANALYZER_PATH + "[/<analyzer-name>]" // mesage
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("expecting GET ").append(ANALYZER_PATH).append("[/<analyzer-name>]")
));
return arangodb::RestStatus::DONE;
case arangodb::rest::RequestType::POST:
if (suffixes.empty()) {
createAnalyzer(*analyzers, sysVocbase.get());
createAnalyzer(*analyzers);
return arangodb::RestStatus::DONE;
}
generateError(arangodb::Result( // generate error
TRI_ERROR_BAD_PARAMETER, // code
std::string("expecting POST ") + ANALYZER_PATH // mesage
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("expecting POST ").append(ANALYZER_PATH)
));
return arangodb::RestStatus::DONE;
default:
generateError(arangodb::Result( // generate error
TRI_ERROR_HTTP_METHOD_NOT_ALLOWED // error code
generateError(arangodb::Result(
TRI_ERROR_HTTP_METHOD_NOT_ALLOWED
));
}
return arangodb::RestStatus::DONE;
}
void RestAnalyzerHandler::getAnalyzer( // get analyzer
IResearchAnalyzerFeature& analyzers, // analyzer feature
std::string const& name // analyzer name (normalized)
void RestAnalyzerHandler::getAnalyzer(
IResearchAnalyzerFeature& analyzers,
std::string const& requestedName
) {
TRI_ASSERT(_request); // ensured by execute()
auto* sysDatabase =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::SystemDatabaseFeature>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
auto normalizedName = sysVocbase ?
IResearchAnalyzerFeature::normalize(requestedName, _vocbase, *sysVocbase) : requestedName;
// ...........................................................................
// end of parameter parsing
// ...........................................................................
if (!IResearchAnalyzerFeature::canUse(name, auth::Level::RO)) {
generateError(arangodb::Result( // generate error
TRI_ERROR_FORBIDDEN, // code
std::string("insufficient rights while getting analyzer: ") + name // message
// need to check if analyzer is from current database or from system database
const auto analyzerVocbase = IResearchAnalyzerFeature::extractVocbaseName(normalizedName);
if (!IResearchAnalyzerFeature::analyzerReachableFromDb(analyzerVocbase, _vocbase.name(), true)) {
std::string errorMessage("Analyzer '");
errorMessage.append(normalizedName)
.append("' is not accessible. Only analyzers from current database ('")
.append(_vocbase.name())
.append("')");
if (_vocbase.name() != arangodb::StaticStrings::SystemDatabase) {
errorMessage.append(" or system database");
}
errorMessage.append(" are available");
generateError(arangodb::Result(
TRI_ERROR_FORBIDDEN,
errorMessage
));
return;
}
auto pool = analyzers.get(name);
if (!pool) {
generateError(arangodb::Result( // generate error
TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND, // code
std::string("unable to find analyzer: ") + name // mesage
if (!IResearchAnalyzerFeature::canUse(normalizedName, auth::Level::RO)) {
generateError(arangodb::Result(
TRI_ERROR_FORBIDDEN,
std::string("insufficient rights while getting analyzer: ").append(normalizedName)
));
return;
}
auto pool = analyzers.get(normalizedName);
if (!pool) {
generateError(arangodb::Result(
TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND,
std::string("unable to find analyzer: ").append(normalizedName)
));
return;
}
arangodb::velocypack::Builder builder;
pool->toVelocyPack(builder);
// generate result + 'error' field + 'code' field
@ -415,39 +390,51 @@ void RestAnalyzerHandler::getAnalyzers( // get all analyzers
generateOk(arangodb::rest::ResponseCode::OK, builder.slice());
}
void RestAnalyzerHandler::removeAnalyzer( // remove
IResearchAnalyzerFeature& analyzers, // analyzer feature
std::string const& name // analyzer name (normalized)
void RestAnalyzerHandler::removeAnalyzer(
IResearchAnalyzerFeature& analyzers,
std::string const& requestedName,
bool force
) {
TRI_ASSERT(_request); // ensured by execute()
auto splittedAnalyzerName = IResearchAnalyzerFeature::splitAnalyzerName(requestedName);
auto name = splittedAnalyzerName.second;
auto force = _request->parsedValue("force", false);
// ...........................................................................
// end of parameter parsing
// ...........................................................................
if (!IResearchAnalyzerFeature::canUse(name, auth::Level::RW)) {
generateError(arangodb::Result( // generate error
TRI_ERROR_FORBIDDEN, // code
std::string("insufficient rights while removing analyzer: ") + name // message
if (!TRI_vocbase_t::IsAllowedName(false, velocypack::StringRef(name.c_str(), name.size()))) {
generateError(arangodb::Result(
TRI_ERROR_BAD_PARAMETER,
std::string("Invalid characters in analyzer name '").append(name)
.append("'.")
));
return;
}
auto res = analyzers.remove(name, force);
if (!IResearchAnalyzerFeature::analyzerReachableFromDb(splittedAnalyzerName.first,
_vocbase.name())) {
generateError(arangodb::Result(
TRI_ERROR_FORBIDDEN,
"Database in analyzer name does not match current database"));
return;
}
auto* sysDatabase =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::SystemDatabaseFeature>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
auto normalizedName = sysVocbase ?
IResearchAnalyzerFeature::normalize(name, _vocbase, *sysVocbase) : std::string(name);
if (!IResearchAnalyzerFeature::canUse(normalizedName, auth::Level::RW)) {
generateError(arangodb::Result(
TRI_ERROR_FORBIDDEN,
std::string("insufficient rights while removing analyzer: ").append(normalizedName)
));
return;
}
auto res = analyzers.remove(normalizedName, force);
if (!res.ok()) {
generateError(res);
return;
}
arangodb::velocypack::Builder builder;
builder.openObject();
builder.add(StaticStrings::AnalyzerNameField, arangodb::velocypack::Value(name));
builder.add(StaticStrings::AnalyzerNameField, arangodb::velocypack::Value(normalizedName));
builder.close();
// generate result + 'error' field + 'code' field

View File

@ -49,22 +49,20 @@ class RestAnalyzerHandler: public RestVocbaseBaseHandler {
virtual char const* name() const override { return "RestAnalyzerHandler"; }
private:
void createAnalyzer(
IResearchAnalyzerFeature& analyzers, // analyzer feature
TRI_vocbase_t const* sysVocbase // system vocbase
);
void createAnalyzer(IResearchAnalyzerFeature& analyzers);
void getAnalyzer(
IResearchAnalyzerFeature& analyzers, // analyzer feature
std::string const& name // analyzer name (normalized)
IResearchAnalyzerFeature& analyzers,
std::string const& requestedName
);
void getAnalyzers(IResearchAnalyzerFeature& analyzers);
void removeAnalyzer(
IResearchAnalyzerFeature& analyzers, // analyzer feature
std::string const& name // analyzer name (normalized)
IResearchAnalyzerFeature& analyzers,
std::string const& requestedName,
bool force
);
};
} // iresearch
} // arangodb
#endif
#endif

View File

@ -23,6 +23,7 @@
#include "v8-analyzers.h"
#include "Basics/StringUtils.h"
#include "Basics/StaticStrings.h"
#include "IResearch/IResearchAnalyzerFeature.h"
#include "IResearch/VelocyPackHelper.h"
#include "RestServer/SystemDatabaseFeature.h"
@ -279,12 +280,22 @@ void JS_Create(v8::FunctionCallbackInfo<v8::Value> const& args) {
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
auto name = TRI_ObjectToString(isolate, args[0]);
auto nameFromArgs = TRI_ObjectToString(isolate, args[0]);
auto splittedAnalyzerName =
arangodb::iresearch::IResearchAnalyzerFeature::splitAnalyzerName(nameFromArgs);
if (!arangodb::iresearch::IResearchAnalyzerFeature::analyzerReachableFromDb(
splittedAnalyzerName.first, vocbase.name())) {
TRI_V8_THROW_EXCEPTION_MESSAGE(
TRI_ERROR_FORBIDDEN,
"Database in analyzer name does not match current database");
return;
}
auto name = splittedAnalyzerName.second;
if (!TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef(name))) {
TRI_V8_THROW_EXCEPTION_MESSAGE(
TRI_ERROR_BAD_PARAMETER,
"invalid characters in analyzer name '" + name + "'"
std::string("invalid characters in analyzer name '").append(name).append("'")
);
return;
@ -443,6 +454,24 @@ void JS_Get(v8::FunctionCallbackInfo<v8::Value> const& args) {
// end of parameter parsing
// ...........................................................................
const auto analyzerVocbase = arangodb::iresearch::IResearchAnalyzerFeature::extractVocbaseName(name);
if (!arangodb::iresearch::IResearchAnalyzerFeature::analyzerReachableFromDb(
analyzerVocbase, vocbase.name(), true)) {
std::string errorMessage("Analyzer '");
errorMessage.append(name)
.append("' is not accessible. Only analyzers from current database ('")
.append(vocbase.name())
.append("')");
if (vocbase.name() != arangodb::StaticStrings::SystemDatabase) {
errorMessage.append(" or system database");
}
errorMessage.append(" are available");
TRI_V8_THROW_EXCEPTION_MESSAGE(
TRI_ERROR_FORBIDDEN, // code
errorMessage
);
}
if (!arangodb::iresearch::IResearchAnalyzerFeature::canUse(name, arangodb::auth::Level::RO)) {
TRI_V8_THROW_EXCEPTION_MESSAGE( // exception
TRI_ERROR_FORBIDDEN, // code
@ -500,12 +529,6 @@ void JS_List(v8::FunctionCallbackInfo<v8::Value> const& args) {
// ...........................................................................
typedef arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool::ptr AnalyzerPoolPtr;
static const auto nameLess = []( // analyzer name less
AnalyzerPoolPtr const& lhs, // left hand side
AnalyzerPoolPtr const& rhs // right hand side
)->bool {
return arangodb::basics::StringUtils::tolower(lhs->name()) < arangodb::basics::StringUtils::tolower(rhs->name());
};
std::vector<AnalyzerPoolPtr> result;
auto visitor = [&result](AnalyzerPoolPtr const& analyzer)->bool {
if (analyzer) {
@ -529,8 +552,6 @@ void JS_List(v8::FunctionCallbackInfo<v8::Value> const& args) {
analyzers->visit(visitor, sysVocbase.get());
}
std::sort(result.begin(), result.end(), nameLess);
auto v8Result = v8::Array::New(isolate);
for (size_t i = 0, count = result.size(); i < count; ++i) {
@ -586,9 +607,27 @@ void JS_Remove(v8::FunctionCallbackInfo<v8::Value> const& args) {
>();
auto sysVocbase = sysDatabase ? sysDatabase->use() : nullptr;
auto name = TRI_ObjectToString(isolate, args[0]);
std::string nameBuf;
auto nameFromArgs = TRI_ObjectToString(isolate, args[0]);
auto splittedAnalyzerName =
arangodb::iresearch::IResearchAnalyzerFeature::splitAnalyzerName(nameFromArgs);
if (!arangodb::iresearch::IResearchAnalyzerFeature::analyzerReachableFromDb(
splittedAnalyzerName.first, vocbase.name())) {
TRI_V8_THROW_EXCEPTION_MESSAGE(
TRI_ERROR_FORBIDDEN,
"Database in analyzer name does not match current database");
return;
}
auto name = splittedAnalyzerName.second;
if (!TRI_vocbase_t::IsAllowedName(false, arangodb::velocypack::StringRef(name))) {
TRI_V8_THROW_EXCEPTION_MESSAGE(
TRI_ERROR_BAD_PARAMETER,
std::string( "Invalid characters in analyzer name '").append(name)
.append("'.")
);
}
std::string nameBuf;
if (sysVocbase) {
nameBuf = arangodb::iresearch::IResearchAnalyzerFeature::normalize( // normalize
name, vocbase, *sysVocbase // args

View File

@ -438,12 +438,6 @@ static void JS_ViewsVocbase(v8::FunctionCallbackInfo<v8::Value> const& args) {
return true;
});
std::sort(views.begin(), views.end(),
[](std::shared_ptr<LogicalView> const& lhs,
std::shared_ptr<LogicalView> const& rhs) -> bool {
return StringUtils::tolower(lhs->name()) <
StringUtils::tolower(rhs->name());
});
bool error = false;
// already create an array of the correct size

View File

@ -122,6 +122,65 @@ struct TestTermAttribute : public irs::term_attribute {
void value(irs::bytes_ref const& value) { value_ = value; }
};
class ReNormalizingAnalyzer : public irs::analysis::analyzer {
public:
DECLARE_ANALYZER_TYPE();
ReNormalizingAnalyzer() : irs::analysis::analyzer(ReNormalizingAnalyzer::type()) {
_attrs.emplace(_attr);
}
virtual irs::attribute_view const& attributes() const NOEXCEPT override {
return _attrs;
}
static ptr make(irs::string_ref const& args) {
auto slice = arangodb::iresearch::slice(args);
if (slice.isNull()) throw std::exception();
if (slice.isNone()) return nullptr;
PTR_NAMED(ReNormalizingAnalyzer, ptr);
return ptr;
}
// test implementation
// string will be normalized as is. But object will be converted!
// need this to test comparsion "old-normalized" against "new-normalized"
static bool normalize(irs::string_ref const& args, std::string& definition) {
auto slice = arangodb::iresearch::slice(args);
arangodb::velocypack::Builder builder;
if (slice.isString()) {
VPackObjectBuilder scope(&builder);
arangodb::iresearch::addStringRef(builder, "args",
arangodb::iresearch::getStringRef(slice));
} else if (slice.isObject() && slice.hasKey("args") && slice.get("args").isString()) {
VPackObjectBuilder scope(&builder);
auto inputDef = arangodb::iresearch::getStringRef(slice.get("args"));
arangodb::iresearch::addStringRef(builder, "args",
inputDef == "123" ? "321" : inputDef);
} else {
return false;
}
definition = builder.buffer()->toString();
return true;
}
virtual bool next() override {
return false;
}
virtual bool reset(irs::string_ref const& data) override {
return false;
}
private:
irs::attribute_view _attrs;
TestAttribute _attr;
};
DEFINE_ANALYZER_TYPE_NAMED(ReNormalizingAnalyzer, "ReNormalizingAnalyzer");
REGISTER_ANALYZER_VPACK(ReNormalizingAnalyzer, ReNormalizingAnalyzer::make, ReNormalizingAnalyzer::normalize);
class TestAnalyzer : public irs::analysis::analyzer {
public:
DECLARE_ANALYZER_TYPE();
@ -862,6 +921,31 @@ TEST_F(IResearchAnalyzerFeatureTest, test_get_feature_mismatch) {
ASSERT_EQ(read, nullptr);
}
TEST_F(IResearchAnalyzerFeatureTest, test_renormalize_for_equal) {
arangodb::iresearch::IResearchAnalyzerFeature feature(server);
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
EXPECT_TRUE(feature.emplace(result, analyzerName(), "ReNormalizingAnalyzer",
VPackParser::fromJson("\"123\"")->slice()) //123 will be stored as is (old-normalized)
.ok());
EXPECT_NE(result.first, nullptr);
}
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
EXPECT_TRUE(feature.emplace(result, analyzerName(), "ReNormalizingAnalyzer",
VPackParser::fromJson("{ \"args\":\"123\"}")->slice()) //123 will be normalized to 321
.ok());
EXPECT_NE(result.first, nullptr);
}
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
EXPECT_FALSE(feature.emplace(result, analyzerName(), "ReNormalizingAnalyzer",
VPackParser::fromJson("{ \"args\":\"1231\"}")->slice()) //Re-normalization should not help
.ok());
EXPECT_EQ(result.first, nullptr);
}
}
// -----------------------------------------------------------------------------
// --SECTION-- get test suite
// -----------------------------------------------------------------------------

View File

@ -84,7 +84,8 @@ class IResearchLinkHelperTest : public ::testing::Test {
features.emplace_back(new arangodb::AqlFeature(server),
true); // required for UserManager::loadFromDB()
features.emplace_back(new arangodb::AuthenticationFeature(server), false); // required for authentication tests
features.emplace_back(new arangodb::DatabaseFeature(server), false);
auto dbFeature = new arangodb::DatabaseFeature(server);
features.emplace_back(dbFeature, false);
features.emplace_back(new arangodb::DatabasePathFeature(server), false); // required for IResearchLink::init(...)
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // required for constructing TRI_vocbase_t
features.emplace_back(new arangodb::SystemDatabaseFeature(server), true); // required by IResearchAnalyzerFeature::storeAnalyzer(...)
@ -117,10 +118,7 @@ class IResearchLinkHelperTest : public ::testing::Test {
auto const databases = arangodb::velocypack::Parser::fromJson(
std::string("[ { \"name\": \"") +
arangodb::StaticStrings::SystemDatabase + "\" } ]");
auto* dbFeature =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::DatabaseFeature>(
"Database");
arangodb::StaticStrings::SystemDatabase + "\" }]");
dbFeature->loadDatabases(databases->slice());
for (auto& f : features) {
@ -134,6 +132,29 @@ class IResearchLinkHelperTest : public ::testing::Test {
"DatabasePath");
arangodb::tests::setDatabasePath(*dbPathFeature); // ensure test data is stored in a unique directory
testFilesystemPath = dbPathFeature->directory();
{
auto vocbase = dbFeature->useDatabase(arangodb::StaticStrings::SystemDatabase);
arangodb::methods::Collections::createSystem(
*vocbase,
arangodb::tests::AnalyzerCollectionName);
}
{
TRI_vocbase_t* vocbase;
dbFeature->createDatabase(1, "testVocbaseWithAnalyzer", vocbase);
arangodb::methods::Collections::createSystem(
*vocbase,
arangodb::tests::AnalyzerCollectionName);
}
{
TRI_vocbase_t* vocbase;
dbFeature->createDatabase(2, "testVocbaseWithView", vocbase);
arangodb::methods::Collections::createSystem(
*vocbase,
arangodb::tests::AnalyzerCollectionName);
auto collectionJson = arangodb::velocypack::Parser::fromJson(
"{ \"id\":102, \"name\": \"foo\" }");
EXPECT_NE(nullptr, vocbase->createCollection(collectionJson->slice()));
}
long systemError;
std::string systemErrorStr;
@ -274,6 +295,37 @@ TEST_F(IResearchLinkHelperTest, test_equals) {
}
}
TEST_F(IResearchLinkHelperTest, test_validate_cross_db_analyzer) {
auto* analyzers =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
ASSERT_NE(nullptr, analyzers);
auto* dbFeature =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::DatabaseFeature>("Database");
ASSERT_NE(nullptr, dbFeature);
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult emplaceResult;
ASSERT_TRUE(analyzers->emplace(emplaceResult, "testVocbaseWithAnalyzer::myIdentity", "identity",
VPackParser::fromJson("{ }")->slice()).ok());
}
// existing analyzer but wrong database
{
auto vocbaseLocal = dbFeature->useDatabase("testVocbaseWithView");
ASSERT_NE(nullptr, vocbaseLocal);
auto json = VPackParser::fromJson(
"{ \"foo\": "
" { "
" \"analyzers\": [ \"testVocbaseWithAnalyzer::myIdentity\" ] "
" } "
" }");
auto validateResult = arangodb::iresearch::IResearchLinkHelper::validateLinks(
*vocbaseLocal, json->slice());
EXPECT_FALSE(validateResult.ok());
EXPECT_EQ(TRI_ERROR_BAD_PARAMETER, validateResult.errorNumber());
}
}
TEST_F(IResearchLinkHelperTest, test_normalize) {
auto* analyzers =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
@ -282,17 +334,6 @@ TEST_F(IResearchLinkHelperTest, test_normalize) {
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::SystemDatabaseFeature>();
auto sysVocbase = sysDatabase->use();
// create analyzer collection
{
if (!sysVocbase->lookupCollection(arangodb::tests::AnalyzerCollectionName)) {
auto collectionJson = arangodb::velocypack::Parser::fromJson(
std::string("{ \"name\": \"") + arangodb::tests::AnalyzerCollectionName +
"\", \"isSystem\": true }");
auto logicalCollection = sysVocbase->createCollection(collectionJson->slice());
ASSERT_TRUE((false == !logicalCollection));
}
}
// analyzer single-server
{
auto json = arangodb::velocypack::Parser::fromJson(

View File

@ -183,11 +183,13 @@ class IResearchLinkMetaTest : public ::testing::Test {
*sysvocbase,
arangodb::tests::AnalyzerCollectionName);
TRI_vocbase_t* vocbase;
dbFeature->createDatabase(1, "testVocbase", vocbase); // required for IResearchAnalyzerFeature::emplace(...)
arangodb::methods::Collections::createSystem(
*vocbase,
arangodb::tests::AnalyzerCollectionName);
*vocbase,
arangodb::tests::AnalyzerCollectionName);
auto* analyzers =
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::iresearch::IResearchAnalyzerFeature>();
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;

View File

@ -106,7 +106,9 @@ class RestAnalyzerHandlerTest : public ::testing::Test {
// setup required application features
features.emplace_back(new arangodb::AuthenticationFeature(server), false); // required for VocbaseContext
features.emplace_back(new arangodb::DatabaseFeature(server), false);
features.emplace_back(new arangodb::SystemDatabaseFeature(server),
true); // required for IResearchAnalyzerFeature
#if USE_ENTERPRISE
features.emplace_back(new arangodb::LdapFeature(server),
false); // required for AuthenticationFeature with USE_ENTERPRISE
@ -329,19 +331,19 @@ TEST_F(RestAnalyzerHandlerTest, test_create) {
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
EXPECT_TRUE((arangodb::rest::ResponseCode::BAD == responce.responseCode()));
EXPECT_TRUE((arangodb::rest::ResponseCode::FORBIDDEN == responce.responseCode()));
auto slice = responce._payload.slice();
EXPECT_TRUE((slice.isObject()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) &&
slice.get(arangodb::StaticStrings::Code).isNumber<size_t>() &&
size_t(arangodb::rest::ResponseCode::BAD) ==
size_t(arangodb::rest::ResponseCode::FORBIDDEN) ==
slice.get(arangodb::StaticStrings::Code).getNumber<size_t>()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) &&
slice.get(arangodb::StaticStrings::Error).isBoolean() &&
true == slice.get(arangodb::StaticStrings::Error).getBoolean()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) &&
slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() &&
TRI_ERROR_BAD_PARAMETER ==
TRI_ERROR_FORBIDDEN ==
slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
}
@ -357,9 +359,9 @@ TEST_F(RestAnalyzerHandlerTest, test_create) {
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::POST);
request._payload.openObject();
request._payload.add("name", VPackValue(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer"));
request._payload.add("name", VPackValue(arangodb::StaticStrings::SystemDatabase + "::testAn:alyzer"));
request._payload.add("type", VPackValue("identity"));
request._payload.add("properties", VPackValue(arangodb::velocypack::ValueType::Null));
request._payload.add("properties", arangodb::velocypack::Value("{\"args\":\"abc\"}"));
request._payload.close();
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
@ -388,7 +390,7 @@ TEST_F(RestAnalyzerHandlerTest, test_create) {
slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
}
// invalid params (name contains invalid symbols)
// name contains :: as link to system database (this is now ok)
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
@ -400,9 +402,9 @@ TEST_F(RestAnalyzerHandlerTest, test_create) {
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::POST);
request._payload.openObject();
request._payload.add("name", VPackValue("::testAnalyzer"));
request._payload.add("name", VPackValue("::testAnalyzerImplicitDbName"));
request._payload.add("type", VPackValue("identity"));
request._payload.add("properties", VPackValue(arangodb::velocypack::ValueType::Null));
request._payload.add("properties", arangodb::velocypack::Value("{\"args\":\"abc\"}"));
request._payload.close();
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
@ -415,22 +417,44 @@ TEST_F(RestAnalyzerHandlerTest, test_create) {
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
EXPECT_TRUE((arangodb::rest::ResponseCode::BAD == responce.responseCode()));
auto slice = responce._payload.slice();
EXPECT_TRUE((slice.isObject()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) &&
slice.get(arangodb::StaticStrings::Code).isNumber<size_t>() &&
size_t(arangodb::rest::ResponseCode::BAD) ==
slice.get(arangodb::StaticStrings::Code).getNumber<size_t>()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Error) &&
slice.get(arangodb::StaticStrings::Error).isBoolean() &&
true == slice.get(arangodb::StaticStrings::Error).getBoolean()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) &&
slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() &&
TRI_ERROR_BAD_PARAMETER ==
slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
EXPECT_TRUE((arangodb::rest::ResponseCode::CREATED == responce.responseCode()));
auto analyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzerImplicitDbName");
EXPECT_NE(nullptr, analyzer);
}
// name contains full database name
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
auto requestPtr = std::make_unique<GeneralRequestMock>(vocbase);
auto& request = *requestPtr;
auto responcePtr = std::make_unique<GeneralResponseMock>();
auto& responce = *responcePtr;
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::POST);
request._payload.openObject();
request._payload.add("name", VPackValue(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzerWithDbName"));
request._payload.add("type", VPackValue("identity"));
request._payload.add("properties", arangodb::velocypack::Value("{\"args\":\"abc\"}"));
request._payload.close();
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
EXPECT_TRUE((arangodb::rest::ResponseCode::CREATED == responce.responseCode()));
auto analyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzerWithDbName");
EXPECT_NE(nullptr, analyzer);
}
// name collision
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
@ -616,21 +640,33 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
auto sysDbFeature = new arangodb::SystemDatabaseFeature(server);
server.addFeature(sysDbFeature);
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
analyzers->prepare(); // add static analyzers
auto cleanup = arangodb::scopeGuard([dbFeature](){ dbFeature->unprepare(); });
// create system vocbase
// create vocbases
{
auto const databases = arangodb::velocypack::Parser::fromJson(
std::string("[ { \"name\": \"") +
arangodb::StaticStrings::SystemDatabase + "\" } ]");
arangodb::StaticStrings::SystemDatabase + "\" }, { \"name\": \"FooDb\" }, { \"name\": \"FooDb2\" } ]");
ASSERT_TRUE((TRI_ERROR_NO_ERROR == dbFeature->loadDatabases(databases->slice())));
arangodb::methods::Collections::createSystem(
*dbFeature->useDatabase(arangodb::StaticStrings::SystemDatabase),
arangodb::tests::AnalyzerCollectionName);
arangodb::methods::Collections::createSystem(
*dbFeature->useDatabase("FooDb"),
arangodb::tests::AnalyzerCollectionName);
arangodb::methods::Collections::createSystem(
*dbFeature->useDatabase("FooDb2"),
arangodb::tests::AnalyzerCollectionName);
sysDbFeature->prepare();
sysDbFeature->start();
}
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
ASSERT_TRUE((analyzers
@ -712,12 +748,12 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::NONE); // for system collections User::collectionAuthLevel(...) returns database auth::Level
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RO); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
EXPECT_TRUE((arangodb::rest::ResponseCode::NOT_FOUND == responce.responseCode()));
EXPECT_EQ(arangodb::rest::ResponseCode::NOT_FOUND, responce.responseCode());
auto slice = responce._payload.slice();
EXPECT_TRUE((slice.isObject()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) &&
@ -774,7 +810,73 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
EXPECT_TRUE(slice.hasKey("properties") && slice.get("properties").isObject());
EXPECT_TRUE((slice.hasKey("features") && slice.get("features").isArray()));
}
// get custom (known analyzer) authorized but from different db
{
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase("FooDb", arangodb::auth::Level::RW);
user.grantDatabase("FooDb2", arangodb::auth::Level::RO);
user.grantDatabase(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO);
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
ASSERT_TRUE((analyzers
->emplace(result, "FooDb::testAnalyzer1",
"identity",
VPackSlice::noneSlice()) // Empty VPack for nullptr
.ok()));
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
"FooDb2");
auto requestPtr = std::make_unique<GeneralRequestMock>(vocbase);
auto& request = *requestPtr;
auto responcePtr = std::make_unique<GeneralResponseMock>();
auto& responce = *responcePtr;
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::GET);
request.addSuffix("FooDb::testAnalyzer1");
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
// user has access but analyzer should not be visible
EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN, responce.responseCode());
}
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
"FooDb2");
auto requestPtr = std::make_unique<GeneralRequestMock>(vocbase);
auto& request = *requestPtr;
auto responcePtr = std::make_unique<GeneralResponseMock>();
auto& responce = *responcePtr;
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::GET);
request.addSuffix(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1");
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
//system should be visible
EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode());
}
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
"FooDb2");
auto requestPtr = std::make_unique<GeneralRequestMock>(vocbase);
auto& request = *requestPtr;
auto responcePtr = std::make_unique<GeneralResponseMock>();
auto& responce = *responcePtr;
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::GET);
request.addSuffix("::testAnalyzer1");
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
//system should be visible
EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode());
}
}
// get custom (known analyzer) not authorized
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
@ -786,8 +888,7 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::GET);
request.addSuffix(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer1");
request.addSuffix("testAnalyzer1");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
@ -799,7 +900,7 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
EXPECT_TRUE((arangodb::rest::ResponseCode::FORBIDDEN == responce.responseCode()));
EXPECT_EQ(arangodb::rest::ResponseCode::FORBIDDEN , responce.responseCode());
auto slice = responce._payload.slice();
EXPECT_TRUE((slice.isObject()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) &&
@ -826,7 +927,7 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::GET);
request.addSuffix(arangodb::StaticStrings::SystemDatabase + "::unknown");
request.addSuffix("unknown");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
@ -865,7 +966,7 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::GET);
request.addSuffix(arangodb::StaticStrings::SystemDatabase + "::unknown");
request.addSuffix("unknown");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
@ -904,7 +1005,7 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::GET);
request.addSuffix("unknownVocbase::unknown");
request.addSuffix("unknown");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
@ -943,7 +1044,7 @@ TEST_F(RestAnalyzerHandlerTest, test_get) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::GET);
request.addSuffix("unknownVocbase::unknown");
request.addSuffix("unknown");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
@ -1393,6 +1494,8 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
auto sysDbFeature = new arangodb::SystemDatabaseFeature(server);
server.addFeature(sysDbFeature);
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
auto cleanup = arangodb::scopeGuard([dbFeature](){ dbFeature->unprepare(); });
@ -1406,6 +1509,8 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
arangodb::methods::Collections::createSystem(
*dbFeature->useDatabase(arangodb::StaticStrings::SystemDatabase),
arangodb::tests::AnalyzerCollectionName);
sysDbFeature->prepare();
sysDbFeature->start();
}
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
@ -1417,6 +1522,15 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer3",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer4",
"identity", VPackSlice::noneSlice())
.ok()));
result.first.reset(); // to release reference!
struct ExecContext : public arangodb::ExecContext {
ExecContext()
@ -1482,7 +1596,7 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
request.addSuffix(arangodb::StaticStrings::SystemDatabase + "::unknown");
request.addSuffix("unknown");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
@ -1521,8 +1635,7 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
request.addSuffix(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer1");
request.addSuffix("testAnalyzer1");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
@ -1564,8 +1677,7 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
request.addSuffix(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer2");
request.addSuffix("testAnalyzer2");
request.values().emplace("force", "false");
auto inUseAnalyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer2"); // hold ref to mark in-use
@ -1611,8 +1723,7 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
request.addSuffix(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer2");
request.addSuffix("testAnalyzer2");
request.values().emplace("force", "true");
auto inUseAnalyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer2"); // hold ref to mark in-use
@ -1646,7 +1757,7 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
EXPECT_TRUE((true == !analyzer));
}
// success removal
// removal with db name in analyzer name
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
@ -1658,7 +1769,7 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
request.addSuffix(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer1");
"::testAnalyzer3");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
@ -1670,7 +1781,65 @@ TEST_F(RestAnalyzerHandlerTest, test_remove) {
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
EXPECT_TRUE((arangodb::rest::ResponseCode::OK == responce.responseCode()));
EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode());
auto analyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer3");
EXPECT_EQ(nullptr, analyzer);
}
// removal with db name prefix as ::
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
auto requestPtr = std::make_unique<GeneralRequestMock>(vocbase);
auto& request = *requestPtr;
auto responcePtr = std::make_unique<GeneralResponseMock>();
auto& responce = *responcePtr;
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
request.addSuffix("::testAnalyzer4");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode());
auto analyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer4");
EXPECT_EQ(nullptr, analyzer);
}
// success removal
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
auto requestPtr = std::make_unique<GeneralRequestMock>(vocbase);
auto& request = *requestPtr;
auto responcePtr = std::make_unique<GeneralResponseMock>();
auto& responce = *responcePtr;
arangodb::iresearch::RestAnalyzerHandler handler(requestPtr.release(),
responcePtr.release());
request.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
request.addSuffix("testAnalyzer1");
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto status = handler.execute();
EXPECT_TRUE((arangodb::RestStatus::DONE == status));
EXPECT_EQ(arangodb::rest::ResponseCode::OK, responce.responseCode());
auto slice = responce._payload.slice();
EXPECT_TRUE((slice.isObject()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::Code) &&

View File

@ -891,7 +891,7 @@ TEST_F(V8AnalyzersTest, test_create) {
EXPECT_TRUE((fn_create->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2"),
TRI_V8_STD_STRING(isolate.get(), arangodb::StaticStrings::SystemDatabase + "::test:Analyzer2"),
TRI_V8_ASCII_STRING(isolate.get(), "identity"),
v8::True(isolate.get()),
};
@ -949,7 +949,7 @@ TEST_F(V8AnalyzersTest, test_create) {
EXPECT_TRUE((fn_create->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), "::testAnalyzer2"s),
TRI_V8_STD_STRING(isolate.get(), "::test:Analyzer2"s),
TRI_V8_ASCII_STRING(isolate.get(), "identity"),
v8::True(isolate.get()),
};
@ -1033,7 +1033,7 @@ TEST_F(V8AnalyzersTest, test_create) {
EXPECT_TRUE((slice.isObject()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) &&
slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() &&
TRI_ERROR_BAD_PARAMETER == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
TRI_ERROR_FORBIDDEN == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
}
// name collision
@ -1271,6 +1271,124 @@ TEST_F(V8AnalyzersTest, test_create) {
auto analyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2");
EXPECT_TRUE((false == !analyzer));
}
// successful creation with DB name prefix
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
v8::Isolate::CreateParams isolateParams;
ArrayBufferAllocator arrayBufferAllocator;
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
auto isolate =
std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams),
[](v8::Isolate* p) -> void { p->Dispose(); });
ASSERT_TRUE((nullptr != isolate));
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
auto context = v8::Context::New(isolate.get());
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
std::unique_ptr<TRI_v8_global_t> v8g(TRI_CreateV8Globals(isolate.get(), 0)); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
v8g->_vocbase = &vocbase;
arangodb::iresearch::TRI_InitV8Analyzers(*v8g, isolate.get());
auto v8Analyzers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->IResearchAnalyzersTempl)
->NewInstance();
auto fn_create =
v8Analyzers->Get(TRI_V8_ASCII_STRING(isolate.get(), "save"));
EXPECT_TRUE((fn_create->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), vocbase.name() + "::testAnalyzer3"s),
TRI_V8_ASCII_STRING(isolate.get(), "identity"),
TRI_V8_ASCII_STRING(isolate.get(), "{\"abc\":1}")
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto result = v8::Function::Cast(*fn_create)
->CallAsFunction(context, fn_create,
static_cast<int>(args.size()), args.data());
EXPECT_TRUE((!result.IsEmpty()));
EXPECT_TRUE((result.ToLocalChecked()->IsObject()));
auto* v8Analyzer = TRI_UnwrapClass<arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool>(
result.ToLocalChecked()->ToObject(TRI_IGETC).FromMaybe(v8::Local<v8::Object>()),
WRP_IRESEARCH_ANALYZER_TYPE, TRI_IGETC);
EXPECT_TRUE((false == !v8Analyzer));
EXPECT_EQ(vocbase.name() + "::testAnalyzer3", v8Analyzer->name());
EXPECT_EQ("identity", v8Analyzer->type());
EXPECT_EQUAL_SLICES(
VPackSlice::emptyObjectSlice(),
v8Analyzer->properties());
EXPECT_TRUE(v8Analyzer->features().empty());
auto analyzer = analyzers->get(vocbase.name() + "::testAnalyzer3");
EXPECT_TRUE((false == !analyzer));
}
// successful creation in system db by :: prefix
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
v8::Isolate::CreateParams isolateParams;
ArrayBufferAllocator arrayBufferAllocator;
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
auto isolate =
std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams),
[](v8::Isolate* p) -> void { p->Dispose(); });
ASSERT_TRUE((nullptr != isolate));
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
auto context = v8::Context::New(isolate.get());
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
std::unique_ptr<TRI_v8_global_t> v8g(TRI_CreateV8Globals(isolate.get(), 0)); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
v8g->_vocbase = &vocbase;
arangodb::iresearch::TRI_InitV8Analyzers(*v8g, isolate.get());
auto v8Analyzers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->IResearchAnalyzersTempl)
->NewInstance();
auto fn_create =
v8Analyzers->Get(TRI_V8_ASCII_STRING(isolate.get(), "save"));
EXPECT_TRUE((fn_create->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), "::testAnalyzer4"s),
TRI_V8_ASCII_STRING(isolate.get(), "identity"),
TRI_V8_ASCII_STRING(isolate.get(), "{\"abc\":1}")
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto result = v8::Function::Cast(*fn_create)
->CallAsFunction(context, fn_create,
static_cast<int>(args.size()), args.data());
EXPECT_TRUE((!result.IsEmpty()));
EXPECT_TRUE((result.ToLocalChecked()->IsObject()));
auto* v8Analyzer = TRI_UnwrapClass<arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool>(
result.ToLocalChecked()->ToObject(TRI_IGETC).FromMaybe(v8::Local<v8::Object>()),
WRP_IRESEARCH_ANALYZER_TYPE, TRI_IGETC);
EXPECT_TRUE((false == !v8Analyzer));
EXPECT_EQ(vocbase.name() + "::testAnalyzer4", v8Analyzer->name());
EXPECT_EQ("identity", v8Analyzer->type());
EXPECT_EQUAL_SLICES(
VPackSlice::emptyObjectSlice(),
v8Analyzer->properties());
EXPECT_TRUE(v8Analyzer->features().empty());
auto analyzer = analyzers->get(vocbase.name() + "::testAnalyzer4");
EXPECT_NE(nullptr, analyzer);
}
}
TEST_F(V8AnalyzersTest, test_get) {
@ -1298,7 +1416,7 @@ TEST_F(V8AnalyzersTest, test_get) {
{
auto const databases = arangodb::velocypack::Parser::fromJson(
std::string("[ { \"name\": \"") +
arangodb::StaticStrings::SystemDatabase + "\" } ]");
arangodb::StaticStrings::SystemDatabase + "\" }, {\"name\" : \"testVocbase\"} ]");
ASSERT_TRUE((TRI_ERROR_NO_ERROR == dbFeature->loadDatabases(databases->slice())));
}
{
@ -1307,12 +1425,21 @@ TEST_F(V8AnalyzersTest, test_get) {
*vocbase,
arangodb::tests::AnalyzerCollectionName);
}
{
auto vocbase = dbFeature->useDatabase("testVocbase");
arangodb::methods::Collections::createSystem(
*vocbase,
arangodb::tests::AnalyzerCollectionName);
}
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
ASSERT_TRUE((analyzers
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, "testVocbase::testAnalyzer1",
"identity", VPackSlice::noneSlice())
.ok()));
struct ExecContext : public arangodb::ExecContext {
ExecContext()
: arangodb::ExecContext(arangodb::ExecContext::Type::Default, "", "",
@ -1545,6 +1672,122 @@ TEST_F(V8AnalyzersTest, test_get) {
EXPECT_TRUE((true == v8Analyzer->features().empty()));
}
// get custom (known analyzer) authorized but wrong current db
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
v8::Isolate::CreateParams isolateParams;
ArrayBufferAllocator arrayBufferAllocator;
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
auto isolate =
std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams),
[](v8::Isolate* p) -> void { p->Dispose(); });
ASSERT_TRUE((nullptr != isolate));
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
auto context = v8::Context::New(isolate.get());
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
std::unique_ptr<TRI_v8_global_t> v8g(TRI_CreateV8Globals(isolate.get(), 0)); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
v8g->_vocbase = &vocbase;
arangodb::iresearch::TRI_InitV8Analyzers(*v8g, isolate.get());
auto v8Analyzers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->IResearchAnalyzersTempl)
->NewInstance();
auto fn_get =
v8Analyzers->Get(TRI_V8_ASCII_STRING(isolate.get(), "analyzer"));
EXPECT_TRUE((fn_get->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_ASCII_STRING(isolate.get(), "testVocbase::testAnalyzer1"),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RO); // for system collections User::collectionAuthLevel(...) returns database auth::Level
user.grantDatabase("testVocbase", arangodb::auth::Level::RO);
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
v8::TryCatch tryCatch(isolate.get());
arangodb::velocypack::Builder responce;
auto result =
v8::Function::Cast(*fn_get)->CallAsFunction(context, fn_get,
static_cast<int>(args.size()),
args.data());
EXPECT_TRUE(result.IsEmpty());
EXPECT_TRUE(tryCatch.HasCaught());
EXPECT_EQ(TRI_ERROR_NO_ERROR, TRI_V8ToVPack(isolate.get(), responce,
tryCatch.Exception(), false));
auto slice = responce.slice();
EXPECT_TRUE((slice.isObject()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) &&
slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() &&
TRI_ERROR_FORBIDDEN ==
slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
}
// get custom (known analyzer) authorized from system with another current db
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
"testVocbase");
v8::Isolate::CreateParams isolateParams;
ArrayBufferAllocator arrayBufferAllocator;
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
auto isolate =
std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams),
[](v8::Isolate* p) -> void { p->Dispose(); });
ASSERT_TRUE((nullptr != isolate));
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
auto context = v8::Context::New(isolate.get());
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
std::unique_ptr<TRI_v8_global_t> v8g(TRI_CreateV8Globals(isolate.get(), 0)); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
v8g->_vocbase = &vocbase;
arangodb::iresearch::TRI_InitV8Analyzers(*v8g, isolate.get());
auto v8Analyzers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->IResearchAnalyzersTempl)
->NewInstance();
auto fn_get =
v8Analyzers->Get(TRI_V8_ASCII_STRING(isolate.get(), "analyzer"));
EXPECT_TRUE((fn_get->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(
isolate.get(), arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(arangodb::StaticStrings::SystemDatabase, arangodb::auth::Level::RO); // for system collections User::collectionAuthLevel(...) returns database auth::Level
user.grantDatabase("testVocbase", arangodb::auth::Level::RO);
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto result =
v8::Function::Cast(*fn_get)->CallAsFunction(context, fn_get,
static_cast<int>(args.size()),
args.data());
EXPECT_TRUE((!result.IsEmpty()));
EXPECT_TRUE((result.ToLocalChecked()->IsObject()));
auto* v8Analyzer = TRI_UnwrapClass<arangodb::iresearch::IResearchAnalyzerFeature::AnalyzerPool>(
result.ToLocalChecked()->ToObject(TRI_IGETC).FromMaybe(v8::Local<v8::Object>()),
WRP_IRESEARCH_ANALYZER_TYPE, TRI_IGETC);
EXPECT_TRUE((false == !v8Analyzer));
EXPECT_TRUE((arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1" ==
v8Analyzer->name()));
EXPECT_TRUE((std::string("identity") == v8Analyzer->type()));
EXPECT_EQUAL_SLICES(
VPackSlice::emptyObjectSlice(),
v8Analyzer->properties());
EXPECT_TRUE((true == v8Analyzer->features().empty()));
}
// get custom (known analyzer) not authorized
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
@ -2293,10 +2536,12 @@ TEST_F(V8AnalyzersTest, test_remove) {
arangodb::application_features::ApplicationServer server(nullptr, nullptr);
arangodb::iresearch::IResearchAnalyzerFeature* analyzers;
arangodb::DatabaseFeature* dbFeature;
arangodb::SystemDatabaseFeature* sysDbFeature(new arangodb::SystemDatabaseFeature(server));
server.addFeature(new arangodb::QueryRegistryFeature(server)); // required for constructing TRI_vocbase_t
server.addFeature(new arangodb::V8DealerFeature(server)); // required for DatabaseFeature::createDatabase(...)
server.addFeature(dbFeature = new arangodb::DatabaseFeature(server)); // required for IResearchAnalyzerFeature::emplace(...)
server.addFeature(analyzers = new arangodb::iresearch::IResearchAnalyzerFeature(server)); // required for running upgrade task
server.addFeature(sysDbFeature);
auto cleanup = arangodb::scopeGuard([dbFeature](){ dbFeature->unprepare(); });
@ -2304,8 +2549,10 @@ TEST_F(V8AnalyzersTest, test_remove) {
{
auto const databases = arangodb::velocypack::Parser::fromJson(
std::string("[ { \"name\": \"") +
arangodb::StaticStrings::SystemDatabase + "\" } ]");
arangodb::StaticStrings::SystemDatabase + "\" }, { \"name\" : \"testVocbase\"} ]");
ASSERT_TRUE((TRI_ERROR_NO_ERROR == dbFeature->loadDatabases(databases->slice())));
sysDbFeature->prepare();
sysDbFeature->start();
}
{
auto vocbase = dbFeature->useDatabase(arangodb::StaticStrings::SystemDatabase);
@ -2313,16 +2560,39 @@ TEST_F(V8AnalyzersTest, test_remove) {
*vocbase,
arangodb::tests::AnalyzerCollectionName);
}
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
ASSERT_TRUE((analyzers
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2",
"identity", VPackSlice::noneSlice())
.ok()));
{
auto vocbase = dbFeature->useDatabase("testVocbase");
arangodb::methods::Collections::createSystem(
*vocbase,
arangodb::tests::AnalyzerCollectionName);
}
{
arangodb::iresearch::IResearchAnalyzerFeature::EmplaceResult result;
ASSERT_TRUE((analyzers
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, arangodb::StaticStrings::SystemDatabase + "::testAnalyzer3",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, "testVocbase::testAnalyzer1",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, "testVocbase::testAnalyzer2",
"identity", VPackSlice::noneSlice())
.ok()));
ASSERT_TRUE((analyzers
->emplace(result, "testVocbase::testAnalyzer3",
"identity", VPackSlice::noneSlice())
.ok()));
}
struct ExecContext : public arangodb::ExecContext {
ExecContext()
: arangodb::ExecContext(arangodb::ExecContext::Type::Default, "", "",
@ -2420,7 +2690,7 @@ TEST_F(V8AnalyzersTest, test_remove) {
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), arangodb::StaticStrings::SystemDatabase + "::unknown"),
TRI_V8_ASCII_STRING(isolate.get(), "unknown"),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
@ -2476,7 +2746,7 @@ TEST_F(V8AnalyzersTest, test_remove) {
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"),
TRI_V8_ASCII_STRING(isolate.get(), "testAnalyzer1"),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
@ -2535,7 +2805,7 @@ TEST_F(V8AnalyzersTest, test_remove) {
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2"),
TRI_V8_ASCII_STRING(isolate.get(), "testAnalyzer2"),
v8::False(isolate.get()),
};
auto inUseAnalyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
@ -2598,7 +2868,7 @@ TEST_F(V8AnalyzersTest, test_remove) {
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), arangodb::StaticStrings::SystemDatabase + "::testAnalyzer2"),
TRI_V8_ASCII_STRING(isolate.get(), "testAnalyzer2"),
v8::True(isolate.get()),
};
auto inUseAnalyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
@ -2651,7 +2921,7 @@ TEST_F(V8AnalyzersTest, test_remove) {
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_STD_STRING(isolate.get(), arangodb::StaticStrings::SystemDatabase + "::testAnalyzer1"),
TRI_V8_ASCII_STRING(isolate.get(), "testAnalyzer1"),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
@ -2671,4 +2941,208 @@ TEST_F(V8AnalyzersTest, test_remove) {
"::testAnalyzer1");
EXPECT_TRUE((true == !analyzer));
}
// removal by system db name with ::
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
v8::Isolate::CreateParams isolateParams;
ArrayBufferAllocator arrayBufferAllocator;
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
auto isolate =
std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams),
[](v8::Isolate* p) -> void { p->Dispose(); });
ASSERT_TRUE((nullptr != isolate));
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
auto context = v8::Context::New(isolate.get());
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
std::unique_ptr<TRI_v8_global_t> v8g(TRI_CreateV8Globals(isolate.get(), 0)); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
v8g->_vocbase = &vocbase;
arangodb::iresearch::TRI_InitV8Analyzers(*v8g, isolate.get());
auto v8Analyzers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->IResearchAnalyzersTempl)
->NewInstance();
auto fn_remove =
v8Analyzers->Get(TRI_V8_ASCII_STRING(isolate.get(), "remove"));
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_ASCII_STRING(isolate.get(), "::testAnalyzer3"),
v8::False(isolate.get()),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
arangodb::velocypack::Builder responce;
v8::TryCatch tryCatch(isolate.get());
auto result = v8::Function::Cast(*fn_remove)
->CallAsFunction(context, fn_remove,
static_cast<int>(args.size()), args.data());
EXPECT_TRUE(!result.IsEmpty());
EXPECT_TRUE((result.ToLocalChecked()->IsUndefined()));
auto analyzer = analyzers->get(arangodb::StaticStrings::SystemDatabase +
"::testAnalyzer3");
EXPECT_EQ(nullptr, analyzer);
}
// removal from wrong db
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
arangodb::StaticStrings::SystemDatabase);
v8::Isolate::CreateParams isolateParams;
ArrayBufferAllocator arrayBufferAllocator;
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
auto isolate =
std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams),
[](v8::Isolate* p) -> void { p->Dispose(); });
ASSERT_TRUE((nullptr != isolate));
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
auto context = v8::Context::New(isolate.get());
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
std::unique_ptr<TRI_v8_global_t> v8g(TRI_CreateV8Globals(isolate.get(), 0)); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
v8g->_vocbase = &vocbase;
arangodb::iresearch::TRI_InitV8Analyzers(*v8g, isolate.get());
auto v8Analyzers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->IResearchAnalyzersTempl)
->NewInstance();
auto fn_remove =
v8Analyzers->Get(TRI_V8_ASCII_STRING(isolate.get(), "remove"));
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_ASCII_STRING(isolate.get(), "testVocbase::testAnalyzer1"),
v8::False(isolate.get()),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
user.grantDatabase("testVocbase", arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
arangodb::velocypack::Builder responce;
v8::TryCatch tryCatch(isolate.get());
auto result = v8::Function::Cast(*fn_remove)
->CallAsFunction(context, fn_remove,
static_cast<int>(args.size()), args.data());
EXPECT_TRUE((result.IsEmpty()));
EXPECT_TRUE((tryCatch.HasCaught()));
EXPECT_TRUE((TRI_ERROR_NO_ERROR == TRI_V8ToVPack(isolate.get(), responce,
tryCatch.Exception(), false)));
auto slice = responce.slice();
EXPECT_TRUE((slice.isObject()));
EXPECT_TRUE((slice.hasKey(arangodb::StaticStrings::ErrorNum) &&
slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() &&
TRI_ERROR_FORBIDDEN ==
slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
auto analyzer = analyzers->get("testVocbase::testAnalyzer1");
EXPECT_NE(nullptr, analyzer);
}
// success removal from non-system db
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
"testVocbase");
v8::Isolate::CreateParams isolateParams;
ArrayBufferAllocator arrayBufferAllocator;
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
auto isolate =
std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams),
[](v8::Isolate* p) -> void { p->Dispose(); });
ASSERT_TRUE((nullptr != isolate));
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
auto context = v8::Context::New(isolate.get());
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
std::unique_ptr<TRI_v8_global_t> v8g(TRI_CreateV8Globals(isolate.get(), 0)); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
v8g->_vocbase = &vocbase;
arangodb::iresearch::TRI_InitV8Analyzers(*v8g, isolate.get());
auto v8Analyzers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->IResearchAnalyzersTempl)
->NewInstance();
auto fn_remove =
v8Analyzers->Get(TRI_V8_ASCII_STRING(isolate.get(), "remove"));
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_ASCII_STRING(isolate.get(), "testAnalyzer2"),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto result = v8::Function::Cast(*fn_remove)
->CallAsFunction(context, fn_remove,
static_cast<int>(args.size()), args.data());
EXPECT_TRUE(!result.IsEmpty());
EXPECT_TRUE((result.ToLocalChecked()->IsUndefined()));
auto analyzer = analyzers->get("testVocbase::testAnalyzer2");
EXPECT_EQ(nullptr, analyzer);
}
// success removal with db name prefix
{
TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1,
"testVocbase");
v8::Isolate::CreateParams isolateParams;
ArrayBufferAllocator arrayBufferAllocator;
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
auto isolate =
std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams),
[](v8::Isolate* p) -> void { p->Dispose(); });
ASSERT_TRUE((nullptr != isolate));
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
auto context = v8::Context::New(isolate.get());
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
std::unique_ptr<TRI_v8_global_t> v8g(TRI_CreateV8Globals(isolate.get(), 0)); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
v8g->_vocbase = &vocbase;
arangodb::iresearch::TRI_InitV8Analyzers(*v8g, isolate.get());
auto v8Analyzers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->IResearchAnalyzersTempl)
->NewInstance();
auto fn_remove =
v8Analyzers->Get(TRI_V8_ASCII_STRING(isolate.get(), "remove"));
EXPECT_TRUE((fn_remove->IsFunction()));
std::vector<v8::Local<v8::Value>> args = {
TRI_V8_ASCII_STRING(isolate.get(), "testVocbase::testAnalyzer3"),
};
arangodb::auth::UserMap userMap; // empty map, no user -> no permissions
auto& user =
userMap
.emplace("", arangodb::auth::User::newUser("", "", arangodb::auth::Source::LDAP))
.first->second;
user.grantDatabase(vocbase.name(), arangodb::auth::Level::RW); // for system collections User::collectionAuthLevel(...) returns database auth::Level
userManager->setAuthInfo(userMap); // set user map to avoid loading configuration from system database
auto result = v8::Function::Cast(*fn_remove)
->CallAsFunction(context, fn_remove,
static_cast<int>(args.size()), args.data());
EXPECT_TRUE(!result.IsEmpty());
EXPECT_TRUE((result.ToLocalChecked()->IsUndefined()));
auto analyzer = analyzers->get("testVocbase::testAnalyzer3");
EXPECT_EQ(nullptr, analyzer);
}
}

View File

@ -922,7 +922,73 @@ function IResearchFeatureDDLTestSuite () {
} // forget variable `view`, it's invalid now
assertEqual(db[viewName], undefined);
},
testLinkWithAnalyzerFromOtherDb: function() {
let databaseNameAnalyzer = "testDatabaseAnalyzer";
let databaseNameView = "testDatabaseView";
let tmpAnalyzerName = "TmpIdentity";
let systemAnalyzerName = "SystemIdentity";
db._useDatabase("_system");
try { db._dropDatabase(databaseNameAnalyzer);} catch(e) {}
try { db._dropDatabase(databaseNameView);} catch(e) {}
analyzers.save(systemAnalyzerName, "identity");
db._createDatabase(databaseNameAnalyzer);
db._createDatabase(databaseNameView);
db._useDatabase(databaseNameAnalyzer);
let tmpIdentity = analyzers.save(tmpAnalyzerName, "identity");
assertTrue(tmpIdentity != null);
tmpIdentity = undefined;
db._useDatabase(databaseNameView);
db._create("FOO");
try {
db._createView("FOO_view", "arangosearch", {links:{"FOO":{analyzers:[databaseNameAnalyzer + "::" + tmpAnalyzerName]}}});
fail();
} catch(e) {
assertEqual(require("internal").errors.ERROR_BAD_PARAMETER.code,
e.errorNum);
}
// but cross-db usage of system analyzers is ok
db._createView("FOO_view", "arangosearch", {links:{"FOO":{analyzers:["::" + systemAnalyzerName]}}});
db._createView("FOO_view2", "arangosearch", {links:{"FOO":{analyzers:["_system::" + systemAnalyzerName]}}});
db._useDatabase("_system");
db._dropDatabase(databaseNameAnalyzer);
db._dropDatabase(databaseNameView);
analyzers.remove(systemAnalyzerName, true);
},
// Commented as this situation is not documented (user should not do this), will be adressed later
//testLinkWithAnalyzerFromOtherDbByAnalyzerDefinitions: function() {
// let databaseNameAnalyzer = "testDatabaseAnalyzer";
// let databaseNameView = "testDatabaseView";
// db._useDatabase("_system");
// try { db._dropDatabase(databaseNameAnalyzer);} catch(e) {}
// try { db._dropDatabase(databaseNameView);} catch(e) {}
// db._createDatabase(databaseNameAnalyzer);
// db._createDatabase(databaseNameView);
// db._useDatabase(databaseNameAnalyzer);
// db._useDatabase(databaseNameView);
// db._create("FOO");
// try {
// db._createView("FOO_view", "arangosearch",
// {
// links:{
// "FOO":{
// analyzerDefinitions:[{name:databaseNameAnalyzer + "::TmpIdentity", type: "identity"}],
// analyzers:[databaseNameAnalyzer + "::TmpIdentity"]
// }
// }
// });
// fail();
// } catch(e) {
// // analyzerDefinitions should be ignored
// // analyzer should not be found
// assertEqual(require("internal").errors.ERROR_BAD_PARAMETER.code,
// e.errorNum);
// }
// db._useDatabase("_system");
// db._dropDatabase(databaseNameAnalyzer);
// db._dropDatabase(databaseNameView);
//},
////////////////////////////////////////////////////////////////////////////
/// @brief test ensure that view is deleted within deleted database
/// Regression test for arangodb/release-3.4#153.
@ -930,7 +996,7 @@ function IResearchFeatureDDLTestSuite () {
testLeftViewInDroppedDatabase: function () {
const dbName = 'TestDB';
const viewName = 'TestView';
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
db._createDatabase(dbName);
@ -946,6 +1012,92 @@ function IResearchFeatureDDLTestSuite () {
assertEqual(db[viewName], undefined);
},
// test for public issue #9652
testAnalyzerWithStopwordsNameConflict : function() {
const dbName = "TestNameConflictDB";
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
db._createDatabase(dbName);
db._useDatabase(dbName);
db._create("col1");
db._create("col2");
analyzers.save("custom_analyzer",
"text",
{
locale: "en.UTF-8",
case: "lower",
stopwords: ["the", "of", "inc", "co", "plc", "ltd", "ag"],
accent: false,
stemming: false
},
["position", "norm","frequency"]);
let v = db._createView("view1", "arangosearch", {
"links": {
"col1": {
"analyzers": ["identity"],
"fields": { "name": { "analyzers": ["custom_analyzer"]}},
"includeAllFields": false,
"storeValues": "none",
"trackListPositions": false
},
"col2": {
"analyzers": ["identity"],
"fields": { "name": { "analyzers": ["custom_analyzer"]}},
"includeAllFields": false,
"storeValues": "none",
"trackListPositions": false
}
}});
let properties = v.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(2, Object.keys(properties.links).length);
assertTrue(Object === properties.links.col1.constructor);
assertTrue(Object === properties.links.col1.fields.constructor);
assertEqual(1, Object.keys(properties.links.col1.fields).length);
assertTrue(Object === properties.links.col1.fields.name.constructor);
assertTrue(Array === properties.links.col1.fields.name.analyzers.constructor);
assertEqual(1, properties.links.col1.fields.name.analyzers.length);
assertTrue(String === properties.links.col1.fields.name.analyzers[0].constructor);
assertEqual("custom_analyzer", properties.links.col1.fields.name.analyzers[0]);
assertTrue(Boolean === properties.links.col1.includeAllFields.constructor);
assertEqual(false, properties.links.col1.includeAllFields);
assertTrue(Boolean === properties.links.col1.trackListPositions.constructor);
assertEqual(false, properties.links.col1.trackListPositions);
assertTrue(String === properties.links.col1.storeValues.constructor);
assertEqual("none", properties.links.col1.storeValues);
assertTrue(Array === properties.links.col1.analyzers.constructor);
assertEqual(1, properties.links.col1.analyzers.length);
assertTrue(String === properties.links.col1.analyzers[0].constructor);
assertEqual("identity", properties.links.col1.analyzers[0]);
assertTrue(Object === properties.links.col2.constructor);
assertTrue(Object === properties.links.col2.fields.constructor);
assertEqual(1, Object.keys(properties.links.col2.fields).length);
assertTrue(Object === properties.links.col2.fields.name.constructor);
assertTrue(Array === properties.links.col2.fields.name.analyzers.constructor);
assertEqual(1, properties.links.col2.fields.name.analyzers.length);
assertTrue(String === properties.links.col2.fields.name.analyzers[0].constructor);
assertEqual("custom_analyzer", properties.links.col2.fields.name.analyzers[0]);
assertTrue(Boolean === properties.links.col2.includeAllFields.constructor);
assertEqual(false, properties.links.col2.includeAllFields);
assertTrue(Boolean === properties.links.col2.trackListPositions.constructor);
assertEqual(false, properties.links.col2.trackListPositions);
assertTrue(String === properties.links.col2.storeValues.constructor);
assertEqual("none", properties.links.col2.storeValues);
assertTrue(Array === properties.links.col2.analyzers.constructor);
assertEqual(1, properties.links.col2.analyzers.length);
assertTrue(String === properties.links.col2.analyzers[0].constructor);
assertEqual("identity", properties.links.col2.analyzers[0]);
db._useDatabase("_system");
db._dropDatabase(dbName);
}
};
}

View File

@ -899,7 +899,73 @@ function IResearchFeatureDDLTestSuite () {
assertEqual(i, r.z);
});
},
testLinkWithAnalyzerFromOtherDb: function() {
let databaseNameAnalyzer = "testDatabaseAnalyzer";
let databaseNameView = "testDatabaseView";
let tmpAnalyzerName = "TmpIdentity";
let systemAnalyzerName = "SystemIdentity";
db._useDatabase("_system");
try { db._dropDatabase(databaseNameAnalyzer);} catch(e) {}
try { db._dropDatabase(databaseNameView);} catch(e) {}
analyzers.save(systemAnalyzerName, "identity");
db._createDatabase(databaseNameAnalyzer);
db._createDatabase(databaseNameView);
db._useDatabase(databaseNameAnalyzer);
let tmpIdentity = analyzers.save(tmpAnalyzerName, "identity");
assertTrue(tmpIdentity != null);
tmpIdentity = undefined;
db._useDatabase(databaseNameView);
db._create("FOO");
try {
db._createView("FOO_view", "arangosearch", {links:{"FOO":{analyzers:[databaseNameAnalyzer + "::" + tmpAnalyzerName]}}});
fail();
} catch(e) {
assertEqual(require("internal").errors.ERROR_BAD_PARAMETER.code,
e.errorNum);
}
// but cross-db usage of system analyzers is ok
db._createView("FOO_view", "arangosearch", {links:{"FOO":{analyzers:["::" + systemAnalyzerName]}}});
db._createView("FOO_view2", "arangosearch", {links:{"FOO":{analyzers:["_system::" + systemAnalyzerName]}}});
db._useDatabase("_system");
db._dropDatabase(databaseNameAnalyzer);
db._dropDatabase(databaseNameView);
analyzers.remove(systemAnalyzerName, true);
},
// Commented as this situation is not documented (user should not do this), will be adressed later
//testLinkWithAnalyzerFromOtherDbByAnalyzerDefinitions: function() {
// let databaseNameAnalyzer = "testDatabaseAnalyzer";
// let databaseNameView = "testDatabaseView";
//
// db._useDatabase("_system");
// try { db._dropDatabase(databaseNameAnalyzer);} catch(e) {}
// try { db._dropDatabase(databaseNameView);} catch(e) {}
// db._createDatabase(databaseNameAnalyzer);
// db._createDatabase(databaseNameView);
// db._useDatabase(databaseNameAnalyzer);
// db._useDatabase(databaseNameView);
// db._create("FOO");
// try {
// db._createView("FOO_view", "arangosearch",
// {
// links:{
// "FOO":{
// analyzerDefinitions:[{name:databaseNameAnalyzer + "::TmpIdentity", type: "identity"}],
// analyzers:[databaseNameAnalyzer + "::TmpIdentity"]
// }
// }
// });
// fail();
// } catch(e) {
// // analyzerDefinitions should be ignored
// // analyzer should not be found
// assertEqual(require("internal").errors.ERROR_BAD_PARAMETER.code,
// e.errorNum);
// }
// db._useDatabase("_system");
// db._dropDatabase(databaseNameAnalyzer);
// db._dropDatabase(databaseNameView);
//},
////////////////////////////////////////////////////////////////////////////
/// @brief test create & drop of a view with a link.
/// Regression test for arangodb/backlog#486.
@ -930,7 +996,7 @@ function IResearchFeatureDDLTestSuite () {
testLeftViewInDroppedDatabase: function () {
const dbName = 'TestDB';
const viewName = 'TestView';
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
db._createDatabase(dbName);
@ -946,6 +1012,92 @@ function IResearchFeatureDDLTestSuite () {
assertEqual(db[viewName], undefined);
},
// test for public issue #9652
testAnalyzerWithStopwordsNameConflict : function() {
const dbName = "TestNameConflictDB";
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
db._createDatabase(dbName);
db._useDatabase(dbName);
db._create("col1");
db._create("col2");
analyzers.save("custom_analyzer",
"text",
{
locale: "en.UTF-8",
case: "lower",
stopwords: ["the", "of", "inc", "co", "plc", "ltd", "ag"],
accent: false,
stemming: false
},
["position", "norm","frequency"]);
let v = db._createView("view1", "arangosearch", {
"links": {
"col1": {
"analyzers": ["identity"],
"fields": { "name": { "analyzers": ["custom_analyzer"]}},
"includeAllFields": false,
"storeValues": "none",
"trackListPositions": false
},
"col2": {
"analyzers": ["identity"],
"fields": { "name": { "analyzers": ["custom_analyzer"]}},
"includeAllFields": false,
"storeValues": "none",
"trackListPositions": false
}
}});
let properties = v.properties();
assertTrue(Object === properties.links.constructor);
assertEqual(2, Object.keys(properties.links).length);
assertTrue(Object === properties.links.col1.constructor);
assertTrue(Object === properties.links.col1.fields.constructor);
assertEqual(1, Object.keys(properties.links.col1.fields).length);
assertTrue(Object === properties.links.col1.fields.name.constructor);
assertTrue(Array === properties.links.col1.fields.name.analyzers.constructor);
assertEqual(1, properties.links.col1.fields.name.analyzers.length);
assertTrue(String === properties.links.col1.fields.name.analyzers[0].constructor);
assertEqual("custom_analyzer", properties.links.col1.fields.name.analyzers[0]);
assertTrue(Boolean === properties.links.col1.includeAllFields.constructor);
assertEqual(false, properties.links.col1.includeAllFields);
assertTrue(Boolean === properties.links.col1.trackListPositions.constructor);
assertEqual(false, properties.links.col1.trackListPositions);
assertTrue(String === properties.links.col1.storeValues.constructor);
assertEqual("none", properties.links.col1.storeValues);
assertTrue(Array === properties.links.col1.analyzers.constructor);
assertEqual(1, properties.links.col1.analyzers.length);
assertTrue(String === properties.links.col1.analyzers[0].constructor);
assertEqual("identity", properties.links.col1.analyzers[0]);
assertTrue(Object === properties.links.col2.constructor);
assertTrue(Object === properties.links.col2.fields.constructor);
assertEqual(1, Object.keys(properties.links.col2.fields).length);
assertTrue(Object === properties.links.col2.fields.name.constructor);
assertTrue(Array === properties.links.col2.fields.name.analyzers.constructor);
assertEqual(1, properties.links.col2.fields.name.analyzers.length);
assertTrue(String === properties.links.col2.fields.name.analyzers[0].constructor);
assertEqual("custom_analyzer", properties.links.col2.fields.name.analyzers[0]);
assertTrue(Boolean === properties.links.col2.includeAllFields.constructor);
assertEqual(false, properties.links.col2.includeAllFields);
assertTrue(Boolean === properties.links.col2.trackListPositions.constructor);
assertEqual(false, properties.links.col2.trackListPositions);
assertTrue(String === properties.links.col2.storeValues.constructor);
assertEqual("none", properties.links.col2.storeValues);
assertTrue(Array === properties.links.col2.analyzers.constructor);
assertEqual(1, properties.links.col2.analyzers.length);
assertTrue(String === properties.links.col2.analyzers[0].constructor);
assertEqual("identity", properties.links.col2.analyzers[0]);
db._useDatabase("_system");
db._dropDatabase(dbName);
}
};
}

View File

@ -1,5 +1,5 @@
/*jshint globalstrict:false, strict:false, maxlen: 500 */
/*global assertUndefined, assertEqual, assertTrue, assertFalse, fail, db._query */
/*global assertUndefined, assertEqual, assertTrue, assertFalse, assertNotNull, fail, db._query */
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
@ -55,42 +55,149 @@ function iResearchFeatureAqlTestSuite () {
testAnalyzersInvalidPropertiesDiscarded : function() {
{
try {analyzers.remove(db._name() + "::normPropAnalyzer"); } catch (e) {}
try {analyzers.remove("normPropAnalyzer"); } catch (e) {}
let analyzer = analyzers.save("normPropAnalyzer", "norm", { "locale":"en", "invalid_param":true});
assertTrue(null != analyzer);
assertTrue(null == analyzer.properties.invalid_param);
analyzers.remove(db._name() + "::normPropAnalyzer", true);
analyzers.remove("normPropAnalyzer", true);
}
{
try {analyzers.remove(db._name() + "::textPropAnalyzer"); } catch (e) {}
try {analyzers.remove("textPropAnalyzer"); } catch (e) {}
let analyzer = analyzers.save("textPropAnalyzer", "text", {"stopwords" : [], "locale":"en", "invalid_param":true});
assertTrue(null != analyzer);
assertTrue(null == analyzer.properties.invalid_param);
analyzers.remove(db._name() + "::textPropAnalyzer", true);
analyzers.remove("textPropAnalyzer", true);
}
{
try {analyzers.remove(db._name() + "::delimiterPropAnalyzer"); } catch (e) {}
try {analyzers.remove("delimiterPropAnalyzer"); } catch (e) {}
let analyzer = analyzers.save("delimiterPropAnalyzer", "delimiter", { "delimiter":"|", "invalid_param":true});
assertTrue(null != analyzer);
assertTrue(null == analyzer.properties.invalid_param);
analyzers.remove(db._name() + "::delimiterPropAnalyzer", true);
analyzers.remove("delimiterPropAnalyzer", true);
}
{
try {analyzers.remove(db._name() + "::stemPropAnalyzer"); } catch (e) {}
try {analyzers.remove("stemPropAnalyzer"); } catch (e) {}
let analyzer = analyzers.save("stemPropAnalyzer", "stem", { "locale":"en", "invalid_param":true});
assertTrue(null != analyzer);
assertTrue(null == analyzer.properties.invalid_param);
analyzers.remove(db._name() + "::stemPropAnalyzer", true);
analyzers.remove("stemPropAnalyzer", true);
}
{
try {analyzers.remove(db._name() + "::ngramPropAnalyzer"); } catch (e) {}
try {analyzers.remove("ngramPropAnalyzer"); } catch (e) {}
let analyzer = analyzers.save("ngramPropAnalyzer", "ngram", { "min":1, "max":5, "preserveOriginal":true, "invalid_param":true});
assertTrue(null != analyzer);
assertTrue(null == analyzer.properties.invalid_param);
analyzers.remove(db._name() + "::ngramPropAnalyzer", true);
analyzers.remove("ngramPropAnalyzer", true);
}
},
testAnalyzerRemovalWithDatabaseName_InSystem: function() {
let dbName = "analyzerWrongDbName1";
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
db._createDatabase(dbName);
db._useDatabase(dbName);
analyzers.save("MyTrigram", "ngram", { min: 2, max: 3, preserveOriginal: true });
db._useDatabase("_system");
try {
analyzers.remove(dbName + "::MyTrigram");
fail(); // removal with db name in wrong current used db should also fail
} catch(e) {
assertEqual(require("internal").errors.ERROR_FORBIDDEN .code,
e.errorNum);
}
db._useDatabase(dbName);
analyzers.remove(dbName + "::MyTrigram", true);
db._useDatabase("_system");
db._dropDatabase(dbName);
},
testAnalyzerCreateRemovalWithDatabaseName_InDb: function() {
let dbName = "analyzerWrongDbName2";
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
db._createDatabase(dbName);
try {
analyzers.save(dbName + "::MyTrigram", "ngram", { min: 2, max: 3, preserveOriginal: true });
fail();
} catch(e) {
assertEqual(require("internal").errors.ERROR_FORBIDDEN.code,
e.errorNum);
}
db._useDatabase(dbName);
analyzers.save(dbName + "::MyTrigram", "ngram", { min: 2, max: 3, preserveOriginal: true });
analyzers.remove(dbName + "::MyTrigram", true);
db._useDatabase("_system");
db._dropDatabase(dbName);
},
testAnalyzerCreateRemovalWithDatabaseName_InSystem: function() {
let dbName = "analyzerWrongDbName3";
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
try { analyzers.remove("MyTrigram"); } catch (e) {}
db._createDatabase(dbName);
db._useDatabase(dbName);
// cross-db request should fail
try {
analyzers.save("::MyTrigram", "ngram", { min: 2, max: 3, preserveOriginal: true });
fail();
} catch(e) {
assertEqual(require("internal").errors.ERROR_FORBIDDEN.code,
e.errorNum);
}
try {
analyzers.save("_system::MyTrigram", "ngram", { min: 2, max: 3, preserveOriginal: true });
fail();
} catch(e) {
assertEqual(require("internal").errors.ERROR_FORBIDDEN.code,
e.errorNum);
}
// in _system db requests should be ok
db._useDatabase("_system");
analyzers.save("::MyTrigram", "ngram", { min: 2, max: 3, preserveOriginal: true });
analyzers.remove("::MyTrigram", true);
analyzers.save("_system::MyTrigram", "ngram", { min: 2, max: 3, preserveOriginal: true });
analyzers.remove("_system::MyTrigram", true);
db._dropDatabase(dbName);
},
testAnalyzerInvalidName: function() {
let dbName = "analyzerWrongDbName4";
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
db._createDatabase(dbName);
db._useDatabase(dbName);
try {
// invalid ':' in name
analyzers.save(dbName + "::MyTr:igram", "ngram", { min: 2, max: 3, preserveOriginal: true });
fail();
} catch(e) {
assertEqual(require("internal").errors.ERROR_BAD_PARAMETER.code,
e.errorNum);
}
db._useDatabase("_system");
db._dropDatabase(dbName);
},
testAnalyzerGetFromOtherDatabase: function() {
let dbName = "analyzerDbName";
let anotherDbName = "anotherDbName";
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
try { db._dropDatabase(anotherDbName); } catch (e) {}
db._createDatabase(dbName);
db._createDatabase(anotherDbName);
db._useDatabase(dbName);
let analyzer = analyzers.save("MyTrigram", "ngram", { min: 2, max: 3, preserveOriginal: true });
assertNotNull(analyzer);
db._useDatabase(anotherDbName);
try {
analyzers.analyzer(dbName + "::MyTrigram");
fail();
} catch(e) {
assertEqual(require("internal").errors.ERROR_FORBIDDEN .code,
e.errorNum);
}
db._useDatabase("_system");
db._dropDatabase(dbName);
db._dropDatabase(anotherDbName);
},
testAnalyzers: function() {
let oldList = analyzers.toArray();
let oldListInCollection = db._analyzers.toArray();
@ -146,7 +253,7 @@ function iResearchFeatureAqlTestSuite () {
}
// removal
analyzers.remove(db._name() + "::testAnalyzer");
analyzers.remove("testAnalyzer");
assertTrue(null === analyzers.analyzer(db._name() + "::testAnalyzer"));
assertEqual(oldList.length, analyzers.toArray().length);
// check the analyzers collection in database
@ -173,7 +280,7 @@ function iResearchFeatureAqlTestSuite () {
testAnalyzersPrefix: function() {
let dbName = "TestDB";
db._useDatabase("_system");
try { db._dropDatabase(dbName); } catch (e) {}
db._createDatabase(dbName);
db._useDatabase(dbName);
@ -187,31 +294,6 @@ function iResearchFeatureAqlTestSuite () {
db._useDatabase(dbName);
analyzers.save("testAnalyzer", "identity", {}, [ "norm" ]);
// retrieval (system)
db._useDatabase("_system");
{
let analyzer = analyzers.analyzer("testAnalyzer");
assertTrue(null !== analyzer);
assertEqual(db._name() + "::testAnalyzer", analyzer.name());
assertEqual("identity", analyzer.type());
assertEqual(0, Object.keys(analyzer.properties()).length);
assertTrue(Array === analyzer.features().constructor);
assertEqual(1, analyzer.features().length);
assertEqual([ "frequency" ], analyzer.features());
}
{
let analyzer = analyzers.analyzer(dbName + "::testAnalyzer");
assertTrue(null !== analyzer);
assertEqual(dbName + "::testAnalyzer", analyzer.name());
assertEqual("identity", analyzer.type());
assertEqual(0, Object.keys(analyzer.properties()).length);
assertTrue(Array === analyzer.features().constructor);
assertEqual(1, analyzer.features().length);
assertEqual([ "norm" ], analyzer.features());
}
// retrieval (dbName)
db._useDatabase(dbName);
@ -245,7 +327,9 @@ function iResearchFeatureAqlTestSuite () {
// removal
analyzers.remove("testAnalyzer", true);
assertTrue(null === analyzers.analyzer("testAnalyzer"));
analyzers.remove("::testAnalyzer", true);
db._useDatabase("_system");
analyzers.remove("testAnalyzer", true);
db._useDatabase(dbName); // switch back to check analyzer with global name
assertTrue(null === analyzers.analyzer("::testAnalyzer"));
assertEqual(oldList.length, analyzers.toArray().length);

View File

@ -184,10 +184,17 @@ function ViewSuite () {
var def = db._createView("def", "arangosearch", {});
assertEqual(def.name(), "def");
var views = db._views();
let expectedViews = new Set();
expectedViews.add(abc.name());
expectedViews.add(def.name());
assertTrue(Array.isArray(views));
assertEqual(views.length, 2);
assertEqual(abc.name(), views[0].name());
assertEqual(def.name(), views[1].name());
assertEqual(views.length, expectedViews.size);
for (var i = 0; i < views.length; i++) {
expectedViews.delete(views[i].name());
}
assertEqual(0, expectedViews.size);
abc.drop();
def.drop();
},