Create an initial web service

Create a maven project structure.

One mock webservice should be included. Both Json and XML payload accepted.
Configuration of this webservice (response body, response headers, http status code) should be fully configured in a configuration file.

Invoication details (headers, payload) should be logged.

Closes #T124
This commit is contained in:
Szakalakamaka
2020-08-25 14:05:39 +02:00
commit dcc996c006
157 changed files with 3572 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
package com.release11.klaus;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class KlausApplication {
public static void main(String[] args) {
SpringApplication.run(KlausApplication.class, args);
}
}
//TODO Jenkins
//TODO history logs
//TODO form validation
//TODO JedisPool jedisPool = new JedisPool(jedisPoolConfig, redisHost, redisPort, timeout, redisPassword)
//TODO JedisPool optimalization https://partners-intl.aliyun.com/help/doc-detail/98726.htm

View File

@@ -0,0 +1,25 @@
package com.release11.klaus.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import redis.clients.jedis.JedisPool;
@Configuration
@PropertySource("classpath:data-access.properties")
public class RedisConfig {
@Autowired
private Environment environment;
@Bean
JedisPool jedisPool(){
final JedisPool pool = new JedisPool(environment.getProperty("redis.host"),
Integer.parseInt(environment.getProperty("redis.port")));
return pool;
}
}

View File

@@ -0,0 +1,39 @@
package com.release11.klaus.controller;
import com.release11.klaus.service.KlausService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.UUID;
@Controller("/")
@Slf4j
@AllArgsConstructor
public class KlausController {
private final KlausService klausService;
@PostMapping(value = "klaus/v1/set/{clientUUID}/{mockedResponseId}")
public ResponseEntity<String> setMockedResponse(@PathVariable UUID clientUUID,
@PathVariable int mockedResponseId,
@RequestParam(required = false) int httpStatus,
RequestEntity<String> requestEntity){
return klausService.setMockedResponse(clientUUID, mockedResponseId, HttpStatus.valueOf(httpStatus), requestEntity);
}
@RequestMapping(value = "klaus/v1/get/{clientUUID}/{mockedResponseId}")
public ResponseEntity getMockedResponse(@PathVariable UUID clientUUID,
@PathVariable int mockedResponseId){
return klausService.getMockedResponse(clientUUID, mockedResponseId);
}
}

View File

@@ -0,0 +1,76 @@
package com.release11.klaus.controller;
import com.release11.klaus.model.MockedResponseDto;
import com.release11.klaus.service.KlausService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.Arrays;
import java.util.UUID;
@Slf4j
@Controller
@RequestMapping
@AllArgsConstructor
public class KlausMvcController {
private final String SET_MOCKED_RESPONSE_PATH_V1 = "/klaus/v1/set/";
private final String apiHost = "http://localhost:8097";
private final KlausService klausService;
@GetMapping("/login")
public String login(Model model) {
UUID uuid = UUID.randomUUID();
model.addAttribute("clientUUID", uuid);
return "login";
}
@GetMapping("/home")
public String showHome(Model model, @RequestParam UUID clientUUID) {
model.addAttribute("mockedResponseDto", new MockedResponseDto(clientUUID, 1,
"body", new HttpHeaders(), "application/xml"));
return "index";
}
//TODO refactor
//TODO form validation and proper error messages
@PostMapping("/home")
public String showHomePost(Model model, MockedResponseDto mockedResponseDto,
@RequestParam(required = false) String[] header,
@RequestParam(required = false) String[] value,
@RequestParam(required = false) int httpStatus) {
log.info(mockedResponseDto.toString());
URI uri = URI.create(apiHost + SET_MOCKED_RESPONSE_PATH_V1 + mockedResponseDto.getClientUUID() + "/"
+ mockedResponseDto.getMockedResponseId());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.valueOf(mockedResponseDto.getMediaType()));
if (header!=null){
for (int i = 0; i < header.length; i++) {
httpHeaders.set(header[i], value[i]);
}}
RequestEntity<String> requestEntity = new RequestEntity<String>(mockedResponseDto.getMessageBody(),
httpHeaders, HttpMethod.POST, uri);
klausService.setMockedResponse(mockedResponseDto.getClientUUID(), mockedResponseDto.getMockedResponseId(),
HttpStatus.valueOf(httpStatus), requestEntity);
model.addAttribute("mockedResponseDto", new MockedResponseDto(mockedResponseDto.getClientUUID(), 1,
"body", new HttpHeaders(), "application/xml"));
model.addAttribute("mockSaved", "true");
return "index";
}
@GetMapping("/home/getMockedResponse")
@ResponseBody
public String showGetMockedResponse(MockedResponseDto mockedResponseDto) {
return klausService.getMockedResponse(mockedResponseDto.getClientUUID(),
mockedResponseDto.getMockedResponseId()).toString();
}
}

