ResourceExistsValidateAspect.java

package io.featureprobe.api.validate;

import io.featureprobe.api.dao.exception.ResourceNotFoundException;
import io.featureprobe.api.dao.repository.EnvironmentRepository;
import io.featureprobe.api.dao.repository.ProjectRepository;
import io.featureprobe.api.dao.repository.SegmentRepository;
import io.featureprobe.api.dao.repository.ToggleRepository;
import io.featureprobe.api.base.enums.ResourceType;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;

import java.lang.reflect.Parameter;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.IntStream;

@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class ResourceExistsValidateAspect {

    ProjectRepository projectRepository;
    ToggleRepository toggleRepository;
    EnvironmentRepository environmentRepository;
    SegmentRepository segmentRepository;

    @Around(value = "@within(resourceExistsValidate)")
    public Object validate(ProceedingJoinPoint joinPoint, ResourceExistsValidate resourceExistsValidate)
            throws Throwable {
        if (joinPoint.getSignature() instanceof MethodSignature) {
            validateResourceKeyExists(joinPoint);
        }
        return joinPoint.proceed();
    }

    private void validateResourceKeyExists(ProceedingJoinPoint joinPoint) {
        List<ResourceKey> resourceKeys = parseResourceKeys(joinPoint);
        if (resourceKeys.isEmpty()) {
            return;
        }
        validateResourceKeyExists(resourceKeys);
    }

    protected void validateResourceKeyExists(List<ResourceKey> resourceKeys) {
        ResourceKey projectKey = filterFirstResourceKey(resourceKeys, ResourceKey::isProject);
        if (projectKey == null) {
            return;
        }
        validateProjectExists(projectKey);
        validateToggleExists(projectKey, resourceKeys);
        validateEnvironmentExists(projectKey, resourceKeys);
        validateSegmentExists(projectKey, resourceKeys);
    }

    protected void validateProjectExists(ResourceKey projectKey) {
        if (projectKey == null || StringUtils.isBlank(projectKey.key)) {
            return;
        }
        if (!projectRepository.existsByKey(projectKey.key)) {
            throwResourceNotFoundException(projectKey);
        }
    }

    protected void validateEnvironmentExists(ResourceKey projectKey, List<ResourceKey> resourceKeys) {
        ResourceKey environmentKey = filterFirstResourceKey(resourceKeys, ResourceKey::isEnvironment);
        if (environmentKey == null) {
            return;
        }
        if (!environmentRepository.existsByProjectKeyAndKey(projectKey.key, environmentKey.getKey())) {
            throwResourceNotFoundException(environmentKey);
        }
    }

    protected void validateToggleExists(ResourceKey projectKey, List<ResourceKey> resourceKeys) {
        ResourceKey toggleKey = filterFirstResourceKey(resourceKeys, ResourceKey::isToggle);
        if (toggleKey == null) {
            return;
        }
        if (!toggleRepository.existsByProjectKeyAndKey(projectKey.key, toggleKey.getKey())) {
            throwResourceNotFoundException(toggleKey);
        }
    }

    protected void validateSegmentExists(ResourceKey projectKey, List<ResourceKey> resourceKeys) {
        ResourceKey segmentKey = filterFirstResourceKey(resourceKeys, ResourceKey::isSegment);
        if (segmentKey == null) {
            return;
        }
        if (!segmentRepository.existsByProjectKeyAndKey(projectKey.key, segmentKey.getKey())) {
            throwResourceNotFoundException(segmentKey);
        }

    }

    private ResourceKey filterFirstResourceKey(List<ResourceKey> resourceKeys, Predicate<ResourceKey> filter) {
        ResourceKey resourceKey = resourceKeys.stream().filter(filter).findFirst().orElse(null);
        if (resourceKey == null || StringUtils.isBlank(resourceKey.key)) {
            return null;
        }
        return resourceKey;
    }

    protected List<ResourceKey> parseResourceKeys(ProceedingJoinPoint joinPoint) {
        List<ResourceKey> resourceKeys = Lists.newArrayList();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Object[] methodArgs = joinPoint.getArgs();
        Parameter[] parameters = signature.getMethod().getParameters();

        IntStream.range(0, parameters.length).forEach(index -> {
            ResourceKey resourceKey = parseResourceKey(methodArgs, parameters, index);
            if (resourceKey != null) {
                resourceKeys.add(resourceKey);
            }
        });

        return resourceKeys;
    }

    private ResourceKey parseResourceKey(Object[] methodArgs, Parameter[] parameters, int index) {
        PathVariable pathVariable = parameters[index].getAnnotation(PathVariable.class);
        String paramName = pathVariable != null ? pathVariable.name() : parameters[index].getName();
        ResourceType resourceType = ResourceType.of(paramName);
        if (resourceType == null) {
            return null;
        }
        if (StringUtils.equals(paramName, resourceType.getParamName())) {
            return new ResourceKey(resourceType, String.valueOf(methodArgs[index]));
        }
        return null;
    }

    private void throwResourceNotFoundException(ResourceKey resourceKey) {
        throw new ResourceNotFoundException(resourceKey.type, resourceKey.getKey());
    }
}