AnnouncementsController.java

package edu.ucsb.cs156.happiercows.controllers;

import com.fasterxml.jackson.core.JsonProcessingException;
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.HttpStatus;
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.Sort;
import org.springframework.format.annotation.DateTimeFormat;

import edu.ucsb.cs156.happiercows.entities.Announcements;
import edu.ucsb.cs156.happiercows.repositories.AnnouncementsRepository;

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

import org.springframework.security.core.Authentication;

import java.util.Optional;

import java.util.ArrayList;
import java.util.List;
import java.time.LocalDateTime;

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

    @Autowired
    UserCommonsRepository userCommonsRepository;

    @Autowired
    AnnouncementsRepository announcementsRepository;

    @Autowired
    ObjectMapper mapper;

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

        // Return the list of announcements
        Iterable<Announcements> messages = announcementsRepository.findAllByCommonsId(commonsId);
        String body = mapper.writeValueAsString(messages);
        return ResponseEntity.ok().body(body);
    }

    @Operation(summary = "Get announcement by id", description = "Get announcement associated with a specific id.")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @GetMapping("/get/by-id")
    public ResponseEntity<String> getAnnouncementsById(@Parameter(description = "The id of the announcement") @RequestParam Long announcementId) throws JsonProcessingException {

        // Try to get the announcement
        Optional<Announcements> announcementLookup = announcementsRepository.findById(announcementId);
        if (!announcementLookup.isPresent()) {
            String responseString = String.format("Announcement with id %d not found", announcementId);
            return ResponseEntity.badRequest().body(responseString);
        }

        // Return the announcement
        String body = mapper.writeValueAsString(announcementLookup);
        return ResponseEntity.ok().body(body);
    }  
    
    @Operation(summary = "Create an announcement", description = "Create an announcement associated with a specific commons")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @PostMapping("/post")
    public ResponseEntity<String> createAnnouncements(@Parameter(description = "The id of the common") @RequestParam Long commonsId,
                                            @Parameter(name="start", description="Start time (in iso format, e.g. YYYY-mm-ddTHH:MM:SS) ex: 2024-03-06T04:26:58.76") @RequestParam("start") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start,
                                            @Parameter(name="end", description="End time (in iso format)") @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end,
                                            @Parameter(name="announcement") @RequestParam String announcement) {
        
        User user = getCurrentUser().getUser();
        Long userId = user.getId();

        // Make sure the commons exists
        if (!commonsRepository.findById(commonsId).isPresent()) {
            String responseString = String.format("Commons with id %d not found", commonsId);
            return ResponseEntity.badRequest().body(responseString);
        }

        // Make sure the user is part of the commons or is an admin
        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.status(HttpStatus.FORBIDDEN).build();
            }
        }

        // Ensure start date is before end date
        if (end != null) {
            if (end.isBefore(start)) {
                return ResponseEntity.badRequest().body("End time must be after start time");
            }
        }

        // Check if announcements is empty
        if (announcement.isEmpty()) {
            return ResponseEntity.badRequest().body("Announcement cannot be empty");
        }

        // Create the announcement
        Announcements announcements = Announcements.builder()
        .commonsId(commonsId)
        .start(start)
        .end(end)
        .announcement(announcement)
        .build();

        // Save the announcement
        announcementsRepository.save(announcements);
        String responseString = String.format("Added announcement to commons with id %d", commonsId);

        return ResponseEntity.ok().body(responseString);
    }

    @Operation(summary = "Delete an announcement", description = "Delete an announcement associated with a specific commons")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @DeleteMapping("")
    public Object deleteAnnouncements(
        @Parameter(description = "The id of the announcement") @RequestParam Long announcementId) {

        // Try to get the announcement
        Optional<Announcements> announcementLookup = announcementsRepository.findById(announcementId);
        if (!announcementLookup.isPresent()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        Announcements announcement = announcementLookup.get();

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

        // Check if the user is an admin
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (!auth.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))){
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }

        // Delete the announcement
        announcementsRepository.delete(announcement);
        String responseString = String.format("announcement with id %d deleted", announcementId);
        return genericMessage(responseString);
    }

    @Operation(summary = "Update an announcement", description = "Update an announcement associated with a specific id")
    @PreAuthorize("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')")
    @PutMapping("")
    public ResponseEntity<String> updateAnnouncements(@Parameter(description = "The id of the announcement") @RequestParam Long announcementId,
                                            @Parameter(description = "The id of the common") @RequestParam Long commonsId,
                                            @Parameter(name="start", description="Start time (in iso format, e.g. YYYY-mm-ddTHH:MM:SS) ex: 2024-03-06T04:26:58.76") @RequestParam("start") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime start,
                                            @Parameter(name="end", description="End time (in iso format)") @RequestParam(name = "end", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime end,
                                            @Parameter(name="announcement") @RequestParam String announcement) {
        
        User user = getCurrentUser().getUser();
        Long userId = user.getId();

        // Try to get the announcement
        Optional<Announcements> exists = announcementsRepository.findById(announcementId);
        if (!exists.isPresent()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }

        Announcements announcements = exists.get();

        // Make sure the commons exists
        if (!commonsRepository.findById(commonsId).isPresent()) {
            String responseString = String.format("Commons with id %d not found", commonsId);
            return ResponseEntity.badRequest().body(responseString);
        }

        // Make sure the user is part of the commons or is an admin
        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.status(HttpStatus.FORBIDDEN).build();
            }
        }

        // Ensure start date is before end date
        if (end != null) {
            if (end.isBefore(start)) {
                return ResponseEntity.badRequest().body("End time must be after start time");
            }
        }

        // Check if announcements is empty
        if (announcement.isEmpty()) {
            return ResponseEntity.badRequest().body("Announcement cannot be empty");
        }

        // Update the announcement
        announcements.setCommonsId(commonsId);
        announcements.setStart(start);
        announcements.setEnd(end);
        announcements.setAnnouncement(announcement);

        // Save the announcement
        announcementsRepository.save(announcements);
        String responseString = String.format("Updated announcement with id %d", announcementId);
        return ResponseEntity.ok().body(responseString);
    }
}