~aleteoryx/muditaos

0f36b07f60d8c57bf9fb4c4fb4e9677f9ee6f339 — Pawel Paprocki 5 years ago a63628a
[EGD-4467] Support for deleting entries in FI DB (#1175)

M changelog.md => changelog.md +1 -0
@@ 14,6 14,7 @@
* `[bluetooth][settings]` Add Bluetooth settings to database.
* `[PowerManagement]` Added CPU load measurement.
* `[alarms]` Added new/edit alarm window
* `[file indexer]` Support for deleting entries in File Indexer DB.

### Changed


M module-services/service-db/agents/file_indexer/FileIndexerAgent.cpp => module-services/service-db/agents/file_indexer/FileIndexerAgent.cpp +112 -15
@@ 7,6 7,7 @@
#include <log/log.hpp>
#include <module-sys/Service/Bus.hpp>
#include <purefs/filesystem_paths.hpp>
#include <Application.hpp>
#include <memory>

FileIndexerAgent::FileIndexerAgent(sys::Service *parentService) : DatabaseAgent(parentService)


@@ 62,6 63,11 @@ void FileIndexerAgent::registerMessages()
    parentService->connect(FileIndexer::Messages::SetPropertiesMessage(),
                           std::bind(&FileIndexerAgent::handleSetProperties, this, _1));

    parentService->connect(FileIndexer::Messages::DeleteFileMessage(),
                           std::bind(&FileIndexerAgent::handleDeleteFile, this, _1));
    parentService->connect(FileIndexer::Messages::DeleteAllFilesInDirMessage(),
                           std::bind(&FileIndexerAgent::handleDeleteAllFilesInDir, this, _1));

    parentService->connect(FileIndexer::Messages::RegisterOnFileChange(),
                           std::bind(&FileIndexerAgent::handleRegisterOnFileChange, this, _1));
    parentService->connect(FileIndexer::Messages::UnregisterOnFileChange(),


@@ 146,7 152,16 @@ auto FileIndexerAgent::dbGetFilesCount() -> unsigned int
    if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
        return 0;
    }
    return (*retQuery)[0].getInt32();
    return (*retQuery)[0].getUInt32();
}

auto FileIndexerAgent::dbGeMetadataCount() -> unsigned int
{
    auto retQuery = database->query(FileIndexer::Statements::getMetadataCount);
    if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
        return 0;
    }
    return (*retQuery)[0].getUInt32();
}

auto FileIndexerAgent::dbListDir(std::unique_ptr<FileIndexer::ListDirData> listDir)


@@ 247,6 262,45 @@ auto FileIndexerAgent::dbSetRecord(std::unique_ptr<FileIndexer::FileRecord> reco
                             retRecord.file_id);
}

auto FileIndexerAgent::dbDeleteFile(std::unique_ptr<FileIndexer::FileRecord> record) -> bool
{
    unsigned int file_id = FileIndexer::FILE_ID_NOT_EXISTS;
    if (false == record->path.empty()) {
        auto retQuery = database->query(FileIndexer::Statements::getFileIdByPath, record->path.c_str());
        if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
            return false;
        }
        else {
            file_id = (*retQuery)[0].getUInt32();
        }
    }
    else {
        file_id = record->file_id;
    }
    return database->execute(FileIndexer::Statements::deleteFileById, file_id, file_id);
}

auto FileIndexerAgent::dbDeleteAllFilesInDir(std::unique_ptr<FileIndexer::FileRecord> record) -> bool
{
    bool retStatus = false;
    if (false == record->directory.empty()) {
        auto retQuery = database->query(FileIndexer::Statements::getFilesIdByDir, record->directory.c_str());
        if (nullptr == retQuery || FileIndexer::ZERO_ROWS_FOUND == retQuery->getRowCount()) {
            return false;
        }
        else {
            do {
                unsigned int file_id = (*retQuery)[0].getUInt32();
                retStatus            = database->execute(FileIndexer::Statements::deleteFileById, file_id, file_id);
            } while (retQuery->nextRow() && retStatus);
            return retStatus;
        }
    }
    else {
        return false;
    }
}

