AnnouncementsController.java

package edu.ucsb.cs156.happiercows.controllers;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;

import edu.ucsb.cs156.happiercows.entities.Announcement;
import edu.ucsb.cs156.happiercows.repositories.AnnouncementRepository;

import edu.ucsb.cs156.happiercows.entities.User;
import edu.ucsb.cs156.happiercows.entities.UserCommons;
import edu.ucsb.cs156.happiercows.repositories.UserCommonsRepository;

import org.springframework.security.core.Authentication;

import java.util.Date;
import java.util.Optional;

@Tag(name = "Announcements")
@RequestMapping("/api/announcements")
@RestController
@Slf4j
public class AnnouncementsController extends ApiController {

    @Autowired
    private AnnouncementRepository announcementRepository;

    @Autowired
    private UserCommonsRepository userCommonsRepository;

    @Autowired
    ObjectMapper mapper;

    @Operation(summary = "Create an announcement", description = "Create an announcement associated with a specific commons")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @PostMapping("/post")
    public ResponseEntity<Object> createAnnouncement(
        @Parameter(description = "The id of the common") @RequestParam Long commonsId,
        @Parameter(description = "The datetime at which the announcement will be shown (defaults to current time)") @RequestParam(required = false) Date startDate,
        @Parameter(description = "The datetime at which the announcement will stop being shown (optional)") @RequestParam(required = false) Date endDate,
        @Parameter(description = "The announcement to be sent out") @RequestParam String announcementText) {

        User user = getCurrentUser().getUser();
        Long userId = user.getId();

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (!auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
            log.info("User is not an admin");

            Optional<UserCommons> userCommonsLookup =
                userCommonsRepository.findByCommonsIdAndUserId(commonsId, userId);

            if (!userCommonsLookup.isPresent()) {
                return ResponseEntity.badRequest().body("Commons_id must exist.");
            }
        }

        if (startDate == null) {
            log.info("Start date not specified. Defaulting to current date.");
            startDate = new Date();
        }

        if (announcementText == "") {
            return ResponseEntity.badRequest().body("Announcement cannot be empty.");
        }

        if (endDate != null && startDate.after(endDate)) {
            return ResponseEntity.badRequest().body("Start date must be before end date.");
        }

        Announcement announcementObj = Announcement.builder()
            .commonsId(commonsId)
            .startDate(startDate)
            .endDate(endDate)
            .announcementText(announcementText)
            .build();

        announcementRepository.save(announcementObj);

        return ResponseEntity.ok(announcementObj);
    }

    @Operation(summary = "Get all announcements", description = "Get all announcements associated with a specific commons.")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @GetMapping("/getbycommonsid")
    public ResponseEntity<Object> getAnnouncements(
        @Parameter(description = "The id of the common") @RequestParam Long commonsId) {

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (!auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
            log.info("User is not an admin");

            User user = getCurrentUser().getUser();
            Long userId = user.getId();

            Optional<UserCommons> userCommonsLookup =
                userCommonsRepository.findByCommonsIdAndUserId(commonsId, userId);

            if (!userCommonsLookup.isPresent()) {
                return ResponseEntity.badRequest().body("Commons_id must exist.");
            }
        }

        int MAX_ANNOUNCEMENTS = 1000;

        Page<Announcement> announcements =
            announcementRepository.findByCommonsId(
                commonsId,
                PageRequest.of(
                    0,
                    MAX_ANNOUNCEMENTS,
                    Sort.by("startDate").descending()));

        return ResponseEntity.ok(announcements);
    }

