BAEL-684 Adding spring JPA to resource servers
This commit is contained in:
parent
5f0f26e374
commit
2ee0a8a7a1
|
@ -1,69 +0,0 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.gateway;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.springframework.boot.test.web.client.TestRestTemplate;
|
|
||||||
import org.springframework.http.*;
|
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
|
||||||
import org.springframework.util.MultiValueMap;
|
|
||||||
|
|
||||||
public class GatewayApplicationLiveTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAccess() throws Exception {
|
|
||||||
TestRestTemplate testRestTemplate = new TestRestTemplate();
|
|
||||||
String testUrl = "http://localhost:8080";
|
|
||||||
|
|
||||||
ResponseEntity<String> response = testRestTemplate.getForEntity(testUrl + "/book-service/books", String.class);
|
|
||||||
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
|
|
||||||
Assert.assertNotNull(response.getBody());
|
|
||||||
|
|
||||||
//try the protected resource and confirm the redirect to login
|
|
||||||
response = testRestTemplate.getForEntity(testUrl + "/book-service/books/1", String.class);
|
|
||||||
Assert.assertEquals(HttpStatus.FOUND, response.getStatusCode());
|
|
||||||
Assert.assertEquals("http://localhost:8080/login", response.getHeaders().get("Location").get(0));
|
|
||||||
|
|
||||||
//login as user/password
|
|
||||||
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
|
||||||
form.add("username", "user");
|
|
||||||
form.add("password", "password");
|
|
||||||
response = testRestTemplate.postForEntity(testUrl + "/login", form, String.class);
|
|
||||||
|
|
||||||
//extract the session from the cookie and propagate it to the next request
|
|
||||||
String sessionCookie = response.getHeaders().get("Set-Cookie").get(0).split(";")[0];
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.add("Cookie", sessionCookie);
|
|
||||||
HttpEntity<String> httpEntity = new HttpEntity<>(headers);
|
|
||||||
|
|
||||||
//request the protected resource
|
|
||||||
response = testRestTemplate.exchange(testUrl + "/book-service/books/1", HttpMethod.GET, httpEntity, String.class);
|
|
||||||
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
|
|
||||||
Assert.assertNotNull(response.getBody());
|
|
||||||
|
|
||||||
//request the admin protected resource to determine it is still protected
|
|
||||||
response = testRestTemplate.exchange(testUrl + "/rating-service/ratings/all", HttpMethod.GET, httpEntity, String.class);
|
|
||||||
Assert.assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
|
|
||||||
|
|
||||||
//login as the admin
|
|
||||||
form.clear();
|
|
||||||
form.add("username", "admin");
|
|
||||||
form.add("password", "admin");
|
|
||||||
response = testRestTemplate.postForEntity(testUrl + "/login", form, String.class);
|
|
||||||
|
|
||||||
//extract the session from the cookie and propagate it to the next request
|
|
||||||
sessionCookie = response.getHeaders().get("Set-Cookie").get(0).split(";")[0];
|
|
||||||
headers = new HttpHeaders();
|
|
||||||
headers.add("Cookie", sessionCookie);
|
|
||||||
httpEntity = new HttpEntity<>(headers);
|
|
||||||
|
|
||||||
//request the protected resource
|
|
||||||
response = testRestTemplate.exchange(testUrl + "/rating-service/ratings/all", HttpMethod.GET, httpEntity, String.class);
|
|
||||||
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
|
|
||||||
Assert.assertNotNull(response.getBody());
|
|
||||||
|
|
||||||
//request the discovery resources as the admin
|
|
||||||
response = testRestTemplate.exchange(testUrl + "/discovery", HttpMethod.GET, httpEntity, String.class);
|
|
||||||
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.gateway;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.boot.test.web.client.TestRestTemplate;
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
|
public class IntegrationTest {
|
||||||
|
|
||||||
|
private TestRestTemplate testRestTemplate = new TestRestTemplate();
|
||||||
|
private String testUrl = "http://localhost:8080";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccess() throws Exception {
|
||||||
|
ResponseEntity<String> response = testRestTemplate.getForEntity(testUrl + "/book-service/books", String.class);
|
||||||
|
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
Assert.assertNotNull(response.getBody());
|
||||||
|
|
||||||
|
//try the protected resource and confirm the redirect to login
|
||||||
|
response = testRestTemplate.getForEntity(testUrl + "/book-service/books/1", String.class);
|
||||||
|
Assert.assertEquals(HttpStatus.FOUND, response.getStatusCode());
|
||||||
|
Assert.assertEquals("http://localhost:8080/login", response.getHeaders().get("Location").get(0));
|
||||||
|
|
||||||
|
//login as user/password
|
||||||
|
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
||||||
|
form.add("username", "user");
|
||||||
|
form.add("password", "password");
|
||||||
|
response = testRestTemplate.postForEntity(testUrl + "/login", form, String.class);
|
||||||
|
|
||||||
|
//extract the session from the cookie and propagate it to the next request
|
||||||
|
String sessionCookie = response.getHeaders().get("Set-Cookie").get(0).split(";")[0];
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add("Cookie", sessionCookie);
|
||||||
|
HttpEntity<String> httpEntity = new HttpEntity<>(headers);
|
||||||
|
|
||||||
|
addBook();
|
||||||
|
|
||||||
|
//request the protected resource
|
||||||
|
response = testRestTemplate.exchange(testUrl + "/book-service/books/1", HttpMethod.GET, httpEntity, String.class);
|
||||||
|
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
Assert.assertNotNull(response.getBody());
|
||||||
|
|
||||||
|
addRatings();
|
||||||
|
|
||||||
|
//request the admin protected resource to determine it is still protected
|
||||||
|
response = testRestTemplate.exchange(testUrl + "/rating-service/ratings", HttpMethod.GET, httpEntity, String.class);
|
||||||
|
Assert.assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
|
||||||
|
|
||||||
|
//login as the admin
|
||||||
|
form.clear();
|
||||||
|
form.add("username", "admin");
|
||||||
|
form.add("password", "admin");
|
||||||
|
response = testRestTemplate.postForEntity(testUrl + "/login", form, String.class);
|
||||||
|
|
||||||
|
//extract the session from the cookie and propagate it to the next request
|
||||||
|
sessionCookie = response.getHeaders().get("Set-Cookie").get(0).split(";")[0];
|
||||||
|
headers = new HttpHeaders();
|
||||||
|
headers.add("Cookie", sessionCookie);
|
||||||
|
httpEntity = new HttpEntity<>(headers);
|
||||||
|
|
||||||
|
//request the protected resource
|
||||||
|
response = testRestTemplate.exchange(testUrl + "/rating-service/ratings", HttpMethod.GET, httpEntity, String.class);
|
||||||
|
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
Assert.assertNotNull(response.getBody());
|
||||||
|
|
||||||
|
//request the discovery resources as the admin
|
||||||
|
response = testRestTemplate.exchange(testUrl + "/discovery", HttpMethod.GET, httpEntity, String.class);
|
||||||
|
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRatings() {
|
||||||
|
//login as user/password
|
||||||
|
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
||||||
|
form.add("username", "user");
|
||||||
|
form.add("password", "password");
|
||||||
|
ResponseEntity<String> response = testRestTemplate.postForEntity(testUrl + "/login", form, String.class);
|
||||||
|
|
||||||
|
//extract the session from the cookie and propagate it to the next request
|
||||||
|
String sessionCookie = response.getHeaders().get("Set-Cookie").get(0).split(";")[0];
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add("Cookie", sessionCookie);
|
||||||
|
headers.add("ContentType", ContentType.APPLICATION_JSON.getMimeType());
|
||||||
|
Rating rating = new Rating(1L, 4);
|
||||||
|
|
||||||
|
HttpEntity<Rating> httpEntity = new HttpEntity<>(rating, headers);
|
||||||
|
|
||||||
|
//request the protected resource
|
||||||
|
ResponseEntity<Rating> bookResponse = testRestTemplate.postForEntity(testUrl + "/rating-service/ratings", httpEntity, Rating.class);
|
||||||
|
Assert.assertEquals(HttpStatus.OK, bookResponse.getStatusCode());
|
||||||
|
Assert.assertEquals(rating.getBookId(), bookResponse.getBody().getBookId());
|
||||||
|
Assert.assertEquals(rating.getStars(), bookResponse.getBody().getStars());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBook(){
|
||||||
|
//login as user/password
|
||||||
|
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
||||||
|
form.add("username", "admin");
|
||||||
|
form.add("password", "admin");
|
||||||
|
ResponseEntity<String> response = testRestTemplate.postForEntity(testUrl + "/login", form, String.class);
|
||||||
|
|
||||||
|
//extract the session from the cookie and propagate it to the next request
|
||||||
|
String sessionCookie = response.getHeaders().get("Set-Cookie").get(0).split(";")[0];
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add("Cookie", sessionCookie);
|
||||||
|
headers.add("ContentType", ContentType.APPLICATION_JSON.getMimeType());
|
||||||
|
Book book = new Book("Baeldung", "How to spring cloud");
|
||||||
|
|
||||||
|
HttpEntity<Book> httpEntity = new HttpEntity<>(book, headers);
|
||||||
|
|
||||||
|
//request the protected resource
|
||||||
|
ResponseEntity<Book> bookResponse = testRestTemplate.postForEntity(testUrl + "/book-service/books", httpEntity, Book.class);
|
||||||
|
Assert.assertEquals(HttpStatus.OK, bookResponse.getStatusCode());
|
||||||
|
Assert.assertEquals(book.getAuthor(), bookResponse.getBody().getAuthor());
|
||||||
|
Assert.assertEquals(book.getTitle(), bookResponse.getBody().getTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public static class Book {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String author;
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
public Book() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Book(String author, String title) {
|
||||||
|
this.author = author;
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthor() {
|
||||||
|
return author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthor(String author) {
|
||||||
|
this.author = author;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public static class Rating {
|
||||||
|
private Long id;
|
||||||
|
private Long bookId;
|
||||||
|
private int stars;
|
||||||
|
|
||||||
|
public Rating() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rating(Long bookId, int stars) {
|
||||||
|
this.bookId = bookId;
|
||||||
|
this.stars = stars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getBookId() {
|
||||||
|
return bookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBookId(Long bookId) {
|
||||||
|
this.bookId = bookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStars() {
|
||||||
|
return stars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStars(int stars) {
|
||||||
|
this.stars = stars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -42,6 +42,17 @@
|
||||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
|
|
@ -3,35 +3,11 @@ package com.baeldung.spring.cloud.bootstrap.svcbook;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableEurekaClient
|
@EnableEurekaClient
|
||||||
@RestController
|
|
||||||
@RequestMapping("/books")
|
|
||||||
public class BookServiceApplication {
|
public class BookServiceApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(BookServiceApplication.class, args);
|
SpringApplication.run(BookServiceApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Book> bookList = Arrays.asList(
|
|
||||||
new Book(1L, "Baeldung goes to the market", "Tim Schimandle"),
|
|
||||||
new Book(2L, "Baeldung goes to the park", "Slavisa")
|
|
||||||
);
|
|
||||||
|
|
||||||
@GetMapping("")
|
|
||||||
public List<Book> findAllBooks() {
|
|
||||||
return bookList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/{bookId}")
|
|
||||||
public Book findBook(@PathVariable Long bookId) {
|
|
||||||
return bookList.stream().filter(b -> b.getId().equals(bookId)).findFirst().orElse(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.baeldung.spring.cloud.bootstrap.svcbook;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
@ -22,8 +23,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
http.httpBasic()
|
http.httpBasic()
|
||||||
.disable()
|
.disable()
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers("/books").permitAll()
|
.antMatchers(HttpMethod.GET, "/books").permitAll()
|
||||||
.antMatchers("/books/*").hasAnyRole("USER", "ADMIN")
|
.antMatchers(HttpMethod.GET, "/books/*").permitAll()
|
||||||
|
.antMatchers(HttpMethod.POST, "/books").hasRole("ADMIN")
|
||||||
|
.antMatchers(HttpMethod.PATCH, "/books/*").hasRole("ADMIN")
|
||||||
|
.antMatchers(HttpMethod.DELETE, "/books/*").hasRole("ADMIN")
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
.and()
|
.and()
|
||||||
.csrf()
|
.csrf()
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.svcbook;
|
package com.baeldung.spring.cloud.bootstrap.svcbook.book;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class Book {
|
public class Book {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
private Long id;
|
private Long id;
|
||||||
private String author;
|
private String author;
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
public Book(Long id, String title, String author) {
|
|
||||||
this.id = id;
|
|
||||||
this.author = author;
|
|
||||||
this.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Book() {
|
public Book() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.svcbook.book;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/books")
|
||||||
|
public class BookController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BookService bookService;
|
||||||
|
|
||||||
|
@GetMapping("")
|
||||||
|
public List<Book> findAllBooks() {
|
||||||
|
return bookService.findAllBooks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{bookId}")
|
||||||
|
public Book findBook(@PathVariable Long bookId) {
|
||||||
|
return bookService.findBookById(bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("")
|
||||||
|
public Book createBook(@RequestBody Book book) {
|
||||||
|
return bookService.createBook(book);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{bookId}")
|
||||||
|
public void deleteBook(@PathVariable Long bookId) {
|
||||||
|
bookService.deleteBook(bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{bookId")
|
||||||
|
public Book updateBook(@RequestBody Map<String, String> updates, @PathVariable Long bookId) {
|
||||||
|
return bookService.updateBook(updates, bookId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.svcbook.book;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||||
|
class BookNotFoundException extends RuntimeException {
|
||||||
|
BookNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.svcbook.book;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
interface BookRepository extends JpaRepository<Book, Long>{
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.svcbook.book;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public class BookService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BookRepository bookRepository;
|
||||||
|
|
||||||
|
public List<Book> findAllBooks() {
|
||||||
|
return bookRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Book findBookById(Long bookId) {
|
||||||
|
return Optional.ofNullable(bookRepository.findOne(bookId))
|
||||||
|
.orElseThrow(() -> new BookNotFoundException("Book not found. ID: " + bookId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public Book createBook(Book book) {
|
||||||
|
Book newBook = new Book();
|
||||||
|
newBook.setTitle(book.getTitle());
|
||||||
|
newBook.setAuthor(book.getAuthor());
|
||||||
|
return bookRepository.save(newBook);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public void deleteBook(Long bookId) {
|
||||||
|
bookRepository.delete(bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public Book updateBook(Map<String, String> updates, Long bookId) {
|
||||||
|
Book book = findBookById(bookId);
|
||||||
|
updates.keySet().forEach(key -> {
|
||||||
|
switch (key) {
|
||||||
|
case "author":
|
||||||
|
book.setAuthor(updates.get(key));
|
||||||
|
break;
|
||||||
|
case "title":
|
||||||
|
book.setTitle(updates.get(key));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return bookRepository.save(book);
|
||||||
|
}
|
||||||
|
}
|
|
@ -42,6 +42,17 @@
|
||||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
|
|
@ -3,39 +3,11 @@ package com.baeldung.spring.cloud.bootstrap.svcrating;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableEurekaClient
|
@EnableEurekaClient
|
||||||
@RestController
|
|
||||||
@RequestMapping("/ratings")
|
|
||||||
public class RatingServiceApplication {
|
public class RatingServiceApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(RatingServiceApplication.class, args);
|
SpringApplication.run(RatingServiceApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Rating> ratingList = Arrays.asList(
|
|
||||||
new Rating(1L, 1L, 2),
|
|
||||||
new Rating(2L, 1L, 3),
|
|
||||||
new Rating(3L, 2L, 4),
|
|
||||||
new Rating(4L, 2L, 5)
|
|
||||||
);
|
|
||||||
|
|
||||||
@GetMapping("")
|
|
||||||
public List<Rating> findRatingsByBookId(@RequestParam Long bookId) {
|
|
||||||
return bookId == null || bookId.equals(0L) ? Collections.EMPTY_LIST : ratingList.stream().filter(r -> r.getBookId().equals(bookId)).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/all")
|
|
||||||
public List<Rating> findAllRatings() {
|
|
||||||
return ratingList;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -2,6 +2,7 @@ package com.baeldung.spring.cloud.bootstrap.svcrating;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
@ -22,8 +23,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
http.httpBasic()
|
http.httpBasic()
|
||||||
.disable()
|
.disable()
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers("/ratings").hasRole("USER")
|
.regexMatchers("^/ratings\\?bookId.*$").authenticated()
|
||||||
.antMatchers("/ratings/all").hasRole("ADMIN")
|
.antMatchers(HttpMethod.POST,"/ratings").authenticated()
|
||||||
|
.antMatchers(HttpMethod.PATCH,"/ratings/*").hasRole("ADMIN")
|
||||||
|
.antMatchers(HttpMethod.DELETE,"/ratings/*").hasRole("ADMIN")
|
||||||
|
.antMatchers(HttpMethod.GET,"/ratings").hasRole("ADMIN")
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
.and()
|
.and()
|
||||||
.csrf()
|
.csrf()
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
package com.baeldung.spring.cloud.bootstrap.svcrating;
|
package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class Rating {
|
public class Rating {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
private Long id;
|
private Long id;
|
||||||
private Long bookId;
|
private Long bookId;
|
||||||
private int stars;
|
private int stars;
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ratings")
|
||||||
|
public class RatingController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RatingService ratingService;
|
||||||
|
|
||||||
|
@GetMapping("")
|
||||||
|
public List<Rating> findRatingsByBookId(@RequestParam(required = false, defaultValue = "0") Long bookId) {
|
||||||
|
if (bookId.equals(0L)) {
|
||||||
|
return ratingService.findAllRatings();
|
||||||
|
}
|
||||||
|
return ratingService.findRatingsByBookId(bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("")
|
||||||
|
public Rating createRating(@RequestBody Rating rating) {
|
||||||
|
return ratingService.createRating(rating);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{ratingId}")
|
||||||
|
public void deleteRating(@PathVariable Long ratingId) {
|
||||||
|
ratingService.deleteRating(ratingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PatchMapping("/{ratingId")
|
||||||
|
public Rating updateRating(@RequestBody Map<String, String> updates, @PathVariable Long ratingId) {
|
||||||
|
return ratingService.updateRating(updates, ratingId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||||
|
class RatingNotFoundException extends RuntimeException {
|
||||||
|
RatingNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
interface RatingRepository extends JpaRepository<Rating, Long>{
|
||||||
|
List<Rating> findRatingsByBookId(Long bookId);
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.baeldung.spring.cloud.bootstrap.svcrating.rating;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public class RatingService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RatingRepository ratingRepository;
|
||||||
|
|
||||||
|
public Rating findRatingById(Long ratingId) {
|
||||||
|
return Optional.ofNullable(ratingRepository.findOne(ratingId))
|
||||||
|
.orElseThrow(() -> new RatingNotFoundException("Rating not found. ID: " + ratingId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Rating> findRatingsByBookId(Long bookId) {
|
||||||
|
return ratingRepository.findRatingsByBookId(bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Rating> findAllRatings() {
|
||||||
|
return ratingRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public Rating createRating(Rating rating) {
|
||||||
|
Rating newRating = new Rating();
|
||||||
|
newRating.setBookId(rating.getBookId());
|
||||||
|
newRating.setStars(rating.getStars());
|
||||||
|
return ratingRepository.save(newRating);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public void deleteRating(Long ratingId) {
|
||||||
|
ratingRepository.delete(ratingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
|
public Rating updateRating(Map<String, String> updates, Long ratingId) {
|
||||||
|
Rating rating = findRatingById(ratingId);
|
||||||
|
updates.keySet().forEach(key -> {
|
||||||
|
switch (key) {
|
||||||
|
case "stars":
|
||||||
|
rating.setStars(Integer.parseInt(updates.get(key)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ratingRepository.save(rating);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue