personalweb

archived
git clone git://git.wimdupont.com/personalweb.git
Log | Files | Refs | LICENSE

commit 73579a1427a3a48adee74b6a861e4e79af4beecb
parent f4a654c9791e0888fa3cded5b67bdbed516f96fb
Author: Wim Dupont <wim@wimdupont.com>
Date:   Sun, 17 Dec 2023 10:17:13 +0100

store generated rss in db


Former-commit-id: d2ccb42f93d9840c8c02c3c8b266d8a92fb44664
Diffstat:
Asrc/main/java/com/wimdupont/personalweb/config/AsciidoctorConfig.java | 23+++++++++++++++++++++++
Asrc/main/java/com/wimdupont/personalweb/config/RssConfig.java | 15+++++++++++++++
Msrc/main/java/com/wimdupont/personalweb/controller/web/FeedController.java | 14++++++--------
Msrc/main/java/com/wimdupont/personalweb/converter/AdocToHtmlStringConverter.java | 16+++++++---------
Asrc/main/java/com/wimdupont/personalweb/exception/RssXmlMapperException.java | 8++++++++
Msrc/main/java/com/wimdupont/personalweb/model/dao/AdocContent.java | 3+++
Asrc/main/java/com/wimdupont/personalweb/model/dao/Rss.java | 31+++++++++++++++++++++++++++++++
Asrc/main/java/com/wimdupont/personalweb/repository/RssRepository.java | 9+++++++++
Msrc/main/java/com/wimdupont/personalweb/service/RssFeedGenerator.java | 14++++++--------
Asrc/main/java/com/wimdupont/personalweb/service/RssService.java | 38++++++++++++++++++++++++++++++++++++++
Asrc/main/resources/db/migration/V1_3__add-rss-table.sql | 6++++++
Msrc/test/java/com/wimdupont/personalweb/service/RssFeedGeneratorTest.java | 14+++++++++++---
12 files changed, 163 insertions(+), 28 deletions(-)

