diff --git a/identity/src/main/java/life/qbic/identity/application/user/registration/Registration.java b/identity/src/main/java/life/qbic/identity/application/user/registration/Registration.java deleted file mode 100644 index 4bb70dcd0..000000000 --- a/identity/src/main/java/life/qbic/identity/application/user/registration/Registration.java +++ /dev/null @@ -1,114 +0,0 @@ -package life.qbic.identity.application.user.registration; - -import life.qbic.application.commons.ApplicationResponse; -import life.qbic.identity.application.user.IdentityService; -import life.qbic.identity.application.user.IdentityService.EmptyUserNameException; -import life.qbic.identity.application.user.IdentityService.UserExistsException; -import life.qbic.identity.application.user.IdentityService.UserNameNotAvailableException; -import life.qbic.identity.domain.model.EmailAddress.EmailValidationException; -import life.qbic.identity.domain.model.EncryptedPassword.PasswordValidationException; -import life.qbic.identity.domain.model.FullName.FullNameValidationException; -import life.qbic.logging.api.Logger; -import life.qbic.logging.service.LoggerFactory; - -/** - * User Registration use case - * - *
Tries to register a new user and create a user account. - * - *
In case a user with the provided mail already exists, the registration will fail and calls - * the failure output method. - * - * @since 1.0.0 - */ -public class Registration implements RegisterUserInput { - - private static final Logger log = LoggerFactory.logger(Registration.class.getName()); - - private RegisterUserOutput registerUserOutput; - - private final IdentityService identityService; - - /** - * Creates the registration use case. - * - *
Upon construction, a dummy output interface is created, that needs to be overridden by - * explicitly setting it via {@link Registration#setRegisterUserOutput(RegisterUserOutput)}. - * - *
The default output implementation just prints to std out on success and std err on failure,
- * after the use case has been executed via
- * {@link RegisterUserInput#register(String, String, char[], String)}.
- *
- * @param identityService the user registration service to save the new user to.
- * @since 1.0.0
- */
- public Registration(IdentityService identityService) {
- this.identityService = identityService;
- }
-
- /**
- * Sets and overrides the use case output.
- *
- * @param registerUserOutput an output interface implementation, so the use case can trigger the
- * callback methods after its execution
- * @since 1.0.0
- */
- public void setRegisterUserOutput(RegisterUserOutput registerUserOutput) {
- this.registerUserOutput = registerUserOutput;
- }
-
- /**
- * @inheritDocs
- */
- @Override
- public void register(String fullName, String email, char[] rawPassword, String userName) {
- if (registerUserOutput == null) {
- log.error("No use case output set.");
- return;
- }
- try {
- identityService.registerUser(fullName, userName, email, rawPassword)
- .ifSuccessOrElse(this::reportSuccess,
- response -> registerUserOutput.onUnexpectedFailure(build(response)));
- } catch (Exception e) {
- log.error("User registration failed", e);
- registerUserOutput.onUnexpectedFailure("Unexpected error occurred.");
- }
- }
-
- private void reportSuccess(ApplicationResponse applicationResponse) {
- registerUserOutput.onUserRegistrationSucceeded();
- }
-
- private UserRegistrationException build(ApplicationResponse applicationResponse) {
- var builder = UserRegistrationException.builder();
-
- for (RuntimeException e : applicationResponse.failures()) {
-
- if (e instanceof EmailValidationException emailValidationException) {
- builder.withEmailFormatException(emailValidationException);
- } else if (e instanceof PasswordValidationException passwordValidationException) {
- builder.withInvalidPasswordException(passwordValidationException);
- } else if (e instanceof FullNameValidationException fullNameValidationException) {
- builder.withFullNameException(fullNameValidationException);
- } else if (e instanceof UserExistsException userExistsException) {
- builder.withUserExistsException(userExistsException);
- } else if (e instanceof UserNameNotAvailableException userNameNotAvailableException) {
- builder.withUserNameNotAvailableException(userNameNotAvailableException);
- } else if (e instanceof EmptyUserNameException emptyUserNameException) {
- builder.withEmptyUserNameException(emptyUserNameException);
- } else {
- builder.withUnexpectedException(e);
- }
- }
- return builder.build();
- }
-
- /**
- * @inheritDocs
- */
- @Override
- public void setOutput(RegisterUserOutput output) {
- registerUserOutput = output;
- }
-}
diff --git a/identity/src/test/groovy/life/qbic/identity/domain/usermanagement/registration/RegistrationSpec.groovy b/identity/src/test/groovy/life/qbic/identity/domain/usermanagement/registration/RegistrationSpec.groovy
deleted file mode 100644
index 688d0aba7..000000000
--- a/identity/src/test/groovy/life/qbic/identity/domain/usermanagement/registration/RegistrationSpec.groovy
+++ /dev/null
@@ -1,128 +0,0 @@
-package life.qbic.identity.domain.usermanagement.registration
-
-import life.qbic.identity.application.user.IdentityService
-import life.qbic.identity.application.user.registration.RegisterUserOutput
-import life.qbic.identity.application.user.registration.Registration
-import life.qbic.identity.application.user.registration.UserRegistrationException
-import life.qbic.identity.domain.model.*
-import life.qbic.identity.domain.registry.DomainRegistry
-import life.qbic.identity.domain.repository.UserDataStorage
-import life.qbic.identity.domain.repository.UserRepository
-import life.qbic.identity.domain.service.UserDomainService
-import org.springframework.data.domain.Pageable
-import spock.lang.Shared
-import spock.lang.Specification
-
-import java.util.stream.Collectors
-
-/**
- * Tests for the registration use case
- *
- * @since 1.0.0
- */
-class RegistrationSpec extends Specification {
-
- @Shared
- public IdentityService userRegistrationService
-
- @Shared
- public TestStorage testStorage
- private String validPassword = "0123456789012"
-
- def setupSpec() {
- testStorage = new TestStorage()
- DomainRegistry domainRegistry = DomainRegistry.instance()
- domainRegistry.registerService(new UserDomainService(UserRepository.getInstance(testStorage)))
- userRegistrationService = new IdentityService(UserRepository.getInstance(testStorage))
- }
-
- def "When a user is already registered with a given email address, abort the registration and communicate the failure"() {
- given: "A repository with one user entry"
- def testUser = User.create(FullName.from("Mr Somebody"), EmailAddress.from("some@body.com"), "svenipopenni", EncryptedPassword.from(validPassword.toCharArray()))
- testStorage.save(testUser)
-
- and:
- def useCaseOutput = Mock(RegisterUserOutput.class)
-
- and: "a new user to register"
- def newUser = User.create(FullName.from("Mr Somebody"), EmailAddress.from("some@body.com"), "svenipopenni", EncryptedPassword.from(validPassword.toCharArray()))
-
- and: "a the use case with output"
- def registration = new Registration(new IdentityService(UserRepository.getInstance(testStorage)))
- registration.setOutput(useCaseOutput)
-
- when: "a user is registered"
- registration.register(newUser.fullName().get(), newUser.emailAddress().get(), "12345678".toCharArray(), newUser.userName())
-
- then:
- 0 * useCaseOutput.onUserRegistrationSucceeded()
- 1 * useCaseOutput.onUnexpectedFailure(_ as UserRegistrationException)
- // the user has not been added to the repository
- testStorage.findUsersByEmailAddress(testUser.emailAddress()).size() == 1
- }
-
- def "When a user is not yet registered with a given email address, register the user"() {
- given: "A repository with one user entry"
- def testUser = User.create(FullName.from("Mr Somebody"), EmailAddress.from("some@body.com"), "svenipopenni", EncryptedPassword.from(validPassword.toCharArray()))
- testStorage.save(testUser)
-
- and:
- def useCaseOutput = Mock(RegisterUserOutput.class)
-
- and: "a new user to register"
- def newUser = User.create(FullName.from("Mr Nobody"), EmailAddress.from("no@body.com"), "svenipopenni2", EncryptedPassword.from(validPassword.toCharArray()))
-
- and: "a the use case with output"
- def registration = new Registration(new IdentityService(UserRepository.getInstance(Mock(UserDataStorage))))
- registration.setOutput(useCaseOutput)
-
- when: "a user is registered"
- registration.register(newUser.fullName().get(), newUser.emailAddress().get(), validPassword.toCharArray(), newUser.userName())
-
- then:
- 1 * useCaseOutput.onUserRegistrationSucceeded()
- 0 * useCaseOutput.onUnexpectedFailure(_ as String)
- 0 * useCaseOutput.onUnexpectedFailure(_ as UserRegistrationException)
- def storedUser = testStorage.findUsersByEmailAddress(newUser.emailAddress()).get(0)
- storedUser.fullName() == newUser.fullName()
- !storedUser.id().get().isBlank()
-
- }
-
- private static class TestStorage implements UserDataStorage {
-
- private List This class is responsible for enabling buttons or triggering other view relevant changes on
- * the view class components
- */
-@Component
-public class UserRegistrationHandler
- implements UserRegistrationHandlerInterface, RegisterUserOutput {
-
- private static final Logger log =
- LoggerFactory.logger(UserRegistrationHandler.class.getName());
- private final RegisterUserInput registrationUseCase;
- private UserRegistrationLayout userRegistrationLayout;
-
- @Autowired
- UserRegistrationHandler(RegisterUserInput registrationUseCase) {
- this.registrationUseCase = registrationUseCase;
- registrationUseCase.setOutput(this);
-
- }
-
- @Override
- public void handle(UserRegistrationLayout registrationLayout) {
- if (userRegistrationLayout != registrationLayout) {
- this.userRegistrationLayout = registrationLayout;
- initFields();
- addListener();
- }
- }
-
- private void initFields() {
- userRegistrationLayout.fullName.setPattern("\\S.*");
- userRegistrationLayout.fullName.setErrorMessage("Please provide your full name here");
- userRegistrationLayout.email.setErrorMessage("Please provide a valid mail address");
- userRegistrationLayout.password.setHelperText("A password must be at least 12 characters");
- userRegistrationLayout.password.setPattern(".{12,}");
- userRegistrationLayout.password.setErrorMessage("Password too short");
- userRegistrationLayout.username.setHelperText("Your unique username, visible to other users");
- userRegistrationLayout.username.setErrorMessage("Please provide a username");
- }
-
- private void addListener() {
- userRegistrationLayout.registerButton.addClickShortcut(Key.ENTER);
-
- userRegistrationLayout.registerButton.addClickListener(
- event -> {
- clearNotifications();
- registrationUseCase.register(
- userRegistrationLayout.fullName.getValue().strip(),
- userRegistrationLayout.email.getValue().strip(),
- userRegistrationLayout.password.getValue().toCharArray(),
- userRegistrationLayout.username.getValue().strip());
- });
- }
-
- private void clearNotifications() {
- userRegistrationLayout.notificationLayout.removeAll();
- }
-
- private void showError(String title, String description) {
- clearNotifications();
- ErrorMessage errorMessage = new ErrorMessage(title, description);
- userRegistrationLayout.notificationLayout.add(errorMessage);
- }
-
- private void showAlreadyUsedEmailError() {
- showError("Email address already in use",
- "If you have difficulties with your password you can reset it.");
- }
-
- private void showUnexpectedError() {
- showError("Registration failed", "Please try again.");
- }
-
- @Override
- public void onUserRegistrationSucceeded() {
- QueryParameters registrationParams = QueryParameters.simple(Map.of("userRegistered", "true"));
- UI.getCurrent().navigate("/login", registrationParams);
- }
-
- @Override
- public void onUnexpectedFailure(UserRegistrationException userRegistrationException) {
- handleRegistrationFailure(userRegistrationException);
- }
-
- @Override
- public void onUnexpectedFailure(String reason) {
- showUnexpectedError();
- }
-
- private void handleRegistrationFailure(UserRegistrationException userRegistrationException) {
- if (userRegistrationException.fullNameException().isPresent()) {
- userRegistrationLayout.fullName.setInvalid(true);
- }
- if (userRegistrationException.passwordException().isPresent()) {
- userRegistrationLayout.password.setInvalid(true);
- }
- if (userRegistrationException.emailFormatException().isPresent()) {
- userRegistrationLayout.email.setInvalid(true);
- }
- if (userRegistrationException.userExistsException().isPresent()) {
- showAlreadyUsedEmailError();
- }
- if (userRegistrationException.userNameNotAvailableException().isPresent()) {
- showUserNameNotAvailableError();
- }
- if (userRegistrationException.emptyUserNameException().isPresent()) {
- showEmptyUserNameError();
- }
- if (userRegistrationException.unexpectedException().isPresent()) {
- showUnexpectedError();
- }
- }
-
- private void showUserNameNotAvailableError() {
- showError("Username already in use", "Please try another username");
- }
-
- private void showEmptyUserNameError() {
- showError("Username must not be empty", "Please try another username");
- }
-}
diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/register/UserRegistrationHandlerInterface.java b/user-interface/src/main/java/life/qbic/datamanager/views/register/UserRegistrationHandlerInterface.java
deleted file mode 100644
index 4f3040447..000000000
--- a/user-interface/src/main/java/life/qbic/datamanager/views/register/UserRegistrationHandlerInterface.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package life.qbic.datamanager.views.register;
-
-/**
- * Interface to handle the {@link UserRegistrationLayout} to the {@link
- * UserRegistrationHandler}.
- *
- * @since 1.0.0
- */
-public interface UserRegistrationHandlerInterface {
-
- /**
- * Registers a {@link UserRegistrationLayout} to an implementing class
- *
- * @param registerLayout The view that is being handled
- * @since 1.0.0
- */
- void handle(UserRegistrationLayout registerLayout);
-}
diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/register/UserRegistrationLayout.java b/user-interface/src/main/java/life/qbic/datamanager/views/register/UserRegistrationLayout.java
index c1a9e67b5..90d909a1a 100644
--- a/user-interface/src/main/java/life/qbic/datamanager/views/register/UserRegistrationLayout.java
+++ b/user-interface/src/main/java/life/qbic/datamanager/views/register/UserRegistrationLayout.java
@@ -1,7 +1,9 @@
package life.qbic.datamanager.views.register;
import com.vaadin.flow.component.HasValueAndElement;
+import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.Text;
+import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.dependency.CssImport;
@@ -13,15 +15,29 @@
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.PageTitle;
+import com.vaadin.flow.router.QueryParameters;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouterLink;
import com.vaadin.flow.server.auth.AnonymousAllowed;
+import com.vaadin.flow.spring.annotation.UIScope;
import java.io.Serial;
+import java.util.Map;
+import java.util.Objects;
import java.util.stream.Stream;
+import life.qbic.application.commons.ApplicationResponse;
import life.qbic.datamanager.views.AppRoutes;
import life.qbic.datamanager.views.landing.LandingPageLayout;
import life.qbic.datamanager.views.login.LoginLayout;
import life.qbic.datamanager.views.login.passwordreset.ResetPasswordLayout;
+import life.qbic.datamanager.views.notifications.ErrorMessage;
+import life.qbic.identity.application.user.IdentityService;
+import life.qbic.identity.application.user.IdentityService.EmptyUserNameException;
+import life.qbic.identity.application.user.IdentityService.UserExistsException;
+import life.qbic.identity.application.user.IdentityService.UserNameNotAvailableException;
+import life.qbic.identity.application.user.registration.UserRegistrationException;
+import life.qbic.identity.domain.model.EmailAddress.EmailValidationException;
+import life.qbic.identity.domain.model.EncryptedPassword.PasswordValidationException;
+import life.qbic.identity.domain.model.FullName.FullNameValidationException;
import org.springframework.beans.factory.annotation.Autowired;
/**
@@ -33,6 +49,7 @@
@Route(value = AppRoutes.REGISTER, layout = LandingPageLayout.class)
@CssImport("./styles/views/login/login-view.css")
@AnonymousAllowed
+@UIScope
public class UserRegistrationLayout extends VerticalLayout {
@Serial
@@ -55,19 +72,76 @@ public class UserRegistrationLayout extends VerticalLayout {
private VerticalLayout fieldLayout;
private final VerticalLayout contentLayout;
private H2 layoutTitle;
+ private IdentityService identityService;
- public UserRegistrationLayout(@Autowired UserRegistrationHandlerInterface registerHandler) {
-
+ public UserRegistrationLayout(@Autowired IdentityService identityService) {
+ this.identityService = Objects.requireNonNull(identityService);
this.addClassName("grid");
- contentLayout = new VerticalLayout();
-
+ this.contentLayout = new VerticalLayout();
initLayout();
styleLayout();
- registerToHandler(registerHandler);
+ initFields();
+ addListener();
+ }
+
+ private void initFields() {
+ fullName.setPattern("\\S.*");
+ fullName.setErrorMessage("Please provide your full name here");
+ email.setErrorMessage("Please provide a valid mail address");
+ password.setHelperText("A password must be at least 12 characters");
+ password.setPattern(".{12,}");
+ password.setErrorMessage("Password too short");
+ username.setHelperText("Your unique username, visible to other users");
+ username.setErrorMessage("Please provide a username");
}
- private void registerToHandler(UserRegistrationHandlerInterface registerHandler) {
- registerHandler.handle(this);
+ private void addListener() {
+ registerButton.addClickShortcut(Key.ENTER);
+
+ registerButton.addClickListener(
+ event -> {
+ clearNotifications();
+ var response = identityService.registerUser(
+ fullName.getValue().strip(),
+ username.getValue().strip(),
+ email.getValue().strip(),
+ password.getValue().toCharArray()
+ );
+ handleResponse(response);
+ });
+ }
+
+ private void handleResponse(ApplicationResponse response) {
+ response.ifSuccessOrElse(this::onUserRegistrationSucceeded, this::handleRegistrationFailure);
+ }
+
+ private void handleRegistrationFailure(ApplicationResponse response) {
+ UserRegistrationException exception = convertToRegistrationException(response);
+ handleRegistrationFailure(exception);
+ }
+
+ private UserRegistrationException convertToRegistrationException(ApplicationResponse applicationResponse) {
+ var builder = UserRegistrationException.builder();
+
+ for (RuntimeException e : applicationResponse.failures()) {
+
+ if (e instanceof EmailValidationException emailValidationException) {
+ builder.withEmailFormatException(emailValidationException);
+ } else if (e instanceof PasswordValidationException passwordValidationException) {
+ builder.withInvalidPasswordException(passwordValidationException);
+ } else if (e instanceof FullNameValidationException fullNameValidationException) {
+ builder.withFullNameException(fullNameValidationException);
+ } else if (e instanceof UserExistsException userExistsException) {
+ builder.withUserExistsException(userExistsException);
+ } else if (e instanceof UserNameNotAvailableException userNameNotAvailableException) {
+ builder.withUserNameNotAvailableException(userNameNotAvailableException);
+ } else if (e instanceof EmptyUserNameException emptyUserNameException) {
+ builder.withEmptyUserNameException(emptyUserNameException);
+ } else {
+ builder.withUnexpectedException(e);
+ }
+ }
+ return builder.build();
}
private void initLayout() {
@@ -163,4 +237,60 @@ private void styleRegisterButton() {
private void setRequiredIndicatorVisible(HasValueAndElement, ?>... components) {
Stream.of(components).forEach(comp -> comp.setRequiredIndicatorVisible(true));
}
+
+ private void showUserNameNotAvailableError() {
+ showError("Username already in use", "Please try another username");
+ }
+
+ private void showEmptyUserNameError() {
+ showError("Username must not be empty", "Please try another username");
+ }
+
+ private void showError(String title, String description) {
+ clearNotifications();
+ ErrorMessage errorMessage = new ErrorMessage(title, description);
+ notificationLayout.add(errorMessage);
+ }
+
+ private void clearNotifications() {
+ notificationLayout.removeAll();
+ }
+
+ private void handleRegistrationFailure(UserRegistrationException userRegistrationException) {
+ if (userRegistrationException.fullNameException().isPresent()) {
+ fullName.setInvalid(true);
+ }
+ if (userRegistrationException.passwordException().isPresent()) {
+ password.setInvalid(true);
+ }
+ if (userRegistrationException.emailFormatException().isPresent()) {
+ email.setInvalid(true);
+ }
+ if (userRegistrationException.userExistsException().isPresent()) {
+ showAlreadyUsedEmailError();
+ }
+ if (userRegistrationException.userNameNotAvailableException().isPresent()) {
+ showUserNameNotAvailableError();
+ }
+ if (userRegistrationException.emptyUserNameException().isPresent()) {
+ showEmptyUserNameError();
+ }
+ if (userRegistrationException.unexpectedException().isPresent()) {
+ showUnexpectedError();
+ }
+ }
+
+ public void onUserRegistrationSucceeded(ApplicationResponse response) {
+ QueryParameters registrationParams = QueryParameters.simple(Map.of("userRegistered", "true"));
+ UI.getCurrent().navigate("/login", registrationParams);
+ }
+
+ private void showUnexpectedError() {
+ showError("Registration failed", "Please try again.");
+ }
+
+ private void showAlreadyUsedEmailError() {
+ showError("Email address already in use",
+ "If you have difficulties with your password you can reset it.");
+ }
}