add book reviews service
This commit is contained in:
parent
e4bc0a44ce
commit
ec121c1ade
|
@ -8,7 +8,8 @@
|
|||
<version>1.0.0-SNAPSHOT</version>
|
||||
<modules>
|
||||
<module>spring-cloud-rest-server</module>
|
||||
<module>spring-cloud-rest-client</module>
|
||||
<module>spring-cloud-rest-client-1</module>
|
||||
<module>spring-cloud-rest-client-2</module>
|
||||
</modules>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.baeldung</groupId>
|
||||
<artifactId>spring-cloud-rest-client</artifactId>
|
||||
<artifactId>spring-cloud-rest-client-1</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.baeldung</groupId>
|
||||
<artifactId>spring-cloud-rest-client-2</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>spring-cloud-rest</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.4.3.RELEASE</version>
|
||||
<relativePath /> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
<rest-assured.version>3.0.1</rest-assured.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-eureka</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.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<version>${rest-assured.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Camden.SR4</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>**/*IntegrationTest.java</exclude>
|
||||
<exclude>**/*LiveTest.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
package org.baeldung;
|
||||
|
||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
|
||||
|
||||
@EnableRedisHttpSession
|
||||
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package org.baeldung;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableEurekaClient
|
||||
public class SpringCloudRestClientApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SpringCloudRestClientApplication.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package org.baeldung.persistence.dao;
|
||||
|
||||
import org.baeldung.persistence.model.BookReview;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
|
||||
@RepositoryRestResource(collectionResourceRel = "reviews", path = "reviews")
|
||||
public interface BookReviewRepository extends CrudRepository<BookReview, Long> {
|
||||
Page<BookReview> findByBookId(@Param("bookId") long bookId, Pageable pageable);
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package org.baeldung.persistence.model;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
@Entity
|
||||
public class BookReview {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
|
||||
private String content;
|
||||
|
||||
private int rating;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Long bookId;
|
||||
|
||||
//
|
||||
|
||||
public BookReview() {
|
||||
super();
|
||||
}
|
||||
|
||||
public BookReview(String content, int rating, long bookId) {
|
||||
super();
|
||||
this.content = content;
|
||||
this.rating = rating;
|
||||
this.bookId = bookId;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public int getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public void setRating(int rating) {
|
||||
this.rating = rating;
|
||||
}
|
||||
|
||||
public Long getBookId() {
|
||||
return bookId;
|
||||
}
|
||||
|
||||
public void setBookId(Long bookId) {
|
||||
this.bookId = bookId;
|
||||
}
|
||||
|
||||
//
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + (int) (bookId ^ (bookId >>> 32));
|
||||
result = (prime * result) + ((content == null) ? 0 : content.hashCode());
|
||||
result = (prime * result) + (int) (id ^ (id >>> 32));
|
||||
result = (prime * result) + rating;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final BookReview other = (BookReview) obj;
|
||||
if (bookId != other.bookId) {
|
||||
return false;
|
||||
}
|
||||
if (content == null) {
|
||||
if (other.content != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!content.equals(other.content)) {
|
||||
return false;
|
||||
}
|
||||
if (id != other.id) {
|
||||
return false;
|
||||
}
|
||||
if (rating != other.rating) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("BookReview [id=")
|
||||
.append(id)
|
||||
.append(", content=")
|
||||
.append(content)
|
||||
.append(", rating=")
|
||||
.append(rating)
|
||||
.append(", bookId=")
|
||||
.append(bookId)
|
||||
.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#### cloud
|
||||
spring.application.name=spring-cloud-eureka-client
|
||||
server.port=0
|
||||
eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://system:systemPass@localhost:8761/eureka}
|
||||
eureka.instance.preferIpAddress=true
|
||||
|
||||
#### persistence
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.url=jdbc:h2:mem:cloud_rest;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
|
||||
#### security
|
||||
security.basic.enabled=true
|
||||
security.basic.path=/**
|
||||
security.user.name=user
|
||||
security.user.password=userPass
|
||||
security.user.role=USER
|
||||
security.sessions=always
|
|
@ -0,0 +1,162 @@
|
|||
package org.baeldung;
|
||||
|
||||
import static io.restassured.RestAssured.preemptive;
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomNumeric;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.response.Response;
|
||||
|
||||
import org.baeldung.persistence.model.BookReview;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = { SpringCloudRestClientApplication.class }, webEnvironment = WebEnvironment.DEFINED_PORT)
|
||||
public class RestApiLiveTest {
|
||||
|
||||
private static final String API_URI = "http://localhost:8084/reviews";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
RestAssured.authentication = preemptive().basic("user", "userPass");
|
||||
}
|
||||
|
||||
// GET
|
||||
|
||||
@Test
|
||||
public void whenGetAllReviews_thenOK() {
|
||||
final Response response = RestAssured.get(API_URI);
|
||||
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCreatedReviewById_thenOK() {
|
||||
final BookReview review = createRandomReview();
|
||||
final String location = createReviewAsUri(review);
|
||||
|
||||
final Response response = RestAssured.get(location);
|
||||
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
|
||||
assertEquals(review.getContent(), response.jsonPath()
|
||||
.get("content"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetCreatedReviewByName_thenOK() {
|
||||
final BookReview review = createRandomReview();
|
||||
createReviewAsUri(review);
|
||||
|
||||
final Response response = RestAssured.get(API_URI + "/search/findByBookId?bookId=" + review.getBookId());
|
||||
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
|
||||
assertTrue(response.jsonPath()
|
||||
.getLong("page.totalElements") > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetNotExistReviewById_thenNotFound() {
|
||||
final Response response = RestAssured.get(API_URI + "/" + randomNumeric(4));
|
||||
assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenGetNotExistReviewByName_thenNotFound() {
|
||||
final Response response = RestAssured.get(API_URI + "/search/findByBookId?bookId=" + randomNumeric(4));
|
||||
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
|
||||
assertTrue(response.jsonPath()
|
||||
.getLong("page.totalElements") == 0);
|
||||
}
|
||||
|
||||
// POST
|
||||
@Test
|
||||
public void whenCreateNewReview_thenCreated() {
|
||||
final BookReview review = createRandomReview();
|
||||
|
||||
final Response response = RestAssured.given()
|
||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||
.body(review)
|
||||
.post(API_URI);
|
||||
assertEquals(HttpStatus.CREATED.value(), response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenInvalidReview_thenError() {
|
||||
final BookReview review = createRandomReview();
|
||||
review.setBookId(null);
|
||||
|
||||
final Response response = RestAssured.given()
|
||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||
.body(review)
|
||||
.post(API_URI);
|
||||
assertEquals(HttpStatus.CONFLICT.value(), response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenUpdateCreatedReview_thenUpdated() {
|
||||
// create
|
||||
final BookReview review = createRandomReview();
|
||||
final String location = createReviewAsUri(review);
|
||||
|
||||
// update
|
||||
review.setRating(4);
|
||||
Response response = RestAssured.given()
|
||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||
.body(review)
|
||||
.put(location);
|
||||
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
|
||||
|
||||
// check if changes saved
|
||||
response = RestAssured.get(location);
|
||||
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
|
||||
assertEquals(4, response.jsonPath()
|
||||
.getInt("rating"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDeleteCreatedReview_thenOk() {
|
||||
// create
|
||||
final BookReview review = createRandomReview();
|
||||
final String location = createReviewAsUri(review);
|
||||
|
||||
// delete
|
||||
Response response = RestAssured.delete(location);
|
||||
assertEquals(HttpStatus.NO_CONTENT.value(), response.getStatusCode());
|
||||
|
||||
// confirm it was deleted
|
||||
response = RestAssured.get(location);
|
||||
assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenDeleteNotExistReview_thenError() {
|
||||
final Response response = RestAssured.delete(API_URI + "/" + randomNumeric(4));
|
||||
assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
|
||||
}
|
||||
|
||||
// =============================== Util
|
||||
|
||||
private BookReview createRandomReview() {
|
||||
final BookReview review = new BookReview();
|
||||
review.setContent(randomAlphabetic(10));
|
||||
review.setRating(3);
|
||||
review.setBookId(1L);
|
||||
return review;
|
||||
}
|
||||
|
||||
private String createReviewAsUri(BookReview review) {
|
||||
final Response response = RestAssured.given()
|
||||
.contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||
.body(review)
|
||||
.post(API_URI);
|
||||
return response.jsonPath()
|
||||
.get("_links.self.href");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package org.baeldung;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class SpringCloudRestClientApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#### cloud
|
||||
spring.application.name=spring-cloud-eureka-client
|
||||
server.port=8084
|
||||
eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://system:systemPass@localhost:8761/eureka}
|
||||
eureka.instance.preferIpAddress=true
|
||||
|
||||
#### persistence
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.url=jdbc:h2:mem:cloud_rest;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=
|
||||
|
||||
#### security
|
||||
security.basic.enabled=true
|
||||
security.basic.path=/**
|
||||
security.user.name=user
|
||||
security.user.password=userPass
|
||||
security.user.role=USER
|
||||
security.sessions=always
|
Loading…
Reference in New Issue