View File

@@ -0,0 +1,22 @@
package com.release11.klaus.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.RequestEntity;
import java.util.UUID;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MockedResponseDto {
private UUID clientUUID;
private int mockedResponseId;
private String messageBody;
private HttpHeaders httpHeaders;
private String mediaType;
}

View File

@@ -0,0 +1,66 @@
package com.release11.klaus.repository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@Slf4j
@Repository
@Transactional
@AllArgsConstructor
public class MockedResponseRedisRepository implements MockedResponseRepository {
private final JedisPool jedisPool;
//TODO redis persistence
public ResponseEntity<String> getMockedResponse(UUID clientUUID, int mockedResponseId){
String body, httpCodeStatus;
String key = String.format("%s_%s", clientUUID.toString(), mockedResponseId);
HttpHeaders responseHeaders = new HttpHeaders();
try(Jedis jedis = jedisPool.getResource()){
body = jedis.get(key + "_body");
Map<String, String> headerMap = jedis.hgetAll(key + "_headers");
headerMap.forEach(responseHeaders::set);
httpCodeStatus = jedis.get(key + "_httpCodeStatus");
}
return new ResponseEntity<String>(body, responseHeaders,
Objects.requireNonNull(HttpStatus.valueOf(Integer.parseInt(httpCodeStatus))));
}
public ResponseEntity<String> setMockedResponse(UUID clientUUID, int mockedResponseId, HttpStatus httpStatus,
RequestEntity<String> requestEntity){
log.info("mockedResponseRepository, setMockedResponse, clientUUID {}, mockedResponseId {}, httpStatus{} requestEntity{} ",
clientUUID, mockedResponseId, httpStatus, requestEntity);
if (httpStatus == null){httpStatus = HttpStatus.OK;}
String key = String.format("%s_%s", clientUUID.toString(), mockedResponseId);
setMockedResponseBody(key + "_body", requestEntity.getBody());
setMockedResponseHeader(key + "_headers", requestEntity.getHeaders().toSingleValueMap());
setMockedResponseBody(key + "_httpCodeStatus", String.valueOf(httpStatus.value()));
return new ResponseEntity<String>("MockedResponse has been setup successfully! :D", new HttpHeaders(), HttpStatus.ACCEPTED);
}
private synchronized void setMockedResponseBody(String key, String body){
try(Jedis jedis = jedisPool.getResource()){
jedis.set(key, body);
}
}
private synchronized void setMockedResponseHeader(String key, Map<String, String> headers){
try(Jedis jedis = jedisPool.getResource()){
jedis.del(key);
headers.forEach((field, value)->jedis.hset(key, field, value));
}
}
}

View File

@@ -0,0 +1,7 @@
package com.release11.klaus.repository;
import org.springframework.stereotype.Repository;
@Repository
public interface MockedResponseRepository {
}

View File

@@ -0,0 +1,13 @@
package com.release11.klaus.service;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import java.util.UUID;
public interface KlausService {
ResponseEntity<String> getMockedResponse(UUID clientUUID, int mockedResponseId);
ResponseEntity<String> setMockedResponse(UUID clientUUID, int mockedResponseId, HttpStatus httpStatus,
RequestEntity<String> requestEntity);
}

View File

@@ -0,0 +1,35 @@
package com.release11.klaus.service;
import com.release11.klaus.repository.MockedResponseRedisRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Slf4j
@Service
@AllArgsConstructor
public class KlausServiceImpl implements KlausService {
private final MockedResponseRedisRepository mockedResponseRedisRepository;
@Override
public ResponseEntity<String> getMockedResponse(UUID clientUUID, int mockedResponseId) {
log.info("KlausServiceImpl, operation getMockedResponse, clientId {}, mockedResponseId {} ",
clientUUID, mockedResponseId);
ResponseEntity<String> responseEntity = mockedResponseRedisRepository.getMockedResponse(clientUUID, mockedResponseId);
return responseEntity;
}
@Override
public ResponseEntity<String> setMockedResponse(UUID clientUUID, int mockedResponseId, HttpStatus httpStatus,
RequestEntity<String> requestEntity) {
log.info("KlausServiceImpl, operation setMockedResponse, messageBody {}, mockedResponseId {} ",
requestEntity, mockedResponseId);
return mockedResponseRedisRepository.setMockedResponse(clientUUID, mockedResponseId, httpStatus,requestEntity);
}
}

View File

@@ -0,0 +1,9 @@
server.port = 8099
spring.output.ansi.enabled = always
logging.file.name=/var/log/klaus/
logging.level.root=INFO
logging.level.org.springframework.web=DEBUG
logging.level.com.release11=DEBUG
logging.file.max-size = 10MB
spring.mvc.log-request-details=true

View File

@@ -0,0 +1,3 @@
//redis.host = redis-server
redis.host = localhost
redis.port = 6379

View File

@@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type='text/javascript'>
function httpGetAsync()
{
var clientUUID = document.getElementById("clientUUID").value;
var mockedResponseId = document.getElementById("mockedResponseId").value;
var url = "http://localhost:8097/klaus/v1/get/" + clientUUID + "/" + mockedResponseId;
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
alert(xmlHttp.responseText);
}
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
var container = document.getElementById("getMockedResponse");
container.appendChild(document.createElement("br"));
container.appendChild(document.createTextNode(xmlHttp.responseText));
}
function httpGet()
{
var clientUUID = document.getElementById("getClientUUID").value;
var mockedResponseId = document.getElementById("getMockedResponseId").value;
var url = "http://localhost:8097/klaus/v1/get/" + clientUUID + "/" + mockedResponseId;
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", url, false ); // false for synchronous request
xmlHttp.send( null );
var container = document.getElementById("getMockedResponse");
var headers = xmlHttp.getAllResponseHeaders();
container.appendChild(document.createTextNode(headers));
container.appendChild(document.createTextNode("Your message: "));
container.appendChild(document.createElement("br"));
container.appendChild(document.createTextNode((xmlHttp.responseText)));
return xmlHttp.responseText;
}
var numberOfHeaders = 0;
function addFields(){
var container = document.getElementById("headers");
numberOfHeaders++;
container.appendChild(document.createElement("br"));
container.appendChild(document.createTextNode("Header " + (numberOfHeaders)));
var headerInput = document.createElement("input");
headerInput.type = "text";
headerInput.name = "header";
container.appendChild(headerInput);
container.appendChild(document.createTextNode("Value " + (numberOfHeaders)));
var valueInput = document.createElement("input");
valueInput.type = "text";
valueInput.name = "value";
container.appendChild(valueInput);
}
</script>
</head>
<body>
<br>----------------------------------------------------------------------------------------------------------------
<div><br>In order to set mockup response. Please send the response, that you want to receive, on:
<br>http://localhost:8097/klaus/v1/set/ <a th:text="${mockedResponseDto.clientUUID}">clientUUID should be here</a>/{mockedResponseId}?httpStatus=200
<br>or simply fill and submit the below form:</div>
<div class="col-lg-4 form-max">
<form action="#" th:action="@{/home}" th:object="${mockedResponseDto}" method="post">
<input type="text" th:field="*{clientUUID}" th:placeholder="*{clientUUID}" hidden/>
<br><label >Mocked response id:</label><br/>
<input type="text" th:field="*{mockedResponseId}" th:value=1/>
<br><label >Mocked response body:</label><br/>
<textarea rows="4" cols="50" th:field="*{messageBody}" th:placeholder='messageBody'></textarea>
<br><label >Mocked response http code status:</label><br/>
<input type="text" th:name="httpStatus" th:value='200' />
<br>Provide mocked response headers: <a href="#" id="addHeader" onclick="addFields()">Add a new header</a>
<div id="headers"></div>
<br><label >Media type:</label>
<select th:field="*{mediaType}">
<option value="application/xml">application/xml</option>
<option value="application/json">application/json</option>
<option value="text/xml">text/xml</option>
</select>
<input type="submit" value="Save mocked response" onclick="submit()"/>
</form>
<p th:if="${mockSaved}">Mock has been saved</p>
<br>----------------------------------------------------------------------------------------------------------------
<div><br>In order to use the mocked response in your integration tests or simply
get your mocked response please send a request to
<br>http://localhost:8097/klaus/v1/get/<a th:text="${mockedResponseDto.clientUUID}">clientUUID should be here</a>/{mockedResponseId}
</div>
<div><br>You will receive the same body and headers as you sent them in the step 1.</div>
<div><br>You can also use the form below:</div>
<form action="#" th:action="@{/home/getMockedResponse}" th:object="${mockedResponseDto}" method="get">
<input type="text" th:field="*{clientUUID}" th:placeholder="*{clientUUID}" hidden/>
<br><label >Mocked response id:</label><br/>
<input type="text" th:field="*{mockedResponseId}" th:placeholder="12345"/>
<input type="submit" value="Get mocked response"/>
</form>
<br>----------------------------------------------------------------------------------------------------------------
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<div><br>Please move to the next step with your own client UUID: </div>
<form action="#" th:action="@{/home}" method="get">
<br><label >Please provide your client UUID:</label><br/>
<input type="text" name="clientUUID" value="436c4774-038f-4540-9c18-2691ca9b53d4" />
<input type="submit" value="Proceed"/>
</form>
<div><br>You can also register new UUID. *some logic to register UUID*:
<br><a th:text="${clientUUID}">clientUUID should be here</a>
</div>
</body>
</html>

