cutelyst  5.0.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validator.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2017-2025 Matthias Fehring <mf@huessenbergnetz.de>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include "validator_p.h"
7 
8 #include <Cutelyst/application.h>
9 #include <Cutelyst/context.h>
10 #include <Cutelyst/request.h>
11 
12 #include <QLoggingCategory>
13 
14 using namespace Cutelyst;
15 using namespace Qt::Literals::StringLiterals;
16 
17 Q_LOGGING_CATEGORY(C_VALIDATOR, "cutelyst.utils.validator", QtWarningMsg)
18 
19 Validator::Validator(const char *translationContext)
20  : d_ptr(new ValidatorPrivate(translationContext))
21 {
22 }
23 
24 Validator::Validator(std::initializer_list<ValidatorRule *> validators,
25  const char *translationContext)
26  : d_ptr(new ValidatorPrivate(validators, translationContext))
27 {
28 }
29 
30 Validator::~Validator() = default;
31 
33 {
34  Q_D(Validator);
35  if (!d->validators.empty()) {
36  qDeleteAll(d->validators.begin(), d->validators.end());
37  d->validators.clear();
38  }
39 }
40 
41 Cutelyst::ValidatorResult Validator::validate(Context *c, ValidatorFlags flags) const
42 {
43  ValidatorResult result;
44 
45  Q_ASSERT(c);
46 
47  ParamsMultiMap params;
48  if (flags.testFlag(BodyParamsOnly)) {
49  params = c->req()->bodyParameters();
50  } else if (flags.testFlag(QueryParamsOnly)) {
51  params = c->req()->queryParameters();
52  } else {
53  params = c->req()->queryParameters();
54  params.unite(c->req()->bodyParameters());
55  }
56 
57  result = validate(c, params, flags);
58 
59  return result;
60 }
61 
63  Validator::validate(Context *c, const ParamsMultiMap &params, ValidatorFlags flags) const
64 {
65  ValidatorResult result;
66 
67  Q_ASSERT(c);
68  Q_D(const Validator);
69 
70  if (d->validators.empty()) {
71  qCWarning(C_VALIDATOR) << "Validation started with empty validator list.";
72  return result;
73  }
74 
75  if (params.empty()) {
76  qCWarning(C_VALIDATOR) << "Validation started with empty parameters.";
77  }
78 
79  const bool stopOnFirstError = flags.testFlag(StopOnFirstError);
80  const bool noTrimming = flags.testFlag(NoTrimming);
81 
82  for (ValidatorRule *v : d->validators) {
83 
84  if (noTrimming) {
85  v->setTrimBefore(false);
86  }
87 
88  const ValidatorReturnType singleResult = v->validate(c, params);
89 
90  if (singleResult.extra.isValid()) {
91  result.addExtra(v->field(), singleResult.extra);
92  }
93 
94  if (singleResult) {
95  result.addValue(v->field(), singleResult.value);
96  } else {
97  result.addError(v->field(), singleResult.errorMessage);
98  if (stopOnFirstError) {
99  break;
100  }
101  }
102  }
103 
104  return result;
105 }
106 
107 AwaitedValidatorResult Validator::coValidate(Context *c, ValidatorFlags flags) const
108 {
109  AwaitedValidatorResult avr{c};
110  auto cb = avr.callback;
111 
112  Q_D(const Validator);
113 
114  ParamsMultiMap params;
115  if (flags.testFlag(BodyParamsOnly)) {
116  params = c->req()->bodyParameters();
117  } else if (flags.testFlag(QueryParamsOnly)) {
118  params = c->req()->queryParameters();
119  } else {
120  params = c->req()->queryParameters();
121  params.unite(c->req()->bodyParameters());
122  }
123 
124  auto async = new AsyncValidator{c};
125  QObject::connect(async,
126  &AsyncValidator::finished,
127  [cb](const Cutelyst::ValidatorResult &result) { cb(result); });
128  async->start(d->validators, flags, params);
129 
130  return avr;
131 }
132 
134  Validator::coValidate(Context *c, const ParamsMultiMap &params, ValidatorFlags flags) const
135 {
136  AwaitedValidatorResult avr{c};
137  auto cb = avr.callback;
138 
139  Q_D(const Validator);
140 
141  auto async = new AsyncValidator{c};
142  QObject::connect(async,
143  &AsyncValidator::finished,
144  [cb](const Cutelyst::ValidatorResult &result) { cb(result); });
145  async->start(d->validators, flags, params);
146 
147  return avr;
148 }
149 
151 {
152  Q_D(Validator);
153  v->setTranslationContext(d->translationContext);
154  d->validators.push_back(v);
155 }
156 
158 {
159  app->loadTranslations(u"plugin_utils_validator"_s);
160 }
161 
162 void AsyncValidator::start(const std::vector<ValidatorRule *> &validators,
163  Validator::ValidatorFlags flags,
164  const ParamsMultiMap &params)
165 {
166  if (validators.empty()) {
167  qCWarning(C_VALIDATOR) << "Validation started with empty validator list.";
168  Q_EMIT finished(m_result);
169  return;
170  }
171 
172  if (params.empty()) {
173  qCWarning(C_VALIDATOR) << "Validation started with empty parameters.";
174  }
175 
176  m_params = params;
177 
178  for (auto validator : validators) {
179  m_validators.enqueue(validator);
180  }
181 
182  m_stopOnFirstError = flags.testFlag(Validator::StopOnFirstError);
183  m_noTrimming = flags.testFlag(Validator::NoTrimming);
184 
185  QMetaObject::invokeMethod(this, "doValidation", Qt::DirectConnection);
186 }
187 
188 void AsyncValidator::doValidation()
189 {
190  if (m_validators.empty() || m_cancelValidation) {
191  Q_EMIT finished(m_result);
192  return;
193  }
194 
195  auto v = m_validators.dequeue();
196 
197  if (m_context.isNull()) {
198  qCCritical(C_VALIDATOR)
199  << "Cutelyst context object was destroyed while performing validation";
200  m_result.addError(v->field(),
201  u"Cutelyst context object was destroyed while performing validation."_s);
202  Q_EMIT finished(m_result);
203  return;
204  }
205 
206  v->validateCb(m_context.get(), m_params, [this, v](ValidatorReturnType &&singleResult) {
207  if (singleResult.extra.isValid()) {
208  m_result.addExtra(v->field(), singleResult.extra);
209  }
210 
211  if (singleResult) {
212  m_result.addValue(v->field(), singleResult.value);
213  } else {
214  m_result.addError(v->field(), singleResult.errorMessage);
215  if (m_stopOnFirstError) {
216  m_cancelValidation = true;
217  }
218  }
219  doValidation();
220  });
221 }
ValidatorResult validate(Context *c, ValidatorFlags flags=NoSpecialBehavior) const
Definition: validator.cpp:41
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
Validator(const char *translationContext=nullptr)
Definition: validator.cpp:19
Request req
Definition: context.h:67
ParamsMultiMap bodyParameters() const
Definition: request.cpp:219
The Cutelyst Context.
Definition: context.h:42
Coroutine awaitable for ValidatorResult.
AwaitedValidatorResult coValidate(Context *c, ValidatorFlags flags=NoSpecialBehavior) const
Definition: validator.cpp:107
The Cutelyst namespace holds all public Cutelyst API.
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
Base class for all validator rules.
Validation processor for input data.
Definition: validator.h:308
void addError(const QString &field, const QString &message)
bool empty() const const
QString field() const noexcept
void addValue(const QString &field, const QVariant &value)
virtual ValidatorReturnType validate(Context *c, const ParamsMultiMap &params) const =0
ParamsMultiMap queryParameters() const
Definition: request.cpp:255
void addValidator(ValidatorRule *v)
Definition: validator.cpp:150
static void loadTranslations(Application *app)
Definition: validator.cpp:157
bool isValid() const const
The Cutelyst application.
Definition: application.h:72
Contains the result of a single input parameter validation.
Definition: validatorrule.h:52
Provides information about performed validations.
void addExtra(const QString &field, const QVariant &extra)
DirectConnection
QMultiMap< Key, T > & unite(QMultiMap< Key, T > &&other)
void loadTranslations(const QString &filename, const QString &directory={}, const QString &prefix={}, const QString &suffix={})