/** * This file is part of lavagna. * * lavagna is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * lavagna is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with lavagna. If not, see <http://www.gnu.org/licenses/>. */ package io.lavagna.service; import io.lavagna.model.*; import io.lavagna.query.ProjectQuery; import org.apache.commons.lang3.tuple.ImmutablePair; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import static org.apache.commons.lang3.StringUtils.trimToNull; /** * {@link Project} related service. */ @Service @Transactional(readOnly = true) public class ProjectService { private final NamedParameterJdbcTemplate jdbc; private final CardLabelRepository cardLabelRepository; private final PermissionService permissionService; private final ProjectQuery queries; public ProjectService(NamedParameterJdbcTemplate jdbc, ProjectQuery queries, CardLabelRepository cardLabelRepository, PermissionService permissionService) { this.jdbc = jdbc; this.queries = queries; this.cardLabelRepository = cardLabelRepository; this.permissionService = permissionService; } private static <T> T firstOrNull(List<T> t) { return t.isEmpty() ? null : t.get(0); } @Transactional(readOnly = false) public Project create(String name, String shortName, String description) { queries.createProject(trimToNull(name), trimToNull(shortName.toUpperCase(Locale.ENGLISH)), trimToNull(description)); Project project = queries.findLastCreatedProject(); // Add default labels to the Project cardLabelRepository.addSystemLabels(project.getId()); // create default column definitions createDefaultColumnDefinitions(project.getId()); // Role anonymousRole = new Role("ANONYMOUS"); permissionService.createFullRoleInProjectId(anonymousRole, project.getId(), false, true, true); permissionService.updatePermissionsToRole(anonymousRole, EnumSet.of(Permission.READ)); // return project; } private void createDefaultColumnDefinitions(int projectId) { List<SqlParameterSource> params = new ArrayList<>(ColumnDefinition.values().length); for (ColumnDefinition definition : ColumnDefinition.values()) { SqlParameterSource param = new MapSqlParameterSource("value", definition.toString()).addValue("color", definition.getDefaultColor()).addValue("projectId", projectId); params.add(param); } jdbc.batchUpdate(queries.createColumnDefinition(), params.toArray(new SqlParameterSource[params.size()])); } @Transactional(readOnly = false) public int updateColumnDefinition(int projectId, int columnDefinitionId, int color) { return queries.updateColumnDefinition(color, projectId, columnDefinitionId); } @Transactional(readOnly = false) public Project updateProject(int projectId, String name, String description, boolean archived) { queries.updateProject(projectId, name, description, archived); return queries.findById(projectId); } /** * Bulk creation of projects. Will skip the project that already exists. * * @param projects */ @Transactional(readOnly = false) public ImmutablePair<List<Project>, List<Project>> createMissing(List<Project> projects) { List<Project> created = new ArrayList<>(); List<Project> skipped = new ArrayList<>(); Set<String> usedShortNames = new HashSet<>(); for (Project pi : findAll()) { usedShortNames.add(pi.getShortName()); } for (Project p : projects) { if (!usedShortNames.contains(p.getShortName())) { Project createdProject = create(p.getName(), p.getShortName(), p.getDescription()); updateProject(createdProject.getId(), createdProject.getName(), createdProject.getDescription(), p.getArchived()); created.add(createdProject); } else { skipped.add(p); } } return ImmutablePair.of(Collections.unmodifiableList(created), Collections.unmodifiableList(skipped)); } public Project findById(int projectId) { return queries.findById(projectId); } public Project findByShortName(String shortName) { return queries.findByShortName(shortName); } public int findIdByShortName(String shortName) { return queries.findIdByShortName(shortName); } public List<Project> findAllProjects(UserWithPermission user) { if (user.getBasePermissions().containsKey(Permission.READ)) { return findAll(); } return findAllForUserWithPermissionInProject(user); } public List<Project> findAll() { return queries.findAll(); } /** * Find the projects that a user has a specific READ permission present as a _project_ level permission. * * @param user * @return */ public List<Project> findAllForUserWithPermissionInProject(User user) { return queries.findAllForUser(user.getId(), Permission.READ.toString()); } // --------- public String findRelatedProjectShortNameByBoardShortname(String shortName) { return fromProjectIdToShortName(firstOrNull(queries.findRelatedProjectIdByBoardShortname(shortName))); } // FIXME: fetch directly the project short name? private String fromProjectIdToShortName(Integer projectId) { return projectId != null ? findById(projectId).getShortName() : null; } public String findRelatedProjectShortNameByCardId(int cardId) { return fromProjectIdToShortName(firstOrNull(queries.findRelatedProjectIdByCardId(cardId))); } public String findRelatedProjectShortNameByEventId(int eventId) { return fromProjectIdToShortName(firstOrNull(queries.findRelatedProjectIdByEventId(eventId))); } public String findRelatedProjectShortNameByColumnId(int columnId) { return fromProjectIdToShortName(firstOrNull(queries.findRelatedProjectIdByColumnId(columnId))); } public String findRelatedProjectShortNameByCardDataId(int cardDataId) { return fromProjectIdToShortName(firstOrNull(queries.findRelatedProjectIdByCardDataId(cardDataId))); } public String findRelatedProjectShortNameByLabelId(Integer labelId) { return fromProjectIdToShortName(firstOrNull(queries.findRelatedProjectIdByLabelId(labelId))); } public String findRelatedProjectShortNameByLabelListValudIdPath(Integer labelListValueIdPath) { return fromProjectIdToShortName(firstOrNull(queries.findRelatedProjectIdByLabelListValudIdPath(labelListValueIdPath))); } public String findRelatedProjectShortNameByColumnDefinitionId(int columnDefinitionId) { return fromProjectIdToShortName(firstOrNull(queries .findRelatedProjectIdByColumnDefinitionId(columnDefinitionId))); } public String findRelatedProjectShortNameByLabelValueId(Integer labelValueId) { return fromProjectIdToShortName(firstOrNull(queries.findRelatedProjectIdByLabelValueId(labelValueId))); } public List<BoardColumnDefinition> findColumnDefinitionsByProjectId(int projectId) { return queries.findColumnDefinitionsByProjectId(projectId); } public Map<ColumnDefinition, BoardColumnDefinition> findMappedColumnDefinitionsByProjectId(int projectId) { Map<ColumnDefinition, BoardColumnDefinition> mappedDefinitions = new EnumMap<>(ColumnDefinition.class); for (BoardColumnDefinition definition : findColumnDefinitionsByProjectId(projectId)) { mappedDefinitions.put(definition.getValue(), definition); } return mappedDefinitions; } public boolean existsWithShortName(String shortName) { return Integer.valueOf(1).equals(queries.existsWithShortName(shortName)); } public List<ProjectWithEventCounts> findProjectsActivityByUserInProjects(int userId, Collection<Integer> projectIds) { if (projectIds.isEmpty()) { return Collections.emptyList(); } else { return queries.findProjectsByUserActivityInProjects(userId, projectIds); } } public List<ProjectWithEventCounts> findProjectsActivityByUser(int userId) { return queries.findProjectsByUserActivity(userId); } public ProjectMetadata getMetadata(String shortName) { SortedMap<Integer, CardLabel> res = new TreeMap<>(); int projectId = findIdByShortName(shortName); for (CardLabel cl : cardLabelRepository.findLabelsByProject(projectId)) { res.put(cl.getId(), cl); } SortedMap<Integer, LabelListValueWithMetadata> labelListValues = cardLabelRepository.findLabeListValueAggregatedByCardLabelId(projectId); Map<ColumnDefinition, BoardColumnDefinition> columnsDefinition = findMappedColumnDefinitionsByProjectId(projectId); return new ProjectMetadata(shortName, res, labelListValues, columnsDefinition); } }