package io.pivotal.accounts.controller;
import java.math.BigDecimal;
import io.pivotal.accounts.domain.Account;
import io.pivotal.accounts.service.AccountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
/**
* REST controller for the accounts microservice. Provides the following
* endpoints:
* <p>
* <ul>
* <li>GET <code>/accounts/{id}</code> retrieves the account with given id.
* <li>POST <code>/accounts</code> stores the account object passed in body.
* <li>GET <code>/accounts/{id}/increaseBalance/{amount}</code> increases the
* balance of the account with given id by amount.
* <li>GET <code>/accounts/{id}/decreaseBalance/{amount}</code> decreases the
* balance of the account with given id by amount.
* </ul>
* <p>
*
* @author David Ferreira Pinto
* @author Maxim Avezbakiev
*
*/
@RestController
public class AccountController {
private static final Logger logger = LoggerFactory.getLogger(AccountController.class);
/**
* The service to delegate calls to.
*/
@Autowired
private AccountService service;
/**
* REST call to retrieve the account with the given id as userId.
*
* @param id
* The id of the user to retrieve the account for.
* @return The account object if found.
*/
@RequestMapping(value = "/account/{id}", method = RequestMethod.GET)
public ResponseEntity<Account> find(@PathVariable("id") final Integer id) {
logger.info("AccountController.find: id=" + id);
Account accountResponse = this.service.findAccount(id);
return new ResponseEntity<Account>(accountResponse, getNoCacheHeaders(), HttpStatus.OK);
}
// TODO: do we need this? need to change web service to use find() above.
@RequestMapping(value = "/account/", method = RequestMethod.GET)
public ResponseEntity<Account> findAccount(@RequestParam(value = "name") final String id) {
logger.info("AccountController.findAccount: id=" + id);
Account accountResponse = this.service.findAccount(id);
return new ResponseEntity<Account>(accountResponse, getNoCacheHeaders(), HttpStatus.OK);
}
/**
* REST call to save the account provided in the request body.
*
* @param accountRequest
* The account to save.
* @param builder
* @return
*/
@RequestMapping(value = "/account", method = RequestMethod.POST)
public ResponseEntity<String> save(@RequestBody Account accountRequest, UriComponentsBuilder builder) {
logger.debug("AccountController.save: userId=" + accountRequest.getUserid());
Integer accountProfileId = this.service.saveAccount(accountRequest);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setLocation(builder.path("/account/{id}").buildAndExpand(accountProfileId).toUri());
return new ResponseEntity<String>(responseHeaders, HttpStatus.CREATED);
}
/**
* REST call to decrease the balance in the account. Decreases the balance
* of the account if the new balance is not lower than zero. Returns HTTP OK
* and the new balance if the decrease was successful, or HTTP
* EXPECTATION_FAILED if the new balance would be negative and the
* old/current balance.
*
* @param userId
* The id of the account.
* @param amount
* The amount to decrease the balance by.
* @return The new balance of the account with HTTP OK.
*/
@RequestMapping(value = "/accounts/{userId}/decreaseBalance/{amount}", method = RequestMethod.GET)
public ResponseEntity<Double> decreaseBalance(@PathVariable("userId") final String userId, @PathVariable("amount") final double amount) {
logger.debug("AccountController.decreaseBalance: id='" + userId + "', amount='" + amount + "'");
Account accountResponse = this.service.findAccount(userId);
BigDecimal currentBalance = accountResponse.getBalance();
BigDecimal newBalance = currentBalance.subtract(new BigDecimal(amount));
if (newBalance.compareTo(BigDecimal.ZERO) >= 0) {
accountResponse.setBalance(newBalance);
this.service.saveAccount(accountResponse);
return new ResponseEntity<Double>(accountResponse.getBalance().doubleValue(), getNoCacheHeaders(), HttpStatus.OK);
} else {
// no sufficient founds available
return new ResponseEntity<Double>(accountResponse.getBalance().doubleValue(), getNoCacheHeaders(), HttpStatus.EXPECTATION_FAILED);
}
}
/**
* REST call to increase the balance in the account. Increases the balance
* of the account if the amount is not negative. Returns HTTP OK and the new
* balance if the increase was successful, or HTTP EXPECTATION_FAILED if the
* amount given is negative.
*
* @param userId
* The id of the account.
* @param amount
* The amount to increase the balance by.
* @return The new balance of the account with HTTP OK.
*/
@RequestMapping(value = "/accounts/{userId}/increaseBalance/{amount}", method = RequestMethod.GET)
public ResponseEntity<Double> increaseBalance(@PathVariable("userId") final String userId, @PathVariable("amount") final double amount) {
logger.debug("AccountController.increaseBalance: id='" + userId + "', amount='" + amount + "'");
Account accountResponse = this.service.findAccount(userId);
BigDecimal currentBalance = accountResponse.getBalance();
logger.debug("AccountController.increaseBalance: current balance='" + currentBalance + "'.");
if (amount > 0) {
BigDecimal newBalance = currentBalance.add(new BigDecimal(amount));
logger.debug("AccountController.increaseBalance: new balance='" + newBalance + "'.");
accountResponse.setBalance(newBalance);
this.service.saveAccount(accountResponse);
return new ResponseEntity<Double>(accountResponse.getBalance().doubleValue(), getNoCacheHeaders(), HttpStatus.OK);
} else {
// amount can not be negative for increaseBalance, please use
// decreaseBalance
return new ResponseEntity<Double>(accountResponse.getBalance().doubleValue(), getNoCacheHeaders(), HttpStatus.EXPECTATION_FAILED);
}
}
private HttpHeaders getNoCacheHeaders() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("Cache-Control", "no-cache");
return responseHeaders;
}
}