171 lines
6.4 KiB
C++
171 lines
6.4 KiB
C++
#include "responsecheck.h"
|
|
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonValue>
|
|
|
|
namespace {
|
|
|
|
QString jsonValueToString(const QJsonValue &value)
|
|
{
|
|
if (value.isString())
|
|
return value.toString();
|
|
if (value.isDouble())
|
|
return QString::number(value.toDouble());
|
|
if (value.isBool())
|
|
return value.toBool() ? QStringLiteral("true") : QStringLiteral("false");
|
|
if (value.isNull() || value.isUndefined())
|
|
return QString();
|
|
if (value.isArray())
|
|
return QString::fromUtf8(QJsonDocument(value.toArray()).toJson(QJsonDocument::Compact));
|
|
if (value.isObject())
|
|
return QString::fromUtf8(QJsonDocument(value.toObject()).toJson(QJsonDocument::Compact));
|
|
return value.toVariant().toString();
|
|
}
|
|
|
|
QJsonValue valueAtPath(const QJsonValue &root, const QString &dotPath)
|
|
{
|
|
if (dotPath.isEmpty())
|
|
return QJsonValue();
|
|
|
|
const QStringList parts = dotPath.split(QLatin1Char('.'), QString::SkipEmptyParts);
|
|
QJsonValue current = root;
|
|
for (const QString &part : parts) {
|
|
if (!current.isObject())
|
|
return QJsonValue();
|
|
current = current.toObject().value(part);
|
|
}
|
|
return current;
|
|
}
|
|
|
|
QString defaultMessage(ResponseOutcome outcome, const QJsonObject &feedback)
|
|
{
|
|
switch (outcome) {
|
|
case ResponseOutcome::Ok:
|
|
return feedback.value(QStringLiteral("successText")).toString();
|
|
case ResponseOutcome::Pending:
|
|
return feedback.value(QStringLiteral("pendingText")).toString(QStringLiteral("..."));
|
|
case ResponseOutcome::Error:
|
|
default:
|
|
return feedback.value(QStringLiteral("errorText")).toString(QStringLiteral("Ошибка"));
|
|
}
|
|
}
|
|
|
|
ResponseOutcome outcomeFromString(const QString &value)
|
|
{
|
|
const QString v = value.trimmed().toLower();
|
|
if (v == QLatin1String("ok"))
|
|
return ResponseOutcome::Ok;
|
|
if (v == QLatin1String("pending"))
|
|
return ResponseOutcome::Pending;
|
|
return ResponseOutcome::Error;
|
|
}
|
|
|
|
bool conditionMatches(const QJsonObject &condition, const QJsonValue &root)
|
|
{
|
|
const QString field = condition.value(QStringLiteral("field")).toString();
|
|
if (field.isEmpty())
|
|
return false;
|
|
|
|
const QString actual = jsonValueToString(valueAtPath(root, field));
|
|
if (condition.contains(QStringLiteral("equals")))
|
|
return actual == condition.value(QStringLiteral("equals")).toString();
|
|
if (condition.contains(QStringLiteral("contains")))
|
|
return actual.contains(condition.value(QStringLiteral("contains")).toString());
|
|
return false;
|
|
}
|
|
|
|
bool ruleMatches(const QJsonObject &rule, const QJsonValue &root)
|
|
{
|
|
if (rule.contains(QStringLiteral("match"))) {
|
|
const QJsonArray conditions = rule.value(QStringLiteral("match")).toArray();
|
|
if (conditions.isEmpty())
|
|
return false;
|
|
for (const QJsonValue &value : conditions) {
|
|
if (!conditionMatches(value.toObject(), root))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
QJsonObject condition;
|
|
condition.insert(QStringLiteral("field"), rule.value(QStringLiteral("field")));
|
|
if (rule.contains(QStringLiteral("equals")))
|
|
condition.insert(QStringLiteral("equals"), rule.value(QStringLiteral("equals")));
|
|
if (rule.contains(QStringLiteral("contains")))
|
|
condition.insert(QStringLiteral("contains"), rule.value(QStringLiteral("contains")));
|
|
return conditionMatches(condition, root);
|
|
}
|
|
|
|
ResponseEvaluation makeEvaluation(ResponseOutcome outcome,
|
|
const QJsonObject &feedback,
|
|
const QString &message = QString(),
|
|
const QString &matchedRule = QString())
|
|
{
|
|
ResponseEvaluation result;
|
|
result.outcome = outcome;
|
|
result.matchedRule = matchedRule;
|
|
result.message = message.isEmpty() ? defaultMessage(outcome, feedback) : message;
|
|
return result;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ResponseEvaluation evaluateResponse(const QByteArray &body,
|
|
int httpStatus,
|
|
const QJsonObject &responseCheck,
|
|
const QJsonObject &feedbackDefaults)
|
|
{
|
|
if (!responseCheck.value(QStringLiteral("enabled")).toBool(false))
|
|
return ResponseEvaluation();
|
|
|
|
if (responseCheck.value(QStringLiteral("requireHttpSuccess")).toBool(true)
|
|
&& httpStatus >= 400) {
|
|
return makeEvaluation(ResponseOutcome::Error, feedbackDefaults);
|
|
}
|
|
|
|
QJsonParseError parseError;
|
|
const QJsonDocument doc = QJsonDocument::fromJson(body, &parseError);
|
|
if (parseError.error != QJsonParseError::NoError || !doc.isObject()) {
|
|
return makeEvaluation(ResponseOutcome::Error, feedbackDefaults,
|
|
feedbackDefaults.value(QStringLiteral("errorText"))
|
|
.toString(QStringLiteral("Ошибка")));
|
|
}
|
|
|
|
const QJsonValue root = doc.object();
|
|
const QJsonArray rules = responseCheck.value(QStringLiteral("rules")).toArray();
|
|
for (const QJsonValue &value : rules) {
|
|
const QJsonObject rule = value.toObject();
|
|
if (!ruleMatches(rule, root))
|
|
continue;
|
|
|
|
const ResponseOutcome outcome = outcomeFromString(rule.value(QStringLiteral("result")).toString());
|
|
const QString matchedRule = rule.value(QStringLiteral("field")).toString();
|
|
const QString message = rule.value(QStringLiteral("message")).toString();
|
|
return makeEvaluation(outcome, feedbackDefaults, message, matchedRule);
|
|
}
|
|
|
|
const ResponseOutcome fallback = outcomeFromString(
|
|
responseCheck.value(QStringLiteral("defaultResult")).toString(QStringLiteral("error")));
|
|
return makeEvaluation(fallback, feedbackDefaults);
|
|
}
|
|
|
|
ResponseEvaluation evaluateBodyMatch(const QByteArray &body,
|
|
int httpStatus,
|
|
const QString &successMatch,
|
|
const QJsonObject &feedbackDefaults)
|
|
{
|
|
const QString bodyText = QString::fromUtf8(body);
|
|
const QString needle = successMatch.trimmed();
|
|
|
|
bool ok = false;
|
|
if (httpStatus > 0 && httpStatus < 400)
|
|
ok = true;
|
|
if (!needle.isEmpty() && bodyText.contains(needle, Qt::CaseInsensitive))
|
|
ok = true;
|
|
|
|
if (ok)
|
|
return makeEvaluation(ResponseOutcome::Ok, feedbackDefaults, QString(), QStringLiteral("bodyMatch"));
|
|
return makeEvaluation(ResponseOutcome::Error, feedbackDefaults);
|
|
}
|