~aleteoryx/muditaos

2c3965df187acfe300f0666b52ca05457bc819b2 — RobertPiet 5 years ago c30101b
[EGD-5557] Contact Endpoint pagination

Pagination added for contact endpoint.
Introduced PagedContext and query message for contacts that returns
the required contacts together with the number of all contacts in db.
M enabled_unittests => enabled_unittests +4 -0
@@ 215,6 215,10 @@ TESTS_LIST["catch2-service-desktop"]="
    Endpoint Factory test;
"
#---------
TESTS_LIST["catch2-service-desktop-endpoint-contacts"]="
    Endpoint Contacts Test;
"
#---------
TESTS_LIST["catch2-service-evtmgr"]="
    ScreenLightControlFunctions;
"

M module-db/Common/Query.cpp => module-db/Common/Query.cpp +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

#include "Query.hpp"


@@ 22,7 22,12 @@ bool QueryCallback::handleQueryResponse(QueryResult *response)
}

EndpointListener::EndpointListener(EndpointQueryCallbackFunction &&_callback, parserFSM::Context &_context)
    : callback{std::move(_callback)}, context{_context}
    : callback{std::move(_callback)}, context(_context)
{}

EndpointListenerWithPages::EndpointListenerWithPages(EndpointQueryCallbackFunctionWithPages &&_callback,
                                                     const parserFSM::PagedContext &_context)
    : callback{std::move(_callback)}, context(_context)
{}

bool EndpointListener::handleQueryResponse(db::QueryResult *response)


@@ 38,6 43,19 @@ bool EndpointListener::handleQueryResponse(db::QueryResult *response)
    return false;
}

bool EndpointListenerWithPages::handleQueryResponse(db::QueryResult *response)
{
    if (callback) {
        LOG_DEBUG("Executing callback...");
        const auto ret = callback(response, context);
        LOG_DEBUG("Callback finished");
        return ret;
    }

    LOG_ERROR("callback is nullptr!");
    return false;
}

Query::Query(Type type) : type(type)
{}


M module-db/Common/Query.hpp => module-db/Common/Query.hpp +16 -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


@@ 14,7 14,7 @@ namespace db
    class QueryResult; // Forward declaration
    using QueryCallbackFunction         = std::function<bool(db::QueryResult *)>;
    using EndpointQueryCallbackFunction = std::function<bool(db::QueryResult *, parserFSM::Context &)>;

    using EndpointQueryCallbackFunctionWithPages = std::function<bool(db::QueryResult *, parserFSM::PagedContext &)>;
    class QueryListener
    {
      public:


@@ 49,6 49,20 @@ namespace db
        parserFSM::Context context;
    };

    class EndpointListenerWithPages : public EndpointListener
    {
      public:
        EndpointListenerWithPages() = default;
        EndpointListenerWithPages(EndpointQueryCallbackFunctionWithPages &&_callback,
                                  const parserFSM::PagedContext &_context);

        bool handleQueryResponse(db::QueryResult *result) override;

      private:
        EndpointQueryCallbackFunctionWithPages callback;
        parserFSM::PagedContext context;
    };

    /// virtual query input interface
    class Query
    {

M module-db/Interface/ContactRecord.cpp => module-db/Interface/ContactRecord.cpp +32 -4
@@ 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

#include "ContactRecord.hpp"


@@ 135,6 135,9 @@ auto ContactRecordInterface::runQuery(std::shared_ptr<db::Query> query) -> std::
    if (typeid(*query) == typeid(db::query::ContactGet)) {
        return getQuery(query);
    }
    else if (typeid(*query) == typeid(db::query::ContactGetWithTotalCount)) {
        return getQueryWithTotalCount(query);
    }
    else if (typeid(*query) == typeid(db::query::ContactGetLetterMap)) {
        return getLetterMapQuery(query);
    }


@@ 162,7 165,7 @@ auto ContactRecordInterface::runQuery(std::shared_ptr<db::Query> query) -> std::
    }
}

