package br.com.conductor.heimdall.core.service; /*- * =========================LICENSE_START================================== * heimdall-core * ======================================================================== * Copyright (C) 2018 Conductor Tecnologia SA * ======================================================================== * Licensed under the Apache License, Version 2.0 (the "License") * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ==========================LICENSE_END=================================== */ import static br.com.conductor.heimdall.core.exception.ExceptionMessage.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import br.com.conductor.heimdall.core.dto.ReferenceIdDTO; import br.com.conductor.heimdall.core.dto.persist.AppPersist; import br.com.conductor.heimdall.core.entity.AccessToken; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; import org.springframework.data.domain.ExampleMatcher.StringMatcher; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import br.com.conductor.heimdall.core.converter.GenericConverter; import br.com.conductor.heimdall.core.dto.AppDTO; import br.com.conductor.heimdall.core.dto.PageDTO; import br.com.conductor.heimdall.core.dto.PageableDTO; import br.com.conductor.heimdall.core.dto.integration.AppCallbackDTO; import br.com.conductor.heimdall.core.dto.page.AppPage; import br.com.conductor.heimdall.core.dto.request.AppRequestDTO; import br.com.conductor.heimdall.core.entity.App; import br.com.conductor.heimdall.core.entity.Developer; import br.com.conductor.heimdall.core.entity.Plan; import br.com.conductor.heimdall.core.exception.HeimdallException; import br.com.conductor.heimdall.core.repository.AccessTokenRepository; import br.com.conductor.heimdall.core.repository.AppRepository; import br.com.conductor.heimdall.core.repository.DeveloperRepository; import br.com.conductor.heimdall.core.repository.PlanRepository; import br.com.conductor.heimdall.core.service.amqp.AMQPCacheService; import br.com.conductor.heimdall.core.util.Pageable; import net.bytebuddy.utility.RandomString; /** * This class provides methods to create, read, update and delete the {@link App} resource. * * @author Filipe Germano * @author <a href="https://dijalmasilva.github.io" target="_blank">Dijalma Silva</a> */ @Service public class AppService { @Autowired private AppRepository appRepository; @Autowired private DeveloperRepository devRepository; @Autowired private PlanRepository planRepository; @Autowired private AccessTokenRepository accessTokenRepository; @Autowired private AMQPCacheService amqpCacheService; /** * Finds a {@link App} by its ID. * * @param id The id of the {@link App} * @return The {@link App} that was found */ @Transactional(readOnly = true) public App find(Long id) { App app = appRepository.findOne(id); HeimdallException.checkThrow(app == null, GLOBAL_RESOURCE_NOT_FOUND); app.setAccessTokens(accessTokenRepository.findByAppId(app.getId())); return app; } /** * Generates a paged list of App. * * @param appDTO The {@link AppDTO} * @param pageableDTO The {@link PageableDTO} * @return The paged {@link App} list as a {@link AppPage} object */ @Transactional(readOnly = true) public AppPage list(AppRequestDTO appDTO, PageableDTO pageableDTO) { App app = GenericConverter.mapper(appDTO, App.class); Example<App> example = Example.of(app, ExampleMatcher.matching().withIgnoreCase().withStringMatcher(StringMatcher.CONTAINING)); Pageable pageable = Pageable.setPageable(pageableDTO.getOffset(), pageableDTO.getLimit()); Page<App> page = appRepository.findAll(example, pageable); AppPage appPage = new AppPage(PageDTO.build(page)); return appPage; } /** * Generates a list of {@link App}. * * @param appDTO The {@link AppDTO} * @return The list of {@link App}'s */ @Transactional(readOnly = true) public List<App> list(AppRequestDTO appDTO) { App app = GenericConverter.mapper(appDTO, App.class); Example<App> example = Example.of(app, ExampleMatcher.matching().withIgnoreCase().withStringMatcher(StringMatcher.CONTAINING)); List<App> apps = appRepository.findAll(example); return apps; } /** * Saves a {@link App} to the repository. * * @param appDTO The {@link AppPersist} * @return The saved {@link App} * @throws HeimdallException Developer not exist, ClientId already used */ public App save(AppPersist appDTO) { if (appDTO.getClientId() != null) { App app = appRepository.findByClientId(appDTO.getClientId()); HeimdallException.checkThrow(Objects.nonNull(app), CLIENT_ID_ALREADY); } else { RandomString randomString = new RandomString(12); String token = randomString.nextString(); while (appRepository.findByClientId(token) != null) { token = randomString.nextString(); } appDTO.setClientId(token); } App app = GenericConverter.mapper(appDTO, App.class); Developer dev = devRepository.findOne(app.getDeveloper().getId()); HeimdallException.checkThrow(dev == null, DEVELOPER_NOT_EXIST); amqpCacheService.dispatchClean(); return appRepository.save(app); } /** * Updates a {@link App} by its ID. * * @param id The ID of the {@link App} * @param appDTO {@link AppDTO} * @return The updated {@link App} * @throws HeimdallException Resource not found */ public App update(Long id, AppDTO appDTO) { App app = appRepository.findOne(id); HeimdallException.checkThrow(app == null, GLOBAL_RESOURCE_NOT_FOUND); updateTokensPlansByApp(id, appDTO.getPlans().stream().map(ReferenceIdDTO::getId).collect(Collectors.toList())); app.setAccessTokens(accessTokenRepository.findByAppId(app.getId())); app = GenericConverter.mapper(appDTO, app); app = appRepository.save(app); amqpCacheService.dispatchClean(); return app; } /** * Updates app's access tokens. * This is used for removing the access token to plan association, only if an app removes one of it's plans. * * @param appId The ID of the {@link App} * @param plansIds List of {@link Plan}'s IDs */ private void updateTokensPlansByApp(Long appId, List<Long> plansIds) { List<AccessToken> accessTokenList = accessTokenRepository.findByAppId(appId); if (Objects.nonNull(accessTokenList)) { accessTokenList.forEach(accessToken -> { if (Objects.nonNull(accessToken.getPlans()) && !accessToken.getPlans().isEmpty()) { List<Plan> planList = accessToken.getPlans().stream().filter(plan -> plansIds.contains(plan.getId())).collect(Collectors.toList()); accessToken.setPlans(planList); accessTokenRepository.save(accessToken); } }); } } /** * Deletes a {@link App} by its ID. * * @param id The ID of the {@link App} * @throws HeimdallException Resource not found */ public void delete(Long id) { App app = appRepository.findOne(id); HeimdallException.checkThrow(app == null, GLOBAL_RESOURCE_NOT_FOUND); amqpCacheService.dispatchClean(); appRepository.delete(app); } /** * Saves a {@link App}. * * @param reqBody {@link AppCallbackDTO} * @return The {@link App} saved * @throws HeimdallException Developer not exist */ @Transactional public App save(AppCallbackDTO reqBody) { App app = appRepository.findByClientId(reqBody.getCode()); Developer dev = devRepository.findByEmail(reqBody.getDeveloper()); HeimdallException.checkThrow(dev == null, DEVELOPER_NOT_EXIST); if (app == null) { app = new App(); } else { List<Plan> plans = appRepository.findPlansByApp(app.getId()); app.setPlans(plans); } app.setClientId(reqBody.getCode()); app.setDeveloper(dev); app.setDescription(reqBody.getDescription()); app.setName(reqBody.getName()); if (app.getPlans() != null && app.getPlans().isEmpty()) { Plan plan = planRepository.findOne(1L); if (plan != null) { app.setPlans(new ArrayList<>(Collections.singletonList(plan))); } } app = appRepository.save(app); amqpCacheService.dispatchClean(); return app; } }