Files
Button/src/responsecheck.cpp
T
2026-08-24 16:40:48 +03:00

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);
}