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