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

Proposed by Michael Zanetti
Status: Merged
Approved by: Michael Zanetti
Approved revision: 14
Merged at revision: 15
Proposed branch: lp:~mzanetti/reminders-app/rework-error-handling
Merge into: lp:reminders-app
Prerequisite: lp:~mzanetti/reminders-app/use-ssl
Diff against target: 877 lines (+292/-250)
17 files modified
src/plugin/Evernote/jobs/createnotejob.cpp (+25/-31)
src/plugin/Evernote/jobs/createnotejob.h (+11/-5)
src/plugin/Evernote/jobs/deletenotejob.cpp (+8/-9)
src/plugin/Evernote/jobs/deletenotejob.h (+5/-3)
src/plugin/Evernote/jobs/evernotejob.cpp (+45/-14)
src/plugin/Evernote/jobs/evernotejob.h (+21/-2)
src/plugin/Evernote/jobs/fetchnotebooksjob.cpp (+8/-12)
src/plugin/Evernote/jobs/fetchnotebooksjob.h (+8/-3)
src/plugin/Evernote/jobs/fetchnotejob.cpp (+6/-16)
src/plugin/Evernote/jobs/fetchnotejob.h (+8/-2)
src/plugin/Evernote/jobs/fetchnotesjob.cpp (+6/-13)
src/plugin/Evernote/jobs/fetchnotesjob.h (+6/-3)
src/plugin/Evernote/jobs/savenotejob.cpp (+26/-32)
src/plugin/Evernote/jobs/savenotejob.h (+11/-3)
src/plugin/Evernote/note.cpp (+1/-1)
src/plugin/Evernote/notesstore.cpp (+71/-93)
src/plugin/Evernote/notesstore.h (+26/-8)
To merge this branch: bzr merge lp:~mzanetti/reminders-app/rework-error-handling
Reviewer Review Type Date Requested Status
Victor Thompson (community) Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
David Planella Approve
Review via email: mp+196987@code.launchpad.net

Commit message

rework error handling and job queue processing.

Description of the change

This reworks how jobs report their results. EvernoteJob.cpp does now all the error handling and tells the job what result code to emit. Creating a new threaded API call now is a matter of copying an existing job and adjusting 2 lines of code.

Along with that I simplified the usage of the job queue. Just create a new job, call enqueue(job) and connect to the jobDone() signal for obtaining results. No need for taking care about processing the queue any more.

Last but not least the simplified error handling made it easy to add a workaround for the connection drops. EvernoteJob.cpp now gives it a second try in case the connection dropped by reopening the connection and starting the job again. In my tests that successfully recovered from 100% of the drops.

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)
Revision history for this message
Victor Thompson (vthompson) wrote :

1. In notes.h, I believe you'll want to include QQmlParserStatus in a Q_INTERFACES macro.
2. I see you're reworking some of the connection error exception handling. I get the following core dump every so often, I wonder if it's related to an error that isn't being handled.

terminate called after throwing an instance of 'apache::thrift::transport::TSSLException'
  what(): SSL_write: errno = 110
Aborted (core dumped)

3. Overall the changes to job handling and error handling look good--is there any particular error path that's being fixed that I might be able to try to reproduce? It's a bit hard to try to verify an overall rework of exception handling.

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

> 1. In notes.h, I believe you'll want to include QQmlParserStatus in a
> Q_INTERFACES macro.

Hmm... Why is that?

> 2. I see you're reworking some of the connection error exception handling. I
> get the following core dump every so often, I wonder if it's related to an
> error that isn't being handled.
>
> terminate called after throwing an instance of
> 'apache::thrift::transport::TSSLException'
> what(): SSL_write: errno = 110
> Aborted (core dumped)

Interesting. I intentionally only caught the exceptions I could see myself in order to learn about libthrift's behavior. I haven't ever gotten this one. I'll add catching that one and also all the others that can happen in theory as I've seen enough of libthrift by now.

Still, if you could tell me how to reproduce this I could check if there is something else I'm doing wrong and try to prevent it from happening instead of just catching and ignoring.

>
> 3. Overall the changes to job handling and error handling look good--is there
> any particular error path that's being fixed that I might be able to try to
> reproduce? It's a bit hard to try to verify an overall rework of exception
> handling.

Nope, not really. The main reason for this commit is that I didn't want to add the try {} catch() block around every single call to the evernote SDK. So this now offers one place to catch them all and still pass the error code back to the job in a Qt style manner (As Qt intentionally does not use exceptions by design).

Revision history for this message
David Planella (dpm) wrote :

>> 1. In notes.h, I believe you'll want to include QQmlParserStatus in a
>> Q_INTERFACES macro.

> Hmm... Why is that?

I'm not familiar with this, so take this as a pointer rather than an suggestion, but my guess would be as it's recommended at http://qt-project.org/doc/qt-5.0/qtqml/qqmlparserstatus.html

In any case, I don't see this affecting this MP.

Revision history for this message
David Planella (dpm) wrote :

I cannot reproduce Victor's core dump after several run tests. Otherwise, LGTM.

In any case, Victor, if you could tell us how to reproduce, we can come back to it.

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

