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

feat: routes for buy and sell stocks #54

Merged
merged 12 commits into from
Mar 6, 2024
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.mandacarubroker.controller;

import com.mandacarubroker.domain.position.RequestStockOwnershipDTO;
import com.mandacarubroker.domain.position.ResponseStockOwnershipDTO;
import com.mandacarubroker.domain.user.ResponseUserDTO;
import com.mandacarubroker.service.PortfolioService;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
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.RestController;

Expand All @@ -21,4 +27,12 @@ public PortfolioController(final PortfolioService receivedPortfolioService) {
public List<ResponseStockOwnershipDTO> getAuthenticatedUserStockPortfolio() {
return portfolioService.getAuthenticatedUserStockPortfolio();
}

@GetMapping("/{stockId}/buy")
public ResponseEntity<ResponseUserDTO> buyStock(
@PathVariable final String stockId,
@RequestBody @Valid final RequestStockOwnershipDTO shares) {
ResponseUserDTO user = portfolioService.buyStock(stockId, shares);
return ResponseEntity.ok(user);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,3 @@ public ResponseEntity<Object> deleteUser(@PathVariable final String id) {
return ResponseEntity.noContent().build();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@

public interface StockOwnershipRepository extends JpaRepository<StockOwnership, String> {
List<StockOwnership> findByUserId(String userId);
StockOwnership findByUserIdAndStockId(String userId, String stockId);
}
103 changes: 101 additions & 2 deletions src/main/java/com/mandacarubroker/service/PortfolioService.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
package com.mandacarubroker.service;

import com.mandacarubroker.domain.position.RequestStockOwnershipDTO;
import com.mandacarubroker.domain.position.ResponseStockOwnershipDTO;
import com.mandacarubroker.domain.position.StockOwnership;
import com.mandacarubroker.domain.position.StockOwnershipRepository;
import com.mandacarubroker.domain.stock.Stock;
import com.mandacarubroker.domain.user.ResponseUserDTO;
import com.mandacarubroker.domain.user.User;
import com.mandacarubroker.domain.user.UserRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class PortfolioService {
private final StockOwnershipRepository stockPositionRepository;
private final StockService stockService;
private final UserRepository userRepository;

public PortfolioService(final StockOwnershipRepository receivedStockPositionRepository) {
public PortfolioService(
final StockOwnershipRepository receivedStockPositionRepository,
final StockService receivedStockService,
final UserRepository recievedUserRepository
) {
this.stockPositionRepository = receivedStockPositionRepository;
this.stockService = receivedStockService;
this.userRepository = recievedUserRepository;
}

public List<ResponseStockOwnershipDTO> getAuthenticatedUserStockPortfolio() {
User user = AuthService.getAuthenticatedUser();
String userId = user.getId();
Expand All @@ -29,4 +41,91 @@ public List<ResponseStockOwnershipDTO> getPortfolioByUserId(final String userId)
.map(ResponseStockOwnershipDTO::fromStockPosition)
.toList();
}

public Optional<ResponseStockOwnershipDTO> getStockPositionByUserIdAndStockId(final String stockId) {
User user = AuthService.getAuthenticatedUser();
String userId = user.getId();

StockOwnership stockPosition = stockPositionRepository.findByUserIdAndStockId(userId, stockId);

if (stockPosition == null) {
return Optional.empty();
}

return Optional.of(ResponseStockOwnershipDTO.fromStockPosition(stockPosition));
}


public StockOwnership createStockPosition(
final RequestStockOwnershipDTO requestStockOwnershipDTO,
final Stock stock,
final User user) {
StockOwnership newStockPosition = new StockOwnership(requestStockOwnershipDTO, stock, user);
return stockPositionRepository.save(newStockPosition);
}

public Optional<StockOwnership> updateShares(
final String stockOwnershipId,
final RequestStockOwnershipDTO requestStockOwnershipDTO
) {
return stockPositionRepository.findById(stockOwnershipId)
.map(stockOwnership -> {
stockOwnership.setShares(requestStockOwnershipDTO.shares());
return stockPositionRepository.save(stockOwnership);
});
}


public ResponseStockOwnershipDTO addStockInPortfolio(
final Stock stock,
final User user,
final RequestStockOwnershipDTO shares
) {

Optional<ResponseStockOwnershipDTO> existentStockPosition =
getStockPositionByUserIdAndStockId(stock.getId());

if (existentStockPosition.isPresent()) {
Optional<StockOwnership> updatedStockPosition = updateShares(
existentStockPosition.get().id(),
new RequestStockOwnershipDTO(
existentStockPosition.get().totalShares()
+ shares.shares())
);

if (updatedStockPosition.isEmpty()) {
throw new IllegalStateException("Error on update shares in portfolio");
}
return ResponseStockOwnershipDTO.fromStockPosition(
updatedStockPosition.get()
);
}

return ResponseStockOwnershipDTO.fromStockPosition(
createStockPosition(
new RequestStockOwnershipDTO(shares.shares()),
stock,
user
)
);
}

public ResponseUserDTO buyStock(final String stockId, final RequestStockOwnershipDTO shares) {
User user = AuthService.getAuthenticatedUser();
Optional<Stock> stock = stockService.getStockById(stockId);

if (stock.isEmpty()) {
throw new IllegalStateException("Stock nonexistent");
}
double userBalance = user.getBalance();
double stockBoughtPrice = stock.get().getPrice() * shares.shares();
if (userBalance < stockBoughtPrice) {
throw new IllegalStateException("Insufficient balance");
}

addStockInPortfolio(stock.get(), user, shares);
user.setBalance(userBalance - stockBoughtPrice);

return ResponseUserDTO.fromUser(userRepository.save(user));
}
}
1 change: 1 addition & 0 deletions src/main/java/com/mandacarubroker/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,4 @@ public Optional<ResponseUserDTO> findByUsername(final String username) {
return user.map(this::userToResponseUserDTO);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.mandacarubroker.domain.stock.Stock;
import com.mandacarubroker.domain.user.RequestUserDTO;
import com.mandacarubroker.domain.user.User;
import com.mandacarubroker.domain.user.UserRepository;
import com.mandacarubroker.security.SecuritySecretsMock;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -24,6 +25,8 @@
public class PortfolioServiceTest {
@MockBean
private StockOwnershipRepository stockPositionRepository;
private StockService stockService;
private UserRepository userRepository;

private PortfolioService portfolioService;

Expand Down Expand Up @@ -60,7 +63,14 @@ void setUp() {
stockPositionRepository = Mockito.mock(StockOwnershipRepository.class);
Mockito.when(stockPositionRepository.findByUserId(validUser.getId())).thenReturn(givenStockOwnerships);

portfolioService = new PortfolioService(stockPositionRepository);
stockService = Mockito.mock(StockService.class);
userRepository = Mockito.mock(UserRepository.class);

portfolioService = new PortfolioService(
stockPositionRepository,
stockService,
userRepository
);
}

@Test
Expand Down
Loading