~aleteoryx/muditaos

87f118fd861fb0e32c578ceccab26ba4ece93ea7 — Pawel Olejniczak 5 years ago 52ad797
[EGD-4317] Fix newly added contact recognized as a duplicate

of temporary contact. Newly added contact is no longer considered
as a duplicate of a temporary one.
It simply replaces the temporary contact, if its number matches.
M changelog.md => changelog.md +1 -0
@@ 13,6 13,7 @@
### Fixed

* Fix absent notifications
* Fix newly added contact recognized as a duplicate of temporary contact.

## [0.52.1 2020-12-23]


M module-apps/application-phonebook/windows/PhonebookNewContact.cpp => module-apps/application-phonebook/windows/PhonebookNewContact.cpp +21 -8
@@ 143,22 143,35 @@ namespace gui
    {
        LOG_DEBUG("%s", __FUNCTION__);
        if (!contact->isTemporary()) {
            auto err = DBServiceAPI::verifyContact(application, *contact);
            LOG_INFO("Contact data verification result: \"%s\"", DBServiceAPI::getVerificationErrorString(err).c_str());
            switch (err) {
            case DBServiceAPI::noError:
            auto result = DBServiceAPI::verifyContact(application, *contact);
            switch (result) {
            case DBServiceAPI::ContactVerificationResult::success:
                break;
            case DBServiceAPI::emptyContactError:
            case DBServiceAPI::ContactVerificationResult::emptyContact:
                return false;
            case DBServiceAPI::primaryNumberError:
            case DBServiceAPI::ContactVerificationResult::primaryNumberDuplicate:
                showDialogDuplicatedNumber(contact->numbers[0].number);
                return false;
            case DBServiceAPI::secondaryNumberError:
            case DBServiceAPI::ContactVerificationResult::secondaryNumberDuplicate:
                showDialogDuplicatedNumber(contact->numbers[1].number);
                return false;
            case DBServiceAPI::speedDialError:
            case DBServiceAPI::ContactVerificationResult::speedDialDuplicate:
                showDialogDuplicatedSpeedDialNumber();
                return false;
            case DBServiceAPI::ContactVerificationResult::temporaryContactExists:
                std::unique_ptr<ContactRecord> tempContact;
                assert(!contact->numbers.empty());
                for (auto number : contact->numbers) {
                    if (number.number.getEntered().size() > 0) {
                        tempContact = DBServiceAPI::MatchContactByPhoneNumber(application, number.number);
                        if (tempContact != nullptr) {
                            contact->ID   = tempContact->ID;
                            contactAction = ContactAction::EditTemporary;
                            break;
                        }
                    }
                }
                return false;
            }
        }
        else {

M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +105 -149
@@ 3,8 3,6 @@

#include "service-db/DBServiceAPI.hpp"
#include "service-db/DBThreadMessage.hpp"
#include "service-db/DBSMSMessage.hpp"
#include "service-db/DBSMSTemplateMessage.hpp"
#include "service-db/DBContactMessage.hpp"
#include "service-db/DBNotesMessage.hpp"
#include "service-db/DBCalllogMessage.hpp"


@@ 13,24 11,18 @@
#include "service-db/QueryMessage.hpp"
#include "service-db/DBServiceName.hpp"

#include <AlarmsRecord.hpp>
#include <CalllogRecord.hpp>
#include <Common/Query.hpp>
#include <ContactRecord.hpp>
#include <MessageType.hpp>
#include <PhoneNumber.hpp>
#include <SMSRecord.hpp>
#include <SMSTemplateRecord.hpp>
#include <Service/Bus.hpp>
#include <Service/Common.hpp>
#include <Tables/CountryCodesTable.hpp>
#include <Tables/Record.hpp>
#include <ThreadRecord.hpp>
#include <Utils.hpp>
#include <ext/alloc_traits.h>
#include <log/log.hpp>
#include <queries/messages/threads/QueryThreadGetByNumber.hpp>
#include <queries/phonebook/QueryNumberGetByID.hpp>

#include <utility>
#include <cassert>


@@ 41,9 33,9 @@ namespace sys
} // namespace sys
struct NotesRecord;

std::unique_ptr<ThreadRecord> DBServiceAPI::ThreadGetByNumber(sys::Service *serv,
                                                              const utils::PhoneNumber::View &phoneNumber,
                                                              std::uint32_t timeout)
auto DBServiceAPI::ThreadGetByNumber(sys::Service *serv,
                                     const utils::PhoneNumber::View &phoneNumber,
                                     std::uint32_t timeout) -> std::unique_ptr<ThreadRecord>
{
    auto [code, msg] = DBServiceAPI::GetQueryWithReply(
        serv, db::Interface::Name::SMSThread, std::make_unique<db::query::ThreadGetByNumber>(phoneNumber), timeout);


@@ 56,13 48,13 @@ std::unique_ptr<ThreadRecord> DBServiceAPI::ThreadGetByNumber(sys::Service *serv
        auto threadResult   = dynamic_cast<db::query::ThreadGetByNumberResult *>(threadResponse.get());
        assert(threadResult != nullptr);

        return std::make_unique<ThreadRecord>(std::move(threadResult->getThread()));
        return std::make_unique<ThreadRecord>(threadResult->getThread());
    }

    return nullptr;
}

bool DBServiceAPI::ThreadGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit)
auto DBServiceAPI::ThreadGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit) -> bool
{
    std::shared_ptr<DBThreadMessage> msg = std::make_shared<DBThreadMessage>(MessageType::DBThreadGetLimitOffset);
    msg->offset                          = offset;


@@ 73,56 65,56 @@ bool DBServiceAPI::ThreadGetLimitOffset(sys::Service *serv, uint32_t offset, uin
    return true;
}

uint32_t DBServiceAPI::ThreadGetCount(sys::Service *serv, EntryState state)
auto DBServiceAPI::ThreadGetCount(sys::Service *serv, EntryState state) -> uint32_t
{
    auto msg = std::make_shared<DBThreadGetCountMessage>(state);

    auto ret                                = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBThreadResponseMessage *threadResponse = reinterpret_cast<DBThreadResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (threadResponse->retCode == true)) {
    auto ret            = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto threadResponse = dynamic_cast<DBThreadResponseMessage *>(ret.second.get());
    assert(threadResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (threadResponse->retCode == 1)) {
        return threadResponse->count;
    }
    else {
        return false;
    }
    return 0;
}

std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactGetByID(sys::Service *serv, uint32_t contactID)
auto DBServiceAPI::ContactGetByID(sys::Service *serv, uint32_t contactID) -> std::unique_ptr<std::vector<ContactRecord>>
{
    ContactRecord rec;
    rec.ID = contactID;

    std::shared_ptr<DBContactMessage> msg = std::make_shared<DBContactMessage>(MessageType::DBContactGetByID, rec);
    return std::move(ContactGetByIDCommon(serv, std::move(msg)));
    return ContactGetByIDCommon(serv, std::move(msg));
}

std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactGetByIDWithTemporary(sys::Service *serv,
                                                                                      uint32_t contactID)
auto DBServiceAPI::ContactGetByIDWithTemporary(sys::Service *serv, uint32_t contactID)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    ContactRecord rec;
    rec.ID                                = contactID;
    std::shared_ptr<DBContactMessage> msg = std::make_shared<DBContactMessage>(MessageType::DBContactGetByID, rec);
    msg->withTemporary                    = true;
    return std::move(ContactGetByIDCommon(serv, std::move(msg)));
    return ContactGetByIDCommon(serv, std::move(msg));
}

