Refactor of History module (#184)
Co-authored-by: widlam <mikolaj.widla@gmail.com> Co-authored-by: Adam Bem <adam.bem@zoho.eu> Reviewed-on: #184 Reviewed-by: Adam Bem <bema@noreply.example.com> Co-authored-by: Mikolaj Widla <widlam@noreply.example.com> Co-committed-by: Mikolaj Widla <widlam@noreply.example.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.1.RELEASE</version>
|
||||
<version>2.7.11</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
<jedis.version>3.3.0</jedis.version>
|
||||
<logback-redis-appender.version>1.1.6</logback-redis-appender.version>
|
||||
<assertj.version>3.16.1</assertj.version>
|
||||
<mapstruct.version>1.3.1.Final</mapstruct.version>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<docker.image.prefix>Release11</docker.image.prefix>
|
||||
<docker.image.name>${project.artifactId}</docker.image.name>
|
||||
<lombok.version>1.18.26</lombok.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -57,21 +58,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.cwbase</groupId>
|
||||
<artifactId>logback-redis-appender</artifactId>
|
||||
<version>${logback-redis-appender.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>redis.clients</groupId>
|
||||
<artifactId>jedis</artifactId>
|
||||
<version>${jedis.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jettison</groupId>
|
||||
<artifactId>jettison</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -101,6 +87,11 @@
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>0.2.0</version>
|
||||
</dependency>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.r11.tools.config;
|
||||
|
||||
import java.util.Objects;
|
||||
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 org.springframework.data.redis.connection.RedisStandaloneConfiguration;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
|
||||
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
|
||||
/**
|
||||
* Class containing configuration for Redis db client
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@Configuration
|
||||
@EnableRedisRepositories
|
||||
@PropertySource("classpath:data-access.properties")
|
||||
public class RedisConfig {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
/**
|
||||
* Bean of JedisPool - the Redis client. It stores requests in "the pool" and then fires them at Redis.
|
||||
* It's considered super lightweight and fast client variant
|
||||
* @return lightweight client of the Redis - the JedisPool
|
||||
*/
|
||||
@Bean
|
||||
JedisPool jedisPool(){
|
||||
final JedisPool pool = new JedisPool(environment.getProperty("redis.host"),
|
||||
Integer.parseInt(environment.getProperty("redis.port")));
|
||||
return pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean of a factory for connenction object.
|
||||
* It's initialized with Redis db url property and is fed to other methods.
|
||||
* @return the factory for RedisTemplates
|
||||
*/
|
||||
@Bean
|
||||
JedisConnectionFactory jedisConnectionFactory() {
|
||||
RedisStandaloneConfiguration redisStandaloneConfiguration =
|
||||
new RedisStandaloneConfiguration(Objects.requireNonNull(environment.getProperty("redis.host")),
|
||||
Integer.parseInt(Objects.requireNonNull(environment.getProperty("redis.port"))));
|
||||
return new JedisConnectionFactory(redisStandaloneConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* RedisTemplate is the tool to store and retrieve given type (object) of hash from the database.
|
||||
* It's like you could store your Java object by just naming it inside database. You might thing about it
|
||||
* as of DAO.
|
||||
* @return RedisTemplate the redis dao.
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate() {
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||
redisTemplate.setConnectionFactory(jedisConnectionFactory());
|
||||
redisTemplate.setExposeConnection(true);
|
||||
redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.r11.tools.configuration;
|
||||
|
||||
import com.r11.tools.repository.MockedResponseRepository;
|
||||
import com.r11.tools.repository.RequestHistoryRepository;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Based on configuration deletes all outdated messages and history records from database.
|
||||
*
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
|
||||
@EnableScheduling
|
||||
@EnableAsync
|
||||
@Configuration
|
||||
public class RetentionConfiguration {
|
||||
|
||||
private final MockedResponseRepository responseRepository;
|
||||
private final RequestHistoryRepository historyRepository;
|
||||
private final RetentionConfigurationProperties retentionProperties;
|
||||
private final Logger log = LogManager.getRootLogger();
|
||||
|
||||
|
||||
public RetentionConfiguration(MockedResponseRepository responseRepository,
|
||||
RequestHistoryRepository historyRepository,
|
||||
RetentionConfigurationProperties retentionProperties){
|
||||
this.historyRepository = historyRepository;
|
||||
this.responseRepository = responseRepository;
|
||||
this.retentionProperties = retentionProperties;
|
||||
}
|
||||
@Scheduled(fixedDelayString = "#{${retention.retention-cooldown} * 60000 }")
|
||||
@Async
|
||||
public void deleteMessagesAndHistoryRecords(){
|
||||
log.info("OUTDATED MESSAGES AND HISTORY RECORDS DELETED!");
|
||||
responseRepository
|
||||
.findAll()
|
||||
.iterator()
|
||||
.forEachRemaining( mockedMessage -> {
|
||||
if (mockedMessage.getCreatedAt().plusMinutes(retentionProperties.getMinutesToDeleteMessage()).isBefore(LocalDateTime.now())){
|
||||
responseRepository.delete(mockedMessage);
|
||||
}
|
||||
} );
|
||||
|
||||
historyRepository
|
||||
.findAll()
|
||||
.iterator()
|
||||
.forEachRemaining(
|
||||
historyRecord -> {
|
||||
if (historyRecord.getDateTimeStamp().plusMinutes(retentionProperties.getMinutesToDeleteHistoryRecord()).isBefore(LocalDateTime.now())){
|
||||
historyRepository.delete(historyRecord);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.r11.tools.configuration;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.validation.constraints.Positive;
|
||||
|
||||
/**
|
||||
* Store all properties needed to change a retention in {@link RetentionConfiguration}
|
||||
*
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "retention")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RetentionConfigurationProperties {
|
||||
@Positive
|
||||
private Integer minutesToDeleteMessage;
|
||||
@Positive
|
||||
private Integer minutesToDeleteHistoryRecord;
|
||||
@Positive
|
||||
private Integer retentionCooldown;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.r11.tools.configuration;
|
||||
|
||||
import com.r11.tools.interceptor.IncomingMockRequestInterceptor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Configuration for {@link IncomingMockRequestInterceptor}
|
||||
*
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
public class WebConfig implements WebMvcConfigurer{
|
||||
|
||||
private final IncomingMockRequestInterceptor requestInterceptor;
|
||||
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor( requestInterceptor )
|
||||
.addPathPatterns("/api/mock/r/**");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.r11.tools.model.EventRequestDto;
|
||||
import com.r11.tools.service.EtrackService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* It's the REST api for {@link com.r11.tools.model.Event}
|
||||
* @author Gabriel Modzelewski
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/event")
|
||||
@AllArgsConstructor
|
||||
public class EventController {
|
||||
private final EtrackService service;
|
||||
|
||||
/**
|
||||
* Returns the list of Events in given time bracket.
|
||||
* The list of objects is received via {@link EventRequestDto}, which contains time brackets,
|
||||
* as well as the key - uuid.
|
||||
* @param event EventRequestDto object that contains data needed to query the database
|
||||
* @return list of {@link com.r11.tools.model.Event}
|
||||
*/
|
||||
@PostMapping
|
||||
public ResponseEntity filterHistory(@RequestBody EventRequestDto event){
|
||||
return new ResponseEntity(service.getEventsByDateTimeAndBusinessKeys(event), HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Events of last 24h from given date.
|
||||
* @param uuid unique id of message list
|
||||
* @param messageId unique id of message in message list
|
||||
* @return list of {@link com.r11.tools.model.Event}
|
||||
*/
|
||||
@GetMapping(path = "/{uuid}/{messageId}")
|
||||
public ResponseEntity getLastDay(@PathVariable UUID uuid,
|
||||
@PathVariable Integer messageId){
|
||||
LocalDateTime requestTime = LocalDateTime.now();
|
||||
LocalDateTime dayBeforeRequest = requestTime.minusDays(1L);
|
||||
EventRequestDto eventRequestDto = EventRequestDto.builder()
|
||||
.clientUUID(uuid)
|
||||
.mockedResponseId(messageId)
|
||||
.localDateTimeFrom(dayBeforeRequest)
|
||||
.localDateTimeTo(requestTime)
|
||||
.build();
|
||||
return new ResponseEntity(service.getEventsByDateTimeAndBusinessKeys(eventRequestDto), HttpStatus.OK);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,13 +3,14 @@ package com.r11.tools.controller;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.r11.tools.model.MockedMessageDto;
|
||||
import com.r11.tools.service.KlausService;
|
||||
import com.r11.tools.utilis.BusinessKey;
|
||||
import com.r11.tools.utilis.TrackingClient;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
@@ -18,17 +19,19 @@ import java.util.*;
|
||||
/**
|
||||
* Returns the homepage and provides the api for javascript async requests.
|
||||
* @author Gabriel Modzelewski
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/mock")
|
||||
@AllArgsConstructor
|
||||
@CrossOrigin(origins = "*")
|
||||
public class MockController {
|
||||
private final KlausService klausService;
|
||||
private final Logger log = LogManager.getRootLogger();
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public void errorHandler(Exception ex){
|
||||
log.error(ex.getStackTrace());
|
||||
log.error(Arrays.toString(ex.getStackTrace()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,22 +178,14 @@ public class MockController {
|
||||
/**
|
||||
* It's one of the most important features - the bread and butter of the Mocked Service. It's link that allows
|
||||
* to receive mocked response from the server and use it to mock!
|
||||
* @param requestEntity Logs the data of request
|
||||
* @param clientUUID the key-uuid of given set of messages
|
||||
* @param mockedResponseId unique id of given message
|
||||
* @return
|
||||
*/
|
||||
@GetMapping(value = "/r/{clientUUID}/{mockedResponseId}")
|
||||
public ResponseEntity getMockedResponse(RequestEntity<String> requestEntity,
|
||||
@RequestMapping(value = "/r/{clientUUID}/{mockedResponseId}")
|
||||
public ResponseEntity getMockedResponse(
|
||||
@PathVariable UUID clientUUID,
|
||||
@PathVariable int mockedResponseId) {
|
||||
TrackingClient.setBusinessKeys(Map.of(BusinessKey.INTERFACE_NAME, "getMockedResponse - request",
|
||||
BusinessKey.CLIENT_UUID, String.valueOf(clientUUID),
|
||||
BusinessKey.MESSAGE_ID, String.valueOf(mockedResponseId)));
|
||||
// log.info(requestEntity.toString().replaceAll("\"", "\\\\\"").substring(1).replaceAll("\n",""));
|
||||
TrackingClient.setBusinessKeys(Map.of(BusinessKey.INTERFACE_NAME, "getMockedResponse - response",
|
||||
BusinessKey.CLIENT_UUID, String.valueOf(clientUUID),
|
||||
BusinessKey.MESSAGE_ID, String.valueOf(mockedResponseId)));
|
||||
MockedMessageDto mockedMessageDto = klausService.getMockedResponse(clientUUID, mockedResponseId);
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
if (mockedMessageDto.getHttpHeaders() != null) mockedMessageDto.getHttpHeaders().forEach(httpHeaders::set);
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
|
||||
|
||||
@@ -15,6 +16,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@ControllerAdvice
|
||||
@CrossOrigin(origins = "*")
|
||||
public class MvcExceptionHandler {
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.r11.tools.mappers.RequestHistoryMapper;
|
||||
import com.r11.tools.model.HistoryRequestModel;
|
||||
import com.r11.tools.model.RequestHistory;
|
||||
import com.r11.tools.model.RequestHistoryDTO;
|
||||
import com.r11.tools.service.RequestHistoryService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* It's the REST api for {@link RequestHistory}
|
||||
* @author Gabriel Modzelewski
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping(path = "/api/event")
|
||||
@AllArgsConstructor
|
||||
@CrossOrigin(origins = "*")
|
||||
public class RequestHistoryController {
|
||||
private final RequestHistoryService service;
|
||||
private final RequestHistoryMapper mapper;
|
||||
|
||||
/**
|
||||
* Returns the list of Events in given time bracket.
|
||||
* The list of objects is received via {@link RequestHistoryDTO}, which contains time brackets,
|
||||
* as well as the key - uuid.
|
||||
* @param historyRequestModel EventRequestDto object that contains data needed to query the database
|
||||
* @return list of {@link RequestHistory}
|
||||
*/
|
||||
@PostMapping
|
||||
public ResponseEntity<List<RequestHistoryDTO>> filterHistory(@RequestBody HistoryRequestModel historyRequestModel){
|
||||
return ResponseEntity.ok(
|
||||
service.getHistoryRecordsBetweenDatesAndByUUIDAndMessageId(historyRequestModel)
|
||||
.stream()
|
||||
.map(mapper::requestHistoryToRequestHistoryDTO)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of Events of last 24h from given date.
|
||||
* @param uuid unique id of message list
|
||||
* @param messageId unique id of message in message list
|
||||
* @return list of {@link RequestHistory}
|
||||
*/
|
||||
@GetMapping(path = "/{uuid}/{messageId}")
|
||||
public ResponseEntity<List<RequestHistoryDTO>> getLastDay(@PathVariable UUID uuid,
|
||||
@PathVariable Integer messageId){
|
||||
LocalDateTime requestTime = LocalDateTime.now();
|
||||
LocalDateTime dayBeforeRequest = requestTime.minusDays(1L);
|
||||
List<RequestHistoryDTO> requestHistory = service.getHistoryRecordsBetweenDatesAndByUUIDAndMessageId(
|
||||
HistoryRequestModel.builder()
|
||||
.localDateTimeFrom(dayBeforeRequest)
|
||||
.localDateTimeTo(requestTime)
|
||||
.clientUUID(uuid)
|
||||
.mockedResponseId(messageId)
|
||||
.build()
|
||||
).stream()
|
||||
.map(mapper::requestHistoryToRequestHistoryDTO)
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity.ok(requestHistory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.r11.tools.interceptor;
|
||||
|
||||
import com.r11.tools.model.RequestHistoryDTO;
|
||||
import com.r11.tools.service.RequestHistoryService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
* Interceptor that gets needed data from HttpRequest, and saves it to history
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class IncomingMockRequestInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final RequestHistoryService historyService;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest httpRequest, HttpServletResponse response, Object handler){
|
||||
Map<String,String> headers = getHeadersFromHttpRequest(httpRequest);
|
||||
Map<String,String> pathVariable = getPathVariablesFromHttpRequest(httpRequest);
|
||||
String requestBody = getRequestBodyFromHttpRequest(httpRequest);
|
||||
|
||||
RequestHistoryDTO historyDTO = RequestHistoryDTO.builder()
|
||||
.httpMethod(HttpMethod.valueOf(httpRequest.getMethod()))
|
||||
.headers( headers )
|
||||
.messageID(Integer.valueOf(pathVariable.get("mockedResponseId")))
|
||||
.clientUUID(UUID.fromString(pathVariable.get("clientUUID")))
|
||||
.dateTimeStamp(LocalDateTime.now())
|
||||
.requestBody(requestBody)
|
||||
.build();
|
||||
historyService.saveRequest(historyDTO);
|
||||
return true;
|
||||
}
|
||||
|
||||
private Map<String,String> getHeadersFromHttpRequest( HttpServletRequest httpRequest ){
|
||||
Set<String> headersName = StreamSupport.stream(
|
||||
Spliterators.spliteratorUnknownSize(
|
||||
httpRequest.getHeaderNames().asIterator(),
|
||||
Spliterator.ORDERED
|
||||
), false
|
||||
).collect(Collectors.toSet());
|
||||
|
||||
return headersName.stream()
|
||||
.collect(Collectors.toMap(
|
||||
value -> value,
|
||||
httpRequest::getHeader
|
||||
));
|
||||
}
|
||||
|
||||
private Map<String,String> getPathVariablesFromHttpRequest( HttpServletRequest httpRequest ){
|
||||
return (Map<String, String>) httpRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
}
|
||||
|
||||
private String getRequestBodyFromHttpRequest( HttpServletRequest httpRequest ){
|
||||
HttpServletRequest wrappedRequest = new ContentCachingRequestWrapper(httpRequest);
|
||||
try {
|
||||
return StreamUtils.copyToString(wrappedRequest.getInputStream(), Charset.defaultCharset());
|
||||
} catch (IOException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import org.mapstruct.*;
|
||||
public interface MockedMessageMapper {
|
||||
@Mapping( target = "compositePrimaryKey", expression = "java(mockedMessageDto.getClientUUID() + \"_\"" +
|
||||
" + mockedMessageDto.getMockedResponseId())")
|
||||
@Mapping( target = "createdAt" , expression = "java(java.time.LocalDateTime.now())")
|
||||
MockedMessage mockedMessageDtoToMockedMessage(MockedMessageDto mockedMessageDto);
|
||||
MockedMessageDto mockedMessageToMockedMessageDto(MockedMessage mockedMessage);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.r11.tools.mappers;
|
||||
|
||||
import com.r11.tools.model.RequestHistory;
|
||||
import com.r11.tools.model.RequestHistoryDTO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
|
||||
/**
|
||||
* Map between DTO and Entity
|
||||
*
|
||||
* @author Mikołaj Widła
|
||||
*
|
||||
*/
|
||||
|
||||
@Mapper
|
||||
public interface RequestHistoryMapper {
|
||||
|
||||
@Mapping(target = "id", expression = "java(null)")
|
||||
@Mapping(target = "clientUUID", expression = "java(requestHistoryDTO.getClientUUID().toString())")
|
||||
RequestHistory requestHistoryDTOToRequestHistory(RequestHistoryDTO requestHistoryDTO);
|
||||
@Mapping(target = "clientUUID", expression = "java(java.util.UUID.fromString(requestHistory.getClientUUID()))")
|
||||
RequestHistoryDTO requestHistoryToRequestHistoryDTO(RequestHistory requestHistory);
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.r11.tools.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.*;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Pojo class for Event entity
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@ToString
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Event implements Comparable<Event>{
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-ddTHH:mm:ss")
|
||||
private LocalDateTime dateTimeStamp;
|
||||
@Nullable
|
||||
private String interfaceName;
|
||||
@Nullable
|
||||
private String clientUUID;
|
||||
@Nullable
|
||||
private Integer messageId;
|
||||
private String thread;
|
||||
private String level;
|
||||
@Nullable
|
||||
private String message;
|
||||
|
||||
@Override
|
||||
public int compareTo(Event o) {
|
||||
return this.getDateTimeStamp().compareTo(o.getDateTimeStamp()) * -1;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
package com.r11.tools.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Pojo for history query request. Contains information necessary to obtain {@link Event} list
|
||||
* @author Rafał Żukowicz
|
||||
* Represents all data needed to get HistoryRecord from database
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EventRequestDto {
|
||||
public class HistoryRequestModel {
|
||||
|
||||
private UUID clientUUID;
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
@@ -2,6 +2,7 @@ package com.r11.tools.model;
|
||||
|
||||
import com.r11.tools.model.constraints.HttpCode;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.validation.constraints.Positive;
|
||||
@@ -34,6 +35,7 @@ public class MockedMessage implements Serializable {
|
||||
private Map<String, String> httpHeaders;
|
||||
@HttpCode
|
||||
private Integer httpStatus;
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.r11.tools.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
import org.springframework.data.redis.core.index.Indexed;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Pojo class for Event entity
|
||||
* @author Rafał Żukowicz
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@RedisHash("mockHistory")
|
||||
public class RequestHistory implements Comparable<RequestHistory>, Serializable {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
@DateTimeFormat(pattern = "yyyy-MM-ddTHH:mm:ss")
|
||||
private LocalDateTime dateTimeStamp;
|
||||
@Indexed
|
||||
private String clientUUID;
|
||||
@Indexed
|
||||
private Integer messageID;
|
||||
private Map<String,String> headers;
|
||||
private HttpMethod httpMethod;
|
||||
private String requestBody;
|
||||
|
||||
@Override
|
||||
public int compareTo(RequestHistory o) {
|
||||
return this.getDateTimeStamp().compareTo(o.getDateTimeStamp()) * -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.r11.tools.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.HttpMethod;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Pojo for history query request. Contains information necessary to obtain {@link RequestHistory} list
|
||||
* @author Rafał Żukowicz
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class RequestHistoryDTO {
|
||||
|
||||
private UUID clientUUID;
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
private LocalDateTime dateTimeStamp;
|
||||
private Integer messageID;
|
||||
private Map<String,String> headers;
|
||||
private HttpMethod httpMethod;
|
||||
private String requestBody;
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.r11.tools.repository;
|
||||
|
||||
import com.r11.tools.model.Event;
|
||||
import com.r11.tools.utilis.BusinessKey;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Event entity dao interface
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@Repository
|
||||
@Transactional
|
||||
public interface EventRepository {
|
||||
List<Event> findEvents(LocalDateTime localDateTimeFrom, LocalDateTime localDateTimeTo,
|
||||
Map<BusinessKey, String> businessKeys);
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package com.r11.tools.repository;
|
||||
|
||||
import static com.r11.tools.utilis.RedisAppender.LOG_PREFIX;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.r11.tools.model.Event;
|
||||
import com.r11.tools.utilis.BusinessKey;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
|
||||
/**
|
||||
* Builds Event list based on logs created via {@link com.r11.tools.utilis.TrackingClient} and {@link com.r11.tools.utilis.RedisAppender}
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@Repository
|
||||
@AllArgsConstructor
|
||||
public class EventRepositoryImpl implements EventRepository {
|
||||
private final JedisPool jedisPool;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Creates list of {@link Event} based on {@link com.r11.tools.model.EventRequestDto} data via searching logs
|
||||
* @param localDateTimeFrom date from which logs are retrieved
|
||||
* @param localDateTimeTo date to which logs are retrieved
|
||||
* @param businessKeys set keys for redis values
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<Event> findEvents(LocalDateTime localDateTimeFrom, LocalDateTime localDateTimeTo,
|
||||
Map<BusinessKey, String> businessKeys) {
|
||||
List<String> eventStrings = findEventsBetweenDates(localDateTimeFrom.toLocalDate(), localDateTimeTo.toLocalDate());
|
||||
if (businessKeys.size() > 0) {
|
||||
eventStrings = businessKeysFilter(eventStrings, businessKeys);
|
||||
}
|
||||
List<Event> events = parseEvents(eventStrings);
|
||||
if (localDateTimeFrom.toLocalTime() != LocalTime.MIN) {
|
||||
events = events.stream().filter(event -> event.getDateTimeStamp().compareTo(localDateTimeFrom) >= 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return events.stream().filter(event -> event.getDateTimeStamp().compareTo(localDateTimeTo) <= 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns logs between given dates
|
||||
* @param localDateFrom date from which logs are retrieved
|
||||
* @param localDateTo date to which logs are retrieved
|
||||
* @return
|
||||
*/
|
||||
private List<String> findEventsBetweenDates(LocalDate localDateFrom, LocalDate localDateTo) {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
return localDateFrom.datesUntil(localDateTo.plusDays(1)).map(day -> LOG_PREFIX + day.toString())
|
||||
.map(key -> jedis.lrange(key, 0, -1)).flatMap(Collection::stream).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters keys so only the ones queried are retirned
|
||||
* @param events list of logs
|
||||
* @param businessKeys set keys for redis values
|
||||
* @return filtered list of logs
|
||||
*/
|
||||
private List<String> businessKeysFilter(List<String> events, Map<BusinessKey, String> businessKeys) {
|
||||
for (Map.Entry<BusinessKey, String> entry : businessKeys.entrySet()) {
|
||||
String stringPattern = entry.getKey().getReasonPhrase()+ "\"" + ":" + "\"" + entry.getValue() + "\"";
|
||||
events = events.stream().filter(s -> s.contains(stringPattern)).collect(Collectors.toList());
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses list of logs into list of {@link Event}
|
||||
* @param eventStrings list of logs
|
||||
* @return list of {@link Event}
|
||||
*/
|
||||
private List<Event> parseEvents(List<String> eventStrings) {
|
||||
List<Event> events = new ArrayList<>();
|
||||
for (String eventString : eventStrings) {
|
||||
eventString = eventString.replaceAll("\\R", "");
|
||||
try {
|
||||
events.add(objectMapper.readValue(eventString, Event.class));
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.r11.tools.repository;
|
||||
|
||||
import com.r11.tools.model.RequestHistory;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* History Record entity dao interface
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@Repository
|
||||
@Transactional
|
||||
public interface RequestHistoryRepository extends CrudRepository<RequestHistory,String> {
|
||||
List<RequestHistory> findAllByClientUUIDAndMessageID(
|
||||
String clientUUID,
|
||||
Integer messageID);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.r11.tools.service;
|
||||
|
||||
import com.r11.tools.model.Event;
|
||||
import com.r11.tools.model.EventRequestDto;
|
||||
import java.util.List;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Spring service interface for {@link com.r11.tools.controller.EventController}
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@Service
|
||||
public interface EtrackService {
|
||||
/**
|
||||
* Searches for {@link Event} objects between date brackets
|
||||
* @param eventsDto object containing required data for request
|
||||
* @return list of {@link Event}
|
||||
*/
|
||||
List<Event> getEventsByDateTimeAndBusinessKeys(EventRequestDto eventsDto);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package com.r11.tools.service;
|
||||
|
||||
import com.r11.tools.model.Event;
|
||||
import com.r11.tools.model.EventRequestDto;
|
||||
import com.r11.tools.repository.EventRepository;
|
||||
import com.r11.tools.utilis.BusinessKey;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Spring Service for {@link com.r11.tools.controller.EventController}. Contains logic required for quering
|
||||
* the database for {@link Event} objects
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class EtrackServiceImpl implements EtrackService {
|
||||
|
||||
private final EventRepository eventRepository;
|
||||
|
||||
/**
|
||||
* Adds {@link BusinessKey} to {@link EventRequestDto}
|
||||
* in order to create query via{@link com.r11.tools.repository.EventRepositoryImpl}
|
||||
* @param eventsDto object containing required data for request
|
||||
* @return list of {@link Event}
|
||||
*/
|
||||
@Override
|
||||
public List<Event> getEventsByDateTimeAndBusinessKeys(EventRequestDto eventsDto) {
|
||||
Map<BusinessKey, String> businessKeys = new HashMap<>();
|
||||
businessKeys.put(BusinessKey.CLIENT_UUID, eventsDto.getClientUUID().toString());
|
||||
if (eventsDto.getMockedResponseId() != null){
|
||||
businessKeys.put(BusinessKey.MESSAGE_ID, String.valueOf(eventsDto.getMockedResponseId()));
|
||||
}
|
||||
List<Event> events = eventRepository.findEvents(eventsDto.getLocalDateTimeFrom(), eventsDto.getLocalDateTimeTo(),
|
||||
businessKeys);
|
||||
Collections.sort(events);
|
||||
return events;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.r11.tools.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.r11.tools.mappers.MockedMessageMapper;
|
||||
import com.r11.tools.model.MockedMessage;
|
||||
import com.r11.tools.model.MockedMessageDto;
|
||||
@@ -13,6 +12,7 @@ import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
@@ -30,7 +30,6 @@ public class KlausServiceImpl implements KlausService {
|
||||
private final MockedMessageMapper mockedMessageMapper;
|
||||
private final Logger log = LogManager.getRootLogger();
|
||||
private final MockedResponseRepository mockedResponseRepository;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Removes message of given id in given key-uuid set
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.r11.tools.service;
|
||||
|
||||
import com.r11.tools.controller.RequestHistoryController;
|
||||
import com.r11.tools.model.HistoryRequestModel;
|
||||
import com.r11.tools.model.RequestHistory;
|
||||
import com.r11.tools.model.RequestHistoryDTO;
|
||||
import java.util.List;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Spring service interface for {@link RequestHistoryController}
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@Service
|
||||
public interface RequestHistoryService {
|
||||
/**
|
||||
* Searches for {@link RequestHistory} objects between date brackets
|
||||
* @param historyRequestModel object containing required data for request
|
||||
* @return list of {@link RequestHistory}
|
||||
*/
|
||||
List<RequestHistory> getHistoryRecordsBetweenDatesAndByUUIDAndMessageId(HistoryRequestModel historyRequestModel);
|
||||
void saveRequest(RequestHistoryDTO requestDTO);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.r11.tools.service;
|
||||
|
||||
import com.r11.tools.controller.RequestHistoryController;
|
||||
import com.r11.tools.mappers.RequestHistoryMapper;
|
||||
import com.r11.tools.model.HistoryRequestModel;
|
||||
import com.r11.tools.model.RequestHistory;
|
||||
import com.r11.tools.model.RequestHistoryDTO;
|
||||
import com.r11.tools.repository.RequestHistoryRepository;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Spring Service for {@link RequestHistoryController}. Contains logic required for quering
|
||||
* the database for {@link RequestHistory} objects
|
||||
* @author Rafał Żukowicz
|
||||
* @author Mikołaj Widła
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class RequestHistoryServiceImpl implements RequestHistoryService {
|
||||
|
||||
private final RequestHistoryRepository repository;
|
||||
private final RequestHistoryMapper requestMapper;
|
||||
|
||||
/**
|
||||
* in order to create query via{@link com.r11.tools.repository.RequestHistoryRepository}
|
||||
* @param historyRequestModel object containing required data for request
|
||||
* @return list of {@link RequestHistory}
|
||||
*/
|
||||
@Override
|
||||
public List<RequestHistory> getHistoryRecordsBetweenDatesAndByUUIDAndMessageId(HistoryRequestModel historyRequestModel) {
|
||||
List<RequestHistory> history = repository.findAllByClientUUIDAndMessageID(
|
||||
historyRequestModel.getClientUUID().toString(),
|
||||
historyRequestModel.getMockedResponseId()
|
||||
);
|
||||
Collections.sort(history);
|
||||
|
||||
return history.stream()
|
||||
.filter( historyRecord -> historyRecord
|
||||
.getDateTimeStamp()
|
||||
.isAfter(historyRequestModel.getLocalDateTimeFrom())
|
||||
).filter(
|
||||
historyRecord-> historyRecord
|
||||
.getDateTimeStamp()
|
||||
.isBefore(historyRequestModel.getLocalDateTimeTo())
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveRequest(RequestHistoryDTO requestDTO) {
|
||||
repository.save(requestMapper.requestHistoryDTOToRequestHistory(requestDTO));
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.r11.tools.utilis;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* Enum of keys for redis database.
|
||||
* @author Rafał Żukowicz
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum BusinessKey {
|
||||
INTERFACE_NAME("interfaceName"),
|
||||
CLIENT_UUID("clientUUID"),
|
||||
MESSAGE_ID("messageId");
|
||||
|
||||
private final String phrase;
|
||||
|
||||
/**
|
||||
* Returns string value of given enum variant
|
||||
* @return string value of enum
|
||||
*/
|
||||
public String getReasonPhrase() {
|
||||
return this.phrase;
|
||||
}
|
||||
}
|
||||
@@ -1,235 +0,0 @@
|
||||
package com.r11.tools.utilis;
|
||||
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.Layout;
|
||||
import ch.qos.logback.core.UnsynchronizedAppenderBase;
|
||||
import com.cwbase.logback.AdditionalField;
|
||||
import com.cwbase.logback.JSONEventLayout;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.Protocol;
|
||||
|
||||
/**
|
||||
* Class is used to insert logs directly to Redis. {@link com.release11.klaus.repository.EventRepositoryImpl} is using those logs.
|
||||
* @author Rafał Żukowicz
|
||||
* @author Gabriel Modzelewski
|
||||
*/
|
||||
public class RedisAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
|
||||
|
||||
JedisPool pool;
|
||||
JSONEventLayout jsonlayout;
|
||||
Layout<ILoggingEvent> layout;
|
||||
String host = "localhost";
|
||||
int port = Protocol.DEFAULT_PORT;
|
||||
String key = null;
|
||||
int timeout = Protocol.DEFAULT_TIMEOUT;
|
||||
String password = null;
|
||||
int database = Protocol.DEFAULT_DATABASE;
|
||||
|
||||
public static final String LOG_PREFIX = "logstash_";
|
||||
|
||||
|
||||
public RedisAppender() {
|
||||
jsonlayout = new JSONEventLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends JedisPool by another log
|
||||
* @param event object containing log info
|
||||
*/
|
||||
@Override
|
||||
protected void append(ILoggingEvent event) {
|
||||
Jedis client = pool.getResource();
|
||||
try {
|
||||
String json = layout == null ? jsonlayout.doLayout(event) : layout.doLayout(event);
|
||||
key = LOG_PREFIX + LocalDate.now();
|
||||
client.rpush(key, json);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String getSource() {
|
||||
return jsonlayout.getSource();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setSource(String source) {
|
||||
jsonlayout.setSource(source);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String getSourceHost() {
|
||||
return jsonlayout.getSourceHost();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setSourceHost(String sourceHost) {
|
||||
jsonlayout.setSourceHost(sourceHost);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String getSourcePath() {
|
||||
return jsonlayout.getSourcePath();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setSourcePath(String sourcePath) {
|
||||
jsonlayout.setSourcePath(sourcePath);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String getTags() {
|
||||
if (jsonlayout.getTags() != null) {
|
||||
Iterator<String> i = jsonlayout.getTags().iterator();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (i.hasNext()) {
|
||||
sb.append(i.next());
|
||||
if (i.hasNext()) {
|
||||
sb.append(',');
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setTags(String tags) {
|
||||
if (tags != null) {
|
||||
String[] atags = tags.split(",");
|
||||
jsonlayout.setTags(Arrays.asList(atags));
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String getType() {
|
||||
return jsonlayout.getType();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setType(String type) {
|
||||
jsonlayout.setType(type);
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
public void setTimeout(int timeout) {
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public int getDatabase() {
|
||||
return database;
|
||||
}
|
||||
|
||||
public void setDatabase(int database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setMdc(boolean flag) {
|
||||
jsonlayout.setProperties(flag);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean getMdc() {
|
||||
return jsonlayout.getProperties();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setLocation(boolean flag) {
|
||||
jsonlayout.setLocationInfo(flag);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean getLocation() {
|
||||
return jsonlayout.getLocationInfo();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setCallerStackIndex(int index) {
|
||||
jsonlayout.setCallerStackIdx(index);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getCallerStackIndex() {
|
||||
return jsonlayout.getCallerStackIdx();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void addAdditionalField(AdditionalField p) {
|
||||
jsonlayout.addAdditionalField(p);
|
||||
}
|
||||
|
||||
public Layout<ILoggingEvent> getLayout() {
|
||||
return layout;
|
||||
}
|
||||
|
||||
public void setLayout(Layout<ILoggingEvent> layout) {
|
||||
this.layout = layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts new instance of JedisPool
|
||||
*/
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
|
||||
config.setTestOnBorrow(true);
|
||||
pool = new JedisPool(config, host, port, timeout, password, database);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops and destroys JedisPool object
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
pool.destroy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.r11.tools.utilis;
|
||||
|
||||
import java.util.Map;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
/**
|
||||
* This static class has one purpose and one purpose only. It logs data about incomming requests.
|
||||
* The data from logs is received via {@link com.release11.klaus.repository.EventRepositoryImpl}
|
||||
* @author Rafał Żukowski
|
||||
*/
|
||||
public final class TrackingClient {
|
||||
|
||||
/**
|
||||
* Logs data inside the given map
|
||||
* @param businessKeysMap map containing all the information about incomming request
|
||||
*/
|
||||
public static void setBusinessKeys(Map<BusinessKey, String> businessKeysMap){
|
||||
for (Map.Entry<BusinessKey, String> entry : businessKeysMap.entrySet()) {
|
||||
MDC.put(entry.getKey().getReasonPhrase(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -5,3 +5,12 @@ spring.mvc.view.suffix=.html
|
||||
logging.level.root=INFO
|
||||
logging.level.org.springframework.web=INFO
|
||||
logging.level.com.release11=INFO
|
||||
|
||||
#database:
|
||||
spring.redis.host=redis
|
||||
spring.redis.port=6379
|
||||
|
||||
#retention
|
||||
retention.minutes-to-delete-message=120
|
||||
retention.minutes-to-delete-history-record=1440
|
||||
retention.retention-cooldown=1440
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
redis.host = redis
|
||||
redis.port = 6379
|
||||
@@ -2,25 +2,8 @@
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml"/>
|
||||
<property name="HOME_LOG" value="/log/mockServices.log"/>
|
||||
<!--https://github.com/kmtong/logback-redis-appender-->
|
||||
<appender name="LOGSTASH" class="com.r11.tools.utilis.RedisAppender">
|
||||
<host>redis</host>
|
||||
<port>6379</port>
|
||||
<key>logstash</key>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<!--https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html-->
|
||||
<Pattern>
|
||||
{"dateTimeStamp" : "%d{yyyy-MM-dd}T%d{HH:mm:ss}", "eventId":"%X{eventId}", "interfaceName":"%X{interfaceName}", "clientUUID":"%X{clientUUID}", "messageId":"%X{messageId}", "thread":"%t","level":"%-5level", "message":"%msg"}%n
|
||||
</Pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<appender-ref ref="LOGSTASH" />
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${HOME_LOG}</file>
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<fileNamePattern>logs/mockServices.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
@@ -34,7 +17,6 @@
|
||||
</appender>
|
||||
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="ASYNC" />
|
||||
<appender-ref ref="CONSOLE" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
@import url('r11tooltip.css');
|
||||
@import url('r11modal.css');
|
||||
@import url('r11flexbox.css');
|
||||
@import url('r11popup.css');
|
||||
@import url('../../highlight.css');
|
||||
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
|
||||
@@ -69,4 +69,27 @@
|
||||
.content p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.refresh-button{
|
||||
float: right;
|
||||
border: none;
|
||||
background-color: unset;
|
||||
font-size: xx-large;
|
||||
}
|
||||
|
||||
.refresh-button:hover{
|
||||
animation-name: rotation;
|
||||
animation-duration: 0.8s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes rotation{
|
||||
from{
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
83
Frontend/assets/css/tools/mock/r11popup.css
Normal file
83
Frontend/assets/css/tools/mock/r11popup.css
Normal file
@@ -0,0 +1,83 @@
|
||||
.popup-flex:not(.hiddable-container){
|
||||
animation: blur 0.5s ease-in-out ;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
.popup-flex{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 50;
|
||||
flex-direction: column;
|
||||
gap: 2%;
|
||||
position: fixed;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.popup-body{
|
||||
min-width: 33%;
|
||||
max-width: 60%;
|
||||
max-height: 70%;
|
||||
background-color: white;
|
||||
box-shadow: 10px 10px 5px lightblue;
|
||||
min-height: 45%;
|
||||
border-radius: 1em;
|
||||
text-align: center;
|
||||
padding: 10px 15px 15px 15px;
|
||||
color: black;
|
||||
border: 1px #2A93B0 solid;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.popup-button-close-container{
|
||||
text-align: right;
|
||||
margin-right: 2%;
|
||||
margin-top: 1%;
|
||||
font-size: xx-large;
|
||||
font-weight: bold;
|
||||
position: sticky;
|
||||
top:0
|
||||
}
|
||||
|
||||
.hiddable-popup-option{
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
padding: 1.5%;
|
||||
}
|
||||
|
||||
.popup-button-close{
|
||||
background: padding-box;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.popup-button-close:hover{
|
||||
color: #2A93B0;
|
||||
}
|
||||
|
||||
.hiddable-container{
|
||||
display:none;
|
||||
}
|
||||
|
||||
.hidden-popup-type{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#history-request-body{
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
@keyframes blur {
|
||||
0% {
|
||||
backdrop-filter: blur(0px);
|
||||
}
|
||||
|
||||
50% {
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
100% {
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
}
|
||||
@@ -68,4 +68,29 @@
|
||||
background-color: #3bc4f1;
|
||||
text-align: left;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.table-default td{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#header-table tr td {
|
||||
border: 1px black solid;
|
||||
padding: 1.5%;
|
||||
|
||||
}
|
||||
|
||||
#header-table{
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.history-header-name{
|
||||
min-width: 10vw;
|
||||
}
|
||||
|
||||
#historyTable, td{
|
||||
padding: 1%;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
@@ -470,7 +470,7 @@ function selectMessage(id){
|
||||
$('.tile[tileid="'+id+'"]').addClass("active");
|
||||
|
||||
initializeHistory();
|
||||
refreshHeaderTable(innerHTML);
|
||||
refreshHeaderTable(document.innerHTML);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -46,13 +46,36 @@ function historyToHtml(){
|
||||
for(let i=0; i<iterations; i++){
|
||||
let style = i%2==0 ? ' class="even"' : '';
|
||||
innerHTML += '<tr' + style + '>' +
|
||||
'<td>' + historyJson[i].dateTimeStamp + '</td>' +
|
||||
'<td>' + historyJson[i].interfaceName + '</td>' +
|
||||
'<td>' + parseTimeStamp(historyJson[i].dateTimeStamp) + '</td>' +
|
||||
'<td>' + historyJson[i].httpMethod + '</td>' +
|
||||
'<td>' + parseRequestBody(historyJson[i].requestBody, i) + '</td>' +
|
||||
'<td> <button id="'+i+'" class="showHeaderButton" onClick="showHeadersHistory(this);"> Show headers </button> </td>' +
|
||||
'</tr>';
|
||||
}
|
||||
return innerHTML;
|
||||
}
|
||||
|
||||
function parseRequestBody(requestBody,i){
|
||||
return requestBody.length == 0 ?
|
||||
"No request body" :
|
||||
'<button id="'+i+'" class="showRequestBodyButton" onClick="showRequestBody(this);"> Show request body </button>'
|
||||
}
|
||||
|
||||
function parseTimeStamp(timeStamp){
|
||||
return timeStamp.substring(0,19).replace('T',' ');
|
||||
}
|
||||
|
||||
function parseHeaders(pos){
|
||||
parsedJson = new Map();
|
||||
headers = historyJson[pos].headers
|
||||
Object.keys( headers ).forEach(
|
||||
(jsonKey) => {
|
||||
parsedJson.set( jsonKey , headers[jsonKey] );
|
||||
}
|
||||
)
|
||||
return parsedJson;
|
||||
}
|
||||
|
||||
function displayHistory(){
|
||||
$('#historyTable tbody').html(historyToHtml());
|
||||
}
|
||||
|
||||
34
Frontend/assets/scripts/tools/mock/popup.js
Normal file
34
Frontend/assets/scripts/tools/mock/popup.js
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
function switchPopups (neededPopupOption) {
|
||||
$('.hiddable-popup-option').addClass('hidden-popup-type');
|
||||
$('#'+neededPopupOption).removeClass('hidden-popup-type');
|
||||
}
|
||||
|
||||
function showPopup(){
|
||||
$('.popup-flex').removeClass('hiddable-container');
|
||||
}
|
||||
|
||||
function hidePopup(){
|
||||
$('.popup-flex').addClass('hiddable-container');
|
||||
$('.hiddable-popup-option').addClass('hidden-popup-type');
|
||||
}
|
||||
|
||||
/*
|
||||
* Event listener that's close the popup when user clicks out of a popup.
|
||||
*/
|
||||
|
||||
window.addEventListener(
|
||||
'click' ,
|
||||
(clickedElement) => {
|
||||
if(!document.getElementById('popup-body').contains(clickedElement.target) && clickedElement.target.className == 'popup-flex' ) {
|
||||
hidePopup();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$('.popup-button-close').click(
|
||||
() => {
|
||||
hidePopup();
|
||||
$('.hiddable-popup-option').addClass('hidden-popup-type')
|
||||
}
|
||||
);
|
||||
@@ -35,7 +35,6 @@ $('#historyTab').click(showHistory);
|
||||
$('#btn-history-filter').click(historyFilterSwitch);
|
||||
|
||||
|
||||
|
||||
const tabitem = $('.tabitem');
|
||||
function showHistory(){
|
||||
$('#headersTab').click(showHeaders);
|
||||
@@ -52,8 +51,6 @@ function initializeHistory(){
|
||||
getLast24hHistoryData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
function showHeaders(){
|
||||
$('#historyTab').click(showHistory);
|
||||
tabitem.removeClass('active');
|
||||
@@ -63,6 +60,109 @@ function showHeaders(){
|
||||
$('#headersTab').off('click');
|
||||
}
|
||||
|
||||
function showHeadersHistory(element){
|
||||
historyTable = '';
|
||||
headers = parseHeaders(element.id)
|
||||
headers.forEach(
|
||||
(value,key) => {
|
||||
historyTable +=
|
||||
'<tr>' +
|
||||
'<td class="history-header-name">'+ key + '</td>' +
|
||||
'<td class="history-header-value">'+ value + '</td>' +
|
||||
'</tr>'
|
||||
}
|
||||
);
|
||||
document.getElementById('header-history-table-body').innerHTML = historyTable;
|
||||
switchPopups('history-headers-table');
|
||||
showPopup();
|
||||
}
|
||||
|
||||
async function formatJSON(json) {
|
||||
const address = window.location.protocol + "//" + window.location.hostname + ":" + 8081 + "/json/formatting";
|
||||
|
||||
var init = {
|
||||
body: json,
|
||||
method: "POST"
|
||||
};
|
||||
var request = new Request(address, init);
|
||||
|
||||
var result = await fetch(request).then(response => {
|
||||
return response.text().then(function (text) {
|
||||
var json = JSON.parse(text);
|
||||
json.status = response.status;
|
||||
return json;
|
||||
});
|
||||
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
async function formatXML(xml) {
|
||||
const address = window.location.protocol + "//" + window.location.hostname + ":" + 8082 + "/prettify";
|
||||
var data = {
|
||||
data: xml,
|
||||
process: "",
|
||||
processor: "libxml",
|
||||
version: "1.0"
|
||||
}
|
||||
|
||||
var init = {
|
||||
body: JSON.stringify(data),
|
||||
method: "POST"
|
||||
};
|
||||
var request = new Request(address, init);
|
||||
|
||||
var result = await fetch(request).then(response => {
|
||||
return response.text().then(function (text) {
|
||||
return JSON.parse(text);
|
||||
});
|
||||
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function showRequestBody(element){
|
||||
var historyRequestBody = historyJson[element.id].requestBody;
|
||||
const popupContent = document.getElementById('code-highlight-content')
|
||||
|
||||
document.getElementById('code-highlight-content').innerText = "Loading...";
|
||||
switch(historyJson[element.id].headers["content-type"]){
|
||||
case "application/json":{
|
||||
formatJSON(historyRequestBody).then(function(result) {
|
||||
|
||||
if (result.status == "200") {
|
||||
popupContent.innerText = result.data;
|
||||
highlightSyntax('code-highlight-content');
|
||||
}
|
||||
else {
|
||||
popupContent.innerText = historyRequestBody;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "application/xml":{
|
||||
formatXML(historyRequestBody).then(function(result) {
|
||||
if (result.status == "OK") {
|
||||
popupContent.innerText = result.result;
|
||||
highlightSyntax('code-highlight-content');
|
||||
}
|
||||
else {
|
||||
popupContent.innerText = historyRequestBody;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
popupContent.innerText = historyRequestBody;
|
||||
highlightSyntax('code-highlight-content');
|
||||
}
|
||||
}
|
||||
switchPopups('history-request-body');
|
||||
showPopup();
|
||||
}
|
||||
|
||||
function focusInTip(element){
|
||||
showTip(element);
|
||||
focusedField = true;
|
||||
@@ -73,6 +173,10 @@ function focusOutTip(element){
|
||||
hidTip(element);
|
||||
}
|
||||
|
||||
function refreshHistoryRecords(){
|
||||
getLast24hHistoryData();
|
||||
}
|
||||
|
||||
function hidTip(element){
|
||||
if(focusedField) return;
|
||||
$('#'+element).removeClass('active');
|
||||
|
||||
@@ -9,6 +9,34 @@
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="popup-flex hiddable-container">
|
||||
<div class="popup-body" id="popup-body">
|
||||
<div class="popup-button-close-container">
|
||||
<button type="button" class="popup-button-close"> X </button>
|
||||
</div>
|
||||
<div class="popup-header-table hiddable-popup-option" id="history-headers-table">
|
||||
<table id="header-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Header Name
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Header Value
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="header-history-table-body">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="popup-request-body hiddable-popup-option" id="history-request-body">
|
||||
<pre class="code-content" id="history-request-body-content"><code id="code-highlight-content"></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="tool extended">
|
||||
<div class="tool-context">
|
||||
@@ -127,7 +155,10 @@
|
||||
<!-- history -->
|
||||
<div id="history" class="medium-vertical-margin tabcontent">
|
||||
<div class="block-display max-width">
|
||||
<button id="btn-history-filter" class="clickable-text highlight switch"><span class="toggleIndicator"></span> filter</button>
|
||||
<button id="btn-history-filter" class="clickable-text highlight switch">
|
||||
<span class="toggleIndicator"></span> filter
|
||||
<button type="button" class="refresh-button" onclick="refreshHistoryRecords();" >↻</button>
|
||||
</button>
|
||||
<div id ="history-filter" class="display-space-between max-width small-vertical-margin hiddable">
|
||||
<div class="three-fourth-width display-space-evenly">
|
||||
<div class="block-display half-width with-padding">
|
||||
@@ -149,7 +180,9 @@
|
||||
<thead>
|
||||
<tr class="bottom-border">
|
||||
<th>Timestamp</th>
|
||||
<th>Type</th>
|
||||
<th>Method</th>
|
||||
<th>Request Body</th>
|
||||
<th>Headers</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -299,5 +332,9 @@
|
||||
<script type="text/javascript" src="../assets/scripts/tools/mock/datatransfer.js"></script>
|
||||
<script type="text/javascript" src="../assets/scripts/tools/mock/historyloader.js"></script>
|
||||
<script type="text/javascript" src="../assets/scripts/tools/mock/fiddle.js"></script>
|
||||
<script type="text/javascript" src="../assets/scripts/tools/mock/popup.js"></script>
|
||||
<script type="text/javascript" src="../assets/scripts/tools/xmlFormatter.js"></script>
|
||||
<script type="text/javascript" src="../assets/scripts/tools/highlight.js"></script>
|
||||
<script type="text/javascript" src="../assets/scripts/common/hljs.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user