RESTful Color Palette Server▲
Sélectionnez
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef APIBEHAVIOR_H
#define APIBEHAVIOR_H
#include "types.h"
#include "utils.h"
#include <QtHttpServer/QHttpServer>
#include <QtConcurrent/qtconcurrentrun.h>
#include <optional>
template
&
lt;typename
T, typename
=
void
&
gt;
class
CrudApi
{
}
;
template
&
lt;typename
T&
gt;
class
CrudApi&
lt;T,
std::
enable_if_t&
lt;std::
conjunction_v&
lt;std::
is_base_of&
lt;Jsonable, T&
gt;,
std::
is_base_of&
lt;Updatable, T&
gt;&
gt;&
gt;&
gt;
{
public
:
explicit
CrudApi(const
IdMap&
lt;T&
gt; &
amp;data, std::
unique_ptr&
lt;FromJsonFactory&
lt;T&
gt;&
gt; factory)
:
data(data), factory(std::
move(factory))
{
}
QFuture&
lt;QHttpServerResponse&
gt; getPaginatedList(const
QHttpServerRequest &
amp;request) const
{
using
PaginatorType =
Paginator&
lt;IdMap&
lt;T&
gt;&
gt;;
std::
optional&
lt;qsizetype&
gt; maybePage;
std::
optional&
lt;qsizetype&
gt; maybePerPage;
std::
optional&
lt;qint64&
gt; maybeDelay;
if
(request.query().hasQueryItem(&
quot;page&
quot;))
maybePage =
request.query().queryItemValue(&
quot;page&
quot;).toLongLong();
if
(request.query().hasQueryItem(&
quot;per_page&
quot;))
maybePerPage =
request.query().queryItemValue(&
quot;per_page&
quot;).toLongLong();
if
(request.query().hasQueryItem(&
quot;delay&
quot;))
maybeDelay =
request.query().queryItemValue(&
quot;delay&
quot;).toLongLong();
if
((maybePage &
amp;&
amp; *
maybePage &
lt; 1
) ||
(maybePerPage &
amp;&
amp; *
maybePerPage &
lt; 1
)) {
return
QtConcurrent::
run([]() {
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
}
);
}
PaginatorType paginator(data, maybePage ? *
maybePage : PaginatorType::
defaultPage,
maybePerPage ? *
maybePerPage : PaginatorType::
defaultPageSize);
return
QtConcurrent::
run([paginator =
std::
move(paginator), maybeDelay]() {
if
(maybeDelay)
QThread::
sleep(*
maybeDelay);
return
paginator.isValid()
? QHttpServerResponse(paginator.toJson())
:
QHttpServerResponse(QHttpServerResponder::StatusCode::
NoContent);
}
);
}
QHttpServerResponse getItem(qint64 itemId) const
{
const
auto
item =
data.find(itemId);
return
item !=
data.end() ? QHttpServerResponse(item-&
gt;toJson())
:
QHttpServerResponse(QHttpServerResponder::StatusCode::
NotFound);
}
QHttpServerResponse postItem(const
QHttpServerRequest &
amp;request)
{
const
std::
optional&
lt;QJsonObject&
gt; json =
byteArrayToJsonObject(request.body());
if
(!
json)
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
const
std::
optional&
lt;T&
gt; item =
factory-&
gt;fromJson(*
json);
if
(!
item)
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
if
(data.contains(item-&
gt;id))
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
AlreadyReported);
const
auto
entry =
data.insert(item-&
gt;id, *
item);
return
QHttpServerResponse(entry-&
gt;toJson(), QHttpServerResponder::StatusCode::
Created);
}
QHttpServerResponse updateItem(qint64 itemId, const
QHttpServerRequest &
amp;request)
{
const
std::
optional&
lt;QJsonObject&
gt; json =
byteArrayToJsonObject(request.body());
if
(!
json)
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
auto
item =
data.find(itemId);
if
(item ==
data.end())
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
NoContent);
if
(!
item-&
gt;update(*
json))
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
return
QHttpServerResponse(item-&
gt;toJson());
}
QHttpServerResponse updateItemFields(qint64 itemId, const
QHttpServerRequest &
amp;request)
{
const
std::
optional&
lt;QJsonObject&
gt; json =
byteArrayToJsonObject(request.body());
if
(!
json)
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
auto
item =
data.find(itemId);
if
(item ==
data.end())
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
NoContent);
item-&
gt;updateFields(*
json);
return
QHttpServerResponse(item-&
gt;toJson());
}
QHttpServerResponse deleteItem(qint64 itemId)
{
if
(!
data.remove(itemId))
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
NoContent);
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
Ok);
}
private
:
IdMap&
lt;T&
gt; data;
std::
unique_ptr&
lt;FromJsonFactory&
lt;T&
gt;&
gt; factory;
}
;
class
SessionApi
{
public
:
explicit
SessionApi(const
IdMap&
lt;SessionEntry&
gt; &
amp;sessions,
std::
unique_ptr&
lt;FromJsonFactory&
lt;SessionEntry&
gt;&
gt; factory)
:
sessions(sessions), factory(std::
move(factory))
{
}
QHttpServerResponse registerSession(const
QHttpServerRequest &
amp;request)
{
const
auto
json =
byteArrayToJsonObject(request.body());
if
(!
json)
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
const
auto
item =
factory-&
gt;fromJson(*
json);
if
(!
item)
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
const
auto
session =
sessions.insert(item-&
gt;id, *
item);
session-&
gt;startSession();
return
QHttpServerResponse(session-&
gt;toJson());
}
QHttpServerResponse login(const
QHttpServerRequest &
amp;request)
{
const
auto
json =
byteArrayToJsonObject(request.body());
if
(!
json ||
!
json-&
gt;contains(&
quot;email&
quot;) ||
!
json-&
gt;contains(&
quot;password&
quot;))
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
auto
maybeSession =
std::
find_if(
sessions.begin(), sessions.end(),
[email =
json-&
gt;value(&
quot;email&
quot;).toString(),
password =
json-&
gt;value(&
quot;password&
quot;).toString()](const
auto
&
amp;it) {
return
it.password ==
password &
amp;&
amp; it.email ==
email;
}
);
if
(maybeSession ==
sessions.end()) {
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
NotFound);
}
maybeSession-&
gt;startSession();
return
QHttpServerResponse(maybeSession-&
gt;toJson());
}
QHttpServerResponse logout(const
QHttpServerRequest &
amp;request)
{
const
auto
maybeToken =
getTokenFromRequest(request);
if
(!
maybeToken)
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
BadRequest);
auto
maybeSession =
std::
find(sessions.begin(), sessions.end(), *
maybeToken);
if
(maybeSession !=
sessions.end())
maybeSession-&
gt;endSession();
return
QHttpServerResponse(QHttpServerResponder::StatusCode::
Ok);
}
bool
authorize(const
QHttpServerRequest &
amp;request) const
{
const
auto
maybeToken =
getTokenFromRequest(request);
if
(maybeToken) {
const
auto
maybeSession =
std::
find(sessions.begin(), sessions.end(), *
maybeToken);
return
maybeSession !=
sessions.end() &
amp;&
amp; *
maybeSession ==
*
maybeToken;
}
return
false
;
}
private
:
IdMap&
lt;SessionEntry&
gt; sessions;
std::
unique_ptr&
lt;FromJsonFactory&
lt;SessionEntry&
gt;&
gt; factory;
}
;
#endif
// APIBEHAVIOR_H