Merge lp:~mzanetti/reminders-app/improve-error-handling into lp:reminders-app

Proposed by Michael Zanetti
Status: Merged
Approved by: Riccardo Padovani
Approved revision: 388
Merged at revision: 389
Proposed branch: lp:~mzanetti/reminders-app/improve-error-handling
Merge into: lp:reminders-app
Prerequisite: lp:~mzanetti/reminders-app/two-job-queues
Diff against target: 438 lines (+110/-78)
7 files modified
src/app/qml/components/StatusBar.qml (+1/-0)
src/libqtevernote/evernoteconnection.cpp (+39/-1)
src/libqtevernote/evernoteconnection.h (+3/-0)
src/libqtevernote/jobs/evernotejob.cpp (+9/-5)
src/libqtevernote/jobs/fetchnotejob.cpp (+2/-0)
src/libqtevernote/notesstore.cpp (+54/-72)
src/libqtevernote/notesstore.h (+2/-0)
To merge this branch: bzr merge lp:~mzanetti/reminders-app/improve-error-handling
Reviewer Review Type Date Requested Status
Riccardo Padovani Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+252227@code.launchpad.net

Commit message

improve some error handling

* Rate limit exceeded
* Upload quota exceeded
* Authentication expired

Description of the change

To test/reproduce the errors:

* Rate limit exceeded
Delete ~/.local/share/com.ubuntu.reminders/, connect to the evernote300 account, scroll from top to bottom and wait until everything is synced. Repeat twice and you'll run into the rate limit.

* Upload quota exceeded
On evernote website, go to account settings, see the quota status. Upload stuff until the quota is reached. Then on the phone, create a note and attach a picture. The app should show the quota warning.

* Authentication expired
With a properly set up account, go to the evernote webbsite -> settings -> applications, and delete the auth for the account. Then do something with the app.

Pleas try to create other error situations and let me know if you manage to get into some error state where we should show something to the user or that is otherwise unhandled correctly.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
388. By Michael Zanetti

update user errors in all callbacks

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Riccardo Padovani (rpadovani) wrote :

Looks good to me, but you discarded 4 gettext, why?

155 - message = gettext("Authentication expired.");
156 + message = "Authentication expired.";

review: Needs Information
Revision history for this message
Michael Zanetti (mzanetti) wrote :

> Looks good to me, but you discarded 4 gettext, why?
>
> 155 - message = gettext("Authentication expired.");
> 156 + message = "Authentication expired.";

Those are in the "EDAMSystemException" case. That one will only throw errors which are not to be presented to the user. The ones to be shown are in the EDAMUserException case. It's a bit odd that both have the same range of error codes, as I believe the "Authentication expired" error can never happen inside the SystemException block. Anyhow, as the api does in theory allow it, I kept the handling and still print this message to the debug output. It is not directly shown to the user any more though, so no need in making translators translate it.

Revision history for this message
Riccardo Padovani (rpadovani) wrote :

