PlatformService.java

package com.distasilucas.cryptobalancetracker.service;

import com.distasilucas.cryptobalancetracker.entity.Platform;
import com.distasilucas.cryptobalancetracker.exception.DuplicatedPlatformException;
import com.distasilucas.cryptobalancetracker.exception.PlatformNotFoundException;
import com.distasilucas.cryptobalancetracker.model.request.platform.PlatformRequest;
import com.distasilucas.cryptobalancetracker.repository.PlatformRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;

import static com.distasilucas.cryptobalancetracker.constants.Constants.ALL_PLATFORMS_CACHE;
import static com.distasilucas.cryptobalancetracker.constants.Constants.PLATFORMS_PLATFORMS_IDS_CACHE;
import static com.distasilucas.cryptobalancetracker.constants.Constants.PLATFORM_PLATFORM_ID_CACHE;
import static com.distasilucas.cryptobalancetracker.constants.ExceptionConstants.DUPLICATED_PLATFORM;
import static com.distasilucas.cryptobalancetracker.constants.ExceptionConstants.PLATFORM_ID_NOT_FOUND;
import static com.distasilucas.cryptobalancetracker.model.CacheType.INSIGHTS_CACHES;
import static com.distasilucas.cryptobalancetracker.model.CacheType.PLATFORMS_CACHES;
import static com.distasilucas.cryptobalancetracker.model.CacheType.USER_CRYPTOS_CACHES;

@Slf4j
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PlatformService {

    private final PlatformRepository platformRepository;
    private final UserCryptoService userCryptoService;
    private final CacheService cacheService;
    private final PlatformService self;

    public PlatformService(PlatformRepository platformRepository,
                           @Lazy UserCryptoService userCryptoService,
                           CacheService cacheService,
                           PlatformService self) {
        this.platformRepository = platformRepository;
        this.userCryptoService = userCryptoService;
        this.cacheService = cacheService;
        this.self = self;
    }

    @Cacheable(cacheNames = ALL_PLATFORMS_CACHE)
    public List<Platform> retrieveAllPlatforms() {
        log.info("Retrieving all platforms");

        return platformRepository.findAll();
    }

    @Cacheable(cacheNames = PLATFORM_PLATFORM_ID_CACHE, key = "#platformId")
    public Platform retrievePlatformById(String platformId) {
        log.info("Retrieving platform with id {}", platformId);

        return platformRepository.findById(platformId)
            .orElseThrow(() -> {
                var message = PLATFORM_ID_NOT_FOUND.formatted(platformId);

                return new PlatformNotFoundException(message);
            });
    }

    public Platform savePlatform(PlatformRequest platformRequest) {
        validatePlatformDoesNotExist(platformRequest.name());
        var platformEntity = platformRequest.toEntity();
        platformRepository.save(platformEntity);
        cacheService.invalidate(PLATFORMS_CACHES);
        log.info("Saved platform {}", platformEntity);

        return platformEntity;
    }

    public Platform updatePlatform(String platformId, PlatformRequest platformRequest) {
        validatePlatformDoesNotExist(platformRequest.name());
        var platform = self.retrievePlatformById(platformId);
        var updatedPlatform = new Platform(platform.getId(), platformRequest.name().toUpperCase());

        log.info("Updating platform. Before: {}. After: {}", platform, updatedPlatform);
        platformRepository.save(updatedPlatform);
        cacheService.invalidate(PLATFORMS_CACHES, USER_CRYPTOS_CACHES, INSIGHTS_CACHES);

        return updatedPlatform;
    }

    public void deletePlatform(String platformId) {
        var platform = self.retrievePlatformById(platformId);
        var userCryptosToDelete = userCryptoService.findAllByPlatformId(platformId);

        userCryptoService.deleteUserCryptos(userCryptosToDelete);
        platformRepository.delete(platform);
        cacheService.invalidate(PLATFORMS_CACHES, INSIGHTS_CACHES);

        log.info("Deleted platform {} and cryptos {}", platform, userCryptosToDelete);
    }

    @Cacheable(cacheNames = PLATFORMS_PLATFORMS_IDS_CACHE, key = "#ids")
    public List<Platform> findAllByIds(Collection<String> ids) {
        log.info("Retrieving platforms for ids {}", ids);

        return platformRepository.findAllByIdIn(ids);
    }

    private void validatePlatformDoesNotExist(String platformName) {
        var platform = platformRepository.findByName(platformName.toUpperCase());

        if (platform.isPresent()) {
            var message = DUPLICATED_PLATFORM.formatted(platformName.toUpperCase());

            throw new DuplicatedPlatformException(message);
        }
    }
}