Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor server-side folder management implementation #271

Merged
merged 12 commits into from
Feb 22, 2017

Conversation

ChristophWurst
Copy link
Member

@ChristophWurst ChristophWurst commented Jan 4, 2017

I finally found the time to study the server-side implementation and refactor it into a testable, extensible form. This is needed for the planned features like threading. Before I start with those I want the code to be tested almost 100% because testing IMAP manually is a major PITA. This PR shows that moving the existing code a bit around can lead to a much better code quality. Actually, I could even remove some of the code in place because it's just not needed or can be done more efficiently.

  • better OOP architecture
  • dependency injection everywhere
  • abstraction where it made sense
  • SRP applied as much as possible
  • added a bunch of tests for the new code
  • REST API compatible to old implementation
  • new code is not optimized
  • old code was not removed

TODO:

  • 100% code coverage (actually only a few code paths missing)
  • check why unified inbox flagged folder is not shown any more well, we never had that feature 🙈
  • file ticket about the js bug that occurs when accounts are added
  • re-enable search mailbox (starred messages filter)

@jancborchardt
Copy link
Member

Aaawesome! :) Let us know as soon as it’s reviewable! cc @nextcloud/mail

@ChristophWurst
Copy link
Member Author

I have to do some more refactoring before I can finish this one unfortunately, otherwise I'd have to add those ugly unified inbox hacks to the new shiny code too 😢 Should be easy though 😉

@ChristophWurst
Copy link
Member Author

@nextcloud/mail this should be ready for a review 🚀

Let me know if something breaks 😉

@ChristophWurst
Copy link
Member Author

Open todo (though that shouldn't break anything when added back): caching.

* better OOP architecture
* dependency injection everywhere
* abstraction where it made sense
* SRP applied as much as possible
* added a bunch of tests for the new code
* REST API compatible to old implementation
* new code is not optimized
* old code was not removed

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
…p?id=50688

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
@ChristophWurst ChristophWurst force-pushed the refactor/server-side-folder-management branch from 428d933 to 9dcf840 Compare February 9, 2017 19:46
@ChristophWurst
Copy link
Member Author

Please help with testing/reviewing this PR @nextcloud/mail @Quix0r

@skjnldsv
Copy link
Member

That is a long pr! Care to told us how to review? :p

@ChristophWurst
Copy link
Member Author

That is a long pr! Care to told us how to review? :p

Yup, sorry 'bout that. Well, just look at the diff 😉 I pretty much re-wrote the old implementation and added a bunch of tests. Unfortunately, I cannot tell you how this could be reviewed easily 😟

@skjnldsv
Copy link
Member

Sooo, read the js and process it into your head? 😆

@nickvergessen nickvergessen removed their request for review February 10, 2017 14:40
@ChristophWurst
Copy link
Member Author

ChristophWurst commented Feb 12, 2017

Sooo, read the js and process it into your head? 😆

Sure you can do that hehe

Most of the changes were made to the server implementation though.

So I basically rewrote the code that handles folder management, which is mainly only about listing folders of an IMAP account. All we do after we got the list is translation, sorting, guessing the folder role and building the folder tree from the source list.

To have the most flexibility, maintainability, testability and readability I tried to split the code into logical parts:

  • a high level service to use in the controller -> a new folder manager
  • a class that translates IMAP into the OOP world -> a folder mapper
  • a class to handle translation -> a translator class

The rest of the rather big diff is caused by tests because I almost hit 100% coverage on the new classes (only one little, uncritical if-statement is not covered).

Thus you can start at the controller, look what it calls (spoiler: a single method on the manager) and review the delegated functionality in there. Everything should be self-explanatory from the class/method names, or at least that was my intention 😉

Let me know if there's anything else you need to know to understand the changes :-)

@Quix0r
Copy link

Quix0r commented Feb 13, 2017

Hmm, /js/models/account.js has lost some coverage: https://coveralls.io/builds/10080949/source?filename=js%2Fmodels%2Faccount.js

@jancborchardt
Copy link
Member

Been using it today and can’t find any issues so far. :) Any blockers or things to add back in this specific PR?

@ChristophWurst
Copy link
Member Author

@jancborchardt cool, thanks for testing! I think this is ready for merging 😉

@ChristophWurst
Copy link
Member Author

About the js code coverage: it's not really reliable. It goes up and down. We're not at 33% coverage anyway, the number seems to be wrong :-|

Copy link
Member

@jancborchardt jancborchardt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn’t find any breakage so far, let’s get it in :)

@jancborchardt jancborchardt merged commit 03573e2 into master Feb 22, 2017
@jancborchardt jancborchardt deleted the refactor/server-side-folder-management branch February 22, 2017 16:47
@jancborchardt
Copy link
Member

Perfect! Looking forward to the other unified folders. ;) What can I test next?

@skjnldsv
Copy link
Member

Damn, sorry @ChristophWurst I forgot this one! 😢
Thanks @jancborchardt

@Quix0r
Copy link

Quix0r commented Feb 23, 2017

@ChristophWurst TODO 3 is still open in your list!

@ChristophWurst
Copy link
Member Author

@Quix0r will file an issue right away 🚀

Copy link

@Quix0r Quix0r left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already got merged but maybe still good to consider that I have found and maybe then reflect this in next PR.

return undefined;
}
return _.find(this.folders, function(folder) {
// TODO: handle special folders in subfolder properly
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use @TODO so documentation tools can pick it up? It is very common though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, will use it next time.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then maybe apply this globally, e.g. search for // TODO and replace it with // @TODO. And same with multi-line comments.


interface IMailManager {

public function getFolders(Account $account);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Methods declared in interfaces are always public, no need to have the then redundant public statement around.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like good practice to always be specific. :)

*/
public function __construct($appName, IRequest $request, AccountService $accountService, $UserId) {
public function __construct($appName, IRequest $request,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really split this into more than one line? If that is the coding-convention here, okay.

$account = $this->getMockBuilder('OCA\Mail\Account')
->disableOriginalConstructor()
->getMock();
public function testIndex() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't kill doc tags, they produce nice documentation (e.g. generated by doxygen).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by doc tags?

Copy link

@Quix0r Quix0r Mar 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
 * Some documented method, does foo things and need bar for its purpose.
 * You have to enter a better description than this
 *
 * @param string $bar Some bar string
 * @return void
 */
public function doFoo (string $bar) {
    // Do foo things with bar
    [...]
}

That is a so called doc-tag. :-) It will produce nice documentation in such tools (see Doxygen, it can process more than just C/C++). And PHP 7 supports native type-hints. 👍

<?php

/**
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you (the team) already made a decision for @copyright tag? Maybe give it to FSFE so they can handle any law issues for you. Maybe on the developer team (in general) to express that people develop code not for themselves but for the project?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @schiessle

What do you think is best for this project?

$this->assertFalse($this->folder->isSearchable());
}

public function testJsonSerialize() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of code is missing documentation (what does it do, what is the method foo()and bar() for?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, many segments of our code are either only documented poorly or not documented at all. We can work on that ofc :-)

@@ -1,81 +0,0 @@
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got rid of an old file? Good. 👍

@lock
Copy link

lock bot commented Nov 21, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and questions.

@lock lock bot locked and limited conversation to collaborators Nov 21, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants