CardRestEndpoint.java

/*
 * The coLAB project
 * Copyright (C) 2021-2023 AlbaSim, MEI, HEIG-VD, HES-SO
 *
 * Licensed under the MIT License
 */
package ch.colabproject.colab.api.rest.card;

import ch.colabproject.colab.api.controller.card.CardManager;
import ch.colabproject.colab.api.controller.card.grid.GridPosition;
import ch.colabproject.colab.api.exceptions.ColabMergeException;
import ch.colabproject.colab.api.model.card.Card;
import ch.colabproject.colab.api.model.card.CardContent;
import ch.colabproject.colab.api.model.link.ActivityFlowLink;
import ch.colabproject.colab.api.model.link.StickyNoteLink;
import ch.colabproject.colab.api.model.team.acl.Assignment;
import ch.colabproject.colab.api.persistence.jpa.card.CardDao;
import ch.colabproject.colab.generator.model.annotations.AuthenticationRequired;
import ch.colabproject.colab.generator.model.exceptions.HttpErrorMessage;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * REST card controller
 *
 * @author sandra
 */
@Path("cards")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@AuthenticationRequired
public class CardRestEndpoint {

    /** logger */
    private static final Logger logger = LoggerFactory.getLogger(CardRestEndpoint.class);

    /**
     * The card persistence manager
     */
    @Inject
    private CardDao cardDao;

    /**
     * The card-related logic
     */
    @Inject
    private CardManager cardManager;

    /**
     * Get card identified by the given id
     *
     * @param id id of the card to fetch
     *
     * @return the card or null
     */
    @GET
    @Path("{id: [0-9]+}")
    public Card getCard(@PathParam("id") Long id) {
        logger.debug("get card #{}", id);
        return cardDao.findCard(id);
    }

    /**
     * Create and persist a new card
     *
     * @param parentId id of the new card's parent
     *
     * @return the persisted new card
     */
    @POST
    @Path("create/{parentId: [0-9]+}")
    public Card createNewCardWithoutType(@PathParam("parentId") Long parentId) {
        logger.debug("create a new card for the parent #{} without type", parentId);
        return cardManager.createNewCard(parentId, null);
    }

    /**
     * Create and persist a new card
     *
     * @param parentId   id of the new card's parent
     * @param cardTypeId id of the card type of the new card
     *
     * @return the persisted new card
     */
    @POST
    @Path("create/{parentId: [0-9]+}/{cardTypeId: [0-9]+}")
    public Card createNewCard(@PathParam("parentId") Long parentId,
            @PathParam("cardTypeId") Long cardTypeId) {
        logger.debug("create a new card for the parent #{} and the type #{}", parentId,
                cardTypeId);
        return cardManager.createNewCard(parentId, cardTypeId);
    }

    /**
     * Save changes to database. Only fields which are editable by users will be
     * impacted.
     *
     * @param card card to update
     *
     * @throws ColabMergeException if the merge is not possible
     */
    @PUT
    public void updateCard(Card card) throws ColabMergeException {
        logger.debug("update card {}", card);
        cardDao.updateCard(card);
    }

    /**
     * Change the position of the card (stay in the same parent)
     * <p>
     * Recompute the position of all the sister cards
     *
     * @param cardId   the id of the card
     * @param position the new position of the card
     */
    @PUT
    @Path("{cardId: [0-9]+}/changePosition")
    public void changeCardPosition(@PathParam("cardId") Long cardId, GridPosition position) {
        cardManager.changeCardPosition(cardId, position);
    }

    /**
     * Move a card to a new parent
     *
     * @param cardId      the id of the card to move
     * @param newParentId the id of the new parent
     *
     * @throws HttpErrorMessage if card or parent does not exist or if parent is a
     *                          child of the card
     */
    @PUT
    @Path("{cardId: [0-9]+}/MoveTo/{newParentId: [0-9]+}")
    public void moveCard(
            @PathParam("cardId") Long cardId,
            @PathParam("newParentId") Long newParentId) {
        cardManager.moveCard(cardId, newParentId);
    }

    /**
     * Move a card into the parent of its parent
     *
     * @param cardId the id of the card
     *
     * @throws HttpErrorMessage if card or parent of parent does not exist
     */
    @PUT
    @Path("{cardId: [0-9]+}/MoveAbove")
    public void moveCardAbove(@PathParam("cardId") Long cardId) {
        cardManager.moveCardAbove(cardId);
    }

    /**
     * Put the given card in the bin. (= set DeletionStatus to BIN + set erasure
     * tracking data)
     *
     * @param cardId the id of the card
     *
     * @throws HttpErrorMessage if card does not exist
     */
    @PUT
    @Path("{cardId: [0-9]+}/PutInBin")
    public void putCardInBin(@PathParam("cardId") Long cardId) {
        logger.debug("put in bin card #{}", cardId);
        cardManager.putCardInBin(cardId);
    }