auto ContactRecordInterface::getQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
auto ContactRecordInterface::getQueryRecords(std::shared_ptr<db::Query> query) -> std::vector<ContactRecord>
{
    auto textFilter = dynamic_cast<const db::query::TextFilter *>(query.get());
    assert(query != nullptr);


@@ 203,11 206,31 @@ auto ContactRecordInterface::getQuery(std::shared_ptr<db::Query> query) -> std::
    for (uint32_t idx = 0; idx < static_cast<uint32_t>(ids.size()); idx++) {
        result[idx].contactPosOnList = offset + idx;
    }
    auto response = std::make_unique<db::query::ContactGetResult>(result);
    return result;
}

auto ContactRecordInterface::getQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
{
    auto response = std::make_unique<db::query::ContactGetResult>(getQueryRecords(query));
    response->setRequestQuery(query);
    return response;
}

auto ContactRecordInterface::getQueryWithTotalCount(std::shared_ptr<db::Query> query)
    -> std::unique_ptr<db::QueryResult>
{
    if (auto queryContacts = dynamic_cast<db::query::ContactGet *>(query.get())) {
        auto querySize = std::make_shared<db::query::ContactGetSize>(queryContacts->getFilterData(),
                                                                     queryContacts->getGroupFilterData(),
                                                                     queryContacts->getContactDisplayMode());
        auto response  = std::make_unique<db::query::ContactGetResultWithTotalCount>(getQueryRecords(query),
                                                                                    getContactsSize(querySize));
        response->setRequestQuery(query);
        return response;
    }
    return nullptr;
}

auto ContactRecordInterface::getLetterMapQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
{
    ContactsMapData result = contactDB->contacts.GetPosOfFirstLetters();


@@ 233,7 256,7 @@ auto ContactRecordInterface::getByIDQuery(std::shared_ptr<db::Query> query) -> s
    return response;
}

auto ContactRecordInterface::getSizeQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
auto ContactRecordInterface::getContactsSize(std::shared_ptr<db::Query> query) -> std::size_t
{
    auto textFilter = dynamic_cast<const db::query::TextFilter *>(query.get());
    assert(query != nullptr);


@@ 280,7 303,12 @@ auto ContactRecordInterface::getSizeQuery(std::shared_ptr<db::Query> query) -> s
    else {
        count = contactDB->name.GetCountByName(countQuery->getFilterData());
    }
    return count;
}

