JAVA-12465: Look into spring-cloud-rest module (#12406)

* JAVA-12465: Look into spring-cloud-rest module

* JAVA-12465: updated README to include ebook reference
This commit is contained in:
freelansam
2022-06-26 21:12:01 +05:30
committed by GitHub
parent 1ee4a09b2e
commit 77c668f340
44 changed files with 3 additions and 1503 deletions
+1 -2
View File
@@ -22,8 +22,7 @@
<module>spring-cloud-eureka</module>
<module>spring-cloud-hystrix</module>
<module>spring-cloud-bootstrap</module>
<module>spring-cloud-ribbon-client</module>
<module>spring-cloud-rest</module>
<module>spring-cloud-ribbon-client</module>
<module>spring-cloud-zookeeper</module>
<module>spring-cloud-gateway</module>
<module>spring-cloud-stream</module>
@@ -1,6 +1,6 @@
## Spring Cloud Bootstrap
## Guide to Microservices: with Spring Boot and Spring Cloud Ebook
This module contains articles about bootstrapping Spring Cloud applications
This module contains articles about bootstrapping Spring Cloud applications that are part of the Guide to Microservices: with Spring Boot and Spring Cloud Ebook.
### Relevant Articles:
@@ -1,24 +0,0 @@
target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
@@ -1,5 +0,0 @@
## Spring Cloud REST
This module contains articles about RESTful APIs with Spring Cloud
Code for an ebook - "A REST API with Spring Boot and Spring Cloud"
@@ -1,16 +0,0 @@
spring.application.name=discovery
server.port=8761
#### cloud
eureka.instance.hostname=localhost
eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://system:systemPass@localhost:8761/eureka}
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
#### security
security.basic.enabled=true
security.basic.path=/**
security.user.name=system
security.user.password=systemPass
security.user.role=ADMIN
security.sessions=always
@@ -1,20 +0,0 @@
spring.application.name=resource
#server.port=0
#### cloud
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
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>com.baeldung</groupId>
<artifactId>spring-cloud-rest</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>spring-cloud-rest</name>
<packaging>pom</packaging>
<parent>
<groupId>com.baeldung.spring.cloud</groupId>
<artifactId>spring-cloud-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modules>
<module>spring-cloud-rest-config-server</module>
<module>spring-cloud-rest-discovery-server</module>
<module>spring-cloud-rest-books-api</module>
<module>spring-cloud-rest-reviews-api</module>
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-maven-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<properties>
<spring-boot-maven-plugin.version>1.4.2.RELEASE</spring-boot-maven-plugin.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
@@ -1,78 +0,0 @@
<?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>com.baeldung</groupId>
<artifactId>spring-cloud-rest-books-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-rest-books-api</name>
<packaging>jar</packaging>
<description>Simple books API</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../../parent-boot-1</relativePath>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<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>
</dependencies>
</project>
@@ -1,14 +0,0 @@
package com.baeldung;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class BooksApiApplication {
public static void main(String[] args) {
SpringApplication.run(BooksApiApplication.class, args);
}
}
@@ -1,8 +0,0 @@
package com.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 {
}
@@ -1,13 +0,0 @@
package com.baeldung.persistence.dao;
import com.baeldung.persistence.model.Book;
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 = "books", path = "books")
public interface BookRepository extends CrudRepository<Book, Long> {
Page<Book> findByTitle(@Param("title") String title, Pageable pageable);
}
@@ -1,111 +0,0 @@
package com.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 Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(nullable = false, unique = true)
private String title;
@Column(nullable = false)
private String author;
//
public Book() {
super();
}
public Book(String title, String author) {
super();
this.title = title;
this.author = author;
}
//
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
//
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((author == null) ? 0 : author.hashCode());
result = (prime * result) + (int) (id ^ (id >>> 32));
result = (prime * result) + ((title == null) ? 0 : title.hashCode());
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 Book other = (Book) obj;
if (author == null) {
if (other.author != null) {
return false;
}
} else if (!author.equals(other.author)) {
return false;
}
if (id != other.id) {
return false;
}
if (title == null) {
if (other.title != null) {
return false;
}
} else if (!title.equals(other.title)) {
return false;
}
return true;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Book [id=").append(id).append(", title=").append(title).append(", author=").append(author).append("]");
return builder.toString();
}
}
@@ -1,9 +0,0 @@
spring.cloud.config.name=resource
spring.cloud.config.discovery.service-id=config
spring.cloud.config.discovery.enabled=true
spring.cloud.config.username=configUser
spring.cloud.config.password=configPassword
eureka.client.serviceUrl.defaultZone=http://system:systemPass@localhost:8761/eureka
server.port=8084
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -1,43 +0,0 @@
package com.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class BooksApiIntegrationTest {
@Test
public void contextLoads() {
}
@EnableRedisHttpSession
@Configuration
static class Config {
@Bean
@SuppressWarnings("unchecked")
public RedisSerializer<Object> defaultRedisSerializer() {
return Mockito.mock(RedisSerializer.class);
}
@Bean
public RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = Mockito.mock(RedisConnectionFactory.class);
RedisConnection connection = Mockito.mock(RedisConnection.class);
Mockito.when(factory.getConnection()).thenReturn(connection);
return factory;
}
}
}
@@ -1,156 +0,0 @@
package com.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 com.baeldung.BooksApiApplication;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import com.baeldung.persistence.model.Book;
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 = { BooksApiApplication.class }, webEnvironment = WebEnvironment.DEFINED_PORT)
public class RestApiLiveTest {
private static final String API_URI = "http://localhost:8084/books";
@Before
public void setUp() {
RestAssured.authentication = preemptive().basic("user", "userPass");
}
// GET
@Test
public void whenGetAllBooks_thenOK() {
final Response response = RestAssured.get(API_URI);
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
}
@Test
public void whenGetCreatedBookById_thenOK() {
final Book book = createRandomBook();
final String location = createBookAsUri(book);
final Response response = RestAssured.get(location);
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
assertEquals(book.getTitle(), response.jsonPath().get("title"));
}
@Test
public void whenGetCreatedBookByName_thenOK() {
final Book book = createRandomBook();
createBookAsUri(book);
final Response response = RestAssured.get(API_URI + "/search/findByTitle?title=" + book.getTitle());
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
assertTrue(response.jsonPath().getLong("page.totalElements") > 0);
}
@Test
public void whenGetNotExistBookById_thenNotFound() {
final Response response = RestAssured.get(API_URI + "/" + randomNumeric(4));
assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
}
@Test
public void whenGetNotExistBookByName_thenNotFound() {
final Response response = RestAssured.get(API_URI + "/search/findByTitle?title=" + randomAlphabetic(20));
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
assertTrue(response.jsonPath().getLong("page.totalElements") == 0);
}
// POST
@Test
public void whenCreateNewBook_thenCreated() {
final Book book = createRandomBook();
final Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).post(API_URI);
assertEquals(HttpStatus.CREATED.value(), response.getStatusCode());
}
@Test
public void whenCreateDuplicateBook_thenError() {
final Book book = createRandomBook();
createBookAsUri(book);
// duplicate
final Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).post(API_URI);
assertEquals(HttpStatus.CONFLICT.value(), response.getStatusCode());
}
@Test
public void whenInvalidBook_thenError() {
final Book book = createRandomBook();
book.setAuthor(null);
final Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).post(API_URI);
assertEquals(HttpStatus.CONFLICT.value(), response.getStatusCode());
}
@Test
public void whenUpdateCreatedBook_thenUpdated() {
// create
final Book book = createRandomBook();
final String location = createBookAsUri(book);
// update
book.setAuthor("newAuthor");
Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).put(location);
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
// check if changes saved
response = RestAssured.get(location);
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
assertEquals("newAuthor", response.jsonPath().get("author"));
}
@Test
public void whenDeleteCreatedBook_thenOk() {
// create
final Book book = createRandomBook();
final String location = createBookAsUri(book);
// 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 whenDeleteNotExistBook_thenError() {
final Response response = RestAssured.delete(API_URI + "/" + randomNumeric(4));
assertEquals(HttpStatus.NOT_FOUND.value(), response.getStatusCode());
}
// =============================== Util
private Book createRandomBook() {
final Book book = new Book();
book.setTitle(randomAlphabetic(10));
book.setAuthor(randomAlphabetic(15));
return book;
}
private String createBookAsUri(Book book) {
final Response response = RestAssured.given().contentType(MediaType.APPLICATION_JSON_VALUE).body(book).post(API_URI);
return response.jsonPath().get("_links.self.href");
}
}
@@ -1,70 +0,0 @@
package com.baeldung;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.baeldung.BooksApiApplication;
import com.baeldung.SessionConfig;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import java.util.Set;
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.test.context.junit4.SpringRunner;
import redis.clients.jedis.Jedis;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { BooksApiApplication.class, SessionConfig.class }, webEnvironment = WebEnvironment.DEFINED_PORT)
public class SessionLiveTest {
private Jedis jedis;
private static final String API_URI = "http://localhost:8084/books";
@Before
public void setUp() {
jedis = new Jedis("localhost", 6379);
jedis.flushAll();
}
@Test
public void whenStart_thenNoSessionsExist() {
final Set<String> result = jedis.keys("*");
assertEquals(0, result.size());
}
@Test
public void givenUnauthorizeUser_whenAccessResources_then_unAuthorized() {
final Response response = RestAssured.get(API_URI);
assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatusCode());
}
@Test
public void givenAuthorizedUser_whenDeleteSession_thenUnauthorized() {
// authorize User
Response response = RestAssured.given().auth().preemptive().basic("user", "userPass").get(API_URI);
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
final String sessionCookie = response.getCookie("SESSION");
// check redis
final Set<String> redisResult = jedis.keys("*");
assertTrue(redisResult.size() > 0);
// login with cookie
response = RestAssured.given().cookie("SESSION", sessionCookie).get(API_URI);
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
// empty redis
jedis.flushAll();
// login with cookie again
response = RestAssured.given().cookie("SESSION", sessionCookie).get(API_URI);
assertEquals(HttpStatus.UNAUTHORIZED.value(), response.getStatusCode());
}
}
@@ -1,22 +0,0 @@
package com.baeldung;
import com.baeldung.BooksApiApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
*
* This Live Test requires:
* * A Redis instance running in port 6379 (e.g. using `docker run --name some-redis -p 6379:6379 -d redis`)
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BooksApiApplication.class)
public class SpringContextLiveTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -1,19 +0,0 @@
#### 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
@@ -1,24 +0,0 @@
target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
@@ -1,58 +0,0 @@
<?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>com.baeldung</groupId>
<artifactId>spring-cloud-rest-config-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-rest-config-server</name>
<packaging>jar</packaging>
<description>Spring Cloud REST configuration server</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../../parent-boot-1</relativePath>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<properties>
<spring-cloud.version>Camden.SR4</spring-cloud.version>
</properties>
</project>
@@ -1,17 +0,0 @@
package com.baeldung;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class SpringCloudRestConfigApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudRestConfigApplication.class, args);
}
}
@@ -1,11 +0,0 @@
server.port=8081
spring.application.name=config
spring.cloud.config.server.git.uri=${HOME}/application-config
eureka.client.region = default
eureka.client.registryFetchIntervalSeconds = 5
eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://system:systemPass@localhost:8761/eureka}
security.user.name=configUser
security.user.password=configPassword
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -1,15 +0,0 @@
package com.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(classes = SpringCloudRestConfigApplication.class)
public class SpringContextTest {
@Test
public void contextLoads() {
}
}
@@ -1,24 +0,0 @@
target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
@@ -1,66 +0,0 @@
<?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>com.baeldung</groupId>
<artifactId>spring-cloud-rest-discovery-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-rest-discovery-server</name>
<packaging>jar</packaging>
<description>Spring Cloud REST server</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../../parent-boot-1</relativePath>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</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>
</dependencies>
<properties>
<spring-cloud.version>Edgware.SR4</spring-cloud.version>
</properties>
</project>
@@ -1,8 +0,0 @@
package com.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 {
}
@@ -1,14 +0,0 @@
package com.baeldung;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class SpringCloudRestServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudRestServerApplication.class, args);
}
}
@@ -1,4 +0,0 @@
spring.cloud.config.name=discovery
spring.cloud.config.uri=http://localhost:8081
spring.cloud.config.username=configUser
spring.cloud.config.password=configPassword
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -1,42 +0,0 @@
package com.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringCloudRestServerIntegrationTest {
@Test
public void contextLoads() {
}
@EnableRedisHttpSession
@Configuration
static class Config {
@Bean
@SuppressWarnings("unchecked")
public RedisSerializer<Object> defaultRedisSerializer() {
return Mockito.mock(RedisSerializer.class);
}
@Bean
public RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = Mockito.mock(RedisConnectionFactory.class);
RedisConnection connection = Mockito.mock(RedisConnection.class);
Mockito.when(factory.getConnection()).thenReturn(connection);
return factory;
}
}
}
@@ -1,21 +0,0 @@
package com.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
*
* This Live Test requires:
* * A Redis instance running in port 6379 (e.g. using `docker run --name some-redis -p 6379:6379 -d redis`)
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringCloudRestServerApplication.class)
public class SpringContextLiveTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -1,88 +0,0 @@
<?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>com.baeldung</groupId>
<artifactId>spring-cloud-rest-reviews-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-rest-reviews-api</name>
<packaging>jar</packaging>
<description>Simple reviews API</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../../parent-boot-1</relativePath>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit-jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<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>com.github.kstyrc</groupId>
<artifactId>embedded-redis</artifactId>
<version>${embedded-redis.version}</version>
</dependency>
</dependencies>
<properties>
<rest-assured.version>3.0.1</rest-assured.version>
<embedded-redis.version>0.6</embedded-redis.version>
</properties>
</project>
@@ -1,14 +0,0 @@
package com.baeldung;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class BookReviewsApiApplication {
public static void main(String[] args) {
SpringApplication.run(BookReviewsApiApplication.class, args);
}
}
@@ -1,8 +0,0 @@
package com.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 {
}
@@ -1,13 +0,0 @@
package com.baeldung.persistence.dao;
import com.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);
}
@@ -1,120 +0,0 @@
package com.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();
}
}
@@ -1,9 +0,0 @@
spring.cloud.config.name=resource
spring.cloud.config.discovery.service-id=config
spring.cloud.config.discovery.enabled=true
spring.cloud.config.username=configUser
spring.cloud.config.password=configPassword
eureka.client.serviceUrl.defaultZone=http://system:systemPass@localhost:8761/eureka
server.port=8085
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -1,43 +0,0 @@
package com.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class BookReviewsApiIntegrationTest {
@Test
public void contextLoads() {
}
@EnableRedisHttpSession
@Configuration
static class Config {
@Bean
@SuppressWarnings("unchecked")
public RedisSerializer<Object> defaultRedisSerializer() {
return Mockito.mock(RedisSerializer.class);
}
@Bean
public RedisConnectionFactory connectionFactory() {
RedisConnectionFactory factory = Mockito.mock(RedisConnectionFactory.class);
RedisConnection connection = Mockito.mock(RedisConnection.class);
Mockito.when(factory.getConnection()).thenReturn(connection);
return factory;
}
}
}
@@ -1,147 +0,0 @@
package com.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 com.baeldung.BookReviewsApiApplication;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import com.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 = { BookReviewsApiApplication.class }, webEnvironment = WebEnvironment.DEFINED_PORT)
public class RestApiLiveTest {
private static final String API_URI = "http://localhost:8085/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 whenGetCreatedReviewByBookId_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 whenGetNotExistReviewByBookId_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 whenCreateInvalidReview_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");
}
}
@@ -1,22 +0,0 @@
package com.baeldung;
import com.baeldung.BookReviewsApiApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
*
* This Live Test requires:
* * A Redis instance running in port 6379 (e.g. using `docker run --name some-redis -p 6379:6379 -d redis`)
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BookReviewsApiApplication.class)
public class SpringContextLiveTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}
@@ -1,19 +0,0 @@
#### cloud
spring.application.name=spring-cloud-eureka-client
server.port=8085
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