~aleteoryx/muditaos

9832365ca21b506bfe552b922bb1c154eb531564 — Mateusz Grzegorzek 5 years ago a05bf00
[EGD-5698] Add pagination for messages

Add pagination for messages
M module-db/Interface/SMSRecord.cpp => module-db/Interface/SMSRecord.cpp +38 -7
@@ 308,6 308,9 @@ std::unique_ptr<db::QueryResult> SMSRecordInterface::runQuery(std::shared_ptr<db
    else if (typeid(*query) == typeid(db::query::SMSGetByThreadID)) {
        return getByThreadIDQuery(query);
    }
    else if (typeid(*query) == typeid(db::query::SMSGetByThreadIDWithTotalCount)) {
        return getByThreadIDQueryWithTotalCount(query);
    }
    else if (typeid(*query) == typeid(db::query::SMSGetForList)) {
        return getForListQuery(query);
    }


@@ 335,6 338,9 @@ std::unique_ptr<db::QueryResult> SMSRecordInterface::runQuery(std::shared_ptr<db
    else if (typeid(*query) == typeid(db::query::SMSGet)) {
        return getQuery(query);
    }
    else if (typeid(*query) == typeid(db::query::SMSGetWithTotalCount)) {
        return getQueryWithTotalCount(query);
    }

    return nullptr;
}


@@ 455,24 461,49 @@ std::unique_ptr<db::QueryResult> SMSRecordInterface::updateQuery(const std::shar
    response->setRequestQuery(query);
    return response;
}
std::unique_ptr<db::QueryResult> SMSRecordInterface::getQuery(const std::shared_ptr<db::Query> &query)

auto SMSRecordInterface::getQueryRecords(const std::shared_ptr<db::Query> &query) -> std::vector<SMSRecord>
{
    const auto localQuery = static_cast<const db::query::SMSGet *>(query.get());

    auto smsVector    = smsDB->sms.getLimitOffset(localQuery->getOffset(), localQuery->getLimit());
    auto recordVector = std::vector<SMSRecord>(smsVector.begin(), smsVector.end());
    return std::vector<SMSRecord>(smsVector.begin(), smsVector.end());
}

    auto response = std::make_unique<db::query::SMSGetResult>(recordVector);
auto SMSRecordInterface::getQuery(const std::shared_ptr<db::Query> &query) -> std::unique_ptr<db::QueryResult>
{
    auto response = std::make_unique<db::query::SMSGetResult>(getQueryRecords(query));
    response->setRequestQuery(query);
    return response;
}
std::unique_ptr<db::QueryResult> SMSRecordInterface::getByThreadIDQuery(const std::shared_ptr<db::Query> &query)

auto SMSRecordInterface::getQueryWithTotalCount(const std::shared_ptr<db::Query> &query)
    -> std::unique_ptr<db::QueryResult>
{
    auto response = std::make_unique<db::query::SMSGetResultWithTotalCount>(getQueryRecords(query), smsDB->sms.count());
    response->setRequestQuery(query);
    return response;
}

auto SMSRecordInterface::getByThreadIDQueryRecords(const std::shared_ptr<db::Query> &query) -> std::vector<SMSRecord>
{
    const auto localQuery = static_cast<const db::query::SMSGetByThreadID *>(query.get());
    auto smsVector        = smsDB->sms.getByThreadId(localQuery->threadId, localQuery->offset, localQuery->limit);
    auto recordVector     = std::vector<SMSRecord>(smsVector.begin(), smsVector.end());
    return std::vector<SMSRecord>(smsVector.begin(), smsVector.end());
}

auto SMSRecordInterface::getByThreadIDQuery(const std::shared_ptr<db::Query> &query) -> std::unique_ptr<db::QueryResult>
{
    auto response = std::make_unique<db::query::SMSGetByThreadIDResult>(getByThreadIDQueryRecords(query));
    response->setRequestQuery(query);
    return response;
}

    auto response = std::make_unique<db::query::SMSGetByThreadIDResult>(recordVector);
auto SMSRecordInterface::getByThreadIDQueryWithTotalCount(const std::shared_ptr<db::Query> &query)
    -> std::unique_ptr<db::QueryResult>
{
    const auto smsGetByThreadIDQuery = static_cast<const db::query::SMSGetByThreadID *>(query.get());
    auto response                    = std::make_unique<db::query::SMSGetByThreadIDResultWithTotalCount>(
        getByThreadIDQueryRecords(query), smsDB->sms.countWithoutDraftsByThreadId(smsGetByThreadIDQuery->threadId));
    response->setRequestQuery(query);
    return response;
}

M module-db/Interface/SMSRecord.hpp => module-db/Interface/SMSRecord.hpp +5 -1
@@ 92,7 92,11 @@ class SMSRecordInterface : public RecordInterface<SMSRecord, SMSRecordField>
    std::unique_ptr<db::QueryResult> removeQuery(const std::shared_ptr<db::Query> &query);
    std::unique_ptr<db::QueryResult> updateQuery(const std::shared_ptr<db::Query> &query);
    std::unique_ptr<db::QueryResult> getQuery(const std::shared_ptr<db::Query> &query);
    std::unique_ptr<db::QueryResult> getByThreadIDQuery(const std::shared_ptr<db::Query> &query);
    auto getQueryRecords(const std::shared_ptr<db::Query> &query) -> std::vector<SMSRecord>;
    auto getQueryWithTotalCount(const std::shared_ptr<db::Query> &query) -> std::unique_ptr<db::QueryResult>;
    auto getByThreadIDQueryRecords(const std::shared_ptr<db::Query> &query) -> std::vector<SMSRecord>;
    auto getByThreadIDQuery(const std::shared_ptr<db::Query> &query) -> std::unique_ptr<db::QueryResult>;
    auto getByThreadIDQueryWithTotalCount(const std::shared_ptr<db::Query> &query) -> std::unique_ptr<db::QueryResult>;
    std::unique_ptr<db::QueryResult> getForListQuery(const std::shared_ptr<db::Query> &query);
    std::unique_ptr<db::QueryResult> getCountByThreadIDQuery(const std::shared_ptr<db::Query> &query);
    std::unique_ptr<db::QueryResult> getLastByThreadIDQuery(const std::shared_ptr<db::Query> &query);

M module-db/queries/messages/sms/QuerySMSGet.cpp => module-db/queries/messages/sms/QuerySMSGet.cpp +25 -3
@@ 10,15 10,37 @@ using namespace db::query;
SMSGet::SMSGet(std::size_t limit, std::size_t offset) : RecordQuery(limit, offset)
{}

SMSGetResult::SMSGetResult(const std::vector<SMSRecord> &records) : RecordQueryResult(records)
{}

[[nodiscard]] auto SMSGet::debugInfo() const -> std::string
{
    return "SMSGet";
}

SMSGetWithTotalCount::SMSGetWithTotalCount(std::size_t limit, std::size_t offset) : SMSGet(limit, offset)
{}

[[nodiscard]] auto SMSGetWithTotalCount::debugInfo() const -> std::string
{
    return "SMSGetWithTotalCount";
}

SMSGetResult::SMSGetResult(std::vector<SMSRecord> records) : RecordQueryResult(std::move(records))
{}

[[nodiscard]] auto SMSGetResult::debugInfo() const -> std::string
{
    return "SMSGetResult";
}

SMSGetResultWithTotalCount::SMSGetResultWithTotalCount(std::vector<SMSRecord> records, std::size_t totalCount)
    : SMSGetResult(std::move(records)), totalCount(totalCount)
{}

auto SMSGetResultWithTotalCount::getTotalCount() const -> std::size_t
{
    return totalCount;
}

[[nodiscard]] auto SMSGetResultWithTotalCount::debugInfo() const -> std::string
{
    return "SMSGetResultWithTotalCount";
}

M module-db/queries/messages/sms/QuerySMSGet.hpp => module-db/queries/messages/sms/QuerySMSGet.hpp +20 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 18,11 18,29 @@ namespace db::query
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class SMSGetWithTotalCount : public SMSGet
    {
      public:
        SMSGetWithTotalCount(std::size_t limit, std::size_t offset);
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class SMSGetResult : public RecordQueryResult<SMSRecord>
    {
      public:
        SMSGetResult(const std::vector<SMSRecord> &records);
        SMSGetResult(std::vector<SMSRecord> records);
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class SMSGetResultWithTotalCount : public SMSGetResult
    {
      public:
        SMSGetResultWithTotalCount(std::vector<SMSRecord> records, std::size_t totalCount);
        [[nodiscard]] auto debugInfo() const -> std::string override;
        auto getTotalCount() const -> std::size_t;

      private:
        std::size_t totalCount;
    };

}; // namespace db::query

M module-db/queries/messages/sms/QuerySMSGetByThreadID.cpp => module-db/queries/messages/sms/QuerySMSGetByThreadID.cpp +30 -2
@@ 5,8 5,8 @@

namespace db::query
{
    SMSGetByThreadID::SMSGetByThreadID(unsigned int threadId, unsigned int offset, unsigned int limit)
        : Query(Query::Type::Read), threadId(threadId), offset(offset), limit(limit)
    SMSGetByThreadID::SMSGetByThreadID(unsigned int threadId, unsigned int limit, unsigned int offset)
        : Query(Query::Type::Read), threadId(threadId), limit(limit), offset(offset)
    {}

    auto SMSGetByThreadID::debugInfo() const -> std::string


@@ 14,15 14,43 @@ namespace db::query
        return "SMSGetByThreadID";
    }

    SMSGetByThreadIDWithTotalCount::SMSGetByThreadIDWithTotalCount(unsigned int threadId,
                                                                   std::size_t limit,
                                                                   std::size_t offset)
        : SMSGetByThreadID(threadId, limit, offset)
    {}

    [[nodiscard]] auto SMSGetByThreadIDWithTotalCount::debugInfo() const -> std::string
    {
        return "SMSGetByThreadIDWithTotalCount";
    }

    SMSGetByThreadIDResult::SMSGetByThreadIDResult(std::vector<SMSRecord> result) : result(std::move(result))
    {}

    auto SMSGetByThreadIDResult::getResults() const -> std::vector<SMSRecord>
    {
        return result;
    }

    auto SMSGetByThreadIDResult::debugInfo() const -> std::string
    {
        return "SMSGetByThreadIDResult";
    }

    SMSGetByThreadIDResultWithTotalCount::SMSGetByThreadIDResultWithTotalCount(std::vector<SMSRecord> records,
                                                                               std::size_t totalCount)
        : SMSGetByThreadIDResult(std::move(records)), totalCount(totalCount)
    {}

    auto SMSGetByThreadIDResultWithTotalCount::getTotalCount() const -> std::size_t
    {
        return totalCount;
    }

    auto SMSGetByThreadIDResultWithTotalCount::debugInfo() const -> std::string
    {
        return "SMSGetByThreadIDResultWithTotalCount";
    }

} // namespace db::query

M module-db/queries/messages/sms/QuerySMSGetByThreadID.hpp => module-db/queries/messages/sms/QuerySMSGetByThreadID.hpp +20 -2
@@ 15,10 15,17 @@ namespace db::query
    {
      public:
        unsigned int threadId;
        unsigned int offset;
        unsigned int limit;
        unsigned int offset;

        SMSGetByThreadID(unsigned int id, unsigned int offset = 0, unsigned int limit = 0);
        explicit SMSGetByThreadID(unsigned int id, unsigned int limit = 0, unsigned int offset = 0);
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class SMSGetByThreadIDWithTotalCount : public SMSGetByThreadID
    {
      public:
        SMSGetByThreadIDWithTotalCount(unsigned int threadId, std::size_t limit, std::size_t offset);
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };



@@ 32,4 39,15 @@ namespace db::query
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class SMSGetByThreadIDResultWithTotalCount : public SMSGetByThreadIDResult
    {
      public:
        SMSGetByThreadIDResultWithTotalCount(std::vector<SMSRecord> records, std::size_t totalCount);
        [[nodiscard]] auto debugInfo() const -> std::string override;
        auto getTotalCount() const -> std::size_t;

      private:
        std::size_t totalCount;
    };

} // namespace db::query

M module-services/service-desktop/endpoints/Context.hpp => module-services/service-desktop/endpoints/Context.hpp +2 -2
@@ 185,7 185,7 @@ namespace parserFSM
    {
        inline constexpr std::size_t contactsPageSize = 10;
        inline constexpr std::size_t calendarEventsPageSize = 10;
        inline constexpr std::size_t threadsPageSize        = 11;
        inline constexpr std::size_t messagesPageSize       = 4;
    }

    class ContextFactory


@@ 201,7 201,7 @@ namespace parserFSM
            case EndpointType::contacts:
                return std::make_unique<PagedContext>(js, endpoint_pageing::contactsPageSize);
            case EndpointType::messages:
                return std::make_unique<PagedContext>(js, endpoint_pageing::threadsPageSize);
                return std::make_unique<PagedContext>(js, endpoint_pageing::messagesPageSize);
            default:
                return std::make_unique<Context>(js);
            }

