diff --git a/src/main/java/org/wise/portal/dao/newsitem/NewsItemDao.java b/src/main/java/org/wise/portal/dao/newsitem/NewsItemDao.java index 5691a6e7ee..b3d71a4367 100644 --- a/src/main/java/org/wise/portal/dao/newsitem/NewsItemDao.java +++ b/src/main/java/org/wise/portal/dao/newsitem/NewsItemDao.java @@ -27,6 +27,7 @@ import org.wise.portal.dao.SimpleDao; import org.wise.portal.domain.newsitem.NewsItem; +import org.wise.portal.domain.newsitem.NewsType; /** * @author Patrick Lawler @@ -34,6 +35,9 @@ */ public interface NewsItemDao extends SimpleDao { - List getListByType(String type); + List getNewsPageNews(NewsType type); + List getHomePageNews(NewsType type); + + List getAllNews(); } diff --git a/src/main/java/org/wise/portal/dao/newsitem/impl/HibernateNewsItemDao.java b/src/main/java/org/wise/portal/dao/newsitem/impl/HibernateNewsItemDao.java index 464a31da18..d8da555785 100644 --- a/src/main/java/org/wise/portal/dao/newsitem/impl/HibernateNewsItemDao.java +++ b/src/main/java/org/wise/portal/dao/newsitem/impl/HibernateNewsItemDao.java @@ -34,6 +34,7 @@ import org.wise.portal.dao.impl.AbstractHibernateDao; import org.wise.portal.dao.newsitem.NewsItemDao; import org.wise.portal.domain.newsitem.NewsItem; +import org.wise.portal.domain.newsitem.NewsType; import org.wise.portal.domain.newsitem.impl.NewsItemImpl; /** @@ -49,14 +50,36 @@ protected Class getDataObjectClass() { } @Override + public List getNewsPageNews(NewsType type) { + return getLatestNews(false, type); + } + + @Override + public List getHomePageNews(NewsType type) { + return getLatestNews(true, type); + } + + @Override + public List getAllNews() { + return getLatestNews(false, NewsType.PUBLIC_AND_TEACHER); + } + @SuppressWarnings("unchecked") - public List getListByType(String type) { + private List getLatestNews(boolean limitQuery, NewsType type) { CriteriaBuilder cb = getCriteriaBuilder(); CriteriaQuery cq = cb.createQuery(NewsItemImpl.class); Root newsItemRoot = cq.from(NewsItemImpl.class); - cq.select(newsItemRoot).where(cb.equal(newsItemRoot.get("type"), type)) - .orderBy(cb.desc(newsItemRoot.get("id"))); + + cq.select(newsItemRoot); + if (type.equals(NewsType.PUBLIC_ONLY)) { + cq.where(cb.equal(newsItemRoot.get("type"), "public")); + } + cq.orderBy(cb.desc(newsItemRoot.get("id"))); + TypedQuery query = entityManager.createQuery(cq); + if (limitQuery) { + query.setMaxResults(3); + } return (List) (Object) query.getResultList(); } } diff --git a/src/main/java/org/wise/portal/domain/newsitem/NewsType.java b/src/main/java/org/wise/portal/domain/newsitem/NewsType.java new file mode 100644 index 0000000000..f834e576d4 --- /dev/null +++ b/src/main/java/org/wise/portal/domain/newsitem/NewsType.java @@ -0,0 +1,13 @@ +package org.wise.portal.domain.newsitem; + +public enum NewsType { + PUBLIC_ONLY, PUBLIC_AND_TEACHER; + + public static NewsType stringToNewsType(String type) throws IllegalArgumentException { + return switch (type) { + case "publicOnly" -> PUBLIC_ONLY; + case "publicAndTeacher" -> PUBLIC_AND_TEACHER; + default -> throw new IllegalArgumentException("Invalid news type: " + type); + }; + } +} diff --git a/src/main/java/org/wise/portal/domain/newsitem/impl/NewsItemImpl.java b/src/main/java/org/wise/portal/domain/newsitem/impl/NewsItemImpl.java index 1b0a5b7166..b9bdc1567e 100644 --- a/src/main/java/org/wise/portal/domain/newsitem/impl/NewsItemImpl.java +++ b/src/main/java/org/wise/portal/domain/newsitem/impl/NewsItemImpl.java @@ -23,6 +23,7 @@ */ package org.wise.portal.domain.newsitem.impl; +import java.io.Serializable; import java.util.Date; import javax.persistence.Column; @@ -52,7 +53,7 @@ @Table(name = NewsItemImpl.DATA_STORE_NAME) @Getter @Setter -public class NewsItemImpl implements NewsItem, Comparable { +public class NewsItemImpl implements NewsItem, Comparable, Serializable { @Transient public static final String DATA_STORE_NAME = "newsitem"; diff --git a/src/main/java/org/wise/portal/presentation/web/controllers/news/NewsAPIController.java b/src/main/java/org/wise/portal/presentation/web/controllers/news/NewsAPIController.java index cf7df012bd..fa8c04382a 100644 --- a/src/main/java/org/wise/portal/presentation/web/controllers/news/NewsAPIController.java +++ b/src/main/java/org/wise/portal/presentation/web/controllers/news/NewsAPIController.java @@ -4,10 +4,12 @@ import org.json.JSONException; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.wise.portal.domain.newsitem.NewsItem; +import org.wise.portal.domain.newsitem.NewsType; import org.wise.portal.service.newsitem.NewsItemService; import java.util.List; @@ -19,19 +21,26 @@ public class NewsAPIController { @Autowired private NewsItemService newsItemService; - @RequestMapping(value = "", method = RequestMethod.GET) - protected String getNews() { - List newsItems = newsItemService.retrieveAllNewsItem(); - JSONArray newsItemsJSON = getNewsItemsJSON(newsItems); - return newsItemsJSON.toString(); + @GetMapping() + protected String getNewsPageNews(@RequestParam String type) { + NewsType newsType = NewsType.stringToNewsType(type); + List newsItems = this.newsItemService.retrieveNewsPageNews(newsType); + return this.getNewsItemsJSONString(newsItems); + } + + @GetMapping("/home") + protected String getHomePageNews(@RequestParam String type) { + NewsType newsType = NewsType.stringToNewsType(type); + List newsItems = this.newsItemService.retrieveAndCacheHomePageNews(newsType); + return this.getNewsItemsJSONString(newsItems); } - private JSONArray getNewsItemsJSON(List newsItems) { + private String getNewsItemsJSONString(List newsItems) { JSONArray newsItemsJSON = new JSONArray(); for (NewsItem newsItem: newsItems) { newsItemsJSON.put(getNewsItemJSON(newsItem)); } - return newsItemsJSON; + return newsItemsJSON.toString(); } private JSONObject getNewsItemJSON(NewsItem newsItem) { diff --git a/src/main/java/org/wise/portal/service/newsitem/NewsItemService.java b/src/main/java/org/wise/portal/service/newsitem/NewsItemService.java index affa99b020..e4042e38c1 100644 --- a/src/main/java/org/wise/portal/service/newsitem/NewsItemService.java +++ b/src/main/java/org/wise/portal/service/newsitem/NewsItemService.java @@ -28,6 +28,7 @@ import org.wise.portal.dao.ObjectNotFoundException; import org.wise.portal.domain.newsitem.NewsItem; +import org.wise.portal.domain.newsitem.NewsType; import org.wise.portal.domain.user.User; /** @@ -48,16 +49,23 @@ public interface NewsItemService { NewsItem createNewsItem(Date date, User owner, String title, String news, String type); /** - * Retrieves all NewsItem from the data store. + * Retrieves the latest NewsItems from the data store. + * @param type the type of news items to retrieve. Get all if type is not present. * @return a Set of NewsItem */ - List retrieveAllNewsItem(); + List retrieveNewsPageNews(NewsType type); /** - * Retrieves all NewsItem by specified type - * @return a Set of NewsItem of specified type + * Uses cached value or retrieves and caches the latest three NewsItems from the data store. + * @param type the type of news items to retrieve. Get all if type is not present. + * @return a Set of NewsItem + */ + List retrieveAndCacheHomePageNews(NewsType type); + + /** Retrieves all NewsItem from the data store. + * @return a Set of NewsItem */ - List retrieveByType(String type); + List retrieveAllNewsItem(); /** * Retrieves a NewsItem given an ID diff --git a/src/main/java/org/wise/portal/service/newsitem/impl/NewsItemServiceImpl.java b/src/main/java/org/wise/portal/service/newsitem/impl/NewsItemServiceImpl.java index a34e57f971..1ed7bff765 100644 --- a/src/main/java/org/wise/portal/service/newsitem/impl/NewsItemServiceImpl.java +++ b/src/main/java/org/wise/portal/service/newsitem/impl/NewsItemServiceImpl.java @@ -24,17 +24,22 @@ package org.wise.portal.service.newsitem.impl; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.wise.portal.dao.ObjectNotFoundException; import org.wise.portal.dao.newsitem.NewsItemDao; import org.wise.portal.domain.newsitem.NewsItem; +import org.wise.portal.domain.newsitem.NewsType; import org.wise.portal.domain.newsitem.impl.NewsItemImpl; import org.wise.portal.domain.user.User; import org.wise.portal.service.newsitem.NewsItemService; import java.util.Date; import java.util.List; +import javax.management.timer.Timer; /** * @author Patrick Lawler @@ -45,14 +50,23 @@ public class NewsItemServiceImpl implements NewsItemService { @Autowired private NewsItemDao newsItemDao; - public List retrieveAllNewsItem() { - return newsItemDao.getList(); + @Override + public List retrieveNewsPageNews(NewsType type) { + return newsItemDao.getNewsPageNews(type); } - public List retrieveByType(String type) { - return newsItemDao.getListByType(type); + @Override + @Cacheable(value = "newsItems", key = "#type") + public List retrieveAndCacheHomePageNews(NewsType type) { + return newsItemDao.getHomePageNews(type); + } + + @Override + public List retrieveAllNewsItem() { + return newsItemDao.getAllNews(); } + @Override public NewsItem retrieveById(Integer id) throws ObjectNotFoundException { try { return newsItemDao.getById(id); @@ -62,6 +76,7 @@ public NewsItem retrieveById(Integer id) throws ObjectNotFoundException { } @Transactional + @CacheEvict(value = "newsItems", allEntries = true) public NewsItem createNewsItem(Date date, User owner, String title, String news, String type) { NewsItem newsItem = new NewsItemImpl(); newsItem.setDate(date); @@ -74,6 +89,7 @@ public NewsItem createNewsItem(Date date, User owner, String title, String news, } @Transactional + @CacheEvict(value = "newsItems", allEntries = true) public void updateNewsItem(Integer id, Date date, User owner, String title, String news, String type) throws ObjectNotFoundException { NewsItem newsItem = newsItemDao.getById(id); @@ -86,8 +102,14 @@ public void updateNewsItem(Integer id, Date date, User owner, String title, } @Transactional + @CacheEvict(value = "newsItems", allEntries = true) public void deleteNewsItem(Integer id) throws ObjectNotFoundException { NewsItem newsItem = newsItemDao.getById(id); newsItemDao.delete(newsItem); } + + @Scheduled(fixedRate = Timer.ONE_HOUR) + @CacheEvict(value = "newsItems", allEntries = true) + public void evictNewsItemsCache() { + } } diff --git a/src/test/java/org/wise/portal/dao/newsitem/impl/HibernateNewsItemDaoTest.java b/src/test/java/org/wise/portal/dao/newsitem/impl/HibernateNewsItemDaoTest.java index 140ba8f969..5b8be1a8e5 100644 --- a/src/test/java/org/wise/portal/dao/newsitem/impl/HibernateNewsItemDaoTest.java +++ b/src/test/java/org/wise/portal/dao/newsitem/impl/HibernateNewsItemDaoTest.java @@ -26,6 +26,7 @@ import java.util.Date; import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -33,6 +34,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.wise.portal.domain.authentication.Schoollevel; import org.wise.portal.domain.newsitem.NewsItem; +import org.wise.portal.domain.newsitem.NewsType; import org.wise.portal.domain.newsitem.impl.NewsItemImpl; import org.wise.portal.domain.user.User; import org.wise.portal.junit.AbstractTransactionalDbTests; @@ -57,16 +59,39 @@ public void setUp() throws Exception { } @Test - public void getListByType_TypeThatDoesNotExist_ShouldReturnNone() { - List newsItems = newsItemDao.getListByType("test"); - assertEquals(0, newsItems.size()); + public void getNewsPageNews_WithPublicOnlyType_ShouldReturnAllNewsItems() { + createNewsItem(teacher, new Date(), "public", "News Title 1", "news1"); + createNewsItem(teacher, new Date(), "teacherOnly", "News Title 2", "news2"); + List newsItems = newsItemDao.getNewsPageNews(NewsType.PUBLIC_ONLY); + assertEquals(1, newsItems.size()); } @Test - public void getListByType_TypeThatExists_ShouldReturnNewsItems() { - createNewsItem(teacher, new Date(), "public", "News Title", "news1"); - List newsItems = newsItemDao.getListByType("public"); - assertEquals(1, newsItems.size()); + public void getNewsPageNews_WithPublicAndTeacherType_ShouldReturnAllNewsItems() { + createNewsItem(teacher, new Date(), "public", "News Title 1", "news1"); + createNewsItem(teacher, new Date(), "teacherOnly", "News Title 2", "news2"); + List newsItems = newsItemDao.getNewsPageNews(NewsType.PUBLIC_AND_TEACHER); + assertEquals(2, newsItems.size()); + } + + @Test + public void getHomePageNews_ShouldReturnThreeNewsItems() { + createNewsItem(teacher, new Date(), "public", "News Title 1", "news1"); + createNewsItem(teacher, new Date(), "teacherOnly", "News Title 2", "news2"); + createNewsItem(teacher, new Date(), "public", "News Title 3", "news3"); + createNewsItem(teacher, new Date(), "teacherOnly", "News Title 4", "news4"); + List newsItems = newsItemDao.getHomePageNews(NewsType.PUBLIC_AND_TEACHER); + assertEquals(3, newsItems.size()); + } + + @Test + public void getAllNews_ShouldReturnAllNewsItems() { + createNewsItem(teacher, new Date(), "public", "News Title 1", "news1"); + createNewsItem(teacher, new Date(), "teacherOnly", "News Title 2", "news2"); + createNewsItem(teacher, new Date(), "public", "News Title 3", "news3"); + createNewsItem(teacher, new Date(), "teacherOnly", "News Title 4", "news4"); + List newsItems = newsItemDao.getAllNews(); + assertEquals(4, newsItems.size()); } private NewsItem createNewsItem(User owner, Date date, String type, String title, String news) { diff --git a/src/test/java/org/wise/portal/service/newsitem/impl/NewsItemServiceImplTest.java b/src/test/java/org/wise/portal/service/newsitem/impl/NewsItemServiceImplTest.java index dd2aaac49f..ce9433ded8 100644 --- a/src/test/java/org/wise/portal/service/newsitem/impl/NewsItemServiceImplTest.java +++ b/src/test/java/org/wise/portal/service/newsitem/impl/NewsItemServiceImplTest.java @@ -36,6 +36,7 @@ import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.Optional; import org.easymock.EasyMockExtension; import org.easymock.Mock; @@ -47,6 +48,7 @@ import org.wise.portal.dao.ObjectNotFoundException; import org.wise.portal.dao.newsitem.NewsItemDao; import org.wise.portal.domain.newsitem.NewsItem; +import org.wise.portal.domain.newsitem.NewsType; import org.wise.portal.domain.newsitem.impl.NewsItemImpl; import org.wise.portal.domain.user.User; import org.wise.portal.domain.user.impl.UserImpl; @@ -158,14 +160,14 @@ public void deleteNewsItem_InvalidNewsItemID_ShouldThrowException() } @Test - public void retrieveAllNewsItem_TwoNewsItemsInDB_ShouldSucceed() { + public void retrieveNewsPageNews_TwoNewsItemsInDB_ShouldSucceed() { List newsItemsInDB = new ArrayList(); newsItemsInDB.add(newsItem1); newsItemsInDB.add(newsItem2); - expect(newsItemDao.getList()).andReturn(newsItemsInDB); + expect(newsItemDao.getNewsPageNews(NewsType.PUBLIC_AND_TEACHER)).andReturn(newsItemsInDB); replay(newsItemDao); - List newsItemsFromDB = newsItemServiceImpl.retrieveAllNewsItem(); + List newsItemsFromDB = newsItemServiceImpl.retrieveNewsPageNews(NewsType.PUBLIC_AND_TEACHER); assertEquals(newsItemsFromDB.size(), 2); assertTrue(newsItemsFromDB.get(0).equals(newsItem1)); assertTrue(newsItemsFromDB.get(1).equals(newsItem2));