EventTrackerService.java

package io.featureprobe.api.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.featureprobe.api.base.enums.ChangeLogType;
import io.featureprobe.api.base.enums.ResourceType;
import io.featureprobe.api.base.enums.ResponseCodeEnum;
import io.featureprobe.api.base.model.BaseResponse;
import io.featureprobe.api.base.util.JsonMapper;
import io.featureprobe.api.dao.entity.Environment;
import io.featureprobe.api.dao.entity.EventTracker;
import io.featureprobe.api.dao.entity.Traffic;
import io.featureprobe.api.dao.exception.ResourceNotFoundException;
import io.featureprobe.api.dao.repository.DebugEventRepository;
import io.featureprobe.api.dao.repository.EnvironmentRepository;
import io.featureprobe.api.dao.repository.EventTrackerRepository;
import io.featureprobe.api.dao.repository.TrafficRepository;
import io.featureprobe.api.dto.DebugEventResponse;
import io.featureprobe.api.dto.EventStreamResponse;
import io.featureprobe.api.dto.EventTrackerStatusRequest;
import io.featureprobe.api.dto.SummaryEvent;
import io.featureprobe.api.mapper.EventMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
@Slf4j
public class EventTrackerService {


    EnvironmentRepository environmentRepository;
    EventTrackerRepository eventTrackerRepository;

    AnalysisServerService analysisServerService;

    DebugEventRepository debugEventRepository;

    TrafficRepository trafficRepository;

    private ChangeLogService changeLogService;

    @PersistenceContext
    public EntityManager entityManager;

    final ObjectMapper mapper = new ObjectMapper();

    @Transactional(rollbackFor = Exception.class)
    public EventStreamResponse getEventStream(String projectKey, String environmentKey, String uuid) {
        EventStreamResponse response = new EventStreamResponse();
        Environment environment = environmentRepository.findByProjectKeyAndKey(projectKey, environmentKey)
                .orElseThrow(() -> new ResourceNotFoundException(ResourceType.ENVIRONMENT,
                        projectKey + "/" + environmentKey));
        response.setProjectKey(projectKey);
        response.setEnvironmentKey(environmentKey);
        response.setDebugUntilTime(environment.getDebuggerUntilTime());
        if (!(environment.getDebuggerUntilTime() > System.currentTimeMillis())) {
            return response;
        }
        response.setDebuggerEnabled(true);
        Optional<EventTracker> eventTrackerOptional = eventTrackerRepository
                .findByProjectKeyAndEnvironmentKeyAndUuid(projectKey, environmentKey, uuid);
        if (eventTrackerOptional.isPresent()) {
            List<Object> events = new ArrayList<>();
            EventTracker eventTracker = eventTrackerOptional.get();
            List<DebugEventResponse> debugEvents = debugEventRepository
                    .findAllBySdkKeyAndTimeGreaterThanEqual(environment.getServerSdkKey(), eventTracker.getTime())
                    .stream().map(debugEvent -> EventMapper.INSTANCE.debugEventToResponse(debugEvent))
                    .collect(Collectors.toList());
            List<Traffic> traffic = trafficRepository
                    .findAllBySdkKeyAndStartDateGreaterThanEqual(environment.getServerSdkKey(),
                    new Date(eventTracker.getTime()));
            List<SummaryEvent> summaryEvents = traffic.stream().map(SummaryEvent::create).collect(Collectors.toList());
            events.addAll(debugEvents);
            events.addAll(summaryEvents);
            try {
                List<Object> accessEvents = callAnalysis(eventTracker.getTime(), environment.getServerSdkKey());
                events.addAll(accessEvents);
            }catch (Exception e) {
                log.error("Get access event stream is error.", e);
            }
            if (CollectionUtils.isNotEmpty(events)) {
                events = events.stream().sorted((a, b) -> compareEvent(a, b)).collect(Collectors.toList());
                updateEventTrackerTime(eventTracker, System.currentTimeMillis());
            }
            response.setEvents(events);
            return response;
        }
        saveEventTracker(projectKey, environmentKey, uuid);
        return response;
    }

    private int compareEvent(Object first, Object second) {
        Map firstEvent = mapper.convertValue(first, Map.class);
        Long firstTime = (Long) firstEvent.get("time");
        if (first instanceof SummaryEvent) {
            firstTime = ((SummaryEvent) first).getStartDate().getTime();
        }
        Map secondEvent = mapper.convertValue(second, Map.class);
        Long secondTime = (Long) secondEvent.get("time");
        if (second instanceof SummaryEvent) {
            secondTime = ((SummaryEvent) second).getStartDate().getTime();
        }
        return Long.compare(firstTime, secondTime);
    }

    @Transactional(rollbackFor = Exception.class)
    public BaseResponse status(String projectKey, String environmentKey, EventTrackerStatusRequest statusRequest) {
        Environment environment = environmentRepository.findByProjectKeyAndKey(projectKey, environmentKey)
                .orElseThrow(() -> new ResourceNotFoundException(ResourceType.ENVIRONMENT,
                        projectKey + "/" + environmentKey));
        if (statusRequest.isEnabled()) {
            environment.setDebuggerUntilTime(System.currentTimeMillis() + 30 * 60 * 1000);
        } else {
            environment.setDebuggerUntilTime(0L);
        }
        environmentRepository.save(environment);
        changeLogService.create(environment, ChangeLogType.CHANGE);
        return new BaseResponse(ResponseCodeEnum.SUCCESS);
    }

    private void saveEventTracker(String projectKey, String environmentKey, String uuid) {
        EventTracker eventTracker = new EventTracker();
        eventTracker.setProjectKey(projectKey);
        eventTracker.setEnvironmentKey(environmentKey);
        eventTracker.setUuid(uuid);
        eventTracker.setTime(System.currentTimeMillis());
        eventTrackerRepository.save(eventTracker);
    }

    private void updateEventTrackerTime(EventTracker eventTracker, long time) {
        eventTracker.setTime(time);
        eventTrackerRepository.save(eventTracker);
    }

    private List<Object> callAnalysis(Long time, String sdkKey) {
        Map<String, Object> params = new HashMap<>();
        params.put("time", time);
        String callRes = analysisServerService.callAnalysisServer("/events",
                analysisServerService.formatHttpQuery(params), sdkKey);
        List<Object> events = JsonMapper.toListObject(callRes, Object.class);
        return events;
    }


}