M module-services/service-desktop/endpoints/messages/MessageHelper.cpp => module-services/service-desktop/endpoints/messages/MessageHelper.cpp +267 -227
@@ 1,4 1,4 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "MessageHelper.hpp"


@@ 147,158 147,20 @@ namespace parserFSM

    auto MessageHelper::requestSMS(Context &context) -> sys::ReturnCodes
    {
        if (context.getBody()[json::messages::count].bool_value()) // get messages count
        {
            auto query = std::make_unique<db::query::SMSGetCount>();

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetCountResult *>(result)) {
                        auto id = smsResult->getResults();

                        context.setResponseBody(json11::Json::object{{json::messages::count, static_cast<int>(id)}});
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        if (context.getBody()[json::messages::count].bool_value()) {
            requestCount(context);
        }
        else if (context.getBody()[json::messages::messageID].int_value() != 0) { // get message by ID

            auto query =
                std::make_unique<db::query::SMSGetByID>(context.getBody()[json::messages::messageID].int_value());

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetByIDResult *>(result)) {

                        context.setResponseBody(MessageHelper::toJson(smsResult->getResults()));
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        else if (context.getBody()[json::messages::messageID].int_value() != 0) {
            getMessageById(context);
        }
        else if (context.getBody()[json::messages::threadID].int_value() != 0) { // get messages by thread ID

            auto query =
                std::make_unique<db::query::SMSGetByThreadID>(context.getBody()[json::messages::threadID].int_value(),
                                                              context.getBody()[json::messages::offset].int_value(),
                                                              context.getBody()[json::messages::limit].is_number()
                                                                  ? context.getBody()[json::messages::limit].int_value()
                                                                  : defaultLimit);

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetByThreadIDResult *>(result)) {

                        json11::Json::array smsArray;
                        for (const auto &record : smsResult->getResults()) {
                            smsArray.emplace_back(MessageHelper::toJson(record));
                        }

                        auto responseBody = json11::Json::object{
                            {json::messages::totalCount, 0},
                            {json::messages::nextPage,
                             json11::Json::object{{json::messages::offset, 0}, {json::messages::limit, 0}}},
                            {json::messages::entries, smsArray},
                        };

                        context.setResponseBody(responseBody);
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        else if (context.getBody()[json::messages::threadID].int_value() != 0) {
            return getMessagesByThreadID(context);
        }
        else if (!context.getBody()[json::messages::messageBody].string_value().empty()) // get by message body
        {
            // not adding pagination for this request, since it is just for development and testing purposes, and it's
            // not going to be used by Mudita Center
            auto query = std::make_unique<db::query::SMSGetByText>(
                context.getBody()[json::messages::messageBody].string_value());
            if (const auto filterByNumber = !context.getBody()[json::messages::phoneNumber].string_value().empty();
                filterByNumber) {
                utils::PhoneNumber number{context.getBody()[json::messages::phoneNumber].string_value()};
                query->filterByPhoneNumber(number.getView());
            }

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetByTextResult *>(result)) {

                        json11::Json::array smsArray;
                        for (const auto &record : smsResult->getResults()) {
                            smsArray.emplace_back(MessageHelper::toJson(record));
                        }

                        context.setResponseBody(smsArray);
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        else if (!context.getBody()[json::messages::messageBody].string_value().empty()) {
            getByMessageBody(context);
        }
        else // get messages
        {
            auto query = std::make_unique<db::query::SMSGet>(context.getBody()[json::messages::limit].is_number()
                                                                 ? context.getBody()[json::messages::limit].int_value()
                                                                 : defaultLimit,
                                                             context.getBody()[json::messages::offset].int_value());

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetResult *>(result)) {

                        json11::Json::array smsArray;
                        for (const auto &record : smsResult->getRecords()) {
                            smsArray.emplace_back(MessageHelper::toJson(record));
                            LOG_DEBUG("Record found!: %" PRIu32 "\n", record.ID);
                        }

                        auto responseBody = json11::Json::object{
                            {json::messages::totalCount, 0},
                            {json::messages::nextPage,
                             json11::Json::object{{json::messages::offset, 0}, {json::messages::limit, 0}}},
                            {json::messages::entries, smsArray},
                        };

                        context.setResponseBody(responseBody);
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        else {
            return getMessages(context);
        }
        return sys::ReturnCodes::Success;
    }


@@ 341,86 203,15 @@ namespace parserFSM

    auto MessageHelper::requestTemplate(Context &context) -> sys::ReturnCodes
    {
        if (context.getBody()[json::messages::count].bool_value()) // get templates count
        {
            auto query = std::make_unique<db::query::SMSTemplateGetCount>();

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSTemplateGetCountResult *>(result)) {
                        auto id = smsResult->getResults();

                        context.setResponseBody(json11::Json::object{{json::messages::count, static_cast<int>(id)}});
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        if (context.getBody()[json::messages::count].bool_value()) {
            getTemplatesCount(context);
        }
        else if (context.getBody()[json::messages::templateID].int_value() != 0) { // get template by ID
            auto query = std::make_unique<db::query::SMSTemplateGetByID>(
                context.getBody()[json::messages::templateID].int_value());

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateGetByIDResult *>(result)) {

                        context.setResponseBody(MessageHelper::toJson(smsTemplateResult->getResults()));
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        else if (context.getBody()[json::messages::templateID].int_value() != 0) {
            getTemplateByID(context);
        }
        else // get messages templates
        {
            auto query =
                std::make_unique<db::query::SMSTemplateGet>(context.getBody()[json::messages::offset].int_value(),
                                                            context.getBody()[json::messages::limit].is_number()
                                                                ? context.getBody()[json::messages::limit].int_value()
                                                                : defaultLimit);
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateGetResult *>(result)) {

                        json11::Json::array smsTemplateArray;
                        for (const auto &record : smsTemplateResult->getResults()) {
                            smsTemplateArray.emplace_back(toJson(record));
                        }

                        auto responseBody = json11::Json::object{
                            {json::messages::totalCount, 0},
                            {json::messages::nextPage,
                             json11::Json::object{{json::messages::offset, 0}, {json::messages::limit, 0}}},
                            {json::messages::entries, smsTemplateArray},
                        };

                        context.setResponseBody(responseBody);
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                context);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        else {
            getMessagesTemplates(context);
        }

        return sys::ReturnCodes::Success;
    }
    auto MessageHelper::updateTemplate(Context &context) -> sys::ReturnCodes


@@ 559,7 350,6 @@ namespace parserFSM
                    }
                },
                ctx);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSThread, std::move(query));
        }


@@ 619,4 409,254 @@ namespace parserFSM
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Success;
    }

    void MessageHelper::requestCount(Context &context)
    {
        auto query = std::make_unique<db::query::SMSGetCount>();

        auto listener = std::make_unique<db::EndpointListener>(
            [](db::QueryResult *result, Context &context) {
                if (auto smsResult = dynamic_cast<db::query::SMSGetCountResult *>(result)) {
                    auto count = smsResult->getResults();
                    context.setResponseBody(json11::Json::object{{json::messages::count, static_cast<int>(count)}});
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
    }

    void MessageHelper::getMessageById(Context &context)
    {
        auto query = std::make_unique<db::query::SMSGetByID>(context.getBody()[json::messages::messageID].int_value());

        auto listener = std::make_unique<db::EndpointListener>(
            [](db::QueryResult *result, Context &context) {
                if (auto smsResult = dynamic_cast<db::query::SMSGetByIDResult *>(result)) {

                    context.setResponseBody(MessageHelper::toJson(smsResult->getResults()));
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
    }

    auto MessageHelper::getMessagesByThreadID(Context &context) -> sys::ReturnCodes
    {
        try {
            auto &ctx                = dynamic_cast<PagedContext &>(context);
            const std::size_t limit  = ctx.getBody()[json::messages::limit].int_value();
            const std::size_t offset = ctx.getBody()[json::messages::offset].int_value();
            ctx.setRequestedLimit(limit);
            ctx.setRequestedOffset(offset);
            auto query = std::make_unique<db::query::SMSGetByThreadIDWithTotalCount>(
                context.getBody()[json::messages::threadID].int_value(), std::min(ctx.getPageSize(), limit), offset);

            auto listener = std::make_unique<db::EndpointListenerWithPages>(
                [](db::QueryResult *result, PagedContext &context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetByThreadIDResultWithTotalCount *>(result)) {

                        const auto &results = smsResult->getResults();
                        json11::Json::array smsArray;
                        smsArray.reserve(results.size());
                        for (const auto &record : results) {
                            smsArray.emplace_back(MessageHelper::toJson(record));
                        }

                        context.setResponseBody(std::move(smsArray));
                        context.setTotalCount(smsResult->getTotalCount());
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                ctx);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        }
        catch (const std::bad_cast &e) {
            LOG_ERROR("%s", e.what());
            return sys::ReturnCodes::Failure;
        }
        return sys::ReturnCodes::Success;
    }

    void MessageHelper::getByMessageBody(Context &context)
    {
        // not adding pagination for this request, since it is just for development and testing purposes, and it's
        // not going to be used by Mudita Center
        auto query =
            std::make_unique<db::query::SMSGetByText>(context.getBody()[json::messages::messageBody].string_value());
        if (const auto filterByNumber = !context.getBody()[json::messages::phoneNumber].string_value().empty();
            filterByNumber) {
            utils::PhoneNumber number{context.getBody()[json::messages::phoneNumber].string_value()};
            query->filterByPhoneNumber(number.getView());
        }

        auto listener = std::make_unique<db::EndpointListener>(
            [](db::QueryResult *result, Context &context) {
                if (auto smsResult = dynamic_cast<db::query::SMSGetByTextResult *>(result)) {

                    const auto &results = smsResult->getResults();
                    json11::Json::array smsArray;
                    smsArray.reserve(results.size());
                    for (const auto &record : results) {
                        smsArray.emplace_back(MessageHelper::toJson(record));
                    }

                    context.setResponseBody(std::move(smsArray));
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
    }

    auto MessageHelper::getMessages(Context &context) -> sys::ReturnCodes
    {
        try {
            auto &ctx                = dynamic_cast<PagedContext &>(context);
            const std::size_t limit  = ctx.getBody()[json::messages::limit].int_value();
            const std::size_t offset = ctx.getBody()[json::messages::offset].int_value();
            ctx.setRequestedLimit(limit);
            ctx.setRequestedOffset(offset);
            auto query = std::make_unique<db::query::SMSGetWithTotalCount>(std::min(ctx.getPageSize(), limit), offset);

            auto listener = std::make_unique<db::EndpointListenerWithPages>(
                [](db::QueryResult *result, PagedContext &context) {
                    if (auto smsResult = dynamic_cast<db::query::SMSGetResultWithTotalCount *>(result)) {

                        const auto &results = smsResult->getRecords();
                        json11::Json::array smsArray;
                        smsArray.reserve(results.size());
                        for (const auto &record : results) {
                            smsArray.emplace_back(MessageHelper::toJson(record));
                            LOG_DEBUG("Record found!: %" PRIu32 "\n", record.ID);
                        }

                        context.setResponseBody(std::move(smsArray));
                        context.setTotalCount(smsResult->getTotalCount());
                        MessageHandler::putToSendQueue(context.createSimpleResponse());
                        return true;
                    }
                    else {
                        return false;
                    }
                },
                ctx);

            query->setQueryListener(std::move(listener));
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMS, std::move(query));
        }
        catch (const std::bad_cast &e) {
            LOG_ERROR("%s", e.what());
            return sys::ReturnCodes::Failure;
        }
        return sys::ReturnCodes::Success;
    }

    void MessageHelper::getTemplatesCount(Context &context)
    {
        auto query = std::make_unique<db::query::SMSTemplateGetCount>();

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto smsResult = dynamic_cast<db::query::SMSTemplateGetCountResult *>(result)) {
                    auto id = smsResult->getResults();

                    context.setResponseBody(json11::Json::object{{json::messages::count, static_cast<int>(id)}});
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
    }

    void MessageHelper::getTemplateByID(Context &context)
    {
        auto query =
            std::make_unique<db::query::SMSTemplateGetByID>(context.getBody()[json::messages::templateID].int_value());

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateGetByIDResult *>(result)) {

                    context.setResponseBody(MessageHelper::toJson(smsTemplateResult->getResults()));
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
    }

    auto MessageHelper::getMessagesTemplates(Context &context) -> sys::ReturnCodes
    {
        auto query = std::make_unique<db::query::SMSTemplateGet>(
            context.getBody()[json::messages::offset].int_value(),
            context.getBody()[json::messages::limit].is_number() ? context.getBody()[json::messages::limit].int_value()
                                                                 : defaultLimit);
        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {
                if (auto smsTemplateResult = dynamic_cast<db::query::SMSTemplateGetResult *>(result)) {

                    json11::Json::array smsTemplateArray;
                    for (const auto &record : smsTemplateResult->getResults()) {
                        smsTemplateArray.emplace_back(toJson(record));
                    }

                    auto responseBody = json11::Json::object{
                        {json::messages::totalCount, 0},
                        {json::messages::nextPage,
                         json11::Json::object{{json::messages::offset, 0}, {json::messages::limit, 0}}},
                        {json::messages::entries, smsTemplateArray},
                    };

                    context.setResponseBody(responseBody);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                else {
                    return false;
                }
            },
            context);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::SMSTemplate, std::move(query));
        return sys::ReturnCodes::Success;
    }
} // namespace parserFSM

M module-services/service-desktop/endpoints/messages/MessageHelper.hpp => module-services/service-desktop/endpoints/messages/MessageHelper.hpp +10 -0
@@ 52,6 52,16 @@ namespace parserFSM
        auto updateTemplate(Context &context) -> sys::ReturnCodes;
        auto deleteTemplate(Context &context) -> sys::ReturnCodes;

        void requestCount(Context &context);
        void getMessageById(Context &context);
        auto getMessagesByThreadID(Context &context) -> sys::ReturnCodes;
        void getByMessageBody(Context &context);
        auto getMessages(Context &context) -> sys::ReturnCodes;

        void getTemplatesCount(Context &context);
        void getTemplateByID(Context &context);
        auto getMessagesTemplates(Context &context) -> sys::ReturnCodes;

        json11::Json receivedJson;

        const int defaultLimit = 100; // will be removed after introducing pagination

M test/pytest/service-desktop/test_messages.py => test/pytest/service-desktop/test_messages.py +153 -17
@@ 5,55 5,191 @@ import pytest
from harness.interface.defs import status


MESSAGES_PAGE_SIZE = 4


@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_messages(harness):
    # getting the messages count
def test_get_messages_without_pagination(harness):
    limit = MESSAGES_PAGE_SIZE - 1
    get_body = {"category": "message", "limit": limit, "offset": 0}
    ret = harness.endpoint_request("messages", "get", get_body)
    assert ret["status"] == status["OK"]

    messages = ret["body"]["entries"]
    messages_count = len(messages)
    assert messages_count == limit


@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_get_messages_with_pagination(harness):
    limit = MESSAGES_PAGE_SIZE + 1
    body = {"category": "message", "limit": limit, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    messages = ret["body"]["entries"]
    messages_count = len(messages)
    assert messages_count == MESSAGES_PAGE_SIZE


@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_get_all_messages(harness):
    # get messages count
    body = {"category": "message", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    count = ret["body"]["count"]
    if count == 0:
        pytest.skip("No messages entries, skipping")
    assert count > 0

    # getting all the messages
    # get first messages page
    body = {"category": "message", "limit": count, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)

    assert ret["status"] == status["OK"]
    assert count == ret["body"]["totalCount"]

    messages = ret["body"]["entries"]  # getting entries
    messages_count = len(messages)
    assert messages_count == count
    all_messages = ret["body"]["entries"]
    if count > MESSAGES_PAGE_SIZE:

    # getting a number of messages
    number_of_requested_messages = 3
    body = {"category": "message", "limit": number_of_requested_messages, "offset": 0}
        # get rest of the messages
        while MESSAGES_PAGE_SIZE == len(ret["body"]["entries"]):

            if "nextPage" in ret["body"]:

                offset = ret["body"]["nextPage"]["offset"]
                messages_left_count =  count - len(all_messages)

                body = {"category": "message", "limit": messages_left_count, "offset": offset}
                ret = harness.endpoint_request("messages", "get", body)

                assert ret["status"] == status["OK"]

                all_messages += ret["body"]["entries"]

            else:
                break

    assert count == len(all_messages)


@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_get_messages_by_thread_id_without_pagination(harness):
    thread_id = 1
    limit = MESSAGES_PAGE_SIZE - 1
    body = {"category": "message", "threadID": thread_id, "limit": limit, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    messages = ret["body"]["entries"]  # getting entries
    messages = ret["body"]["entries"]
    messages_count = len(messages)
    assert messages_count == number_of_requested_messages
    assert messages_count == limit
    for message in ret["body"]["entries"]:
        assert message["threadID"] == thread_id

    # getting messages binded to threadID

@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_get_messages_by_thread_id_with_pagination(harness):
    thread_id = 1
    body = {"category": "message", "threadID": thread_id, "limit": 10, "offset": 0}
    limit = MESSAGES_PAGE_SIZE + 1
    body = {"category": "message", "threadID": thread_id, "limit": limit, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    messages = ret["body"]["entries"]
    messages_count = len(messages)
    assert messages_count == MESSAGES_PAGE_SIZE
    for message in ret["body"]["entries"]:
        assert message["threadID"] == thread_id


@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_get_all_messages_by_thread_id(harness):
    thread_id = 1
    # get messages count
    body = {"category": "message", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    messages_count = ret["body"]["count"]
    assert messages_count > 0

    # get first messages page
    body = {"category": "message", "threadID": thread_id, "limit": messages_count, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)

    assert ret["status"] == status["OK"]
    messages_by_thread_id_count = ret["body"]["totalCount"]

    all_messages = ret["body"]["entries"]
    if messages_by_thread_id_count > MESSAGES_PAGE_SIZE:

        # get rest of the messages
        while MESSAGES_PAGE_SIZE == len(ret["body"]["entries"]):

            if "nextPage" in ret["body"]:

                offset = ret["body"]["nextPage"]["offset"]
                messages_left_count =  messages_by_thread_id_count - len(all_messages)

                body = {"category": "message", "threadID": thread_id, "limit": messages_left_count, "offset": offset}
                ret = harness.endpoint_request("messages", "get", body)

                assert ret["status"] == status["OK"]

                all_messages += ret["body"]["entries"]

            else:
                break

    assert messages_by_thread_id_count == len(all_messages)


@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_remove_message(harness):
    # get messages count
    body = {"category": "message", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    count = ret["body"]["count"]
    assert count > 0

    # get message
    number_of_requested_messages = 1
    body = {"category": "message", "limit": number_of_requested_messages, "offset": 0}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    messages = ret["body"]["entries"]
    messages_count = len(messages)
    assert messages_count == number_of_requested_messages

    # remove message
    sms_to_remove = messages[0]
    body = {"category": "message", "messageID": sms_to_remove["messageID"]}
    ret = harness.endpoint_request("messages", "del", body)
    assert ret["status"] == status["OK"]

    # getting the messages count again
    # get messages count again
    body = {"category": "message", "count": True}
    ret = harness.endpoint_request("messages", "get", body)
    assert ret["status"] == status["OK"]

    assert ret["body"]["count"] == count - 1


@pytest.mark.service_desktop_test
@pytest.mark.usefixtures("usb_unlocked")
def test_get_message_by_id(harness):
    message_id = 1
    get_body = {"category": "message", "messageID": message_id}
    ret = harness.endpoint_request("messages", "get", get_body)
    assert ret["status"] == status["OK"]
    assert ret["body"]["messageID"] == message_id