std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactGetByIDCommon(
    sys::Service *serv, std::shared_ptr<DBContactMessage> contactMsg)
auto DBServiceAPI::ContactGetByIDCommon(sys::Service *serv, std::shared_ptr<DBContactMessage> contactMsg)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    auto ret = sys::Bus::SendUnicast(contactMsg, service::name::db, serv, DefaultTimeoutInMs);
    DBContactResponseMessage *contactResponse = reinterpret_cast<DBContactResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(contactMsg, service::name::db, serv, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactResponseMessage *>(ret.second.get());
    assert(contactResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode != 0)) {
        return std::move(contactResponse->records);
    }
    else {
        return std::make_unique<std::vector<ContactRecord>>();
    }
    return std::make_unique<std::vector<ContactRecord>>();
}

std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactGetBySpeeddial(sys::Service *serv, UTF8 speeddial)
auto DBServiceAPI::ContactGetBySpeeddial(sys::Service *serv, UTF8 speeddial)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    if (speeddial.length() == 0)
    if (speeddial.length() == 0) {
        return std::make_unique<std::vector<ContactRecord>>();
    }

    ContactRecord rec;
    rec.speeddial = speeddial;


@@ 130,17 122,11 @@ std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactGetBySpeeddial(
    std::shared_ptr<DBContactMessage> msg =
        std::make_shared<DBContactMessage>(MessageType::DBContactGetBySpeedDial, rec);

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBContactResponseMessage *contactResponse = reinterpret_cast<DBContactResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode == true)) {
        return std::move(contactResponse->records);
    }
    else {
        return std::make_unique<std::vector<ContactRecord>>();
    }
    return ContactGetByIDCommon(serv, msg);
}

