apibehavior.h Example FileΒΆ

<@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 &quot;types.h&quot;</@preprocessor>
<@preprocessor>#include &quot;utils.h&quot;</@preprocessor>

<@preprocessor>#include &lt;QtHttpServer/QHttpServer&gt;</@preprocessor>
<@preprocessor>#include &lt;QtConcurrent/qtconcurrentrun.h&gt;</@preprocessor>

<@preprocessor>#include &lt;optional&gt;</@preprocessor>

<@keyword>template</@keyword><@op>&lt;</@op><@keyword>typename</@keyword> T<@op>,</@op> <@keyword>typename</@keyword> <@op>=</@op> <@type>void</@type><@op>&gt;</@op>
<@keyword>class</@keyword> CrudApi
{
};

<@keyword>template</@keyword><@op>&lt;</@op><@keyword>typename</@keyword> T<@op>&gt;</@op>
<@keyword>class</@keyword> CrudApi<@op>&lt;</@op>T<@op>,</@op>
              std<@op>::</@op>enable_if_t<@op>&lt;</@op>std<@op>::</@op>conjunction_v<@op>&lt;</@op>std<@op>::</@op>is_base_of<@op>&lt;</@op>Jsonable<@op>,</@op> T<@op>&gt;</@op><@op>,</@op>
                                                  std<@op>::</@op>is_base_of<@op>&lt;</@op>Updatable<@op>,</@op> T<@op>&gt;</@op><@op>&gt;</@op><@op>&gt;</@op><@op>&gt;</@op>
{
<@keyword>public</@keyword>:
    <@keyword>explicit</@keyword> CrudApi(<@keyword>const</@keyword> IdMap<@op>&lt;</@op>T<@op>&gt;</@op> <@op>&amp;</@op>data<@op>,</@op> std<@op>::</@op>unique_ptr<@op>&lt;</@op>FromJsonFactory<@op>&lt;</@op>T<@op>&gt;</@op><@op>&gt;</@op> factory)
        : data(data)<@op>,</@op> factory(std<@op>::</@op>move(factory))
    {
    }

    <@type>QFuture</@type><@op>&lt;</@op><@type>QHttpServerResponse</@type><@op>&gt;</@op> getPaginatedList(<@keyword>const</@keyword> <@type>QHttpServerRequest</@type> <@op>&amp;</@op>request) <@keyword>const</@keyword>
    {
        <@keyword>using</@keyword> PaginatorType <@op>=</@op> Paginator<@op>&lt;</@op>IdMap<@op>&lt;</@op>T<@op>&gt;</@op><@op>&gt;</@op>;
        std<@op>::</@op>optional<@op>&lt;</@op>qsizetype<@op>&gt;</@op> maybePage;
        std<@op>::</@op>optional<@op>&lt;</@op>qsizetype<@op>&gt;</@op> maybePerPage;
        std<@op>::</@op>optional<@op>&lt;</@op><@type>qint64</@type><@op>&gt;</@op> maybeDelay;
        <@keyword>if</@keyword> (request<@op>.</@op>query()<@op>.</@op>hasQueryItem(<@string>&quot;page&quot;</@string>))
            maybePage <@op>=</@op> request<@op>.</@op>query()<@op>.</@op>queryItemValue(<@string>&quot;page&quot;</@string>)<@op>.</@op>toLongLong();
        <@keyword>if</@keyword> (request<@op>.</@op>query()<@op>.</@op>hasQueryItem(<@string>&quot;per_page&quot;</@string>))
            maybePerPage <@op>=</@op> request<@op>.</@op>query()<@op>.</@op>queryItemValue(<@string>&quot;per_page&quot;</@string>)<@op>.</@op>toLongLong();
        <@keyword>if</@keyword> (request<@op>.</@op>query()<@op>.</@op>hasQueryItem(<@string>&quot;delay&quot;</@string>))
            maybeDelay <@op>=</@op> request<@op>.</@op>query()<@op>.</@op>queryItemValue(<@string>&quot;delay&quot;</@string>)<@op>.</@op>toLongLong();

        <@keyword>if</@keyword> ((maybePage <@op>&amp;</@op><@op>&amp;</@op> <@op>*</@op>maybePage <@op>&lt;</@op> <@number>1</@number>) <@op>|</@op><@op>|</@op> (maybePerPage <@op>&amp;</@op><@op>&amp;</@op> <@op>*</@op>maybePerPage <@op>&lt;</@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>&gt;</@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>&amp;</@op>request)
    {
        <@keyword>const</@keyword> std<@op>::</@op>optional<@op>&lt;</@op><@type>QJsonObject</@type><@op>&gt;</@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>&lt;</@op>T<@op>&gt;</@op> item <@op>=</@op> factory<@op>-</@op><@op>&gt;</@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>&gt;</@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>&gt;</@op>id<@op>,</@op> <@op>*</@op>item);
        <@keyword>return</@keyword> <@type>QHttpServerResponse</@type>(entry<@op>-</@op><@op>&gt;</@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>&amp;</@op>request)
    {
        <@keyword>const</@keyword> std<@op>::</@op>optional<@op>&lt;</@op><@type>QJsonObject</@type><@op>&gt;</@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>&gt;</@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>&gt;</@op>toJson());
    }

    <@type>QHttpServerResponse</@type> updateItemFields(<@type>qint64</@type> itemId<@op>,</@op> <@keyword>const</@keyword> <@type>QHttpServerRequest</@type> <@op>&amp;</@op>request)
    {
        <@keyword>const</@keyword> std<@op>::</@op>optional<@op>&lt;</@op><@type>QJsonObject</@type><@op>&gt;</@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>&gt;</@op>updateFields(<@op>*</@op>json);

        <@keyword>return</@keyword> <@type>QHttpServerResponse</@type>(item<@op>-</@op><@op>&gt;</@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>&lt;</@op>T<@op>&gt;</@op> data;
    std<@op>::</@op>unique_ptr<@op>&lt;</@op>FromJsonFactory<@op>&lt;</@op>T<@op>&gt;</@op><@op>&gt;</@op> factory;
};

<@keyword>class</@keyword> SessionApi
{
<@keyword>public</@keyword>:
    <@keyword>explicit</@keyword> SessionApi(<@keyword>const</@keyword> IdMap<@op>&lt;</@op>SessionEntry<@op>&gt;</@op> <@op>&amp;</@op>sessions<@op>,</@op>
                        std<@op>::</@op>unique_ptr<@op>&lt;</@op>FromJsonFactory<@op>&lt;</@op>SessionEntry<@op>&gt;</@op><@op>&gt;</@op> factory)
        : sessions(sessions)<@op>,</@op> factory(std<@op>::</@op>move(factory))
    {
    }

    <@type>QHttpServerResponse</@type> registerSession(<@keyword>const</@keyword> <@type>QHttpServerRequest</@type> <@op>&amp;</@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>&gt;</@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>&gt;</@op>id<@op>,</@op> <@op>*</@op>item);
        session<@op>-</@op><@op>&gt;</@op>startSession();
        <@keyword>return</@keyword> <@type>QHttpServerResponse</@type>(session<@op>-</@op><@op>&gt;</@op>toJson());
    }

    <@type>QHttpServerResponse</@type> login(<@keyword>const</@keyword> <@type>QHttpServerRequest</@type> <@op>&amp;</@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>&gt;</@op>contains(<@string>&quot;email&quot;</@string>) <@op>|</@op><@op>|</@op> <@op>!</@op>json<@op>-</@op><@op>&gt;</@op>contains(<@string>&quot;password&quot;</@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>&gt;</@op>value(<@string>&quot;email&quot;</@string>)<@op>.</@op>toString()<@op>,</@op>
                 password <@op>=</@op> json<@op>-</@op><@op>&gt;</@op>value(<@string>&quot;password&quot;</@string>)<@op>.</@op>toString()<@op>]</@op>(<@keyword>const</@keyword> <@keyword>auto</@keyword> <@op>&amp;</@op>it) {
                    <@keyword>return</@keyword> it<@op>.</@op>password <@op>=</@op><@op>=</@op> password <@op>&amp;</@op><@op>&amp;</@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>&gt;</@op>startSession();
        <@keyword>return</@keyword> <@type>QHttpServerResponse</@type>(maybeSession<@op>-</@op><@op>&gt;</@op>toJson());
    }

    <@type>QHttpServerResponse</@type> logout(<@keyword>const</@keyword> <@type>QHttpServerRequest</@type> <@op>&amp;</@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>&gt;</@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>&amp;</@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>&amp;</@op><@op>&amp;</@op> <@op>*</@op>maybeSession <@op>=</@op><@op>=</@op> <@op>*</@op>maybeToken;
        }
        <@keyword>return</@keyword> <@keyword>false</@keyword>;
    }

<@keyword>private</@keyword>:
    IdMap<@op>&lt;</@op>SessionEntry<@op>&gt;</@op> sessions;
    std<@op>::</@op>unique_ptr<@op>&lt;</@op>FromJsonFactory<@op>&lt;</@op>SessionEntry<@op>&gt;</@op><@op>&gt;</@op> factory;
};

<@preprocessor>#endif // APIBEHAVIOR_H</@preprocessor>