diff --git a/services/blob/docker-server/contents/server/src/DatabaseEntities/BlobItem.cpp b/services/blob/docker-server/contents/server/src/DatabaseEntities/BlobItem.cpp index 3d1107916..8f46cb5c9 100644 --- a/services/blob/docker-server/contents/server/src/DatabaseEntities/BlobItem.cpp +++ b/services/blob/docker-server/contents/server/src/DatabaseEntities/BlobItem.cpp @@ -1,70 +1,74 @@ #include "BlobItem.h" #include "AwsTools.h" #include "Constants.h" namespace comm { namespace network { namespace database { const std::string BlobItem::FIELD_BLOB_HASH = "blobHash"; const std::string BlobItem::FIELD_S3_PATH = "s3Path"; const std::string BlobItem::FIELD_CREATED = "created"; std::string BlobItem::tableName = BLOB_TABLE_NAME; BlobItem::BlobItem( const std::string blobHash, const S3Path s3Path, uint64_t created) : blobHash(blobHash), s3Path(s3Path), created(created) { this->validate(); } BlobItem::BlobItem(const AttributeValues &itemFromDB) { this->assignItemFromDatabase(itemFromDB); } void BlobItem::validate() const { if (!this->blobHash.size()) { throw std::runtime_error("blobHash empty"); } this->s3Path.validate(); } void BlobItem::assignItemFromDatabase(const AttributeValues &itemFromDB) { try { this->blobHash = itemFromDB.at(BlobItem::FIELD_BLOB_HASH).GetS(); this->s3Path = S3Path(itemFromDB.at(BlobItem::FIELD_S3_PATH).GetS()); this->created = std::stoll( std::string(itemFromDB.at(BlobItem::FIELD_CREATED).GetS()).c_str()); } catch (std::logic_error &e) { throw std::runtime_error( "invalid blob item provided, " + std::string(e.what())); } this->validate(); } std::string BlobItem::getTableName() const { return BlobItem::tableName; } -std::string BlobItem::getPrimaryKey() const { - return BlobItem::FIELD_BLOB_HASH; +PrimaryKey BlobItem::getPrimaryKey() const { + return PrimaryKey(BlobItem::FIELD_BLOB_HASH); +} + +PrimaryKeyValue BlobItem::getPrimaryKeyValue() const { + return PrimaryKeyValue(this->blobHash); } std::string BlobItem::getBlobHash() const { return this->blobHash; } S3Path BlobItem::getS3Path() const { return this->s3Path; } uint64_t BlobItem::getCreated() const { return this->created; } } // namespace database } // namespace network } // namespace comm diff --git a/services/blob/docker-server/contents/server/src/DatabaseEntities/BlobItem.h b/services/blob/docker-server/contents/server/src/DatabaseEntities/BlobItem.h index fc9fa94e0..d70a83f50 100644 --- a/services/blob/docker-server/contents/server/src/DatabaseEntities/BlobItem.h +++ b/services/blob/docker-server/contents/server/src/DatabaseEntities/BlobItem.h @@ -1,46 +1,47 @@ #pragma once #include "Item.h" #include "S3Path.h" #include namespace comm { namespace network { namespace database { class BlobItem : public Item { std::string blobHash; S3Path s3Path; uint64_t created = 0; void validate() const override; public: static std::string tableName; static const std::string FIELD_BLOB_HASH; static const std::string FIELD_S3_PATH; static const std::string FIELD_CREATED; BlobItem() { } BlobItem( const std::string blobHash, const S3Path s3Path, uint64_t created = 0); BlobItem(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 getBlobHash() const; S3Path getS3Path() const; uint64_t getCreated() const; }; } // namespace database } // namespace network } // namespace comm diff --git a/services/blob/docker-server/contents/server/src/DatabaseEntities/Item.h b/services/blob/docker-server/contents/server/src/DatabaseEntities/Item.h index bb25cdba0..ecc24a1d5 100644 --- a/services/blob/docker-server/contents/server/src/DatabaseEntities/Item.h +++ b/services/blob/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/blob/docker-server/contents/server/src/DatabaseEntities/ReverseIndexItem.cpp b/services/blob/docker-server/contents/server/src/DatabaseEntities/ReverseIndexItem.cpp index 45e682d37..7de0c6d49 100644 --- a/services/blob/docker-server/contents/server/src/DatabaseEntities/ReverseIndexItem.cpp +++ b/services/blob/docker-server/contents/server/src/DatabaseEntities/ReverseIndexItem.cpp @@ -1,59 +1,63 @@ #include "ReverseIndexItem.h" #include "AwsTools.h" #include "Constants.h" namespace comm { namespace network { namespace database { const std::string ReverseIndexItem::FIELD_HOLDER = "holder"; const std::string ReverseIndexItem::FIELD_BLOB_HASH = "blobHash"; std::string ReverseIndexItem::tableName = REVERSE_INDEX_TABLE_NAME; ReverseIndexItem::ReverseIndexItem( const std::string holder, const std::string blobHash) : holder(holder), blobHash(blobHash) { this->validate(); } ReverseIndexItem::ReverseIndexItem(const AttributeValues &itemFromDB) { this->assignItemFromDatabase(itemFromDB); } void ReverseIndexItem::validate() const { if (!this->holder.size()) { throw std::runtime_error("reverse index empty"); } if (!this->blobHash.size()) { throw std::runtime_error("blobHash empty"); } } void ReverseIndexItem::assignItemFromDatabase( const AttributeValues &itemFromDB) { this->holder = itemFromDB.at(ReverseIndexItem::FIELD_HOLDER).GetS(); this->blobHash = itemFromDB.at(ReverseIndexItem::FIELD_BLOB_HASH).GetS(); this->validate(); } std::string ReverseIndexItem::getTableName() const { return ReverseIndexItem::tableName; } -std::string ReverseIndexItem::getPrimaryKey() const { - return ReverseIndexItem::FIELD_HOLDER; +PrimaryKey ReverseIndexItem::getPrimaryKey() const { + return PrimaryKey(ReverseIndexItem::FIELD_HOLDER); +} + +PrimaryKeyValue ReverseIndexItem::getPrimaryKeyValue() const { + return PrimaryKeyValue(this->holder); } std::string ReverseIndexItem::getHolder() const { return this->holder; } std::string ReverseIndexItem::getBlobHash() const { return this->blobHash; } } // namespace database } // namespace network } // namespace comm diff --git a/services/blob/docker-server/contents/server/src/DatabaseEntities/ReverseIndexItem.h b/services/blob/docker-server/contents/server/src/DatabaseEntities/ReverseIndexItem.h index 2569cc5c4..c6b429572 100644 --- a/services/blob/docker-server/contents/server/src/DatabaseEntities/ReverseIndexItem.h +++ b/services/blob/docker-server/contents/server/src/DatabaseEntities/ReverseIndexItem.h @@ -1,38 +1,40 @@ #pragma once #include "Item.h" #include namespace comm { namespace network { namespace database { class ReverseIndexItem : public Item { std::string holder; std::string blobHash; void validate() const override; public: static std::string tableName; static const std::string FIELD_HOLDER; static const std::string FIELD_BLOB_HASH; ReverseIndexItem() { } ReverseIndexItem(const std::string holder, const std::string blobHash); ReverseIndexItem(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 getHolder() const; std::string getBlobHash() const; }; } // namespace database } // namespace network } // namespace comm diff --git a/services/blob/docker-server/contents/server/src/DatabaseManager.cpp b/services/blob/docker-server/contents/server/src/DatabaseManager.cpp index bada6bf36..6f36dfe97 100644 --- a/services/blob/docker-server/contents/server/src/DatabaseManager.cpp +++ b/services/blob/docker-server/contents/server/src/DatabaseManager.cpp @@ -1,142 +1,152 @@ #include "DatabaseManager.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) { +void DatabaseManager::innerRemoveItem(const Item &item) { Aws::DynamoDB::Model::DeleteItemRequest request; request.SetTableName(item.getTableName()); + PrimaryKey pk = item.getPrimaryKey(); + PrimaryKeyValue primaryKeyValue = item.getPrimaryKeyValue(); request.AddKey( - item.getPrimaryKey(), Aws::DynamoDB::Model::AttributeValue(key)); + pk.partitionKey, + Aws::DynamoDB::Model::AttributeValue(primaryKeyValue.partitionKey)); + if (pk.sortKey != nullptr && primaryKeyValue.sortKey != nullptr) { + request.AddKey( + *pk.sortKey, + Aws::DynamoDB::Model::AttributeValue(*primaryKeyValue.sortKey)); + } const Aws::DynamoDB::Model::DeleteItemOutcome &outcome = getDynamoDBClient()->DeleteItem(request); if (!outcome.IsSuccess()) { throw std::runtime_error(outcome.GetError().GetMessage()); } } void DatabaseManager::putBlobItem(const BlobItem &item) { Aws::DynamoDB::Model::PutItemRequest request; request.SetTableName(BlobItem::tableName); request.AddItem( BlobItem::FIELD_BLOB_HASH, Aws::DynamoDB::Model::AttributeValue(item.getBlobHash())); request.AddItem( BlobItem::FIELD_S3_PATH, Aws::DynamoDB::Model::AttributeValue(item.getS3Path().getFullPath())); request.AddItem( BlobItem::FIELD_CREATED, Aws::DynamoDB::Model::AttributeValue( std::to_string(getCurrentTimestamp()))); this->innerPutItem(std::make_shared(item), request); } std::shared_ptr DatabaseManager::findBlobItem(const std::string &blobHash) { Aws::DynamoDB::Model::GetItemRequest request; request.AddKey( BlobItem::FIELD_BLOB_HASH, Aws::DynamoDB::Model::AttributeValue(blobHash)); return std::move(this->innerFindItem(request)); } void DatabaseManager::removeBlobItem(const std::string &blobHash) { - this->innerRemoveItem(*(createItemByType()), blobHash); + std::shared_ptr item = this->findBlobItem(blobHash); + if (item == nullptr) { + return; + } + this->innerRemoveItem(*item); } void DatabaseManager::putReverseIndexItem(const ReverseIndexItem &item) { if (this->findReverseIndexItemByHolder(item.getHolder()) != nullptr) { throw std::runtime_error( "An item for the given holder [" + item.getHolder() + "] already exists"); } Aws::DynamoDB::Model::PutItemRequest request; request.SetTableName(ReverseIndexItem::tableName); request.AddItem( ReverseIndexItem::FIELD_HOLDER, Aws::DynamoDB::Model::AttributeValue(item.getHolder())); request.AddItem( ReverseIndexItem::FIELD_BLOB_HASH, Aws::DynamoDB::Model::AttributeValue(item.getBlobHash())); this->innerPutItem(std::make_shared(item), request); } std::shared_ptr DatabaseManager::findReverseIndexItemByHolder(const std::string &holder) { Aws::DynamoDB::Model::GetItemRequest request; request.AddKey( ReverseIndexItem::FIELD_HOLDER, Aws::DynamoDB::Model::AttributeValue(holder)); return std::move(this->innerFindItem(request)); } std::vector> DatabaseManager::findReverseIndexItemsByHash(const std::string &blobHash) { std::vector> result; Aws::DynamoDB::Model::QueryRequest req; req.SetTableName(ReverseIndexItem::tableName); req.SetKeyConditionExpression("blobHash = :valueToMatch"); AttributeValues attributeValues; attributeValues.emplace(":valueToMatch", blobHash); req.SetExpressionAttributeValues(attributeValues); req.SetIndexName("blobHash-index"); 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; } bool DatabaseManager::removeReverseIndexItem(const std::string &holder) { std::shared_ptr item = findReverseIndexItemByHolder(holder); if (item == nullptr) { return false; } - this->innerRemoveItem(*item, item->getHolder()); + this->innerRemoveItem(*item); return true; } } // namespace database } // namespace network } // namespace comm diff --git a/services/blob/docker-server/contents/server/src/DatabaseManager.h b/services/blob/docker-server/contents/server/src/DatabaseManager.h index c7b4d9a42..fbd80bc4a 100644 --- a/services/blob/docker-server/contents/server/src/DatabaseManager.h +++ b/services/blob/docker-server/contents/server/src/DatabaseManager.h @@ -1,75 +1,75 @@ #pragma once #include "AwsTools.h" #include "DatabaseEntitiesTools.h" #include #include #include #include #include #include #include #include #include #ifdef COMM_SERVICES_DEV_MODE #include "DatabaseSimulator.h" #endif namespace comm { namespace network { namespace database { // this class should be thread-safe in case any shared resources appear class DatabaseManager { #ifdef COMM_SERVICES_DEV_MODE DatabaseSimulator dbSimulator; #endif void innerPutItem( std::shared_ptr item, const Aws::DynamoDB::Model::PutItemRequest &request); template std::shared_ptr innerFindItem(Aws::DynamoDB::Model::GetItemRequest &request); - void innerRemoveItem(const Item &item, const std::string &key); + void innerRemoveItem(const Item &item); public: static DatabaseManager &getInstance(); void putBlobItem(const BlobItem &item); std::shared_ptr findBlobItem(const std::string &blobHash); void removeBlobItem(const std::string &blobHash); void putReverseIndexItem(const ReverseIndexItem &item); std::shared_ptr findReverseIndexItemByHolder(const std::string &holder); std::vector> findReverseIndexItemsByHash(const std::string &blobHash); bool removeReverseIndexItem(const std::string &holder); }; template std::shared_ptr DatabaseManager::innerFindItem(Aws::DynamoDB::Model::GetItemRequest &request) { std::shared_ptr item = createItemByType(); request.SetTableName(item->getTableName()); const Aws::DynamoDB::Model::GetItemOutcome &outcome = getDynamoDBClient()->GetItem(request); if (!outcome.IsSuccess()) { throw std::runtime_error(outcome.GetError().GetMessage()); } const AttributeValues &outcomeItem = outcome.GetResult().GetItem(); if (!outcomeItem.size()) { return nullptr; } item->assignItemFromDatabase(outcomeItem); return std::move(item); } } // namespace database } // namespace network } // namespace comm