std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactGetByPhoneNumber(sys::Service *serv, UTF8 phoneNumber)
auto DBServiceAPI::ContactGetByPhoneNumber(sys::Service *serv, UTF8 phoneNumber)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    ContactRecord rec;
    auto number = ContactRecord::Number(utils::PhoneNumber(phoneNumber).getView(), ContactNumberType ::PAGER);


@@ 148,123 134,99 @@ std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactGetByPhoneNumbe

    std::shared_ptr<DBContactMessage> msg = std::make_shared<DBContactMessage>(MessageType::DBContactGetByNumber, rec);

    auto ret              = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto *contactResponse = dynamic_cast<DBContactResponseMessage *>(ret.second.get());

    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode == true)) {
        return std::move(contactResponse->records);
    }
    else {
        return std::make_unique<std::vector<ContactRecord>>();
    }
    return ContactGetByIDCommon(serv, msg);
}

std::unique_ptr<ContactRecord> DBServiceAPI::MatchContactByPhoneNumber(sys::Service *serv,
                                                                       const utils::PhoneNumber::View &numberView)
auto DBServiceAPI::MatchContactByPhoneNumber(sys::Service *serv, const utils::PhoneNumber::View &numberView)
    -> std::unique_ptr<ContactRecord>
{
    auto msg = std::make_shared<DBContactNumberMessage>(numberView);

    auto ret              = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto *contactResponse = dynamic_cast<DBContactNumberResponseMessage *>(ret.second.get());
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactNumberResponseMessage *>(ret.second.get());
    assert(contactResponse);
    return std::move(contactResponse->contact);
}

DBServiceAPI::ContactVerificationError DBServiceAPI::verifyContact(sys::Service *serv, const ContactRecord &rec)
auto DBServiceAPI::verifyContact(sys::Service *serv, const ContactRecord &rec)
    -> DBServiceAPI::ContactVerificationResult
{

    if (rec.primaryName.length() == 0 && rec.alternativeName.length() == 0 && rec.numbers.empty() &&
        rec.mail.length() == 0 && rec.address.length() == 0 && rec.note.length() == 0) {
        return emptyContactError;
        return ContactVerificationResult::emptyContact;
    }

    auto retSpeedDial = ContactGetBySpeeddial(serv, rec.speeddial);
    if (!retSpeedDial->empty() && (*retSpeedDial)[0].ID != rec.ID) {
        return speedDialError;
        return ContactVerificationResult::speedDialDuplicate;
    }

    if (rec.numbers.size() > 0 && rec.numbers[0].number.getEntered().size() > 0) {
        auto retPhone1 = MatchContactByPhoneNumber(serv, rec.numbers[0].number);
        if (retPhone1 && retPhone1->ID != rec.ID) {
            return primaryNumberError;
            if (retPhone1->isTemporary()) {
                return ContactVerificationResult::temporaryContactExists;
            }
            return ContactVerificationResult::primaryNumberDuplicate;
        }
    }

    if (rec.numbers.size() > 1 && rec.numbers[1].number.getEntered().size() > 0) {
        auto retPhone2 = MatchContactByPhoneNumber(serv, rec.numbers[1].number);
        if (retPhone2 && retPhone2->ID != rec.ID) {
            return secondaryNumberError;
            if (retPhone2->isTemporary()) {
                return ContactVerificationResult::temporaryContactExists;
            }
            return ContactVerificationResult::secondaryNumberDuplicate;
        }
    }

    return noError;
    return ContactVerificationResult::success;
}

