Skip to content

Commit

Permalink
Merge branch 'main' into 122-feature-allow-control-plane-user-to-push…
Browse files Browse the repository at this point in the history
…-api-activity-log-to-external-system
  • Loading branch information
KsiBart authored Nov 22, 2024
2 parents 2071e78 + 975f12e commit aa40790
Show file tree
Hide file tree
Showing 24 changed files with 1,213 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,17 @@ app:
controlPlane:
url: http://localhost:8001
token: Bearer ${KRAKEN_MGMT_API_ACCESS_TOKEN:FAKE_TOKEN}
push-activity-log-external:
enabled: false
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 +111,4 @@ app:
unified-asset:
endpoints:
exposure:
include: asset
include: asset
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ app:
endpoints:
exposure:
include: asset, component, product, ingestion, component-operation, component-mapper
features:
push-activity-log-external:
enabled: false

initialize-exclude-assets:
- classpath:/mef-sonata/apis/api.product.offering.yaml
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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.PushApiActivityLogEnabled;
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)));
}

@Operation(summary = "check if push log enabled")
@GetMapping("/enabled")
public HttpResponse<PushApiActivityLogEnabled> isPushApiActivityLogEnabled() {
return HttpResponse.ok(this.apiActivityPushService.isPushApiActivityLogEnabled());
}
}
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,12 @@
package com.consoleconnect.kraken.operator.controller.dto.push;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PushApiActivityLogEnabled {
private boolean enabled;
}
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,144 @@
package com.consoleconnect.kraken.operator.controller.service.push;

import static com.consoleconnect.kraken.operator.core.toolkit.JsonToolkit.fromJson;
import static com.consoleconnect.kraken.operator.core.toolkit.JsonToolkit.toJson;

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.PushApiActivityLogEnabled;
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.exception.KrakenException;
import com.consoleconnect.kraken.operator.core.model.AppProperty;
import com.consoleconnect.kraken.operator.core.repo.MgmtEventRepository;
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 jakarta.persistence.criteria.Predicate;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
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";
public static final String PUSH_API_ACTIVITY_LOGS_IS_DISABLED =
"Push api activity logs is disabled.";
public static final String THE_SAME_PARAMETERS_ALREADY_EXISTS_ERROR =
"Push event with the same parameters already exists with status 'ack' or 'in_progress'.";

private final MgmtEventRepository mgmtEventRepository;
private final EnvironmentService environmentService;
private final AppProperty appProperty;

public ApiRequestActivityPushResult createPushApiActivityLogInfo(
CreatePushApiActivityRequest request, String userId) {
validateRequest(request);
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(toJson(createData(request, userId, environment)));
var saved = mgmtEventRepository.save(entity);
return new ApiRequestActivityPushResult(saved.getId());
}

private void validateRequest(CreatePushApiActivityRequest searchRequest) {
validateIfFeatureIsEnabled();
validatePushEventWithTheSameParameters(searchRequest);
}

private void validateIfFeatureIsEnabled() {
if (!appProperty.getFeatures().getPushActivityLogExternal().isEnabled()) {
throw new KrakenException(400, PUSH_API_ACTIVITY_LOGS_IS_DISABLED);
}
}

private void validatePushEventWithTheSameParameters(CreatePushApiActivityRequest searchRequest) {
boolean exists =
mgmtEventRepository.existsBy(
List.of(EventStatusType.ACK.name(), EventStatusType.IN_PROGRESS.name()),
searchRequest.getEnvId(),
toUtcString(searchRequest.getStartTime()),
toUtcString(searchRequest.getEndTime()),
MgmtEventType.PUSH_API_ACTIVITY_LOG.name());

if (exists) {
throw new KrakenException(400, THE_SAME_PARAMETERS_ALREADY_EXISTS_ERROR);
}
}

private String toUtcString(ZonedDateTime zonedDateTime) {
return zonedDateTime
.withZoneSameInstant(ZoneOffset.UTC)
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
}

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(toUtcString(request.getStartTime()));
data.setEndTime(toUtcString(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 = fromJson(e.getPayload(), PushLogActivityLogInfo.class);
return new PushApiActivityLogHistory(
e.getId(),
e.getCreatedAt(),
payload.getEnvName(),
ZonedDateTime.parse(payload.getStartTime()),
ZonedDateTime.parse(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();
};
}

public PushApiActivityLogEnabled isPushApiActivityLogEnabled() {
return new PushApiActivityLogEnabled(
appProperty.getFeatures().getPushActivityLogExternal().isEnabled());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.consoleconnect.kraken.operator.controller.service.push;

import lombok.Data;

@Data
public class PushLogActivityLogInfo {
private String user;
private String startTime;
private String envId;
private String envName;
private String endTime;
}
Loading

0 comments on commit aa40790

Please sign in to comment.