HackerRank Springboot Test — Trades Api
HackerRank has a Backend role assessment Test where they ask you to design REST Apis and demonstrate your knowledge in API design.
In this tutorial, we will discuss how to solve the Hacker Rank problem for writing REST APIs for trading stocks using Springboot and JPA.
HackerRank has a Backend role assessment Test where they ask you to design REST Apis and demonstrate your knowledge in API design.
Problem Statement :
Example of a trade data JSON object :
{
"id":1,
"type": "buy",
"userId": 23,
"symbol": "ABX",
"shares": 30,
"price": 134,
"timestamp": 1531522701000
}
The REST service must expose the /trades
endpoint, which allows for managing the collection of trade records in the following way:
POST request to /trades
:
creates a new trade
expects a JSON trade object without an id property as a body payload. You can assume that the given object is always valid.
adds the given trade object to the collection of trades and assigns a unique integer id to it. The first created trade must have id 1, the second one 2, and so on.
the response code is 201, and the response body is the created trade object
GET request to /trades
:
return a collection of all trades
the response code is 200, and the response body is an array of all trade objects ordered by their ids in increasing order
optionally accepts query parameters type and userId, for example
/trades/?type=buy&userId=122
. All these parameters are optional. In case they are present, only objects matching the parameters must be returned.
GET request to /trades/<id>
:
returns a trade with the given id
if the matching trade exists, the response code is 200 and the response body is the matching trade object
if there is no trade with the given id in the collection, the response code is 404
DELETE, PUT, PATCH request to /trades/<id>
:
the response code is 405 because the API does not allow deleting or modifying trades for any id value
Writing APIs in springboot is fairly easy and has in build support, I would not give you steps to download or set up the environment for spring boot rather you can use some online resources to configure your development environment.
I have used Eclipse with h2 Database support but you can use PostgreSQL it is fairly easy to set up.
Prerequisite :
1 Setup Spring boot starter App using https://start.spring.io/ .
2. Download and set up the Database or use h2 db that don’t any need extra configuration.
3. Install Eclipse.
Design The Schema :
For the given JSON let’s create a Table or Entity in Sprinboot, as we know that Entities in Springboot are class representations of Database tables.
@Entity
public class StockTrade {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String type;
private Integer userId;
private String symbol;
private Integer shares;
private Integer price;
private Long timestamp;
public StockTrade() {
}
}
// Getter and Setters
I will create a JPARepository corresponding to this table to retrieve the rows from the Table
@Repository
public interface StockTradeRepository extends JpaRepository<StockTrade, Integer> {
}
Service
Now I will create a Service class that will be responsible for accessing this Table and getting the databases upon the requirements :
package com.hackerrank.stocktrades.service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.hackerrank.stocktrades.model.StockTrade;
import com.hackerrank.stocktrades.model.StockTradeDTO;
import com.hackerrank.stocktrades.repository.StockTradeRepository;
@Service
public class TradeService {
@Autowired
StockTradeRepository stockRepository;
private static ModelMapper modelMapper = new ModelMapper();
public StockTradeDTO createNewStock(StockTradeDTO stockTradeDto) throws java.text.ParseException {
stockTradeDto.setId(null);
StockTrade stockEntity = modelMapper.map(stockTradeDto, StockTrade.class);
stockEntity = stockRepository.save(stockEntity);
return getStockDto(stockEntity);
}
public List<StockTradeDTO> getStock() {
return convertEntityToDTO(stockRepository.findAll());
}
public List<StockTradeDTO> filterBy(String type, Integer userId) {
List<StockTrade> trades = stockRepository.findAll();
if (type != null && userId != null) {
// Filter trades by both type and userId
trades = trades.stream().filter(t -> t.getType().equals(type) && t.getUserId() == userId)
.collect(Collectors.toList());
} else if (type != null) {
// Filter trades by type
trades = trades.stream().filter(t -> t.getType().equals(type)).collect(Collectors.toList());
} else if (userId != null) {
// Filter trades by userId
trades = trades.stream().filter(t -> t.getUserId() == userId).collect(Collectors.toList());
}
return convertEntityToDTO(trades);
}
public StockTradeDTO getStockById(int id) {
if(stockRepository.existsById(id)) {
StockTrade stockTradeEntity = stockRepository.findById(id).get();
return modelMapper.map(stockTradeEntity, StockTradeDTO.class);
}
else {
return null;
}
}
private List<StockTradeDTO> convertEntityToDTO(List<StockTrade> list) {
List<StockTradeDTO> dtoList = new ArrayList<StockTradeDTO>();
for (StockTrade stock : list) {
dtoList.add(modelMapper.map(stock, StockTradeDTO.class));
}
return dtoList;
}
private StockTradeDTO getStockDto(StockTrade stockEntity) {
StockTradeDTO w = modelMapper.map(stockEntity, StockTradeDTO.class);
return w;
}
}
In Service Class, I have methods to Create Stock, Get Stock by Id, and filter Stock by Type :
1. createNewStock(StockTradeDTO stockTradeDto)
2. getStock()
3. filterBy(String type, Integer userId)
4. getStockById(int id)
Controller :
Let’s call these service methods from the Controller and expose them :
StockTradeRestController.Java
@RestController
@RequestMapping(path = "/trades")
public class StockTradeRestController {
@Autowired
TradeService tradeService;
}
POST request to /trades
:
Add createStock Method in the controller , It will create an new Stock in the Database.
@PostMapping
public ResponseEntity<?> createStock(@RequestBody StockTradeDTO weather) throws ParseException {
StockTradeDTO w = tradeService.createNewStock(weather);
return new ResponseEntity<>(w, HttpStatus.CREATED);
}
GET request to /trades
:
GET request to /trades/<id>
:
Add getAllTrades() and getWeatherById(@PathVariable int id) in the controller class, It will return all Stocks from the Database.
@GetMapping()
public ResponseEntity<?> getAllTrades() {
return ResponseEntity.ok(tradeService.getStock());
}
@GetMapping("/{id}")
public ResponseEntity<?> getWeatherById(@PathVariable int id) {
StockTradeDTO trade = tradeService.getStockById(id);
if (trade != null) {
return new ResponseEntity<>(trade, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
Lets add Filter by type and StockId :
@GetMapping("/")
public ResponseEntity<?> getFilters(@RequestParam(required = false) String type,
@RequestParam(required = false) Integer userId) {
List<StockTradeDTO> weathers = new ArrayList<>();
if (type != null || userId != null) {
weathers = tradeService.filterBy(type, userId);
return ResponseEntity.ok(weathers);
}
return ResponseEntity.ok(tradeService.getStock());
}
In the last how my pom.xml looksalike, add these depencies in :
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197</version>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.4.5</version> <!-- Or the latest version available -->
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version> <!-- or the latest version -->
</dependency>
</dependencies>
Add these in application.properties to configure the Database:
server.port=8080
server.address=0.0.0.0
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
I hope it will help you to solve the Hacker Rank Backend role assessment, Most of the questions are almost similar.