/**
* 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.BoardColumn.BoardColumnLocation;
import io.lavagna.model.CardLabelValue.LabelValue;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Component
public class EventEmitter {
private final SimpMessageSendingOperations messagingTemplate;
private final ApiHooksService apiHookService;
public EventEmitter(SimpMessageSendingOperations messageSendingOperations, ApiHooksService apiHooksService) {
this.messagingTemplate = messageSendingOperations;
this.apiHookService = apiHooksService;
}
private static Event event(LavagnaEvent type) {
return new Event(type, null);
}
private static ImportEvent importEvent(int currentBoard, int boards, String boardName) {
return new ImportEvent(currentBoard, boards, boardName);
}
private static Event event(LavagnaEvent type, Object payload) {
return new Event(type, payload);
}
private static String columnDestination(String boardShortName, BoardColumnLocation location) {
return "/event/board/" + boardShortName + "/location/" + location + "/column";
}
private static String column(int columnId) {
return "/event/column/" + columnId + "/card";
}
private static String board(String projectShortName, String boardShortName) {
return "/event/" + projectShortName + "/" + boardShortName + "/card";
}
private static String cardData(int cardId) {
return "/event/card/" + cardId + "/card-data";
}
// ------------ project
public void emitCreateProject(String projectShortName, User user) {
messagingTemplate.convertAndSend("/event/project", event(LavagnaEvent.CREATE_PROJECT, projectShortName));
apiHookService.createdProject(projectShortName, user, LavagnaEvent.CREATE_PROJECT);
}
public void emitUpdateProject(String projectShortName, User user) {
messagingTemplate.convertAndSend("/event/project", event(LavagnaEvent.UPDATE_PROJECT, projectShortName));
apiHookService.updatedProject(projectShortName, user, LavagnaEvent.UPDATE_PROJECT);
}
public void emitImportProject(String importId, int currentBoard, int boards, String boardName, User user) {
messagingTemplate.convertAndSend("/event/import/" + importId, importEvent(currentBoard, boards, boardName));
}
public void emitUpdateColumnDefinition(String shortName, User user) {
emitUpdateProject(shortName, user);
emitProjectMetadataHasChanged(shortName);
}
// ------------ board
public void emitCreateBoard(String projectShortName, String boardShortName, User user) {
messagingTemplate.convertAndSend("/event/project/" + projectShortName + "/board",
event(LavagnaEvent.CREATE_BOARD));
apiHookService.createdBoard(boardShortName, user, LavagnaEvent.CREATE_BOARD);
}
public void emitUpdateBoard(String boardShortName, User user) {
messagingTemplate.convertAndSend("/event/board/" + boardShortName, event(LavagnaEvent.UPDATE_BOARD));
apiHookService.updatedBoard(boardShortName, user, LavagnaEvent.UPDATE_BOARD);
}
// ------------ column
public void emitCreateColumn(String boardShortName, BoardColumnLocation location, String columnName, User user) {
messagingTemplate
.convertAndSend(columnDestination(boardShortName, location), event(LavagnaEvent.CREATE_COLUMN));
apiHookService.createdColumn(boardShortName, columnName, user, LavagnaEvent.CREATE_COLUMN);
}
public void emitUpdateColumn(String boardShortName, BoardColumnLocation location, int columnId,
BoardColumn column, BoardColumn updatedColumn, User user) {
messagingTemplate.convertAndSend(columnDestination(boardShortName, location), event(LavagnaEvent.UPDATE_COLUMN));
messagingTemplate.convertAndSend("/event/column/" + columnId, event(LavagnaEvent.UPDATE_COLUMN));
apiHookService.updateColumn(boardShortName, column, updatedColumn, user, LavagnaEvent.UPDATE_COLUMN);
}
public void emitUpdateColumnPosition(String boardShortName, BoardColumnLocation location) {
messagingTemplate.convertAndSend(columnDestination(boardShortName, location),
event(LavagnaEvent.UPDATE_COLUMN_POSITION));
// updated position will not be exposed to the api hooks
}
// ------------ card
public void emitCreateCard(String projectShortName, String boardShortName, int columnId, Card card, User user) {
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.CREATE_CARD));
messagingTemplate.convertAndSend(board(projectShortName, boardShortName),
event(LavagnaEvent.CREATE_CARD, card.getId()));
apiHookService.createdCard(boardShortName, card, user, LavagnaEvent.CREATE_CARD);
}
public void emitUpdateCardName(String projectShortName, String boardShortName, int columnId, Card beforeUpdate, Card newCard, User user) {
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.UPDATE_CARD));
messagingTemplate.convertAndSend(board(projectShortName, boardShortName),
event(LavagnaEvent.UPDATE_CARD, beforeUpdate.getId()));
apiHookService.updatedCardName(boardShortName, beforeUpdate, newCard, user, LavagnaEvent.UPDATE_CARD);
}
public void emitUpdateCardPosition(int columnId) {
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.UPDATE_CARD_POSITION));
// updated position will not be exposed to the api hooks
}
public void emitMoveCardOutsideOfBoard(String boardShortName, BoardColumnLocation location) {
messagingTemplate.convertAndSend("/event/board/" + boardShortName + "/location/" + location + "/card",
event(LavagnaEvent.UPDATE_CARD_POSITION));
// apihook: this will be handled by emitCardHasMoved
}
public void emitMoveCardFromOutsideOfBoard(String boardShortName, BoardColumnLocation location) {
messagingTemplate.convertAndSend("/event/board/" + boardShortName + "/location/" + location + "/card",
event(LavagnaEvent.UPDATE_CARD_POSITION));
// apihook: this will be handled by emitCardHasMoved
}
public void emitCardHasMoved(String projectShortName, String boardShortName, Collection<Integer> affected, BoardColumn from, BoardColumn to, User user) {
for (Integer a : affected) {
messagingTemplate.convertAndSend(board(projectShortName, boardShortName),
event(LavagnaEvent.UPDATE_CARD_POSITION, a));
}
apiHookService.moveCards(from, to, affected, user, LavagnaEvent.UPDATE_CARD_POSITION);
}
public void emitCreateRole() {
messagingTemplate.convertAndSend("/event/permission", event(LavagnaEvent.CREATE_ROLE));
}
// ------------ permission
public void emitCreateRole(String projectShortName) {
messagingTemplate.convertAndSend("/event/permission/project/" + projectShortName,
event(LavagnaEvent.CREATE_ROLE));
}
public void emitDeleteRole() {
messagingTemplate.convertAndSend("/event/permission", event(LavagnaEvent.DELETE_ROLE));
}
public void emitDeleteRole(String projectShortName) {
messagingTemplate.convertAndSend("/event/permission/project/" + projectShortName,
event(LavagnaEvent.DELETE_ROLE));
}
public void emitUpdatePermissionsToRole() {
messagingTemplate.convertAndSend("/event/permission", event(LavagnaEvent.UPDATE_PERMISSION_TO_ROLE));
}
public void emitUpdatePermissionsToRole(String projectShortName) {
messagingTemplate.convertAndSend("/event/permission/project/" + projectShortName,
event(LavagnaEvent.UPDATE_PERMISSION_TO_ROLE));
}
public void emitAssignRoleToUsers(String role) {
messagingTemplate.convertAndSend("/event/permission", event(LavagnaEvent.ASSIGN_ROLE_TO_USERS, role));
}
public void emitAssignRoleToUsers(String role, String projectShortName) {
messagingTemplate.convertAndSend("/event/permission/project/" + projectShortName,
event(LavagnaEvent.ASSIGN_ROLE_TO_USERS, role));
}
public void emitRemoveRoleToUsers(String role) {
messagingTemplate.convertAndSend("/event/permission", event(LavagnaEvent.REMOVE_ROLE_TO_USERS, role));
}
public void emitRemoveRoleToUsers(String role, String projectShortName) {
messagingTemplate.convertAndSend("/event/permission/project/" + projectShortName,
event(LavagnaEvent.REMOVE_ROLE_TO_USERS, role));
}
// ------------ card description
public void emitUpdateDescription(int columnId, int cardId, CardDataHistory previousDescription, CardDataHistory newDescription, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.UPDATE_DESCRIPTION));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.UPDATE_DESCRIPTION));
apiHookService.updateCardDescription(cardId, previousDescription, newDescription, user, LavagnaEvent.UPDATE_DESCRIPTION);
}
// ------------ comment
public void emitCreateComment(int columnId, int cardId, CardData comment, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.CREATE_COMMENT));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.CREATE_COMMENT));
apiHookService.createdComment(cardId, comment, user, LavagnaEvent.CREATE_COMMENT);
}
public void emitUpdateComment(int cardId, CardData previousComment, String newComment, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.UPDATE_COMMENT));
apiHookService.updatedComment(cardId, previousComment, newComment, user, LavagnaEvent.UPDATE_COMMENT);
}
public void emitDeleteComment(int columnId, int cardId, CardData deletedComment, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.DELETE_COMMENT));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.DELETE_COMMENT));
apiHookService.deletedComment(cardId, deletedComment, user, LavagnaEvent.DELETE_COMMENT);
}
public void emitUndoDeleteComment(int columnId, int cardId, CardData undeletedComment, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.UNDO_DELETE_COMMENT));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.UNDO_DELETE_COMMENT));
apiHookService.undeletedComment(cardId, undeletedComment, user, LavagnaEvent.UNDO_DELETE_COMMENT);
}
// ------------ action list handling
public void emitCreateActionList(int cardId, String name, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.CREATE_ACTION_LIST));
apiHookService.createActionList(cardId, name, user, LavagnaEvent.CREATE_ACTION_LIST);
}
public void emitDeleteActionList(int columnId, int cardId, String name, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.DELETE_ACTION_LIST));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.DELETE_ACTION_LIST));
apiHookService.deleteActionList(cardId, name, user, LavagnaEvent.DELETE_ACTION_LIST);
}
public void emitUpdateActionList(int cardId, String oldName, String newName, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.UPDATE_ACTION_LIST));
apiHookService.updatedNameActionList(cardId, oldName, newName, user, LavagnaEvent.UPDATE_ACTION_LIST);
}
public void emitReorderActionLists(int cardId) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.REORDER_ACTION_LIST));
}
public void emitCreateActionItem(int columnId, int cardId, String actionItemListName, String actionItem, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.CREATE_ACTION_ITEM));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.REORDER_ACTION_LIST));
apiHookService.createActionItem(cardId, actionItemListName, actionItem, user, LavagnaEvent.CREATE_ACTION_ITEM);
}
public void emitDeleteActionItem(int columnId, int cardId, String actionItemListName, String actionItem, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.DELETE_ACTION_ITEM));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.DELETE_ACTION_ITEM));
apiHookService.deletedActionItem(cardId, actionItemListName, actionItem, user, LavagnaEvent.DELETE_ACTION_ITEM);
}
public void emitToggleActionItem(int columnId, int cardId, String actionItemListName, String actionItem, boolean toggle, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.TOGGLE_ACTION_ITEM));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.TOGGLE_ACTION_ITEM));
apiHookService.toggledActionItem(cardId, actionItemListName, actionItem, toggle, user, LavagnaEvent.TOGGLE_ACTION_ITEM);
}
public void emitUpdateUpdateActionItem(int cardId, String actionItemListName, String oldActionItem, String newActionItem, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.UPDATE_ACTION_ITEM));
apiHookService.updatedActionItem(cardId, actionItemListName, oldActionItem, newActionItem, user, LavagnaEvent.UPDATE_ACTION_ITEM);
}
public void emitMoveActionItem(int cardId, String fromActionItemListName, String toActionItemListName, String actionItem, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.MOVE_ACTION_ITEM));
apiHookService.movedActionItem(cardId, fromActionItemListName, toActionItemListName, actionItem, user, LavagnaEvent.MOVE_ACTION_ITEM);
}
public void emitReorderActionItems(int cardId) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.REORDER_ACTION_ITEM));
}
public void emiteUndoDeleteActionItem(int columnId, int cardId, String actionItemListName, String actionItem, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.UNDO_DELETE_ACTION_ITEM));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.UNDO_DELETE_ACTION_ITEM));
apiHookService.undoDeleteActionItem(cardId, actionItemListName, actionItem, user, LavagnaEvent.UNDO_DELETE_ACTION_ITEM);
}
public void emitUndoDeleteActionList(int columnId, int cardId, String name, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.UNDO_DELETE_ACTION_LIST));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.UNDO_DELETE_ACTION_LIST));
apiHookService.undeletedActionList(cardId, name, user, LavagnaEvent.UNDO_DELETE_ACTION_LIST);
}
// ------------
public void emitUploadFile(int columnId, int cardId, List<String> fileNames, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.CREATE_FILE));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.CREATE_FILE));
apiHookService.uploadedFile(cardId, fileNames, user, LavagnaEvent.CREATE_FILE);
}
public void emitDeleteFile(int columnId, int cardId, String fileName, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.DELETE_FILE));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.DELETE_FILE));
apiHookService.deletedFile(cardId, fileName, user, LavagnaEvent.DELETE_FILE);
}
public void emiteUndoDeleteFile(int columnId, int cardId, String fileName, User user) {
messagingTemplate.convertAndSend(cardData(cardId), event(LavagnaEvent.UNDO_DELETE_FILE));
messagingTemplate.convertAndSend(column(columnId), event(LavagnaEvent.UNDO_DELETE_FILE));
apiHookService.undoDeletedFile(cardId, fileName, user, LavagnaEvent.UNDO_DELETE_FILE);
}
// ------------
private static Triple<Set<Integer>, Set<Integer>, Set<String>> extractFrom(List<CardFull> l) {
Set<Integer> cardIds = new HashSet<>();
Set<Integer> columnIds = new HashSet<>();
Set<String> projectShortNames = new HashSet<>();
for (CardFull cf : l) {
cardIds.add(cf.getId());
columnIds.add(cf.getColumnId());
projectShortNames.add(cf.getProjectShortName());
}
return Triple.of(cardIds, columnIds, projectShortNames);
}
public void emitRemoveLabelValueToCards(List<CardFull> affectedCards, int labelId, LabelValue labelValue, User user) {
sendEventForLabel(affectedCards, LavagnaEvent.REMOVE_LABEL_VALUE);
apiHookService.removedLabelValueToCards(affectedCards, labelId, labelValue, user, LavagnaEvent.REMOVE_LABEL_VALUE);
}
private void sendEventForLabel(List<CardFull> affectedCards, LavagnaEvent ev) {
Triple<Set<Integer>, Set<Integer>, Set<String>> a = extractFrom(affectedCards);
for (int cardId : a.getLeft()) {
messagingTemplate.convertAndSend(cardData(cardId), event(ev));
}
for (int columnId : a.getMiddle()) {
messagingTemplate.convertAndSend(column(columnId), event(ev));
}
for (String projectShortName : a.getRight()) {
messagingTemplate.convertAndSend("/event/project/" + projectShortName + "/label-value", event(ev));
}
}
public void emitAddLabelValueToCards(List<CardFull> affectedCards, int labelId, LabelValue labelValue, User user) {
sendEventForLabel(affectedCards, LavagnaEvent.ADD_LABEL_VALUE_TO_CARD);
apiHookService.addLabelValueToCards(affectedCards, labelId, labelValue, user, LavagnaEvent.ADD_LABEL_VALUE_TO_CARD);
}
public void emitUpdateOrAddValueToCards(List<CardFull> updated, List<CardFull> added, int labelId, LabelValue labelValue, User user) {
sendEventForLabel(updated, LavagnaEvent.UPDATE_LABEL_VALUE);
sendEventForLabel(added, LavagnaEvent.ADD_LABEL_VALUE_TO_CARD);
apiHookService.updateLabelValueToCards(updated, labelId, labelValue, user, LavagnaEvent.UPDATE_LABEL_VALUE);
apiHookService.addLabelValueToCards(added, labelId, labelValue, user, LavagnaEvent.ADD_LABEL_VALUE_TO_CARD);
}
public void emitAddLabel(String projectShortName) {
messagingTemplate.convertAndSend("/event/project/" + projectShortName + "/label", event(LavagnaEvent.ADD_LABEL));
emitProjectMetadataHasChanged(projectShortName);
}
public void emitUpdateLabel(String projectShortName, int labelId) {
messagingTemplate.convertAndSend("/event/project/" + projectShortName + "/label", event(LavagnaEvent.UPDATE_LABEL, labelId));
emitProjectMetadataHasChanged(projectShortName);
}
public void emitDeleteLabel(String projectShortName, int labelId) {
messagingTemplate.convertAndSend("/event/project/" + projectShortName + "/label", event(LavagnaEvent.DELETE_LABEL, labelId));
emitProjectMetadataHasChanged(projectShortName);
}
public void emitUpdateLabeListValueId(int labelListValueId) {
messagingTemplate.convertAndSend("/event/label-list-values/" + labelListValueId, event(LavagnaEvent.UPDATE_LABEL_LIST_VALUE, labelListValueId));
}
// user profile update
public void emitUpdateUserProfile(int userId) {
messagingTemplate.convertAndSend("/event/user", event(LavagnaEvent.UPDATE_USER, userId));
}
private void emitProjectMetadataHasChanged(String projectShortName) {
messagingTemplate.convertAndSend("/event/project/" + projectShortName, LavagnaEvent.PROJECT_METADATA_HAS_CHANGED);
}
public enum LavagnaEvent {
CREATE_PROJECT, CREATE_BOARD, //
UPDATE_PROJECT, UPDATE_BOARD, //
CREATE_COLUMN, UPDATE_COLUMN, UPDATE_COLUMN_POSITION, //
CREATE_CARD, UPDATE_CARD, //
UPDATE_CARD_POSITION, //
//
PROJECT_METADATA_HAS_CHANGED,
//
// role and permission
CREATE_ROLE, DELETE_ROLE, UPDATE_PERMISSION_TO_ROLE, ASSIGN_ROLE_TO_USERS, REMOVE_ROLE_TO_USERS,
//
UPDATE_DESCRIPTION,
//
CREATE_COMMENT, UPDATE_COMMENT, DELETE_COMMENT, UNDO_DELETE_COMMENT,
//
CREATE_ACTION_LIST, DELETE_ACTION_LIST, UPDATE_ACTION_LIST, REORDER_ACTION_LIST, UNDO_DELETE_ACTION_LIST,
//
CREATE_ACTION_ITEM, DELETE_ACTION_ITEM, TOGGLE_ACTION_ITEM, UPDATE_ACTION_ITEM, MOVE_ACTION_ITEM, REORDER_ACTION_ITEM, UNDO_DELETE_ACTION_ITEM,
//
CREATE_FILE, DELETE_FILE, UNDO_DELETE_FILE,
//
ADD_LABEL_VALUE_TO_CARD, UPDATE_LABEL_VALUE, REMOVE_LABEL_VALUE, UNDO_REMOVE_LABEL_VALUE, ADD_LABEL, UPDATE_LABEL, DELETE_LABEL,
//
UPDATE_USER, UPDATE_LABEL_LIST_VALUE;
}
// ------------
public static class Event {
private final LavagnaEvent type;
private final Object payload;
@java.beans.ConstructorProperties({ "type", "payload" }) private Event(LavagnaEvent type,
Object payload) {
this.type = type;
this.payload = payload;
}
public LavagnaEvent getType() {
return this.type;
}
public Object getPayload() {
return this.payload;
}
}
public static class ImportEvent {
private final int currentBoard;
private final int boards;
private final String boardName;
@java.beans.ConstructorProperties({ "currentBoard", "boards", "boardName" }) private ImportEvent(
int currentBoard,
int boards, String boardName) {
this.currentBoard = currentBoard;
this.boards = boards;
this.boardName = boardName;
}
public int getCurrentBoard() {
return this.currentBoard;
}
public int getBoards() {
return this.boards;
}
public String getBoardName() {
return this.boardName;
}
}
}