auto FileIndexerAgent::dbUpdateRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> bool
{
    return database->execute(FileIndexer::Statements::updateFileInfo,


@@ 267,7 321,7 @@ auto FileIndexerAgent::handleGetRecord(sys::Message *req) -> sys::MessagePointer
        return std::make_shared<FileIndexer::Messages::GetRecordResponseMessage>(
            std::make_unique<FileIndexer::FileRecord>(msg->dbRecord));
    }
    return std::make_shared<sys::ResponseMessage>();
    return app::msgHandled();
}

auto FileIndexerAgent::handleSetRecord(sys::Message *req) -> sys::MessagePointer


@@ 285,13 339,56 @@ auto FileIndexerAgent::handleSetRecord(sys::Message *req) -> sys::MessagePointer
            dbSetRecord(std::make_unique<FileIndexer::FileRecord>(record));

            for (auto recipient : fileChangeRecipents[record.directory]) {
                auto notifeMsg = std::make_shared<FileIndexer::Messages::FileChanged>(
                auto notifyMsg = std::make_shared<FileIndexer::Messages::DirectoryContentChangedMessage>(
                    std::make_unique<std::string>(record.directory));
                sys::Bus::SendUnicast(std::move(notifeMsg), recipient, parentService);
                sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
            }
        }
    }
    return std::make_shared<sys::ResponseMessage>();
    return app::msgHandled();
}

auto FileIndexerAgent::handleDeleteFile(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<FileIndexer::Messages::DeleteFileMessage *>(req)) {
        auto recordPtr                 = std::move(msg->record);
        FileIndexer::FileRecord record = *recordPtr;
        msg->dbRecord                  = dbGetRecord(std::make_unique<FileIndexer::FileRecord>(record));

        auto deleteMsg = std::make_shared<FileIndexer::Messages::FileDeletedMessage>(std::move(recordPtr));

        sys::Bus::SendUnicast(std::move(deleteMsg), msg->sender, parentService);
        dbDeleteFile(std::make_unique<FileIndexer::FileRecord>(record));

        for (auto recipient : fileChangeRecipents[record.directory]) {
            auto notifyMsg = std::make_shared<FileIndexer::Messages::DirectoryContentChangedMessage>(
                std::make_unique<std::string>(record.directory));
            sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
        }
    }
    return app::msgHandled();
}

auto FileIndexerAgent::handleDeleteAllFilesInDir(sys::Message *req) -> sys::MessagePointer
{
    if (auto msg = dynamic_cast<FileIndexer::Messages::DeleteAllFilesInDirMessage *>(req)) {
        auto recordPtr                 = std::move(msg->record);
        FileIndexer::FileRecord record = *recordPtr;
        msg->dbRecord                  = dbGetRecord(std::make_unique<FileIndexer::FileRecord>(record));

        auto deleteMsg =
            std::make_shared<FileIndexer::Messages::AllFilesInDirDeletedDeletedMessage>(std::move(recordPtr));

        sys::Bus::SendUnicast(std::move(deleteMsg), msg->sender, parentService);
        dbDeleteAllFilesInDir(std::make_unique<FileIndexer::FileRecord>(record));

        for (auto recipient : fileChangeRecipents[record.directory]) {
            auto notifyMsg = std::make_shared<FileIndexer::Messages::DirectoryContentChangedMessage>(
                std::make_unique<std::string>(record.directory));
            sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
        }
    }
    return app::msgHandled();
}

auto FileIndexerAgent::dbGetProperty(std::unique_ptr<FileIndexer::FileMetadata> metaData) -> FileIndexer::FileMetadata


@@ 306,7 403,7 @@ auto FileIndexerAgent::dbGetProperty(std::unique_ptr<FileIndexer::FileMetadata> 
        return *metaData;
    }
    FileIndexer::FileMetadata retMetaData;
    retMetaData.file_id = (*retQuery)[0].getInt32();
    retMetaData.file_id = (*retQuery)[0].getUInt32();
    retMetaData.properties.clear();
    retMetaData.properties.emplace((*retQuery)[1].getString(), (*retQuery)[2].getString());
    return retMetaData;


