Merge pull request #6205 from Icinga/feature/api-verbose-errors

API: Unify verbose error messages
This commit is contained in:
Michael Friedrich 2018-04-17 16:40:11 +02:00 committed by GitHub
commit a8b5d8e64a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 193 additions and 81 deletions

View File

@ -103,7 +103,7 @@ The output will be sent back as a JSON object:
> **Tip**
>
> You can use the `pretty` parameter to beautify the JSON response with Icinga v2.9+.
> You can use the [pretty](12-icinga2-api.md#icinga2-api-parameters-global) parameter to beautify the JSON response with Icinga v2.9+.
You can also use [jq](https://stedolan.github.io/jq/) or `python -m json.tool`
in combination with curl on the CLI.
@ -124,7 +124,8 @@ The API will return standard [HTTP statuses](https://www.ietf.org/rfc/rfc2616.tx
including error codes.
When an error occurs, the response body will contain additional information
about the problem and its source.
about the problem and its source. Set `verbose` to true to retrieve more
insights into what may be causing the error.
A status code between 200 and 299 generally means that the request was
successful.
@ -271,6 +272,27 @@ Here are the exact same query parameters as a JSON object:
The [match function](18-library-reference.md#global-functions-match) is available as global function
in Icinga 2.
### Global Parameters <a id="icinga2-api-parameters-global"></a>
Name | Description
----------------|--------------------
pretty | Pretty-print the JSON response.
verbose | Add verbose debug information inside the `diagnostic_information` key into the response if available. This helps with troubleshooting failing requests.
Example as URL parameter:
```
/v1/objects/hosts?pretty=1
```
Example as JSON object:
```
{ "pretty": true }
```
Both parameters have been added in Icinga 2 v2.9.
### Request Method Override <a id="icinga2-api-requests-method-override"></a>
`GET` requests do not allow you to send a request body. In case you cannot pass everything as URL

View File

@ -702,6 +702,33 @@ Look into the log and check whether the feature logs anything specific for this
grep GraphiteWriter /var/log/icinga2/icinga2.log
```
## REST API Troubleshooting <a id="troubleshooting-api"></a>
In order to analyse errors on API requests, you can explicitly enable the [verbose parameter](12-icinga2-api.md#icinga2-api-parameters-global).
```
$ curl -k -s -u root:icinga -H 'Accept: application/json' -X DELETE 'https://localhost:5665/v1/objects/hosts/example-cmdb?pretty=1&verbose=1'
{
"diagnostic_information": "Error: Object does not exist.\n\n ....",
"error": 404.0,
"status": "No objects found."
}
```
## REST API Troubleshooting: No Objects Found <a id="troubleshooting-api-no-objects-found"></a>
Please note that the `404` status with no objects being found can also originate
from missing or too strict object permissions for the authenticated user.
This is a security feature to disable object name guessing. If this would not be the
case, restricted users would be able to get a list of names of your objects just by
trying every character combination.
In order to analyse and fix the problem, please check the following:
- use an administrative account with full permissions to check whether the objects are actually there.
- verify the permissions on the affected ApiUser object and fix them.
## Certificate Troubleshooting <a id="troubleshooting-certificate"></a>

View File

@ -182,7 +182,7 @@ String Comment::AddComment(const Checkable::Ptr& checkable, CommentType entryTyp
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::CreateObject(Comment::TypeInstance, fullName, config, errors)) {
if (!ConfigObjectUtility::CreateObject(Comment::TypeInstance, fullName, config, errors, nullptr)) {
ObjectLock olock(errors);
for (const String& error : errors) {
Log(LogCritical, "Comment", error);
@ -214,7 +214,7 @@ void Comment::RemoveComment(const String& id, const MessageOrigin::Ptr& origin)
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::DeleteObject(comment, false, errors)) {
if (!ConfigObjectUtility::DeleteObject(comment, false, errors, nullptr)) {
ObjectLock olock(errors);
for (const String& error : errors) {
Log(LogCritical, "Comment", error);

View File

@ -255,7 +255,7 @@ String Downtime::AddDowntime(const Checkable::Ptr& checkable, const String& auth
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::CreateObject(Downtime::TypeInstance, fullName, config, errors)) {
if (!ConfigObjectUtility::CreateObject(Downtime::TypeInstance, fullName, config, errors, nullptr)) {
ObjectLock olock(errors);
for (const String& error : errors) {
Log(LogCritical, "Downtime", error);
@ -308,7 +308,7 @@ void Downtime::RemoveDowntime(const String& id, bool cancelled, bool expired, co
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::DeleteObject(downtime, false, errors)) {
if (!ConfigObjectUtility::DeleteObject(downtime, false, errors, nullptr)) {
ObjectLock olock(errors);
for (const String& error : errors) {
Log(LogCritical, "Downtime", error);

View File

@ -62,7 +62,7 @@ bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& reques
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
"No objects found.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return true;
}
} else {
@ -75,6 +75,11 @@ bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& reques
Log(LogNotice, "ApiActionHandler")
<< "Running action " << actionName;
bool verbose = false;
if (params)
verbose = HttpUtility::GetLastParameter(params, "verbose");
for (const ConfigObject::Ptr& obj : objs) {
try {
results.emplace_back(action->Invoke(obj, params));
@ -84,8 +89,9 @@ bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& reques
{ "status", "Action execution failed: '" + DiagnosticInformation(ex, false) + "'." }
});
if (HttpUtility::GetLastParameter(params, "verboseErrors"))
fail->Set("diagnostic information", DiagnosticInformation(ex));
/* Exception for actions. Normally we would handle this inside SendJsonError(). */
if (verbose)
fail->Set("diagnostic_information", DiagnosticInformation(ex));
results.emplace_back(std::move(fail));
}

View File

@ -116,15 +116,14 @@ Value ApiListener::ConfigUpdateObjectAPIHandler(const MessageOrigin::Ptr& origin
/* object does not exist, create it through the API */
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::CreateObject(ptype,
objName, config, errors)) {
if (!ConfigObjectUtility::CreateObject(ptype, objName, config, errors, nullptr)) {
Log(LogCritical, "ApiListener")
<< "Could not create object '" << objName << "':";
ObjectLock olock(errors);
ObjectLock olock(errors);
for (const String& error : errors) {
Log(LogCritical, "ApiListener", error);
}
Log(LogCritical, "ApiListener", error);
}
return Empty;
}
@ -256,7 +255,7 @@ Value ApiListener::ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin
Array::Ptr errors = new Array();
if (!ConfigObjectUtility::DeleteObject(object, true, errors)) {
if (!ConfigObjectUtility::DeleteObject(object, true, errors, nullptr)) {
Log(LogCritical, "ApiListener", "Could not delete object:");
ObjectLock olock(errors);

View File

@ -91,7 +91,7 @@ bool ConfigFilesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
response.WriteBody(content.CStr(), content.GetLength());
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 500, "Could not read file.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
}
return true;

View File

@ -98,7 +98,7 @@ String ConfigObjectUtility::CreateObjectConfig(const Type::Ptr& type, const Stri
}
bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& fullName,
const String& config, const Array::Ptr& errors)
const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation)
{
{
boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex());
@ -121,7 +121,7 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full
Utility::MkDirP(Utility::DirName(path), 0700);
if (Utility::PathExists(path)) {
errors->Add("Configuration file '" + path + "' already exists.");
errors->Add("Cannot create object '" + fullName + "'. Configuration file '" + path + "' already exists.");
return false;
}
@ -151,7 +151,10 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full
}
for (const boost::exception_ptr& ex : upq.GetExceptions()) {
errors->Add(DiagnosticInformation(ex));
errors->Add(DiagnosticInformation(ex, false));
if (diagnosticInformation)
diagnosticInformation->Add(DiagnosticInformation(ex));
}
}
@ -168,7 +171,10 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full
}
if (errors)
errors->Add(DiagnosticInformation(ex));
errors->Add(DiagnosticInformation(ex, false));
if (diagnosticInformation)
diagnosticInformation->Add(DiagnosticInformation(ex));
return false;
}
@ -176,17 +182,21 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full
return true;
}
bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors)
bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade,
const Array::Ptr& errors, const Array::Ptr& diagnosticInformation)
{
std::vector<Object::Ptr> parents = DependencyGraph::GetParents(object);
Type::Ptr type = object->GetReflectionType();
String name = object->GetName();
if (!parents.empty() && !cascade) {
if (errors)
errors->Add("Object '" + object->GetName() + "' of type '" + type->GetName() +
if (errors) {
errors->Add("Object '" + name + "' of type '" + type->GetName() +
"' cannot be deleted because other objects depend on it. "
"Use cascading delete to delete it anyway.");
}
return false;
}
@ -197,10 +207,10 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo
if (!parentObj)
continue;
DeleteObjectHelper(parentObj, cascade, errors);
DeleteObjectHelper(parentObj, cascade, errors, diagnosticInformation);
}
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, object->GetName());
ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, name);
try {
/* mark this object for cluster delete event */
@ -215,12 +225,15 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo
} catch (const std::exception& ex) {
if (errors)
errors->Add(DiagnosticInformation(ex));
errors->Add(DiagnosticInformation(ex, false));
if (diagnosticInformation)
diagnosticInformation->Add(DiagnosticInformation(ex));
return false;
}
String path = GetObjectConfigPath(object->GetReflectionType(), object->GetName());
String path = GetObjectConfigPath(object->GetReflectionType(), name);
if (Utility::PathExists(path)) {
if (unlink(path.CStr()) < 0 && errno != ENOENT) {
@ -234,7 +247,7 @@ bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bo
return true;
}
bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors)
bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation)
{
if (object->GetPackage() != "_api") {
if (errors)
@ -243,5 +256,5 @@ bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cas
return false;
}
return DeleteObjectHelper(object, cascade, errors);
return DeleteObjectHelper(object, cascade, errors, diagnosticInformation);
}

View File

@ -45,13 +45,15 @@ public:
bool ignoreOnError, const Array::Ptr& templates, const Dictionary::Ptr& attrs);
static bool CreateObject(const Type::Ptr& type, const String& fullName,
const String& config, const Array::Ptr& errors);
const String& config, const Array::Ptr& errors, const Array::Ptr& diagnosticInformation);
static bool DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors);
static bool DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors,
const Array::Ptr& diagnosticInformation);
private:
static String EscapeName(const String& name);
static bool DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors);
static bool DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors,
const Array::Ptr& diagnosticInformation);
};
}

View File

@ -54,7 +54,7 @@ void ConfigPackagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& req
packages = ConfigPackageUtility::GetPackages();
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 500, "Could not retrieve packages.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return;
}
@ -89,7 +89,7 @@ void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& re
String packageName = HttpUtility::GetLastParameter(params, "package");
if (!ConfigPackageUtility::ValidateName(packageName)) {
HttpUtility::SendJsonError(response, params, 400, "Invalid package name.");
HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
return;
}
@ -97,13 +97,14 @@ void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& re
boost::mutex::scoped_lock lock(ConfigPackageUtility::GetStaticMutex());
ConfigPackageUtility::CreatePackage(packageName);
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 500, "Could not create package.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
HttpUtility::SendJsonError(response, params, 500, "Could not create package '" + packageName + "'.",
DiagnosticInformation(ex));
return;
}
Dictionary::Ptr result1 = new Dictionary({
{ "code", 200 },
{ "package", packageName },
{ "status", "Created package." }
});
@ -125,31 +126,28 @@ void ConfigPackagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest&
String packageName = HttpUtility::GetLastParameter(params, "package");
if (!ConfigPackageUtility::ValidateName(packageName)) {
HttpUtility::SendJsonError(response, params, 400, "Invalid package name.");
HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
return;
}
int code = 200;
String status = "Deleted package.";
DictionaryData result1;
try {
ConfigPackageUtility::DeletePackage(packageName);
} catch (const std::exception& ex) {
code = 500;
status = "Failed to delete package.";
if (HttpUtility::GetLastParameter(params, "verboseErrors"))
result1.emplace_back("diagnostic information", DiagnosticInformation(ex));
HttpUtility::SendJsonError(response, params, 500, "Failed to delete package '" + packageName + "'.",
DiagnosticInformation(ex));
return;
}
result1.emplace_back("package", packageName);
result1.emplace_back("code", code);
result1.emplace_back("status", status);
Dictionary::Ptr result = new Dictionary({
{ "results", new Array({ new Dictionary(std::move(result1)) }) }
Dictionary::Ptr result1 = new Dictionary({
{ "code", 200 },
{ "package", packageName },
{ "status", "Deleted package." }
});
response.SetStatus(code, (code == 200) ? "OK" : "Internal Server Error");
Dictionary::Ptr result = new Dictionary({
{ "results", new Array({ result1 }) }
});
response.SetStatus(200, "OK");
HttpUtility::SendJsonBody(response, params, result);
}

View File

@ -59,10 +59,10 @@ void ConfigStagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& reque
String stageName = HttpUtility::GetLastParameter(params, "stage");
if (!ConfigPackageUtility::ValidateName(packageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name.");
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
if (!ConfigPackageUtility::ValidateName(stageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name.");
return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name '" + stageName + "'.");
ArrayData results;
@ -95,9 +95,10 @@ void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& requ
String packageName = HttpUtility::GetLastParameter(params, "package");
if (!ConfigPackageUtility::ValidateName(packageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name.");
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
bool reload = true;
if (params->Contains("reload"))
reload = HttpUtility::GetLastParameter(params, "reload");
@ -116,13 +117,17 @@ void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& requ
ConfigPackageUtility::AsyncTryActivateStage(packageName, stageName, reload);
} catch (const std::exception& ex) {
return HttpUtility::SendJsonError(response, params, 500,
"Stage creation failed.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
"Stage creation failed.",
DiagnosticInformation(ex));
}
String responseStatus = "Created stage. ";
responseStatus += (reload ? " Icinga2 will reload." : " Icinga2 reload skipped.");
if (reload)
responseStatus += "Reload triggered.";
else
responseStatus += "Reload skipped.";
Dictionary::Ptr result1 = new Dictionary({
{ "package", packageName },
@ -153,21 +158,23 @@ void ConfigStagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& re
String stageName = HttpUtility::GetLastParameter(params, "stage");
if (!ConfigPackageUtility::ValidateName(packageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name.");
return HttpUtility::SendJsonError(response, params, 400, "Invalid package name '" + packageName + "'.");
if (!ConfigPackageUtility::ValidateName(stageName))
return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name.");
return HttpUtility::SendJsonError(response, params, 400, "Invalid stage name '" + stageName + "'.");
try {
ConfigPackageUtility::DeleteStage(packageName, stageName);
} catch (const std::exception& ex) {
return HttpUtility::SendJsonError(response, params, 500,
"Failed to delete stage.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
"Failed to delete stage '" + stageName + "' in package '" + packageName + "'.",
DiagnosticInformation(ex));
}
Dictionary::Ptr result1 = new Dictionary({
{ "code", 200 },
{ "package", packageName },
{ "stage", stageName },
{ "status", "Stage deleted." }
});

View File

@ -74,6 +74,7 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
Dictionary::Ptr result1 = new Dictionary();
String status;
Array::Ptr errors = new Array();
Array::Ptr diagnosticInformation = new Array();
bool ignoreOnError = false;
@ -86,10 +87,22 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
String config;
bool verbose = false;
if (params)
verbose = HttpUtility::GetLastParameter(params, "verbose");
/* Object creation can cause multiple errors and optionally diagnostic information.
* We can't use SendJsonError() here.
*/
try {
config = ConfigObjectUtility::CreateObjectConfig(type, name, ignoreOnError, templates, attrs);
} catch (const std::exception& ex) {
errors->Add(DiagnosticInformation(ex));
errors->Add(DiagnosticInformation(ex, false));
diagnosticInformation->Add(DiagnosticInformation(ex));
if (verbose)
result1->Set("diagnostic_information", diagnosticInformation);
result1->Set("errors", errors);
result1->Set("code", 500);
@ -101,11 +114,14 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
return true;
}
if (!ConfigObjectUtility::CreateObject(type, name, config, errors)) {
if (!ConfigObjectUtility::CreateObject(type, name, config, errors, diagnosticInformation)) {
result1->Set("errors", errors);
result1->Set("code", 500);
result1->Set("status", "Object could not be created.");
if (verbose)
result1->Set("diagnostic_information", diagnosticInformation);
response.SetStatus(500, "Object could not be created");
HttpUtility::SendJsonBody(response, params, result);

View File

@ -65,11 +65,12 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
"No objects found.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return true;
}
bool cascade = HttpUtility::GetLastParameter(params, "cascade");
bool verbose = HttpUtility::GetLastParameter(params, "verbose");
ArrayData results;
@ -79,8 +80,9 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
int code;
String status;
Array::Ptr errors = new Array();
Array::Ptr diagnosticInformation = new Array();
if (!ConfigObjectUtility::DeleteObject(obj, cascade, errors)) {
if (!ConfigObjectUtility::DeleteObject(obj, cascade, errors, diagnosticInformation)) {
code = 500;
status = "Object could not be deleted.";
success = false;
@ -89,13 +91,18 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
status = "Object was deleted.";
}
results.push_back(new Dictionary({
Dictionary::Ptr result = new Dictionary({
{ "type", type->GetName() },
{ "name", obj->GetName() },
{ "code", code },
{ "status", status },
{ "errors", errors }
}));
});
if (verbose)
result->Set("diagnostic_information", diagnosticInformation);
results.push_back(result);
}
Dictionary::Ptr result = new Dictionary({

View File

@ -89,11 +89,18 @@ void HttpUtility::SendJsonError(HttpResponse& response, const Dictionary::Ptr& p
response.SetStatus(code, HttpUtility::GetErrorNameByCode(code));
result->Set("error", code);
bool verbose = false;
if (params)
verbose = HttpUtility::GetLastParameter(params, "verbose");
if (!info.IsEmpty())
result->Set("status", info);
if (!diagnosticInformation.IsEmpty())
result->Set("diagnostic information", diagnosticInformation);
if (verbose) {
if (!diagnosticInformation.IsEmpty())
result->Set("diagnostic_information", diagnosticInformation);
}
HttpUtility::SendJsonBody(response, params, result);
}

View File

@ -63,7 +63,7 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
"No objects found.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return true;
}
@ -71,12 +71,17 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
if (attrsVal.GetReflectionType() != Dictionary::TypeInstance) {
HttpUtility::SendJsonError(response, params, 400,
"Invalid type for 'attrs' attribute specified. Dictionary type is required.", Empty);
"Invalid type for 'attrs' attribute specified. Dictionary type is required.");
return true;
}
Dictionary::Ptr attrs = attrsVal;
bool verbose = false;
if (params)
verbose = HttpUtility::GetLastParameter(params, "verbose");
ArrayData results;
for (const ConfigObject::Ptr& obj : objs) {
@ -100,7 +105,10 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r
result1->Set("status", "Attributes updated.");
} catch (const std::exception& ex) {
result1->Set("code", 500);
result1->Set("status", "Attribute '" + key + "' could not be set: " + DiagnosticInformation(ex));
result1->Set("status", "Attribute '" + key + "' could not be set: " + DiagnosticInformation(ex, false));
if (verbose)
result1->Set("diagnostic_information", DiagnosticInformation(ex));
}
results.push_back(std::move(result1));

View File

@ -129,7 +129,7 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
uattrs = params->Get("attrs");
} catch (const std::exception&) {
HttpUtility::SendJsonError(response, params, 400,
"Invalid type for 'attrs' attribute specified. Array type is required.", Empty);
"Invalid type for 'attrs' attribute specified. Array type is required.");
return true;
}
@ -137,7 +137,7 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
ujoins = params->Get("joins");
} catch (const std::exception&) {
HttpUtility::SendJsonError(response, params, 400,
"Invalid type for 'joins' attribute specified. Array type is required.", Empty);
"Invalid type for 'joins' attribute specified. Array type is required.");
return true;
}
@ -145,7 +145,7 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
umetas = params->Get("meta");
} catch (const std::exception&) {
HttpUtility::SendJsonError(response, params, 400,
"Invalid type for 'meta' attribute specified. Array type is required.", Empty);
"Invalid type for 'meta' attribute specified. Array type is required.");
return true;
}
@ -166,7 +166,7 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
"No objects found.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return true;
}

View File

@ -104,7 +104,7 @@ bool StatusHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
"No objects found.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return true;
}

View File

@ -127,7 +127,7 @@ bool TemplateQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest&
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
"No templates found.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return true;
}

View File

@ -91,7 +91,7 @@ bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& requ
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
"No objects found.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return true;
}

View File

@ -97,7 +97,7 @@ bool VariableQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest&
} catch (const std::exception& ex) {
HttpUtility::SendJsonError(response, params, 404,
"No variables found.",
HttpUtility::GetLastParameter(params, "verboseErrors") ? DiagnosticInformation(ex) : "");
DiagnosticInformation(ex));
return true;
}