diff --git a/services/backup/docker-server/contents/server/src/DatabaseEntities/BackupItem.cpp b/services/backup/docker-server/contents/server/src/DatabaseEntities/BackupItem.cpp index b5c6840f7..5fc222e23 100644 --- a/services/backup/docker-server/contents/server/src/DatabaseEntities/BackupItem.cpp +++ b/services/backup/docker-server/contents/server/src/DatabaseEntities/BackupItem.cpp @@ -1,111 +1,115 @@ #include "BackupItem.h" #include "Constants.h" namespace comm { namespace network { namespace database { const std::string BackupItem::FIELD_USER_ID = "userID"; const std::string BackupItem::FIELD_BACKUP_ID = "backupID"; const std::string BackupItem::FIELD_CREATED = "created"; const std::string BackupItem::FIELD_RECOVERY_DATA = "recoveryData"; const std::string BackupItem::FIELD_COMPACTION_HOLDER = "compactionHolder"; const std::string BackupItem::FIELD_ATTACHMENT_HOLDERS = "attachmentHolders"; std::string BackupItem::tableName = BACKUP_TABLE_NAME; BackupItem::BackupItem( std::string userID, std::string backupID, uint64_t created, std::string recoveryData, std::string compactionHolder, std::vector attachmentHolders) : userID(userID), backupID(backupID), created(created), recoveryData(recoveryData), compactionHolder(compactionHolder), attachmentHolders(attachmentHolders) { this->validate(); } BackupItem::BackupItem(const AttributeValues &itemFromDB) { this->assignItemFromDatabase(itemFromDB); } void BackupItem::validate() const { if (!this->userID.size()) { throw std::runtime_error("userID empty"); } if (!this->backupID.size()) { throw std::runtime_error("backupID empty"); } if (!this->created) { throw std::runtime_error("created not provided"); } if (!this->recoveryData.size()) { throw std::runtime_error("recoveryData empty"); } } void BackupItem::assignItemFromDatabase(const AttributeValues &itemFromDB) { try { this->userID = itemFromDB.at(BackupItem::FIELD_USER_ID).GetS(); this->backupID = itemFromDB.at(BackupItem::FIELD_BACKUP_ID).GetS(); this->created = std::stoll( std::string(itemFromDB.at(BackupItem::FIELD_CREATED).GetS()).c_str()); this->recoveryData = itemFromDB.at(BackupItem::FIELD_RECOVERY_DATA).GetS(); auto compactionHolder = itemFromDB.find(BackupItem::FIELD_COMPACTION_HOLDER); if (compactionHolder != itemFromDB.end()) { this->compactionHolder = compactionHolder->second.GetS(); } auto attachmentsHolders = itemFromDB.find(BackupItem::FIELD_ATTACHMENT_HOLDERS); if (attachmentsHolders != itemFromDB.end()) { this->attachmentHolders = attachmentsHolders->second.GetSS(); } } catch (std::logic_error &e) { throw std::runtime_error( "invalid backup item provided, " + std::string(e.what())); } this->validate(); } std::string BackupItem::getTableName() const { return BackupItem::tableName; } -std::string BackupItem::getPrimaryKey() const { - return BackupItem::FIELD_USER_ID; +PrimaryKey BackupItem::getPrimaryKey() const { + return PrimaryKey(BackupItem::FIELD_USER_ID, BackupItem::FIELD_BACKUP_ID); +} + +PrimaryKeyValue BackupItem::getPrimaryKeyValue() const { + return PrimaryKeyValue(this->userID, this->backupID); } std::string BackupItem::getUserID() const { return this->userID; } std::string BackupItem::getBackupID() const { return this->backupID; } uint64_t BackupItem::getCreated() const { return this->created; } std::string BackupItem::getRecoveryData() const { return this->recoveryData; } std::string BackupItem::getCompactionHolder() const { return this->compactionHolder; } std::vector BackupItem::getAttachmentHolders() const { return this->attachmentHolders; } } // namespace database } // namespace network } // namespace comm diff --git a/services/backup/docker-server/contents/server/src/DatabaseEntities/BackupItem.h b/services/backup/docker-server/contents/server/src/DatabaseEntities/BackupItem.h index 5fc157780..90633bb7d 100644 --- a/services/backup/docker-server/contents/server/src/DatabaseEntities/BackupItem.h +++ b/services/backup/docker-server/contents/server/src/DatabaseEntities/BackupItem.h @@ -1,58 +1,59 @@ #pragma once #include "Item.h" #include #include namespace comm { namespace network { namespace database { class BackupItem : public Item { std::string userID; std::string backupID; uint64_t created; std::string recoveryData; std::string compactionHolder; std::vector attachmentHolders; void validate() const override; public: static std::string tableName; static const std::string FIELD_USER_ID; static const std::string FIELD_BACKUP_ID; static const std::string FIELD_CREATED; static const std::string FIELD_RECOVERY_DATA; static const std::string FIELD_COMPACTION_HOLDER; static const std::string FIELD_ATTACHMENT_HOLDERS; BackupItem() { } BackupItem( std::string userID, std::string backupID, uint64_t created, std::string recoveryData, std::string compactionHolder, std::vector attachmentHolders); BackupItem(const AttributeValues &itemFromDB); void assignItemFromDatabase(const AttributeValues &itemFromDB) override; std::string getTableName() const override; - std::string getPrimaryKey() const override; + PrimaryKey getPrimaryKey() const override; + PrimaryKeyValue getPrimaryKeyValue() const override; std::string getUserID() const; std::string getBackupID() const; uint64_t getCreated() const; std::string getRecoveryData() const; std::string getCompactionHolder() const; std::vector getAttachmentHolders() const; }; } // namespace database } // namespace network } // namespace comm diff --git a/services/backup/docker-server/contents/server/src/DatabaseEntities/Item.h b/services/backup/docker-server/contents/server/src/DatabaseEntities/Item.h index bb25cdba0..ecc24a1d5 100644 --- a/services/backup/docker-server/contents/server/src/DatabaseEntities/Item.h +++ b/services/backup/docker-server/contents/server/src/DatabaseEntities/Item.h @@ -1,26 +1,49 @@ #pragma once #include #include +#include #include namespace comm { namespace network { namespace database { typedef Aws::Map AttributeValues; +struct PrimaryKeyBase { + PrimaryKeyBase(const std::string partitionKey) + : partitionKey(partitionKey), sortKey(nullptr) { + } + PrimaryKeyBase(const std::string partitionKey, const std::string sortKey) + : partitionKey(partitionKey), + sortKey(std::make_unique(sortKey)) { + } + + const std::string partitionKey; + std::unique_ptr sortKey; +}; + +struct PrimaryKey : PrimaryKeyBase { + using PrimaryKeyBase::PrimaryKeyBase; +}; + +struct PrimaryKeyValue : PrimaryKeyBase { + using PrimaryKeyBase::PrimaryKeyBase; +}; + class Item { virtual void validate() const = 0; public: virtual std::string getTableName() const = 0; - virtual std::string getPrimaryKey() const = 0; + virtual PrimaryKey getPrimaryKey() const = 0; + virtual PrimaryKeyValue getPrimaryKeyValue() const = 0; virtual void assignItemFromDatabase(const AttributeValues &itemFromDB) = 0; }; } // namespace database } // namespace network } // namespace comm diff --git a/services/backup/docker-server/contents/server/src/DatabaseEntities/LogItem.cpp b/services/backup/docker-server/contents/server/src/DatabaseEntities/LogItem.cpp index c4e0f5ac4..1dbc67f66 100644 --- a/services/backup/docker-server/contents/server/src/DatabaseEntities/LogItem.cpp +++ b/services/backup/docker-server/contents/server/src/DatabaseEntities/LogItem.cpp @@ -1,98 +1,102 @@ #include "LogItem.h" #include "AwsTools.h" #include "Constants.h" #include namespace comm { namespace network { namespace database { const std::string LogItem::FIELD_BACKUP_ID = "backupID"; const std::string LogItem::FIELD_LOG_ID = "logID"; const std::string LogItem::FIELD_PERSISTED_IN_BLOB = "persistedInBlob"; const std::string LogItem::FIELD_VALUE = "value"; const std::string LogItem::FIELD_ATTACHMENT_HOLDERS = "attachmentHolders"; std::string LogItem::tableName = LOG_TABLE_NAME; LogItem::LogItem( const std::string backupID, const std::string logID, const bool persistedInBlob, const std::string value, std::vector attachmentHolders) : backupID(backupID), logID(logID), persistedInBlob(persistedInBlob), value(value), attachmentHolders(attachmentHolders) { this->validate(); } LogItem::LogItem(const AttributeValues &itemFromDB) { this->assignItemFromDatabase(itemFromDB); } void LogItem::validate() const { if (!this->backupID.size()) { throw std::runtime_error("backupID empty"); } if (!this->logID.size()) { throw std::runtime_error("logID empty"); } if (!this->value.size()) { throw std::runtime_error("value empty"); } // todo maybe check if values is not too big if persistedInBlob is false } void LogItem::assignItemFromDatabase(const AttributeValues &itemFromDB) { try { this->backupID = itemFromDB.at(LogItem::FIELD_BACKUP_ID).GetS(); this->logID = itemFromDB.at(LogItem::FIELD_LOG_ID).GetS(); this->persistedInBlob = std::stoi( std::string(itemFromDB.at(LogItem::FIELD_PERSISTED_IN_BLOB).GetS()) .c_str()); this->value = itemFromDB.at(LogItem::FIELD_VALUE).GetS(); this->attachmentHolders = itemFromDB.at(LogItem::FIELD_ATTACHMENT_HOLDERS).GetSS(); } catch (std::logic_error &e) { throw std::runtime_error( "invalid log item provided, " + std::string(e.what())); } this->validate(); } std::string LogItem::getTableName() const { return LogItem::tableName; } -std::string LogItem::getPrimaryKey() const { - return LogItem::FIELD_BACKUP_ID; +PrimaryKey LogItem::getPrimaryKey() const { + return PrimaryKey(LogItem::FIELD_BACKUP_ID, LogItem::FIELD_LOG_ID); +} + +PrimaryKeyValue LogItem::getPrimaryKeyValue() const { + return PrimaryKeyValue(this->backupID, this->logID); } std::string LogItem::getBackupID() const { return this->backupID; } std::string LogItem::getLogID() const { return this->logID; } bool LogItem::getPersistedInBlob() const { return this->persistedInBlob; } std::string LogItem::getValue() const { return this->value; } std::vector LogItem::getAttachmentHolders() const { return this->attachmentHolders; } } // namespace database } // namespace network } // namespace comm diff --git a/services/backup/docker-server/contents/server/src/DatabaseEntities/LogItem.h b/services/backup/docker-server/contents/server/src/DatabaseEntities/LogItem.h index f7148c8b9..70e23f025 100644 --- a/services/backup/docker-server/contents/server/src/DatabaseEntities/LogItem.h +++ b/services/backup/docker-server/contents/server/src/DatabaseEntities/LogItem.h @@ -1,54 +1,55 @@ #pragma once #include "Item.h" #include #include namespace comm { namespace network { namespace database { class LogItem : public Item { std::string backupID; std::string logID; bool persistedInBlob; std::string value; std::vector attachmentHolders; void validate() const override; public: static std::string tableName; static const std::string FIELD_BACKUP_ID; static const std::string FIELD_LOG_ID; static const std::string FIELD_PERSISTED_IN_BLOB; static const std::string FIELD_VALUE; static const std::string FIELD_ATTACHMENT_HOLDERS; LogItem() { } LogItem( const std::string backupID, const std::string logID, const bool persistedInBlob, const std::string value, std::vector attachmentHolders); LogItem(const AttributeValues &itemFromDB); void assignItemFromDatabase(const AttributeValues &itemFromDB) override; std::string getTableName() const override; - std::string getPrimaryKey() const override; + PrimaryKey getPrimaryKey() const override; + PrimaryKeyValue getPrimaryKeyValue() const override; std::string getBackupID() const; std::string getLogID() const; bool getPersistedInBlob() const; std::string getValue() const; std::vector getAttachmentHolders() const; }; } // namespace database } // namespace network } // namespace comm diff --git a/services/backup/docker-server/contents/server/src/DatabaseManager.cpp b/services/backup/docker-server/contents/server/src/DatabaseManager.cpp index e94838005..ef280a445 100644 --- a/services/backup/docker-server/contents/server/src/DatabaseManager.cpp +++ b/services/backup/docker-server/contents/server/src/DatabaseManager.cpp @@ -1,187 +1,188 @@ #include "DatabaseManager.h" #include "Constants.h" #include "Tools.h" #include #include #include #include #include namespace comm { namespace network { namespace database { DatabaseManager &DatabaseManager::getInstance() { static DatabaseManager instance; return instance; } void DatabaseManager::innerPutItem( std::shared_ptr item, const Aws::DynamoDB::Model::PutItemRequest &request) { const Aws::DynamoDB::Model::PutItemOutcome outcome = getDynamoDBClient()->PutItem(request); if (!outcome.IsSuccess()) { throw std::runtime_error(outcome.GetError().GetMessage()); } } void DatabaseManager::innerRemoveItem( const Item &item, const std::string &key) { Aws::DynamoDB::Model::DeleteItemRequest request; request.SetTableName(item.getTableName()); request.AddKey( - item.getPrimaryKey(), Aws::DynamoDB::Model::AttributeValue(key)); + item.getPrimaryKey().partitionKey, + Aws::DynamoDB::Model::AttributeValue(key)); const Aws::DynamoDB::Model::DeleteItemOutcome &outcome = getDynamoDBClient()->DeleteItem(request); if (!outcome.IsSuccess()) { throw std::runtime_error(outcome.GetError().GetMessage()); } } void DatabaseManager::putBackupItem(const BackupItem &item) { Aws::DynamoDB::Model::PutItemRequest request; request.SetTableName(BackupItem::tableName); request.AddItem( BackupItem::FIELD_USER_ID, Aws::DynamoDB::Model::AttributeValue(item.getUserID())); request.AddItem( BackupItem::FIELD_CREATED, Aws::DynamoDB::Model::AttributeValue( std::to_string(getCurrentTimestamp()))); request.AddItem( BackupItem::FIELD_BACKUP_ID, Aws::DynamoDB::Model::AttributeValue(item.getBackupID())); request.AddItem( BackupItem::FIELD_RECOVERY_DATA, Aws::DynamoDB::Model::AttributeValue(item.getRecoveryData())); request.AddItem( BackupItem::FIELD_COMPACTION_HOLDER, Aws::DynamoDB::Model::AttributeValue(item.getCompactionHolder())); request.AddItem( BackupItem::FIELD_ATTACHMENT_HOLDERS, Aws::DynamoDB::Model::AttributeValue(item.getAttachmentHolders())); this->innerPutItem(std::make_shared(item), request); } std::shared_ptr DatabaseManager::findLastBackupItem(const std::string &userID) { std::shared_ptr item = createItemByType(); Aws::DynamoDB::Model::QueryRequest req; req.SetTableName(BackupItem::tableName); - req.SetKeyConditionExpression(item->getPrimaryKey() + " = :valueToMatch"); + req.SetKeyConditionExpression(BackupItem::FIELD_USER_ID + " = :valueToMatch"); AttributeValues attributeValues; attributeValues.emplace(":valueToMatch", userID); req.SetExpressionAttributeValues(attributeValues); req.SetIndexName("userID-created-index"); req.SetLimit(1); req.SetScanIndexForward(false); const Aws::DynamoDB::Model::QueryOutcome &outcome = getDynamoDBClient()->Query(req); if (!outcome.IsSuccess()) { throw std::runtime_error(outcome.GetError().GetMessage()); } const Aws::Vector &items = outcome.GetResult().GetItems(); if (items.empty()) { return nullptr; } return std::make_shared(items[0]); } void DatabaseManager::removeBackupItem(std::shared_ptr item) { Aws::DynamoDB::Model::DeleteItemRequest request; request.SetTableName(item->getTableName()); request.AddKey( BackupItem::FIELD_USER_ID, Aws::DynamoDB::Model::AttributeValue(item->getUserID())); request.AddKey( BackupItem::FIELD_BACKUP_ID, Aws::DynamoDB::Model::AttributeValue(item->getBackupID())); const Aws::DynamoDB::Model::DeleteItemOutcome &outcome = comm::network::getDynamoDBClient()->DeleteItem(request); if (!outcome.IsSuccess()) { throw std::runtime_error(outcome.GetError().GetMessage()); } } void DatabaseManager::putLogItem(const LogItem &item) { Aws::DynamoDB::Model::PutItemRequest request; request.SetTableName(LogItem::tableName); request.AddItem( LogItem::FIELD_BACKUP_ID, Aws::DynamoDB::Model::AttributeValue(item.getBackupID())); request.AddItem( LogItem::FIELD_LOG_ID, Aws::DynamoDB::Model::AttributeValue(item.getLogID())); request.AddItem( LogItem::FIELD_PERSISTED_IN_BLOB, Aws::DynamoDB::Model::AttributeValue( std::to_string(item.getPersistedInBlob()))); request.AddItem( LogItem::FIELD_VALUE, Aws::DynamoDB::Model::AttributeValue(item.getValue())); request.AddItem( LogItem::FIELD_ATTACHMENT_HOLDERS, Aws::DynamoDB::Model::AttributeValue(item.getAttachmentHolders())); this->innerPutItem(std::make_shared(item), request); } std::vector> DatabaseManager::findLogItemsForBackup(const std::string &backupID) { std::vector> result; std::shared_ptr item = createItemByType(); Aws::DynamoDB::Model::QueryRequest req; req.SetTableName(LogItem::tableName); - req.SetKeyConditionExpression(item->getPrimaryKey() + " = :valueToMatch"); + req.SetKeyConditionExpression(LogItem::FIELD_BACKUP_ID + " = :valueToMatch"); AttributeValues attributeValues; attributeValues.emplace(":valueToMatch", backupID); req.SetExpressionAttributeValues(attributeValues); const Aws::DynamoDB::Model::QueryOutcome &outcome = getDynamoDBClient()->Query(req); if (!outcome.IsSuccess()) { throw std::runtime_error(outcome.GetError().GetMessage()); } const Aws::Vector &items = outcome.GetResult().GetItems(); for (auto &item : items) { result.push_back(std::make_shared(item)); } return result; } void DatabaseManager::removeLogItem(std::shared_ptr item) { Aws::DynamoDB::Model::DeleteItemRequest request; request.SetTableName(item->getTableName()); request.AddKey( LogItem::FIELD_BACKUP_ID, Aws::DynamoDB::Model::AttributeValue(item->getBackupID())); request.AddKey( LogItem::FIELD_LOG_ID, Aws::DynamoDB::Model::AttributeValue(item->getLogID())); const Aws::DynamoDB::Model::DeleteItemOutcome &outcome = getDynamoDBClient()->DeleteItem(request); if (!outcome.IsSuccess()) { throw std::runtime_error(outcome.GetError().GetMessage()); } } } // namespace database } // namespace network } // namespace comm