@@ 323,7 420,7 @@ auto FileIndexerAgent::dbGetAllProperties(std::unique_ptr<FileIndexer::FileMetad
        return retMetaData;
    }
    if (retQuery->getRowCount() > 0) {
        retMetaData.file_id = (*retQuery)[0].getInt32();
        retMetaData.file_id = (*retQuery)[0].getUInt32();
        retMetaData.properties.clear();
        do {
            retMetaData.properties.emplace((*retQuery)[1].getString(), (*retQuery)[2].getString());


@@ 342,7 439,7 @@ auto FileIndexerAgent::dbSetProperty(std::unique_ptr<FileIndexer::FileMetadata> 
        if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
            return false;
        }
        fileId = (*retQuery)[0].getInt32();
        fileId = (*retQuery)[0].getUInt32();
    }
    else if (metaData->file_id != FileIndexer::FILE_ID_NOT_EXISTS) {
        retQuery = database->query(FileIndexer::Statements::getFileInfoById, metaData->file_id);


@@ 373,7 470,7 @@ auto FileIndexerAgent::dbSetProperties(std::unique_ptr<FileIndexer::FileMetadata
        if (nullptr == retQuery || FileIndexer::ONE_ROW_FOUND != retQuery->getRowCount()) {
            return false;
        }
        fileId = (*retQuery)[0].getInt32();
        fileId = (*retQuery)[0].getUInt32();
    }
    else if (metaData->file_id != FileIndexer::FILE_ID_NOT_EXISTS) {
        retQuery = database->query(FileIndexer::Statements::getFileInfoById, metaData->file_id);


@@ 419,7 516,7 @@ auto FileIndexerAgent::handleGetProperty(sys::Message *req) -> sys::MessagePoint
        return std::make_shared<FileIndexer::Messages::GetPropertyResponseMessage>(
            std::make_unique<FileIndexer::FileMetadata>(msg->dbMetaData));
    }
    return std::make_shared<sys::ResponseMessage>();
    return app::msgHandled();
}

auto FileIndexerAgent::handleGetAllProperties(sys::Message *req) -> sys::MessagePointer


@@ 431,7 528,7 @@ auto FileIndexerAgent::handleGetAllProperties(sys::Message *req) -> sys::Message
        return std::make_shared<FileIndexer::Messages::GetPropertyResponseMessage>(
            std::make_unique<FileIndexer::FileMetadata>(msg->dbMetaData));
    }
    return std::make_shared<sys::ResponseMessage>();
    return app::msgHandled();
}

auto FileIndexerAgent::handleSetProperty(sys::Message *req) -> sys::MessagePointer


@@ 454,7 551,7 @@ auto FileIndexerAgent::handleSetProperty(sys::Message *req) -> sys::MessagePoint
            dbSetProperty(std::make_unique<FileIndexer::FileMetadata>(metaData));
        }
    }
    return std::make_shared<sys::ResponseMessage>();
    return app::msgHandled();
}

auto FileIndexerAgent::handleSetProperties(sys::Message *req) -> sys::MessagePointer


@@ 469,10 566,10 @@ auto FileIndexerAgent::handleSetProperties(sys::Message *req) -> sys::MessagePoi
        dbSetProperties(std::make_unique<FileIndexer::FileMetadata>(metaData));

        for (auto recipient : fileChangeRecipents[metaData.directory]) {
            auto notifyMsg =
                std::make_shared<FileIndexer::Messages::FileChanged>(std::make_unique<std::string>(metaData.directory));
            auto notifyMsg = std::make_shared<FileIndexer::Messages::DirectoryContentChangedMessage>(
                std::make_unique<std::string>(metaData.directory));
            sys::Bus::SendUnicast(std::move(notifyMsg), recipient, parentService);
        }
    }
    return std::make_shared<sys::ResponseMessage>();
    return app::msgHandled();
}