std::string DBServiceAPI::getVerificationErrorString(const ContactVerificationError err)
{
    switch (err) {
    case noError:
        return "No error occurred";
    case emptyContactError:
        return "Contact record is empty";
    case speedDialError:
        return "Invalid or duplicate speed dial number";
    case primaryNumberError:
        return "Invalid or duplicate primary number";
    case secondaryNumberError:
        return "Invalid or duplicate secondary number";
    default:
        return "Unknown error";
    }
}

bool DBServiceAPI::ContactAdd(sys::Service *serv, const ContactRecord &rec)
auto DBServiceAPI::ContactAdd(sys::Service *serv, const ContactRecord &rec) -> bool
{
    std::shared_ptr<DBContactMessage> msg = std::make_shared<DBContactMessage>(MessageType::DBContactAdd, rec);

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBContactResponseMessage *contactResponse = reinterpret_cast<DBContactResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactResponseMessage *>(ret.second.get());
    assert(contactResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode != 0)) {
        return true;
    }
    else {
        return false;
    }
    return false;
}

bool DBServiceAPI::ContactRemove(sys::Service *serv, uint32_t id)
auto DBServiceAPI::ContactRemove(sys::Service *serv, uint32_t id) -> bool
{
    std::shared_ptr<DBContactMessage> msg = std::make_shared<DBContactMessage>(MessageType::DBContactRemove);
    msg->id                               = id;

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBContactResponseMessage *contactResponse = reinterpret_cast<DBContactResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactResponseMessage *>(ret.second.get());
    assert(contactResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode != 0)) {
        return true;
    }
    else {
        return false;
    }
    return false;
}

bool DBServiceAPI::ContactUpdate(sys::Service *serv, const ContactRecord &rec)
auto DBServiceAPI::ContactUpdate(sys::Service *serv, const ContactRecord &rec) -> bool
{
    std::shared_ptr<DBContactMessage> msg = std::make_shared<DBContactMessage>(MessageType::DBContactUpdate, rec);

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBContactResponseMessage *contactResponse = reinterpret_cast<DBContactResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactResponseMessage *>(ret.second.get());
    assert(contactResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode != 0)) {
        return true;
    }
    else {
        return false;
    }
    return false;
}

std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactSearch(sys::Service *serv,
                                                                        UTF8 primaryName,
                                                                        UTF8 alternativeName,
                                                                        UTF8 number)
