Began implementing more detailled states for server API requests
This commit is contained in:
parent
7be33026cf
commit
369cf2c1e9
3 changed files with 115 additions and 104 deletions
|
@ -6,18 +6,6 @@ Page {
|
|||
id: loginPage
|
||||
|
||||
property string accountId
|
||||
/* Possible states for loginStatus:
|
||||
- newAccount: No information about the account is known yet
|
||||
- unknownLoggedOut: The user is logged out and the server version is not known
|
||||
- unknownLoggedIn: The user is logged in but the server version is not known
|
||||
- legacyLoggedOut: The user is logged out and the server does not accept Login Flow V2
|
||||
- legacyPending: The user has provided his credentials but they are not verified yet
|
||||
- legacyLoggedIn: The user has provided his credentials and they have been accepted by the server
|
||||
- flowV2LoggedOut: The user is logged out and the server does support Login Flow V2
|
||||
- flowV2Pending: The user has initiated the Login Flow V2 workflow
|
||||
- flowV2LoggedIn: The Login Flow V2 workflow has been successfully finished
|
||||
*/
|
||||
property string loginStatus: "unknownLoggedOut"
|
||||
|
||||
ConfigurationGroup {
|
||||
id: account
|
||||
|
@ -40,24 +28,18 @@ Page {
|
|||
|
||||
Connections {
|
||||
target: notesApi
|
||||
onStatusStatusChanged: {
|
||||
|
||||
}
|
||||
onLoginStatusChanged: {
|
||||
|
||||
}
|
||||
onStatusInstalledChanged: {
|
||||
if (notesApi.statusInstalled) {
|
||||
console.log("Nextcloud instance found")
|
||||
}
|
||||
}
|
||||
onStatusVersionChanged: {
|
||||
if (notesApi.statusVersion.split('.')[0] < 16) {
|
||||
if (loginStatus === "unknownLoggedOut")
|
||||
loginStatus = "legacyLoggedOut"
|
||||
if (loginStatus === "unknownLoggedIn")
|
||||
loginStatus = "legacyLoggedIn"
|
||||
}
|
||||
if (notesApi.statusVersion.split('.')[0] >= 16) {
|
||||
if (loginStatus === "unknownLoggedOut")
|
||||
loginStatus = "flowV2LoggedOut"
|
||||
if (loginStatus === "unknownLoggedIn")
|
||||
loginStatus = "flowV2LoggedIn"
|
||||
}
|
||||
}
|
||||
onLoginUrlChanged: {
|
||||
if (notesApi.loginUrl) {
|
||||
|
|
126
src/notesapi.cpp
126
src/notesapi.cpp
|
@ -10,6 +10,11 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co
|
|||
// TODO verify connections (also in destructor)
|
||||
m_loginPollTimer.setInterval(5000);
|
||||
connect(&m_loginPollTimer, SIGNAL(timeout()), this, SLOT(pollLoginUrl()));
|
||||
m_statusReply = NULL;
|
||||
m_loginReply = NULL;
|
||||
m_pollReply = NULL;
|
||||
m_statusStatus = RequestStatus::StatusNone;
|
||||
m_loginStatus = RequestStatus::StatusNone;
|
||||
mp_model = new NotesModel(this);
|
||||
mp_modelProxy = new NotesProxyModel(this);
|
||||
mp_modelProxy->setSourceModel(mp_model);
|
||||
|
@ -18,9 +23,6 @@ NotesApi::NotesApi(const QString statusEndpoint, const QString loginEndpoint, co
|
|||
mp_modelProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
mp_modelProxy->setFilterRole(NotesModel::ContentRole);
|
||||
connect(this, SIGNAL(urlChanged(QUrl)), this, SLOT(verifyUrl(QUrl)));
|
||||
connect(this, SIGNAL(statusBusyChanged(bool)), this, SIGNAL(busyChanged(bool)));
|
||||
connect(this, SIGNAL(loginBusyChanged(bool)), this, SIGNAL(busyChanged(bool)));
|
||||
connect(this, SIGNAL(notesBusyChanged(bool)), this, SIGNAL(busyChanged(bool)));
|
||||
connect(&m_manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(requireAuthentication(QNetworkReply*,QAuthenticator*)));
|
||||
connect(&m_manager, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(onNetworkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)));
|
||||
connect(&m_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));
|
||||
|
@ -154,48 +156,56 @@ void NotesApi::setDataFile(QString dataFile) {
|
|||
}
|
||||
}
|
||||
|
||||
void NotesApi::getStatus() {
|
||||
bool NotesApi::getStatus() {
|
||||
if (m_statusStatus != RequestStatus::StatusBusy) {
|
||||
m_statusStatus = RequestStatus::StatusBusy;
|
||||
emit statusStatusChanged(m_statusStatus);
|
||||
QUrl url = apiEndpointUrl(m_statusEndpoint);
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "POST" << url.toDisplayString();
|
||||
m_request.setUrl(url);
|
||||
m_statusReplies << m_manager.post(m_request, QByteArray());
|
||||
emit statusBusyChanged(true);
|
||||
m_statusReply = m_manager.post(m_request, QByteArray());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NotesApi::initiateFlowV2Login() {
|
||||
bool NotesApi::initiateFlowV2Login() {
|
||||
if (m_loginStatus == RequestStatus::StatusInitiated || m_loginStatus == RequestStatus::StatusBusy) {
|
||||
abortFlowV2Login();
|
||||
}
|
||||
if (m_loginStatus != RequestStatus::StatusInitiated) {
|
||||
m_loginStatus = RequestStatus::StatusInitiated;
|
||||
emit loginStatusChanged(m_loginStatus);
|
||||
QUrl url = apiEndpointUrl(m_loginEndpoint);
|
||||
if (url.isValid() && !url.scheme().isEmpty() && !url.host().isEmpty()) {
|
||||
qDebug() << "POST" << url.toDisplayString();
|
||||
m_request.setUrl(url);
|
||||
m_loginReplies << m_manager.post(m_request, QByteArray());
|
||||
m_loginPollTimer.start();
|
||||
emit loginBusyChanged(true);
|
||||
m_loginReply = m_manager.post(m_request, QByteArray());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NotesApi::abortFlowV2Login() {
|
||||
// TODO crashes!
|
||||
m_loginPollTimer.stop();
|
||||
m_loginUrl.clear();
|
||||
emit loginUrlChanged(m_loginUrl);
|
||||
m_pollUrl.clear();
|
||||
m_pollToken.clear();
|
||||
for (int i = 0; i < m_loginReplies.size(); ++i) {
|
||||
m_loginReplies[i]->abort();
|
||||
}
|
||||
for (int i = 0; i < m_pollReplies.size(); ++i) {
|
||||
m_pollReplies[i]->abort();
|
||||
}
|
||||
if (m_loginReply->isRunning())
|
||||
m_loginReply->abort();
|
||||
if (m_pollReply->isRunning())
|
||||
m_pollReply->abort();
|
||||
}
|
||||
|
||||
void NotesApi::pollLoginUrl() {
|
||||
if (m_pollUrl.isValid() && !m_pollUrl.scheme().isEmpty() && !m_pollUrl.host().isEmpty() && !m_pollToken.isEmpty()) {
|
||||
qDebug() << "POST" << m_pollUrl.toDisplayString();
|
||||
m_request.setUrl(m_pollUrl);
|
||||
m_pollReplies << m_manager.post(m_request, QByteArray("token=").append(m_pollToken));
|
||||
emit loginBusyChanged(true);
|
||||
m_pollReply = m_manager.post(m_request, QByteArray("token=").append(m_pollToken));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +217,7 @@ void NotesApi::getAllNotes(QStringList excludeFields) {
|
|||
qDebug() << "GET" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.get(m_authenticatedRequest);
|
||||
emit notesBusyChanged(true);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +229,7 @@ void NotesApi::getNote(double noteId, QStringList excludeFields) {
|
|||
qDebug() << "GET" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.get(m_authenticatedRequest);
|
||||
emit notesBusyChanged(true);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,7 +244,7 @@ void NotesApi::createNote(QVariantMap fields) {
|
|||
qDebug() << "POST" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.post(m_authenticatedRequest, note.toJsonDocument().toJson());
|
||||
emit notesBusyChanged(true);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +259,7 @@ void NotesApi::updateNote(double noteId, QVariantMap fields) {
|
|||
qDebug() << "PUT" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.put(m_authenticatedRequest, note.toJsonDocument().toJson());
|
||||
emit notesBusyChanged(true);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,7 +273,7 @@ void NotesApi::deleteNote(double noteId) {
|
|||
qDebug() << "DELETE" << url.toDisplayString();
|
||||
m_authenticatedRequest.setUrl(url);
|
||||
m_notesReplies << m_manager.deleteResource(m_authenticatedRequest);
|
||||
emit notesBusyChanged(true);
|
||||
emit busyChanged(true);
|
||||
}
|
||||
mp_model->removeNote(noteId);
|
||||
}
|
||||
|
@ -308,27 +318,24 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
|
|||
qDebug() << "Notes reply";
|
||||
}*/
|
||||
|
||||
if (m_loginReplies.contains(reply)) {
|
||||
if (reply == m_loginReply) {
|
||||
qDebug() << "Login reply";
|
||||
if (json.isObject())
|
||||
updateLoginFlow(json.object());
|
||||
//m_loginReplies.removeAll(reply);
|
||||
emit loginBusyChanged(loginBusy());
|
||||
m_loginReply = NULL;
|
||||
}
|
||||
else if (m_pollReplies.contains(reply)) {
|
||||
else if (reply == m_pollReply) {
|
||||
qDebug() << "Poll reply, finished";
|
||||
if (json.isObject())
|
||||
updateLoginCredentials(json.object());
|
||||
//m_pollReplies.removeAll(reply);
|
||||
m_pollReply = NULL;
|
||||
abortFlowV2Login();
|
||||
emit loginBusyChanged(loginBusy());
|
||||
}
|
||||
else if (m_statusReplies.contains(reply)) {
|
||||
else if (reply == m_statusReply) {
|
||||
qDebug() << "Status reply";
|
||||
if (json.isObject())
|
||||
updateStatus(json.object());
|
||||
//m_statusReplies.removeAll(reply);
|
||||
emit statusBusyChanged(statusBusy());
|
||||
m_statusReply = NULL;
|
||||
}
|
||||
else if (m_notesReplies.contains(reply)) {
|
||||
qDebug() << "Notes reply";
|
||||
|
@ -338,8 +345,8 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
|
|||
emit lastSyncChanged(m_lastSync);
|
||||
}
|
||||
}
|
||||
//m_notesReplies.removeAll(reply);
|
||||
emit notesBusyChanged(notesBusy());
|
||||
m_notesReplies.removeOne(reply);
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
else {
|
||||
qDebug() << "Unknown reply";
|
||||
|
@ -349,23 +356,22 @@ void NotesApi::replyFinished(QNetworkReply *reply) {
|
|||
else if (reply->error() == QNetworkReply::AuthenticationRequiredError) {
|
||||
emit error(AuthenticationError);
|
||||
}
|
||||
else {
|
||||
if (m_pollReplies.contains(reply)) {
|
||||
qDebug() << "Poll reply";
|
||||
//qDebug() << "Polling not finished yet" << m_pollUrl;
|
||||
}
|
||||
else if (m_statusReplies.contains(reply)) {
|
||||
updateStatus(QJsonObject());
|
||||
qDebug() << "Could not retreive status";
|
||||
else if (reply->error() == QNetworkReply::ContentNotFoundError && reply == m_pollReply) {
|
||||
qDebug() << "Polling not finished yet" << reply->url().toDisplayString();
|
||||
}
|
||||
else {
|
||||
if (reply == m_loginReply)
|
||||
m_loginReply = NULL;
|
||||
else if (reply == m_pollReply)
|
||||
m_pollReply = NULL;
|
||||
else if (reply == m_statusReply)
|
||||
m_statusReply = NULL;
|
||||
else if (m_notesReplies.contains(reply)) {
|
||||
m_notesReplies.removeOne(reply);
|
||||
emit busyChanged(busy());
|
||||
}
|
||||
emit error(CommunicationError);
|
||||
}
|
||||
}
|
||||
m_loginReplies.removeAll(reply);
|
||||
m_pollReplies.removeAll(reply);
|
||||
m_statusReplies.removeAll(reply);
|
||||
m_notesReplies.removeAll(reply);
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
|
@ -428,9 +434,11 @@ void NotesApi::updateStatus(const QJsonObject &status) {
|
|||
m_status_extendedSupport = status.value("extendedSupport").toBool();
|
||||
emit statusExtendedSupportChanged(m_status_extendedSupport);
|
||||
}
|
||||
m_statusStatus = RequestStatus::StatusFinished;
|
||||
emit statusStatusChanged(m_statusStatus);
|
||||
}
|
||||
|
||||
void NotesApi::updateLoginFlow(const QJsonObject &login) {
|
||||
bool NotesApi::updateLoginFlow(const QJsonObject &login) {
|
||||
QUrl url;
|
||||
QString token;
|
||||
if (!login.isEmpty()) {
|
||||
|
@ -448,15 +456,24 @@ void NotesApi::updateLoginFlow(const QJsonObject &login) {
|
|||
qDebug() << "Invalid Poll URL:" << url;
|
||||
}
|
||||
}
|
||||
|
||||
url = login.value("login").toString();
|
||||
if (m_loginUrl != url && url.isValid()) {
|
||||
if (m_loginUrl != url && url.isValid() && m_pollUrl.isValid() && !m_pollToken.isEmpty()) {
|
||||
m_loginUrl = url;
|
||||
m_loginStatus = RequestStatus::StatusBusy;
|
||||
emit loginUrlChanged(m_loginUrl);
|
||||
emit loginStatusChanged(m_loginStatus);
|
||||
m_loginPollTimer.start();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
abortFlowV2Login();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NotesApi::updateLoginCredentials(const QJsonObject &credentials) {
|
||||
bool NotesApi::updateLoginCredentials(const QJsonObject &credentials) {
|
||||
QString serverAddr;
|
||||
QString loginName;
|
||||
QString appPassword;
|
||||
|
@ -471,7 +488,14 @@ void NotesApi::updateLoginCredentials(const QJsonObject &credentials) {
|
|||
if (!appPassword.isEmpty() && appPassword != password())
|
||||
setPassword(appPassword);
|
||||
}
|
||||
if (!serverAddr.isEmpty() && !loginName.isEmpty() && !appPassword.isEmpty()) {
|
||||
qDebug() << "Login successfull for user" << loginName << "on" << serverAddr;
|
||||
m_loginStatus = RequestStatus::StatusFinished;
|
||||
emit loginStatusChanged(m_loginStatus);
|
||||
return true;
|
||||
}
|
||||
qDebug() << "Login failed for user" << loginName << "on" << serverAddr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString NotesApi::errorMessage(int error) const {
|
||||
|
|
|
@ -73,15 +73,14 @@ public:
|
|||
Q_PROPERTY(QDateTime lastSync READ lastSync NOTIFY lastSyncChanged)
|
||||
QDateTime lastSync() const { return m_lastSync; }
|
||||
|
||||
Q_PROPERTY(bool statusBusy READ statusBusy NOTIFY statusBusyChanged)
|
||||
bool statusBusy() const { return !m_statusReplies.empty(); }
|
||||
Q_PROPERTY(bool loginBusy READ loginBusy NOTIFY loginBusyChanged)
|
||||
bool loginBusy() const { return !m_loginReplies.empty() || !m_pollReplies.empty() || m_loginPollTimer.isActive(); }
|
||||
Q_PROPERTY(bool notesBusy READ notesBusy NOTIFY notesBusyChanged)
|
||||
bool notesBusy() const { return !m_notesReplies.empty(); }
|
||||
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
|
||||
bool busy() const { return statusBusy() | loginBusy() | notesBusy(); }
|
||||
bool busy() const { return !m_notesReplies.empty();; }
|
||||
|
||||
enum RequestStatus { StatusNone, StatusInitiated, StatusBusy, StatusFinished, StatusError };
|
||||
Q_ENUM(RequestStatus)
|
||||
|
||||
Q_PROPERTY(RequestStatus statusStatus READ statusStatus NOTIFY statusStatusChanged)
|
||||
RequestStatus statusStatus() const { return m_statusStatus; }
|
||||
Q_PROPERTY(bool statusInstalled READ statusInstalled NOTIFY statusInstalledChanged)
|
||||
bool statusInstalled() const { return m_status_installed; }
|
||||
Q_PROPERTY(bool statusMaintenance READ statusMaintenance NOTIFY statusMaintenanceChanged)
|
||||
|
@ -98,11 +97,14 @@ public:
|
|||
QString statusProductName() const { return m_status_productname; }
|
||||
Q_PROPERTY(bool statusExtendedSupport READ statusExtendedSupport NOTIFY statusExtendedSupportChanged)
|
||||
bool statusExtendedSupport() const { return m_status_extendedSupport; }
|
||||
|
||||
Q_PROPERTY(RequestStatus loginStatus READ loginStatus NOTIFY loginStatusChanged)
|
||||
RequestStatus loginStatus() const { return m_loginStatus; }
|
||||
Q_PROPERTY(QUrl loginUrl READ loginUrl NOTIFY loginUrlChanged)
|
||||
QUrl loginUrl() const { return m_loginUrl; }
|
||||
|
||||
Q_INVOKABLE void getStatus();
|
||||
Q_INVOKABLE void initiateFlowV2Login();
|
||||
Q_INVOKABLE bool getStatus();
|
||||
Q_INVOKABLE bool initiateFlowV2Login();
|
||||
Q_INVOKABLE void abortFlowV2Login();
|
||||
Q_INVOKABLE void getAllNotes(QStringList excludeFields = QStringList());
|
||||
Q_INVOKABLE void getNote(double noteId, QStringList excludeFields = QStringList());
|
||||
|
@ -136,10 +138,9 @@ signals:
|
|||
void dataFileChanged(QString dataFile);
|
||||
void networkAccessibleChanged(bool accessible);
|
||||
void lastSyncChanged(QDateTime lastSync);
|
||||
void statusBusyChanged(bool busy);
|
||||
void loginBusyChanged(bool busy);
|
||||
void notesBusyChanged(bool busy);
|
||||
void busyChanged(bool busy);
|
||||
|
||||
void statusStatusChanged(RequestStatus status);
|
||||
void statusInstalledChanged(bool installed);
|
||||
void statusMaintenanceChanged(bool maintenance);
|
||||
void statusNeedsDbUpgradeChanged(bool needsDbUpgrade);
|
||||
|
@ -148,6 +149,8 @@ signals:
|
|||
void statusEditionChanged(QString edition);
|
||||
void statusProductNameChanged(QString productName);
|
||||
void statusExtendedSupportChanged(bool extendedSupport);
|
||||
|
||||
void loginStatusChanged(RequestStatus status);
|
||||
void loginUrlChanged(QUrl url);
|
||||
void error(int error);
|
||||
|
||||
|
@ -174,8 +177,9 @@ private:
|
|||
|
||||
// Nextcloud status.php
|
||||
const QString m_statusEndpoint;
|
||||
QVector<QNetworkReply*> m_statusReplies;
|
||||
QNetworkReply* m_statusReply;
|
||||
void updateStatus(const QJsonObject &status);
|
||||
RequestStatus m_statusStatus;
|
||||
bool m_status_installed;
|
||||
bool m_status_maintenance;
|
||||
bool m_status_needsDbUpgrade;
|
||||
|
@ -187,10 +191,11 @@ private:
|
|||
|
||||
// Nextcloud Login Flow v2 - https://docs.nextcloud.com/server/18/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
|
||||
const QString m_loginEndpoint;
|
||||
QVector<QNetworkReply*> m_loginReplies;
|
||||
QVector<QNetworkReply*> m_pollReplies;
|
||||
void updateLoginFlow(const QJsonObject &login);
|
||||
void updateLoginCredentials(const QJsonObject &credentials);
|
||||
QNetworkReply* m_loginReply;
|
||||
QNetworkReply* m_pollReply;
|
||||
bool updateLoginFlow(const QJsonObject &login);
|
||||
bool updateLoginCredentials(const QJsonObject &credentials);
|
||||
RequestStatus m_loginStatus;
|
||||
QTimer m_loginPollTimer;
|
||||
QUrl m_loginUrl;
|
||||
QUrl m_pollUrl;
|
||||
|
|
Loading…
Reference in a new issue