AccessTokenService.java

package io.featureprobe.api.service;

import io.featureprobe.api.auth.TokenHelper;
import io.featureprobe.api.base.db.ExcludeTenant;
import io.featureprobe.api.base.enums.AccessTokenType;
import io.featureprobe.api.base.enums.MemberSourceEnum;
import io.featureprobe.api.base.enums.ResourceType;
import io.featureprobe.api.base.util.KeyGenerateUtil;
import io.featureprobe.api.dao.entity.AccessToken;
import io.featureprobe.api.dao.entity.Member;
import io.featureprobe.api.dao.exception.ResourceConflictException;
import io.featureprobe.api.dao.exception.ResourceNotFoundException;
import io.featureprobe.api.dao.repository.AccessTokenRepository;
import io.featureprobe.api.dao.utils.PageRequestUtil;
import io.featureprobe.api.dto.AccessTokenCreateRequest;
import io.featureprobe.api.dto.AccessTokenResponse;
import io.featureprobe.api.dto.AccessTokenSearchRequest;
import io.featureprobe.api.dto.MemberCreateRequest;
import io.featureprobe.api.dto.TokenResponse;
import io.featureprobe.api.mapper.AccessTokenMapper;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.Predicate;
import java.util.Date;
import java.util.Optional;

@AllArgsConstructor
@Service
public class AccessTokenService {

    private AccessTokenRepository accessTokenRepository;

    @PersistenceContext
    public EntityManager entityManager;

    private MemberService memberService;

    @Transactional(rollbackFor = Exception.class)
    public TokenResponse create(AccessTokenCreateRequest createRequest) {
        validateExists(createRequest.getName(), createRequest.getType());

        AccessToken accessToken = accessTokenRepository.save(newToken(createRequest));
        return new TokenResponse(accessToken.getToken());
    }

    private AccessToken newToken(AccessTokenCreateRequest createRequest) {
        AccessToken token = AccessTokenMapper.INSTANCE.requestToEntity(createRequest);
        token.setToken(KeyGenerateUtil.getAPIAccessToken());
        if (createRequest.getType() == AccessTokenType.PERSON) {
            token.setMemberId(TokenHelper.getUserId());
            token.setRole(null);
        } else {
            Member member = createAccessTokenMember(createRequest, token);
            token.setMemberId(member.getId());
        }
        return token;
    }

    private Member createAccessTokenMember(AccessTokenCreateRequest createRequest, AccessToken token) {
        MemberCreateRequest memberCreateRequest = new MemberCreateRequest();
        String account = "api:" + token.getName();
        memberCreateRequest.setAccounts(Lists.newArrayList(account));
        memberCreateRequest.setPassword(String.valueOf(System.nanoTime()));
        memberCreateRequest.setSource(MemberSourceEnum.ACCESS_TOKEN.name());
        memberCreateRequest.setRole(createRequest.getRole());
        memberService.createUserInCurrentOrganization(memberCreateRequest);
        Member member = memberService.findByAccount(account).get();
        return member;
    }

    @Transactional(rollbackFor = Exception.class)
    public AccessTokenResponse delete(Long tokenId) {
        AccessToken accessToken = getAccessTokenById(tokenId);
        accessToken.setDeleted(true);
        if (accessToken.getMemberId() != null && accessToken.getType() == AccessTokenType.APPLICATION) {
            Member member = memberService.findById(accessToken.getMemberId()).orElse(null);
            if (member != null) {
                memberService.delete(member.getAccount());
            }
        }
        return AccessTokenMapper.INSTANCE.entityToResponse(accessTokenRepository.save(accessToken));
    }

    public Page<AccessTokenResponse> list(AccessTokenSearchRequest searchRequest) {
        Pageable pageable = PageRequestUtil.toCreatedTimeDescSortPageable(searchRequest);
        Page<AccessToken> tokens = accessTokenRepository.findAll(buildQuerySpec(searchRequest), pageable);
        return tokens.map(token -> AccessTokenMapper.INSTANCE.entityToResponse(token));
    }

    private Specification<AccessToken> buildQuerySpec(AccessTokenSearchRequest searchRequest) {
        return (root, query, cb) -> {
            Predicate p3 = cb.equal(root.get("type"), searchRequest.getType());
            return query.where(p3).getRestriction();
        };
    }

    public AccessTokenResponse queryById(Long tokenId) {
        return AccessTokenMapper.INSTANCE.entityToResponse(getAccessTokenById(tokenId));
    }

    private AccessToken getAccessTokenById(Long tokenId) {
        AccessToken accessToken = accessTokenRepository.findById(tokenId)
                .orElseThrow(() -> new ResourceNotFoundException(ResourceType.ACCESS_TOKEN, String.valueOf(tokenId)));
        return accessToken;
    }

    public void validateExists(String name, AccessTokenType type) {
        boolean existed = accessTokenRepository.existsByNameAndType(name, type);
        if (existed) {
            throw new ResourceConflictException(ResourceType.ACCESS_TOKEN);
        }
    }

    @ExcludeTenant
    public Optional<AccessToken> findByToken(String token) {
        return accessTokenRepository.findByToken(token);
    }

    @ExcludeTenant
    public void updateVisitedTime(Long id) {
        AccessToken accessToken = getAccessTokenById(id);
        accessToken.setVisitedTime(new Date());
        accessTokenRepository.save(accessToken);
    }
}