auto DBServiceAPI::ContactSearch(sys::Service *serv, UTF8 primaryName, UTF8 alternativeName, UTF8 number)
    -> std::unique_ptr<std::vector<ContactRecord>>
{
    std::shared_ptr<DBContactSearchMessage> msg =
        std::make_shared<DBContactSearchMessage>(MessageType::DBContactSearch,


@@ 272,25 234,25 @@ std::unique_ptr<std::vector<ContactRecord>> DBServiceAPI::ContactSearch(sys::Ser
                                                 (alternativeName.length() > 0) ? alternativeName.c_str() : "",
                                                 (number.length() > 0) ? number.c_str() : "");

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBContactResponseMessage *contactResponse = reinterpret_cast<DBContactResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto contactResponse = dynamic_cast<DBContactResponseMessage *>(ret.second.get());
    assert(contactResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (contactResponse->retCode != 0)) {
        return std::move(contactResponse->records);
    }
    else {
        return std::make_unique<std::vector<ContactRecord>>();
    }
    return std::make_unique<std::vector<ContactRecord>>();
}

CalllogRecord DBServiceAPI::CalllogAdd(sys::Service *serv, const CalllogRecord &rec)
auto DBServiceAPI::CalllogAdd(sys::Service *serv, const CalllogRecord &rec) -> CalllogRecord
{
    std::shared_ptr<DBCalllogMessage> msg = std::make_shared<DBCalllogMessage>(MessageType::DBCalllogAdd, rec);

    LOG_DEBUG("CalllogAdd %s", utils::to_string(rec).c_str());

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBCalllogResponseMessage *calllogResponse = reinterpret_cast<DBCalllogResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto calllogResponse = dynamic_cast<DBCalllogResponseMessage *>(ret.second.get());
    assert(calllogResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode != 0)) {
        auto records = *calllogResponse->records;
        if (!records.empty()) {
            return records[0];


@@ 300,52 262,49 @@ CalllogRecord DBServiceAPI::CalllogAdd(sys::Service *serv, const CalllogRecord &
    return CalllogRecord();
}

bool DBServiceAPI::CalllogRemove(sys::Service *serv, uint32_t id)
auto DBServiceAPI::CalllogRemove(sys::Service *serv, uint32_t id) -> bool
{
    std::shared_ptr<DBCalllogMessage> msg = std::make_shared<DBCalllogMessage>(MessageType::DBCalllogRemove);
    msg->id                               = id;

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBCalllogResponseMessage *calllogResponse = reinterpret_cast<DBCalllogResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto calllogResponse = dynamic_cast<DBCalllogResponseMessage *>(ret.second.get());
    assert(calllogResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode != 0)) {
        return true;
    }
    else {
        return false;
    }
    return false;
}

bool DBServiceAPI::CalllogUpdate(sys::Service *serv, const CalllogRecord &rec)
auto DBServiceAPI::CalllogUpdate(sys::Service *serv, const CalllogRecord &rec) -> bool
{
    std::shared_ptr<DBCalllogMessage> msg = std::make_shared<DBCalllogMessage>(MessageType::DBCalllogUpdate, rec);

    LOG_DEBUG("CalllogUpdate %s", utils::to_string(rec).c_str());

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBCalllogResponseMessage *calllogResponse = reinterpret_cast<DBCalllogResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto calllogResponse = dynamic_cast<DBCalllogResponseMessage *>(ret.second.get());
    assert(calllogResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode != 0)) {
        return true;
    }
    else {
        return false;
    }
    return false;
}

uint32_t DBServiceAPI::CalllogGetCount(sys::Service *serv, EntryState state)
auto DBServiceAPI::CalllogGetCount(sys::Service *serv, EntryState state) -> uint32_t
{
    std::shared_ptr<DBCalllogMessage> msg = std::make_shared<DBCalllogGetCount>(state);

    auto ret                                  = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBCalllogResponseMessage *calllogResponse = reinterpret_cast<DBCalllogResponseMessage *>(ret.second.get());
    if ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode == true)) {
    auto ret             = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto calllogResponse = dynamic_cast<DBCalllogResponseMessage *>(ret.second.get());
    assert(calllogResponse != nullptr);
    if ((ret.first == sys::ReturnCodes::Success) && (calllogResponse->retCode != 0)) {
        return calllogResponse->count;
    }
    else {
        return 0;
    }
    return 0;
}

bool DBServiceAPI::CalllogGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit)
auto DBServiceAPI::CalllogGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit) -> bool
{
    std::shared_ptr<DBCalllogMessage> msg = std::make_shared<DBCalllogMessage>(MessageType::DBCalllogGetLimitOffset);
    msg->offset                           = offset;


@@ 355,22 314,21 @@ bool DBServiceAPI::CalllogGetLimitOffset(sys::Service *serv, uint32_t offset, ui
    return true;
}

uint32_t DBServiceAPI::GetCountryCodeByMCC(sys::Service *serv, uint32_t mcc)
auto DBServiceAPI::GetCountryCodeByMCC(sys::Service *serv, uint32_t mcc) -> uint32_t
{
    std::shared_ptr<DBCountryCodeMessage> msg =
        std::make_shared<DBCountryCodeMessage>(MessageType::DBCountryCode, mcc, 0);

    auto ret                               = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    DBCountryCodeResponseMessage *response = reinterpret_cast<DBCountryCodeResponseMessage *>(ret.second.get());
    auto ret      = sys::Bus::SendUnicast(msg, service::name::db, serv, DefaultTimeoutInMs);
    auto response = dynamic_cast<DBCountryCodeResponseMessage *>(ret.second.get());
    assert(response != nullptr);
    if (ret.first == sys::ReturnCodes::Success) {
        return (response->row.country_code);
    }
    else {
        return (0);
    }
    return 0;
}

bool DBServiceAPI::DBBackup(sys::Service *serv, std::string backupPath)
auto DBServiceAPI::DBBackup(sys::Service *serv, std::string backupPath) -> bool
{
    LOG_INFO("DBBackup %s", backupPath.c_str());



@@ 381,8 339,6 @@ bool DBServiceAPI::DBBackup(sys::Service *serv, std::string backupPath)
    if (ret.first == sys::ReturnCodes::Success) {
        return true;
    }
    else {
        LOG_ERROR("DBBackup error, return code: %s", c_str(ret.first));
        return false;
    }
    LOG_ERROR("DBBackup error, return code: %s", c_str(ret.first));
    return false;
}

M module-services/service-db/service-db/DBServiceAPI.hpp => module-services/service-db/service-db/DBServiceAPI.hpp +40 -42
@@ 6,11 6,9 @@
#include <BaseInterface.hpp>
#include <Common/Common.hpp>
#include <Common/Query.hpp>
#include <Interface/AlarmsRecord.hpp>
#include <Interface/CalllogRecord.hpp>
#include <Interface/ContactRecord.hpp>
#include <Interface/NotesRecord.hpp>
#include <Interface/SMSRecord.hpp>
#include <Interface/SMSTemplateRecord.hpp>
#include <Interface/ThreadRecord.hpp>
#include <PhoneNumber.hpp>


@@ 41,26 39,27 @@ class DBServiceAPI
  public:
    static constexpr unsigned int DefaultTimeoutInMs = 5000U;

    enum ContactVerificationError
    enum class ContactVerificationResult
    {
        emptyContactError,
        speedDialError,
        primaryNumberError,
        secondaryNumberError,
        noError
        emptyContact,
        temporaryContactExists,
        primaryNumberDuplicate,
        secondaryNumberDuplicate,
        speedDialDuplicate,
        success
    };

    static std::unique_ptr<ThreadRecord> ThreadGetByNumber(sys::Service *serv,
                                                           const utils::PhoneNumber::View &phoneNumber,
                                                           std::uint32_t timeout = DefaultTimeoutInMs);
    static bool ThreadGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit);
    static uint32_t ThreadGetCount(sys::Service *serv, EntryState state = EntryState::ALL);
    static auto ThreadGetByNumber(sys::Service *serv,
                                  const utils::PhoneNumber::View &phoneNumber,
                                  std::uint32_t timeout = DefaultTimeoutInMs) -> std::unique_ptr<ThreadRecord>;
    static auto ThreadGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit) -> bool;
    static auto ThreadGetCount(sys::Service *serv, EntryState state = EntryState::ALL) -> uint32_t;

    static auto GetQuery(sys::Service *serv, db::Interface::Name database, std::unique_ptr<db::Query> query) -> bool;
    static sys::SendResult GetQueryWithReply(sys::Service *serv,
                                             db::Interface::Name database,
                                             std::unique_ptr<db::Query> query,
                                             std::uint32_t timeout);
    static auto GetQueryWithReply(sys::Service *serv,
                                  db::Interface::Name database,
                                  std::unique_ptr<db::Query> query,
                                  std::uint32_t timeout) -> sys::SendResult;

    /**
     * @brief Function is checking if new contact can be added to database. Function is blocking.


@@ 75,19 74,20 @@ class DBServiceAPI
     *
     * @note This function is blocking. It's checking until first error.
     */
    static ContactVerificationError verifyContact(sys::Service *serv, const ContactRecord &rec);
    static std::string getVerificationErrorString(const ContactVerificationError err);
    static std::unique_ptr<std::vector<ContactRecord>> ContactGetByID(sys::Service *serv, uint32_t contactID);
    static std::unique_ptr<std::vector<ContactRecord>> ContactGetByIDWithTemporary(sys::Service *serv,
                                                                                   uint32_t contactID);
    static auto verifyContact(sys::Service *serv, const ContactRecord &rec) -> ContactVerificationResult;
    static auto ContactGetByID(sys::Service *serv, uint32_t contactID) -> std::unique_ptr<std::vector<ContactRecord>>;
    static auto ContactGetByIDWithTemporary(sys::Service *serv, uint32_t contactID)
        -> std::unique_ptr<std::vector<ContactRecord>>;

  private:
    static std::unique_ptr<std::vector<ContactRecord>> ContactGetByIDCommon(
        sys::Service *serv, std::shared_ptr<DBContactMessage> contactMsg);
    static auto ContactGetByIDCommon(sys::Service *serv, std::shared_ptr<DBContactMessage> contactMsg)
        -> std::unique_ptr<std::vector<ContactRecord>>;

  public:
    static std::unique_ptr<std::vector<ContactRecord>> ContactGetBySpeeddial(sys::Service *serv, UTF8 speeddial);
    static std::unique_ptr<std::vector<ContactRecord>> ContactGetByPhoneNumber(sys::Service *serv, UTF8 phoneNumber);
    static auto ContactGetBySpeeddial(sys::Service *serv, UTF8 speeddial)
        -> std::unique_ptr<std::vector<ContactRecord>>;
    static auto ContactGetByPhoneNumber(sys::Service *serv, UTF8 phoneNumber)
        -> std::unique_ptr<std::vector<ContactRecord>>;

    /**
     * @brief Matches single Contact by a provided phone number


@@ 96,24 96,22 @@ class DBServiceAPI
     * @param numberView - number to match contact with
     * @return std::unique_ptr<ContactRecord>
     */
    static std::unique_ptr<ContactRecord> MatchContactByPhoneNumber(sys::Service *serv,
                                                                    const utils::PhoneNumber::View &numberView);
    static bool ContactAdd(sys::Service *serv, const ContactRecord &rec);
    static bool ContactRemove(sys::Service *serv, uint32_t id);
    static bool ContactUpdate(sys::Service *serv, const ContactRecord &rec);
    static std::unique_ptr<std::vector<ContactRecord>> ContactSearch(sys::Service *serv,
                                                                     UTF8 primaryName,
                                                                     UTF8 alternativeName,
                                                                     UTF8 number);
    static auto MatchContactByPhoneNumber(sys::Service *serv, const utils::PhoneNumber::View &numberView)
        -> std::unique_ptr<ContactRecord>;
    static auto ContactAdd(sys::Service *serv, const ContactRecord &rec) -> bool;
    static auto ContactRemove(sys::Service *serv, uint32_t id) -> bool;
    static auto ContactUpdate(sys::Service *serv, const ContactRecord &rec) -> bool;
    static auto ContactSearch(sys::Service *serv, UTF8 primaryName, UTF8 alternativeName, UTF8 number)
        -> std::unique_ptr<std::vector<ContactRecord>>;

    static CalllogRecord CalllogAdd(sys::Service *serv, const CalllogRecord &rec);
    static bool CalllogRemove(sys::Service *serv, uint32_t id);
    static bool CalllogUpdate(sys::Service *serv, const CalllogRecord &rec);
    static uint32_t CalllogGetCount(sys::Service *serv, EntryState state = EntryState::ALL);
    static bool CalllogGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit);
    static auto CalllogAdd(sys::Service *serv, const CalllogRecord &rec) -> CalllogRecord;
    static auto CalllogRemove(sys::Service *serv, uint32_t id) -> bool;
    static auto CalllogUpdate(sys::Service *serv, const CalllogRecord &rec) -> bool;
    static auto CalllogGetCount(sys::Service *serv, EntryState state = EntryState::ALL) -> uint32_t;
    static auto CalllogGetLimitOffset(sys::Service *serv, uint32_t offset, uint32_t limit) -> bool;

    /* country codes */
    static uint32_t GetCountryCodeByMCC(sys::Service *serv, uint32_t mcc);
    static auto GetCountryCodeByMCC(sys::Service *serv, uint32_t mcc) -> uint32_t;

    static bool DBBackup(sys::Service *serv, std::string backupPath);
    static auto DBBackup(sys::Service *serv, std::string backupPath) -> bool;
};

