Implement Service Refactoring
<UserStory> 1958: Service Refactoring </UserStory> UserStory #1958 <Changes> 1. Added DataStoreLockGuard.h include to project file and InventoryManagementService for thread-safe datastore operations. 2. Enhanced DataStore::getUsers to load SerializedUser records, refresh cache, and attach notifications with recipient validation. 3. Enhanced DataStore::getInventoryItems to load SerializedInventoryItem records and refresh cache for tracked inventory items. 4. Refactored InventoryManagementService::sendLowStockAlerts to use tracked inventory and user maps with DataStoreLockGuard, ensuring safe access. 5. Removed legacy observer management, load/save inventory items, and related persistence functions from InventoryManagementService. 6. Updated InventoryManagementService::addInventoryItem to insert new records into tracked inventory map and persist changes via saveInventoryItems. 7. Updated InventoryManagementService::addInventoryItemStock to validate item existence, update quantity, mark record as MODIFIED, and persist changes. 8. Refactored InventoryManagementService::getInventoryItems to return object map extracted from tracked records with DataStoreLockGuard. 9. Updated InventoryManagementService::removeInventoryItem to validate item ID, mark state as INACTIVE, set record state to MODIFIED, and persist changes. 10. Updated InventoryManagementService::getInventoryItem to safely retrieve inventory items from tracked records with error handling. </Changes> <Test> N/A </Test> <Review> Sreeja Reghukumar </Review>
This commit is contained in:
+1
@@ -157,6 +157,7 @@
|
|||||||
<ClInclude Include="core\patterns\Observer.h" />
|
<ClInclude Include="core\patterns\Observer.h" />
|
||||||
<ClInclude Include="core\patterns\Subject.h" />
|
<ClInclude Include="core\patterns\Subject.h" />
|
||||||
<ClInclude Include="datastores\DataStore.h" />
|
<ClInclude Include="datastores\DataStore.h" />
|
||||||
|
<ClInclude Include="datastores\DataStoreLockGuard.h" />
|
||||||
<ClInclude Include="datastores\sharedmemory\FileHeader.h" />
|
<ClInclude Include="datastores\sharedmemory\FileHeader.h" />
|
||||||
<ClInclude Include="datastores\sharedmemory\MappingInfo.h" />
|
<ClInclude Include="datastores\sharedmemory\MappingInfo.h" />
|
||||||
<ClInclude Include="datastores\sharedmemory\RecordState.h" />
|
<ClInclude Include="datastores\sharedmemory\RecordState.h" />
|
||||||
|
|||||||
@@ -228,6 +228,22 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
util::Map<std::string, TrackedRecord<User>>& DataStore::getUsers()
|
util::Map<std::string, TrackedRecord<User>>& DataStore::getUsers()
|
||||||
{
|
{
|
||||||
|
auto users = loadRecords<User, SerializedUser>(m_users);
|
||||||
|
refreshCache(m_userCache, users);
|
||||||
|
auto& notifications = getNotifications();
|
||||||
|
int numberOfNotifications = m_notificationCache.getSize();
|
||||||
|
for (int index = 0; index < numberOfNotifications; index++)
|
||||||
|
{
|
||||||
|
Notification* notification = notifications.getValueAt(index).data;
|
||||||
|
const std::string& recipientUserId = notification->getRecipientUserId();
|
||||||
|
int userIndex = m_userCache.find(recipientUserId);
|
||||||
|
if (userIndex == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid recipient user ID");
|
||||||
|
}
|
||||||
|
User* user = m_userCache.getValueAt(userIndex).data;
|
||||||
|
user->addNotification(notification);
|
||||||
|
}
|
||||||
return m_userCache;
|
return m_userCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,6 +296,8 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
util::Map<std::string, TrackedRecord<InventoryItem>>& DataStore::getInventoryItems()
|
util::Map<std::string, TrackedRecord<InventoryItem>>& DataStore::getInventoryItems()
|
||||||
{
|
{
|
||||||
|
auto inventoryItems = loadRecords<InventoryItem, SerializedInventoryItem>(m_inventoryItems);
|
||||||
|
refreshCache(m_inventoryItemCache, inventoryItems);
|
||||||
return m_inventoryItemCache;
|
return m_inventoryItemCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,6 +437,7 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
void DataStore::saveInventoryItems()
|
void DataStore::saveInventoryItems()
|
||||||
{
|
{
|
||||||
|
saveRecords<InventoryItem, SerializedInventoryItem>(m_inventoryItems, m_inventoryItemCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
File: DataStoreLockGuard.h
|
||||||
|
Description: Defines the DataStoreLockGuard class used to manage DataStore
|
||||||
|
locking and unlocking automatically within a scope.
|
||||||
|
Author: Trenser
|
||||||
|
Date: 12-June-2026
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "DataStore.h"
|
||||||
|
|
||||||
|
class DataStoreLockGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit DataStoreLockGuard(DataStore& dataStore)
|
||||||
|
: m_dataStore(dataStore)
|
||||||
|
{
|
||||||
|
m_dataStore.lockDataStore();
|
||||||
|
}
|
||||||
|
~DataStoreLockGuard()
|
||||||
|
{
|
||||||
|
m_dataStore.unlockDataStore();
|
||||||
|
}
|
||||||
|
DataStoreLockGuard(const DataStoreLockGuard&) = delete;
|
||||||
|
DataStoreLockGuard& operator=(const DataStoreLockGuard&) = delete;
|
||||||
|
private:
|
||||||
|
DataStore& m_dataStore;
|
||||||
|
};
|
||||||
+59
-122
@@ -19,7 +19,7 @@ Date: 22-May-2026
|
|||||||
#include "User.h"
|
#include "User.h"
|
||||||
#include "Utility.h"
|
#include "Utility.h"
|
||||||
#include "Vector.h"
|
#include "Vector.h"
|
||||||
|
#include "DataStoreLockGuard.h"
|
||||||
|
|
||||||
util::Map<std::string, User*> InventoryManagementService::m_observers{};
|
util::Map<std::string, User*> InventoryManagementService::m_observers{};
|
||||||
|
|
||||||
@@ -58,18 +58,19 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
void InventoryManagementService::sendLowStockAlerts()
|
void InventoryManagementService::sendLowStockAlerts()
|
||||||
{
|
{
|
||||||
auto& inventoryItems = m_dataStore.getInventoryItems();
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
if (inventoryItems.isEmpty())
|
auto& trackedInventoryItemsMap = m_dataStore.getInventoryItems();
|
||||||
|
auto& trackedUserMap = m_dataStore.getUsers();
|
||||||
|
if (trackedInventoryItemsMap.isEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int inventoryItemsSize = inventoryItems.getSize();
|
int inventoryItemsSize = trackedInventoryItemsMap.getSize();
|
||||||
auto& usersMap = m_dataStore.getUsers();
|
int usersMapSize = trackedUserMap.getSize();
|
||||||
int usersMapSize = usersMap.getSize();
|
|
||||||
util::Vector<User*> adminUsers;
|
util::Vector<User*> adminUsers;
|
||||||
for (int index = 0; index < usersMapSize; index++)
|
for (int index = 0; index < usersMapSize; index++)
|
||||||
{
|
{
|
||||||
User* user = usersMap.getValueAt(index);
|
User* user = trackedUserMap.getValueAt(index).data;
|
||||||
if (user->getUserType() == util::UserType::ADMIN)
|
if (user->getUserType() == util::UserType::ADMIN)
|
||||||
{
|
{
|
||||||
adminUsers.push_back(user);
|
adminUsers.push_back(user);
|
||||||
@@ -82,7 +83,7 @@ void InventoryManagementService::sendLowStockAlerts()
|
|||||||
}
|
}
|
||||||
for (int index = 0; index < inventoryItemsSize; index++)
|
for (int index = 0; index < inventoryItemsSize; index++)
|
||||||
{
|
{
|
||||||
InventoryItem* inventoryItem = inventoryItems.getValueAt(index);
|
InventoryItem* inventoryItem = trackedInventoryItemsMap.getValueAt(index).data;
|
||||||
if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD)
|
if (inventoryItem && inventoryItem->getQuantity() < config::threshold::INVENTORY_LOW_STOCK_THRESHOLD)
|
||||||
{
|
{
|
||||||
sendLowStockAlertsToAdmins(*this, inventoryItem, adminUsers);
|
sendLowStockAlertsToAdmins(*this, inventoryItem, adminUsers);
|
||||||
@@ -90,95 +91,6 @@ void InventoryManagementService::sendLowStockAlerts()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Function: getObserverIDs
|
|
||||||
Description: Retrieves the IDs of all observers currently attached to the
|
|
||||||
InventoryManagementService.
|
|
||||||
Parameters:
|
|
||||||
- None
|
|
||||||
Returns:
|
|
||||||
- util::Vector<std::string>: Vector of observer user IDs
|
|
||||||
*/
|
|
||||||
util::Vector<std::string> InventoryManagementService::getObserverIDs()
|
|
||||||
{
|
|
||||||
util::Vector<std::string> observerIDs;
|
|
||||||
int numberOfObservers = m_observers.getSize();
|
|
||||||
for (int index = 0; index < numberOfObservers; index++)
|
|
||||||
{
|
|
||||||
User* observer = m_observers.getValueAt(index);
|
|
||||||
if (observer)
|
|
||||||
{
|
|
||||||
observerIDs.push_back(observer->getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return observerIDs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Function: loadInventoryItems
|
|
||||||
Description: Loads inventory items from persistent storage into the datastore.
|
|
||||||
Uses FileManager to deserialize inventory items from the configured file.
|
|
||||||
Parameters:
|
|
||||||
- None
|
|
||||||
Returns:
|
|
||||||
- void
|
|
||||||
*/
|
|
||||||
void InventoryManagementService::loadInventoryItems()
|
|
||||||
{
|
|
||||||
util::FileManager<InventoryItem> inventoryItemFileManager(config::file::INVENTORYITEM_FILE);
|
|
||||||
auto& inventoryItems = m_dataStore.getInventoryItems();
|
|
||||||
auto inventoryItemsMap = inventoryItemFileManager.load();
|
|
||||||
int numberOfInventoryItems = inventoryItemsMap.getSize();
|
|
||||||
for (int index = 0; index < numberOfInventoryItems; index++)
|
|
||||||
{
|
|
||||||
inventoryItems[inventoryItemsMap.getKeyAt(index)] = inventoryItemsMap.getValueAt(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Function: saveInventoryItems
|
|
||||||
Description: Saves inventory items from the datastore to persistent storage.
|
|
||||||
Uses FileManager to serialize inventory items into the configured file.
|
|
||||||
Parameters:
|
|
||||||
- None
|
|
||||||
Returns:
|
|
||||||
- void
|
|
||||||
*/
|
|
||||||
void InventoryManagementService::saveInventoryItems()
|
|
||||||
{
|
|
||||||
util::FileManager<InventoryItem> inventoryItemFileManager(config::file::INVENTORYITEM_FILE);
|
|
||||||
auto& inventoryItems = m_dataStore.getInventoryItems();
|
|
||||||
inventoryItemFileManager.save(inventoryItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Function: loadObservers
|
|
||||||
Description: Loads observer IDs from persistent storage and attaches corresponding
|
|
||||||
users as observers to the InventoryManagementService.
|
|
||||||
Parameters:
|
|
||||||
- None
|
|
||||||
Returns:
|
|
||||||
- void
|
|
||||||
*/
|
|
||||||
void InventoryManagementService::loadObservers()
|
|
||||||
{
|
|
||||||
util::loadObservers(config::file::INVENTORYMANAGEMENTOBSERVERS, this, m_dataStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Function: saveObservers
|
|
||||||
Description: Saves the current observer IDs of the InventoryManagementService
|
|
||||||
to persistent storage for future retrieval.
|
|
||||||
Parameters:
|
|
||||||
- None
|
|
||||||
Returns:
|
|
||||||
- void
|
|
||||||
*/
|
|
||||||
void InventoryManagementService::saveObservers()
|
|
||||||
{
|
|
||||||
util::saveObservers(config::file::INVENTORYMANAGEMENTOBSERVERS, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Function: addInventoryItem
|
Function: addInventoryItem
|
||||||
Description: Creates a new inventory item using the Factory and inserts it
|
Description: Creates a new inventory item using the Factory and inserts it
|
||||||
@@ -190,8 +102,11 @@ Return type: void
|
|||||||
*/
|
*/
|
||||||
void InventoryManagementService::addInventoryItem(const std::string& partName, int quantity, double price)
|
void InventoryManagementService::addInventoryItem(const std::string& partName, int quantity, double price)
|
||||||
{
|
{
|
||||||
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
|
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
|
||||||
InventoryItem* newItem = Factory::getObject<InventoryItem>(partName, quantity, price);
|
InventoryItem* newItem = Factory::getObject<InventoryItem>(partName, quantity, price);
|
||||||
m_dataStore.getInventoryItems().insert(newItem->getId(), newItem);
|
trackedInventoryItemMap.insert(newItem->getId(), util::createNewRecord(newItem));
|
||||||
|
m_dataStore.saveInventoryItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -203,16 +118,22 @@ Return type: void
|
|||||||
*/
|
*/
|
||||||
void InventoryManagementService::addInventoryItemStock(const std::string& selectedItemId, int quantity)
|
void InventoryManagementService::addInventoryItemStock(const std::string& selectedItemId, int quantity)
|
||||||
{
|
{
|
||||||
int index = m_dataStore.getInventoryItems().find(selectedItemId);
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
if (index != -1)
|
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
|
||||||
{
|
int index = trackedInventoryItemMap.find(selectedItemId);
|
||||||
InventoryItem* item = m_dataStore.getInventoryItems().getValueAt(index);
|
if (index == -1)
|
||||||
if (item != nullptr)
|
{
|
||||||
{
|
throw std::runtime_error("Inventory update failed: Item ID '" + selectedItemId + "' not found.");
|
||||||
int totalQuantity = item->getQuantity() + quantity;
|
}
|
||||||
item->setQuantity(totalQuantity);
|
InventoryItem* item = trackedInventoryItemMap.getValueAt(index).data;
|
||||||
}
|
if (item == nullptr)
|
||||||
}
|
{
|
||||||
|
throw std::runtime_error("Inventory update failed. Item does not exist.\n");
|
||||||
|
}
|
||||||
|
int totalQuantity = item->getQuantity() + quantity;
|
||||||
|
item->setQuantity(totalQuantity);
|
||||||
|
trackedInventoryItemMap.getValueAt(index).state = RecordState::MODIFIED;
|
||||||
|
m_dataStore.saveInventoryItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -223,7 +144,10 @@ Return type: util::Map<std::string, InventoryItem*>
|
|||||||
*/
|
*/
|
||||||
util::Map<std::string, InventoryItem*> InventoryManagementService::getInventoryItems()
|
util::Map<std::string, InventoryItem*> InventoryManagementService::getInventoryItems()
|
||||||
{
|
{
|
||||||
return m_dataStore.getInventoryItems();
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
|
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
|
||||||
|
auto inventoryMap = util::getObjects(trackedInventoryItemMap);
|
||||||
|
return inventoryMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -234,15 +158,21 @@ Return type: void
|
|||||||
*/
|
*/
|
||||||
void InventoryManagementService::removeInventoryItem(const std::string& inventoryItemID)
|
void InventoryManagementService::removeInventoryItem(const std::string& inventoryItemID)
|
||||||
{
|
{
|
||||||
int index = m_dataStore.getInventoryItems().find(inventoryItemID);
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
if (index != -1)
|
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
|
||||||
{
|
int index = trackedInventoryItemMap.find(inventoryItemID);
|
||||||
InventoryItem* item = m_dataStore.getInventoryItems().getValueAt(index);
|
if (index == -1)
|
||||||
if (item != nullptr)
|
{
|
||||||
{
|
throw std::runtime_error("Inventory removal failed: Item ID '" + inventoryItemID + "' not found.");
|
||||||
item->setState(util::State::INACTIVE);
|
}
|
||||||
}
|
InventoryItem* item = trackedInventoryItemMap.getValueAt(index).data;
|
||||||
}
|
if (item == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Inventory removal failed: Item ID does not exist.");
|
||||||
|
}
|
||||||
|
item->setState(util::State::INACTIVE);
|
||||||
|
trackedInventoryItemMap.getValueAt(index).state = RecordState::MODIFIED;
|
||||||
|
m_dataStore.saveInventoryItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -253,12 +183,19 @@ Return type: InventoryItem*
|
|||||||
*/
|
*/
|
||||||
InventoryItem* InventoryManagementService::getInventoryItem(const std::string& inventoryItemID)
|
InventoryItem* InventoryManagementService::getInventoryItem(const std::string& inventoryItemID)
|
||||||
{
|
{
|
||||||
int index = m_dataStore.getInventoryItems().find(inventoryItemID);
|
DataStoreLockGuard lock(m_dataStore);
|
||||||
if (index != -1)
|
auto& trackedInventoryItemMap = m_dataStore.getInventoryItems();
|
||||||
|
int index = trackedInventoryItemMap.find(inventoryItemID);
|
||||||
|
if (index == -1)
|
||||||
{
|
{
|
||||||
return m_dataStore.getInventoryItems().getValueAt(index);
|
return nullptr;
|
||||||
}
|
}
|
||||||
return nullptr;
|
InventoryItem* inventoryItem = trackedInventoryItemMap.getValueAt(index).data;
|
||||||
|
if (inventoryItem == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Item ID does not exist.");
|
||||||
|
}
|
||||||
|
return inventoryItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
-2
@@ -33,8 +33,6 @@ public:
|
|||||||
void sendNotification(User* user, const std::string& title, const std::string& message) override;
|
void sendNotification(User* user, const std::string& title, const std::string& message) override;
|
||||||
void attach(User* user) override;
|
void attach(User* user) override;
|
||||||
void detach(User* user) override;
|
void detach(User* user) override;
|
||||||
void loadInventoryItems();
|
|
||||||
void saveInventoryItems();
|
|
||||||
void loadObservers();
|
void loadObservers();
|
||||||
void saveObservers();
|
void saveObservers();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user