0
\$\begingroup\$

For readability sake, is it better to use optionals or write my code in an iterative manner?

Here's a route I'm working on, does this look readable do you?

@PostMapping("/verify-email")
public ResponseEntity<String> verifyAccount(@RequestBody String email) {
    Optional<AppUser> optionalUser = appUserService.findByEmail(email);
    return optionalUser
            .filter(user -> !user.isEnabled()).stream().findFirst()
            .map(verificationTokenService::findByUser)
            .map(verificationToken -> {
                verificationToken.ifPresent(verificationTokenService::delete);
                verificationTokenService.createVerificationToken(optionalUser.get(), VerificationTokenType.VERIFY_ACCOUNT);
                String message = String.format("Account verification link has been sent to %s", email);
                return ResponseEntity.ok(message);
            }).orElseThrow(() -> new VerificationTokenException());
} 

I like my code to be readable and clean, and I'm seriously considering writing it. Or is this a good way to use optional?

\$\endgroup\$
4
  • \$\begingroup\$ I don't see how it would become more readable by using conventional structures (although that is purely opinionated) and optionals are used exactly the way they were intended: as method return values. A method return statement inside the stream operation can be a bit hard to follow so I might try to move it to the end of the method. \$\endgroup\$ Commented Oct 7, 2022 at 4:28
  • 1
    \$\begingroup\$ Thank you for your input. The only problem is in a stream, a return must be specified or I'll get the infamous "Missing return statement" error \$\endgroup\$ Commented Oct 7, 2022 at 5:55
  • \$\begingroup\$ you could make a separate Function<VerificationToken, ResponseEntity> from the multi-line lambda code \$\endgroup\$ Commented Oct 7, 2022 at 7:14
  • 1
    \$\begingroup\$ Welcome to the Code Review Community, our goal here is to help you improve your code by making insightful observations about the code that is written. The code must be working as intended already. We don't answer How to ... questions because that means the code isn't working as intended yet. There is a site that can help you, Please read Where can I get help? and How do I ask a good question?. \$\endgroup\$ Commented Oct 7, 2022 at 12:42

2 Answers 2

2
\$\begingroup\$

Your code is missing some important context. Based on the context, it looks to me like .map(verificationTokenService::findByUser) would return an Optional<VerificationToken> or a similar optional, which would mean that if verificationToken is not present, the .map(verificationToken -> { operation will never be executed. But that lambda executes verificationToken.ifPresent(...) which indicates that it's expected to be executed even ehen the token is not present.

If this is correct, it may be a sign that splitting the code into separate operations (instead of one single optional stream) would make it more readable. It would also mean that your code wasn't tested and did not work as expected and would thus be off-topic for codereview...

\$\endgroup\$
0
\$\begingroup\$

Continuing on from @TorbenPutkonen:

  • The variable optionalUser is not useful. Just chain onto .findByEmail().
  • It may make sense to move filtering for a disabeld user onto the database layer and thus having a method .findDisabledUserByEmail() instead if .findByEmail().
  • .stream().findFirst() seems completely pointless.
  • Since verificationTokenService::findByUser returns an Optional itself that would make this an Optional<Optional<...>> which is an anti-pattern. It would make more sense to use .flatMap() instead of .map() to "flatten" the type to a simple Optional<...> here.
  • The return value of the second .map() has nothing to do with the Optional value. You sould move those last three lines out of the lambda. Instead use .idPresentOrElse().

Possible simplified version:

@PostMapping("/verify-email")
public ResponseEntity<String> verifyAccount(@RequestBody String email) {
    appUserService.findByEmail(email)
            .filter(user -> !user.isEnabled())
            .flatMap(verificationTokenService::findByUser)
            .ifPresentOrElse(
                verificationTokenService::delete, 
                () -> throw new VerificationTokenException()
            );

    verificationTokenService.createVerificationToken(optionalUser.get(), VerificationTokenType.VERIFY_ACCOUNT);
    String message = String.format("Account verification link has been sent to %s", email);
    return ResponseEntity.ok(message);
}
\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.