M module-services/service-db/test/test-service-db-api.cpp => module-services/service-db/test/test-service-db-api.cpp +10 -12
@@ 6,55 6,53 @@
#include <service-db/DBServiceAPI.hpp> // for DBServiceAPI, DBServiceAPI::noError, DBServiceAPI::ContactVerificationError, DBServiceAPI::emptyContactError
#include <memory> // for allocator

#include "utf8/UTF8.hpp" // for UTF8

TEST_CASE("DB_API")
{
    SECTION("DBServiceAPI::verifyContact emptyContactError")
    {
        ContactRecord testRecord;

        DBServiceAPI::ContactVerificationError err = DBServiceAPI::verifyContact(nullptr, testRecord);
        REQUIRE(err == DBServiceAPI::emptyContactError);
        DBServiceAPI::ContactVerificationResult err = DBServiceAPI::verifyContact(nullptr, testRecord);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::emptyContact);

        testRecord.primaryName = "testName";
        err                    = DBServiceAPI::verifyContact(nullptr, testRecord);
        testRecord.primaryName.clear();
        REQUIRE(err == DBServiceAPI::noError);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::success);

        testRecord.alternativeName = "testName";
        err                        = DBServiceAPI::verifyContact(nullptr, testRecord);
        testRecord.alternativeName.clear();
        REQUIRE(err == DBServiceAPI::noError);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::success);

        // Here should be tested contact with single number, but it would require involving sys::bus.

        testRecord.mail = "test@mail.com";
        err             = DBServiceAPI::verifyContact(nullptr, testRecord);
        testRecord.mail.clear();
        REQUIRE(err == DBServiceAPI::noError);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::success);

        testRecord.mail = "test@mail.com";
        err             = DBServiceAPI::verifyContact(nullptr, testRecord);
        testRecord.mail.clear();
        REQUIRE(err == DBServiceAPI::noError);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::success);

        testRecord.speeddial = "1";
        err                  = DBServiceAPI::verifyContact(nullptr, testRecord);
        testRecord.speeddial.clear();
        REQUIRE(err == DBServiceAPI::emptyContactError);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::emptyContact);

        testRecord.address = "test address";
        err                = DBServiceAPI::verifyContact(nullptr, testRecord);
        testRecord.address.clear();
        REQUIRE(err == DBServiceAPI::noError);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::success);

        testRecord.note = "test note";
        err             = DBServiceAPI::verifyContact(nullptr, testRecord);
        testRecord.note.clear();
        REQUIRE(err == DBServiceAPI::noError);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::success);

        err = DBServiceAPI::verifyContact(nullptr, testRecord);
        REQUIRE(err == DBServiceAPI::emptyContactError);
        REQUIRE(err == DBServiceAPI::ContactVerificationResult::emptyContact);
    }
}

