summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJędrzej Nowacki <jedrzej.nowacki@digia.com>2014-04-04 15:19:47 +0200
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-08-08 11:07:18 +0200
commit19a551e09264513766c569770539d55b6cc00d35 (patch)
tree2b8d62be4bcbd23f9c0cb87d7c7d796c543320f4
parente7c189f13d0fc0d72cbcaf1770055da252e0fb3f (diff)
downloadqtenginio-19a551e09264513766c569770539d55b6cc00d35.tar.gz
Allow update all item's properties in one EnginioModel::setData call
This change adds a new preconfigured role to EnginioModel, which can be used to update multiple properties of an item in one function call. [ChangeLog] EnginioModel is able to update multiple properties of an item in one setData function call. It can be achieved by using Enginio::JsonObjectRole. Change-Id: Ibc640d44f28d40fa349c6efdf65e3bb321a4fc79 Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
-rw-r--r--src/enginio_client/enginio.h1
-rw-r--r--src/enginio_client/enginiobasemodel_p.h22
-rw-r--r--src/enginio_client/enginiomodel.cpp30
-rw-r--r--src/enginio_client/enginiomodel.h1
-rw-r--r--src/enginio_client/enginiostring_p.h1
-rw-r--r--tests/auto/enginiomodel/tst_enginiomodel.cpp202
6 files changed, 229 insertions, 28 deletions
diff --git a/src/enginio_client/enginio.h b/src/enginio_client/enginio.h
index fb3088b..666678d 100644
--- a/src/enginio_client/enginio.h
+++ b/src/enginio_client/enginio.h
@@ -91,6 +91,7 @@ public:
UpdatedAtRole,
IdRole,
ObjectTypeRole,
+ JsonObjectRole,
CustomPropertyRole = Qt::UserRole + 10 // the first fully dynamic role
};
diff --git a/src/enginio_client/enginiobasemodel_p.h b/src/enginio_client/enginiobasemodel_p.h
index c815aad..dbcb403 100644
--- a/src/enginio_client/enginiobasemodel_p.h
+++ b/src/enginio_client/enginiobasemodel_p.h
@@ -830,11 +830,21 @@ public:
EnginioReplyState *setDataNow(const int row, const QVariant &value, int role, const QJsonObject &oldObject, const QString &id)
{
Q_ASSERT(!id.isEmpty());
- const QString roleName(_roles.value(role));
- Q_ASSERT(!roleName.isEmpty());
QJsonObject deltaObject;
QJsonObject newObject = oldObject;
- deltaObject[roleName] = newObject[roleName] = QJsonValue::fromVariant(value);
+ if (role != Enginio::JsonObjectRole) {
+ const QString roleName(_roles.value(role));
+ Q_ASSERT(!roleName.isEmpty());
+ deltaObject[roleName] = newObject[roleName] = QJsonValue::fromVariant(value);
+ } else {
+ const QJsonObject updateObject = value.toJsonObject();
+ if (updateObject.isEmpty()) {
+ QNetworkReply *nreply = new EnginioFakeReply(_enginio, EnginioClientConnectionPrivate::constructErrorMessage(EnginioString::EnginioModel_Trying_to_update_an_object_with_unknown_role));
+ return _enginio->createReply(nreply);
+ }
+ for (QJsonObject::const_iterator i = updateObject.constBegin(); i != updateObject.constEnd(); ++i)
+ deltaObject[i.key()] = i.value();
+ }
deltaObject[EnginioString::id] = id;
deltaObject[EnginioString::objectType] = newObject[EnginioString::objectType];
ObjectAdaptor<QJsonObject> aDeltaObject(deltaObject);
@@ -876,11 +886,11 @@ public:
const QJsonObject object = _data.at(row).toObject();
if (!object.isEmpty()) {
+ if (role == Qt::DisplayRole || role == Enginio::JsonObjectRole)
+ return _data.at(row);
const QString roleName = _roles.value(role);
if (!roleName.isEmpty())
return object[roleName];
- else if (role == Qt::DisplayRole)
- return _data.at(row);
}
return QVariant();
@@ -1021,7 +1031,7 @@ struct EnginioModelPrivateT : public EnginioBaseModelPrivate
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()); }
-
+ Reply *setData(const int row, const QVariant &value, int role) { return static_cast<Reply*>(Base::setData(row, value, role)); }
bool queryIsEmpty() const Q_DECL_OVERRIDE
{
return ObjectAdaptor<Data>(_query, static_cast<ClientPrivate*>(_enginio)).isEmpty();
diff --git a/src/enginio_client/enginiomodel.cpp b/src/enginio_client/enginiomodel.cpp
index 722a2fc..3ff34a3 100644
--- a/src/enginio_client/enginiomodel.cpp
+++ b/src/enginio_client/enginiomodel.cpp
@@ -404,6 +404,7 @@ EnginioBaseModel::~EnginioBaseModel()
\value ObjectTypeRole \c What is item type
\value SyncedRole \c Mark if an item is in sync with the backend
\value CustomPropertyRole \c The first role id that may be used for dynamically created roles.
+ \value JsonObjectRole \c Object like representation of an item
\omitvalue InvalidRole
Additionally EnginioModel supports dynamic roles which are mapped
@@ -562,6 +563,35 @@ EnginioReply *EnginioModel::setData(int row, const QVariant &value, const QStrin
return d->setValue(row, role, value);
}
+/*!
+ \overload
+ Update a \a value on \a row of this model's local cache
+ and send an update request to the Enginio backend.
+
+ All properties of the \a value will be used to update the item in \a row.
+ This can be useful to update multiple item's properties with one request.
+
+ \return reply from backend
+ \sa EnginioClient::update()
+*/
+EnginioReply *EnginioModel::setData(int row, const QJsonObject &value)
+{
+ Q_D(EnginioModel);
+ if (Q_UNLIKELY(!d->enginio())) {
+ qWarning("EnginioModel::setData(): Enginio client is not set");
+ return 0;
+ }
+
+ if (unsigned(row) >= unsigned(d->rowCount())) {
+ EnginioClientConnectionPrivate *client = EnginioClientConnectionPrivate::get(d->enginio());
+ QNetworkReply *nreply = new EnginioFakeReply(client, EnginioClientConnectionPrivate::constructErrorMessage(EnginioString::EnginioModel_setProperty_row_is_out_of_range));
+ EnginioReply *ereply = new EnginioReply(client, nreply);
+ return ereply;
+ }
+
+ return d->setData(row, value, Enginio::JsonObjectRole);
+}
+
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 f3cb0ca..538a532 100644
--- a/src/enginio_client/enginiomodel.h
+++ b/src/enginio_client/enginiomodel.h
@@ -80,6 +80,7 @@ public:
Q_INVOKABLE EnginioReply *append(const QJsonObject &value);
Q_INVOKABLE EnginioReply *remove(int row);
Q_INVOKABLE EnginioReply *setData(int row, const QVariant &value, const QString &role);
+ Q_INVOKABLE EnginioReply *setData(int row, const QJsonObject &value);
using EnginioBaseModel::setData;
Q_INVOKABLE EnginioReply *reload();
diff --git a/src/enginio_client/enginiostring_p.h b/src/enginio_client/enginiostring_p.h
index 559f4a3..dcc45a2 100644
--- a/src/enginio_client/enginiostring_p.h
+++ b/src/enginio_client/enginiostring_p.h
@@ -128,6 +128,7 @@
F(EnginioModel_was_removed_before_this_request_was_prepared, "EnginioModel was removed before this request was prepared")\
F(EnginioModel_The_query_was_changed_before_the_request_could_be_sent, "EnginioModel: The query was changed before the request could be sent")\
F(EnginioModel_Trying_to_update_an_object_with_unknown_role, "EnginioModel: Trying to update an object with unknown role")\
+ F(EnginioModel_Trying_to_update_an_item_with_an_empty_object, "EnginioModel: Trying to update an item with an empty object")\
F(Content_Range, "Content-Range")\
F(Content_Type, "Content-Type")\
F(Get, "GET")\
diff --git a/tests/auto/enginiomodel/tst_enginiomodel.cpp b/tests/auto/enginiomodel/tst_enginiomodel.cpp
index 47ad81b..0dc651a 100644
--- a/tests/auto/enginiomodel/tst_enginiomodel.cpp
+++ b/tests/auto/enginiomodel/tst_enginiomodel.cpp
@@ -99,9 +99,11 @@ private slots:
void deleteModelDurringRequests();
void updatingRoles();
void setData();
+ void setJsonData();
+ void data();
+ void setInvalidJsonData();
void reload();
void identityChange();
-
private:
template<class T>
void externallyRemovedImpl();
@@ -1275,25 +1277,31 @@ void tst_EnginioModel::delayedRequestBeforeInitialModelReset()
EnginioClient client;
client.setBackendId(_backendId);
client.setServiceUrl(EnginioTests::TESTAPP_URL);
- for (int i = 0; i < 24 ; ++i) {
- QString objectType = "objects." + EnginioTests::CUSTOM_OBJECT1;
- QJsonObject query;
- query.insert("objectType", objectType);
+ QString objectType = "objects." + EnginioTests::CUSTOM_OBJECT1;
+ QJsonObject query;
+ query.insert("objectType", objectType);
+ query.insert("title", QString::fromUtf8("appendAndRemoveModel"));
+
+ QJsonObject object;
+ object["title"] = "New Title";
+
+ for (int i = 0; i < 24 ; ++i) {
EnginioModel model;
QSignalSpy resetSpy(&model, SIGNAL(modelReset()));
model.setQuery(query);
model.setClient(&client);
- query.insert("title", QString::fromUtf8("appendAndRemoveModel"));
EnginioReply *append1 = model.append(query);
EnginioReply *append2 = model.append(query);
- EnginioReply *update = model.setData(0, QString::fromUtf8("appendAndRemoveModel1"), "title");
+ EnginioReply *update1 = model.setData(0, QString::fromUtf8("appendAndRemoveModel1"), "title");
+ EnginioReply *update2 = model.setData(0, object);
EnginioReply *remove = model.remove(1);
- QTRY_VERIFY(append1->isFinished() && append2->isFinished() && remove->isFinished() && update->isFinished());
+ QTRY_VERIFY(append1->isFinished() && append2->isFinished() && remove->isFinished() && update1->isFinished() && update2->isFinished());
QVERIFY(!append1->isError() || append1->errorString().contains("EnginioModel: The query was changed before the request could be sent"));
QVERIFY(!append2->isError() || append2->errorString().contains("EnginioModel: The query was changed before the request could be sent"));
- QVERIFY(!update->isError() || update->errorString().contains("EnginioModel: The query was changed before the request could be sent"));
+ QVERIFY(!update1->isError() || update1->errorString().contains("EnginioModel: The query was changed before the request could be sent"));
+ QVERIFY(!update2->isError() || update1->errorString().contains("EnginioModel: The query was changed before the request could be sent"));
QVERIFY(!remove->isError() || remove->errorString().contains("EnginioModel: The query was changed before the request could be sent"));
if (resetSpy.isEmpty())
break;
@@ -1353,10 +1361,15 @@ void tst_EnginioModel::deleteModelDurringRequests()
EnginioModel model;
model.setQuery(query);
model.setClient(&client);
+
query.insert("title", QString::fromUtf8("deleteModelDurringRequests"));
+ QJsonObject object = query;
+ object.insert("count", 111);
+
replies.append(model.append(query));
replies.append(model.append(query));
replies.append(model.setData(0, QString::fromUtf8("deleteModelDurringRequests1"), "title"));
+ replies.append(model.setData(1, object));
replies.append(model.remove(0));
}
@@ -1449,6 +1462,21 @@ void tst_EnginioModel::updatingRoles()
QCOMPARE(model.roleNames()[CustomModel::InvalidRole], QByteArray("objectType"));
}
+struct CustomModel: public EnginioModel {
+ enum Roles {
+ TitleRole = Enginio::CustomPropertyRole,
+ CountRole
+ };
+
+ virtual QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE
+ {
+ QHash<int, QByteArray> roles = EnginioModel::roleNames();
+ roles.insert(TitleRole, "title");
+ roles.insert(CountRole, "count");
+ return roles;
+ }
+};
+
void tst_EnginioModel::setData()
{
QString propertyName = "title";
@@ -1460,18 +1488,7 @@ void tst_EnginioModel::setData()
client.setBackendId(_backendId);
client.setServiceUrl(EnginioTests::TESTAPP_URL);
- struct Model: public EnginioModel {
- enum Roles {
- TitleRole = Enginio::CustomPropertyRole
- };
-
- virtual QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE
- {
- QHash<int, QByteArray> roles = EnginioModel::roleNames();
- roles.insert(TitleRole, "title");
- return roles;
- }
- } model;
+ CustomModel model;
model.disableNotifications();
model.setQuery(query);
@@ -1505,7 +1522,148 @@ void tst_EnginioModel::setData()
QVERIFY(!model.setData(model.index(-1, 1), QVariant()));
// make a correct setData call
- QVERIFY(model.setData(model.index(0), QString::fromLatin1("1111"), Model::TitleRole));
+ QVERIFY(model.setData(model.index(0), QString::fromLatin1("1111"), CustomModel::TitleRole));
+}
+
+void tst_EnginioModel::setJsonData()
+{
+ EnginioClient client;
+ client.setBackendId(_backendId);
+ client.setServiceUrl(EnginioTests::TESTAPP_URL);
+
+ QString propertyName1 = "title";
+ QString propertyName2 = "count";
+ QString objectType = "objects." + EnginioTests::CUSTOM_OBJECT1;
+ QJsonObject query;
+ query.insert("objectType", objectType);
+
+ CustomModel model;
+
+ model.disableNotifications();
+ model.setQuery(query);
+
+ { // init the model
+ QSignalSpy spy(&model, SIGNAL(modelReset()));
+ model.setClient(&client);
+
+ QTRY_VERIFY(spy.count() > 0);
+ }
+
+ if (model.rowCount() < 1) {
+ QJsonObject o;
+ o.insert(propertyName1, QString::fromLatin1("o"));
+ o.insert(propertyName2, 123);
+ o.insert("objectType", objectType);
+ model.append(o);
+ }
+
+ QTRY_VERIFY(model.rowCount());
+
+ QJsonObject object = model.data(model.index(0)).toJsonValue().toObject();
+ QVERIFY(!object.isEmpty());
+
+ object[propertyName1] = object[propertyName1].toString() + QString::fromLatin1("o");
+ object[propertyName2] = object[propertyName2].toInt() + 123;
+
+ EnginioReply *reply = model.setData(0, object);
+ QTRY_VERIFY(reply->isFinished());
+ CHECK_NO_ERROR(reply);
+
+ QJsonObject data = reply->data();
+ QCOMPARE(data[propertyName1], object[propertyName1]);
+ QCOMPARE(data[propertyName2], object[propertyName2]);
+}
+
+void tst_EnginioModel::data()
+{
+ EnginioClient client;
+ client.setBackendId(_backendId);
+ client.setServiceUrl(EnginioTests::TESTAPP_URL);
+
+ QString propertyName1 = "title";
+ QString propertyName2 = "count";
+ QString objectType = "objects." + EnginioTests::CUSTOM_OBJECT1;
+ QJsonObject query;
+ query.insert("objectType", objectType);
+
+ CustomModel model;
+
+ model.disableNotifications();
+ model.setQuery(query);
+
+ { // init the model
+ QSignalSpy spy(&model, SIGNAL(modelReset()));
+ model.setClient(&client);
+
+ QTRY_VERIFY(spy.count() > 0);
+ }
+
+ if (model.rowCount() < 1) {
+ QJsonObject o;
+ o.insert(propertyName1, QString::fromLatin1("o"));
+ o.insert(propertyName2, 123);
+ o.insert("objectType", objectType);
+ model.append(o);
+ }
+
+ QTRY_VERIFY(model.rowCount());
+ QModelIndex index = model.index(0);
+ QJsonObject item = model.data(index, Enginio::JsonObjectRole).toJsonValue().toObject();
+ QVERIFY(!item.isEmpty());
+
+ QCOMPARE(model.data(index, Enginio::IdRole).toJsonValue(), QJsonValue(item["id"]));
+ QCOMPARE(model.data(index, Enginio::CreatedAtRole).toJsonValue(), QJsonValue(item["createdAt"]));
+ QCOMPARE(model.data(index, Enginio::UpdatedAtRole).toJsonValue(), QJsonValue(item["updatedAt"]));
+}
+
+void tst_EnginioModel::setInvalidJsonData()
+{
+ EnginioClient client;
+ client.setBackendId(_backendId);
+ client.setServiceUrl(EnginioTests::TESTAPP_URL);
+
+ QString propertyName1 = "title";
+ QString propertyName2 = "count";
+ QString objectType = "objects." + EnginioTests::CUSTOM_OBJECT1;
+ QJsonObject query;
+ query.insert("objectType", objectType);
+
+ CustomModel model;
+
+ model.disableNotifications();
+ model.setQuery(query);
+
+ QJsonObject object;
+ {
+ QSignalSpy spy(&model, SIGNAL(modelReset()));
+ model.setClient(&client);
+
+ object.insert(propertyName1, QString::fromLatin1("o"));
+ object.insert(propertyName2, 123);
+ object.insert("objectType", objectType);
+
+ QTRY_VERIFY(spy.count() > 0);
+ }
+
+ if (model.rowCount() < 1) {
+ QJsonObject o;
+ o.insert(propertyName1, QString::fromLatin1("o"));
+ o.insert(propertyName2, 123);
+ o.insert("objectType", objectType);
+ model.append(o);
+ }
+
+ QVector<EnginioReply *> replies;
+ replies.append(model.setData(-1, object));
+ replies.append(model.setData(model.rowCount(), object));
+
+ QTRY_VERIFY(model.rowCount());
+ replies.append(model.setData(0, QJsonObject()));
+
+ foreach (const EnginioReply *reply, replies){
+ QTRY_VERIFY(reply->isFinished());
+ QVERIFY(reply->isError());
+ }
}
struct DeleteReplyCountHelper