RESTful Color Palette Server▲
Sélectionnez
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef TYPES_H
#define TYPES_H
#include <QtGui/QColor>
#include <QtCore/QDateTime>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonParseError>
#include <QtCore/QString>
#include <QtCore/qtypes.h>
#include <algorithm>
#include <optional>
struct
Jsonable
{
virtual
QJsonObject toJson() const
=
0
;
virtual
~
Jsonable() =
default
;
}
;
struct
Updatable
{
virtual
bool
update(const
QJsonObject &
amp;json) =
0
;
virtual
void
updateFields(const
QJsonObject &
amp;json) =
0
;
virtual
~
Updatable() =
default
;
}
;
template
&
lt;typename
T&
gt;
struct
FromJsonFactory
{
virtual
std::
optional&
lt;T&
gt; fromJson(const
QJsonObject &
amp;json) const
=
0
;
virtual
~
FromJsonFactory() =
default
;
}
;
struct
User : public
Jsonable, public
Updatable
{
qint64 id;
QString email;
QString firstName;
QString lastName;
QUrl avatarUrl;
QDateTime createdAt;
QDateTime updatedAt;
explicit
User(const
QString &
amp;email, const
QString &
amp;firstName, const
QString &
amp;lastName,
const
QUrl &
amp;avatarUrl,
const
QDateTime &
amp;createdAt =
QDateTime::
currentDateTimeUtc(),
const
QDateTime &
amp;updatedAt =
QDateTime::
currentDateTimeUtc())
:
id(nextId()),
email(email),
firstName(firstName),
lastName(lastName),
avatarUrl(avatarUrl),
createdAt(createdAt),
updatedAt(updatedAt)
{
}
bool
update(const
QJsonObject &
amp;json) override
{
if
(!
json.contains(&
quot;email&
quot;) ||
!
json.contains(&
quot;first_name&
quot;) ||
!
json.contains(&
quot;last_name&
quot;)
||
!
json.contains(&
quot;avatar&
quot;))
return
false
;
email =
json.value(&
quot;email&
quot;).toString();
firstName =
json.value(&
quot;first_name&
quot;).toString();
lastName =
json.value(&
quot;last_name&
quot;).toString();
avatarUrl.setPath(json.value(&
quot;avatar&
quot;).toString());
updateTimestamp();
return
true
;
}
void
updateFields(const
QJsonObject &
amp;json) override
{
if
(json.contains(&
quot;email&
quot;))
email =
json.value(&
quot;email&
quot;).toString();
if
(json.contains(&
quot;first_name&
quot;))
firstName =
json.value(&
quot;first_name&
quot;).toString();
if
(json.contains(&
quot;last_name&
quot;))
lastName =
json.value(&
quot;last_name&
quot;).toString();
if
(json.contains(&
quot;avatar&
quot;))
avatarUrl.setPath(json.value(&
quot;avatar&
quot;).toString());
updateTimestamp();
}
QJsonObject toJson() const
override
{
return
QJsonObject{
{
&
quot;id&
quot;, id }
,
{
&
quot;email&
quot;, email }
,
{
&
quot;first_name&
quot;, firstName }
,
{
&
quot;last_name&
quot;, lastName }
,
{
&
quot;avatar&
quot;, avatarUrl.toString() }
,
{
&
quot;createdAt&
quot;, createdAt.toString(Qt::
ISODateWithMs) }
,
{
&
quot;updatedAt&
quot;, updatedAt.toString(Qt::
ISODateWithMs) }
}
;
}
private
:
void
updateTimestamp() {
updatedAt =
QDateTime::
currentDateTimeUtc(); }
static
qint64 nextId()
{
static
qint64 lastId =
1
;
return
lastId++
;
}
}
;
struct
UserFactory : public
FromJsonFactory&
lt;User&
gt;
{
UserFactory(const
QString &
amp;scheme, const
QString &
amp;hostName, int
port)
:
scheme(scheme), hostName(hostName), port(port)
{
}
std::
optional&
lt;User&
gt; fromJson(const
QJsonObject &
amp;json) const
override
{
if
(!
json.contains(&
quot;email&
quot;) ||
!
json.contains(&
quot;first_name&
quot;) ||
!
json.contains(&
quot;last_name&
quot;)
||
!
json.contains(&
quot;avatar&
quot;)) {
return
std::
nullopt;
}
if
(json.contains(&
quot;createdAt&
quot;) &
amp;&
amp; json.contains(&
quot;updatedAt&
quot;)) {
return
User(
json.value(&
quot;email&
quot;).toString(), json.value(&
quot;first_name&
quot;).toString(),
json.value(&
quot;last_name&
quot;).toString(), json.value(&
quot;avatar&
quot;).toString(),
QDateTime::
fromString(json.value(&
quot;createdAt&
quot;).toString(), Qt::
ISODateWithMs),
QDateTime::
fromString(json.value(&
quot;updatedAt&
quot;).toString(), Qt::
ISODateWithMs));
}
QUrl avatarUrl(json.value(&
quot;avatar&
quot;).toString());
if
(!
avatarUrl.isValid()) {
avatarUrl.setPath(json.value(&
quot;avatar&
quot;).toString());
}
avatarUrl.setScheme(scheme);
avatarUrl.setHost(hostName);
avatarUrl.setPort(port);
return
User(json.value(&
quot;email&
quot;).toString(), json.value(&
quot;first_name&
quot;).toString(),
json.value(&
quot;last_name&
quot;).toString(), avatarUrl);
}
private
:
QString scheme;
QString hostName;
int
port;
}
;
struct
Color : public
Jsonable, public
Updatable
{
qint64 id;
QString name;
QColor color;
QString pantone;
QDateTime createdAt;
QDateTime updatedAt;
explicit
Color(const
QString &
amp;name, const
QString &
amp;color, const
QString &
amp;pantone,
const
QDateTime &
amp;createdAt =
QDateTime::
currentDateTimeUtc(),
const
QDateTime &
amp;updatedAt =
QDateTime::
currentDateTimeUtc())
:
id(nextId()),
name(name),
color(QColor(color)),
pantone(pantone),
createdAt(createdAt),
updatedAt(updatedAt)
{
}
QJsonObject toJson() const
override
{
return
QJsonObject{
{
&
quot;id&
quot;, id }
,
{
&
quot;name&
quot;, name }
,
{
&
quot;color&
quot;, color.name() }
,
{
&
quot;pantone_value&
quot;, pantone }
,
{
&
quot;createdAt&
quot;, createdAt.toString(Qt::
ISODateWithMs) }
,
{
&
quot;updatedAt&
quot;, updatedAt.toString(Qt::
ISODateWithMs) }
}
;
}
bool
update(const
QJsonObject &
amp;json) override
{
if
(!
json.contains(&
quot;name&
quot;) ||
!
json.contains(&
quot;color&
quot;) ||
!
json.contains(&
quot;pantone_value&
quot;))
return
false
;
name =
json.value(&
quot;name&
quot;).toString();
color =
QColor(json.value(&
quot;color&
quot;).toString());
pantone =
json.value(&
quot;pantone_value&
quot;).toString();
updateTimestamp();
return
true
;
}
void
updateFields(const
QJsonObject &
amp;json) override
{
if
(json.contains(&
quot;name&
quot;))
name =
json.value(&
quot;name&
quot;).toString();
if
(json.contains(&
quot;color&
quot;))
color =
QColor(json.value(&
quot;color&
quot;).toString());
if
(json.contains(&
quot;pantone_value&
quot;))
pantone =
json.value(&
quot;pantone_value&
quot;).toString();
updateTimestamp();
}
private
:
void
updateTimestamp() {
updatedAt =
QDateTime::
currentDateTimeUtc(); }
static
qint64 nextId()
{
static
qint64 lastId =
1
;
return
lastId++
;
}
}
;
struct
ColorFactory : public
FromJsonFactory&
lt;Color&
gt;
{
std::
optional&
lt;Color&
gt; fromJson(const
QJsonObject &
amp;json) const
override
{
if
(!
json.contains(&
quot;name&
quot;) ||
!
json.contains(&
quot;color&
quot;) ||
!
json.contains(&
quot;pantone_value&
quot;))
return
std::
nullopt;
if
(json.contains(&
quot;createdAt&
quot;) &
amp;&
amp; json.contains(&
quot;updatedAt&
quot;)) {
return
Color(
json.value(&
quot;name&
quot;).toString(), json.value(&
quot;color&
quot;).toString(),
json.value(&
quot;pantone_value&
quot;).toString(),
QDateTime::
fromString(json.value(&
quot;createdAt&
quot;).toString(), Qt::
ISODateWithMs),
QDateTime::
fromString(json.value(&
quot;updatedAt&
quot;).toString(), Qt::
ISODateWithMs));
}
return
Color(json.value(&
quot;name&
quot;).toString(), json.value(&
quot;color&
quot;).toString(),
json.value(&
quot;pantone_value&
quot;).toString());
}
}
;
struct
SessionEntry : public
Jsonable
{
qint64 id;
QString email;
QString password;
std::
optional&
lt;QUuid&
gt; token;
explicit
SessionEntry(const
QString &
amp;email, const
QString &
amp;password)
:
id(nextId()), email(email), password(password)
{
}
void
startSession() {
token =
generateToken(); }
void
endSession() {
token =
std::
nullopt; }
QJsonObject toJson() const
override
{
return
token
? QJsonObject{
{
&
quot;id&
quot;, id }
,
{
&
quot;token&
quot;, token-&
gt;toString(QUuid::StringFormat::
WithoutBraces) }
}
:
QJsonObject{}
;
}
bool
operator
==
(const
QString &
amp;otherToken) const
{
return
token &
amp;&
amp; *
token ==
QUuid::
fromString(otherToken);
}
private
:
QUuid generateToken() {
return
QUuid::
createUuid(); }
static
qint64 nextId()
{
static
qint64 lastId =
1
;
return
lastId++
;
}
}
;
struct
SessionEntryFactory : public
FromJsonFactory&
lt;SessionEntry&
gt;
{
std::
optional&
lt;SessionEntry&
gt; fromJson(const
QJsonObject &
amp;json) const
override
{
if
(!
json.contains(&
quot;email&
quot;) ||
!
json.contains(&
quot;password&
quot;))
return
std::
nullopt;
return
SessionEntry(json.value(&
quot;email&
quot;).toString(), json.value(&
quot;password&
quot;).toString());
}
}
;
template
&
lt;typename
T&
gt;
class
IdMap : public
QMap&
lt;qint64, T&
gt;
{
public
:
IdMap() =
default
;
explicit
IdMap(const
FromJsonFactory&
lt;T&
gt; &
amp;factory, const
QJsonArray &
amp;array) : QMap&
lt;qint64, T&
gt;()
{
for
(const
auto
&
amp;jsonValue : array) {
if
(jsonValue.isObject()) {
const
auto
maybeT =
factory.fromJson(jsonValue.toObject());
if
(maybeT) {
QMap&
lt;qint64, T&
gt;::
insert(maybeT-&
gt;id, *
maybeT);
}
}
}
}
}
;
template
&
lt;typename
T&
gt;
class
Paginator : public
Jsonable
{
public
:
static
constexpr
qsizetype defaultPage =
1
;
static
constexpr
qsizetype defaultPageSize =
6
;
explicit
Paginator(const
T &
amp;container, qsizetype page, qsizetype size)
{
const
auto
containerSize =
container.size();
const
auto
pageIndex =
page -
1
;
const
auto
pageSize =
qMin(size, containerSize);
const
auto
totalPages =
(containerSize %
pageSize) ==
0
? (containerSize /
pageSize)
:
(containerSize /
pageSize) +
1
;
valid =
containerSize &
gt; (pageIndex *
pageSize);
if
(valid) {
QJsonArray data;
auto
iter =
container.begin();
std::
advance(iter, std::
min(pageIndex *
pageSize, containerSize));
for
(qsizetype i =
0
; i &
lt; pageSize &
amp;&
amp; iter !=
container.end(); ++
i, ++
iter) {
data.push_back(iter-&
gt;toJson());
}
json =
QJsonObject{
{
&
quot;page&
quot;, pageIndex +
1
}
,
{
&
quot;per_page&
quot;, pageSize }
,
{
&
quot;total&
quot;, containerSize }
,
{
&
quot;total_pages&
quot;, totalPages }
,
{
&
quot;data&
quot;, data }
}
;
}
else
{
json =
QJsonObject{}
;
}
}
QJsonObject toJson() const
{
return
json; }
constexpr
bool
isValid() const
{
return
valid; }
private
:
QJsonObject json;
bool
valid;
}
;
#endif
// TYPES_H