Skip to content

section 6 - Spring MVC Rest Services #38

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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,40 @@
<description>spring-6-rest-mvc</description>
<properties>
<java.version>21</java.version>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand All @@ -48,6 +70,35 @@
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>21</source>
<target>21</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<version>0.2.0</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package guru.springframework.spring6restmvc.bootstrap;

import guru.springframework.spring6restmvc.entities.Beer;
import guru.springframework.spring6restmvc.entities.Customer;
import guru.springframework.spring6restmvc.model.BeerStyle;
import guru.springframework.spring6restmvc.repositories.BeerRepository;
import guru.springframework.spring6restmvc.repositories.CustomerRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;

/**
* Created by jt, Spring Framework Guru.
*/
@Component
@RequiredArgsConstructor
public class BootstrapData implements CommandLineRunner {
private final BeerRepository beerRepository;
private final CustomerRepository customerRepository;

@Override
public void run(String... args) throws Exception {
loadBeerData();
loadCustomerData();
}

private void loadBeerData() {
if (beerRepository.count() == 0){
Beer beer1 = Beer.builder()
.beerName("Galaxy Cat")
.beerStyle(BeerStyle.PALE_ALE)
.upc("12356")
.price(new BigDecimal("12.99"))
.quantityOnHand(122)
.createdDate(LocalDateTime.now())
.updateDate(LocalDateTime.now())
.build();

Beer beer2 = Beer.builder()
.beerName("Crank")
.beerStyle(BeerStyle.PALE_ALE)
.upc("12356222")
.price(new BigDecimal("11.99"))
.quantityOnHand(392)
.createdDate(LocalDateTime.now())
.updateDate(LocalDateTime.now())
.build();

Beer beer3 = Beer.builder()
.beerName("Sunshine City")
.beerStyle(BeerStyle.IPA)
.upc("12356")
.price(new BigDecimal("13.99"))
.quantityOnHand(144)
.createdDate(LocalDateTime.now())
.updateDate(LocalDateTime.now())
.build();

beerRepository.save(beer1);
beerRepository.save(beer2);
beerRepository.save(beer3);
}

}

private void loadCustomerData() {

if (customerRepository.count() == 0) {
Customer customer1 = Customer.builder()
.name("Customer 1")
.version(1)
.createdDate(LocalDateTime.now())
.updateDate(LocalDateTime.now())
.build();

Customer customer2 = Customer.builder()
.name("Customer 2")
.version(1)
.createdDate(LocalDateTime.now())
.updateDate(LocalDateTime.now())
.build();

Customer customer3 = Customer.builder()
.name("Customer 3")
.version(1)
.createdDate(LocalDateTime.now())
.updateDate(LocalDateTime.now())
.build();

customerRepository.saveAll(Arrays.asList(customer1, customer2, customer3));
}

}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package guru.springframework.spring6restmvc.controller;

import guru.springframework.spring6restmvc.model.BeerDTO;
import guru.springframework.spring6restmvc.services.BeerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;

/**
* Created by jt, Spring Framework Guru.
*/
@Slf4j
@RequiredArgsConstructor
@RestController
public class BeerController {

public static final String BEER_PATH = "/api/v1/beer";
public static final String BEER_PATH_ID = BEER_PATH + "/{beerId}";

private final BeerService beerService;

@PatchMapping(BEER_PATH_ID)
public ResponseEntity updateBeerPatchById(@PathVariable("beerId")UUID beerId, @RequestBody BeerDTO beer){

beerService.patchBeerById(beerId, beer);

return new ResponseEntity(HttpStatus.NO_CONTENT);
}

@DeleteMapping(BEER_PATH_ID)
public ResponseEntity deleteById(@PathVariable("beerId") UUID beerId){

if(! beerService.deleteById(beerId)){
throw new NotFoundException();
}

return new ResponseEntity(HttpStatus.NO_CONTENT);
}

@PutMapping(BEER_PATH_ID)
public ResponseEntity updateById(@PathVariable("beerId")UUID beerId, @Validated @RequestBody BeerDTO beer){

if( beerService.updateBeerById(beerId, beer).isEmpty()){
throw new NotFoundException();
}

return new ResponseEntity(HttpStatus.NO_CONTENT);
}

@PostMapping(BEER_PATH)
public ResponseEntity handlePost(@Validated @RequestBody BeerDTO beer){

BeerDTO savedBeer = beerService.saveNewBeer(beer);

HttpHeaders headers = new HttpHeaders();
headers.add("Location", BEER_PATH + "/" + savedBeer.getId().toString());

return new ResponseEntity(headers, HttpStatus.CREATED);
}

@GetMapping(value = BEER_PATH)
public List<BeerDTO> listBeers(){
return beerService.listBeers();
}


@GetMapping(value = BEER_PATH_ID)
public BeerDTO getBeerById(@PathVariable("beerId") UUID beerId){

log.debug("Get Beer by Id - in controller");

return beerService.getBeerById(beerId).orElseThrow(NotFoundException::new);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package guru.springframework.spring6restmvc.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Created by jt, Spring Framework Guru.
*/
@ControllerAdvice
public class CustomErrorController {

@ExceptionHandler
ResponseEntity handleJPAViolations(TransactionSystemException exception){
return ResponseEntity.badRequest().build();
}

@ExceptionHandler(MethodArgumentNotValidException.class)
ResponseEntity handleBindErrors(MethodArgumentNotValidException exception){

List errorList = exception.getFieldErrors().stream()
.map(fieldError -> {
Map<String, String > errorMap = new HashMap<>();
errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
return errorMap;
}).collect(Collectors.toList());

return ResponseEntity.badRequest().body(errorList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package guru.springframework.spring6restmvc.controller;

import guru.springframework.spring6restmvc.model.CustomerDTO;
import guru.springframework.spring6restmvc.services.CustomerService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.UUID;

/**
* Created by jt, Spring Framework Guru.
*/

@RequiredArgsConstructor
@RestController
public class CustomerController {
public static final String CUSTOMER_PATH = "/api/v1/customer";
public static final String CUSTOMER_PATH_ID = CUSTOMER_PATH + "/{customerId}";

private final CustomerService customerService;

@PatchMapping(CUSTOMER_PATH_ID)
public ResponseEntity patchCustomerById(@PathVariable("customerId") UUID customerId,
@RequestBody CustomerDTO customer){

customerService.patchCustomerById(customerId, customer);

return new ResponseEntity(HttpStatus.NO_CONTENT);
}

@DeleteMapping(CUSTOMER_PATH_ID)
public ResponseEntity deleteCustomerById(@PathVariable("customerId") UUID customerId){

if (!customerService.deleteCustomerById(customerId)){
throw new NotFoundException();
}

return new ResponseEntity(HttpStatus.NO_CONTENT);
}

@PutMapping(CUSTOMER_PATH_ID)
public ResponseEntity updateCustomerByID(@PathVariable("customerId") UUID customerId,
@RequestBody CustomerDTO customer){

if (customerService.updateCustomerById(customerId, customer).isEmpty()){
throw new NotFoundException();
}

return new ResponseEntity(HttpStatus.NO_CONTENT);
}

@PostMapping(CUSTOMER_PATH)
public ResponseEntity handlePost(@RequestBody CustomerDTO customer){
CustomerDTO savedCustomer = customerService.saveNewCustomer(customer);

HttpHeaders headers = new HttpHeaders();
headers.add("Location", CUSTOMER_PATH + "/" + savedCustomer.getId().toString());

return new ResponseEntity(headers, HttpStatus.CREATED);
}

@GetMapping(CUSTOMER_PATH)
public List<CustomerDTO> listAllCustomers(){
return customerService.getAllCustomers();
}

@GetMapping(value = CUSTOMER_PATH_ID)
public CustomerDTO getCustomerById(@PathVariable("customerId") UUID id){
return customerService.getCustomerById(id).orElseThrow(NotFoundException::new);
}

}
Loading