summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--doc/qtenginiooverview.qdocconf1
-rw-r--r--src/enginio_client/doc/qtenginio.qdocconf1
-rw-r--r--src/enginio_client/enginio.h1
-rw-r--r--src/enginio_client/enginio_client.pro2
-rw-r--r--src/enginio_client/enginiobasemodel_p.h70
-rw-r--r--src/enginio_client/enginioclient.cpp15
-rw-r--r--src/enginio_client/enginioclient.h4
-rw-r--r--src/enginio_client/enginioclient_p.h71
-rw-r--r--src/enginio_client/enginioclientconnection.h4
-rw-r--r--src/enginio_client/enginiomodel.cpp50
-rw-r--r--src/enginio_client/enginiomodel.h3
-rw-r--r--src/enginio_client/enginioreply.h2
-rw-r--r--src/enginio_client/enginioreplystate.h4
-rw-r--r--src/enginio_client/enginiostring_p.h1
-rw-r--r--src/enginio_plugin/doc/qtenginioqml.qdocconf3
-rw-r--r--src/enginio_plugin/enginio_plugin.pro4
-rw-r--r--src/enginio_plugin/enginioqmlmodel.cpp6
-rw-r--r--src/enginio_plugin/enginioqmlmodel_p.h1
-rw-r--r--src/src.pro4
-rw-r--r--tests/auto/auto.pro5
-rw-r--r--tests/auto/enginioclient/tst_enginioclient.cpp86
-rw-r--r--tests/auto/enginiomodel/tst_enginiomodel.cpp365
-rw-r--r--tests/auto/files/files.pro1
-rw-r--r--tests/auto/files/tst_files.cpp94
-rw-r--r--tests/auto/qmltests/tst_model.qml31
26 files changed, 648 insertions, 183 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.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/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..dbcb403 100644
--- a/src/enginio_client/enginiobasemodel_p.h
+++ b/src/enginio_client/enginiobasemodel_p.h
@@ -399,6 +399,26 @@ protected:
}
};
+ class RefreshQueryAfterAuthChange // It is needed for compilers that don't support variadic templates
+ {
+ EnginioBaseModelPrivate *model;
+ public:
+ RefreshQueryAfterAuthChange(EnginioBaseModelPrivate *m)
+ : model(m)
+ {
+ Q_ASSERT(m);
+ }
+
+ void operator ()(Enginio::AuthenticationState state) const
+ {
+ // TODO we do not want to refresh on a failed attempt to login
+ if (state == Enginio::NotAuthenticated // logout
+ || state == Enginio::Authenticated // successful login
+ || state == Enginio::AuthenticationFailure) // token refresh failed
+ model->execute();
+ }
+ };
+
public:
EnginioBaseModelPrivate(EnginioBaseModel *q_ptr)
: _enginio(0)
@@ -418,7 +438,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 +606,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);
@@ -804,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);
@@ -850,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();
@@ -952,6 +988,7 @@ struct EnginioModelPrivateT : public EnginioBaseModelPrivate
_enginio = EnginioClientConnectionPrivate::get(const_cast<EnginioClientConnection*>(enginio));
_clientConnections.append(QObject::connect(enginio, &QObject::destroyed, EnginioDestroyed(this)));
_clientConnections.append(QObject::connect(enginio, &EnginioClientConnection::backendIdChanged, QueryChanged(this)));
+ _clientConnections.append(QObject::connect(enginio, &EnginioClientConnection::authenticationStateChanged, RefreshQueryAfterAuthChange(this)));
} else {
_enginio = 0;
}
@@ -993,7 +1030,8 @@ 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()); }
+ 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/enginioclient.cpp b/src/enginio_client/enginioclient.cpp
index 6a47696..3d1b888 100644
--- a/src/enginio_client/enginioclient.cpp
+++ b/src/enginio_client/enginioclient.cpp
@@ -262,6 +262,21 @@ QNetworkRequest EnginioClientConnectionPrivate::prepareRequest(const QUrl &url)
return req;
}
+bool EnginioClientConnectionPrivate::appendIdToPathIfPossible(QString *path, const QString &id, QByteArray *errorMsg, EnginioClientConnectionPrivate::PathOptions flags, QByteArray errorMessageHint)
+{
+ Q_ASSERT(path && errorMsg);
+ if (id.isEmpty()) {
+ if (flags == RequireIdInPath) {
+ *errorMsg = constructErrorMessage(errorMessageHint);
+ return false;
+ }
+ return true;
+ }
+ path->append('/');
+ path->append(id);
+ return true;
+}
+
EnginioClientConnectionPrivate::EnginioClientConnectionPrivate() :
_identity(),
_serviceUrl(EnginioString::apiEnginIo),
diff --git a/src/enginio_client/enginioclient.h b/src/enginio_client/enginioclient.h
index e65231c..94a3384 100644
--- a/src/enginio_client/enginioclient.h
+++ b/src/enginio_client/enginioclient.h
@@ -56,8 +56,8 @@ class ENGINIOCLIENT_EXPORT EnginioClient : public EnginioClientConnection
{
Q_OBJECT
- Q_ENUMS(Enginio::Operation); // TODO remove me QTBUG-33577
- Q_ENUMS(Enginio::AuthenticationState); // TODO remove me QTBUG-33577
+ Q_ENUMS(Enginio::Operation) // TODO remove me QTBUG-33577
+ Q_ENUMS(Enginio::AuthenticationState) // TODO remove me QTBUG-33577
Q_DECLARE_PRIVATE(EnginioClient)
public:
diff --git a/src/enginio_client/enginioclient_p.h b/src/enginio_client/enginioclient_p.h
index 93ce864..286a30c 100644
--- a/src/enginio_client/enginioclient_p.h
+++ b/src/enginio_client/enginioclient_p.h
@@ -87,11 +87,11 @@ QT_BEGIN_NAMESPACE
CHECK_AND_SET_URL_PATH_IMPL(Url, Object, Operation, EnginioClientConnectionPrivate::Default)
#define CHECK_AND_SET_PATH_WITH_ID(Url, Object, Operation) \
- CHECK_AND_SET_URL_PATH_IMPL(Url, Object, Operation, EnginioClientConnectionPrivate::IncludeIdInPath)
+ CHECK_AND_SET_URL_PATH_IMPL(Url, Object, Operation, EnginioClientConnectionPrivate::RequireIdInPath)
class ENGINIOCLIENT_EXPORT EnginioClientConnectionPrivate : public QObjectPrivate
{
- enum PathOptions { Default, IncludeIdInPath = 1};
+ enum PathOptions { Default, RequireIdInPath = 1};
struct ENGINIOCLIENT_EXPORT GetPathReturnValue : public QPair<bool, QString>
{
@@ -105,6 +105,8 @@ class ENGINIOCLIENT_EXPORT EnginioClientConnectionPrivate : public QObjectPrivat
operator QString() const { return second; }
};
+ static bool appendIdToPathIfPossible(QString *path, const QString &id, QByteArray *errorMsg, PathOptions flags, QByteArray errorMessageHint = EnginioString::Requested_operation_requires_non_empty_id_value);
+
template<class T>
static GetPathReturnValue getPath(const T &object, int operation, QString *path, QByteArray *errorMsg, PathOptions flags = Default)
{
@@ -114,6 +116,7 @@ class ENGINIOCLIENT_EXPORT EnginioClientConnectionPrivate : public QObjectPrivat
QString &result = *path;
result.reserve(96);
result.append(QStringLiteral("/v1/"));
+ QString id = object[EnginioString::id].toString();
switch (operation) {
case Enginio::ObjectOperation: {
@@ -122,8 +125,9 @@ class ENGINIOCLIENT_EXPORT EnginioClientConnectionPrivate : public QObjectPrivat
msg = constructErrorMessage(EnginioString::Requested_object_operation_requires_non_empty_objectType_value);
return GetPathReturnValue(Failed);
}
-
result.append(objectType.replace('.', '/'));
+ if (!appendIdToPathIfPossible(path, id, errorMsg, flags))
+ return GetPathReturnValue(Failed);
break;
}
case Enginio::AccessControlOperation:
@@ -133,83 +137,65 @@ class ENGINIOCLIENT_EXPORT EnginioClientConnectionPrivate : public QObjectPrivat
msg = constructErrorMessage(EnginioString::Requested_object_acl_operation_requires_non_empty_objectType_value);
return GetPathReturnValue(Failed);
}
-
result.append(objectType.replace('.', '/'));
- QString id = object[EnginioString::id].toString();
- if (id.isEmpty()) {
- msg = constructErrorMessage(EnginioString::Requested_object_acl_operation_requires_non_empty_id_value);
+ if (!appendIdToPathIfPossible(path, id, errorMsg, RequireIdInPath, EnginioString::Requested_object_acl_operation_requires_non_empty_id_value))
return GetPathReturnValue(Failed);
- }
- result.append('/');
- result.append(id);
result.append('/');
result.append(EnginioString::access);
return GetPathReturnValue(true, EnginioString::access);
}
case Enginio::FileOperation: {
result.append(EnginioString::files);
- // if we have a fileID, it becomes "view", otherwise it is up/download
- QString fileId = object[EnginioString::id].toString();
- if (!fileId.isEmpty()) {
- result.append('/');
- result.append(fileId);
- }
+ if (!appendIdToPathIfPossible(path, id, errorMsg, flags))
+ return GetPathReturnValue(Failed);
break;
}
case Enginio::FileGetDownloadUrlOperation: {
result.append(EnginioString::files);
- QString fileId = object[EnginioString::id].toString();
- if (fileId.isEmpty()) {
- msg = constructErrorMessage(EnginioString::Download_operation_requires_non_empty_fileId_value);
+ if (!appendIdToPathIfPossible(path, id, errorMsg, RequireIdInPath, EnginioString::Download_operation_requires_non_empty_fileId_value))
return GetPathReturnValue(Failed);
- }
- result.append(QLatin1Char('/') + fileId + QStringLiteral("/download_url"));
+ result.append(QStringLiteral("/download_url"));
break;
}
case Enginio::FileChunkUploadOperation: {
- const QString fileId = object[EnginioString::id].toString();
- Q_ASSERT(!fileId.isEmpty());
- result.append(EnginioString::files + QLatin1Char('/') + fileId + QStringLiteral("/chunk"));
+ Q_ASSERT(!id.isEmpty());
+ result.append(EnginioString::files);
+ if (!appendIdToPathIfPossible(path, id, errorMsg, flags))
+ return GetPathReturnValue(Failed);
+ result.append(QStringLiteral("/chunk"));
break;
}
case Enginio::SearchOperation:
result.append(EnginioString::search);
+ if (!appendIdToPathIfPossible(path, id, errorMsg, flags))
+ return GetPathReturnValue(Failed);
break;
case Enginio::SessionOperation:
result.append(EnginioString::session);
+ if (!appendIdToPathIfPossible(path, id, errorMsg, flags))
+ return GetPathReturnValue(Failed);
break;
case Enginio::UserOperation:
result.append(EnginioString::users);
+ if (!appendIdToPathIfPossible(path, id, errorMsg, flags))
+ return GetPathReturnValue(Failed);
break;
case Enginio::UsergroupOperation:
result.append(EnginioString::usergroups);
+ if (!appendIdToPathIfPossible(path, id, errorMsg, flags))
+ return GetPathReturnValue(Failed);
break;
case Enginio::UsergroupMembersOperation:
{
- QString id = object[EnginioString::id].toString();
- if (id.isEmpty()) {
- msg = constructErrorMessage(EnginioString::Requested_usergroup_member_operation_requires_non_empty_id_value);
- return GetPathReturnValue(Failed);
- }
result.append(EnginioString::usergroups);
- result.append('/');
- result.append(id);
+ if (!appendIdToPathIfPossible(path, id, errorMsg, RequireIdInPath, EnginioString::Requested_usergroup_member_operation_requires_non_empty_id_value))
+ return GetPathReturnValue(Failed);
result.append('/');
result.append(EnginioString::members);
return GetPathReturnValue(true, EnginioString::member);
}
}
- if (flags & IncludeIdInPath) {
- QString id = object[EnginioString::id].toString();
- if (id.isEmpty()) {
- msg = constructErrorMessage(EnginioString::Requested_operation_requires_non_empty_id_value);
- return GetPathReturnValue(Failed);
- }
- result.append('/');
- result.append(id);
- }
-
return GetPathReturnValue(true, QString());
}
@@ -557,7 +543,6 @@ public:
url.setQuery(urlQuery);
QNetworkRequest req = prepareRequest(url);
-
return networkManager()->get(req);
}
@@ -565,7 +550,7 @@ public:
QNetworkReply *downloadUrl(const ObjectAdaptor<T> &object)
{
QUrl url(_serviceUrl);
- CHECK_AND_SET_PATH(url, object, Enginio::FileGetDownloadUrlOperation);
+ CHECK_AND_SET_PATH_WITH_ID(url, object, Enginio::FileGetDownloadUrlOperation);
if (object.contains(EnginioString::variant)) {
QString variant = object[EnginioString::variant].toString();
QUrlQuery query;
diff --git a/src/enginio_client/enginioclientconnection.h b/src/enginio_client/enginioclientconnection.h
index bf8b1f9..4caae50 100644
--- a/src/enginio_client/enginioclientconnection.h
+++ b/src/enginio_client/enginioclientconnection.h
@@ -65,8 +65,8 @@ class ENGINIOCLIENT_EXPORT EnginioClientConnection : public QObject
Q_PROPERTY(EnginioIdentity *identity READ identity WRITE setIdentity NOTIFY identityChanged FINAL)
Q_PROPERTY(Enginio::AuthenticationState authenticationState READ authenticationState NOTIFY authenticationStateChanged FINAL)
- Q_ENUMS(Enginio::Operation); // TODO remove me QTBUG-33577
- Q_ENUMS(Enginio::AuthenticationState); // TODO remove me QTBUG-33577
+ Q_ENUMS(Enginio::Operation) // TODO remove me QTBUG-33577
+ Q_ENUMS(Enginio::AuthenticationState) // TODO remove me QTBUG-33577
public:
~EnginioClientConnection();
diff --git a/src/enginio_client/enginiomodel.cpp b/src/enginio_client/enginiomodel.cpp
index 3d5f93d..3ff34a3 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();
@@ -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
@@ -480,6 +481,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,6 +563,34 @@ 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
{
diff --git a/src/enginio_client/enginiomodel.h b/src/enginio_client/enginiomodel.h
index 81c7eeb..538a532 100644
--- a/src/enginio_client/enginiomodel.h
+++ b/src/enginio_client/enginiomodel.h
@@ -80,8 +80,11 @@ 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();
+
Q_SIGNALS:
void queryChanged(const QJsonObject &query);
void clientChanged(EnginioClient *client);
diff --git a/src/enginio_client/enginioreply.h b/src/enginio_client/enginioreply.h
index bb743b2..d8d7b35 100644
--- a/src/enginio_client/enginioreply.h
+++ b/src/enginio_client/enginioreply.h
@@ -108,6 +108,6 @@ ENGINIOCLIENT_EXPORT QDebug operator<<(QDebug d, const EnginioReply *reply);
#endif
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(const EnginioReply*);
+Q_DECLARE_METATYPE(const EnginioReply*)
#endif // ENGINIOREPLY_H
diff --git a/src/enginio_client/enginioreplystate.h b/src/enginio_client/enginioreplystate.h
index b4105e7..cbe0cc7 100644
--- a/src/enginio_client/enginioreplystate.h
+++ b/src/enginio_client/enginioreplystate.h
@@ -59,8 +59,8 @@ class EnginioReplyStatePrivate;
class ENGINIOCLIENT_EXPORT EnginioReplyState: public QObject
{
Q_OBJECT
- Q_ENUMS(QNetworkReply::NetworkError); // TODO remove me QTBUG-33577
- Q_ENUMS(Enginio::ErrorType); // TODO remove me QTBUG-33577
+ Q_ENUMS(QNetworkReply::NetworkError) // TODO remove me QTBUG-33577
+ Q_ENUMS(Enginio::ErrorType) // TODO remove me QTBUG-33577
Q_PROPERTY(Enginio::ErrorType errorType READ errorType NOTIFY dataChanged)
Q_PROPERTY(QNetworkReply::NetworkError networkError READ networkError NOTIFY dataChanged)
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/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/enginioclient/tst_enginioclient.cpp b/tests/auto/enginioclient/tst_enginioclient.cpp
index aafebbe..d1f25cc 100644
--- a/tests/auto/enginioclient/tst_enginioclient.cpp
+++ b/tests/auto/enginioclient/tst_enginioclient.cpp
@@ -140,6 +140,49 @@ void tst_EnginioClient::initTestCase()
prepareForSearch();
EnginioTests::prepareTestUsersAndUserGroups(_backendId);
+
+ // create some todos objects
+ QJsonObject todos;
+ todos["name"] = QStringLiteral("todos");
+
+ QJsonObject title;
+ title["name"] = QStringLiteral("title");
+ title["type"] = QStringLiteral("string");
+ title["indexed"] = true;
+ QJsonObject completed;
+ completed["name"] = QStringLiteral("completed");
+ completed["type"] = QStringLiteral("boolean");
+ completed["indexed"] = false;
+ QJsonObject count;
+ count["name"] = QStringLiteral("count");
+ count["type"] = QStringLiteral("number");
+ count["indexed"] = false;
+
+ QJsonArray properties;
+ properties.append(title);
+ properties.append(completed);
+ properties.append(count);
+ todos["properties"] = properties;
+
+ QVERIFY(_backendManager.createObjectType(_backendName, EnginioTests::TESTAPP_ENV, todos));
+ {
+ EnginioClient client;
+ QObject::connect(&client, SIGNAL(error(EnginioReply *)), this, SLOT(error(EnginioReply *)));
+ client.setBackendId(_backendId);
+ client.setServiceUrl(EnginioTests::TESTAPP_URL);
+
+ QJsonObject object;
+ object["objectType"] = QString::fromUtf8("objects.todos");
+ object["title"] = QString::fromUtf8("init todo 1");
+ object["completed"] = false;
+ EnginioReply* reply1 = client.create(object);
+ object["title"] = QString::fromUtf8("init todo 2");
+ EnginioReply* reply2 = client.create(object);
+ QTRY_VERIFY(reply1->isFinished());
+ CHECK_NO_ERROR(reply1);
+ QTRY_VERIFY(reply2->isFinished());
+ CHECK_NO_ERROR(reply2);
+ }
}
void tst_EnginioClient::cleanupTestCase()
@@ -177,20 +220,31 @@ void tst_EnginioClient::query_todos()
QSignalSpy spy(&client, SIGNAL(finished(EnginioReply *)));
QSignalSpy spyError(&client, SIGNAL(error(EnginioReply*)));
//![query-todo]
- QJsonObject object;
- object["objectType"] = QString::fromUtf8("objects.todos");
- const EnginioReply* reply = client.query(object);
+ QJsonObject query;
+ query["objectType"] = QString::fromUtf8("objects.todos");
+ EnginioReply *reply = client.query(query);
//![query-todo]
QVERIFY(reply);
QTRY_COMPARE(spy.count(), 1);
QCOMPARE(spyError.count(), 0);
- const EnginioReply *response = spy[0][0].value<EnginioReply*>();
+ EnginioReply *response = spy[0][0].value<EnginioReply*>();
QCOMPARE(response, reply);
CHECK_NO_ERROR(response);
QVERIFY(!response->data().isEmpty());
QVERIFY(!response->data()["results"].isUndefined());
+
+ { // try to query the object with an id
+ QJsonArray results = reply->data()["results"].toArray();
+ QVERIFY(results.count() > 1); // the test assumes that querying by id will return less items.
+ QString id = results[0].toObject()["id"].toString();
+ query["id"] = id;
+ reply = client.query(query);
+ QTRY_VERIFY(reply->isFinished());
+ CHECK_NO_ERROR(reply);
+ QCOMPARE(reply->data()["id"].toString(), id);
+ }
}
void tst_EnginioClient::query_todos_filter()
@@ -882,30 +936,6 @@ void tst_EnginioClient::deleteReply()
void tst_EnginioClient::create_todos()
{
- QJsonObject todos;
- todos["name"] = QStringLiteral("todos");
-
- QJsonObject title;
- title["name"] = QStringLiteral("title");
- title["type"] = QStringLiteral("string");
- title["indexed"] = true;
- QJsonObject completed;
- completed["name"] = QStringLiteral("completed");
- completed["type"] = QStringLiteral("boolean");
- completed["indexed"] = false;
- QJsonObject count;
- count["name"] = QStringLiteral("count");
- count["type"] = QStringLiteral("number");
- count["indexed"] = false;
-
- QJsonArray properties;
- properties.append(title);
- properties.append(completed);
- properties.append(count);
- todos["properties"] = properties;
-
- QVERIFY(_backendManager.createObjectType(_backendName, EnginioTests::TESTAPP_ENV, todos));
-
EnginioClient client;
QObject::connect(&client, SIGNAL(error(EnginioReply *)), this, SLOT(error(EnginioReply *)));
client.setBackendId(_backendId);
diff --git a/tests/auto/enginiomodel/tst_enginiomodel.cpp b/tests/auto/enginiomodel/tst_enginiomodel.cpp
index 5edc707..0dc651a 100644
--- a/tests/auto/enginiomodel/tst_enginiomodel.cpp
+++ b/tests/auto/enginiomodel/tst_enginiomodel.cpp
@@ -50,6 +50,7 @@
#include <Enginio/enginioreply.h>
#include <Enginio/enginiomodel.h>
#include <Enginio/enginioidentity.h>
+#include <Enginio/enginiooauth2authentication.h>
#include "../common/common.h"
@@ -98,6 +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();
@@ -119,6 +125,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()
@@ -1254,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;
@@ -1332,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));
}
@@ -1428,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";
@@ -1439,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);
@@ -1484,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
@@ -1557,5 +1736,149 @@ 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);
+}
+
+void tst_EnginioModel::identityChange()
+{
+ EnginioClient client;
+ QObject::connect(&client, SIGNAL(error(EnginioReply *)), this, SLOT(error(EnginioReply *)));
+ client.setBackendId("5376019e698b3c6ad500095a");
+
+ QJsonObject query;
+ query["objectType"] = "objects.EnginioModelIdentityChange";
+
+ EnginioModel model;
+ model.setQuery(query);
+ model.setClient(&client);
+
+ EnginioOAuth2Authentication identity1;
+ identity1.setUser("test1");
+ identity1.setPassword("test1");
+
+ EnginioOAuth2Authentication identity2;
+ identity2.setUser("test2");
+ identity2.setPassword("test2");
+
+ QTRY_COMPARE(model.rowCount(), 1); // There is only one publicly visible element
+
+ client.setIdentity(&identity1);
+ QTRY_COMPARE(model.rowCount(), 2); // Test1 sees one additional element
+
+ client.setIdentity(&identity2);
+ QTRY_COMPARE(model.rowCount(), 3); // Test2 sees two additional elements
+
+ client.setIdentity(0);
+ QTRY_COMPARE(model.rowCount(), 1);
+}
+
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/files/tst_files.cpp b/tests/auto/files/tst_files.cpp
index cf26117..f66cb2a 100644
--- a/tests/auto/files/tst_files.cpp
+++ b/tests/auto/files/tst_files.cpp
@@ -57,10 +57,8 @@ class tst_Files: public QObject
{
Q_OBJECT
- QString _backendName;
- EnginioTests::EnginioBackendManager _backendManager;
- QByteArray _backendId;
-
+ QString _id;
+ EnginioClient _client;
public slots:
void error(EnginioReply *reply) {
qDebug() << "\n\n### ERROR";
@@ -70,32 +68,29 @@ public slots:
}
private slots:
- void initTestCase();
- void cleanupTestCase();
+ void initTestCase()
+ {
+ QObject::connect(&_client, SIGNAL(error(EnginioReply *)), this, SLOT(error(EnginioReply *)));
+ _client.setBackendId("5376019e698b3c6ad500095a");
+ }
+
+ void cleanup()
+ {
+ if (!_id.isEmpty()) {
+ QJsonObject obj;
+ obj["objectType"] = QString::fromUtf8("objects.FilesFileUploadDownload");
+ obj["id"] = _id;
+ EnginioReply *reply = _client.remove(obj);
+ QTRY_VERIFY(reply->isFinished());
+ CHECK_NO_ERROR(reply);
+ }
+ _id.clear();
+ }
+
void fileUploadDownload_data();
void fileUploadDownload();
};
-void tst_Files::initTestCase()
-{
- if (EnginioTests::TESTAPP_URL.isEmpty())
- QFAIL("Needed environment variable ENGINIO_API_URL is not set!");
-
- _backendName = QStringLiteral("Files") + QString::number(QDateTime::currentMSecsSinceEpoch());
- QVERIFY(_backendManager.createBackend(_backendName));
-
- QJsonObject apiKeys = _backendManager.backendApiKeys(_backendName, EnginioTests::TESTAPP_ENV);
- _backendId = apiKeys["backendId"].toString().toUtf8();
-
- QVERIFY(!_backendId.isEmpty());
-
- EnginioTests::prepareTestObjectType(_backendName);
-}
-
-void tst_Files::cleanupTestCase()
-{
- QVERIFY(_backendManager.removeBackend(_backendName));
-}
void tst_Files::fileUploadDownload_data()
{
@@ -127,36 +122,28 @@ void tst_Files::fileUploadDownload()
{
QFETCH(int, chunkSize);
- EnginioClient client;
- QObject::connect(&client, SIGNAL(error(EnginioReply *)), this, SLOT(error(EnginioReply *)));
- client.setBackendId(_backendId);
- client.setServiceUrl(EnginioTests::TESTAPP_URL);
-
if (chunkSize > 0) {
- EnginioClientConnectionPrivate *clientPrivate = EnginioClientConnectionPrivate::get(&client);
+ EnginioClientConnectionPrivate *clientPrivate = EnginioClientConnectionPrivate::get(&_client);
clientPrivate->_uploadChunkSize = chunkSize;
}
- QSignalSpy spyError(&client, SIGNAL(error(EnginioReply*)));
+ QSignalSpy spyError(&_client, SIGNAL(error(EnginioReply*)));
//![upload-create-object]
QJsonObject obj;
- obj["objectType"] = QString::fromUtf8("objects.%1").arg(EnginioTests::CUSTOM_OBJECT1);
- obj["title"] = QString::fromUtf8("Object With File");
- const EnginioReply* createReply = client.create(obj);
+ obj["objectType"] = QString::fromUtf8("objects.FilesFileUploadDownload");
+ const EnginioReply* createReply = _client.create(obj);
//![upload-create-object]
QVERIFY(createReply);
QTRY_VERIFY(createReply->isFinished());
CHECK_NO_ERROR(createReply);
QCOMPARE(spyError.count(), 0);
- QCOMPARE(createReply->networkError(), QNetworkReply::NoError);
QJsonObject data = createReply->data();
QVERIFY(!data.isEmpty());
- QCOMPARE(data["title"], obj["title"]);
QCOMPARE(data["objectType"], obj["objectType"]);
- QString id = data["id"].toString();
- QVERIFY(!id.isEmpty());
+ _id = data["id"].toString();
+ QVERIFY(!_id.isEmpty());
QString fileName = QStringLiteral("test.png");
QString filePath = QStringLiteral(TEST_FILE_PATH);
@@ -168,8 +155,8 @@ void tst_Files::fileUploadDownload()
{
//![upload]
QJsonObject object;
- object["id"] = id;
- object["objectType"] = QString::fromUtf8("objects.%1").arg(EnginioTests::CUSTOM_OBJECT1);
+ object["id"] = _id;
+ object["objectType"] = QString::fromUtf8("objects.FilesFileUploadDownload");
object["propertyName"] = QStringLiteral("fileAttachment");
QJsonObject fileObject;
@@ -178,7 +165,7 @@ void tst_Files::fileUploadDownload()
QJsonObject uploadJson;
uploadJson[QStringLiteral("targetFileProperty")] = object;
uploadJson[QStringLiteral("file")] = fileObject;
- const EnginioReply *responseUpload = client.uploadFile(uploadJson, QUrl(filePath));
+ const EnginioReply *responseUpload = _client.uploadFile(uploadJson, QUrl(filePath));
//![upload]
QVERIFY(responseUpload);
@@ -197,11 +184,10 @@ void tst_Files::fileUploadDownload()
QJsonObject obj2;
obj2 = QJsonDocument::fromJson(
"{\"include\": {\"fileAttachment\": {}},"
- "\"objectType\": \"objects." + EnginioTests::CUSTOM_OBJECT1.toUtf8() + "\","
- "\"query\": {\"id\": \"" + id.toUtf8() + "\"}}").object();
+ "\"objectType\": \"objects.FilesFileUploadDownload\","
+ "\"query\": {\"id\": \"" + _id.toUtf8() + "\"}}").object();
- const EnginioReply *reply = client.query(obj2);
- QVERIFY(reply);
+ const EnginioReply *reply = _client.query(obj2);
QTRY_VERIFY(reply->isFinished());
CHECK_NO_ERROR(reply);
QCOMPARE(spyError.count(), 0);
@@ -216,7 +202,7 @@ void tst_Files::fileUploadDownload()
while (!resultObject["fileAttachment"].isObject() && --ok) {
qDebug() << resultObject;
QTest::qWait(1000); // We failed the test, but still we need to gather some debug data.
- const EnginioReply *reply = client.query(obj2);
+ const EnginioReply *reply = _client.query(obj2);
QTRY_VERIFY(reply->isFinished());
CHECK_NO_ERROR(reply);
QCOMPARE(spyError.count(), 0);
@@ -244,7 +230,7 @@ void tst_Files::fileUploadDownload()
QJsonObject object;
object["id"] = fileId; // ID of an existing object with attached file
- const EnginioReply *replyDownload = client.downloadUrl(object);
+ const EnginioReply *replyDownload = _client.downloadUrl(object);
//![download]
QVERIFY(replyDownload);
@@ -257,7 +243,7 @@ void tst_Files::fileUploadDownload()
QVERIFY(!downloadData["expiresAt"].toString().isEmpty());
QNetworkRequest req;
req.setUrl(QUrl(downloadData["expiringUrl"].toString()));
- QNetworkReply *reply = client.networkManager()->get(req);
+ QNetworkReply *reply = _client.networkManager()->get(req);
QTRY_VERIFY(reply->isFinished());
if (reply->error() != QNetworkReply::NoError) {
// the test has failed already, let's printout some debugging information
@@ -277,7 +263,7 @@ void tst_Files::fileUploadDownload()
{
QJsonObject fileObject;
fileObject.insert("id", fileId);
- EnginioReply *fileInfo = client.query(fileObject, Enginio::FileOperation);
+ EnginioReply *fileInfo = _client.query(fileObject, Enginio::FileOperation);
QVERIFY(fileInfo);
QTRY_VERIFY(fileInfo->isFinished());
CHECK_NO_ERROR(fileInfo);
@@ -293,7 +279,7 @@ void tst_Files::fileUploadDownload()
int count = 0;
while (thumbnailStatus == "processing" && ++count < 20) {
QTest::qWait(1000);
- fileInfo = client.query(fileObject, Enginio::FileOperation);
+ fileInfo = _client.query(fileObject, Enginio::FileOperation);
QVERIFY(fileInfo);
QTRY_VERIFY(fileInfo->isFinished());
CHECK_NO_ERROR(fileInfo);
@@ -317,7 +303,7 @@ void tst_Files::fileUploadDownload()
object["id"] = fileId; // ID of an existing object with attached file
object[EnginioString::variant] = QStringLiteral("thumbnail");
- const EnginioReply* replyDownload = client.downloadUrl(object);
+ const EnginioReply *replyDownload = _client.downloadUrl(object);
QVERIFY(replyDownload);
QTRY_VERIFY(replyDownload->isFinished());
@@ -330,7 +316,7 @@ void tst_Files::fileUploadDownload()
QNetworkRequest req;
req.setUrl(QUrl(downloadData["expiringUrl"].toString()));
- QNetworkReply *reply = client.networkManager()->get(req);
+ QNetworkReply *reply = _client.networkManager()->get(req);
QTRY_VERIFY(reply->isFinished());
if (reply->error() != QNetworkReply::NoError) {
// the test has failed already, let's printout some debugging information
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)
+ }
+ }
}