View File

@@ -0,0 +1,13 @@
package com.release11.klaus;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class KlausApplicationTests {
@Test
void contextLoads() {
}
}

View File

@@ -0,0 +1,19 @@
package com.release11.klaus.config;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class RedisConfigTest {
@Test
void jedisPool() {
}
}

View File

@@ -0,0 +1,16 @@
package com.release11.klaus.controller;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class KlausControllerTest {
@Test
void getMockedResponse() {
}
@Test
void setMockedResponse() {
}
}

View File

@@ -0,0 +1,12 @@
package com.release11.klaus.controller;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class KlausMvcControllerTest {
@Test
void showHome() {
}
}

View File

@@ -0,0 +1,16 @@
package com.release11.klaus.repository;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class MockedResponseRedisRepositoryTest {
@Test
void getMockedResponse() {
}
@Test
void setMockedResponse() {
}
}

View File

@@ -0,0 +1,61 @@
package com.release11.klaus.service;
import com.release11.klaus.repository.MockedResponseRedisRepository;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.*;
import java.net.URI;
import java.util.UUID;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
@SpringBootTest
class KlausServiceImplTest {
@Mock
MockedResponseRedisRepository mockedResponseRedisRepository;
@InjectMocks
KlausServiceImpl klausServiceImpl;
private final static UUID uuid = UUID.fromString("e4248095-100e-4f1f-8226-e722014ae29f");
private final static URI uri = URI.create("http//:localhost:8080");
private static ResponseEntity<String> mockedResponseEntity;
private static RequestEntity<String> mockedRequestEntity;
@BeforeAll
public static void initializeMockEntities(){
HttpHeaders httpHeaders = new HttpHeaders();
mockedResponseEntity = new ResponseEntity<String>("body", httpHeaders, HttpStatus.ACCEPTED);
mockedRequestEntity = new RequestEntity<String>(HttpMethod.POST, uri);
}
@Test
void getMockedResponse() {
when(mockedResponseRedisRepository.getMockedResponse(any(), anyInt()))
.thenReturn(mockedResponseEntity);
ResponseEntity<String> responseEntity = klausServiceImpl.getMockedResponse(uuid, 1);
assertThat(responseEntity).isNotNull();
verify(mockedResponseRedisRepository).getMockedResponse(any(), anyInt());
}
@Test
void setMockedResponse() {
when(mockedResponseRedisRepository.setMockedResponse(any(), anyInt(), any(), any()))
.thenReturn(mockedResponseEntity);
ResponseEntity<String> responseEntity = klausServiceImpl.setMockedResponse(uuid, 1,
HttpStatus.ACCEPTED, mockedRequestEntity);
assertThat(responseEntity).isNotNull();
verify(mockedResponseRedisRepository).setMockedResponse(any(), anyInt(), any(), any());
}
}