// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "NotificationsRecord.hpp" #include "module-db/queries/notifications/QueryNotificationsGet.hpp" #include "module-db/queries/notifications/QueryNotificationsIncrement.hpp" #include "module-db/queries/notifications/QueryNotificationsDecrement.hpp" #include "module-db/queries/notifications/QueryNotificationsMultipleIncrement.hpp" #include "module-db/queries/notifications/QueryNotificationsClear.hpp" #include "module-db/queries/notifications/QueryNotificationsGetAll.hpp" #include "module-db/databases/NotificationsDB.hpp" #include #include #include #include #include NotificationsRecord::NotificationsRecord(const NotificationsTableRow &tableRow, std::optional record) : Record{tableRow.ID}, value{tableRow.value}, contactRecord{std::move(record)} { if (tableRow.key > static_cast(Key::NotValidKey) && tableRow.key < static_cast(Key::NumberOfKeys)) { key = static_cast(tableRow.key); return; } LOG_ERROR("Invalid record"); key = Key::NotValidKey; ID = DB_ID_NONE; } bool NotificationsRecord::isValidKey(Key key) { return key != Key::NotValidKey && key != Key::NumberOfKeys; } std::ostream &operator<<(std::ostream &out, const NotificationsRecord &rec) { out << " " << rec.ID << " " << static_cast(rec.key) << " " << rec.value; if (rec.contactRecord.has_value()) { out << " " << rec.contactRecord.value().ID; } return out; } NotificationsRecordInterface::NotificationsRecordInterface(NotificationsDB *notificationsDb, ContactRecordInterface *contactsDb) : notificationsDb(notificationsDb), contactsDb(contactsDb) { Expects(contactsDb != nullptr); } bool NotificationsRecordInterface::Add(const NotificationsRecord &rec) { assert(0 && "Not implemented"); return false; } std::unique_ptr> NotificationsRecordInterface::GetLimitOffsetByField( uint32_t offset, uint32_t limit, NotificationsRecordField field, const char *str) { assert(0 && "Not implemented"); return GetLimitOffset(offset, limit); } std::unique_ptr> NotificationsRecordInterface::GetLimitOffset(uint32_t offset, uint32_t limit) { if (limit == 0) { limit = GetCount(); } auto rows = notificationsDb->notifications.getLimitOffset(offset, limit); auto records = std::make_unique>(); for (auto &r : rows) { records->push_back(NotificationsRecord{r, getContactRecord(r.contactID)}); } return records; } bool NotificationsRecordInterface::Update(const NotificationsRecord &rec) { auto entry = notificationsDb->notifications.getById(rec.ID); if (!entry.isValid() || entry.key != static_cast(rec.key)) { return false; } uint32_t contactId = rec.contactRecord.has_value() ? rec.contactRecord.value().ID : DB_ID_NONE; return notificationsDb->notifications.update(NotificationsTableRow{ Record(rec.ID), .key = static_cast(rec.key), .value = rec.value, .contactID = contactId}); } bool NotificationsRecordInterface::RemoveByID(uint32_t id) { assert(0 && "Not implemented"); return false; } bool NotificationsRecordInterface::RemoveByField(NotificationsRecordField field, const char *str) { assert(0 && "Not implemented"); return false; } std::optional NotificationsRecordInterface::getContactRecord(uint32_t id) const { if (id != DB_ID_NONE) { if (auto contactRecord = contactsDb->GetByIdWithTemporary(id); contactRecord.isValid()) { return std::make_optional(std::move(contactRecord)); } } return std::nullopt; } NotificationsRecord NotificationsRecordInterface::GetByID(uint32_t id) { auto tableRow = notificationsDb->notifications.getById(id); return NotificationsRecord{tableRow, getContactRecord(tableRow.contactID)}; } uint32_t NotificationsRecordInterface::GetCount() { return notificationsDb->notifications.count(); } NotificationsRecord NotificationsRecordInterface::GetByKey(NotificationsRecord::Key key) { if (!NotificationsRecord::isValidKey(key)) { return NotificationsRecord(); } auto tableRow = notificationsDb->notifications.getByKey(static_cast(key)); return NotificationsRecord{tableRow, getContactRecord(tableRow.contactID)}; } std::unique_ptr NotificationsRecordInterface::runQuery(std::shared_ptr query) { if (const auto local_query = dynamic_cast(query.get())) { return runQueryImpl(local_query); } if (const auto local_query = dynamic_cast(query.get())) { return runQueryImpl(local_query); } if (const auto local_query = dynamic_cast(query.get())) { return runQueryImpl(local_query); } if (const auto local_query = dynamic_cast(query.get())) { return runQueryImpl(local_query); } if (const auto local_query = dynamic_cast(query.get())) { return runQueryImpl(local_query); } if (const auto local_query = dynamic_cast(query.get())) { return runQueryImpl(local_query); } return nullptr; } std::unique_ptr NotificationsRecordInterface::runQueryImpl( const db::query::notifications::Get *query) { auto value = GetByKey(query->getKey()); return std::make_unique(std::move(value)); } std::unique_ptr NotificationsRecordInterface::runQueryImpl( const db::query::notifications::Increment *query) { auto ret = processIncrement(query->getKey(), query->getNumber(), 1); return std::make_unique(ret); } std::unique_ptr NotificationsRecordInterface::runQueryImpl( const db::query::notifications::Decrement *query) { auto ret = processDecrement(query->getKey(), query->getCount()); return std::make_unique(ret); } std::unique_ptr NotificationsRecordInterface::runQueryImpl( const db::query::notifications::MultipleIncrement *query) { auto ret = false; const auto &numbers = query->getNumbers(); std::optional number; if (!numbers.empty()) { if (all_of(numbers.begin(), numbers.end(), [&](const auto &number) { return number == numbers.at(0); })) { number = std::make_optional(numbers.at(0)); } ret = processIncrement(query->getKey(), std::move(number), numbers.size()); } return std::make_unique(ret); } std::unique_ptr NotificationsRecordInterface::runQueryImpl( const db::query::notifications::Clear *query) { auto ret = false; if (auto record = GetByKey(query->getKey()); record.isValid()) { record.value = 0; record.contactRecord.reset(); ret = Update(record); } return std::make_unique(ret); } std::unique_ptr NotificationsRecordInterface::runQueryImpl( const db::query::notifications::GetAll *query) { auto numberOfNotifications = GetCount(); auto records = GetLimitOffset(0, numberOfNotifications); return std::make_unique(std::move(records)); } bool NotificationsRecordInterface::processIncrement(NotificationsRecord::Key key, std::optional &&number, size_t size) { auto ret = false; if (auto record = GetByKey(key); record.isValid() && number.has_value()) { auto ¤tContactRecord = record.contactRecord; if (size == 1) { if (auto numberMatch = contactsDb->MatchByNumber(number.value()); numberMatch.has_value()) { if (record.value == 0) { currentContactRecord = std::move(numberMatch.value().contact); } else if (currentContactRecord.has_value() && numberMatch.value().contactId != currentContactRecord.value().ID) { currentContactRecord.reset(); } } else { currentContactRecord.reset(); } } else if (size > 1) { currentContactRecord.reset(); } record.value += size; ret = Update(record); } return ret; } bool NotificationsRecordInterface::processDecrement(NotificationsRecord::Key key, size_t delta) { auto ret = false; if (auto record = GetByKey(key); record.isValid() && record.value > 0) { if (delta >= record.value) { record.contactRecord.reset(); record.value = 0; } else { record.value -= delta; } ret = Update(record); } return ret; }