> >> 1. In notes.h, I believe you'll want to include QQmlParserStatus in a
> >> Q_INTERFACES macro.
>
> > Hmm... Why is that?
>
> I'm not familiar with this, so take this as a pointer rather than an
> suggestion, but my guess would be as it's recommended at http://qt-
> project.org/doc/qt-5.0/qtqml/qqmlparserstatus.html
>
> In any case, I don't see this affecting this MP.

Oh... now I get it... Yeah. in this merge notes.h still inherits QQmlParserStatus. This has been changed in a newer one. If you want me I can add it in here for completeness, but it would be removed again with the next merge. Sorry for the confusion.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) :
review: Approve (continuous-integration)
Revision history for this message
Victor Thompson (vthompson) wrote :

I haven't been able to reproduce. To me it seems like the core happened after letting the app idle while I was reviewing the code. So maybe it was related to the connection timing out.

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

Thanks Victor.

I'll pay extra attention to this.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/plugin/Evernote/jobs/createnotejob.cpp'
2--- src/plugin/Evernote/jobs/createnotejob.cpp 2013-11-26 17:18:33 +0000
3+++ src/plugin/Evernote/jobs/createnotejob.cpp 2013-11-27 21:48:53 +0000
4@@ -22,36 +22,30 @@
5
6 #include <QDebug>
7
8-CreateNoteJob::CreateNoteJob(Note *note, QObject *parent) :
9+CreateNoteJob::CreateNoteJob(const QString &title, const QString &notebookGuid, const QString &content, QObject *parent) :
10 EvernoteJob(parent),
11- m_note(note)
12-{
13-}
14-
15-void CreateNoteJob::run()
16-{
17- NotesStore::ErrorCode errorCode = NotesStore::ErrorCodeNoError;
18-
19- try {
20- evernote::edam::Note input;
21- input.title = m_note->title().toStdString();
22- input.__isset.title = true;
23- input.notebookGuid = m_note->notebookGuid().toStdString();
24- input.__isset.notebookGuid = true;
25- input.content = m_note->content().toStdString();
26- input.__isset.content = true;
27- input.contentLength = m_note->content().length();
28- input.__isset.contentLength = true;
29-
30- evernote::edam::Note result;
31- client()->createNote(result, token().toStdString(), input);
32-
33- m_note->setGuid(QString::fromStdString(result.guid));
34-
35- } catch(evernote::edam::EDAMUserException e) {
36- errorCode = NotesStore::ErrorCodeUserException;
37- } catch(evernote::edam::EDAMSystemException) {
38- errorCode = NotesStore::ErrorCodeSystemException;
39- }
40- emit resultReady(errorCode, m_note);
41+ m_title(title),
42+ m_notebookGuid(notebookGuid),
43+ m_content(content)
44+{
45+}
46+
47+void CreateNoteJob::startJob()
48+{
49+ evernote::edam::Note input;
50+ input.title = m_title.toStdString();
51+ input.__isset.title = true;
52+ input.notebookGuid = m_notebookGuid.toStdString();
53+ input.__isset.notebookGuid = true;
54+ input.content = m_content.toStdString();
55+ input.__isset.content = true;
56+ input.contentLength = m_content.length();
57+ input.__isset.contentLength = true;
58+
59+ client()->createNote(m_resultNote, token().toStdString(), input);
60+}
61+
62+void CreateNoteJob::emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage)
63+{
64+ emit jobDone(errorCode, errorMessage, m_resultNote);
65 }
66
67=== modified file 'src/plugin/Evernote/jobs/createnotejob.h'
68--- src/plugin/Evernote/jobs/createnotejob.h 2013-11-25 00:49:48 +0000
69+++ src/plugin/Evernote/jobs/createnotejob.h 2013-11-27 21:48:53 +0000
70@@ -8,15 +8,21 @@
71 {
72 Q_OBJECT
73 public:
74- explicit CreateNoteJob(Note *note, QObject *parent = 0);
75-
76- void run();
77+ explicit CreateNoteJob(const QString &title, const QString &notebookGuid, const QString &content, QObject *parent = 0);
78
79 signals:
80- void resultReady(NotesStore::ErrorCode errorCode, Note *note);
81+ void jobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage, evernote::edam::Note note);
82+
83+protected:
84+ void startJob();
85+ void emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage);
86
87 private:
88- Note *m_note;
89+ QString m_title;
90+ QString m_notebookGuid;
91+ QString m_content;
92+
93+ evernote::edam::Note m_resultNote;
94 };
95
96 #endif // CREATENOTEJOB_H
97
98=== modified file 'src/plugin/Evernote/jobs/deletenotejob.cpp'
99--- src/plugin/Evernote/jobs/deletenotejob.cpp 2013-11-26 17:18:33 +0000
100+++ src/plugin/Evernote/jobs/deletenotejob.cpp 2013-11-27 21:48:53 +0000
101@@ -26,13 +26,12 @@
102 {
103 }
104
105-void DeleteNoteJob::run()
106-{
107- NotesStore::ErrorCode errorCode = NotesStore::ErrorCodeNoError;
108- try {
109- client()->deleteNote(token().toStdString(), m_guid.toStdString());
110- } catch(...) {
111- catchTransportException();
112- }
113- emit resultReady(errorCode, m_guid);
114+void DeleteNoteJob::startJob()
115+{
116+ client()->deleteNote(token().toStdString(), m_guid.toStdString());
117+}
118+
119+void DeleteNoteJob::emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage)
120+{
121+ emit jobDone(errorCode, errorMessage, m_guid);
122 }
123
124=== modified file 'src/plugin/Evernote/jobs/deletenotejob.h'
125--- src/plugin/Evernote/jobs/deletenotejob.h 2013-11-25 00:49:48 +0000
126+++ src/plugin/Evernote/jobs/deletenotejob.h 2013-11-27 21:48:53 +0000
127@@ -9,10 +9,12 @@
128 public:
129 DeleteNoteJob(const QString &guid, QObject *parent = 0);
130
131- void run();
132-
133 signals:
134- void resultReady(NotesStore::ErrorCode errorCode, const QString &guid);
135+ void jobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage, const QString &guid);
136+
137+protected:
138+ void startJob();
139+ void emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage);
140
141 private:
142 QString m_guid;
143
144=== modified file 'src/plugin/Evernote/jobs/evernotejob.cpp'
145--- src/plugin/Evernote/jobs/evernotejob.cpp 2013-11-26 17:18:33 +0000
146+++ src/plugin/Evernote/jobs/evernotejob.cpp 2013-11-27 21:48:53 +0000
147@@ -38,11 +38,55 @@
148 m_token(NotesStore::instance()->token())
149 {
150 connect(this, &EvernoteJob::finished, this, &EvernoteJob::deleteLater);
151+ connect(this, &EvernoteJob::finished, NotesStore::instance(), &NotesStore::startNextJob);
152 }
153
154 EvernoteJob::~EvernoteJob()
155 {
156-
157+}
158+
159+void EvernoteJob::run()
160+{
161+ if (m_token.isEmpty()) {
162+ qWarning() << "No token set. Cannot execute job.";
163+ emitJobDone(NotesStore::ErrorCodeUserException, QStringLiteral("No token set."));
164+ return;
165+ }
166+
167+ try {
168+ startJob();
169+
170+ } catch (const TTransportException & e) {
171+
172+ // The connection broke down. libthrift + evernote servers seem to be quite flaky
173+ // so lets try to start the job once more.
174+ qWarning() << "Got a transport exception:" << e.what() << ". Trying to reestablish connection...";
175+ try {
176+ NotesStore::instance()->m_httpClient->close();
177+ NotesStore::instance()->m_httpClient->open();
178+
179+ startJob();
180+ } catch (const TTransportException &e) {
181+ // Giving up... the connection seems to be down for real.
182+ qWarning() << "Cannot reestablish connection:" << e.what();
183+ emitJobDone(NotesStore::ErrorCodeConnectionLost, e.what());
184+ } catch (const evernote::edam::EDAMUserException &e) {
185+ emitJobDone(NotesStore::ErrorCodeUserException, e.what());
186+ } catch (const evernote::edam::EDAMSystemException &e) {
187+ emitJobDone(NotesStore::ErrorCodeSystemException, e.what());
188+ } catch (const evernote::edam::EDAMNotFoundException &e) {
189+ emitJobDone(NotesStore::ErrorCodeNotFoundExcpetion, e.what());
190+ }
191+
192+ } catch (const evernote::edam::EDAMUserException &e) {
193+ emitJobDone(NotesStore::ErrorCodeUserException, e.what());
194+ } catch (const evernote::edam::EDAMSystemException &e) {
195+ emitJobDone(NotesStore::ErrorCodeSystemException, e.what());
196+ } catch (const evernote::edam::EDAMNotFoundException &e) {
197+ emitJobDone(NotesStore::ErrorCodeNotFoundExcpetion, e.what());
198+ }
199+
200+ emitJobDone(NotesStore::ErrorCodeNoError, QString());
201 }
202
203 evernote::edam::NoteStoreClient *EvernoteJob::client()
204@@ -54,16 +98,3 @@
205 {
206 return m_token;
207 }
208-
209-void EvernoteJob::catchTransportException()
210-{
211- try {
212- // this function is meant to be called from a catch block
213- // rethrow the exception to catch it again
214- throw;
215- } catch (const TTransportException & e) {
216- qDebug() << e.what();
217- } catch (const TException & e) {
218- qDebug() << e.what();
219- }
220-}
221
222=== modified file 'src/plugin/Evernote/jobs/evernotejob.h'
223--- src/plugin/Evernote/jobs/evernotejob.h 2013-11-25 00:49:48 +0000
224+++ src/plugin/Evernote/jobs/evernotejob.h 2013-11-27 21:48:53 +0000
225@@ -10,6 +10,19 @@
226
227 #include <QThread>
228
229+/* How to create a new Job type:
230+ * - Subclass EvernoteJob
231+ * - Implement startJob() in which you do the call to evernote.
232+ * - No need to catch exceptions, EvernoteJob will deal with those.
233+ * - Define a jobDone() signal with the result parameters you need.
234+ * - Keep the convention of jobDone(NotesStore::ErrorCode errorCode, const QString &message [, ...])
235+ * - Emit jobDone() in your implementation of emitJobDone().
236+ * - NOTE: emitJobDone() might be called with an error even before startJob() is triggered.
237+ *
238+ * Jobs can be enqueue()d in NotesStore.
239+ * They will destroy themselves when finished.
240+ * The jobqueue will take care about starting them.
241+ */
242 class EvernoteJob : public QThread
243 {
244 Q_OBJECT
245@@ -17,12 +30,18 @@
246 explicit EvernoteJob(QObject *parent = 0);
247 virtual ~EvernoteJob();
248
249+ void run() final;
250+
251+signals:
252+ void connectionLost(const QString &errorMessage);
253+
254 protected:
255+ virtual void startJob() = 0;
256+ virtual void emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage) = 0;
257+
258 evernote::edam::NoteStoreClient* client();
259 QString token();
260
261- void catchTransportException();
262-
263 private:
264 QString m_token;
265 };
266
267=== modified file 'src/plugin/Evernote/jobs/fetchnotebooksjob.cpp'
268--- src/plugin/Evernote/jobs/fetchnotebooksjob.cpp 2013-11-26 17:18:33 +0000
269+++ src/plugin/Evernote/jobs/fetchnotebooksjob.cpp 2013-11-27 21:48:53 +0000
270@@ -28,16 +28,12 @@
271 }
272
273
274-void FetchNotebooksJob::run()
275-{
276- NotesStore::ErrorCode errorCode = NotesStore::ErrorCodeNoError;
277- std::vector<evernote::edam::Notebook> results;
278- try {
279- client()->listNotebooks(results, token().toStdString());
280- } catch(evernote::edam::EDAMUserException) {
281- errorCode = NotesStore::ErrorCodeUserException;
282- } catch(evernote::edam::EDAMSystemException) {
283- errorCode = NotesStore::ErrorCodeSystemException;
284- }
285- emit resultReady(errorCode, results);
286+void FetchNotebooksJob::startJob()
287+{
288+ client()->listNotebooks(m_results, token().toStdString());
289+}
290+
291+void FetchNotebooksJob::emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage)
292+{
293+ emit jobDone(errorCode, errorMessage, m_results);
294 }
295
296=== modified file 'src/plugin/Evernote/jobs/fetchnotebooksjob.h'
297--- src/plugin/Evernote/jobs/fetchnotebooksjob.h 2013-11-25 00:49:48 +0000
298+++ src/plugin/Evernote/jobs/fetchnotebooksjob.h 2013-11-27 21:48:53 +0000
299@@ -9,10 +9,15 @@
300 public:
301 explicit FetchNotebooksJob(QObject *parent = 0);
302
303- void run();
304-
305 signals:
306- void resultReady(NotesStore::ErrorCode errorCode, const std::vector<evernote::edam::Notebook> &results);
307+ void jobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage, const std::vector<evernote::edam::Notebook> &results);
308+
309+protected:
310+ void startJob();
311+ void emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage);
312+
313+private:
314+ std::vector<evernote::edam::Notebook> m_results;
315 };
316
317 #endif // FETCHNOTEBOOKSJOB_H
318
319=== modified file 'src/plugin/Evernote/jobs/fetchnotejob.cpp'
320--- src/plugin/Evernote/jobs/fetchnotejob.cpp 2013-11-26 17:18:33 +0000
321+++ src/plugin/Evernote/jobs/fetchnotejob.cpp 2013-11-27 21:48:53 +0000
322@@ -26,22 +26,12 @@
323 {
324 }
325
326-void FetchNoteJob::run()
327+void FetchNoteJob::startJob()
328 {
329- NotesStore::ErrorCode errorCode = NotesStore::ErrorCodeNoError;
330- evernote::edam::Note result;
331- try {
332- client()->getNote(result, token().toStdString(), m_guid.toStdString(), true, true, false, false);
333- } catch (evernote::edam::EDAMUserException) {
334- errorCode = NotesStore::ErrorCodeUserException;
335- } catch (evernote::edam::EDAMSystemException) {
336- errorCode = NotesStore::ErrorCodeSystemException;
337- } catch (evernote::edam::EDAMNotFoundException) {
338- errorCode = NotesStore::ErrorCodeNotFoundExcpetion;
339- } catch (...) {
340- catchTransportException();
341- errorCode = NotesStore::ErrorCodeConnectionLost;
342- }
343+ client()->getNote(m_result, token().toStdString(), m_guid.toStdString(), true, true, false, false);
344+}
345
346- emit resultReady(errorCode, result);
347+void FetchNoteJob::emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage)
348+{
349+ emit resultReady(errorCode, errorMessage, m_result);
350 }
351
352=== modified file 'src/plugin/Evernote/jobs/fetchnotejob.h'
353--- src/plugin/Evernote/jobs/fetchnotejob.h 2013-11-25 00:49:48 +0000
354+++ src/plugin/Evernote/jobs/fetchnotejob.h 2013-11-27 21:48:53 +0000
355@@ -9,14 +9,20 @@
356 public:
357 explicit FetchNoteJob(const QString &guid, QObject *parent = 0);
358
359- void run();
360 signals:
361- void resultReady(NotesStore::ErrorCode error, const evernote::edam::Note &note);
362+ void resultReady(NotesStore::ErrorCode error, const QString &errorMessage, const evernote::edam::Note &note);
363+
364+protected:
365+ void startJob();
366+ void emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage);
367
368 private:
369 evernote::edam::NoteStoreClient *m_client;
370 QString m_token;
371 QString m_guid;
372+
373+ evernote::edam::Note m_result;
374+
375 };
376
377 #endif // FETCHNOTEJOB_H
378
379=== modified file 'src/plugin/Evernote/jobs/fetchnotesjob.cpp'
380--- src/plugin/Evernote/jobs/fetchnotesjob.cpp 2013-11-26 17:18:33 +0000
381+++ src/plugin/Evernote/jobs/fetchnotesjob.cpp 2013-11-27 21:48:53 +0000
382@@ -30,7 +30,7 @@
383 {
384 }
385
386-void FetchNotesJob::run()
387+void FetchNotesJob::startJob()
388 {
389 // TODO: fix start/end (use smaller chunks and continue fetching if there are more notes available)
390 int32_t start = 0;
391@@ -48,17 +48,10 @@
392 resultSpec.includeTitle = true;
393 resultSpec.__isset.includeTitle = true;
394
395- NotesStore::ErrorCode errorCode = NotesStore::ErrorCodeNoError;
396- evernote::edam::NotesMetadataList results;
397+ client()->findNotesMetadata(m_results, token().toStdString(), filter, start, end, resultSpec);
398+}
399
400- try {
401- client()->findNotesMetadata(results, token().toStdString(), filter, start, end, resultSpec);
402- } catch(evernote::edam::EDAMUserException) {
403- errorCode = NotesStore::ErrorCodeUserException;
404- } catch(evernote::edam::EDAMSystemException) {
405- errorCode = NotesStore::ErrorCodeSystemException;
406- } catch(evernote::edam::EDAMNotFoundException) {
407- errorCode = NotesStore::ErrorCodeNotFoundExcpetion;
408- }
409- emit resultReady(errorCode, results);
410+void FetchNotesJob::emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage)
411+{
412+ emit jobDone(errorCode, errorMessage, m_results);
413 }
414
415=== modified file 'src/plugin/Evernote/jobs/fetchnotesjob.h'
416--- src/plugin/Evernote/jobs/fetchnotesjob.h 2013-11-25 00:49:48 +0000
417+++ src/plugin/Evernote/jobs/fetchnotesjob.h 2013-11-27 21:48:53 +0000
418@@ -9,13 +9,16 @@
419 public:
420 explicit FetchNotesJob(const QString &filterNotebookGuid, QObject *parent = 0);
421
422- void run();
423-
424 signals:
425- void resultReady(NotesStore::ErrorCode errorCode, const evernote::edam::NotesMetadataList &results);
426+ void jobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage, const evernote::edam::NotesMetadataList &results);
427+
428+protected:
429+ void startJob();
430+ void emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage);
431
432 private:
433 QString m_filterNotebookGuid;
434+ evernote::edam::NotesMetadataList m_results;
435 };
436
437 #endif // FETCHNOTESJOB_H
438
439=== modified file 'src/plugin/Evernote/jobs/savenotejob.cpp'
440--- src/plugin/Evernote/jobs/savenotejob.cpp 2013-11-26 17:18:33 +0000
441+++ src/plugin/Evernote/jobs/savenotejob.cpp 2013-11-27 21:48:53 +0000
442@@ -25,36 +25,30 @@
443
444 SaveNoteJob::SaveNoteJob(Note *note, QObject *parent) :
445 EvernoteJob(parent),
446- m_note(note)
447-{
448-}
449-
450-void SaveNoteJob::run()
451-{
452- NotesStore::ErrorCode errorCode = NotesStore::ErrorCodeNoError;
453- try {
454- evernote::edam::Note note;
455- note.guid = m_note->guid().toStdString();
456- note.__isset.guid = true;
457- note.title = m_note->title().toStdString();
458- note.__isset.title = true;
459- note.notebookGuid = m_note->notebookGuid().toStdString();
460- note.__isset.notebookGuid = true;
461- note.content = m_note->content().toStdString();
462- note.__isset.content = true;
463- note.contentLength = m_note->content().length();
464-
465- client()->updateNote(note, token().toStdString(), note);
466-
467- } catch (evernote::edam::EDAMUserException e) {
468- errorCode = NotesStore::ErrorCodeUserException;
469- qDebug() << QString::fromStdString(e.parameter);
470- } catch (evernote::edam::EDAMSystemException) {
471- errorCode = NotesStore::ErrorCodeSystemException;
472- } catch (...) {
473- catchTransportException();
474- errorCode = NotesStore::ErrorCodeConnectionLost;
475- }
476-
477- emit resultReady(errorCode, m_note);
478+ m_guid(note->guid()),
479+ m_title(note->title()),
480+ m_notebookGuid(note->notebookGuid()),
481+ m_content(note->content())
482+{
483+}
484+
485+void SaveNoteJob::startJob()
486+{
487+ evernote::edam::Note note;
488+ note.guid = m_guid.toStdString();
489+ note.__isset.guid = true;
490+ note.title = m_title.toStdString();
491+ note.__isset.title = true;
492+ note.notebookGuid = m_notebookGuid.toStdString();
493+ note.__isset.notebookGuid = true;
494+ note.content = m_content.toStdString();
495+ note.__isset.content = true;
496+ note.contentLength = m_content.length();
497+
498+ client()->updateNote(m_note, token().toStdString(), note);
499+}
500+
501+void SaveNoteJob::emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage)
502+{
503+ emit jobDone(errorCode, errorMessage, m_note);
504 }
505
506=== modified file 'src/plugin/Evernote/jobs/savenotejob.h'
507--- src/plugin/Evernote/jobs/savenotejob.h 2013-11-25 00:49:48 +0000
508+++ src/plugin/Evernote/jobs/savenotejob.h 2013-11-27 21:48:53 +0000
509@@ -9,12 +9,20 @@
510 public:
511 explicit SaveNoteJob(Note *note, QObject *parent = 0);
512
513- void run();
514 signals:
515- void resultReady(NotesStore::ErrorCode errorCode, Note *note);
516+ void jobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage, const evernote::edam::Note &note);
517+
518+protected:
519+ void startJob();
520+ void emitJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage);
521
522 private:
523- Note *m_note;
524+ QString m_guid;
525+ QString m_title;
526+ QString m_notebookGuid;
527+ QString m_content;
528+
529+ evernote::edam::Note m_note;
530 };
531
532 #endif // SAVENOTEJOB_H
533
534=== modified file 'src/plugin/Evernote/note.cpp'
535--- src/plugin/Evernote/note.cpp 2013-11-26 17:18:33 +0000
536+++ src/plugin/Evernote/note.cpp 2013-11-27 21:48:53 +0000
537@@ -35,7 +35,7 @@
538
539 void Note::setGuid(const QString &guid)
540 {
541- if (m_guid == guid) {
542+ if (m_guid != guid) {
543 m_guid = guid;
544 emit guidChanged();
545 }
546
547=== modified file 'src/plugin/Evernote/notesstore.cpp'
548--- src/plugin/Evernote/notesstore.cpp 2013-11-27 21:48:53 +0000
549+++ src/plugin/Evernote/notesstore.cpp 2013-11-27 21:48:53 +0000
550@@ -77,12 +77,12 @@
551 }
552
553 boost::shared_ptr<TBufferedTransport> bufferedTransport(new TBufferedTransport(socket));
554- boost::shared_ptr<THttpClient> userStoreHttpClient (new THttpClient(bufferedTransport,
555+ m_httpClient = boost::shared_ptr<THttpClient>(new THttpClient(bufferedTransport,
556 EVERNOTE_HOST.toStdString(),
557 EDAM_USER_STORE_PATH.toStdString()));
558- userStoreHttpClient->open();
559+ m_httpClient->open();
560
561- boost::shared_ptr<TProtocol> iprot(new TBinaryProtocol(userStoreHttpClient));
562+ boost::shared_ptr<TProtocol> iprot(new TBinaryProtocol(m_httpClient));
563 m_client = new evernote::edam::NoteStoreClient(iprot);
564
565 qDebug() << "NoteStore client created.";
566@@ -153,27 +153,6 @@
567 return m_notes.value(guid);
568 }
569
570-void NotesStore::saveNote(const QString &guid)
571-{
572- Note *note = m_notes.value(guid);
573-
574- QString enml = Html2EnmlConverter::html2enml(note->content());
575- note->setContent(enml);
576-
577- SaveNoteJob *job = new SaveNoteJob(note, this);
578- connect(job, &SaveNoteJob::resultReady, this, &NotesStore::saveNoteJobDone);
579- m_jobQueue.append(job);
580- startJobQueue();
581-}
582-
583-void NotesStore::deleteNote(const QString &guid)
584-{
585- DeleteNoteJob *job = new DeleteNoteJob(guid, this);
586- connect(job, &DeleteNoteJob::resultReady, this, &NotesStore::deleteNoteJobDone);
587- m_jobQueue.append(job);
588- startJobQueue();
589-}
590-
591 QList<Notebook *> NotesStore::notebooks() const
592 {
593 return m_notebooks.values();
594@@ -184,6 +163,12 @@
595 return m_notebooks.value(guid);
596 }
597
598+void NotesStore::enqueue(EvernoteJob *job)
599+{
600+ m_jobQueue.append(job);
601+ startJobQueue();
602+}
603+
604 void NotesStore::startJobQueue()
605 {
606 if (m_jobQueue.isEmpty()) {
607@@ -205,23 +190,15 @@
608
609 void NotesStore::refreshNotes(const QString &filterNotebookGuid)
610 {
611- if (m_token.isEmpty()) {
612- qWarning() << "No token set. Cannot fetch notes.";
613- return;
614- }
615-
616 FetchNotesJob *job = new FetchNotesJob(filterNotebookGuid);
617- connect(job, &FetchNotesJob::resultReady, this, &NotesStore::fetchNotesJobDone);
618-
619- m_jobQueue.append(job);
620- startJobQueue();
621+ connect(job, &FetchNotesJob::jobDone, this, &NotesStore::fetchNotesJobDone);
622+ enqueue(job);
623 }
624
625-void NotesStore::fetchNotesJobDone(ErrorCode errorCode, const evernote::edam::NotesMetadataList &results)
626+void NotesStore::fetchNotesJobDone(ErrorCode errorCode, const QString &errorMessage, const evernote::edam::NotesMetadataList &results)
627 {
628 if (errorCode != ErrorCodeNoError) {
629- qWarning() << "Failed to fetch notes list:" << errorCodeToString(errorCode);
630- startNextJob();
631+ qWarning() << "Failed to fetch notes list:" << errorMessage;
632 return;
633 }
634
635@@ -240,29 +217,19 @@
636 emit noteAdded(note->guid());
637 }
638 }
639-
640- startNextJob();
641 }
642
643 void NotesStore::refreshNoteContent(const QString &guid)
644 {
645- if (m_token.isEmpty()) {
646- qWarning() << "No token set. Cannot fetch note.";
647- return;
648- }
649-
650 FetchNoteJob *job = new FetchNoteJob(guid, this);
651 connect(job, &FetchNoteJob::resultReady, this, &NotesStore::fetchNoteJobDone);
652- m_jobQueue.append(job);
653-
654- startJobQueue();
655+ enqueue(job);
656 }
657
658-void NotesStore::fetchNoteJobDone(ErrorCode errorCode, const evernote::edam::Note &result)
659+void NotesStore::fetchNoteJobDone(ErrorCode errorCode, const QString &errorMessage, const evernote::edam::Note &result)
660 {
661 if (errorCode != ErrorCodeNoError) {
662- qWarning() << "Error fetching note:" << errorCode;
663- startNextJob();
664+ qWarning() << "Error fetching note:" << errorMessage;
665 return;
666 }
667
668@@ -271,29 +238,19 @@
669 note->setTitle(QString::fromStdString(result.title));
670 note->setContent(QString::fromStdString(result.content));
671 emit noteChanged(note->guid());
672-
673- startNextJob();
674 }
675
676 void NotesStore::refreshNotebooks()
677 {
678- if (m_token.isEmpty()) {
679- qWarning() << "No token set. Cannot refresh notebooks.";
680- return;
681- }
682-
683 FetchNotebooksJob *job = new FetchNotebooksJob();
684- connect(job, &FetchNotebooksJob::resultReady, this, &NotesStore::fetchNotebooksJobDone);
685-
686- m_jobQueue.append(job);
687- startJobQueue();
688+ connect(job, &FetchNotebooksJob::jobDone, this, &NotesStore::fetchNotebooksJobDone);
689+ enqueue(job);
690 }
691
692-void NotesStore::fetchNotebooksJobDone(ErrorCode errorCode, const std::vector<evernote::edam::Notebook> &results)
693+void NotesStore::fetchNotebooksJobDone(ErrorCode errorCode, const QString &errorMessage, const std::vector<evernote::edam::Notebook> &results)
694 {
695 if (errorCode != ErrorCodeNoError) {
696- qWarning() << "Error fetching notebooks:" << errorCodeToString(errorCode);
697- startNextJob();
698+ qWarning() << "Error fetching notebooks:" << errorMessage;
699 return;
700 }
701
702@@ -312,51 +269,72 @@
703 qDebug() << "got new notebook" << notebook->guid();
704 }
705 }
706-
707- startNextJob();
708 }
709
710 void NotesStore::createNote(const QString &title, const QString &notebookGuid, const QString &content)
711 {
712- Note *note = new Note();
713- note->setTitle(title);
714- note->setNotebookGuid(notebookGuid);
715- note->setContent(content);
716- CreateNoteJob *job = new CreateNoteJob(note);
717- connect(job, &CreateNoteJob::resultReady, this, &NotesStore::createNoteJobDone);
718-
719- m_jobQueue.append(job);
720- startJobQueue();
721+ CreateNoteJob *job = new CreateNoteJob(title, notebookGuid, content);
722+ connect(job, &CreateNoteJob::jobDone, this, &NotesStore::createNoteJobDone);
723+ enqueue(job);
724 }
725
726-void NotesStore::createNoteJobDone(NotesStore::ErrorCode errorCode, Note *note)
727+void NotesStore::createNoteJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage, const evernote::edam::Note &result)
728 {
729 if (errorCode != ErrorCodeNoError) {
730- qWarning() << "Error creating note:" << errorCodeToString(errorCode);
731- delete note;
732- startNextJob();
733+ qWarning() << "Error creating note:" << errorMessage;
734 return;
735 }
736
737+ Note *note = new Note(QString::fromStdString(result.guid));
738+ note->setNotebookGuid(QString::fromStdString(result.notebookGuid));
739+ note->setTitle(QString::fromStdString(result.title));
740+ note->setContent(QString::fromStdString(result.content));
741+
742 m_notes.insert(note->guid(), note);
743 noteAdded(note->guid());
744- startNextJob();
745-}
746-
747-void NotesStore::saveNoteJobDone(NotesStore::ErrorCode errorCode, Note *note)
748-{
749- startNextJob();
750-}
751-
752-void NotesStore::deleteNoteJobDone(NotesStore::ErrorCode errorCode, const QString &guid)
753-{
754- if (errorCode != ErrorCodeNoError) {
755- qWarning() << "Cannot delete note:" << errorCodeToString(errorCode);
756- startNextJob();
757+}
758+
759+void NotesStore::saveNote(const QString &guid)
760+{
761+ Note *note = m_notes.value(guid);
762+
763+ QString enml = Html2EnmlConverter::html2enml(note->content());
764+ note->setContent(enml);
765+
766+ SaveNoteJob *job = new SaveNoteJob(note, this);
767+ connect(job, &SaveNoteJob::jobDone, this, &NotesStore::saveNoteJobDone);
768+ enqueue(job);
769+}
770+
771+void NotesStore::saveNoteJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage, const evernote::edam::Note &result)
772+{
773+ if (errorCode != ErrorCodeNoError) {
774+ qWarning() << "error saving note" << errorMessage;
775+ return;
776+ }
777+
778+ Note *note = m_notes.value(QString::fromStdString(result.guid));
779+ if (note) {
780+ note->setTitle(QString::fromStdString(result.title));
781+ note->setNotebookGuid(QString::fromStdString(result.notebookGuid));
782+
783+ emit noteChanged(note->guid());
784+ }
785+}
786+
787+void NotesStore::deleteNote(const QString &guid)
788+{
789+ DeleteNoteJob *job = new DeleteNoteJob(guid, this);
790+ connect(job, &DeleteNoteJob::jobDone, this, &NotesStore::deleteNoteJobDone);
791+ enqueue(job);
792+}
793+
794+void NotesStore::deleteNoteJobDone(NotesStore::ErrorCode errorCode, const QString &errorMessage, const QString &guid)
795+{
796+ if (errorCode != ErrorCodeNoError) {
797+ qWarning() << "Cannot delete note:" << errorMessage;
798 return;
799 }
800 emit noteRemoved(guid);
801 m_notes.take(guid)->deleteLater();
802- startNextJob();
803 }
804-
805
806=== modified file 'src/plugin/Evernote/notesstore.h'
807--- src/plugin/Evernote/notesstore.h 2013-11-25 00:49:48 +0000
808+++ src/plugin/Evernote/notesstore.h 2013-11-27 21:48:53 +0000
809@@ -1,6 +1,9 @@
810 #ifndef NOTESSTORE_H
811 #define NOTESSTORE_H
812
813+// Thrift
814+#include <transport/THttpClient.h>
815+
816 // Evernote SDK
817 #include <NoteStore.h>
818 #include <NoteStore_constants.h>
819@@ -14,6 +17,8 @@
820 class Notebook;
821 class Note;
822
823+using namespace apache::thrift::transport;
824+
825 class NotesStore : public QObject
826 {
827 Q_OBJECT
828@@ -63,14 +68,19 @@
829 void notebookChanged(const QString &guid);
830
831 private slots:
832- void fetchNotesJobDone(ErrorCode errorCode, const evernote::edam::NotesMetadataList &results);
833- void fetchNotebooksJobDone(ErrorCode errorCode, const std::vector<evernote::edam::Notebook> &results);
834- void fetchNoteJobDone(ErrorCode errorCode, const evernote::edam::Note &result);
835- void createNoteJobDone(ErrorCode errorCode, Note *note);
836- void saveNoteJobDone(ErrorCode errorCode, Note *note);
837- void deleteNoteJobDone(ErrorCode errorCode, const QString &guid);
838+ void fetchNotesJobDone(ErrorCode errorCode, const QString &errorMessage, const evernote::edam::NotesMetadataList &results);
839+ void fetchNotebooksJobDone(ErrorCode errorCode, const QString &errorMessage, const std::vector<evernote::edam::Notebook> &results);
840+ void fetchNoteJobDone(ErrorCode errorCode, const QString &errorMessage, const evernote::edam::Note &result);
841+ void createNoteJobDone(ErrorCode errorCode, const QString &errorMessage, const evernote::edam::Note &result);
842+ void saveNoteJobDone(ErrorCode errorCode, const QString &errorMessage, const evernote::edam::Note &result);
843+ void deleteNoteJobDone(ErrorCode errorCode, const QString &errorMessage, const QString &guid);
844
845+ // Use this to enqueue a new job. It will automatically start it if there is no other job pending.
846+ void enqueue(EvernoteJob *job);
847 void startJobQueue();
848+
849+ // You should not use this. It's called by the job queue.
850+ // If you have a new job to run, just enqueue it. The queue will process it eventually.
851 void startNextJob();
852
853 private:
854@@ -78,13 +88,21 @@
855 static NotesStore *s_instance;
856
857 QString m_token;
858- evernote::edam::NoteStoreClient *m_client;
859
860 QHash<QString, Notebook*> m_notebooks;
861 QHash<QString, Note*> m_notes;
862
863+ // There must be only one job running at a time
864+ // Do not start jobs other than with startJobQueue()
865 QList<EvernoteJob*> m_jobQueue;
866- QThread *m_currentJob;
867+ EvernoteJob *m_currentJob;
868+
869+ // Those two are accessed from the job thread.
870+ // Make sure to not access them while any jobs are running
871+ // or we need to mutex them.
872+ evernote::edam::NoteStoreClient *m_client;
873+ boost::shared_ptr<THttpClient> m_httpClient;
874+
875 };
876
877 #endif // NOTESSTORE_H

Subscribers

People subscribed via source and target branches