Okay, then looks good to me, thanks for the explanation!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/app/qml/components/StatusBar.qml'
2--- src/app/qml/components/StatusBar.qml 2015-03-04 00:23:45 +0000
3+++ src/app/qml/components/StatusBar.qml 2015-03-08 22:06:13 +0000
4@@ -38,6 +38,7 @@
5 id: label
6 width: parent.width - x
7 wrapMode: Text.WordWrap
8+ anchors.verticalCenter: parent.verticalCenter
9 }
10 }
11 }
12
13=== modified file 'src/libqtevernote/evernoteconnection.cpp'
14--- src/libqtevernote/evernoteconnection.cpp 2015-03-08 22:06:13 +0000
15+++ src/libqtevernote/evernoteconnection.cpp 2015-03-08 22:06:13 +0000
16@@ -38,6 +38,7 @@
17 #include <Errors_types.h>
18
19 #include <QUrl>
20+#include <QTime>
21
22 #include <libintl.h>
23
24@@ -65,6 +66,9 @@
25 m_userStoreHttpClient(0)
26 {
27 qRegisterMetaType<EvernoteConnection::ErrorCode>("EvernoteConnection::ErrorCode");
28+
29+ m_reconnectTimer.setSingleShot(true);
30+ connect(&m_reconnectTimer, &QTimer::timeout, this, &EvernoteConnection::connectToEvernote);
31 }
32
33 void EvernoteConnection::setupUserStore()
34@@ -148,6 +152,10 @@
35 void EvernoteConnection::disconnectFromEvernote()
36 {
37 qCDebug(dcConnection) << "Disconnecting from Evernote.";
38+
39+ m_errorMessage.clear();
40+ emit errorChanged();
41+
42 if (!isConnected()) {
43 qCWarning(dcConnection()) << "Not connected. Can't disconnect.";
44 return;
45@@ -314,6 +322,36 @@
46 emit errorChanged();
47 return false;
48 }
49+ } catch (const evernote::edam::EDAMUserException &e) {
50+ qCWarning(dcConnection) << "EDAMUserException getting note store path:" << e.what() << "EDAM Error Code:" << e.errorCode;
51+ switch (e.errorCode) {
52+ case evernote::edam::EDAMErrorCode::AUTH_EXPIRED:
53+ m_errorMessage = gettext("Authentication for Evernote server expired. Please renew login information in the accounts settings.");
54+ break;
55+ default:
56+ m_errorMessage = QString(gettext("Unknown error connecting to Evernote: %1")).arg(e.errorCode);
57+ break;
58+ }
59+ emit errorChanged();
60+ return false;
61+ } catch (const evernote::edam::EDAMSystemException &e) {
62+ qCWarning(dcConnection) << "EDAMSystemException getting note store path:" << e.what() << e.errorCode;
63+ switch (e.errorCode) {
64+ case evernote::edam::EDAMErrorCode::RATE_LIMIT_REACHED:
65+ m_errorMessage = gettext("Error connecting to Evernote: Rate limit exceeded. Please try again later.");
66+ m_reconnectTimer.stop();
67+ m_reconnectTimer.start(e.rateLimitDuration * 1000);
68+ {
69+ QTime time = QTime::fromMSecsSinceStartOfDay(e.rateLimitDuration * 1000);
70+ qCDebug(dcConnection) << "Cannot connect. Rate limit exceeded. Reconnecting in" << time.toString("mm:ss");
71+ }
72+ break;
73+ default:
74+ m_errorMessage = gettext("Unknown error connecting to Evernote: %1");
75+ break;
76+ }
77+ emit errorChanged();
78+ return false;
79 } catch (const TTransportException & e) {
80 qCWarning(dcConnection) << "Failed to fetch notestore path:" << e.what();
81 m_errorMessage = QString(gettext("Error connecting to Evernote: Connection failure when downloading server information."));
82@@ -321,7 +359,7 @@
83 return false;
84 } catch (const TException & e) {
85 qCWarning(dcConnection) << "Generic Thrift exception when fetching notestore path:" << e.what();
86- m_errorMessage = gettext("Unknown error connecting to Evernote");
87+ m_errorMessage = gettext("Unknown error connecting to Evernote.");
88 emit errorChanged();
89 return false;
90 }
91
92=== modified file 'src/libqtevernote/evernoteconnection.h'
93--- src/libqtevernote/evernoteconnection.h 2015-03-08 22:06:13 +0000
94+++ src/libqtevernote/evernoteconnection.h 2015-03-08 22:06:13 +0000
95@@ -28,6 +28,7 @@
96 #include <transport/THttpClient.h>
97
98 #include <QObject>
99+#include <QTimer>
100
101 namespace evernote {
102 namespace edam {
103@@ -141,6 +142,8 @@
104
105 evernote::edam::UserStoreClient *m_userstoreClient;
106 boost::shared_ptr<THttpClient> m_userStoreHttpClient;
107+
108+ QTimer m_reconnectTimer;
109 };
110
111 #endif // EVERNOTECONNECTION_H
112
113=== modified file 'src/libqtevernote/jobs/evernotejob.cpp'
114--- src/libqtevernote/jobs/evernotejob.cpp 2015-03-08 22:06:13 +0000
115+++ src/libqtevernote/jobs/evernotejob.cpp 2015-03-08 22:06:13 +0000
116@@ -100,6 +100,7 @@
117 }
118 } catch (const evernote::edam::EDAMUserException &e) {
119 QString message;
120+ EvernoteConnection::ErrorCode errorCode = EvernoteConnection::ErrorCodeUserException;
121 switch (e.errorCode) {
122 case evernote::edam::EDAMErrorCode::UNKNOWN:
123 message = "Unknown Error: %1";
124@@ -118,15 +119,18 @@
125 break;
126 case evernote::edam::EDAMErrorCode::LIMIT_REACHED:
127 message = "Limit reached: %1";
128+ errorCode = EvernoteConnection::ErrorCodeLimitExceeded;
129 break;
130 case evernote::edam::EDAMErrorCode::QUOTA_REACHED:
131 message = "Quota reached: %1";
132+ errorCode = EvernoteConnection::ErrorCodeQutaExceeded;
133 break;
134 case evernote::edam::EDAMErrorCode::INVALID_AUTH:
135 message = "Invalid auth: %1";
136 break;
137 case evernote::edam::EDAMErrorCode::AUTH_EXPIRED:
138 message = "Auth expired: %1";
139+ errorCode = EvernoteConnection::ErrorCodeAuthExpired;
140 break;
141 case evernote::edam::EDAMErrorCode::DATA_CONFLICT:
142 message = "Data conflict: %1";
143@@ -161,26 +165,26 @@
144 }
145 message = message.arg(QString::fromStdString(e.parameter));
146 qCWarning(dcJobQueue) << metaObject()->className() << "EDAMUserException:" << message;
147- emitJobDone(EvernoteConnection::ErrorCodeUserException, message);
148+ emitJobDone(errorCode, message);
149 } catch (const evernote::edam::EDAMSystemException &e) {
150 qCWarning(dcJobQueue) << "EDAMSystemException in" << metaObject()->className() << e.what() << e.errorCode << QString::fromStdString(e.message);
151 QString message;
152 EvernoteConnection::ErrorCode errorCode;
153 switch (e.errorCode) {
154 case evernote::edam::EDAMErrorCode::AUTH_EXPIRED:
155- message = gettext("Authentication expired.");
156+ message = "Authentication expired.";
157 errorCode = EvernoteConnection::ErrorCodeAuthExpired;
158 break;
159 case evernote::edam::EDAMErrorCode::LIMIT_REACHED:
160- message = gettext("Limit exceeded.");
161+ message = "Limit exceeded.";
162 errorCode = EvernoteConnection::ErrorCodeLimitExceeded;
163 break;
164 case evernote::edam::EDAMErrorCode::RATE_LIMIT_REACHED:
165- message = gettext("Rate limit exceeded.");
166+ message = "Rate limit exceeded.";
167 errorCode = EvernoteConnection::ErrorCodeRateLimitExceeded;
168 break;
169 case evernote::edam::EDAMErrorCode::QUOTA_REACHED:
170- message = gettext("Quota exceeded.");
171+ message = "Quota exceeded.";
172 errorCode = EvernoteConnection::ErrorCodeQutaExceeded;
173 break;
174 default:
175
176=== modified file 'src/libqtevernote/jobs/fetchnotejob.cpp'
177--- src/libqtevernote/jobs/fetchnotejob.cpp 2015-02-26 22:47:10 +0000
178+++ src/libqtevernote/jobs/fetchnotejob.cpp 2015-03-08 22:06:13 +0000
179@@ -53,6 +53,8 @@
180
181 void FetchNoteJob::startJob()
182 {
183+ // Just in case we error out, make sure the reply can be idenfied by note guid
184+ m_result.guid = m_guid.toStdString();
185 client()->getNote(m_result, token().toStdString(), m_guid.toStdString(), m_what == LoadContent, m_what == LoadResources, false, false);
186 }
187
188
189=== modified file 'src/libqtevernote/notesstore.cpp'
190--- src/libqtevernote/notesstore.cpp 2015-03-08 22:06:13 +0000
191+++ src/libqtevernote/notesstore.cpp 2015-03-08 22:06:13 +0000
192@@ -307,9 +307,11 @@
193
194 notebook->setLoading(false);
195
196+ handleUserError(errorCode);
197 if (errorCode != EvernoteConnection::ErrorCodeNoError) {
198 qCWarning(dcSync) << "Error creating notebook:" << errorMessage;
199 notebook->setSyncError(true);
200+ emit notebookChanged(notebook->guid());
201 return;
202 }
203 QString guid = QString::fromStdString(result.guid);
204@@ -503,6 +505,8 @@
205 }
206
207 tag->setLoading(false);
208+
209+ handleUserError(errorCode);
210 if (errorCode != EvernoteConnection::ErrorCodeNoError) {
211 qCWarning(dcSync) << "Error creating tag on server:" << errorMessage;
212 tag->setSyncError(true);
213@@ -540,6 +544,8 @@
214 return;
215 }
216 tag->setLoading(false);
217+
218+ handleUserError(errorCode);
219 if (errorCode != EvernoteConnection::ErrorCodeNoError) {
220 qCWarning(dcSync) << "Error updating tag on server" << errorMessage;
221 tag->setSyncError(true);
222@@ -625,26 +631,8 @@
223
224 void NotesStore::fetchNotesJobDone(EvernoteConnection::ErrorCode errorCode, const QString &errorMessage, const evernote::edam::NotesMetadataList &results, const QString &filterNotebookGuid)
225 {
226- switch (errorCode) {
227- case EvernoteConnection::ErrorCodeNoError:
228- // All is well...
229- break;
230- case EvernoteConnection::ErrorCodeUserException:
231- qCWarning(dcSync) << "FetchNotesJobDone: EDAMUserException:" << errorMessage;
232- m_loading = false;
233- emit loadingChanged();
234- return; // silently discarding...
235- case EvernoteConnection::ErrorCodeConnectionLost:
236- qCWarning(dcSync) << "FetchNotesJobDone: Connection with evernote lost:" << errorMessage;
237- m_loading = false;
238- emit loadingChanged();
239- return; // silently discarding...
240- case EvernoteConnection::ErrorCodeNotFoundExcpetion:
241- qCWarning(dcSync) << "FetchNotesJobDone: Item not found on server:" << errorMessage;
242- m_loading = false;
243- emit loadingChanged();
244- return; // silently discarding...
245- default:
246+ handleUserError(errorCode);
247+ if (errorCode != EvernoteConnection::ErrorCodeNoError) {
248 qCWarning(dcSync) << "FetchNotesJobDone: Failed to fetch notes list:" << errorMessage << errorCode;
249 m_loading = false;
250 emit loadingChanged();
251@@ -825,24 +813,10 @@
252 QModelIndex noteIndex = index(m_notes.indexOf(note));
253 QVector<int> roles;
254
255- switch (errorCode) {
256- case EvernoteConnection::ErrorCodeNoError:
257- // All is well
258- break;
259- case EvernoteConnection::ErrorCodeUserException:
260- qCWarning(dcSync) << "FetchNoteJobDone: EDAMUserException:" << errorMessage;
261- emit dataChanged(noteIndex, noteIndex, roles);
262- return; // silently discarding...
263- case EvernoteConnection::ErrorCodeConnectionLost:
264- qCWarning(dcSync) << "FetchNoteJobDone: Connection with evernote lost:" << errorMessage;
265- emit dataChanged(noteIndex, noteIndex, roles);
266- return; // silently discarding...
267- case EvernoteConnection::ErrorCodeNotFoundExcpetion:
268- qCWarning(dcSync) << "FetchNoteJobDone: Item not found on server:" << errorMessage;
269- emit dataChanged(noteIndex, noteIndex, roles);
270- return; // silently discarding...
271- default:
272- qCWarning(dcSync) << "FetchNoteJobDone: Failed to fetch note content:" << errorMessage << errorCode;
273+ handleUserError(errorCode);
274+ if (errorCode != EvernoteConnection::ErrorCodeNoError) {
275+ note->setLoading(false);
276+ roles << RoleLoading;
277 note->setSyncError(true);
278 roles << RoleSyncError;
279 emit dataChanged(noteIndex, noteIndex, roles);
280@@ -952,20 +926,10 @@
281 m_notebooksLoading = false;
282 emit notebooksLoadingChanged();
283
284- switch (errorCode) {
285- case EvernoteConnection::ErrorCodeNoError:
286- // All is well...
287- break;
288- case EvernoteConnection::ErrorCodeUserException:
289- qCWarning(dcSync) << "FetchNotebooksJobDone: EDAMUserException:" << errorMessage;
290- // silently discarding...
291- return;
292- case EvernoteConnection::ErrorCodeConnectionLost:
293- qCWarning(dcSync) << "FetchNotebooksJobDone: Connection lost:" << errorMessage;
294- return; // silently discarding
295- default:
296+ handleUserError(errorCode);
297+ if (errorCode != EvernoteConnection::ErrorCodeNoError) {
298 qCWarning(dcSync) << "FetchNotebooksJobDone: Failed to fetch notes list:" << errorMessage << errorCode;
299- return; // silently discarding
300+ return;
301 }
302
303 QList<Notebook*> unhandledNotebooks = m_notebooks;
304@@ -1064,20 +1028,10 @@
305 m_tagsLoading = false;
306 emit tagsLoadingChanged();
307
308- switch (errorCode) {
309- case EvernoteConnection::ErrorCodeNoError:
310- // All is well...
311- break;
312- case EvernoteConnection::ErrorCodeUserException:
313- qCWarning(dcSync) << "FetchTagsJobDone: EDAMUserException:" << errorMessage;
314- // silently discarding...
315- return;
316- case EvernoteConnection::ErrorCodeConnectionLost:
317- qCWarning(dcSync) << "FetchTagsJobDone: Connection lost:" << errorMessage;
318- return; // silently discarding
319- default:
320+ handleUserError(errorCode);
321+ if (errorCode != EvernoteConnection::ErrorCodeNoError) {
322 qCWarning(dcSync) << "FetchTagsJobDone: Failed to fetch notes list:" << errorMessage << errorCode;
323- return; // silently discarding
324+ return;
325 }
326
327 QHash<QString, Tag*> unhandledTags = m_tagsHash;
328@@ -1204,6 +1158,7 @@
329 note->setLoading(false);
330 roles << RoleLoading;
331
332+ handleUserError(errorCode);
333 if (errorCode != EvernoteConnection::ErrorCodeNoError) {
334 qCWarning(dcSync) << "Error creating note on server:" << tmpGuid << errorMessage;
335 note->setSyncError(true);
336@@ -1305,13 +1260,13 @@
337 int idx = m_notes.indexOf(note);
338 note->setLoading(false);
339
340+ handleUserError(errorCode);
341 if (errorCode != EvernoteConnection::ErrorCodeNoError) {
342- qCWarning(dcSync) << "Error saving note:" << errorMessage;
343+ qCWarning(dcSync) << "Unhandled error saving note:" << errorCode << "Message:" << errorMessage;
344 note->setSyncError(true);
345 emit dataChanged(index(idx), index(idx), QVector<int>() << RoleLoading << RoleSyncError);
346 return;
347 }
348- note->setSyncError(false);
349
350 note->setUpdateSequenceNumber(result.updateSequenceNum);
351 note->setLastSyncedSequenceNumber(result.updateSequenceNum);
352@@ -1328,19 +1283,25 @@
353
354 void NotesStore::saveNotebookJobDone(EvernoteConnection::ErrorCode errorCode, const QString &errorMessage, const evernote::edam::Notebook &result)
355 {
356- if (errorCode != EvernoteConnection::ErrorCodeNoError) {
357- qCWarning(dcSync) << "Error saving notebook to server" << errorMessage;
358- return;
359- }
360-
361 Notebook *notebook = m_notebooksHash.value(QString::fromStdString(result.guid));
362 if (!notebook) {
363 qCWarning(dcSync) << "Save notebook job done but notebook can't be found any more!";
364 return;
365 }
366+
367+ handleUserError(errorCode);
368+ if (errorCode != EvernoteConnection::ErrorCodeNoError) {
369+ qCWarning(dcSync) << "Error saving notebook to server" << errorCode << errorMessage;
370+ notebook->setSyncError(true);
371+ emit notebookChanged(notebook->guid());
372+ return;
373+ }
374+
375+ notebook->setLoading(false);
376+ notebook->setSyncError(false);
377+
378 qCDebug(dcSync) << "Notebooks saved to server:" << notebook->guid();
379 updateFromEDAM(result, notebook);
380- notebook->setLoading(false);
381 emit notebookChanged(notebook->guid());
382 syncToCacheFile(notebook);
383 }
384@@ -1411,6 +1372,7 @@
385
386 void NotesStore::deleteNoteJobDone(EvernoteConnection::ErrorCode errorCode, const QString &errorMessage, const QString &guid)
387 {
388+ handleUserError(errorCode);
389 if (errorCode != EvernoteConnection::ErrorCodeNoError) {
390 qCWarning(dcSync) << "Cannot delete note from server:" << errorMessage;
391 return;
392@@ -1431,6 +1393,7 @@
393
394 void NotesStore::expungeNotebookJobDone(EvernoteConnection::ErrorCode errorCode, const QString &errorMessage, const QString &guid)
395 {
396+ handleUserError(errorCode);
397 if (errorCode != EvernoteConnection::ErrorCodeNoError) {
398 qCWarning(dcSync) << "Error expunging notebook:" << errorMessage;
399 return;
400@@ -1644,6 +1607,25 @@
401 notebook->setLastSyncedSequenceNumber(evNotebook.updateSequenceNum);
402 }
403
404+bool NotesStore::handleUserError(EvernoteConnection::ErrorCode errorCode)
405+{
406+ switch (errorCode) {
407+ case EvernoteConnection::ErrorCodeAuthExpired:
408+ m_errorQueue.append(gettext("Authentication for Evernote server expired. Please renew login information in the accounts settings."));
409+ break;
410+ case EvernoteConnection::ErrorCodeLimitExceeded:
411+ m_errorQueue.append(gettext("Rate limit for Evernote server exceeded. Please try again later."));
412+ break;
413+ case EvernoteConnection::ErrorCodeQutaExceeded:
414+ m_errorQueue.append(gettext("Upload quota for Evernote server exceed. Please try again later."));
415+ break;
416+ default:
417+ return false;
418+ }
419+ emit errorChanged();
420+ return true;
421+}
422+
423
424 void NotesStore::expungeTag(const QString &guid)
425 {
426
427=== modified file 'src/libqtevernote/notesstore.h'
428--- src/libqtevernote/notesstore.h 2015-03-04 00:23:45 +0000
429+++ src/libqtevernote/notesstore.h 2015-03-08 22:06:13 +0000
430@@ -203,6 +203,8 @@
431 QVector<int> updateFromEDAM(const evernote::edam::NoteMetadata &evNote, Note *note);
432 void updateFromEDAM(const evernote::edam::Notebook &evNotebook, Notebook *notebook);
433
434+ bool handleUserError(EvernoteConnection::ErrorCode errorCode);
435+
436 private:
437 explicit NotesStore(QObject *parent = 0);
438 static NotesStore *s_instance;

Subscribers

People subscribed via source and target branches