    /**
     * Restore from the bin. The object won't contain any deletion or erasure data
     * anymore.
     * <p>
     * It means that the object is back at its place (as much as possible).
     * <p>
     * If the parent card is deleted, the card is moved at the root of the project.
     *
     * @param cardId the id of the card
     *
     * @throws HttpErrorMessage if card does not exist
     */
    @PUT
    @Path("{cardId: [0-9]+}/RestoreFromBin")
    public void restoreCardFromBin(@PathParam("cardId") Long cardId) {
        logger.debug("restore from bin card #{}", cardId);
        cardManager.restoreCardFromBin(cardId);
    }

    /**
     * Set the deletion status to TO_DELETE.
     * <p>
     * It means that the object is only visible in the bin panel.
     *
     * @param cardId the id of the card
     *
     * @throws HttpErrorMessage if card does not exist
     */
    @PUT
    @Path("{cardId: [0-9]+}/MarkAsToDeleteForever")
    public void markCardAsToDeleteForever(@PathParam("cardId") Long cardId) {
        logger.debug("mark card #{} as to delete forever", cardId);
        cardManager.markCardAsToDeleteForever(cardId);
    }

    /**
     * Get all card content variants of a card
     *
     * @param cardId Card id of the searched content variants
     *
     * @return list of card contents
     */
    @GET
    @Path("{id: [0-9]+}/CardContents")
    public List<CardContent> getContentVariantsOfCard(@PathParam("id") Long cardId) {
        logger.debug("Get card #{} content variants", cardId);
        return cardManager.getContentVariants(cardId);
    }

    /**
     * Get all sticky note links of which the card is the destination
     *
     * @param cardId Card id of the searched links
     *
     * @return list of links
     */
    @GET
    @Path("{id: [0-9]+}/StickyNoteLinksDestinationCard")
    public List<StickyNoteLink> getStickyNoteLinksAsDest(@PathParam("id") Long cardId) {
        logger.debug("Get sticky note links to card #{} as destination", cardId);
        return cardManager.getStickyNoteLinkAsDest(cardId);
    }

    /**
     * Get all sticky note links of which the card is the source
     *
     * @param cardId Card id of the searched links
     *
     * @return list of links
     */
    @GET
    @Path("{id: [0-9]+}/StickyNoteLinksSrcCard")
    public List<StickyNoteLink> getStickyNoteLinksAsSrc(@PathParam("id") Long cardId) {
        logger.debug("Get sticky note links to card #{} as source", cardId);
        return cardManager.getStickyNoteLinkAsSrcCard(cardId);
    }

    /**
     * Get all activity flow links of which the card is the previous one
     *
     * @param cardId Card id of the searched links
     *
     * @return list of links
     */
    @GET
    @Path("{id: [0-9]+}/ActivityFlowLinksPreviousCard")
    public List<ActivityFlowLink> getActivityFlowLinksAsPrevious(@PathParam("id") Long cardId) {
        logger.debug("Get activity flow links to card #{} as previous", cardId);
        return cardManager.getActivityFlowLinkAsPrevious(cardId);
    }

    /**
     * Get all activity flow links of which the card is the next one
     *
     * @param cardId Card id of the searched links
     *
     * @return list of links
     */
    @GET
    @Path("{id: [0-9]+}/ActivityFlowLinksNextCard")
    public List<ActivityFlowLink> getActivityFlowLinksAsNext(@PathParam("id") Long cardId) {
        logger.debug("Get activity flow links to card #{} as next", cardId);
        return cardManager.getActivityFlowLinkAsNext(cardId);
    }

    /**
     * Retrieve the list of assignments for the given card
     *
     * @param cardId id of the card
     *
     * @return list of assignments
     */
    @GET
    @Path("{cardId: [0-9]+}/assignments")
    public List<Assignment> getAssignments(@PathParam("cardId") Long cardId) {
        logger.debug("Get Card #{} assignments list", cardId);
        return cardManager.getAssignments(cardId);
    }

    /**
     * @param cardId the card id
     */
    @POST
    @Path("createCardType/{cardId: [0-9]+}")
    public void createCardType(@PathParam("cardId") Long cardId) {

        cardManager.createCardType(cardId);
    }

    /**
     * Remove the card type of the card. For now, it can be done only if there is no
     * resource in the
     * card type.
     *
     * @param cardId the card id
     */
    @POST
    @Path("removeCardType/{cardId: [0-9]+}")
    public void removeCardType(@PathParam("cardId") Long cardId) {

        cardManager.removeCardType(cardId);
    }
}