auto ContactRecordInterface::getSizeQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
{
    auto count = getContactsSize(query);
    debug_db_data("Contact count query result: %lu", static_cast<unsigned long>(count));

    auto response = std::make_unique<db::query::RecordsSizeQueryResult>(count);

M module-db/Interface/ContactRecord.hpp => module-db/Interface/ContactRecord.hpp +4 -1
@@ 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


@@ 236,10 236,13 @@ class ContactRecordInterface : public RecordInterface<ContactRecord, ContactReco

    const std::uint32_t favouritesGroupId;
    auto getQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto getQueryRecords(std::shared_ptr<db::Query> query) -> std::vector<ContactRecord>;
    auto getQueryWithTotalCount(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto getForListQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto getLetterMapQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;

    auto getByIDQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto getContactsSize(std::shared_ptr<db::Query> query) -> std::size_t;
    auto getSizeQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto addQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    auto updateQuery(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;

M module-db/queries/Filter.hpp => module-db/queries/Filter.hpp +3 -3
@@ 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


@@ 95,7 95,7 @@ namespace db::query
         *
         * @param groupText group filter text
         */
        ContactGroupFilter(const std::uint32_t &groupId) : filterData(std::move(groupId))
        ContactGroupFilter(std::uint32_t groupId) : filterData(groupId)
        {}

        [[nodiscard]] const std::uint32_t &getGroupFilterData() const noexcept


@@ 121,7 121,7 @@ namespace db::query
         *
         * @param mode contact display mode
         */
        ContactDisplayMode(const std::uint32_t &mode) : displayMode(std::move(mode))
        ContactDisplayMode(std::uint32_t mode) : displayMode(mode)
        {}

        [[nodiscard]] const std::uint32_t &getContactDisplayMode() const noexcept

M module-db/queries/RecordQuery.cpp => module-db/queries/RecordQuery.cpp +2 -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

#include "RecordQuery.hpp"


@@ 13,7 13,7 @@ using namespace db::query;
RecordQuery::RecordQuery() noexcept : Query(Query::Type::Read)
{}

RecordQuery::RecordQuery(std::size_t offset, std::size_t limit) noexcept
RecordQuery::RecordQuery(std::size_t limit, std::size_t offset) noexcept
    : Query(Query::Type::Read), limit(limit), offset(offset)
{}


M module-db/queries/phonebook/QueryContactGet.cpp => module-db/queries/phonebook/QueryContactGet.cpp +31 -3
@@ 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

#include "QueryContactGet.hpp"


@@ 14,14 14,32 @@ ContactGet::ContactGet(const std::string &filter, const std::uint32_t &groupFilt
ContactGet::ContactGet(std::size_t limit,
                       std::size_t offset,
                       const std::string &filter,
                       const std::uint32_t &groupFilter,
                       const std::uint32_t &displayMode)
                       std::uint32_t groupFilter,
                       std::uint32_t displayMode)
    : RecordQuery(limit, offset), TextFilter(filter), ContactGroupFilter(groupFilter), ContactDisplayMode(displayMode)
{}

ContactGetWithTotalCount::ContactGetWithTotalCount(std::size_t limit,
                                                   std::size_t offset,
                                                   const std::string &filter,
                                                   std::uint32_t groupFilter,
                                                   std::uint32_t displayMode)
    : ContactGet(limit, offset, filter, groupFilter, displayMode)
{}

ContactGetResult::ContactGetResult(const std::vector<ContactRecord> &records) : RecordQueryResult(records)
{}

ContactGetResultWithTotalCount::ContactGetResultWithTotalCount(const std::vector<ContactRecord> &records,
                                                               std::size_t allLength)
    : ContactGetResult(records), allLength(allLength)
{}

auto ContactGetResultWithTotalCount::getAllLength() const -> std::size_t
{
    return allLength;
}

ContactGetSize::ContactGetSize(const std::string &filter,
                               const std::uint32_t &groupFilter,
                               const std::uint32_t &displayMode)


@@ 50,11 68,21 @@ ContactGetLetterMapResult ::ContactGetLetterMapResult(ContactsMapData &LetterMap
    return "ContactGet";
}

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

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

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

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

M module-db/queries/phonebook/QueryContactGet.hpp => module-db/queries/phonebook/QueryContactGet.hpp +26 -4
@@ 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


@@ 37,9 37,9 @@ namespace db::query
         */
        ContactGet(std::size_t limit,
                   std::size_t offset,
                   const std::string &filter        = "",
                   const std::uint32_t &groupFilter = 0,
                   const std::uint32_t &displayMode = 0);
                   const std::string &filter = "",
                   std::uint32_t groupFilter = 0,
                   std::uint32_t displayMode = 0);

        /**
         * @brief debug info


@@ 72,6 72,17 @@ namespace db::query
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class ContactGetResultWithTotalCount : public ContactGetResult
    {
      public:
        ContactGetResultWithTotalCount(const std::vector<ContactRecord> &records, std::size_t allLength);
        [[nodiscard]] auto debugInfo() const -> std::string override;
        auto getAllLength() const -> std::size_t;

      private:
        std::size_t allLength;
    };

    /**
     * @brief A query to get a number of contacts filtered with a text
     *


@@ 99,6 110,17 @@ namespace db::query
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class ContactGetWithTotalCount : public ContactGet
    {
      public:
        ContactGetWithTotalCount(std::size_t limit,
                                 std::size_t offset,
                                 const std::string &filter = "",
                                 std::uint32_t groupFilter = 0,
                                 std::uint32_t displayMode = 0);
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class ContactGetLetterMap : public RecordQuery,
                                public TextFilter,
                                public ContactGroupFilter,

M module-services/service-desktop/endpoints/Context.hpp => module-services/service-desktop/endpoints/Context.hpp +84 -3
@@ 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


@@ 15,7 15,12 @@ namespace parserFSM
        inline constexpr auto endpoint = "endpoint";
        inline constexpr auto uuid     = "uuid";
        inline constexpr auto status   = "status";
        inline constexpr auto totalCount = "totalCount";
        inline constexpr auto body     = "body";
        inline constexpr auto offset     = "offset";
        inline constexpr auto limit      = "limit";
        inline constexpr auto nextPage   = "nextPage";
        inline constexpr auto entries    = "entries";
    } // namespace json

    struct endpointResponseContext


@@ 28,7 33,7 @@ namespace parserFSM

    class Context
    {
      private:
      protected:
        json11::Json body;
        EndpointType endpoint;
        uint32_t uuid;


@@ 84,8 89,9 @@ namespace parserFSM
            uuid     = invalidUuid;
            method   = http::Method::get;
        }
        virtual ~Context() noexcept = default;

        auto createSimpleResponse() -> std::string
        virtual auto createSimpleResponse() -> std::string
        {
            json11::Json responseJson = json11::Json::object{{json::endpoint, static_cast<int>(getEndpoint())},
                                                             {json::status, static_cast<int>(responseContext.status)},


@@ 123,4 129,79 @@ namespace parserFSM
        }
    };

    class PagedContext : public Context
    {
      private:
        // from request
        std::size_t requestedLimit, requestedOffset;
        // set by query (during helper run)
        std::size_t totalCount;
        // set it before calling handle on helper
        std::size_t pageSize;

      public:
        explicit PagedContext(json11::Json &js, size_t pageSize) : Context(js), pageSize(pageSize)
        {}
        PagedContext() = default;
        void setRequestedLimit(std::size_t limit)
        {
            requestedLimit = limit;
        }
        void setRequestedOffset(std::size_t offset)
        {
            requestedOffset = offset;
        }
        void setTotalCount(std::size_t count)
        {
            totalCount = count;
        }
        std::size_t getPageSize() const
        {
            return pageSize;
        }

        auto createSimpleResponse() -> std::string override
        {
            auto elemsCount = responseContext.body.array_items().size();
            auto newBody    = json11::Json::object{{json::entries, responseContext.body},
                                                {json::totalCount, static_cast<int>(totalCount)}};
            if (requestedLimit > elemsCount) {
                std::size_t offset = requestedOffset + elemsCount;
                if (offset < totalCount) {
                    auto lastTableIndex = std::min(totalCount, offset + requestedLimit - elemsCount);
                    std::size_t limit   = std::min(pageSize, lastTableIndex - offset);
                    auto nextPageParams = json11::Json::object{{json::offset, static_cast<int>(offset)},
                                                               {json::limit, static_cast<int>(limit)}};
                    newBody.insert({json::nextPage, nextPageParams});
                }
            }

            setResponseBody(newBody);
            return Context::createSimpleResponse();
        }
    };

    namespace endpoint_pageing
    {
        inline constexpr std::size_t contactsPageSize = 10;
    }

    class ContextFactory
    {
      public:
        static auto create(json11::Json &js) -> std::unique_ptr<parserFSM::Context>
        {
            switch (static_cast<EndpointType>(js[json::endpoint].int_value())) {
            // enable for pagination in other endpoints
            // case EndpointType::calendarEvents:
            // case EndpointType::calllog:
            case EndpointType::contacts:
                // case EndpointType::messages:
                return std::make_unique<PagedContext>(js, endpoint_pageing::contactsPageSize);
            default:
                return std::make_unique<Context>(js);
            }
        }
    };

} // namespace parserFSM

M module-services/service-desktop/endpoints/Endpoint.hpp => module-services/service-desktop/endpoints/Endpoint.hpp +3 -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


@@ 10,6 10,7 @@
#include <Common/Query.hpp>
#include <Service/Service.hpp>
#include <string>
#include <memory>

namespace parserFSM
{


@@ 20,7 21,7 @@ namespace parserFSM
      public:
        Endpoint(sys::Service *_ownerServicePtr) : ownerServicePtr(_ownerServicePtr){};
        virtual ~Endpoint()                           = default;
        virtual auto handle(Context &context) -> void = 0;
        virtual auto handle(parserFSM::Context &context) -> void = 0;
        auto c_str() -> const char *
        {
            return debugName.c_str();

M module-services/service-desktop/endpoints/contacts/ContactHelper.cpp => module-services/service-desktop/endpoints/contacts/ContactHelper.cpp +39 -30
@@ 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

#include "ContactHelper.hpp"


@@ 27,6 27,7 @@
#include <memory>
#include <utility>
#include <vector>
#include <algorithm>

using namespace parserFSM;



@@ 72,38 73,46 @@ auto ContactHelper::requestDataFromDB(Context &context) -> sys::ReturnCodes
    if (context.getBody()[json::contacts::id].int_value() != 0) {
        return requestContactByID(context);
    }
    else if (context.getBody()[json::contacts::count].bool_value()) {
    else if (context.getBody()[json::contacts::limit].bool_value()) {
        return requestCount(context);
    }

    auto limit = context.getBody()[json::contacts::count].int_value();
    auto offset = context.getBody()[json::contacts::offset].int_value();
    auto query  = std::make_unique<db::query::ContactGet>(offset, limit, "");

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

                auto recordsPtr = std::make_unique<std::vector<ContactRecord>>(contactResult->getRecords());
                json11::Json::array contactsArray;

                for (auto record : *recordsPtr.get()) {
                    contactsArray.emplace_back(ContactHelper::to_json(record));
    try {
        auto &ctx          = dynamic_cast<PagedContext &>(context);
        std::size_t limit  = ctx.getBody()[json::contacts::limit].int_value();
        std::size_t offset = ctx.getBody()[json::contacts::offset].int_value();
        ctx.setRequestedLimit(limit);
        ctx.setRequestedOffset(offset);
        auto query = std::make_unique<db::query::ContactGetWithTotalCount>(std::min(ctx.getPageSize(), limit), offset);
        auto listener = std::make_unique<db::EndpointListenerWithPages>(
            [](db::QueryResult *result, PagedContext &context) {
                if (auto contactResult = dynamic_cast<db::query::ContactGetResultWithTotalCount *>(result)) {

                    auto recordsPtr = std::make_unique<std::vector<ContactRecord>>(contactResult->getRecords());
                    context.setTotalCount(contactResult->getAllLength());
                    json11::Json::array contactsArray;

                    for (const auto &record : *recordsPtr.get()) {
                        contactsArray.emplace_back(ContactHelper::to_json(record));
                    }

                    context.setResponseBody(contactsArray);

                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }

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

    query->setQueryListener(std::move(listener));

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

    return sys::ReturnCodes::Success;
}


@@ 118,7 127,7 @@ sys::ReturnCodes ContactHelper::requestCount(Context &context)

                auto count = contactResult->getSize();

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

M module-services/service-desktop/endpoints/contacts/ContactHelper.hpp => module-services/service-desktop/endpoints/contacts/ContactHelper.hpp +5 -3
@@ 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


@@ 26,8 26,10 @@ namespace parserFSM

    class ContactHelper : public DBHelper
    {

      public:
        ContactHelper(sys::Service *_ownerServicePtr) : DBHelper(_ownerServicePtr){};
        ContactHelper(sys::Service *_ownerServicePtr) : DBHelper(_ownerServicePtr)
        {}

        auto createDBEntry(Context &context) -> sys::ReturnCodes override;
        auto requestDataFromDB(Context &context) -> sys::ReturnCodes override;


@@ 42,7 44,7 @@ namespace parserFSM

    namespace json::contacts
    {
        inline constexpr auto count           = "count";
        inline constexpr auto limit           = "limit";
        inline constexpr auto offset          = "offset";
        inline constexpr auto primaryName     = "priName";
        inline constexpr auto alternativeName = "altName";

M module-services/service-desktop/endpoints/contacts/ContactsEndpoint.cpp => module-services/service-desktop/endpoints/contacts/ContactsEndpoint.cpp +1 -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

#include "ContactHelper.hpp"


@@ 9,7 9,6 @@
#include <memory>

using namespace parserFSM;

auto ContactsEndpoint::handle(Context &context) -> void
{
    switch (context.getMethod()) {

M module-services/service-desktop/endpoints/contacts/ContactsEndpoint.hpp => module-services/service-desktop/endpoints/contacts/ContactsEndpoint.hpp +1 -1
@@ 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

M module-services/service-desktop/parser/MessageHandler.cpp => module-services/service-desktop/parser/MessageHandler.cpp +8 -8
@@ 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

#include "MessageHandler.hpp"


@@ 35,18 35,18 @@ MessageHandler::MessageHandler(const std::string &message, sys::Service *OwnerSe

void MessageHandler::processMessage()
{
    Context context(messageJson);
    auto context = ContextFactory::create(messageJson);

    LOG_DEBUG("[MsgHandler]\nmethod: %d\nendpoint: %d\nuuid: %" PRIu32 "\nbody: %s\n",
              static_cast<int>(context.getMethod()),
              static_cast<int>(context.getEndpoint()),
              context.getUuid(),
              context.getBody().dump().c_str());
              static_cast<int>(context->getMethod()),
              static_cast<int>(context->getEndpoint()),
              context->getUuid(),
              context->getBody().dump().c_str());

    auto handler = EndpointFactory::create(context, OwnerServicePtr);
    auto handler = EndpointFactory::create(*context, OwnerServicePtr);

    if (handler != nullptr) {
        handler->handle(context);
        handler->handle(*context);
    }
    else {
        LOG_ERROR("No way to handle!");

M module-services/service-desktop/tests/CMakeLists.txt => module-services/service-desktop/tests/CMakeLists.txt +14 -0
@@ 15,3 15,17 @@ add_catch2_executable(
    DEPS
        PurePhone.img-target
)

add_catch2_executable(
    NAME
        service-desktop-endpoint-contacts
    SRCS
        tests-main.cpp
        test-contacts.cpp
    LIBS
        service-desktop
        module-utils
        module-apps
        module-vfs
        iosyscalls
)

A module-services/service-desktop/tests/test-contacts.cpp => module-services/service-desktop/tests/test-contacts.cpp +204 -0
@@ 0,0 1,204 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <endpoints/Endpoint.hpp>
#include <endpoints/EndpointFactory.hpp>

#include <catch2/catch.hpp>
#include <json/json11.hpp>
#include <purefs/filesystem_paths.hpp>
#include <utf8/UTF8.hpp>
#include <vfs.hpp>

#include <memory>
#include <filesystem>
#include <string>
#include <vector>

#include <module-db/Interface/ContactRecord.hpp>
#include <module-db/Databases/ContactsDB.hpp>

class vfs vfs;
std::unique_ptr<ContactsDB> contactsDb = nullptr;
std::vector<std::string> messageStrings;

// stubs
std::pair<bool, std::uint64_t> DBServiceAPI::GetQuery(sys::Service *serv,
                                                      db::Interface::Name database,
                                                      std::unique_ptr<db::Query> query)
{
    auto queryType = query->type;
    LOG_DEBUG("query is: %d", static_cast<uint32_t>(queryType));

    ContactRecordInterface cri(contactsDb.get());
    auto result        = cri.runQuery(std::move(query));
    auto queryListener = result->getRequestQuery()->getQueryListener();
    queryListener->handleQueryResponse(result.get());

    return std::make_pair(true, 0);
}
auto DBServiceAPI::ThreadGetByNumber(sys::Service *serv,
                                     const utils::PhoneNumber::View &phoneNumber,
                                     std::uint32_t timeout) -> std::unique_ptr<ThreadRecord>
{
    return nullptr;
}
auto DBServiceAPI::ThreadGetCount(sys::Service *serv, EntryState state) -> uint32_t
{
    return 0;
}

auto DBServiceAPI::GetQueryWithReply(sys::Service *serv,
                                     db::Interface::Name database,
                                     std::unique_ptr<db::Query> query,
                                     std::uint32_t timeout) -> sys::SendResult
{
    return {};
}

auto DBServiceAPI::verifyContact(sys::Service *serv, const ContactRecord &rec) -> ContactVerificationResult
{
    return {};
}
auto DBServiceAPI::ContactGetByID(sys::Service *serv, uint32_t contactID) -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::ContactGetByIDWithTemporary(sys::Service *serv, uint32_t contactID)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::ContactGetByIDCommon(sys::Service *serv, std::shared_ptr<DBContactMessage> contactMsg)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::ContactGetBySpeeddial(sys::Service *serv, UTF8 speeddial)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::ContactGetByPhoneNumber(sys::Service *serv, UTF8 phoneNumber)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::MatchContactByPhoneNumber(sys::Service *serv, const utils::PhoneNumber::View &numberView)
    -> std::unique_ptr<ContactRecord>
{
    return nullptr;
}
auto DBServiceAPI::ContactAdd(sys::Service *serv, const ContactRecord &rec) -> bool
{
    return false;
}
auto DBServiceAPI::ContactRemove(sys::Service *serv, uint32_t id) -> bool
{
    return false;
}
auto DBServiceAPI::ContactUpdate(sys::Service *serv, const ContactRecord &rec) -> bool
{
    return false;
}
auto DBServiceAPI::ContactSearch(sys::Service *serv, UTF8 primaryName, UTF8 alternativeName, UTF8 number)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    return nullptr;
}
auto DBServiceAPI::CalllogAdd(sys::Service *serv, const CalllogRecord &rec) -> CalllogRecord
{
    return {};
}
auto DBServiceAPI::CalllogRemove(sys::Service *serv, uint32_t id) -> bool
{
    return false;
}
auto DBServiceAPI::CalllogUpdate(sys::Service *serv, const CalllogRecord &rec) -> bool
{
    return false;
}
auto DBServiceAPI::CalllogGetCount(sys::Service *serv, EntryState state) -> uint32_t
{
    return 0;
}
auto DBServiceAPI::CalllogGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit) -> bool
{
    return false;
}
auto DBServiceAPI::GetCountryCodeByMCC(sys::Service *serv, uint32_t mcc) -> uint32_t
{
    return 0;
}
auto DBServiceAPI::DBBackup(sys::Service *serv, std::string backupPath) -> bool
{
    return false;
}
bool DBServiceAPI::AddSMS(sys::Service *serv, const SMSRecord &record, std::unique_ptr<db::QueryListener> &&listener)
{
    return false;
}

xQueueHandle parserFSM::MessageHandler::sendQueue;
parserFSM::MessageHandler::MessageHandler(const std::string &message, sys::Service *OwnerService)
    : OwnerServicePtr(OwnerService)
{}
void parserFSM::MessageHandler::processMessage()
{}
void parserFSM::MessageHandler::putToSendQueue(const std::string &msg)
{
    messageStrings.push_back(msg);
    LOG_DEBUG("response is: %s", msg.c_str());
}
//~stubs

TEST_CASE("Endpoint Contacts Test")
{
    vfs.Init();
    Database::initialize();

    const auto contactsPath = purefs::dir::getUserDiskPath() / "contacts.db";
    std::filesystem::remove(contactsPath);

    contactsDb = std::make_unique<ContactsDB>(contactsPath.c_str());
    REQUIRE(contactsDb->isInitialized());
    messageStrings.clear();

    SECTION("Request with pages")
    {
        auto count       = 29; // requested number of record to return
        auto endpoint    = 7;
        auto uuid        = "1103";
        auto totalCount  = contactsDb->contacts.count();
        auto testMessage = "{\"endpoint\":" + std::to_string(endpoint) + ", \"method\":1, \"uuid\":" + uuid +
                           ", \"body\":{\"limit\":" + std::to_string(count) + ", \"offset\":20}}";
        std::string err;
        auto msgJson = json11::Json::parse(testMessage, err);
        REQUIRE(err.empty());

        parserFSM::PagedContext context(msgJson, 10);
        auto handler = EndpointFactory::create(context, nullptr);
        handler->handle(context);
        auto pageSize = context.getPageSize();
        REQUIRE(10 == pageSize);
        REQUIRE(1 == messageStrings.size());
        auto msg = messageStrings[0];
        REQUIRE(msg.size() > 10);
        auto retJson = json11::Json::parse(msg.substr(10), err); // string length and go to real data

        REQUIRE(err.empty());
        REQUIRE(uuid == retJson[parserFSM::json::uuid].string_value());
        REQUIRE(endpoint == retJson[parserFSM::json::endpoint].int_value());

        auto body = retJson[parserFSM::json::body];
        REQUIRE(totalCount == static_cast<uint32_t>(body[parserFSM::json::totalCount].int_value()));

        auto nextPage = body[parserFSM::json::nextPage];
        REQUIRE(static_cast<int>(pageSize) == nextPage[parserFSM::json::limit].int_value());
        // base offset + pageSize cause asked for 29
        REQUIRE(30 == nextPage[parserFSM::json::offset].int_value());

        auto entries = body[parserFSM::json::entries];
        REQUIRE(pageSize == entries.array_items().size());
    }
}

R test/pytest/service-desktop/test_calllog.py => test/pytest/service-desktop/disabled_test_calllog.py +0 -0
R test/pytest/service-desktop/test_messages.py => test/pytest/service-desktop/disabled_test_messages.py +0 -0
M test/pytest/service-desktop/test_contacts.py => test/pytest/service-desktop/test_contacts.py +16 -16
@@ 7,49 7,49 @@ from harness.interface.defs import status
@pytest.mark.service_desktop_test
def test_contacts(harness):
    # getting the contacts count
    body = {"count": True}
    body = {"limit": True}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]

    count = ret["body"]["count"]
    count = ret["body"]["limit"]
    if count == 0:
        pytest.skip("No contacts entries, skipping")

    # getting all contacts
    batch_size = 30
    batch_size = 10
    divider = int(count / batch_size)
    reminder = count % batch_size
    contacts = []
    for i in range(divider):
        body = {"count": batch_size, "offset": batch_size*i}
        body = {"limit": batch_size, "offset": batch_size*i}
        ret = harness.endpoint_request("contacts", "get", body)
        assert ret["status"] == status["OK"]
        contacts = contacts + ret["body"]
        contacts = contacts + ret["body"]["entries"]

    body = {"count": reminder, "offset": count-reminder}
    body = {"limit": reminder, "offset": count-reminder}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]
    contacts = contacts + ret["body"]
    contacts = contacts + ret["body"]["entries"]

    contacts_length = len(contacts)
    assert contacts_length
    assert contacts_length == count

    # try to get more than available
    batch_size = 30
    batch_size = 10
    divider = int((count+10) / batch_size)
    reminder = (count+10) % batch_size
    contacts = []
    for i in range(divider):
        body = {"count": batch_size, "offset": batch_size * i}
        body = {"limit": batch_size, "offset": batch_size * i}
        ret = harness.endpoint_request("contacts", "get", body)
        assert ret["status"] == status["OK"]
        contacts = contacts + ret["body"]
        contacts = contacts + ret["body"]["entries"]

    body = {"count": reminder, "offset": (count+10)-reminder}
    body = {"limit": reminder, "offset": (count+10)-reminder}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]
    contacts = contacts + ret["body"]
    contacts = contacts + ret["body"]["entries"]

    contacts_length = len(contacts)
    assert contacts_length


@@ 78,10 78,10 @@ def test_contacts(harness):
    assert ret["status"] == status["NotAcceptable"]

    # checking count after adding
    body = {"count": True}
    body = {"limit": True}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]
    assert ret["body"]["count"] == count + 1
    assert ret["body"]["limit"] == count + 1

    # updating existing contact
    body = {"address": "6 Czeczota St.\n02600 Warsaw",


@@ 112,8 112,8 @@ def test_contacts(harness):
    assert ret["status"] == status["OK"]

    # verifying count
    body = {"count": True}
    body = {"limit": True}
    ret = harness.endpoint_request("contacts", "get", body)
    assert ret["status"] == status["OK"]

    assert ret["body"]["count"] == count
    assert ret["body"]["limit"] == count