diff --git a/src/blackcore/webdataservices.cpp b/src/blackcore/webdataservices.cpp index 4298d5f5a..080714877 100644 --- a/src/blackcore/webdataservices.cpp +++ b/src/blackcore/webdataservices.cpp @@ -109,6 +109,17 @@ namespace BlackCore return cl; } + QList CWebDataServices::connectDataPublishSignal(QObject *receiver, std::function dataPublished) + { + Q_ASSERT_X(receiver, Q_FUNC_INFO, "Missing receiver"); + QList cl; + if (!m_databaseWriter) { return cl; } + QMetaObject::Connection con = connect(this->m_databaseWriter, &CDatabaseWriter::published, receiver, dataPublished); + Q_ASSERT_X(con, Q_FUNC_INFO, "connect failed publishing signal"); + cl.push_back(con); + return cl; + } + CServerList CWebDataServices::getVatsimFsdServers() const { if (m_vatsimDataFileReader) { return m_vatsimDataFileReader->getFsdServers(); } @@ -144,9 +155,9 @@ namespace BlackCore if (m_vatsimDataFileReader) { m_vatsimDataFileReader->updateWithVatsimDataFileData(aircraftToBeUdpated); } } - CStatusMessageList CWebDataServices::asyncWriteModel(const CAircraftModel &model) const + CStatusMessageList CWebDataServices::asyncPublishModels(const CAircraftModelList &models) const { - if (m_databaseWriter) { return m_databaseWriter->asyncWriteModel(model);} + if (m_databaseWriter) { return m_databaseWriter->asyncPublishModels(models);} return CStatusMessageList(); } diff --git a/src/blackcore/webdataservices.h b/src/blackcore/webdataservices.h index 3ef050a4c..01a1af0c0 100644 --- a/src/blackcore/webdataservices.h +++ b/src/blackcore/webdataservices.h @@ -83,6 +83,12 @@ namespace BlackCore QObject *receiver, std::function dataRead) override; + //! \copydoc BlackMisc::Network::IWebDataServicesProvider::connectDataPublishSignal + //! \ingroup webdatareaderprovider + virtual QList connectDataPublishSignal( + QObject *receiver, + std::function dataPublished) override; + //! \copydoc BlackMisc::Network::IWebDataServicesProvider::triggerRead //! \ingroup webdatareaderprovider virtual BlackMisc::Network::CEntityFlags::Entity triggerRead(BlackMisc::Network::CEntityFlags::Entity whatToRead) override; @@ -227,15 +233,15 @@ namespace BlackCore //! \ingroup webdatareaderprovider virtual void updateWithVatsimDataFileData(BlackMisc::Simulation::CSimulatedAircraft &aircraftToBeUdpated) const override; - //! \copydoc BlackMisc::Network::IWebDataServicesProvider::asyncWriteModel + //! \copydoc BlackMisc::Network::IWebDataServicesProvider::asyncPublishModels //! \ingroup webdatareaderprovider - virtual BlackMisc::CStatusMessageList asyncWriteModel(const BlackMisc::Simulation::CAircraftModel &model) const override; + virtual BlackMisc::CStatusMessageList asyncPublishModels(const BlackMisc::Simulation::CAircraftModelList &models) const override; //! \copydoc BlackMisc::Network::IWebDataServicesProvider::canConnectSwiftDb //! \ingroup webdatareaderprovider virtual bool canConnectSwiftDb() const override; - //! Save all DB data to JSON files + //! Save all DB data to a JSON files //! \ingroup webdatareaderprovider virtual bool writeDbDataToDisk(const QString &dir) const override; diff --git a/src/blackmisc/datastore.cpp b/src/blackmisc/datastore.cpp index 6bd1cd3ec..7ab585df8 100644 --- a/src/blackmisc/datastore.cpp +++ b/src/blackmisc/datastore.cpp @@ -35,6 +35,14 @@ namespace BlackMisc this->m_dbKey = k; } + int IDatastoreObjectWithIntegerKey::stringToDbKey(const QString &candidate) + { + if (candidate.isEmpty()) { return invalidDbKey(); } + bool ok; + int k = candidate.toInt(&ok); + return ok ? k : invalidDbKey(); + } + QJsonValue IDatastoreObjectWithIntegerKey::getDbKeyAsJsonValue() const { if (this->hasValidDbKey()) { return QJsonValue(this->m_dbKey); } diff --git a/src/blackmisc/datastore.h b/src/blackmisc/datastore.h index 9dd671003..15a528e8f 100644 --- a/src/blackmisc/datastore.h +++ b/src/blackmisc/datastore.h @@ -55,6 +55,9 @@ namespace BlackMisc //! Invalid key static int invalidDbKey() { return -1; } + //! Convert string to DB key + static int stringToDbKey(const QString &candidate); + protected: //! Constructor IDatastoreObjectWithIntegerKey() {} diff --git a/src/blackmisc/datastoreutility.cpp b/src/blackmisc/datastoreutility.cpp index bb75993d7..01712cbf4 100644 --- a/src/blackmisc/datastoreutility.cpp +++ b/src/blackmisc/datastoreutility.cpp @@ -7,12 +7,16 @@ * contained in the LICENSE file. */ +#include "blackmisc/simulation/aircraftmodellist.h" #include "blackmisc/datastoreutility.h" #include "blackmisc/blackmiscfreefunctions.h" #include "blackmisc/stringutils.h" #include #include +using namespace BlackMisc; +using namespace BlackMisc::Simulation; + namespace BlackMisc { bool CDatastoreUtility::dbBoolStringToBool(const QString &dbBool) @@ -48,7 +52,6 @@ namespace BlackMisc QDateTime CDatastoreUtility::parseTimestamp(const QString ×tamp) { - Q_ASSERT_X(!timestamp.isEmpty(), Q_FUNC_INFO, "Missing timestamp"); if (!timestamp.isEmpty()) { QString ts(timestamp.trimmed().remove(' ').remove('-').remove(':')); // normalize @@ -61,33 +64,76 @@ namespace BlackMisc } } - bool CDatastoreUtility::parseSwiftWriteResponse(const QString &jsonResponse, CStatusMessageList &messages, CVariant &key) + bool CDatastoreUtility::parseSwiftPublishResponse(const QString &jsonResponse, CAircraftModelList &publishedModels, CAircraftModelList &skippedModels, CStatusMessageList &messages) { - if (jsonResponse.isEmpty()) { return ""; } - QJsonDocument jsonDoc(QJsonDocument::fromJson(jsonResponse.toUtf8())); - if (!jsonDoc.isObject()) { return ""; } - QJsonObject json(jsonDoc.object()); - Q_ASSERT_X(!json.value("id").isNull(), Q_FUNC_INFO, "malformed response"); - if (json.value("id").isNull()) { return false; } - QString id(json.value("id").toString().trimmed()); - QJsonArray msgObject(json.value("messages").toArray()); - messages.push_back(CStatusMessageList::fromDatabaseJson(msgObject)); - bool success = false; + static const CLogCategoryList cats({ CLogCategory::swiftDbWebservice()}); + if (jsonResponse.isEmpty()) + { + messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Empty JSON data")); + return false; + } - int intKey; - bool isInt; - intKey = id.toInt(&isInt); - if (isInt) + QJsonDocument jsonDoc(QJsonDocument::fromJson(jsonResponse.toUtf8())); + + // array of messages + if (jsonDoc.isArray()) { - key.setValue(intKey); - success = (intKey >= 0); + CStatusMessageList msgs(CStatusMessageList::fromDatabaseJson(jsonDoc.array())); + messages.push_back(msgs); + return true; } - else + + // no object -> most likely some fucked up HTML string with the PHP error + if (!jsonDoc.isObject()) { - key.setValue(id); - success = !id.isEmpty(); + QString phpError(jsonResponse); + phpError.remove(QRegExp("<[^>]*>")); + messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, phpError)); + return false; } - return success; + + // fully blown object + QJsonObject json(jsonDoc.object()); + bool data = false; + if (json.contains("msgs")) + { + QJsonValue msgJson(json.take("msgs")); + CStatusMessageList msgs(CStatusMessageList::fromDatabaseJson(msgJson.toArray())); + if (!msgs.isEmpty()) + { + messages.push_back(msgs); + data = true; + } + } + + if (json.contains("publishedModels")) + { + QJsonValue publishedJson(json.take("publishedModels")); + CAircraftModelList published = CAircraftModelList::fromDatabaseJson(publishedJson.toArray()); + if (!published.isEmpty()) + { + publishedModels.push_back(published); + data = true; + } + } + + if (json.contains("skippedModels")) + { + QJsonValue skippedJson(json.take("skippedModels")); + CAircraftModelList skipped = CAircraftModelList::fromDatabaseJson(skippedJson.toArray()); + if (!skipped.isEmpty()) + { + skippedModels.push_back(skipped); + data = true; + } + } + + if (!data) + { + messages.push_back(CStatusMessage(cats, CStatusMessage::SeverityError, "Received response, but no JSON data")); + } + + return data; } } // namespace diff --git a/src/blackmisc/datastoreutility.h b/src/blackmisc/datastoreutility.h index 3e6f92569..3be293322 100644 --- a/src/blackmisc/datastoreutility.h +++ b/src/blackmisc/datastoreutility.h @@ -41,8 +41,8 @@ namespace BlackMisc //! Parse a timestamp object static QDateTime parseTimestamp(const QString ×tamp); - //! Get id from a DB response - static bool parseSwiftWriteResponse(const QString &jsonResponse, BlackMisc::CStatusMessageList &messages, BlackMisc::CVariant &key); + //! Get data from a DB response + static bool parseSwiftPublishResponse(const QString &jsonResponse, BlackMisc::Simulation::CAircraftModelList &publishedModels, BlackMisc::Simulation::CAircraftModelList &skippedModels, BlackMisc::CStatusMessageList &messages); }; } // namespace diff --git a/src/blackmisc/network/networkutils.cpp b/src/blackmisc/network/networkutils.cpp index 54c3957ad..72aabf139 100644 --- a/src/blackmisc/network/networkutils.cpp +++ b/src/blackmisc/network/networkutils.cpp @@ -54,7 +54,7 @@ namespace BlackMisc { QStringList ips; if (!CNetworkUtils::hasConnectedInterface(false)) return ips; - foreach(const QHostAddress & address, QNetworkInterface::allAddresses()) + foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) { if (address.isLoopback() || address.isNull()) continue; if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost)) @@ -207,13 +207,24 @@ namespace BlackMisc qurl.addQueryItem("XDEBUG_SESSION_START", "ECLIPSE_DBGP"); } - QHttpPart CNetworkUtils::getJsonTextMutlipart(const QJsonObject &json) + QHttpPart CNetworkUtils::getJsonTextMultipart(const QJsonObject &json) + { + const QByteArray bytes(QJsonDocument(json).toJson(QJsonDocument::Compact)); + return getJsonTextMultipart(bytes); + } + + QHttpPart CNetworkUtils::getJsonTextMultipart(const QJsonArray &json) + { + const QByteArray bytes(QJsonDocument(json).toJson(QJsonDocument::Compact)); + return getJsonTextMultipart(bytes); + } + + QHttpPart CNetworkUtils::getJsonTextMultipart(const QByteArray &bytes) { - const QByteArray jsonData(QJsonDocument(json).toJson(QJsonDocument::Compact)); QHttpPart textPart; QString name("form-data; name=\"swiftjson\""); textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(name)); - textPart.setBody(jsonData); + textPart.setBody(bytes); return textPart; } diff --git a/src/blackmisc/network/networkutils.h b/src/blackmisc/network/networkutils.h index 83167b42a..5e4947efe 100644 --- a/src/blackmisc/network/networkutils.h +++ b/src/blackmisc/network/networkutils.h @@ -99,7 +99,13 @@ namespace BlackMisc static void addDebugFlag(QUrlQuery &qurl); //! Multipart for JSON - static QHttpPart getJsonTextMutlipart(const QJsonObject &json); + static QHttpPart getJsonTextMultipart(const QJsonObject &json); + + //! Multipart for JSON + static QHttpPart getJsonTextMultipart(const QJsonArray &json); + + //! Multipart for JSON + static QHttpPart getJsonTextMultipart(const QByteArray &bytes); //! Our tweakes network request static QNetworkRequest getNetworkRequest(const CUrl &url, RequestType type = Get); diff --git a/src/blackmisc/network/webdataservicesprovider.cpp b/src/blackmisc/network/webdataservicesprovider.cpp index d8818952f..f2df65492 100644 --- a/src/blackmisc/network/webdataservicesprovider.cpp +++ b/src/blackmisc/network/webdataservicesprovider.cpp @@ -261,11 +261,11 @@ namespace BlackMisc return this->m_webDataReaderProvider->updateWithVatsimDataFileData(aircraftToBeUdpated); } - CStatusMessageList CWebDataServicesAware::asyncWriteModel(const CAircraftModel &model) const + CStatusMessageList CWebDataServicesAware::asyncPublishModels(const CAircraftModelList &models) const { Q_ASSERT_X(this->m_webDataReaderProvider, Q_FUNC_INFO, "Missing provider"); if (!hasProvider()) { return CStatusMessageList(); } - return this->m_webDataReaderProvider->asyncWriteModel(model); + return this->m_webDataReaderProvider->asyncPublishModels(models); } void CWebDataServicesAware::setProvider(IWebDataServicesProvider *webDataReaderProvider) @@ -296,6 +296,16 @@ namespace BlackMisc } } + void CWebDataServicesAware::connectDataPublishSignal(QObject *receiver, std::function dataPublished) + { + Q_ASSERT_X(this->m_webDataReaderProvider, Q_FUNC_INFO, "Missing provider"); + if (!hasProvider()) { return; } + if (receiver) + { + this->m_swiftConnections.append(this->m_webDataReaderProvider->connectDataPublishSignal(receiver, dataPublished)); + } + } + CEntityFlags::Entity CWebDataServicesAware::triggerRead(CEntityFlags::Entity whatToRead) { Q_ASSERT_X(this->m_webDataReaderProvider, Q_FUNC_INFO, "Missing provider"); diff --git a/src/blackmisc/network/webdataservicesprovider.h b/src/blackmisc/network/webdataservicesprovider.h index 171cccca4..996d09031 100644 --- a/src/blackmisc/network/webdataservicesprovider.h +++ b/src/blackmisc/network/webdataservicesprovider.h @@ -184,10 +184,10 @@ namespace BlackMisc //! \threadsafe virtual int getMetarsCount() const = 0; - //! Write directly to database - virtual BlackMisc::CStatusMessageList asyncWriteModel(const BlackMisc::Simulation::CAircraftModel &model) const = 0; + //! Publish models to database + virtual BlackMisc::CStatusMessageList asyncPublishModels(const BlackMisc::Simulation::CAircraftModelList &models) const = 0; - //! Relay signals for swift data + //! Relay signals for read swift data //! Connect signals to slot receiver. As the interface is no QObject, slots can not be connected directly. //! In order to disconnect a list of connections is provided, which have to be disconnected manually. //! \note receiver is required for connection type @@ -195,16 +195,24 @@ namespace BlackMisc QObject *receiver, std::function dataRead) = 0; + //! Relay signals for published swift data + //! Connect signals to slot receiver. As the interface is no QObject, slots can not be connected directly. + //! In order to disconnect a list of connections is provided, which have to be disconnected manually. + //! \note receiver is required for connection type + virtual QList connectDataPublishSignal( + QObject *receiver, + std::function dataPublished) = 0; + //! Trigger read of new data virtual BlackMisc::Network::CEntityFlags::Entity triggerRead(BlackMisc::Network::CEntityFlags::Entity whatToRead) = 0; //! Can connect to swift DB? virtual bool canConnectSwiftDb() const = 0; - //! Write data to disk + //! Write data to disk (mainly for testing scenarios) virtual bool writeDbDataToDisk(const QString &dir) const = 0; - //! Load DB data from disk + //! Load DB data from disk (mainly for testing scenarios) virtual bool readDbDataFromDisk(const QString &dir, bool inBackground) = 0; }; @@ -322,8 +330,8 @@ namespace BlackMisc //! \copydoc IWebDataServicesProvider::updateWithVatsimDataFileData void updateWithVatsimDataFileData(BlackMisc::Simulation::CSimulatedAircraft &aircraftToBeUdpated) const; - //! \copydoc IWebDataServicesProvider::asyncWriteModel - BlackMisc::CStatusMessageList asyncWriteModel(const BlackMisc::Simulation::CAircraftModel &model) const; + //! \copydoc IWebDataServicesProvider::asyncPublishModels + BlackMisc::CStatusMessageList asyncPublishModels(const BlackMisc::Simulation::CAircraftModelList &models) const; //! Set the provider virtual void setProvider(IWebDataServicesProvider *webDataReaderProvider); @@ -339,6 +347,11 @@ namespace BlackMisc QObject *receiver, std::function dataRead); + //! \copydoc IWebDataServicesProvider::connectDataPublishSignal + virtual void connectDataPublishSignal( + QObject *receiver, + std::function dataPublished); + //! \copydoc IWebDataServicesProvider::triggerRead BlackMisc::Network::CEntityFlags::Entity triggerRead(BlackMisc::Network::CEntityFlags::Entity whatToRead);