Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JHipster : Registering a user with additional information

Tags:

jhipster

I am developping an application using JHispter 3.12.2.

Since I needed more information than what JHipster provides, I created an entity UserExtra containing two Strings : a phone number and a skype address. I linked this entity to the JHI_User in a One to One relationship.

Now the problem I am facing is, when a user is registering, I want to create a new UserExtra associated with the registered User.

To achieve that, I have tried several things on the client side.

The way the standard JHipster register page works is by using a variable named vm.registerAccount that holds the firstName, lastName, login, password and so on.

I have tried using another variable vm.userExtra holding my phone number and skype. Then, I tried several things :

  • In the register.controller.js, I passed my userExtra to the createAccount function :

    Auth.createAccount(vm.registerAccount, vm.userExtra).then(function () {
                        vm.success = 'OK';
                    }).catch(function (response) {
                        vm.success = null;
                        if (response.status === 400 && response.data === 'login already in use') {
                            vm.errorUserExists = 'ERROR';
                        } else if (response.status === 400 && response.data === 'e-mail address already in use') {
                            vm.errorEmailExists = 'ERROR';
                        } else {
                            vm.error = 'ERROR';
                        }
                    });
  • I modified the createAccount function of auth.service.js to follow the changes made :

    function createAccount (account, userExtra, callback) {
                var cb = callback || angular.noop;
    
    
            return Register.save(account, userExtra,
                function () {
                    return cb(account);
                },
                function (err) {
                    this.logout();
                    return cb(err);
                }.bind(this)).$promise;
        }</pre>
    
  • Finally, I updated the registerAccount function of AccountResource.java on the server side :

    public ResponseEntity registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM, UserExtra userExtra) { ... }

But the registerAccount function wasn't even executed from what I remember.

I also tried adding the new user in the callback of the createAccount function of register.controller.js like this :

Auth.createAccount(vm.registerAccount).then(function (result) {
                vm.userExtra.user = result;
                UserExtra.save(vm.userExtra, function() { // success }, function() { // fail });
                vm.success = 'OK';
            })...

But I was only getting an error while trying to save the new UserExtra.

I am pretty sure I need to modify the registerAccount() function inside AccountResource.java, but I just can't retrieve the additional information I am trying to send from the client side. I managed to create a new User inside this function and link it to the JHI_User, but without the additional information.

It's very likely somebody has already faced this issue, what could possibly be the best solution to that problem ?

Edit with solution :

Thanks to Gaël Marziou, I fixed my problem.

Here's a minimal example of a project using the solution I'm describing here.

On the client side, in the register.html page that I have rewritten to my use, I have two fields bound to vm.registerAccount properties :

<input class="form-control" id="phone" ng-model="vm.registerAccount.phone" placeholder="{{'global.form.phone.placeholder' | translate}}" />
...
<input class="form-control" id="skype" ng-model="vm.registerAccount.skype" placeholder="{{'global.form.skype.placeholder' | translate}}" />

In ManagedUserVM I really just added my two fields and their getters :

private String phone;

private String skype;

public String getPhone() {
    return phone;
}

public String getSkype() {
    return skype;
}

I changed my UserExtra class to map the User and UserExtra ids so that they are mirrored. This accelerates the retrieval process and makes more sense as UserExtra really is just an extension to User :

public class UserExtra implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    private Long id;

    @Column(name = "phone")
    private String phone;

    @Column(name = "skype")
    private String skype;

    @OneToOne
    @MapsId
    private User user;
    ...
}

I created a new custom function called createUser() in UserService which needs my two fields in addition to the basic ones. I didn't update the existing function so that I don't have to change the test classes :

public User createUser(String login, String password, String firstName, String lastName, String email,
                       String langKey, String phone, String skype) {

    User newUser = new User();
    Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER);
    Set<Authority> authorities = new HashSet<>();
    String encryptedPassword = passwordEncoder.encode(password);
    newUser.setLogin(login);
    // new user gets initially a generated password
    newUser.setPassword(encryptedPassword);
    newUser.setFirstName(firstName);
    newUser.setLastName(lastName);
    newUser.setEmail(email);
    newUser.setLangKey(langKey);
    // new user is not active
    newUser.setActivated(false);
    // new user gets registration key
    newUser.setActivationKey(RandomUtil.generateActivationKey());
    authorities.add(authority);
    newUser.setAuthorities(authorities);
    userRepository.save(newUser);
    userSearchRepository.save(newUser);
    log.debug("Created Information for User: {}", newUser);

    // Create and save the UserExtra entity
    UserExtra newUserExtra = new UserExtra();
    newUserExtra.setUser(newUser);
    newUserExtra.setPhone(phone);
    newUserExtra.setSkype(skype);
    userExtraRepository.save(newUserExtra);
    userExtraSearchRepository.save(newUserExtra);
    log.debug("Created Information for UserExtra: {}", newUserExtra);

    return newUser;
}

Finally, I updated the registerAccount() function of AccountResource to call my custom function using the two additional fields :

public ResponseEntity<?> registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {

    HttpHeaders textPlainHeaders = new HttpHeaders();
    textPlainHeaders.setContentType(MediaType.TEXT_PLAIN);

    return userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase())
        .map(user -> new ResponseEntity<>("login already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
        .orElseGet(() -> userRepository.findOneByEmail(managedUserVM.getEmail())
            .map(user -> new ResponseEntity<>("e-mail address already in use", textPlainHeaders, HttpStatus.BAD_REQUEST))
            .orElseGet(() -> {
                User user = userService
                    .createUser(managedUserVM.getLogin(), managedUserVM.getPassword(),
                        managedUserVM.getFirstName(), managedUserVM.getLastName(),
                        managedUserVM.getEmail().toLowerCase(), managedUserVM.getLangKey(),
                        managedUserVM.getPhone(), managedUserVM.getSkype());

                mailService.sendActivationEmail(user);
                return new ResponseEntity<>(HttpStatus.CREATED);
            })
    );
}
like image 721
Paul-Etienne Avatar asked Feb 01 '17 17:02

Paul-Etienne


1 Answers

ManagedUserVM is a view model so simplest way is to add your extra fields to it, a view model can be an aggregate of several entities. This way you don't change your angular service, your client code does not need to know how a User is stored on server.

Then on server, you should consider modifying the UserExtra entity to use same id as User rather than a generated id, use @MapIds. See this very nice article for details.

like image 54
Gaël Marziou Avatar answered Oct 31 '22 18:10

Gaël Marziou