diff --git a/src/main/java/com/wimdupont/personalweb/config/AsciidoctorConfig.java b/src/main/java/com/wimdupont/personalweb/config/AsciidoctorConfig.java @@ -0,0 +1,23 @@ +package com.wimdupont.personalweb.config; + +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Options; +import org.asciidoctor.SafeMode; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AsciidoctorConfig { + + @Bean + public Asciidoctor asciidoctor() { + return Asciidoctor.Factory.create(); + } + + @Bean + public Options options() { + return Options.builder() + .safe(SafeMode.UNSAFE) + .build(); + } +} diff --git a/src/main/java/com/wimdupont/personalweb/config/RssConfig.java b/src/main/java/com/wimdupont/personalweb/config/RssConfig.java @@ -0,0 +1,15 @@ +package com.wimdupont.personalweb.config; + +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RssConfig { + + @Bean + public XmlMapper xmlMapper() { + return new XmlMapper(); + } + +} diff --git a/src/main/java/com/wimdupont/personalweb/controller/web/FeedController.java b/src/main/java/com/wimdupont/personalweb/controller/web/FeedController.java @@ -1,8 +1,6 @@ package com.wimdupont.personalweb.controller.web; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import com.wimdupont.personalweb.service.RssFeedGenerator; +import com.wimdupont.personalweb.service.RssService; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; @@ -14,17 +12,17 @@ import static org.springframework.http.MediaType.APPLICATION_XML_VALUE; @Controller public class FeedController { - private final RssFeedGenerator rssFeedGenerator; + private final RssService rssService; - public FeedController(RssFeedGenerator rssFeedGenerator) { - this.rssFeedGenerator = rssFeedGenerator; + public FeedController(RssService rssService) { + this.rssService = rssService; } @GetMapping(path = "/rss.xml") @ResponseBody - public ResponseEntity<String> getRss() throws JsonProcessingException { + public ResponseEntity<String> getRss() { return ResponseEntity.ok() .contentType(MediaType.parseMediaType(APPLICATION_XML_VALUE)) - .body(new XmlMapper().writeValueAsString(rssFeedGenerator.getRssFeed())); + .body(rssService.retrieveRss().getContent()); } } diff --git a/src/main/java/com/wimdupont/personalweb/converter/AdocToHtmlStringConverter.java b/src/main/java/com/wimdupont/personalweb/converter/AdocToHtmlStringConverter.java @@ -2,23 +2,21 @@ package com.wimdupont.personalweb.converter; import org.asciidoctor.Asciidoctor; import org.asciidoctor.Options; -import org.asciidoctor.SafeMode; import org.springframework.stereotype.Component; @Component public class AdocToHtmlStringConverter { - private final Asciidoctor ASCIIDOCTOR; - private final Options OPTIONS; + private final Asciidoctor asciidoctor; + private final Options options; - public AdocToHtmlStringConverter() { - this.ASCIIDOCTOR = Asciidoctor.Factory.create(); - this.OPTIONS = Options.builder() - .safe(SafeMode.UNSAFE) - .build(); + public AdocToHtmlStringConverter(Asciidoctor asciidoctor, + Options options) { + this.asciidoctor = asciidoctor; + this.options = options; } public String convert(String adocString) { - return ASCIIDOCTOR.convert(adocString, OPTIONS); + return asciidoctor.convert(adocString, options); } } diff --git a/src/main/java/com/wimdupont/personalweb/exception/RssXmlMapperException.java b/src/main/java/com/wimdupont/personalweb/exception/RssXmlMapperException.java @@ -0,0 +1,8 @@ +package com.wimdupont.personalweb.exception; + +public class RssXmlMapperException extends RuntimeException{ + + public RssXmlMapperException(Throwable throwable){ + super(throwable); + } +} diff --git a/src/main/java/com/wimdupont/personalweb/model/dao/AdocContent.java b/src/main/java/com/wimdupont/personalweb/model/dao/AdocContent.java @@ -20,6 +20,7 @@ public class AdocContent extends Auditable implements AdocContentMeta { private AdocContentType contentType; private LocalDateTime committedDate; + @Override public String getPath() { return path; } @@ -44,6 +45,7 @@ public class AdocContent extends Auditable implements AdocContentMeta { this.htmlText = htmlText; } + @Override public AdocContentType getContentType() { return contentType; } @@ -52,6 +54,7 @@ public class AdocContent extends Auditable implements AdocContentMeta { this.contentType = contentType; } + @Override public LocalDateTime getCommittedDate() { return committedDate; } diff --git a/src/main/java/com/wimdupont/personalweb/model/dao/Rss.java b/src/main/java/com/wimdupont/personalweb/model/dao/Rss.java @@ -0,0 +1,31 @@ +package com.wimdupont.personalweb.model.dao; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import org.hibernate.annotations.UuidGenerator; + +@Entity +public class Rss extends Auditable { + + @Id + @UuidGenerator + private String id; + private String content; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + +} diff --git a/src/main/java/com/wimdupont/personalweb/repository/RssRepository.java b/src/main/java/com/wimdupont/personalweb/repository/RssRepository.java @@ -0,0 +1,9 @@ +package com.wimdupont.personalweb.repository; + +import com.wimdupont.personalweb.model.dao.Rss; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface RssRepository extends JpaRepository<Rss, String> { +} diff --git a/src/main/java/com/wimdupont/personalweb/service/RssFeedGenerator.java b/src/main/java/com/wimdupont/personalweb/service/RssFeedGenerator.java @@ -13,14 +13,12 @@ import java.util.List; public class RssFeedGenerator { private final AdocContentService adocContentService; - private RssFeed rssFeed; + private final RssService rssService; - public RssFeedGenerator(AdocContentService adocContentService) { + public RssFeedGenerator(AdocContentService adocContentService, + RssService rssService) { this.adocContentService = adocContentService; - } - - public RssFeed getRssFeed() { - return rssFeed; + this.rssService = rssService; } public void generate() { @@ -33,10 +31,10 @@ public class RssFeedGenerator { .item(getItems()) .build(); - this.rssFeed = RssFeed.Builder.newBuilder() + rssService.save(RssFeed.Builder.newBuilder() .version("2.0") .channel(channel) - .build(); + .build()); } private List<Item> getItems() { diff --git a/src/main/java/com/wimdupont/personalweb/service/RssService.java b/src/main/java/com/wimdupont/personalweb/service/RssService.java @@ -0,0 +1,38 @@ +package com.wimdupont.personalweb.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.wimdupont.personalweb.exception.RssXmlMapperException; +import com.wimdupont.personalweb.model.dao.Rss; +import com.wimdupont.personalweb.model.rss.RssFeed; +import com.wimdupont.personalweb.repository.RssRepository; +import org.springframework.stereotype.Service; + +@Service +public class RssService { + + private final XmlMapper xmlMapper; + private final RssRepository rssRepository; + + public RssService(XmlMapper xmlMapper, + RssRepository rssRepository) { + this.xmlMapper = xmlMapper; + this.rssRepository = rssRepository; + } + + public Rss retrieveRss() { + return rssRepository.findAll().stream() + .findAny() + .orElse(new Rss()); + } + + public void save(RssFeed rssFeed) { + try { + var rss = retrieveRss(); + rss.setContent(xmlMapper.writeValueAsString(rssFeed)); + rssRepository.save(rss); + } catch (JsonProcessingException e) { + throw new RssXmlMapperException(e); + } + } +} diff --git a/src/main/resources/db/migration/V1_3__add-rss-table.sql b/src/main/resources/db/migration/V1_3__add-rss-table.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS rss ( + id VARCHAR(36) primary key NOT NULL, + content TEXT, + created_date DATETIME, + modified_date DATETIME +) diff --git a/src/test/java/com/wimdupont/personalweb/service/RssFeedGeneratorTest.java b/src/test/java/com/wimdupont/personalweb/service/RssFeedGeneratorTest.java @@ -2,9 +2,12 @@ package com.wimdupont.personalweb.service; import com.wimdupont.personalweb.model.dao.AdocContentMother; import com.wimdupont.personalweb.model.dao.AdocContentMother.Defaults; +import com.wimdupont.personalweb.model.rss.RssFeed; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -17,19 +20,24 @@ class RssFeedGeneratorTest { @Mock private AdocContentService adocContentService; + @Mock + private RssService rssService; @InjectMocks private RssFeedGenerator rssFeedGenerator; + @Captor + private ArgumentCaptor<RssFeed> rssFeedArgumentCaptor; @Test public void generate() { - Mockito.when(adocContentService.findAllRSSData()) .thenReturn(List.of(AdocContentMother.withDefaults().build())); rssFeedGenerator.generate(); - var channel = rssFeedGenerator.getRssFeed().channel(); - Assertions.assertEquals("2.0", rssFeedGenerator.getRssFeed().version()); + Mockito.verify(rssService, Mockito.times(1)) + .save(rssFeedArgumentCaptor.capture()); + var channel = rssFeedArgumentCaptor.getValue().channel(); + Assertions.assertEquals("2.0", rssFeedArgumentCaptor.getValue().version()); Assertions.assertEquals("Wim Dupont", channel.title()); Assertions.assertEquals("https://wimdupont.com", channel.link()); Assertions.assertEquals("Updates from Wim Dupont", channel.description());