mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-04-03 07:35:48 +08:00
refs #787, support for Header / shared files in database reader / web services
* split JsonDatastoreResponse in subclass HeaderResponse
* support for request newtwork request duration ("started") in application
* removed ps_setupChanged because it was unused
This commit is contained in:
@@ -31,6 +31,7 @@ using namespace BlackMisc;
|
||||
using namespace BlackMisc::Db;
|
||||
using namespace BlackMisc::Network;
|
||||
using namespace BlackCore;
|
||||
using namespace BlackCore::Data;
|
||||
|
||||
namespace BlackCore
|
||||
{
|
||||
@@ -130,41 +131,74 @@ namespace BlackCore
|
||||
|
||||
CDatabaseReader::JsonDatastoreResponse CDatabaseReader::transformReplyIntoDatastoreResponse(QNetworkReply *nwReply) const
|
||||
{
|
||||
this->threadAssertCheck();
|
||||
|
||||
JsonDatastoreResponse datastoreResponse;
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
nwReply->abort();
|
||||
datastoreResponse.setMessage(CStatusMessage(this, CStatusMessage::SeverityError, "Terminated data parsing process"));
|
||||
return datastoreResponse; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
if (nwReply->error() == QNetworkReply::NoError)
|
||||
const bool ok = this->setHeaderInfoPart(datastoreResponse, nwReply);
|
||||
if (ok)
|
||||
{
|
||||
const QString dataFileData = nwReply->readAll().trimmed();
|
||||
nwReply->close(); // close asap
|
||||
if (dataFileData.isEmpty())
|
||||
{
|
||||
datastoreResponse.setMessage(CStatusMessage(this, CStatusMessage::SeverityError, "Empty response, no data"));
|
||||
datastoreResponse.m_updated = QDateTime::currentDateTimeUtc();
|
||||
datastoreResponse.setUpdateTimestamp(QDateTime::currentDateTimeUtc());
|
||||
}
|
||||
else
|
||||
{
|
||||
datastoreResponse = CDatabaseReader::stringToDatastoreResponse(dataFileData);
|
||||
CDatabaseReader::stringToDatastoreResponse(dataFileData, datastoreResponse);
|
||||
}
|
||||
}
|
||||
return datastoreResponse;
|
||||
}
|
||||
|
||||
CDatabaseReader::HeaderResponse CDatabaseReader::transformReplyIntoHeaderResponse(QNetworkReply *nwReply) const
|
||||
{
|
||||
HeaderResponse headerResponse;
|
||||
const bool success = this->setHeaderInfoPart(headerResponse, nwReply);
|
||||
Q_UNUSED(success);
|
||||
nwReply->close();
|
||||
return headerResponse;
|
||||
}
|
||||
|
||||
bool CDatabaseReader::setHeaderInfoPart(CDatabaseReader::HeaderResponse &headerResponse, QNetworkReply *nwReply) const
|
||||
{
|
||||
Q_ASSERT_X(nwReply, Q_FUNC_INFO, "Missing reply");
|
||||
this->threadAssertCheck();
|
||||
if (this->isAbandoned())
|
||||
{
|
||||
nwReply->abort();
|
||||
headerResponse.setMessage(CStatusMessage(this, CStatusMessage::SeverityError, "Terminated data parsing process"));
|
||||
return false; // stop, terminate straight away, ending thread
|
||||
}
|
||||
|
||||
headerResponse.setUrl(nwReply->url());
|
||||
const QVariant started = nwReply->property("started");
|
||||
if (started.isValid() && started.canConvert<qint64>())
|
||||
{
|
||||
const qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
const qint64 start = started.value<qint64>();
|
||||
headerResponse.setLoadTimeMs(now - start);
|
||||
}
|
||||
|
||||
const QDateTime lastModified = nwReply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
|
||||
const qulonglong size = static_cast<qlonglong>(nwReply->header(QNetworkRequest::ContentLengthHeader).toULongLong());
|
||||
headerResponse.setUpdateTimestamp(lastModified);
|
||||
headerResponse.setContentLengthHeader(size);
|
||||
|
||||
if (nwReply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
// do not close because of obtaining data
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// no valid response
|
||||
const QString error(nwReply->errorString());
|
||||
const QString url(nwReply->url().toString());
|
||||
nwReply->abort();
|
||||
datastoreResponse.setMessage(CStatusMessage(this, CStatusMessage::SeverityError,
|
||||
QString("Reading data failed: " + error + " " + url)));
|
||||
headerResponse.setMessage(CStatusMessage(this, CStatusMessage::SeverityError,
|
||||
QString("Reading data failed: " + error + " " + url)));
|
||||
return false;
|
||||
}
|
||||
return datastoreResponse;
|
||||
}
|
||||
|
||||
CDatabaseReader::JsonDatastoreResponse CDatabaseReader::setStatusAndTransformReplyIntoDatastoreResponse(QNetworkReply *nwReply)
|
||||
@@ -191,11 +225,50 @@ namespace BlackCore
|
||||
static const QDateTime e;
|
||||
const CDbInfoList il(infoList());
|
||||
if (il.isEmpty() || entity == CEntityFlags::NoEntity) { return e; }
|
||||
CDbInfo info = il.findFirstByEntityOrDefault(entity);
|
||||
|
||||
// for some entities there can be more than one entry because of the
|
||||
// raw tables (see DB view last updates)
|
||||
const CDbInfo info = il.findFirstByEntityOrDefault(entity);
|
||||
if (!info.isValid()) { return e; }
|
||||
return info.getUtcTimestamp();
|
||||
}
|
||||
|
||||
QDateTime CDatabaseReader::getSharedFileTimestamp(CEntityFlags::Entity entity) const
|
||||
{
|
||||
const int key = static_cast<int>(entity);
|
||||
if (m_sharedFileResponses.contains(key))
|
||||
{
|
||||
return this->m_sharedFileResponses[key].getUpdateTimestamp();
|
||||
}
|
||||
else
|
||||
{
|
||||
return QDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
bool CDatabaseReader::requestHeadersOfSharedFiles(const CEntityFlags::Entity &entities)
|
||||
{
|
||||
CEntityFlags::Entity allEntities(entities);
|
||||
CEntityFlags::Entity currentEntity = CEntityFlags::iterateDbEntities(allEntities);
|
||||
CUrl urlDbData = CGlobalSetup::buildDbDataDirectory(getWorkingSharedUrl());
|
||||
|
||||
int c = 0;
|
||||
while (currentEntity != CEntityFlags::NoEntity)
|
||||
{
|
||||
const QString fileName = CDbInfo::entityToSharedFileName(currentEntity);
|
||||
Q_ASSERT_X(!fileName.isEmpty(), Q_FUNC_INFO, "No file name for entity");
|
||||
CUrl url = urlDbData;
|
||||
url.appendPath(fileName);
|
||||
|
||||
const QString entityString = CEntityFlags::flagToString(currentEntity);
|
||||
CLogMessage(this).info("Triggered read of header for shared file of %1") << entityString;
|
||||
const QNetworkReply *reply = sApp->headerFromNetwork(url, { this, &CDatabaseReader::receivedSharedFileHeader });
|
||||
if (reply) { c++; }
|
||||
currentEntity = CEntityFlags::iterateDbEntities(allEntities);
|
||||
}
|
||||
return c > 0;
|
||||
}
|
||||
|
||||
int CDatabaseReader::getCountFromInfoObjects(CEntityFlags::Entity entity) const
|
||||
{
|
||||
static const QDateTime e;
|
||||
@@ -240,6 +313,22 @@ namespace BlackCore
|
||||
return oldS != currentS;
|
||||
}
|
||||
|
||||
void CDatabaseReader::receivedSharedFileHeader(QNetworkReply *nwReplyPtr)
|
||||
{
|
||||
// wrap pointer, make sure any exit cleans up reply
|
||||
// required to use delete later as object is created in a different thread
|
||||
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> nwReply(nwReplyPtr);
|
||||
if (this->isAbandoned()) { return; }
|
||||
|
||||
const HeaderResponse headerResponse = this->transformReplyIntoHeaderResponse(nwReply.data());
|
||||
const QString fileName = nwReply->url().fileName();
|
||||
const CEntityFlags::Entity entity = CEntityFlags::singleEntityByName(fileName);
|
||||
const int key = static_cast<int>(entity);
|
||||
this->m_sharedFileResponses[key] = headerResponse;
|
||||
|
||||
emit this->sharedFileHeaderRead(entity, fileName, !headerResponse.hasWarningOrAboveMessage());
|
||||
}
|
||||
|
||||
bool CDatabaseReader::hasReceivedOkReply() const
|
||||
{
|
||||
QReadLocker rl(&this->m_statusLock);
|
||||
@@ -324,14 +413,13 @@ namespace BlackCore
|
||||
return CNetworkUtils::canConnect(url);
|
||||
}
|
||||
|
||||
CDatabaseReader::JsonDatastoreResponse CDatabaseReader::stringToDatastoreResponse(const QString &jsonContent)
|
||||
void CDatabaseReader::stringToDatastoreResponse(const QString &jsonContent, JsonDatastoreResponse &datastoreResponse)
|
||||
{
|
||||
CDatabaseReader::JsonDatastoreResponse datastoreResponse;
|
||||
if (jsonContent.isEmpty())
|
||||
{
|
||||
datastoreResponse.setMessage(CStatusMessage(getLogCategories(), CStatusMessage::SeverityError, "Empty string, no data"));
|
||||
datastoreResponse.m_updated = QDateTime::currentDateTimeUtc();
|
||||
return datastoreResponse;
|
||||
datastoreResponse.setUpdateTimestamp(QDateTime::currentDateTimeUtc());
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonDocument jsonResponse = QJsonDocument::fromJson(jsonContent.toUtf8());
|
||||
@@ -339,17 +427,16 @@ namespace BlackCore
|
||||
{
|
||||
// directly an array, no further info
|
||||
datastoreResponse.setJsonArray(jsonResponse.array());
|
||||
datastoreResponse.m_updated = QDateTime::currentDateTimeUtc();
|
||||
datastoreResponse.setUpdateTimestamp(QDateTime::currentDateTimeUtc());
|
||||
}
|
||||
else
|
||||
{
|
||||
const QJsonObject responseObject(jsonResponse.object());
|
||||
datastoreResponse.setJsonArray(responseObject["data"].toArray());
|
||||
const QString ts(responseObject["latest"].toString());
|
||||
datastoreResponse.m_updated = ts.isEmpty() ? QDateTime::currentDateTimeUtc() : CDatastoreUtility::parseTimestamp(ts);
|
||||
datastoreResponse.m_restricted = responseObject["restricted"].toBool();
|
||||
datastoreResponse.setUpdateTimestamp(ts.isEmpty() ? QDateTime::currentDateTimeUtc() : CDatastoreUtility::parseTimestamp(ts));
|
||||
datastoreResponse.setRestricted(responseObject["restricted"].toBool());
|
||||
}
|
||||
return datastoreResponse;
|
||||
}
|
||||
|
||||
void CDatabaseReader::JsonDatastoreResponse::setJsonArray(const QJsonArray &value)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QJsonArray>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QReadWriteLock>
|
||||
#include <QString>
|
||||
@@ -44,21 +45,17 @@ namespace BlackCore
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//! Response from our database
|
||||
struct JsonDatastoreResponse
|
||||
//! Header response part
|
||||
struct HeaderResponse
|
||||
{
|
||||
QJsonArray m_jsonArray; //!< JSON array data
|
||||
QDateTime m_updated; //!< when was the latest updated?
|
||||
int m_arraySize = -1; //!< size of array, if applicable (copied to member for debugging purposes)
|
||||
bool m_restricted = false; //!< restricted reponse, only changed data
|
||||
BlackMisc::CStatusMessage m_message; //!< last error or warning
|
||||
|
||||
//! Any data?
|
||||
bool isEmpty() const { return m_jsonArray.isEmpty(); }
|
||||
|
||||
//! Number of elements
|
||||
int size() const { return m_jsonArray.size(); }
|
||||
private:
|
||||
QDateTime m_updated; //!< when was the latest update?
|
||||
BlackMisc::CStatusMessage m_message; //!< last error or warning
|
||||
BlackMisc::Network::CUrl m_url; //!< loaded url
|
||||
qulonglong m_contentLengthHeader = 0; //!< content length
|
||||
qint64 m_loadTimeMs = -1; //!< how long did it take to load
|
||||
|
||||
public:
|
||||
//! Any timestamp?
|
||||
bool hasTimestamp() const { return m_updated.isValid(); }
|
||||
|
||||
@@ -68,8 +65,17 @@ namespace BlackCore
|
||||
//! Is response newer?
|
||||
bool isNewer(qint64 mSecsSinceEpoch) const { return m_updated.toMSecsSinceEpoch() > mSecsSinceEpoch; }
|
||||
|
||||
//! Incremental data
|
||||
bool isRestricted() const { return m_restricted; }
|
||||
//! Get the update timestamp
|
||||
const QDateTime &getUpdateTimestamp() const { return m_updated; }
|
||||
|
||||
//! Set update timestamp, default normally "last-modified"
|
||||
void setUpdateTimestamp(const QDateTime &updated) { m_updated = updated; }
|
||||
|
||||
//! Header content length
|
||||
qulonglong getContentLengthHeader() const { return m_contentLengthHeader; }
|
||||
|
||||
//! Set the content length
|
||||
void setContentLengthHeader(qulonglong size) { m_contentLengthHeader = size; }
|
||||
|
||||
//! Error message?
|
||||
bool hasErrorMessage() const { return m_message.getSeverity() == BlackMisc::CStatusMessage::SeverityError; }
|
||||
@@ -83,6 +89,40 @@ namespace BlackCore
|
||||
//! Set the error/warning message
|
||||
void setMessage(const BlackMisc::CStatusMessage &lastErrorOrWarning) { m_message = lastErrorOrWarning; }
|
||||
|
||||
//! URL loaded
|
||||
const BlackMisc::Network::CUrl &getUrl() const { return m_url; }
|
||||
|
||||
//! Set the loaded URL
|
||||
void setUrl(const BlackMisc::Network::CUrl &url) { m_url = url; }
|
||||
|
||||
//! Load time in ms (from request to response)
|
||||
qint64 getLoadTimeMs() const { return m_loadTimeMs; }
|
||||
|
||||
//! Set the load time (delta start -> response received)
|
||||
void setLoadTimeMs(qint64 deltaTime) { m_loadTimeMs = deltaTime; }
|
||||
};
|
||||
|
||||
//! Response from our database (depneding on JSON DB backend generates)
|
||||
struct JsonDatastoreResponse : public HeaderResponse
|
||||
{
|
||||
private:
|
||||
QJsonArray m_jsonArray; //!< JSON array data
|
||||
int m_arraySize = -1; //!< size of array, if applicable (copied to member for debugging purposes)
|
||||
bool m_restricted = false; //!< restricted reponse, only changed data
|
||||
|
||||
public:
|
||||
//! Any data?
|
||||
bool isEmpty() const { return m_jsonArray.isEmpty(); }
|
||||
|
||||
//! Number of elements
|
||||
int size() const { return m_jsonArray.size(); }
|
||||
|
||||
//! Incremental data, restricted by query?
|
||||
bool isRestricted() const { return m_restricted; }
|
||||
|
||||
//! Incremental data, restricted by query
|
||||
void setRestricted(bool restricted) { m_restricted = restricted; }
|
||||
|
||||
//! Get the JSON array
|
||||
QJsonArray getJsonArray() const { return m_jsonArray; }
|
||||
|
||||
@@ -125,6 +165,12 @@ namespace BlackCore
|
||||
//! \sa BlackCore::Db::CInfoDataReader
|
||||
QDateTime getLatestEntityTimestampFromInfoObjects(BlackMisc::Network::CEntityFlags::Entity entity) const;
|
||||
|
||||
//! Timestamp of shared file for entiry
|
||||
QDateTime getSharedFileTimestamp(BlackMisc::Network::CEntityFlags::Entity entity) const;
|
||||
|
||||
//! Request header of shared file
|
||||
bool requestHeadersOfSharedFiles(const BlackMisc::Network::CEntityFlags::Entity &entities);
|
||||
|
||||
//! Count from info objects
|
||||
//! \sa BlackCore::Db::CInfoDataReader
|
||||
int getCountFromInfoObjects(BlackMisc::Network::CEntityFlags::Entity entity) const;
|
||||
@@ -141,18 +187,23 @@ namespace BlackCore
|
||||
//! Name of parameter for latest id
|
||||
static const QString ¶meterLatestId();
|
||||
|
||||
//! sift DB server reachable?
|
||||
//! swift DB server reachable?
|
||||
static bool canPingSwiftServer();
|
||||
|
||||
//! Transform JSON data to response struct
|
||||
static JsonDatastoreResponse stringToDatastoreResponse(const QString &jsonContent);
|
||||
//! Transform JSON data to response struct data
|
||||
//! \private used also for samples
|
||||
static void stringToDatastoreResponse(const QString &jsonContent, CDatabaseReader::JsonDatastoreResponse &datastoreResponse);
|
||||
|
||||
signals:
|
||||
//! Combined read signal
|
||||
void dataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number);
|
||||
|
||||
//! Header of shared file read
|
||||
void sharedFileHeaderRead(BlackMisc::Network::CEntityFlags::Entity entity, const QString &fileName, bool success);
|
||||
|
||||
protected:
|
||||
CDatabaseReaderConfigList m_config; //!< DB reder configuration
|
||||
QMap<int, HeaderResponse> m_sharedFileResponses; //!< file responses of the shared files
|
||||
QString m_statusMessage; //!< Returned status message from watchdog
|
||||
QNetworkReply::NetworkError m_1stReplyStatus = QNetworkReply::UnknownServerError; //!< Successful connection?
|
||||
bool m_1stReplyReceived = false; //!< Successful connection? Does not mean data / authorizations are correct
|
||||
@@ -180,7 +231,7 @@ namespace BlackCore
|
||||
//! Obtain a working shared URL
|
||||
static BlackMisc::Network::CUrl getWorkingSharedUrl();
|
||||
|
||||
//! \name Cache access
|
||||
//! \name Cache access
|
||||
//! @{
|
||||
//! Synchronize caches for given entities
|
||||
virtual void synchronizeCaches(BlackMisc::Network::CEntityFlags::Entity entities) = 0;
|
||||
@@ -202,9 +253,18 @@ namespace BlackCore
|
||||
//! @}
|
||||
|
||||
private:
|
||||
//! Received a reply of a header for a shared file
|
||||
void receivedSharedFileHeader(QNetworkReply *nwReplyPtr);
|
||||
|
||||
//! Check if terminated or error, otherwise split into array of objects
|
||||
JsonDatastoreResponse transformReplyIntoDatastoreResponse(QNetworkReply *nwReply) const;
|
||||
|
||||
//! Check if terminated or error, otherwise set header information
|
||||
HeaderResponse transformReplyIntoHeaderResponse(QNetworkReply *nwReply) const;
|
||||
|
||||
//! Set the header part
|
||||
bool setHeaderInfoPart(HeaderResponse &headerResponse, QNetworkReply *nwReply) const;
|
||||
|
||||
//! Feedback about connection status
|
||||
//! \threadsafe
|
||||
void setReplyStatus(QNetworkReply::NetworkError status, const QString &message = "");
|
||||
|
||||
Reference in New Issue
Block a user