diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | doc/qtenginiooverview.qdocconf | 1 | ||||
-rw-r--r-- | src/enginio_client/doc/qtenginio.qdocconf | 1 | ||||
-rw-r--r-- | src/enginio_client/enginio_client.pro | 2 | ||||
-rw-r--r-- | src/enginio_client/enginiobasemodel_p.h | 27 | ||||
-rw-r--r-- | src/enginio_client/enginiomodel.cpp | 22 | ||||
-rw-r--r-- | src/enginio_client/enginiomodel.h | 2 | ||||
-rw-r--r-- | src/enginio_plugin/doc/qtenginioqml.qdocconf | 3 | ||||
-rw-r--r-- | src/enginio_plugin/enginio_plugin.pro | 4 | ||||
-rw-r--r-- | src/enginio_plugin/enginioqmlmodel.cpp | 6 | ||||
-rw-r--r-- | src/enginio_plugin/enginioqmlmodel_p.h | 1 | ||||
-rw-r--r-- | src/src.pro | 4 | ||||
-rw-r--r-- | tests/auto/auto.pro | 5 | ||||
-rw-r--r-- | tests/auto/enginiomodel/tst_enginiomodel.cpp | 130 | ||||
-rw-r--r-- | tests/auto/files/files.pro | 1 | ||||
-rw-r--r-- | tests/auto/qmltests/tst_model.qml | 31 |
16 files changed, 217 insertions, 25 deletions
diff --git a/.qmake.conf b/.qmake.conf index 4a96a7b..65bfdeb 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,4 +2,4 @@ load(qt_build_config) CONFIG += warning_clean CONFIG += qt_example_installs -MODULE_VERSION = 1.0.6 +MODULE_VERSION = 1.1.0 diff --git a/doc/qtenginiooverview.qdocconf b/doc/qtenginiooverview.qdocconf index 638fa00..abc7a99 100644 --- a/doc/qtenginiooverview.qdocconf +++ b/doc/qtenginiooverview.qdocconf @@ -4,7 +4,6 @@ include(enginio_global.qdocconf) project = QtEnginioOverview description = Enginio Client Library -url = http://qt-project.org/doc/qt-$QT_VER version = $QT_VERSION sourcedirs += . diff --git a/src/enginio_client/doc/qtenginio.qdocconf b/src/enginio_client/doc/qtenginio.qdocconf index b4f7f2b..ca1385c 100644 --- a/src/enginio_client/doc/qtenginio.qdocconf +++ b/src/enginio_client/doc/qtenginio.qdocconf @@ -4,7 +4,6 @@ include(../../../doc/enginio_global.qdocconf) project = QtEnginio description = Client library for Enginio -url = http://qt-project.org/doc/qt-$QT_VER version = $QT_VERSION sourcedirs += .. \ diff --git a/src/enginio_client/enginio_client.pro b/src/enginio_client/enginio_client.pro index ef3f868..f3a158d 100644 --- a/src/enginio_client/enginio_client.pro +++ b/src/enginio_client/enginio_client.pro @@ -1,5 +1,5 @@ TARGET = Enginio -QT += core-private network +QT = core-private network DEFINES += ENGINIOCLIENT_LIBRARY MODULE = enginio diff --git a/src/enginio_client/enginiobasemodel_p.h b/src/enginio_client/enginiobasemodel_p.h index bd6bd2f..cf0fd49 100644 --- a/src/enginio_client/enginiobasemodel_p.h +++ b/src/enginio_client/enginiobasemodel_p.h @@ -418,7 +418,7 @@ public: _notifications.disable(); } - void receivedNotification(QJsonObject data); + void receivedNotification(const QJsonObject &data); void receivedRemoveNotification(const QJsonObject &object, int rowHint = NoHintRow); void receivedUpdateNotification(const QJsonObject &object, const QString &idHint = QString(), int row = NoHintRow); void receivedCreateNotification(const QJsonObject &object); @@ -586,21 +586,27 @@ public: filter.insert(EnginioString::data, objectType); _notifications.connectToBackend(this, _enginio, filter); - // send full query - QJsonObject query = queryAsJson(); - ObjectAdaptor<QJsonObject> aQuery(query); - QNetworkReply *nreply = _enginio->query(aQuery, static_cast<Enginio::Operation>(_operation)); - EnginioReplyState *ereply = _enginio->createReply(nreply); - if (_canFetchMore) - _latestRequestedOffset = query[EnginioString::limit].toDouble(); - FinishedFullQueryRequest finshedRequest = { this, ereply }; - QObject::connect(ereply, &EnginioReplyState::dataChanged, _replyConnectionConntext, finshedRequest); + EnginioReplyState *ereply = reload(); QObject::connect(ereply, &EnginioReplyState::dataChanged, ereply, &EnginioReplyState::deleteLater); } else { fullQueryReset(QJsonArray()); } } + EnginioReplyState *reload() + { + // send full query + QJsonObject query = queryAsJson(); + ObjectAdaptor<QJsonObject> aQuery(query); + QNetworkReply *nreply = _enginio->query(aQuery, static_cast<Enginio::Operation>(_operation)); + EnginioReplyState *ereply = _enginio->createReply(nreply); + if (_canFetchMore) + _latestRequestedOffset = query[EnginioString::limit].toDouble(); + FinishedFullQueryRequest finshedRequest = { this, ereply }; + QObject::connect(ereply, &EnginioReplyState::dataChanged, _replyConnectionConntext, finshedRequest); + return ereply; + } + void finishedIncrementalUpdateRequest(const EnginioReplyState *reply, const QJsonObject &query) { Q_ASSERT(_canFetchMore); @@ -993,6 +999,7 @@ struct EnginioModelPrivateT : public EnginioBaseModelPrivate Reply *append(const QJsonObject &value) { return static_cast<Reply*>(Base::append(value)); } Reply *remove(int row) { return static_cast<Reply*>(Base::remove(row)); } Reply *setValue(int row, const QString &role, const QVariant &value) { return static_cast<Reply*>(Base::setValue(row, role, value)); } + Reply *reload() { return static_cast<Reply*>(Base::reload()); } bool queryIsEmpty() const Q_DECL_OVERRIDE { diff --git a/src/enginio_client/enginiomodel.cpp b/src/enginio_client/enginiomodel.cpp index 3d5f93d..722a2fc 100644 --- a/src/enginio_client/enginiomodel.cpp +++ b/src/enginio_client/enginiomodel.cpp @@ -161,7 +161,7 @@ EnginioBaseModelPrivate::~EnginioBaseModelPrivate() delete _replyConnectionConntext; } -void EnginioBaseModelPrivate::receivedNotification(QJsonObject data) +void EnginioBaseModelPrivate::receivedNotification(const QJsonObject &data) { const QJsonObject origin = data[EnginioString::origin].toObject(); const QString requestId = origin[EnginioString::apiRequestId].toString(); @@ -480,6 +480,25 @@ void EnginioModel::setOperation(Enginio::Operation operation) } /*! + Reload the model data from the server. + This is similar to reset and will emit \l modelAboutToBeReset() and \l modelReset(). + This function invalidated the internal state of the model, reloads it from the backend + and resets all views. + + \note when using this function while other requests to the server are made the result + is undefined. For example when calling append() and then reset() before append finished, + the model may or may not contain the result of the append operation. + + \return reply from backend + \since 1.1 +*/ +EnginioReply *EnginioModel::reload() +{ + Q_D(EnginioModel); + return d->reload(); +} + +/*! \include model-append.qdocinc \sa EnginioClient::create() */ @@ -543,7 +562,6 @@ EnginioReply *EnginioModel::setData(int row, const QVariant &value, const QStrin return d->setValue(row, role, value); } - Qt::ItemFlags EnginioBaseModel::flags(const QModelIndex &index) const { return QAbstractListModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; diff --git a/src/enginio_client/enginiomodel.h b/src/enginio_client/enginiomodel.h index 81c7eeb..f3cb0ca 100644 --- a/src/enginio_client/enginiomodel.h +++ b/src/enginio_client/enginiomodel.h @@ -82,6 +82,8 @@ public: Q_INVOKABLE EnginioReply *setData(int row, const QVariant &value, const QString &role); using EnginioBaseModel::setData; + Q_INVOKABLE EnginioReply *reload(); + Q_SIGNALS: void queryChanged(const QJsonObject &query); void clientChanged(EnginioClient *client); diff --git a/src/enginio_plugin/doc/qtenginioqml.qdocconf b/src/enginio_plugin/doc/qtenginioqml.qdocconf index bd7b333..7546037 100644 --- a/src/enginio_plugin/doc/qtenginioqml.qdocconf +++ b/src/enginio_plugin/doc/qtenginioqml.qdocconf @@ -4,7 +4,6 @@ include(../../../doc/enginio_global.qdocconf) project = QtEnginioQml description = Client library for Enginio from QML -url = http://qt-project.org/doc/qt-$QT_VER version = $QT_VERSION sourcedirs += .. \ @@ -46,7 +45,7 @@ qhp.QtEnginioQml.subprojects = qmltypes examples qhp.QtEnginioQml.subprojects.qmltypes.title = QML Types qhp.QtEnginioQml.subprojects.qmltypes.indexTitle = Enginio QML Types and Examples -qhp.QtEnginioQml.subprojects.qmltypes.selectors = fake:qmlclass +qhp.QtEnginioQml.subprojects.qmltypes.selectors = qmlclass qhp.QtEnginioQml.subprojects.qmltypes.sortPages = true qhp.QtEnginioQml.subprojects.examples.title = Examples diff --git a/src/enginio_plugin/enginio_plugin.pro b/src/enginio_plugin/enginio_plugin.pro index eef4dfb..89fa978 100644 --- a/src/enginio_plugin/enginio_plugin.pro +++ b/src/enginio_plugin/enginio_plugin.pro @@ -1,8 +1,8 @@ -requires(qtHaveModule(quick)) +requires(qtHaveModule(qml)) TARGETPATH = Enginio -QT += qml quick enginio enginio-private core-private +QT = qml enginio enginio-private core-private QMAKE_DOCS = $$PWD/doc/qtenginioqml.qdocconf OTHER_FILES += \ diff --git a/src/enginio_plugin/enginioqmlmodel.cpp b/src/enginio_plugin/enginioqmlmodel.cpp index 43d0198..05410cf 100644 --- a/src/enginio_plugin/enginioqmlmodel.cpp +++ b/src/enginio_plugin/enginioqmlmodel.cpp @@ -255,6 +255,12 @@ EnginioQmlReply *EnginioQmlModel::setProperty(int row, const QString &role, cons return d->setValue(row, role, value); } +EnginioQmlReply *EnginioQmlModel::reload() +{ + Q_D(EnginioQmlModel); + return d->reload(); +} + EnginioQmlClient *EnginioQmlModel::client() const { Q_D(const EnginioQmlModel); diff --git a/src/enginio_plugin/enginioqmlmodel_p.h b/src/enginio_plugin/enginioqmlmodel_p.h index 9b52308..c78d9af 100644 --- a/src/enginio_plugin/enginioqmlmodel_p.h +++ b/src/enginio_plugin/enginioqmlmodel_p.h @@ -79,6 +79,7 @@ public: Q_INVOKABLE EnginioQmlReply *append(const QJSValue &value); Q_INVOKABLE EnginioQmlReply *remove(int row); Q_INVOKABLE EnginioQmlReply *setProperty(int row, const QString &role, const QVariant &value); + Q_INVOKABLE EnginioQmlReply *reload(); Q_SIGNALS: void queryChanged(const QJSValue &query); diff --git a/src/src.pro b/src/src.pro index c3f8a69..e20d0b4 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,10 +1,8 @@ -requires(qtHaveModule(network)) - TEMPLATE = subdirs SUBDIRS += enginio_client -qtHaveModule(quick) { +qtHaveModule(qml) { SUBDIRS += enginio_plugin enginio_plugin.depends = enginio_client } diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 817c5f6..ebe3dcd 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -3,10 +3,13 @@ TEMPLATE = subdirs SUBDIRS += \ # cmake \ enginioclient \ - files \ notifications \ identity \ +qtHaveModule(gui) { + SUBDIRS += files +} + qtHaveModule(quick) { SUBDIRS += qmltests } diff --git a/tests/auto/enginiomodel/tst_enginiomodel.cpp b/tests/auto/enginiomodel/tst_enginiomodel.cpp index 5edc707..744ce12 100644 --- a/tests/auto/enginiomodel/tst_enginiomodel.cpp +++ b/tests/auto/enginiomodel/tst_enginiomodel.cpp @@ -98,6 +98,8 @@ private slots: void deleteModelDurringRequests(); void updatingRoles(); void setData(); + void reload(); + private: template<class T> void externallyRemovedImpl(); @@ -119,6 +121,23 @@ void tst_EnginioModel::initTestCase() // The test operates on user data. EnginioTests::prepareTestUsersAndUserGroups(_backendId); EnginioTests::prepareTestObjectType(_backendName); + + // Object types for the reload test + QJsonObject reload1; + reload1["name"] = QStringLiteral("reload1"); + QJsonObject title; + title["name"] = QStringLiteral("title"); + title["type"] = QStringLiteral("string"); + title["indexed"] = false; + QJsonArray properties; + properties.append(title); + reload1["properties"] = properties; + QVERIFY(_backendManager.createObjectType(_backendName, EnginioTests::TESTAPP_ENV, reload1)); + + QJsonObject reload2; + reload2["name"] = QStringLiteral("reload2"); + reload2["properties"] = properties; + QVERIFY(_backendManager.createObjectType(_backendName, EnginioTests::TESTAPP_ENV, reload2)); } void tst_EnginioModel::cleanupTestCase() @@ -1557,5 +1576,116 @@ void tst_EnginioModel::deleteReply() QTRY_COMPARE(counter, replies.count()); } +QJsonObject createTestObject(const QString &name, const QString &type) +{ + QJsonObject obj; + obj.insert("title", name); + obj.insert("objectType", type); + return obj; +} + +void tst_EnginioModel::reload() +{ + QString objectType = "objects.reload1"; + + EnginioClient client; + client.setBackendId(_backendId); + client.setServiceUrl(EnginioTests::TESTAPP_URL); + + QJsonObject query; + query.insert("objectType", objectType); + + EnginioModel model; + model.disableNotifications(); + model.setQuery(query); + model.setClient(&client); + + QCOMPARE(model.rowCount(), 0); + + // create an object, since notification are disabled the model cannot know about it + EnginioReply *r1(client.create(createTestObject(QString::fromLatin1("o1"), objectType))); + QTRY_VERIFY(r1->isFinished()); + CHECK_NO_ERROR(r1); + QCOMPARE(model.rowCount(), 0); + + // reload and verify that the object appears + EnginioReply *reload(model.reload()); + QTRY_VERIFY(reload->isFinished()); + CHECK_NO_ERROR(reload); + QCOMPARE(model.rowCount(), 1); + + // this test mostly checks that we don't crash + // when calling reload while other operations are on-going it is undefined + // if the result contains the data of them or not + + // create object and reload before that is finished + EnginioReply *r2(client.create(createTestObject(QString::fromLatin1("o2"), objectType))); + EnginioReply *reload2 = model.reload(); + QVERIFY(reload2); + QTRY_VERIFY(r2->isFinished()); + CHECK_NO_ERROR(r2); + QTRY_VERIFY(reload2->isFinished()); + CHECK_NO_ERROR(reload2); + + // create object bug delay it's response until reload is done + EnginioReply *r3(client.create(createTestObject(QString::fromLatin1("o3"), objectType))); + QVERIFY(r3); + r3->setDelayFinishedSignal(true); + EnginioReply *reload3(model.reload()); + QVERIFY(reload3); + QTRY_VERIFY(reload3->isFinished()); + r3->setDelayFinishedSignal(false); + QTRY_VERIFY(r3->isFinished()); + + // make sure we are in a defined state again + reload = model.reload(); + QTRY_VERIFY(reload->isFinished()); + QCOMPARE(model.rowCount(), 3); + + EnginioReply *r4(model.append(createTestObject(QString::fromLatin1("o4"), objectType))); + EnginioReply *reload4(model.reload()); + EnginioReply *r5(model.append(createTestObject(QString::fromLatin1("o5"), objectType))); + EnginioReply *reload5(model.reload()); + QTRY_VERIFY(r4->isFinished() && reload4->isFinished() && r5->isFinished() && reload5->isFinished()); + + reload = model.reload(); + QTRY_VERIFY(reload->isFinished()); + QCOMPARE(model.rowCount(), 5); + + // randomly reordered append and reset calls + EnginioReply *r6(model.append(createTestObject(QString::fromLatin1("o6"), objectType))); + r6->setDelayFinishedSignal(true); + EnginioReply *reload6(model.reload()); + reload6->setDelayFinishedSignal(true); + EnginioReply *r7(model.append(createTestObject(QString::fromLatin1("o7"), objectType))); + r7->setDelayFinishedSignal(true); + EnginioReply *reload7(model.reload()); + EnginioReply *c7(model.setData(6, QString::fromLatin1("object7"), QString::fromLatin1("title"))); + EnginioReply *c5(model.setData(4, QString::fromLatin1("object5"), QString::fromLatin1("title"))); + QTRY_VERIFY(reload7->isFinished()); + r6->setDelayFinishedSignal(false); + r7->setDelayFinishedSignal(false); + reload6->setDelayFinishedSignal(false); + QTRY_VERIFY(c5->isFinished() && c7->isFinished() && r6->isFinished() && r7->isFinished() && reload6->isFinished()); + + reload = model.reload(); + QTRY_VERIFY(reload->isFinished()); + QCOMPARE(model.rowCount(), 7); + + // completely try to mess it up by changing the query + // object type reload2 is empty + EnginioReply *reload8 = model.reload(); + reload8->setDelayFinishedSignal(true); + QJsonObject query2; + query2.insert("objectTypes", "objects.reload2"); + model.setQuery(query2); + + QSignalSpy spy(&model, SIGNAL(modelReset())); + QTRY_VERIFY(spy.count()); + reload8->setDelayFinishedSignal(false); + QTRY_VERIFY(reload8->isFinished()); + QCOMPARE(model.rowCount(), 0); +} + QTEST_MAIN(tst_EnginioModel) #include "tst_enginiomodel.moc" diff --git a/tests/auto/files/files.pro b/tests/auto/files/files.pro index 7907092..ae191b7 100644 --- a/tests/auto/files/files.pro +++ b/tests/auto/files/files.pro @@ -1,5 +1,4 @@ QT += testlib enginio enginio-private core-private -QT -= gui DEFINES += TEST_FILE_PATH=\\\"$$_PRO_FILE_PWD_/../common/enginio.png\\\" diff --git a/tests/auto/qmltests/tst_model.qml b/tests/auto/qmltests/tst_model.qml index e98b41a..b7535bf 100644 --- a/tests/auto/qmltests/tst_model.qml +++ b/tests/auto/qmltests/tst_model.qml @@ -511,4 +511,35 @@ Item { tryCompare(modelRowCount, "rowCountChangedCounter", initialRowCountChangedCounter + 4) } } + + TestCase { + name: "EnginioModel: reload" + + EnginioModel { + id: model + client: EnginioClient { + id: client + serviceUrl: AppConfig.backendData.serviceUrl + + property int errorCount: 0 + onError: { + ++errorCount + replyDumpDebugInfo(reply) + } + + backendId: AppConfig.backendData.id + } + query: { + "objectType": AppConfig.testObjectType, + "query": {"testCase": "EnginioModel: rowCount"} + } + } + + function test_rowCount() + { + var r = model.reload(); + tryCompare(r, "isFinished", true); + compare(client.errorCount, 0) + } + } } |