/**
* 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.model.Event.EventType;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import static java.util.EnumSet.of;
@Service
@Transactional(readOnly = true)
public class CardDataService {
private final EventRepository eventRepository;
private final CardDataRepository cardDataRepository;
public CardDataService(EventRepository eventRepository, CardDataRepository cardDataRepository) {
this.eventRepository = eventRepository;
this.cardDataRepository = cardDataRepository;
}
public List<CardDataFull> findDescriptionByCardId(int cardId) {
return cardDataRepository.findAllDataByCardIdAndType(cardId, CardType.DESCRIPTION);
}
public CardDataHistory findLatestDescriptionByCardId(int cardId) {
List<CardDataFull> descriptions = findDescriptionByCardId(cardId);
// look for duplicates, the event model will keep the entire history of
// the description
CardDataHistory description = null;
for (CardDataFull des : descriptions) {
if (description == null) {
description = new CardDataHistory(des.getId(), des.getContent(), des.getOrder(), des.getUserId(),
des.getTime(), des.getUserId(), des.getTime());
}
if (des.getEventType() == EventType.DESCRIPTION_CREATE) {
description.setUserId(des.getUserId());
description.setTime(des.getTime());
}
if (des.getEventType() == EventType.DESCRIPTION_UPDATE) {
description.setUpdatedCount(description.getUpdatedCount() + 1);
// never null because the object's fields are always initialized
if (des.getTime().getTime() > description.getUpdateDate().getTime()) {
description.setUpdateUser(des.getUserId());
description.setUpdateDate(des.getTime());
}
}
}
return description;
}
public List<CardDataFull> findAllCommentsByCardId(int cardId) {
return cardDataRepository.findAllDataByCardIdAndType(cardId, CardType.COMMENT);
}
public List<CardData> findAllActionListsAndItemsByCardId(int cardId) {
return cardDataRepository.findAllDataLightByCardIdAndTypes(cardId,
of(CardType.ACTION_CHECKED, CardType.ACTION_UNCHECKED, CardType.ACTION_LIST));
}
@Transactional(readOnly = false)
private CardData createDescription(int cardId, String content, Date time, int userId) {
CardData description = cardDataRepository.createData(cardId, CardType.DESCRIPTION, content);
eventRepository.insertCardDataEvent(description.getId(), cardId, EventType.DESCRIPTION_CREATE, userId,
description.getId(), time);
return description;
}
@Transactional(readOnly = false)
public CardData createComment(int cardId, String content, Date time, int userId) {
CardData comment = cardDataRepository.createData(cardId, CardType.COMMENT, content);
eventRepository.insertCardDataEvent(comment.getId(), cardId, EventType.COMMENT_CREATE, userId,
comment.getId(), time);
return comment;
}
@Transactional(readOnly = false)
public CardData createActionList(int cardId, String name, int userId, Date time) {
CardData actionList = cardDataRepository.createData(cardId, CardType.ACTION_LIST, name);
eventRepository.insertCardDataEvent(actionList.getId(), cardId, EventType.ACTION_LIST_CREATE, userId,
actionList.getId(), time);
return actionList;
}
@Transactional(readOnly = false)
public CardData createActionItem(int cardId, int actionListId, String name, int userId, Date time) {
CardData actionItem = cardDataRepository.createDataWithReferenceOrder(cardId, actionListId,
CardType.ACTION_UNCHECKED, name);
eventRepository.insertCardDataEvent(actionItem.getId(), cardId, EventType.ACTION_ITEM_CREATE, userId,
actionItem.getReferenceId(), time);
return actionItem;
}
@Transactional(readOnly = false)
public ImmutablePair<Boolean, CardData> createFile(String name, String digest, long fileSize, int cardId,
InputStream content, String contentType, User user, Date time) {
if (!cardDataRepository.fileExists(digest)) {
cardDataRepository.addUploadContent(digest, fileSize, content, contentType);
}
if (!cardDataRepository.isFileAvailableByCard(digest, cardId)) {
CardData file = cardDataRepository.createData(cardId, CardType.FILE, digest);
cardDataRepository.createUploadInfo(digest, name, name, file.getId());
eventRepository.insertFileEvent(file.getId(), cardId, EventType.FILE_UPLOAD, user.getId(), file.getId(),
name, time);
return ImmutablePair.of(true, file);
}
return ImmutablePair.<Boolean, CardData>of(false, null);
}
/**
* Checked and filtered.
*/
@Transactional(readOnly = false)
public void moveActionItem(int cardId, int dataId, Integer newReferenceId, List<Integer> newDataOrder, User user,
Date time) {
CardData actionItem = cardDataRepository.getUndeletedDataLightById(dataId);
CardData newActionList = cardDataRepository.getDataLightById(newReferenceId);
// checks
Validate.isTrue(actionItem.getCardId() == cardId
&& (actionItem.getType() == CardType.ACTION_CHECKED || actionItem.getType() == CardType.ACTION_UNCHECKED));
Validate.isTrue(newActionList.getCardId() == cardId && newActionList.getType() == CardType.ACTION_LIST);
//
cardDataRepository.updateReferenceId(cardId, dataId, newReferenceId);
cardDataRepository.updateOrderByCardAndReferenceId(cardId, newReferenceId, newDataOrder);
eventRepository.insertCardDataEvent(dataId, cardId, EventType.ACTION_ITEM_MOVE, user.getId(),
actionItem.getReferenceId(), newReferenceId, time);
}
@Transactional(readOnly = false)
public int toggleActionItem(int actionitemId, boolean status, int userId, Date time) {
CardData actionItem = cardDataRepository.getUndeletedDataLightById(actionitemId);
eventRepository.insertCardDataEvent(actionitemId, actionItem.getCardId(), status ? EventType.ACTION_ITEM_CHECK
: EventType.ACTION_ITEM_UNCHECK, userId, actionItem.getReferenceId(), time);
return cardDataRepository.updateType(actionitemId, of(CardType.ACTION_CHECKED, CardType.ACTION_UNCHECKED),
status ? CardType.ACTION_CHECKED : CardType.ACTION_UNCHECKED);
}
@Transactional(readOnly = false)
public int updateActionItem(int actionItemId, String content) {
return cardDataRepository.updateContent(actionItemId, of(CardType.ACTION_CHECKED, CardType.ACTION_UNCHECKED),
content);
}
@Transactional(readOnly = false)
public int updateActionList(int actionListId, String content) {
return cardDataRepository.updateContent(actionListId, of(CardType.ACTION_LIST), content);
}
@Transactional(readOnly = false)
public int updateComment(int commentId, String content, Date time, User user) {
CardData comment = cardDataRepository.getUndeletedDataLightById(commentId);
// save old comment as history and update the reference id of the event
CardData historyComment = cardDataRepository.createDataWithReferenceOrder(comment.getCardId(), comment.getId(),
CardType.COMMENT_HISTORY, comment.getContent());
// insert new data
eventRepository.insertCardDataEvent(commentId, comment.getCardId(), EventType.COMMENT_UPDATE, user.getId(),
historyComment.getId(), time);
return cardDataRepository.updateContent(commentId, of(CardType.COMMENT), content);
}
@Transactional(readOnly = false)
public int updateDescription(int cardId, String content, Date time, int userId) {
// if no data is returned, then create the description
List<CardData> descriptions = cardDataRepository.findAllDataLightByCardIdAndType(cardId, CardType.DESCRIPTION);
if (descriptions.isEmpty()) {
return createDescription(cardId, content, time, userId).getId();
}
// there can (should) be only one
Validate.isTrue(1 == descriptions.size());
CardData description = descriptions.get(0);
// save old description as history and update the reference id of the
// event
CardData historyDescription = cardDataRepository.createDataWithReferenceOrder(description.getCardId(),
description.getId(), CardType.DESCRIPTION_HISTORY, description.getContent());
// insert new data
eventRepository.insertCardDataEvent(description.getId(), description.getCardId(), EventType.DESCRIPTION_UPDATE,
userId, historyDescription.getId(), time);
return cardDataRepository.updateContent(description.getId(), of(CardType.DESCRIPTION), content);
}
@Transactional(readOnly = false)
public Event deleteActionItem(int actionItemId, User user, Date time) {
CardData actionItem = cardDataRepository.getUndeletedDataLightById(actionItemId);
Event event = eventRepository.insertCardDataEvent(actionItemId, actionItem.getCardId(),
EventType.ACTION_ITEM_DELETE, user.getId(), actionItem.getReferenceId(), time);
cardDataRepository.softDelete(actionItemId, of(CardType.ACTION_CHECKED, CardType.ACTION_UNCHECKED));
return event;
}
@Transactional(readOnly = false)
public Event deleteActionList(int actionListId, User user, Date time) {
CardData actionList = cardDataRepository.getUndeletedDataLightById(actionListId);
Validate.isTrue(actionList.getType() == CardType.ACTION_LIST);
Event event = eventRepository.insertCardDataEvent(actionListId, actionList.getCardId(),
EventType.ACTION_LIST_DELETE, user.getId(), actionListId, time);
cardDataRepository.softDeleteOnCascade(actionListId, of(CardType.ACTION_LIST));
return event;
}
@Transactional(readOnly = false)
public Event deleteComment(int commentId, User user, Date time) {
CardData comment = cardDataRepository.getUndeletedDataLightById(commentId);
Event event = eventRepository.insertCardDataEvent(commentId, comment.getCardId(), EventType.COMMENT_DELETE,
user.getId(), commentId, time);
cardDataRepository.softDelete(commentId, of(CardType.COMMENT));
return event;
}
@Transactional(readOnly = false)
public Event deleteFile(int cardDataId, User user, Date time) {
FileDataLight file = cardDataRepository.getUndeletedFileByCardDataId(cardDataId);
Event event = eventRepository.insertFileEvent(file.getCardDataId(), file.getCardId(), EventType.FILE_DELETE,
user.getId(), file.getCardDataId(), file.getName(), time);
cardDataRepository.softDelete(file.getCardDataId(), of(CardType.FILE));
return event;
}
@Transactional(readOnly = false)
public void undoDeleteActionItem(Event event) {
eventRepository.remove(event.getId(), event.getCardId(), event.getEvent());
cardDataRepository.undoSoftDelete(event.getDataId(), of(CardType.ACTION_CHECKED, CardType.ACTION_UNCHECKED));
}
@Transactional(readOnly = false)
public void undoDeleteComment(Event event) {
eventRepository.remove(event.getId(), event.getCardId(), event.getEvent());
cardDataRepository.undoSoftDelete(event.getDataId(), of(CardType.COMMENT));
}
@Transactional(readOnly = false)
public void undoDeleteActionList(Event event) {
eventRepository.remove(event.getId(), event.getCardId(), event.getEvent());
cardDataRepository.undoSoftDeleteOnCascade(event.getDataId(), of(CardType.ACTION_LIST),
of(EventType.ACTION_ITEM_DELETE));
}
@Transactional(readOnly = false)
public void undoDeleteFile(Event event) {
eventRepository.remove(event.getId(), event.getCardId(), event.getEvent());
cardDataRepository.undoSoftDelete(event.getDataId(), of(CardType.FILE));
}
}