Skip to content

Commit

Permalink
Merge pull request #72 from wethmiranasinghe/main
Browse files Browse the repository at this point in the history
Adding cover images to News and Events
  • Loading branch information
wethmiranasinghe authored Sep 6, 2024
2 parents d1bdc75 + 038236c commit 87440f7
Show file tree
Hide file tree
Showing 10 changed files with 473 additions and 327 deletions.
13 changes: 11 additions & 2 deletions back-end/src/main/java/com/example/demo/news/NewsAndEvents.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDate;

/**
* Represents a news or event entity stored in the database.
Expand All @@ -28,12 +29,20 @@ public class NewsAndEvents {
generator = "news_and_events_sequence"
)
private Long newsID;

private String newsTitle;

@Column(columnDefinition = "TEXT")
private String newsDescription;

private String newsUrl;
private String newsCoverImage;

@Lob
@Column(name = "newsCoverImage", columnDefinition = "BLOB")
private byte[] newsCoverImage;

private String newsDate;

private String newsAuthor;

/**
Expand All @@ -44,7 +53,7 @@ public NewsAndEvents(String newsTitle,
String newsUrl,
String newsAuthor,
String newsDate,
String newsCoverImage){
byte[] newsCoverImage) {
this.newsTitle = newsTitle;
this.newsDescription = newsDescription;
this.newsUrl = newsUrl;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.example.demo.news;

import com.example.demo.registration.RegistrationRequest;
import lombok.AllArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Optional;
Expand All @@ -17,25 +18,32 @@ public class NewsAndEventsController {

private final NewsAndEventsService newsAndEventsService;

/**
* Endpoint to fetch all news and events.
*/
@GetMapping
public List<NewsAndEvents> getAllNewsAndEvents() {
public List<NewsAndEventsResponse> getAllNewsAndEvents() {
return newsAndEventsService.getAllNewsAndEvents();
}

@GetMapping("/{newsId}")
public Optional<NewsAndEvents> getNewsById(@PathVariable Long newsId) {
return newsAndEventsService.getNewsAndEventById(newsId);
public NewsAndEventsResponse getNewsById(@PathVariable Long newsId) {
return newsAndEventsService.getNewsAndEventById(newsId)
.map(NewsAndEventsResponse::new)
.orElseThrow(() -> new RuntimeException("News not found"));
}

/**
* Endpoint to add a new news or event.
* Endpoint to add a new news or event with image upload.
*/
@PostMapping
public NewsAndEvents addNewsAndEvents(@RequestBody NewsAndEventsRequest newsAndEventsRequest) {
return newsAndEventsService.addNewsAndEvent(newsAndEventsRequest);
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public NewsAndEvents addNewsAndEvents(
@RequestParam("newsTitle") String newsTitle,
@RequestParam("newsDescription") String newsDescription,
@RequestParam("newsUrl") String newsUrl,
@RequestParam("newsAuthor") String newsAuthor,
@RequestParam("newsDate") String newsDate,
@RequestParam("newsCoverImage") MultipartFile newsCoverImage) {

// Call the service to add the news with the uploaded image
return newsAndEventsService.addNewsAndEvent(newsTitle, newsDescription, newsUrl, newsAuthor, newsDate, newsCoverImage);
}

/**
Expand All @@ -45,5 +53,4 @@ public NewsAndEvents addNewsAndEvents(@RequestBody NewsAndEventsRequest newsAndE
public void deleteNewsById(@PathVariable Long newsId) {
newsAndEventsService.deleteNewsAndEvent(newsId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import lombok.Getter;
import lombok.ToString;

import java.time.LocalDate;

/**
* Represents a request object for creating or updating a NewsAndEvents entity.
*/
Expand All @@ -20,5 +22,5 @@ public class NewsAndEventsRequest {
private final String newsUrl;
private final String newsAuthor;
private final String newsDate;
private final String newsCoverImage;
private final byte[] newsCoverImage;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.demo.news;

import lombok.Getter;
import lombok.Setter;

import java.util.Base64;

@Getter
@Setter
public class NewsAndEventsResponse {

private Long newsID;
private String newsTitle;
private String newsDescription;
private String newsUrl;
private String newsAuthor;
private String newsDate;
private String newsCoverImage; // Base64 encoded image

public NewsAndEventsResponse(NewsAndEvents newsAndEvents) {
this.newsID = newsAndEvents.getNewsID();
this.newsTitle = newsAndEvents.getNewsTitle();
this.newsDescription = newsAndEvents.getNewsDescription();
this.newsUrl = newsAndEvents.getNewsUrl();
this.newsAuthor = newsAndEvents.getNewsAuthor();
this.newsDate = newsAndEvents.getNewsDate().toString();

// Convert image byte array to Base64 encoded string
this.newsCoverImage = Base64.getEncoder().encodeToString(newsAndEvents.getNewsCoverImage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,90 @@
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class NewsAndEventsService {

private final NewsAndEventsRepository newsAndEventsRepository;

public List<NewsAndEvents> getAllNewsAndEvents() {
return newsAndEventsRepository.findAllOrderByNewsIDDesc();
}


/**
* Fetch all news and events and map them to NewsAndEventsResponse objects.
*/
public List<NewsAndEventsResponse> getAllNewsAndEvents() {
return newsAndEventsRepository.findAllOrderByNewsIDDesc()
.stream()
.map(NewsAndEventsResponse::new) // Map each entity to its response DTO
.collect(Collectors.toList());
}

/**
* Fetch a specific news or event by its ID.
*/
public Optional<NewsAndEvents> getNewsAndEventById(Long newsID) {
return newsAndEventsRepository.findById(newsID);
}

/**
* Add a new news or event with an optional image upload.
* Handles both text data and the image file.
*
* @param newsTitle The title of the news/event.
* @param newsDescription The description of the news/event.
* @param newsUrl An optional URL for the news/event.
* @param newsAuthor The author of the news/event.
* @param newsDate The date of the news/event.
* @param newsCoverImage An optional image file to be uploaded.
* @return The saved NewsAndEvents entity.
*/
@Transactional
public NewsAndEvents addNewsAndEvent(NewsAndEventsRequest newsAndEventsRequest) {
public NewsAndEvents addNewsAndEvent(String newsTitle, String newsDescription, String newsUrl,
String newsAuthor, String newsDate, MultipartFile newsCoverImage) {

byte[] imageBytes = null;
if (newsCoverImage != null && !newsCoverImage.isEmpty()) {
try {
imageBytes = newsCoverImage.getBytes(); // Get the image bytes from MultipartFile
} catch (IOException e) {
throw new RuntimeException("Failed to process image file", e); // Better error handling
}
}

NewsAndEvents newsAndEvents = new NewsAndEvents(
newsAndEventsRequest.getNewsTitle(),
newsAndEventsRequest.getNewsDescription(),
newsAndEventsRequest.getNewsUrl(),
newsAndEventsRequest.getNewsAuthor(),
newsAndEventsRequest.getNewsDate(),
newsAndEventsRequest.getNewsCoverImage()
newsTitle,
newsDescription,
newsUrl,
newsAuthor,
newsDate,
imageBytes // Store the image bytes
);
System.out.println("News Added");
return newsAndEventsRepository.save(newsAndEvents);

System.out.println("News Added: " + newsTitle); // Optional: log the added news

return newsAndEventsRepository.save(newsAndEvents); // Save the entity to the database
}

/**
* Delete a news or event by its ID.
*
* @param newsID The ID of the news/event to be deleted.
*/
@Transactional
public void deleteNewsAndEvent(Long newsID) {
newsAndEventsRepository.deleteById(newsID);
if (!newsAndEventsRepository.existsById(newsID)) {
throw new IllegalArgumentException("News with ID " + newsID + " does not exist."); // Handle non-existing entries
}
newsAndEventsRepository.deleteById(newsID); // Perform the delete operation
}

// public NewsAndEvents updateNewsAndEvent(Long newsID, NewsAndEvents updatednewsAndEvent) {
// return NewsAndEventsRepository.findByID(newsID)
// .map(newsAndEvents -> {
// newsAndEvents.setNewsAuthor(updatednewsAndEvent.getNewsAuthor());
// return NewsAndEventsRepository.save(newsAndEvents);
// })
// .orElseGet(() -> {
// updatednewsAndEvent.setNewsAuthor(updatednewsAndEvent.getNewsAuthor());
// });
// }
// Uncomment and modify this method if you need to implement update functionality
// public NewsAndEvents updateNewsAndEvent(Long newsID, NewsAndEventsRequest updatedRequest) {
// // Fetch existing news by ID, update fields, and save.
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import com.example.demo.news.NewsAndEvents;
import com.example.demo.news.NewsAndEventsController;
import com.example.demo.news.NewsAndEventsRequest;
import com.example.demo.news.NewsAndEventsService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

Expand Down Expand Up @@ -43,7 +43,6 @@ void setUp() {
responds with an HTTP 200 status and returns an empty JSON array when
there are no news or events available from the service layer
*/

@Test
void testGetAllNewsAndEvents() throws Exception {
when(newsAndEventsService.getAllNewsAndEvents()).thenReturn(Collections.emptyList());
Expand All @@ -59,11 +58,10 @@ void testGetAllNewsAndEvents() throws Exception {
Test to verify that when a GET request is made to /api/v1/news/{newsId},
the endpoint correctly retrieves and returns the news item with the specified newsId
*/

@Test
void testGetNewsById() throws Exception {
Long newsId = 1L;
NewsAndEvents newsAndEvents = new NewsAndEvents("Title", "Description", "Url", "Author", "Date", "Image");
NewsAndEvents newsAndEvents = new NewsAndEvents("Title", "Description", "Url", "Author", "Date", null);
when(newsAndEventsService.getNewsAndEventById(newsId)).thenReturn(Optional.of(newsAndEvents));

mockMvc.perform(get("/api/v1/news/{newsId}", newsId))
Expand All @@ -75,33 +73,51 @@ void testGetNewsById() throws Exception {

/*
Test to ensure that when a POST request is made to /api/v1/news with a
JSON payload representing a new news item, the endpoint correctly processes
the request, adds the item using the service layer, and returns a response
indicating success with the added news item's details
MultipartFile and other parameters representing a new news item, the endpoint
correctly processes the request, adds the item using the service layer, and returns
a response indicating success with the added news item's details.
*/

@Test
void testAddNewsAndEvents() throws Exception {
NewsAndEventsRequest request = new NewsAndEventsRequest("Title", "Description", "Url", "Author", "Date", "Image");
NewsAndEvents newsAndEvents = new NewsAndEvents(request.getNewsTitle(), request.getNewsDescription(), request.getNewsUrl(), request.getNewsAuthor(), request.getNewsDate(), request.getNewsCoverImage());

when(newsAndEventsService.addNewsAndEvent(any(NewsAndEventsRequest.class))).thenReturn(newsAndEvents);

mockMvc.perform(post("/api/v1/news")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"newsTitle\":\"Title\",\"newsDescription\":\"Description\",\"newsUrl\":\"Url\",\"newsAuthor\":\"Author\",\"newsDate\":\"Date\",\"newsCoverImage\":\"Image\"}"))
MockMultipartFile imageFile = new MockMultipartFile("newsCoverImage", "image.jpg", MediaType.IMAGE_JPEG_VALUE, "test image".getBytes());

NewsAndEvents newsAndEvents = new NewsAndEvents("Title", "Description", "Url", "Author", "Date", imageFile.getBytes());

when(newsAndEventsService.addNewsAndEvent(
eq("Title"),
eq("Description"),
eq("Url"),
eq("Author"),
eq("Date"),
any(MockMultipartFile.class)
)).thenReturn(newsAndEvents);

mockMvc.perform(multipart("/api/v1/news")
.file(imageFile)
.param("newsTitle", "Title")
.param("newsDescription", "Description")
.param("newsUrl", "Url")
.param("newsAuthor", "Author")
.param("newsDate", "Date")
.contentType(MediaType.MULTIPART_FORM_DATA))
.andExpect(status().isOk())
.andExpect(jsonPath("$.newsTitle").value("Title"));

verify(newsAndEventsService, times(1)).addNewsAndEvent(any(NewsAndEventsRequest.class));
verify(newsAndEventsService, times(1)).addNewsAndEvent(
eq("Title"),
eq("Description"),
eq("Url"),
eq("Author"),
eq("Date"),
any(MockMultipartFile.class)
);
}

/*
Test to ensure that when a DELETE request is made to /api/v1/news/{newsId},
the endpoint correctly handles the request to delete the news or events item with
the specified newsId.
*/

@Test
void testDeleteNewsById() throws Exception {
Long newsId = 1L;
Expand Down
Loading

0 comments on commit 87440f7

Please sign in to comment.