M module-services/service-db/agents/file_indexer/FileIndexerAgent.hpp => module-services/service-db/agents/file_indexer/FileIndexerAgent.hpp +5 -0
@@ 28,6 28,8 @@ class FileIndexerAgent : public DatabaseAgent
    auto handleSetProperties(sys::Message *req) -> sys::MessagePointer;
    auto handleGetRecord(sys::Message *req) -> sys::MessagePointer;
    auto handleSetRecord(sys::Message *req) -> sys::MessagePointer;
    auto handleDeleteFile(sys::Message *req) -> sys::MessagePointer;
    auto handleDeleteAllFilesInDir(sys::Message *req) -> sys::MessagePointer;

    using MapOfRecipentsToBeNotified = std::map<std::string, std::set<std::string>>;
    MapOfRecipentsToBeNotified fileChangeRecipents;


@@ 44,8 46,11 @@ class FileIndexerAgent : public DatabaseAgent
    auto dbGetAllProperties(std::unique_ptr<FileIndexer::FileMetadata> metaData) -> FileIndexer::FileMetadata;
    auto dbGetRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> FileIndexer::FileRecord;
    auto dbSetRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> bool;
    auto dbDeleteFile(std::unique_ptr<FileIndexer::FileRecord> record) -> bool;
    auto dbDeleteAllFilesInDir(std::unique_ptr<FileIndexer::FileRecord> record) -> bool;
    auto dbUpdateRecord(std::unique_ptr<FileIndexer::FileRecord> record) -> bool;
    auto getDbInitString() -> const std::string override;
    auto getDbFilePath() -> const std::string override;
    auto dbGetFilesCount() -> unsigned int;
    auto dbGeMetadataCount() -> unsigned int;
};

M module-services/service-db/agents/file_indexer/FileIndexer_queries.hpp => module-services/service-db/agents/file_indexer/FileIndexer_queries.hpp +23 -1
@@ 10,6 10,10 @@ namespace FileIndexer::Statements
                        SELECT COUNT(FT.file_id) AS FILE_PATH_EXISTS FROM  file_tab AS FT;
                        )sql";

    constexpr auto getMetadataCount = R"sql(
                        SELECT COUNT(MT.file_id) AS FILE_PATH_EXISTS FROM  metadata_tab AS MT;
                        )sql";

    constexpr auto checkFileExists = R"sql(
                        SELECT COUNT(size) AS FILE_PATH_EXISTS FROM  file_tab AS FT
                        WHERE FT.path = '%q'


@@ 17,7 21,7 @@ namespace FileIndexer::Statements
                        )sql";

    constexpr auto getFileIdByPath = R"sql(
                        SELECT FT.file_id file_tab AS FT
                        SELECT FT.file_id FROM file_tab AS FT
                        WHERE FT.path = '%q'
                        COLLATE NOCASE;
                        )sql";


@@ 61,6 65,24 @@ namespace FileIndexer::Statements
                        WHERE file_id = '%lu' ;
                        )sql";

    constexpr auto deleteFileById = R"sql(
                        DELETE FROM file_tab AS FT
                        WHERE FT.file_id = '%lu';
                        DELETE FROM metadata_tab AS MT
                        WHERE MT.file_id = '%lu';
                        )sql";

    constexpr auto deleteAllFileInfoInDir = R"sql(
                        DELETE FROM file_tab AS FT
                        WHERE FT.directory = '%q';
                        )sql";

    constexpr auto getFilesIdByDir = R"sql(
                        SELECT file_id FROM  file_tab AS FT
                        WHERE FT.directory = '%q'
                        COLLATE NOCASE;
                        )sql";

    constexpr auto getPropertyValue = R"sql(
                         SELECT MT.file_id, MT.property, MT.value
                         FROM metadata_tab AS MT, file_tab AS FT

