diff --git a/SpeckleConnector/Connector.xcodeproj/project.pbxproj b/SpeckleConnector/Connector.xcodeproj/project.pbxproj index 4f81038..37e3043 100644 --- a/SpeckleConnector/Connector.xcodeproj/project.pbxproj +++ b/SpeckleConnector/Connector.xcodeproj/project.pbxproj @@ -31,6 +31,11 @@ 215F08462C9633A800CD343B /* EverythingSendFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 215F08452C9633A800CD343B /* EverythingSendFilter.cpp */; }; 2192460D2CA3469D00CF5703 /* RootCollection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2192460B2CA3469D00CF5703 /* RootCollection.cpp */; }; 219F30422C769283009834E9 /* ConfigTests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 219F30402C769282009834E9 /* ConfigTests.cpp */; }; + 21AEF9EB2CAB56E5000B8681 /* SendError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9E32CAB56E5000B8681 /* SendError.cpp */; }; + 21AEF9EC2CAB56E5000B8681 /* SendViaBrowserArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9E52CAB56E5000B8681 /* SendViaBrowserArgs.cpp */; }; + 21AEF9EF2CAB5720000B8681 /* SendObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9EE2CAB5720000B8681 /* SendObject.cpp */; }; + 21AEF9F32CAC12D1000B8681 /* SendManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9F02CAC12D1000B8681 /* SendManager.cpp */; }; + 21AEF9FA2CAC3897000B8681 /* ConversionResult.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21AEF9F92CAC3897000B8681 /* ConversionResult.cpp */; }; 21B67CA32C769CB400FD64FC /* libActiveLib.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F69EF52C64FE91008B6A06 /* libActiveLib.a */; }; 21B67CA42C769CB400FD64FC /* libArchicad27.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 21F69ECD2C64C035008B6A06 /* libArchicad27.a */; }; 21B67CAC2C77329800FD64FC /* BaseBridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 21B67CA52C77329800FD64FC /* BaseBridge.cpp */; }; @@ -306,6 +311,16 @@ 219F30352C768F0A009834E9 /* Connector-AC27-Test.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Connector-AC27-Test.bundle"; sourceTree = BUILT_PRODUCTS_DIR; }; 219F30402C769282009834E9 /* ConfigTests.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = ConfigTests.cpp; sourceTree = ""; }; 219F30432C7693B6009834E9 /* Connector-AC27-Debug.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "Connector-AC27-Debug.xctestplan"; sourceTree = SOURCE_ROOT; }; + 21AEF9E32CAB56E5000B8681 /* SendError.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SendError.cpp; sourceTree = ""; }; + 21AEF9E42CAB56E5000B8681 /* SendError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendError.h; sourceTree = ""; }; + 21AEF9E52CAB56E5000B8681 /* SendViaBrowserArgs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SendViaBrowserArgs.cpp; sourceTree = ""; }; + 21AEF9E62CAB56E5000B8681 /* SendViaBrowserArgs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendViaBrowserArgs.h; sourceTree = ""; }; + 21AEF9ED2CAB5720000B8681 /* SendObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendObject.h; sourceTree = ""; }; + 21AEF9EE2CAB5720000B8681 /* SendObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SendObject.cpp; sourceTree = ""; }; + 21AEF9F02CAC12D1000B8681 /* SendManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SendManager.cpp; sourceTree = ""; }; + 21AEF9F12CAC12D1000B8681 /* SendManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SendManager.h; sourceTree = ""; }; + 21AEF9F82CAC3897000B8681 /* ConversionResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConversionResult.h; sourceTree = ""; }; + 21AEF9F92CAC3897000B8681 /* ConversionResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConversionResult.cpp; sourceTree = ""; }; 21B67CA52C77329800FD64FC /* BaseBridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BaseBridge.cpp; sourceTree = ""; }; 21B67CA62C77329800FD64FC /* BaseBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseBridge.h; sourceTree = ""; }; 21B67CA72C77329800FD64FC /* GetSourceApplicationName.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GetSourceApplicationName.cpp; sourceTree = ""; }; @@ -945,6 +960,30 @@ path = ConnectorTests; sourceTree = ""; }; + 21AEF9E72CAB56E5000B8681 /* Arg */ = { + isa = PBXGroup; + children = ( + 21AEF9F92CAC3897000B8681 /* ConversionResult.cpp */, + 21AEF9F82CAC3897000B8681 /* ConversionResult.h */, + 21AEF9E32CAB56E5000B8681 /* SendError.cpp */, + 21AEF9E42CAB56E5000B8681 /* SendError.h */, + 21AEF9EE2CAB5720000B8681 /* SendObject.cpp */, + 21AEF9ED2CAB5720000B8681 /* SendObject.h */, + 21AEF9E52CAB56E5000B8681 /* SendViaBrowserArgs.cpp */, + 21AEF9E62CAB56E5000B8681 /* SendViaBrowserArgs.h */, + ); + path = Arg; + sourceTree = ""; + }; + 21AEF9F22CAC12D1000B8681 /* Serialise */ = { + isa = PBXGroup; + children = ( + 21AEF9F02CAC12D1000B8681 /* SendManager.cpp */, + 21AEF9F12CAC12D1000B8681 /* SendManager.h */, + ); + path = Serialise; + sourceTree = ""; + }; 21B67CAB2C77329800FD64FC /* Base */ = { isa = PBXGroup; children = ( @@ -1029,6 +1068,7 @@ 21D0BD5D2C89BFEA0077E104 /* Send */ = { isa = PBXGroup; children = ( + 21AEF9E72CAB56E5000B8681 /* Arg */, 21D0BD962C8F13F30077E104 /* GetSendFilters.cpp */, 21D0BD952C8F13F30077E104 /* GetSendFilters.h */, 21D0BD8D2C8EE4490077E104 /* Send.cpp */, @@ -1143,6 +1183,7 @@ 21F69F092C677BC0008B6A06 /* Event */, 21F69F0E2C677BC0008B6A06 /* Interface */, 21D0BDD82C9387E60077E104 /* Record */, + 21AEF9F22CAC12D1000B8681 /* Serialise */, 21B67CBA2C774C6500FD64FC /* Version.h */, ); path = Connector; @@ -1411,8 +1452,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 21AEF9FA2CAC3897000B8681 /* ConversionResult.cpp in Sources */, 21D0BDAB2C8F363E0077E104 /* CardSetting.cpp in Sources */, 21B67CE32C78D1FB00FD64FC /* SayHiArg.cpp in Sources */, + 21AEF9EB2CAB56E5000B8681 /* SendError.cpp in Sources */, 215F08462C9633A800CD343B /* EverythingSendFilter.cpp in Sources */, 21F69FBB2C762EF0008B6A06 /* ConfigBridge.cpp in Sources */, 21F69F8A2C70D2C4008B6A06 /* AccountBridge.cpp in Sources */, @@ -1427,11 +1470,13 @@ 21B67CAC2C77329800FD64FC /* BaseBridge.cpp in Sources */, 2192460D2CA3469D00CF5703 /* RootCollection.cpp in Sources */, 21D0BD6A2C8A0DB40077E104 /* GetIsDevMode.cpp in Sources */, + 21AEF9F32CAC12D1000B8681 /* SendManager.cpp in Sources */, 210CC8832C80E6A300610F58 /* TriggerEvent.cpp in Sources */, 21B67CEB2C78D27200FD64FC /* DocumentInfo.cpp in Sources */, 21B67CB92C774BFA00FD64FC /* GetConnectorVersion.cpp in Sources */, 21B67CD92C78C83800FD64FC /* TestBridge.cpp in Sources */, 214B7A372C764BCD00D586C1 /* UpdateConfig.cpp in Sources */, + 21AEF9EC2CAB56E5000B8681 /* SendViaBrowserArgs.cpp in Sources */, 21FF70492CA1A7F400AAD99A /* RecordCollection.cpp in Sources */, 21B67CC02C775A0D00FD64FC /* GetDocumentInfo.cpp in Sources */, 21D0BDD42C935D1A0077E104 /* UpdateModel.cpp in Sources */, @@ -1442,6 +1487,7 @@ 215F082A2C947F4400CD343B /* CardMover.cpp in Sources */, 215F08372C95808B00CD343B /* ReceiverModelCard.cpp in Sources */, 21D0BDD72C935DAE0077E104 /* RemoveModel.cpp in Sources */, + 21AEF9EF2CAB5720000B8681 /* SendObject.cpp in Sources */, 21B67CDC2C78C88000FD64FC /* SayHi.cpp in Sources */, 215F082E2C94C5C000CD343B /* FilterMover.cpp in Sources */, 21F69F122C677BC0008B6A06 /* ConnectorMenu.cpp in Sources */, diff --git a/SpeckleConnector/Connector/ConnectorResource.h b/SpeckleConnector/Connector/ConnectorResource.h index 82885a1..daec179 100755 --- a/SpeckleConnector/Connector/ConnectorResource.h +++ b/SpeckleConnector/Connector/ConnectorResource.h @@ -54,6 +54,9 @@ enum WarningString { //Error strings (errors displayed in alerts) enum ErrorString { noSelectedModelItemsID = 1, + modelCardNotFoundID, + noProjectOpenID, + accountNotFoundID, }; #endif //CONNECTOR_RESOURCE diff --git a/SpeckleConnector/Connector/Database/ModelCardDatabase.cpp b/SpeckleConnector/Connector/Database/ModelCardDatabase.cpp index c10d1b6..9a2f0b3 100644 --- a/SpeckleConnector/Connector/Database/ModelCardDatabase.cpp +++ b/SpeckleConnector/Connector/Database/ModelCardDatabase.cpp @@ -71,6 +71,16 @@ ModelCardDatabase::ModelCardDatabase() { ModelCardDatabase::~ModelCardDatabase() {} +/*-------------------------------------------------------------------- + Get a specified card from the database + + return: The requested card (nullptr on failure) + --------------------------------------------------------------------*/ +ModelCard::Unique ModelCardDatabase::getCard(const speckle::utility::String& cardID) const { + return m_store->getObject(cardID); +} //ModelCardDatabase::getCard + + /*-------------------------------------------------------------------- Get all model cards diff --git a/SpeckleConnector/Connector/Database/ModelCardDatabase.h b/SpeckleConnector/Connector/Database/ModelCardDatabase.h index 79a2353..2f1e080 100644 --- a/SpeckleConnector/Connector/Database/ModelCardDatabase.h +++ b/SpeckleConnector/Connector/Database/ModelCardDatabase.h @@ -29,6 +29,11 @@ namespace connector::database { // MARK: - Functions (const) + /*! + Get a specified card from the database + @return The requested card (nullptr on failure) + */ + record::ModelCard::Unique getCard(const speckle::utility::String& cardID) const; /*! Get all model cards @return All the cards diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.cpp new file mode 100644 index 0000000..c12a724 --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.cpp @@ -0,0 +1,63 @@ +#include "Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.h" + +#include "Active/Serialise/item/Wrapper/ValueWrap.h" + +using namespace active::serialise; +using namespace connector::interfac::browser::bridge; +using namespace speckle::utility; + +namespace { + + ///Serialisation fields + enum FieldIndex { + errorID, + cardID, + }; + + ///Serialisation field IDs + static std::array fieldID = { + Identity{"error"}, + Identity{"modelCardId"}, + }; + +} + +/*-------------------------------------------------------------------- + Fill an inventory with the package items + + inventory: The inventory to receive the package items + + return: True if the package has added items to the inventory + --------------------------------------------------------------------*/ +bool ConversionResult::fillInventory(active::serialise::Inventory& inventory) const { + using enum Entry::Type; + inventory.merge(Inventory{ + { + { fieldID[errorID], errorID, element }, + { fieldID[cardID], cardID, element }, + }, + }.withType(&typeid(ConversionResult))); + return true; +} //ConversionResult::fillInventory + + +/*-------------------------------------------------------------------- + Get the specified cargo + + item: The inventory item to retrieve + + return: The requested cargo (nullptr on failure) + --------------------------------------------------------------------*/ +Cargo::Unique ConversionResult::getCargo(const active::serialise::Inventory::Item& item) const { + if (item.ownerType != &typeid(ConversionResult)) + return nullptr; + using namespace active::serialise; + switch (item.index) { + case errorID: + return std::make_unique>(message); + case cardID: + return std::make_unique>(modelCardID); + default: + return nullptr; //Requested an unknown index + } +} //ConversionResult::getCargo diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.h b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.h new file mode 100644 index 0000000..53146fa --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.h @@ -0,0 +1,68 @@ +#ifndef CONNECTOR_INTERFACE_BRIDGE_SEND_CONVERSION_RESULT +#define CONNECTOR_INTERFACE_BRIDGE_SEND_CONVERSION_RESULT + +#include "Active/Serialise/Package/Wrapper/PackageWrap.h" +#include "Connector/Interface/Browser/Bridge/Send/Arg/SendError.h" +#include "Speckle/Utility/String.h" + +namespace connector::interfac::browser::bridge { + + /*! + A send error to return to the JS in the event of an error + */ + class ConversionResult final : public active::serialise::Package { + public: + enum class Status { + success = 1, + info, + warning, + error, + }; + + // MARK: - Constructors + + /*! + Constructor + @param errMess The error message + @param card The ID of the model card associated with the wrror + */ + ConversionResult(const speckle::utility::String& errMess, const speckle::utility::String& card) : message{errMess}, modelCardID{card} {} + + // MARK: - Public variables + + ///The error message + speckle::utility::String message; + ///The ID of the model card associated with the data + speckle::utility::String modelCardID; + ///The element conversion status + Status status = Status::info; + ///For receive conversion reports, this is the id of the speckle object. For send, it's the host app object id. + speckle::utility::String sourceId; + ///For receive conversion reports, this is the type of the speckle object. For send, it's the host app object type. + speckle::utility::String sourceType; + ///For receive conversion reports, this is the id of the host app object. For send, it's the speckle object id. + speckle::utility::String resultId; + ///For receive conversion reports, this is the type of the host app object. For send, it's the speckle object type. + speckle::utility::String resultType; + ///The exception (nullopt = no exception) + SendError::Option error; + + // MARK: - Serialisation + + /*! + Fill an inventory with the package items + @param inventory The inventory to receive the package items + @return True if the package has added items to the inventory + */ + bool fillInventory(active::serialise::Inventory& inventory) const override; + /*! + Get the specified cargo + @param item The inventory item to retrieve + @return The requested cargo (nullptr on failure) + */ + Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override; + }; + +} + +#endif //CONNECTOR_INTERFACE_BRIDGE_SEND_CONVERSION_RESULT diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendError.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendError.cpp new file mode 100644 index 0000000..a16a49b --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendError.cpp @@ -0,0 +1,63 @@ +#include "Connector/Interface/Browser/Bridge/Send/Arg/SendError.h" + +#include "Active/Serialise/item/Wrapper/ValueWrap.h" + +using namespace active::serialise; +using namespace connector::interfac::browser::bridge; +using namespace speckle::utility; + +namespace { + + ///Serialisation fields + enum FieldIndex { + errorID, + cardID, + }; + + ///Serialisation field IDs + static std::array fieldID = { + Identity{"error"}, + Identity{"modelCardId"}, + }; + +} + +/*-------------------------------------------------------------------- + Fill an inventory with the package items + + inventory: The inventory to receive the package items + + return: True if the package has added items to the inventory + --------------------------------------------------------------------*/ +bool SendError::fillInventory(active::serialise::Inventory& inventory) const { + using enum Entry::Type; + inventory.merge(Inventory{ + { + { fieldID[errorID], errorID, element }, + { fieldID[cardID], cardID, element }, + }, + }.withType(&typeid(SendError))); + return true; +} //SendError::fillInventory + + +/*-------------------------------------------------------------------- + Get the specified cargo + + item: The inventory item to retrieve + + return: The requested cargo (nullptr on failure) + --------------------------------------------------------------------*/ +Cargo::Unique SendError::getCargo(const active::serialise::Inventory::Item& item) const { + if (item.ownerType != &typeid(SendError)) + return nullptr; + using namespace active::serialise; + switch (item.index) { + case errorID: + return std::make_unique>(message); + case cardID: + return std::make_unique>(modelCardID); + default: + return nullptr; //Requested an unknown index + } +} //SendError::getCargo diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendError.h b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendError.h new file mode 100644 index 0000000..fd9a1c4 --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendError.h @@ -0,0 +1,54 @@ +#ifndef CONNECTOR_INTERFACE_BRIDGE_SEND_ERROR +#define CONNECTOR_INTERFACE_BRIDGE_SEND_ERROR + +#include "Active/Serialise/Package/Wrapper/PackageWrap.h" +#include "Speckle/Utility/String.h" + +namespace connector::interfac::browser::bridge { + + /*! + A send error to return to the JS in the event of an error + */ + class SendError final : public active::serialise::Package { + public: + + // MARK: Types + + ///Optional + using Option = std::optional; + + // MARK: - Constructors + + /*! + Constructor + @param errMess The error message + @param card The ID of the model card associated with the wrror + */ + SendError(const speckle::utility::String& errMess, const speckle::utility::String& card) : message{errMess}, modelCardID{card} {} + + // MARK: - Public variables + + ///The error message + speckle::utility::String message; + ///The ID of the model card associated with the data + speckle::utility::String modelCardID; + + // MARK: - Serialisation + + /*! + Fill an inventory with the package items + @param inventory The inventory to receive the package items + @return True if the package has added items to the inventory + */ + bool fillInventory(active::serialise::Inventory& inventory) const override; + /*! + Get the specified cargo + @param item The inventory item to retrieve + @return The requested cargo (nullptr on failure) + */ + Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override; + }; + +} + +#endif //CONNECTOR_INTERFACE_BRIDGE_SEND_ERROR diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendObject.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendObject.cpp new file mode 100644 index 0000000..94666e6 --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendObject.cpp @@ -0,0 +1,146 @@ +#include "Connector/Interface/Browser/Bridge/Send/Send.h" + +#include "Active/Serialise/CargoHold.h" +#include "Active/Serialise/Package/Wrapper/PackageWrap.h" +#include "Connector/Connector.h" +#include "Connector/ConnectorResource.h" +#include "Connector/Database/ModelCardDatabase.h" +#include "Speckle/Interface/Browser/Bridge/BrowserBridge.h" +#include "Speckle/Serialise/Detached/Storage/DetachedMemoryStore.h" +#include "Speckle/Utility/Exception.h" + +using namespace active::serialise; +using namespace connector::interfac::browser::bridge; +using namespace speckle::database; +using namespace speckle::serialise; +using namespace speckle::utility; + +namespace { + + ///Serialisation fields + enum FieldIndex { + errorID, + cardID, + }; + + ///Serialisation field IDs + static std::array fieldID = { + Identity{"error"}, + Identity{"modelCardId"}, + }; + + /*! + A send error to return to the JS in the event of an error + */ + class SendError final : public active::serialise::Package { + public: + + // MARK: - Constructors + + /*! + Constructor + @param errMess The error message + @param card The ID of the model card associated with the wrror + */ + SendError(const String& errMess, const String& card) : message{errMess}, modelCardID{card} {} + + // MARK: - Public variables + + ///The error message + String message; + ///The ID of the model card associated with the data + String modelCardID; + + // MARK: - Serialisation + + /*! + Fill an inventory with the package items + @param inventory The inventory to receive the package items + @return True if the package has added items to the inventory + */ + bool fillInventory(active::serialise::Inventory& inventory) const override; + /*! + Get the specified cargo + @param item The inventory item to retrieve + @return The requested cargo (nullptr on failure) + */ + Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override; + }; + + + +} + +/*-------------------------------------------------------------------- + Fill an inventory with the package items + + inventory: The inventory to receive the package items + + return: True if the package has added items to the inventory + --------------------------------------------------------------------*/ +bool SendError::fillInventory(active::serialise::Inventory& inventory) const { + using enum Entry::Type; + inventory.merge(Inventory{ + { + { fieldID[errorID], errorID, element }, + { fieldID[cardID], cardID, element }, + }, + }.withType(&typeid(SendError))); + return true; +} //SendError::fillInventory + + +/*-------------------------------------------------------------------- + Get the specified cargo + + item: The inventory item to retrieve + + return: The requested cargo (nullptr on failure) + --------------------------------------------------------------------*/ +Cargo::Unique SendError::getCargo(const active::serialise::Inventory::Item& item) const { + if (item.ownerType != &typeid(SendError)) + return nullptr; + using namespace active::serialise; + switch (item.index) { + case errorID: + return std::make_unique>(message); + case cardID: + return std::make_unique>(modelCardID); + default: + return nullptr; //Requested an unknown index + } +} //SendError::getCargo + + +/*-------------------------------------------------------------------- + Default constructor + --------------------------------------------------------------------*/ +Send::Send() : BridgeMethod{"Send", [&](const SendArgs& args) { + run(args); +}} {} + + +/*-------------------------------------------------------------------- + Send a specified model + + modelCardID: The ID of the model to send + --------------------------------------------------------------------*/ +void Send::run(const String& modelCardID) const { + //Find the specified model card + auto modelCardDatabase = connector()->getModelCardDatabase(); + auto modelCard = modelCardDatabase->getCard(modelCardID); + if (!modelCard) { + getBridge()->sendEvent("setModelError", + std::make_unique(connector()->getLocalString(errorString, modelCardNotFoundID), modelCardID)); + return; + } + //Get the active project + auto project = connector()->getActiveProject().lock(); + if (!project) { + getBridge()->sendEvent("setModelError", + std::make_unique(connector()->getLocalString(errorString, noProjectOpenID), modelCardID)); + return; + } + + DetachedMemoryStore detachedObjects; +} //Send::run diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendObject.h b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendObject.h new file mode 100644 index 0000000..9ea777a --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendObject.h @@ -0,0 +1,53 @@ +#ifndef CONNECTOR_INTERFACE_BRIDGE_SEND_OBJECT +#define CONNECTOR_INTERFACE_BRIDGE_SEND_OBJECT + +#include "Active/Serialise/CargoHold.h" +#include "Active/Serialise/Item/Wrapper/ValueWrap.h" +#include "Connector/Interface/Browser/Bridge/Config/Arg/ConnectorConfig.h" +#include "Speckle/Interface/Browser/Bridge/BridgeMethod.h" + +namespace connector::interfac::browser::bridge { + + /*! + Class defining the primary content of a send + */ + class SendObject final : public active::serialise::Package { + public: + + // MARK: - Constructors + + /*! + Constructor + @param errMess The error message + @param card The ID of the model card associated with the wrror + */ + SendObject() {} + + // MARK: - Public variables + + ///The root object id which should be used for creating the version + speckle::utility::String id; + ///The total number of children + int32_t totalChildrenCount; + ///JSON batches for the root object and child (detached) objects + std::vector batches; + + // MARK: - Serialisation + + /*! + Fill an inventory with the package items + @param inventory The inventory to receive the package items + @return True if the package has added items to the inventory + */ + bool fillInventory(active::serialise::Inventory& inventory) const override; + /*! + Get the specified cargo + @param item The inventory item to retrieve + @return The requested cargo (nullptr on failure) + */ + Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override; + }; + +} + +#endif //CONNECTOR_INTERFACE_BRIDGE_SEND_OBJECT diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.cpp new file mode 100644 index 0000000..103d72f --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.cpp @@ -0,0 +1,106 @@ +#include "Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.h" + +#include "Connector/Record/Model/ModelCard.h" +#include "Speckle/Record/Credentials/Account.h" + +using namespace active::serialise; +using namespace connector::record; +using namespace connector::interfac::browser::bridge; +using namespace speckle::database; +using namespace speckle::record::cred; +using namespace speckle::serialise; +using namespace speckle::utility; + +namespace { + + ///Serialisation fields + enum FieldIndex { + cardID, + projID, + modID, + tokenID, + serverID, + accID, + messageID, + }; + + ///Serialisation field IDs + static std::array fieldID = { + Identity{"modelCardId"}, + Identity{"projectId"}, + Identity{"modelId"}, + Identity{"token"}, + Identity{"serverUrl"}, + Identity{"accountId"}, + Identity{"message"}, + }; + +} + +/*-------------------------------------------------------------------- + Constructor + + modelCard: The model card to populate into the send info for the browser + account: The account linked to the send + --------------------------------------------------------------------*/ +SendViaBrowserArgs::SendViaBrowserArgs(const ModelCard& modelCard, const Account& account) : + modelCardID(modelCard.getID()), projectID(modelCard.getProjectID()), modelID(modelCard.getModelID()), token{account.getToken()}, + serverURL{account.getServerURL()}, accountID{account.getID()} { + +} //SendViaBrowserArgs::SendViaBrowserArgs + + +/*-------------------------------------------------------------------- + Fill an inventory with the package items + + inventory: The inventory to receive the package items + + return: True if the package has added items to the inventory + --------------------------------------------------------------------*/ +bool SendViaBrowserArgs::fillInventory(active::serialise::Inventory& inventory) const { + using enum Entry::Type; + inventory.merge(Inventory{ + { + { fieldID[cardID], cardID, element }, + { fieldID[projID], projID, element }, + { fieldID[modID], modID, element }, + { fieldID[tokenID], tokenID, element }, + { fieldID[serverID], serverID, element }, + { fieldID[accID], accID, element }, + { fieldID[messageID], messageID, element }, + }, + }.withType(&typeid(SendViaBrowserArgs))); + return true; +} //SendViaBrowserArgs::fillInventory + + +/*-------------------------------------------------------------------- + Get the specified cargo + + item: The inventory item to retrieve + + return: The requested cargo (nullptr on failure) + --------------------------------------------------------------------*/ +Cargo::Unique SendViaBrowserArgs::getCargo(const active::serialise::Inventory::Item& item) const { + if (item.ownerType != &typeid(SendViaBrowserArgs)) + return nullptr; + using namespace active::serialise; + switch (item.index) { + case cardID: + return std::make_unique(modelCardID); + case projID: + return std::make_unique(projectID); + case modID: + return std::make_unique(modelID); + case tokenID: + return std::make_unique(token); + case serverID: + return std::make_unique(serverURL); + case accID: + return std::make_unique(accountID); + case messageID: + return std::make_unique(message); + default: + return nullptr; //Requested an unknown index + } +} //SendViaBrowserArgs::getCargo diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.h b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.h new file mode 100644 index 0000000..6dac55d --- /dev/null +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.h @@ -0,0 +1,78 @@ +#ifndef CONNECTOR_INTERFACE_BRIDGE_SEND_VIA_BROWSER_ARGS +#define CONNECTOR_INTERFACE_BRIDGE_SEND_VIA_BROWSER_ARGS + +#include "Active/Serialise/Package/Package.h" +#include "Active/Utility/String.h" +#include "Connector/Interface/Browser/Bridge/Send/Arg/ConversionResult.h" +#include "Connector/Interface/Browser/Bridge/Send/Arg/SendObject.h" +#include "Speckle/Database/Identity/RecordID.h" + +namespace speckle::record::cred { + class Account; +} + +namespace connector::record { + class ModelCard; +} + +namespace connector::interfac::browser::bridge { + + class ConnectorConfig; + + /*! + A commit of a project version (model) to the Speckle server + + An object of this type is prepared by the Send bridge method + */ + class SendViaBrowserArgs final : public active::serialise::Package { + public: + + // MARK: - Constructors + + /*! + Constructor + @param modelCard The model card to populate into the send info for the browser + @param account The account linked to the send + */ + SendViaBrowserArgs(const connector::record::ModelCard& modelCard, const speckle::record::cred::Account& account); + + // MARK: - Public variables + + ///ID of the model card driving the send request + speckle::database::RecordID modelCardID; + ///The source project ID (from the model card) + speckle::database::RecordID projectID; + ///The model ID (from the model card) + speckle::database::RecordID modelID; + ///The account token (from the user account info) + speckle::utility::String token; + ///The server URL (from the user account info) + speckle::utility::String serverURL; + ///The user account ID + speckle::utility::String accountID; + ///The send message + speckle::utility::String message; //TODO: Clarify what this is used for + ///The conversion report (summarising the conversion results on an element-by-element basis) + std::vector sendConversionResults; + ///The commit content + SendObject sendObject; + + // MARK: - Serialisation + + /*! + Fill an inventory with the package items + @param inventory The inventory to receive the package items + @return True if the package has added items to the inventory + */ + bool fillInventory(active::serialise::Inventory& inventory) const override; + /*! + Get the specified cargo + @param item The inventory item to retrieve + @return The requested cargo (nullptr on failure) + */ + Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override; + }; + +} + +#endif //CONNECTOR_INTERFACE_BRIDGE_SEND_VIA_BROWSER_ARGS diff --git a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Send.cpp b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Send.cpp index 73c41b8..4fe5560 100644 --- a/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Send.cpp +++ b/SpeckleConnector/Connector/Interface/Browser/Bridge/Send/Send.cpp @@ -4,86 +4,23 @@ #include "Active/Serialise/Package/Wrapper/PackageWrap.h" #include "Connector/Connector.h" #include "Connector/ConnectorResource.h" +#include "Connector/Database/ModelCardDatabase.h" +#include "Connector/Interface/Browser/Bridge/Send/Arg/SendError.h" +#include "Connector/Interface/Browser/Bridge/Send/Arg/SendViaBrowserArgs.h" +#include "Speckle/Database/AccountDatabase.h" #include "Speckle/Interface/Browser/Bridge/BrowserBridge.h" +#include "Speckle/Record/Credentials/Account.h" +#include "Speckle/Serialise/Detached/Storage/DetachedMemoryStore.h" #include "Speckle/Utility/Exception.h" using namespace active::serialise; using namespace connector::interfac::browser::bridge; +using namespace speckle::database; +using namespace speckle::serialise; using namespace speckle::utility; namespace { - - ///Serialisation fields - enum FieldIndex { - errorID, - cardID, - }; - - ///Serialisation field IDs - static std::array fieldID = { - Identity{"error"}, - Identity{"modelCardId"}, - }; - - /*! - A send error to return to the JS in the event of an error - */ - class SendError final : public active::serialise::Package { - public: - - // MARK: - Constructors - /*! - Constructor - @param errMess The error message - @param card The ID of the model card associated with the wrror - */ - SendError(const String& errMess, const String& card) : message{errMess}, modelCardID{card} {} - - // MARK: - Public variables - - ///The error message - String message; - ///The ID of the model card associated with the data - String modelCardID; - - // MARK: - Serialisation - - /*! - Fill an inventory with the package items - @param inventory The inventory to receive the package items - @return True if the package has added items to the inventory - */ - bool fillInventory(active::serialise::Inventory& inventory) const override { - using enum Entry::Type; - inventory.merge(Inventory{ - { - { fieldID[errorID], errorID, element }, - { fieldID[cardID], cardID, element }, - }, - }.withType(&typeid(SendError))); - return true; - } - /*! - Get the specified cargo - @param item The inventory item to retrieve - @return The requested cargo (nullptr on failure) - */ - Cargo::Unique getCargo(const active::serialise::Inventory::Item& item) const override { - if (item.ownerType != &typeid(SendError)) - return nullptr; - using namespace active::serialise; - switch (item.index) { - case errorID: - return std::make_unique>(message); - case cardID: - return std::make_unique>(modelCardID); - default: - return nullptr; //Requested an unknown index - } - } - }; - } /*-------------------------------------------------------------------- @@ -100,7 +37,31 @@ Send::Send() : BridgeMethod{"Send", [&](const SendArgs& args) { modelCardID: The ID of the model to send --------------------------------------------------------------------*/ void Send::run(const String& modelCardID) const { - ///TODO: Find and send selected elements - the following is a placeholder - getBridge()->sendEvent("setModelError", - std::make_unique(connector()->getLocalString(errorString, noSelectedModelItemsID), modelCardID)); + //Find the specified model card + auto modelCardDatabase = connector()->getModelCardDatabase(); + auto modelCard = modelCardDatabase->getCard(modelCardID); + if (!modelCard) { + getBridge()->sendEvent("setModelError", + std::make_unique(connector()->getLocalString(errorString, modelCardNotFoundID), modelCardID)); + return; + } + auto accountDatabase = connector()->getAccountDatabase(); + auto account = accountDatabase->getAccount(modelCard->getAccountID(), modelCard->getServerURL()); + if (!account) { + getBridge()->sendEvent("setModelError", + std::make_unique(connector()->getLocalString(errorString, accountNotFoundID), modelCardID)); + return; + } + //Get the active project + auto project = connector()->getActiveProject().lock(); + if (!project) { + getBridge()->sendEvent("setModelError", + std::make_unique(connector()->getLocalString(errorString, noProjectOpenID), modelCardID)); + return; + } + //We currently collect all detached object serialised data into a memory-based store - in future may be able to batch send and cache locally + DetachedMemoryStore detachedObjects; + auto result = std::make_unique(modelCard, account); + + getBridge()->sendEvent("sendByBrowser", std::move(result)); } //Send::run diff --git a/SpeckleConnector/Connector/Record/Model/ModelCard.h b/SpeckleConnector/Connector/Record/Model/ModelCard.h index a192a8b..64fd80b 100644 --- a/SpeckleConnector/Connector/Record/Model/ModelCard.h +++ b/SpeckleConnector/Connector/Record/Model/ModelCard.h @@ -19,6 +19,8 @@ namespace connector::record { // MARK: - Types using base = speckle::database::Record; + ///Unique pointer + using Unique = std::unique_ptr; //List of card settings using SettingList = active::container::Vector; diff --git a/SpeckleConnector/RINT/Connector.grc b/SpeckleConnector/RINT/Connector.grc index bac807c..c1e3741 100644 --- a/SpeckleConnector/RINT/Connector.grc +++ b/SpeckleConnector/RINT/Connector.grc @@ -5,4 +5,7 @@ 'STR#' 32604 "Error strings" { /* [ 1] */ "No objects were found to convert. Please update your publish filter!" +/* [ 2] */ "The specified model card cannot be found. Try another card or create a new one" +/* [ 3] */ "Please open a project first" +/* [ 4] */ "The specified Speckle account cannot be found. Check that you have logged into your Speckle account with the Speckle Manager" } diff --git a/SpeckleLib/Speckle/Database/AccountDatabase.cpp b/SpeckleLib/Speckle/Database/AccountDatabase.cpp index b6f2967..eb8fbe6 100644 --- a/SpeckleLib/Speckle/Database/AccountDatabase.cpp +++ b/SpeckleLib/Speckle/Database/AccountDatabase.cpp @@ -81,6 +81,27 @@ AccountDatabase::AccountDatabase(const active::file::Path& path) { AccountDatabase::~AccountDatabase() {} +/*-------------------------------------------------------------------- + Get a specified account. NB: The server URL is provided as a fallback for the search if the specified accountID is not found + + accountID: The account ID (the primary search field) + serverURL: The server URL (a fallback search field if the account ID does not exist) + + return: The requested account (nullptr on failure) + --------------------------------------------------------------------*/ +std::unique_ptr AccountDatabase::getAccount(const String& accountID, const String& serverURL) const { + //First attempt to find a matching account ID + auto matchingAccount = m_store->getObjects([&accountID](const auto& acc) { return acc.getID() == accountID; }); + if (!matchingAccount.empty()) + return matchingAccount.release(matchingAccount.begin()); + //Alternatively seek an account with a matching server URL + matchingAccount = m_store->getObjects([&serverURL](const auto& acc) { return acc.getServerURL() == serverURL; }); + if (!matchingAccount.empty()) + return matchingAccount.release(matchingAccount.begin()); + return nullptr; +} //AccountDatabase::getAccount + + /*-------------------------------------------------------------------- Get all accounts diff --git a/SpeckleLib/Speckle/Database/AccountDatabase.h b/SpeckleLib/Speckle/Database/AccountDatabase.h index 28295e9..7e4f6dd 100644 --- a/SpeckleLib/Speckle/Database/AccountDatabase.h +++ b/SpeckleLib/Speckle/Database/AccountDatabase.h @@ -4,6 +4,10 @@ #include "Active/File/Path.h" #include "Speckle/Record/Credentials/Account.h" +namespace speckle::record::cred { + class Account; +} + namespace speckle::database { /*! @@ -27,6 +31,13 @@ namespace speckle::database { // MARK: - Functions (const) + /*! + Get a specified account. NB: The server URL is provided as a fallback for the search if the specified accountID is not found + @param accountID The account ID (the primary search field) + @param serverURL The server URL (a fallback search field if the account ID does not exist) + @return The requested account (nullptr on failure) + */ + std::unique_ptr getAccount(const speckle::utility::String& accountID, const speckle::utility::String& serverURL) const; /*! Get all accounts @return All the accounts diff --git a/SpeckleLib/Speckle/Record/Credentials/Account.h b/SpeckleLib/Speckle/Record/Credentials/Account.h index 757f6a5..fc902a4 100644 --- a/SpeckleLib/Speckle/Record/Credentials/Account.h +++ b/SpeckleLib/Speckle/Record/Credentials/Account.h @@ -48,6 +48,17 @@ namespace speckle::record::cred { // MARK: - Functions (const) + /*! + Get the account server URL + @return The account server URL (nullopt if none specified) + */ + speckle::utility::String getServerURL() const { return m_serverInfo.getURL(); } + /*! + Get the account token + @return The account token + */ + speckle::utility::String getToken() const { return m_token; } + // MARK: - Functions (mutating) // MARK: - Serialisation diff --git a/SpeckleLib/Speckle/Record/Credentials/ServerInfo.cpp b/SpeckleLib/Speckle/Record/Credentials/ServerInfo.cpp index 9a237e5..377e9fd 100644 --- a/SpeckleLib/Speckle/Record/Credentials/ServerInfo.cpp +++ b/SpeckleLib/Speckle/Record/Credentials/ServerInfo.cpp @@ -89,7 +89,7 @@ Cargo::Unique ServerInfo::getCargo(const Inventory::Item& item) const { case frontEndID: return std::make_unique>(m_frontend2); case urlID: - return std::make_unique(m_url); + return std::make_unique(m_url); case migrationID: return std::make_unique(PackageUniqueWrap{m_migration}); default: diff --git a/SpeckleLib/Speckle/Record/Credentials/ServerInfo.h b/SpeckleLib/Speckle/Record/Credentials/ServerInfo.h index 7e7d1a0..409f2d0 100644 --- a/SpeckleLib/Speckle/Record/Credentials/ServerInfo.h +++ b/SpeckleLib/Speckle/Record/Credentials/ServerInfo.h @@ -32,7 +32,7 @@ namespace speckle::record::cred { */ ServerInfo(const utility::String& name, const utility::String::Option company = std::nullopt, const utility::String::Option version = std::nullopt, const utility::String::Option contact = std::nullopt, - const utility::String::Option description = std::nullopt, const utility::String::Option url = std::nullopt, + const utility::String::Option description = std::nullopt, const utility::String url = {}, bool isFrontEnd = false, std::unique_ptr migration = nullptr) : m_name{name}, m_company{company}, m_version{version}, m_adminContact{contact}, m_description{description}, m_url{url}, m_frontend2{isFrontEnd}, m_migration{std::move(migration)} {} @@ -90,7 +90,7 @@ namespace speckle::record::cred { Get the URL @return The URL */ - const utility::String::Option& getURL() const { return m_url; } + const utility::String& getURL() const { return m_url; } /*! Get the migration history @return The migration history (nullptr = no history) @@ -143,7 +143,7 @@ namespace speckle::record::cred { This field is not returned from the GQL API, it should be populated after construction. See "Speckle.Core.Credentials.AccountManager" */ - utility::String::Option m_url; + utility::String m_url; ///Server migration record std::unique_ptr m_migration; }; diff --git a/SpeckleLib/Speckle/Serialise/Detached/DetachedWrap.cpp b/SpeckleLib/Speckle/Serialise/Detached/DetachedWrap.cpp index 141b4b9..79192f8 100644 --- a/SpeckleLib/Speckle/Serialise/Detached/DetachedWrap.cpp +++ b/SpeckleLib/Speckle/Serialise/Detached/DetachedWrap.cpp @@ -3,7 +3,6 @@ #include "Active/Serialise/Item/Wrapper/ValueWrap.h" #include "Active/Utility/BufferOut.h" #include "Active/Utility/SHA256.h" -#include "Speckle/Serialise/Detached/DetachmentManager.h" #include "Speckle/Utility/String.h" using namespace active::serialise; diff --git a/SpeckleLib/Speckle/Serialise/Detached/Storage/DetachedMemoryStore.h b/SpeckleLib/Speckle/Serialise/Detached/Storage/DetachedMemoryStore.h new file mode 100644 index 0000000..5708f54 --- /dev/null +++ b/SpeckleLib/Speckle/Serialise/Detached/Storage/DetachedMemoryStore.h @@ -0,0 +1,52 @@ +#ifndef SPECKLE_SERIALISE_DETACHED_MEMORY_STORE +#define SPECKLE_SERIALISE_DETACHED_MEMORY_STORE + +#include "Speckle/Serialise/Detached/Storage/DetachedObjectStore.h" + +#include + +namespace speckle::serialise { + + /*! + Memory-based storage for detached objects + + Detached object serialised data is held in a simple unordered_map keying the data against the object reference. + This class primarily intended for collecting the JSON from detached objects and holding until they can be forwarded to the server. The data + will not persist beyond this usage. + */ + class DetachedMemoryStore : public DetachedObjectStore, public std::unordered_map { + public: + + // MARK: - Types + + using base = std::unordered_map; + + // MARK: - Functions (const) + + /*! + Retrieve a detached object from storage + @param reference The required object reference + @return The object data (nullopt if the object cannot be found in storage) + */ + utility::String::Option retrieve(const database::RecordID& reference) const override { + if (auto iter = base::find(reference); iter != base::end()) + return iter->second; + return std::nullopt; + } + + // MARK: - Functions (mutating) + + /*! + File a detached object in storage + @param reference The object reference (typically a hash generated from the object data) + @param data The object data (currently expected to be serialised as JSON) + @return True if the object was filed (typically ignored if it duplicates an object already in the store) + */ + bool file(const database::RecordID& reference, const utility::String& data) override { + return base::insert(std::make_pair(reference, data)).second; + } + }; + +} + +#endif //SPECKLE_SERIALISE_DETACHED_MEMORY_STORE diff --git a/SpeckleLib/Speckle/Serialise/Detached/Storage/DetachedObjectStore.h b/SpeckleLib/Speckle/Serialise/Detached/Storage/DetachedObjectStore.h index 546d572..441c699 100644 --- a/SpeckleLib/Speckle/Serialise/Detached/Storage/DetachedObjectStore.h +++ b/SpeckleLib/Speckle/Serialise/Detached/Storage/DetachedObjectStore.h @@ -28,7 +28,7 @@ namespace speckle::serialise { @param reference The required object reference @return The object data (nullopt if the object cannot be found in storage) */ - virtual utility::String::Option retrieve(const database::RecordID& reference) = 0; + virtual utility::String::Option retrieve(const database::RecordID& reference) const = 0; // MARK: - Functions (mutating) @@ -38,7 +38,7 @@ namespace speckle::serialise { @param data The object data (currently expected to be serialised as JSON) @return True if the object was filed (typically rejected if it duplicates an object already in the store) */ - virtual bool file(const database::RecordID& reference, const utility::String& data) const = 0; + virtual bool file(const database::RecordID& reference, const utility::String& data) = 0; }; } diff --git a/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj b/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj index 82a12ac..d4d6ea1 100644 --- a/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj +++ b/SpeckleLib/SpeckleLib.xcodeproj/project.pbxproj @@ -170,6 +170,7 @@ 21AEF9BB2CA6DF84000B8681 /* DetachmentManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DetachmentManager.cpp; sourceTree = ""; }; 21AEF9BD2CA6FDA4000B8681 /* DetachedWrap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DetachedWrap.cpp; sourceTree = ""; }; 21AEF9DB2CAAA4EA000B8681 /* DetachedObjectStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachedObjectStore.h; sourceTree = ""; }; + 21AEF9DF2CAAC2AF000B8681 /* DetachedMemoryStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetachedMemoryStore.h; sourceTree = ""; }; 21B67CFE2C7CE15100FD64FC /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = ""; }; 21B67D092C7E0E8D00FD64FC /* ErrorReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ErrorReport.h; sourceTree = ""; }; 21B67D0C2C7E0E8D00FD64FC /* ErrorReport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ErrorReport.cpp; sourceTree = ""; }; @@ -433,6 +434,7 @@ isa = PBXGroup; children = ( 21AEF9DB2CAAA4EA000B8681 /* DetachedObjectStore.h */, + 21AEF9DF2CAAC2AF000B8681 /* DetachedMemoryStore.h */, ); path = Storage; sourceTree = "";