M module-services/service-desktop/endpoints/backup/BackupRestore.cpp => module-services/service-desktop/endpoints/backup/BackupRestore.cpp +3 -2
@@ 9,6 9,7 @@
#include <purefs/filesystem_paths.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/DBServiceName.hpp>
#include <module-utils/Utils.hpp>

#include <cassert>
#include <filesystem>


@@ 116,7 117,7 @@ bool BackupRestore::PackUserFiles()
    std::string backupPathDB = PATH_BACKUP;
    backupPathDB += "/";

    const auto backupOSPath                  = purefs::dir::getBackupOSPath();
    const auto backupOSPath = purefs::dir::getBackupOSPath();

    if (std::filesystem::is_empty(backupOSPath.c_str())) {
        LOG_ERROR("PackUserFiles: backup dir %s is empty, nothing to backup, quitting...", backupOSPath.c_str());


@@ 330,7 331,7 @@ bool BackupRestore::UnpackBackupFile()
bool BackupRestore::ReplaceUserFiles()
{
    /* replace existing files that have respective backup files existing */
    const auto backupOSPath                  = purefs::dir::getBackupOSPath();
    const auto backupOSPath = purefs::dir::getBackupOSPath();
    if (std::filesystem::is_directory(backupOSPath) && std::filesystem::is_empty(backupOSPath)) {
        LOG_INFO("ReplaceUserFiles: dir emtpy, nothing to restore, quitting...");
        return false;