M module-services/service-db/service-db/FileIndexerMessages.hpp => module-services/service-db/service-db/FileIndexerMessages.hpp +44 -3
@@ 109,11 109,11 @@ namespace FileIndexer
            std::unique_ptr<std::string> directory;
        };

        class FileChanged : public FileIndexerMessage
        class DirectoryContentChangedMessage : public FileIndexerMessage
        {
          public:
            FileChanged() = default;
            explicit FileChanged(std::unique_ptr<std::string> dir) : directory(std::move(dir))
            DirectoryContentChangedMessage() = default;
            explicit DirectoryContentChangedMessage(std::unique_ptr<std::string> dir) : directory(std::move(dir))
            {}

            std::unique_ptr<std::string> directory;


@@ 194,6 194,47 @@ namespace FileIndexer
            std::unique_ptr<FileRecord> record;
        };

        class DeleteRecordMessage : public RecordMessage
        {
          public:
            DeleteRecordMessage() = default;
            DeleteRecordMessage(std::unique_ptr<FileRecord> record) : RecordMessage(std::move(record))
            {}
        };

        class DeleteFileMessage : public RecordMessage
        {
          public:
            DeleteFileMessage() = default;
            DeleteFileMessage(std::unique_ptr<FileRecord> record) : RecordMessage(std::move(record))
            {}
        };

        class DeleteAllFilesInDirMessage : public RecordMessage
        {
          public:
            DeleteAllFilesInDirMessage() = default;
            DeleteAllFilesInDirMessage(std::unique_ptr<FileRecord> record) : RecordMessage(std::move(record))
            {}
        };

        class FileDeletedMessage : public RecordMessage
        {
          public:
            FileDeletedMessage() = default;
            explicit FileDeletedMessage(std::unique_ptr<FileRecord> record) : RecordMessage(std::move(record))
            {}
        };

        class AllFilesInDirDeletedDeletedMessage : public RecordMessage
        {
          public:
            AllFilesInDirDeletedDeletedMessage() = default;
            explicit AllFilesInDirDeletedDeletedMessage(std::unique_ptr<FileRecord> record)
                : RecordMessage(std::move(record))
            {}
        };

        /// Property manipulation
        class PropertyMessage : public FileIndexerMessage
        {

M module-services/service-db/test/CMakeLists.txt => module-services/service-db/test/CMakeLists.txt +1 -0
@@ 14,5 14,6 @@ add_catch2_executable(
            module-audio
            service-cellular
            module-cellular
            iosyscalls
)


M module-services/service-db/test/test-service-db-file_indexer.cpp => module-services/service-db/test/test-service-db-file_indexer.cpp +89 -0
@@ 223,4 223,93 @@ TEST_CASE("FileIndexer")
        }
        REQUIRE(newPropertyValueFound == newPropertyValue);
    }

    SECTION("DeleteFile /DeleteAllFilesInDir")
    {
        FileIndexer::ListDirData inputData{};
        FileIndexer::FileRecord record{};
        std::cout << "DeleteFile /DeleteAllFilesInDir" << std::endl << std::flush;

        // SetRecord - add files
        auto recordPtr = std::make_unique<FileIndexer::FileRecord>(record);
        std::unique_ptr<FileIndexerTest> fiAgentTest{new FileIndexerTest(&file_indexer_client_service)};
        record.directory = "wav";
        record.path      = "wav/file1.wav";

        recordPtr                  = std::make_unique<FileIndexer::FileRecord>(record);
        auto setMsg                = std::make_shared<FileIndexer::Messages::SetRecordMessage>(std::move(recordPtr));
        setMsg->sender             = serviceName;
        sys::MessagePointer recMsg = fiAgentTest->handleSetRecordTest(setMsg.get());

        record.path    = "wav/file2.wav";
        recordPtr      = std::make_unique<FileIndexer::FileRecord>(record);
        setMsg         = std::make_shared<FileIndexer::Messages::SetRecordMessage>(std::move(recordPtr));
        setMsg->sender = serviceName;
        recMsg         = fiAgentTest->handleSetRecordTest(setMsg.get());

        record.path    = "wav/file3.wav";
        recordPtr      = std::make_unique<FileIndexer::FileRecord>(record);
        setMsg         = std::make_shared<FileIndexer::Messages::SetRecordMessage>(std::move(recordPtr));
        setMsg->sender = serviceName;
        recMsg         = fiAgentTest->handleSetRecordTest(setMsg.get());

        std::cout << "Get listDir" << std::endl << std::flush;
        inputData.directory   = "wav";
        inputData.list_offset = 0;
        inputData.list_limit  = 10;
        auto inputDataPtr     = std::make_unique<FileIndexer::ListDirData>(inputData);
        auto getListDirMsg    = std::make_shared<FileIndexer::Messages::GetListDirMessage>(std::move(inputDataPtr));
        recMsg                = fiAgentTest->handleListDirTest(getListDirMsg.get());
        auto msg              = dynamic_cast<FileIndexer::Messages::GetListDirResponseMessage *>(recMsg.get());
        auto fileCount        = msg->getCount();
        auto fileList         = msg->getResults();
        LOG_INFO("Total number of files in %s = %d", inputData.directory.c_str(), fileCount);
        LOG_INFO("Limit= %d Offset= %d", inputData.list_limit, inputData.list_offset);
        for (auto fileRec : fileList) {
            LOG_INFO("file id= %d file path= %s  size = %d  mime= %d  mtime = %d directory= %s ftype= %d",
                     fileRec.file_id,
                     fileRec.path.c_str(),
                     fileRec.size,
                     fileRec.mime_type,
                     fileRec.mtime,
                     fileRec.directory.c_str(),
                     fileRec.file_type);
        }

        std::cout << "DeleteFile" << std::endl << std::flush;
        record.path      = "wav/file1.wav";
        record.directory = "wav";
        recordPtr        = std::make_unique<FileIndexer::FileRecord>(record);
        auto delMsg      = std::make_shared<FileIndexer::Messages::DeleteFileMessage>(std::move(recordPtr));
        delMsg->sender   = serviceName;
        auto delRecMsg   = fiAgentTest->handleDeleteFileTest(delMsg.get());

        inputData.directory    = "wav";
        inputData.list_offset  = 0;
        inputData.list_limit   = 10;
        inputDataPtr           = std::make_unique<FileIndexer::ListDirData>(inputData);
        getListDirMsg          = std::make_shared<FileIndexer::Messages::GetListDirMessage>(std::move(inputDataPtr));
        recMsg                 = fiAgentTest->handleListDirTest(getListDirMsg.get());
        msg                    = dynamic_cast<FileIndexer::Messages::GetListDirResponseMessage *>(recMsg.get());
        auto fileCountAfterDel = msg->getCount();
        REQUIRE(fileCountAfterDel == fileCount - 1);

        std::cout << "DeleteAllFilesInDir" << std::endl << std::flush;
        record.directory  = "wav";
        recordPtr         = std::make_unique<FileIndexer::FileRecord>(record);
        auto delAllMsg    = std::make_shared<FileIndexer::Messages::DeleteAllFilesInDirMessage>(std::move(recordPtr));
        delAllMsg->sender = serviceName;
        delRecMsg         = fiAgentTest->handleDeleteAllFilesInDirTest(delAllMsg.get());

        inputData.directory   = "wav";
        inputData.list_offset = 0;
        inputData.list_limit  = 10;
        inputDataPtr          = std::make_unique<FileIndexer::ListDirData>(inputData);
        getListDirMsg         = std::make_shared<FileIndexer::Messages::GetListDirMessage>(std::move(inputDataPtr));
        recMsg                = fiAgentTest->handleListDirTest(getListDirMsg.get());
        msg                   = dynamic_cast<FileIndexer::Messages::GetListDirResponseMessage *>(recMsg.get());
        fileCountAfterDel     = msg->getCount();
        REQUIRE(fileCountAfterDel == 0);
        std::cout << "DeleteFile /DeleteAllFilesInDir  finished" << std::endl << std::flush;
    }
}

M module-services/service-db/test/test-service-db-file_indexer.hpp => module-services/service-db/test/test-service-db-file_indexer.hpp +10 -0
@@ 48,6 48,16 @@ class FileIndexerTest : public FileIndexerAgent
        return FileIndexerAgent::handleUnregisterOnFileChange(req);
    }

    auto handleDeleteFileTest(FileIndexer::Messages::DeleteFileMessage *req) -> sys::MessagePointer
    {
        return FileIndexerAgent::handleDeleteFile(req);
    }

    auto handleDeleteAllFilesInDirTest(FileIndexer::Messages::DeleteAllFilesInDirMessage *req) -> sys::MessagePointer
    {
        return FileIndexerAgent::handleDeleteAllFilesInDir(req);
    }

    unsigned int getNumberOfRecipients(std::string directory)
    {
        unsigned int recipientCount = 0;