Skip to content

Commit

Permalink
feat(sdk): kraken-31 - pushing api activity log to other system
Browse files Browse the repository at this point in the history
  • Loading branch information
jaroslawmalekcodete committed Nov 14, 2024
1 parent d165a92 commit 49d359c
Show file tree
Hide file tree
Showing 20 changed files with 988 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,19 @@ app:
controlPlane:
url: http://localhost:8001
token: Bearer ${KRAKEN_MGMT_API_ACCESS_TOKEN:FAKE_TOKEN}
external-system:
enabled: false
url: ${EXTERNAL_SYSTEM_PUSH_LOG_URL:http://localhost:8888}
token: Bearer ${EXTERNAL_SYSTEM_PUSH_LOG_TOKEN:FAKE_TOKEN}
batch-size: ${EXTERNAL_SYSTEM_PUSH_LOG_BATCH_SIZE:200}
cron-job:
pull-api-server-info: 0/20 * * * * *
pull-server-assets: 0 0/1 * * * *
pull-latest-release: 0 0/1 * * * *
pull-reset-event: 0 0/1 * * * *
push-heartbeat: 0/20 * * * * *
push-log: 0/20 * * * * *
push-log-external-system: 0 0/1 * * * *
accept-asset-kinds:
- kraken.component.api
- kraken.component.api-target
Expand All @@ -107,4 +113,4 @@ app:
unified-asset:
endpoints:
exposure:
include: asset
include: asset
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.consoleconnect.kraken.operator.controller.api;

import static com.consoleconnect.kraken.operator.core.service.UnifiedAssetService.getSearchPageRequest;

import com.consoleconnect.kraken.operator.auth.security.UserContext;
import com.consoleconnect.kraken.operator.controller.dto.push.ApiRequestActivityPushResult;
import com.consoleconnect.kraken.operator.controller.dto.push.CreatePushApiActivityRequest;
import com.consoleconnect.kraken.operator.controller.dto.push.PushApiActivityLogHistory;
import com.consoleconnect.kraken.operator.controller.service.push.ApiActivityPushService;
import com.consoleconnect.kraken.operator.core.model.HttpResponse;
import com.consoleconnect.kraken.operator.core.request.PushLogSearchRequest;
import com.consoleconnect.kraken.operator.core.toolkit.Paging;
import com.consoleconnect.kraken.operator.core.toolkit.PagingHelper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.time.ZonedDateTime;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

@AllArgsConstructor
@RestController()
@RequestMapping(value = "/push-api-activity-log", produces = MediaType.APPLICATION_JSON_VALUE)
@Tag(name = "API Activities Push Logs", description = "API Activities Push Logs")
public class APIActivityPushLogController {

private final ApiActivityPushService apiActivityPushService;

@Operation(summary = "Store api activity log info")
@PostMapping
public Mono<HttpResponse<ApiRequestActivityPushResult>> createPushApiActivityLogInfo(
@RequestBody CreatePushApiActivityRequest request) {
return UserContext.getUserId()
.publishOn(Schedulers.boundedElastic())
.map(userId -> apiActivityPushService.createPushApiActivityLogInfo(request, userId))
.map(HttpResponse::ok);
}

@Operation(summary = "search push history")
@GetMapping("/history")
public HttpResponse<Paging<PushApiActivityLogHistory>> searchHistory(
@RequestParam(value = "orderBy", required = false, defaultValue = "createdAt") String orderBy,
@RequestParam(value = "direction", required = false, defaultValue = "DESC")
Sort.Direction direction,
@RequestParam(value = "page", required = false, defaultValue = PagingHelper.DEFAULT_PAGE_STR)
int page,
@RequestParam(value = "size", required = false, defaultValue = PagingHelper.DEFAULT_SIZE_STR)
int size,
@RequestParam(value = "requestStartTime", required = false) ZonedDateTime requestStartTime,
@RequestParam(value = "requestEndTime", required = false) ZonedDateTime requestEndTime) {
return HttpResponse.ok(
this.apiActivityPushService.searchHistory(
PushLogSearchRequest.builder()
.queryStart(requestStartTime)
.queryEnd(requestEndTime)
.build(),
getSearchPageRequest(page, size, direction, orderBy)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.consoleconnect.kraken.operator.controller.dto.push;

import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ApiRequestActivityPushResult {
private UUID id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.consoleconnect.kraken.operator.controller.dto.push;

import java.time.ZonedDateTime;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CreatePushApiActivityRequest {
private ZonedDateTime startTime;
private ZonedDateTime endTime;
private String envId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.consoleconnect.kraken.operator.controller.dto.push;

import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PushApiActivityLogHistory {
private UUID id;
private ZonedDateTime createdAt;
private String envName;
private ZonedDateTime startTime;
private ZonedDateTime endTime;
private String pushedBy;
private String status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.consoleconnect.kraken.operator.controller.service.push;

import com.consoleconnect.kraken.operator.controller.dto.push.ApiRequestActivityPushResult;
import com.consoleconnect.kraken.operator.controller.dto.push.CreatePushApiActivityRequest;
import com.consoleconnect.kraken.operator.controller.dto.push.PushApiActivityLogHistory;
import com.consoleconnect.kraken.operator.controller.model.Environment;
import com.consoleconnect.kraken.operator.controller.service.EnvironmentService;
import com.consoleconnect.kraken.operator.core.entity.MgmtEventEntity;
import com.consoleconnect.kraken.operator.core.enums.EventStatusType;
import com.consoleconnect.kraken.operator.core.enums.MgmtEventType;
import com.consoleconnect.kraken.operator.core.repo.MgmtEventRepository;
import com.consoleconnect.kraken.operator.core.request.PushLogSearchRequest;
import com.consoleconnect.kraken.operator.core.toolkit.JsonToolkit;
import com.consoleconnect.kraken.operator.core.toolkit.Paging;
import com.consoleconnect.kraken.operator.core.toolkit.PagingHelper;
import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class ApiActivityPushService {

public static final String CREATED_AT = "createdAt";
public static final String EVENT_TYPE = "eventType";

private final MgmtEventRepository mgmtEventRepository;
private final EnvironmentService environmentService;

public ApiRequestActivityPushResult createPushApiActivityLogInfo(
CreatePushApiActivityRequest request, String userId) {
var environment = environmentService.findOne(request.getEnvId());
var entity = new MgmtEventEntity();
entity.setStatus(EventStatusType.ACK.name());
entity.setEventType(MgmtEventType.PUSH_API_ACTIVITY_LOG.name());
entity.setPayload(JsonToolkit.toJson(createData(request, userId, environment)));
var saved = mgmtEventRepository.save(entity);
return new ApiRequestActivityPushResult(saved.getId());
}

private PushLogActivityLogInfo createData(
CreatePushApiActivityRequest request, String userId, Environment environment) {
var data = new PushLogActivityLogInfo();
data.setUser(userId);
data.setEnvId(request.getEnvId());
data.setEnvName(environment.getName());
data.setStartTime(request.getStartTime());
data.setEndTime(request.getEndTime());
return data;
}

public Paging<PushApiActivityLogHistory> searchHistory(
PushLogSearchRequest searchRequest, PageRequest pageRequest) {
Page<MgmtEventEntity> pushEvents =
mgmtEventRepository.findAll(getMgmtEventEntitySpecification(searchRequest), pageRequest);
return PagingHelper.toPaging(
pushEvents,
e -> {
var payload = JsonToolkit.fromJson(e.getPayload(), PushLogActivityLogInfo.class);
return new PushApiActivityLogHistory(
e.getId(),
e.getCreatedAt(),
payload.getEnvName(),
payload.getStartTime(),
payload.getEndTime(),
payload.getUser(),
e.getStatus());
});
}

private static Specification<MgmtEventEntity> getMgmtEventEntitySpecification(
PushLogSearchRequest searchRequest) {
return (root, query, criteriaBuilder) -> {
var predicateList = new ArrayList<Predicate>();
predicateList.add(
criteriaBuilder.equal(root.get(EVENT_TYPE), MgmtEventType.PUSH_API_ACTIVITY_LOG.name()));
if (searchRequest.getQueryStart() != null) {
predicateList.add(
criteriaBuilder.greaterThanOrEqualTo(
root.get(CREATED_AT), searchRequest.getQueryStart()));
}
if (searchRequest.getQueryEnd() != null) {
predicateList.add(
criteriaBuilder.lessThanOrEqualTo(root.get(CREATED_AT), searchRequest.getQueryEnd()));
}
return query.where(predicateList.toArray(new Predicate[0])).getRestriction();
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.consoleconnect.kraken.operator.controller.service.push;

import java.time.ZonedDateTime;
import lombok.Data;

@Data
public class PushLogActivityLogInfo {
private String user;
private ZonedDateTime startTime;
private String envId;
private String envName;
private ZonedDateTime endTime;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.consoleconnect.kraken.operator.controller.api;

import static com.consoleconnect.kraken.operator.core.service.UnifiedAssetService.getSearchPageRequest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import com.consoleconnect.kraken.operator.config.TestApplication;
import com.consoleconnect.kraken.operator.controller.WebTestClientHelper;
import com.consoleconnect.kraken.operator.controller.dto.push.ApiRequestActivityPushResult;
import com.consoleconnect.kraken.operator.controller.dto.push.CreatePushApiActivityRequest;
import com.consoleconnect.kraken.operator.controller.dto.push.PushApiActivityLogHistory;
import com.consoleconnect.kraken.operator.controller.service.push.ApiActivityPushService;
import com.consoleconnect.kraken.operator.core.model.HttpResponse;
import com.consoleconnect.kraken.operator.core.request.PushLogSearchRequest;
import com.consoleconnect.kraken.operator.core.toolkit.Paging;
import com.consoleconnect.kraken.operator.core.toolkit.PagingHelper;
import com.consoleconnect.kraken.operator.test.AbstractIntegrationTest;
import com.consoleconnect.kraken.operator.test.MockIntegrationTest;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.domain.Sort;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;

@ActiveProfiles("test-auth-server-enabled")
@MockIntegrationTest
@ContextConfiguration(classes = TestApplication.class)
class APIActivityPushLogControllerTest extends AbstractIntegrationTest {

@MockBean private ApiActivityPushService service;
@Autowired private ObjectMapper objectMapper;
private final WebTestClientHelper testClientHelper;

@Autowired
APIActivityPushLogControllerTest(WebTestClient webTestClient) {
testClientHelper = new WebTestClientHelper(webTestClient);
}

@Test
void givenApiActivityLogs_whenCreatingApiLogsPushRequest_thenReturnsOk() {
// given
var envId = UUID.randomUUID();
var now = ZonedDateTime.now(ZoneOffset.UTC);
var request = new CreatePushApiActivityRequest(now.minusDays(10), now, envId.toString());

var pushResult = new ApiRequestActivityPushResult(UUID.randomUUID());
when(service.createPushApiActivityLogInfo(request, "anonymous")).thenReturn(pushResult);
// when
var path = "/push-api-activity-log";
testClientHelper.postAndVerify(
(uriBuilder -> uriBuilder.path(path).build()),
request,
bodyStr -> {
// then
var result =
content(bodyStr, new TypeReference<HttpResponse<ApiRequestActivityPushResult>>() {});
assertThat(result.getData()).isEqualTo(pushResult);
});
}

@Test
void givenApiActivityLogs_whenSearchPushHistory_thenReturnsOk() {
// given
var pageRequest =
getSearchPageRequest(
PagingHelper.DEFAULT_PAGE, PagingHelper.DEFAULT_SIZE, Sort.Direction.DESC, "createdAt");
var historyList = List.of(new PushApiActivityLogHistory());
when(service.searchHistory(PushLogSearchRequest.builder().build(), pageRequest))
.thenReturn(PagingHelper.toPage(historyList, 0, 1));
// when
var path = "/push-api-activity-log/history";
testClientHelper.getAndVerify(
(uriBuilder -> uriBuilder.path(path).build()),
bodyStr -> {
// then
var result =
content(
bodyStr, new TypeReference<HttpResponse<Paging<PushApiActivityLogHistory>>>() {});
assertThat(result.getData().getData()).isEqualTo(historyList);
});
}

@SneakyThrows
private <T> T content(String response, TypeReference<T> typeReference) {
return objectMapper.readValue(response, typeReference);
}
}
Loading

0 comments on commit 49d359c

Please sign in to comment.