    @Operation(summary = "Get current announcements", description = "Get current active announcements associated with a specific commons.")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @GetMapping("/current")
    public ResponseEntity<Object> getCurrentAnnouncements(
        @Parameter(description = "The id of the common") @RequestParam Long commonsId) {

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (!auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
            log.info("User is not an admin");

            User user = getCurrentUser().getUser();
            Long userId = user.getId();

            Optional<UserCommons> userCommonsLookup =
                userCommonsRepository.findByCommonsIdAndUserId(commonsId, userId);

            if (!userCommonsLookup.isPresent()) {
                return ResponseEntity.badRequest().body("Commons_id must exist.");
            }
        }

        int MAX_ANNOUNCEMENTS = 1000;

        Page<Announcement> announcements =
            announcementRepository.findCurrentAnnouncementsByCommonsId(
                commonsId,
                PageRequest.of(
                    0,
                    MAX_ANNOUNCEMENTS,
                    Sort.by("startDate").descending()));

        return ResponseEntity.ok(announcements);
    }

    @Operation(summary = "Get announcements by id", description = "Get announcement by its id.")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @GetMapping("/getbyid")
    public ResponseEntity<Object> getAnnouncementById(
        @Parameter(description = "The id of the announcement") @RequestParam Long id) {

        Optional<Announcement> announcementLookup =
            announcementRepository.findByAnnouncementId(id);

        if (!announcementLookup.isPresent()) {
            return ResponseEntity.badRequest().body("Cannot find announcement. id is invalid.");
        }

        return ResponseEntity.ok(announcementLookup.get());
    }

    @Operation(summary = "Edit an announcement", description = "Edit an announcement by its id.")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @PutMapping("/put")
    public ResponseEntity<Object> editAnnouncement(
        @Parameter(description = "The id of the announcement") @RequestParam Long id,
        @Parameter(description = "The id of the common") @RequestParam Long commonsId,
        @Parameter(description = "The datetime at which the announcement will be shown (defaults to current time)") @RequestParam(required = false) Date startDate,
        @Parameter(description = "The datetime at which the announcement will stop being shown (optional)") @RequestParam(required = false) Date endDate,
        @Parameter(description = "The announcement to be sent out") @RequestParam String announcementText) {

        User user = getCurrentUser().getUser();
        Long userId = user.getId();

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (!auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
            log.info("User is not an admin");

            Optional<UserCommons> userCommonsLookup =
                userCommonsRepository.findByCommonsIdAndUserId(commonsId, userId);

            if (!userCommonsLookup.isPresent()) {
                return ResponseEntity.badRequest().body("Commons_id must exist.");
            }
        }

        if (announcementText == "") {
            return ResponseEntity.badRequest().body("Announcement cannot be empty.");
        }

        if (startDate == null) {
            log.info("Start date not specified. Defaulting to current date.");
            startDate = new Date();
        }

        if (endDate != null && startDate.after(endDate)) {
            return ResponseEntity.badRequest().body("Start date must be before end date.");
        }

        Optional<Announcement> announcementLookup =
            announcementRepository.findByAnnouncementId(id);

        if (!announcementLookup.isPresent()) {
            return ResponseEntity.badRequest().body("Announcement could not be found. Invalid id.");
        }

        Announcement announcementObj = announcementLookup.get();

        announcementObj.setStartDate(startDate);
        announcementObj.setEndDate(endDate);
        announcementObj.setAnnouncementText(announcementText);

        announcementRepository.save(announcementObj);

        return ResponseEntity.ok(announcementObj);
    }

    @Operation(summary = "Delete an announcement", description = "Delete an announcement associated with an id")
    @PreAuthorize("hasAnyRole('ROLE_ADMIN')")
    @DeleteMapping("/delete")
    public ResponseEntity<Object> deleteAnnouncement(
        @Parameter(description = "The id of the chat message") @RequestParam Long id) {

        Optional<Announcement> announcementLookup =
            announcementRepository.findByAnnouncementId(id);

        if (!announcementLookup.isPresent()) {
            return ResponseEntity.badRequest().body("Cannot find announcement. id is invalid.");
        }

        Announcement announcementObj = announcementLookup.get();

        announcementRepository.delete(announcementObj);

        return ResponseEntity.ok(announcementObj);
    }
}