Refactor of History module #184

Merged
bema merged 25 commits from widlam/refactor/issue#162 into master 2023-05-19 13:10:48 +02:00
40 changed files with 855 additions and 693 deletions

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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);
}
}
);
}
}

View File

@@ -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;
}

View File

@@ -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/**");
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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 {
/**

View File

@@ -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);
}
}

View File

@@ -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 "";
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}
}

View File

@@ -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

View File

@@ -1,2 +0,0 @@
redis.host = redis
redis.port = 6379

View File

@@ -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>

View File

@@ -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';

View File

@@ -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);
}
}

View 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);
}
}

View File

@@ -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;
}

View File

@@ -470,7 +470,7 @@ function selectMessage(id){
$('.tile[tileid="'+id+'"]').addClass("active");
initializeHistory();
refreshHeaderTable(innerHTML);
refreshHeaderTable(document.innerHTML);
}

View File

@@ -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());
}

View 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')
}
);

View File

@@ -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');

View File

@@ -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>