under construction

This commit is contained in:
2021-08-20 18:07:12 +09:00
parent c1d7e9a5ba
commit 2ee272598a
120 changed files with 20026 additions and 129 deletions

1
sitemap/README.md Normal file
View File

@@ -0,0 +1 @@
# Sitemap & Syndication

34
sitemap/build.gradle.kts Normal file
View File

@@ -0,0 +1,34 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-springboot")
id("org.springframework.boot") version "2.5.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
version = "1.0.0"
description = "Sitemap builder"
tasks.bootJar{
enabled = false
}
tasks.jar {
enabled = true
}
dependencies {
implementation("com.github.spullara.mustache.java:compiler:0.9.10")
implementation("org.springframework.boot:spring-boot-starter")
compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

View File

@@ -0,0 +1,40 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.sitemap;
/*import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template;*/
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import java.io.StringWriter;
/**
* @link https://www.sitemaps.org/ko/protocol.html
*/
public abstract class BaseSitemapService {
private final Mustache template;
protected BaseSitemapService() {
MustacheFactory factory = new DefaultMustacheFactory();
template = factory.compile("sitemap/sitemap.mustache");
}
protected abstract Sitemap sitemap();
public String getSitemap() {
StringWriter writer = new StringWriter();
template.execute(writer, sitemap());
return writer.toString()
.replaceAll("[\n\t]", "")
.replaceAll("\\s{2,}", " ");
}
}

View File

@@ -0,0 +1,29 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.sitemap;
public enum Frequency {
ALWAYS("always"),
HOURLY("hourly"),
DAILY("daily"),
WEEKLY("weekly"),
MONTHLY("monthly"),
YEARLY("yearly"),
NEVER("never");
private final String value;
Frequency(final String value) {
this.value = value;
}
@Override
public String toString() {
return value;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.sitemap;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class Sitemap {
private final List<SitemapItem> items;
public Sitemap() {
items = new ArrayList<>();
}
public void addItem(final SitemapItem item) {
this.items.add(item);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.sitemap;
import com.elex_project.asgard.syndication.Formatter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
@Slf4j
@Data
@AllArgsConstructor
@Builder
public class SitemapItem {
private static final ZoneId defaultZone = ZoneOffset.ofHours(0).normalized();
private String loc;
private LocalDateTime lastMod;
private Frequency changeFreq;
private Float priority;
public String getLastModIso(){
return Formatter.formatIso(lastMod);
/*return lastMod.atZone(ZoneId.systemDefault())
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(defaultZone));*/
}
}

View File

@@ -0,0 +1,8 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.sitemap;

View File

@@ -0,0 +1,43 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.syndication;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import java.io.StringWriter;
//@Service
public abstract class BaseSyndicationService {
private final Mustache templateRss, templateAtom;
public BaseSyndicationService(){
MustacheFactory factory = new DefaultMustacheFactory();
templateRss = factory.compile("syndication/rss2.mustache");
templateAtom = factory.compile("syndication/atom.mustache");
}
protected abstract Syndication syndication();
public String getRssSyndication() {
StringWriter writer = new StringWriter();
templateRss.execute(writer, syndication());
return writer.toString()
.replaceAll("[\n\t]","")
.replaceAll("\\s{2,}", " ");
}
public String getAtomSyndication() {
StringWriter writer = new StringWriter();
templateAtom.execute(writer, syndication());
return writer.toString()
.replaceAll("[\n\t]","")
.replaceAll("\\s{2,}", " ");
}
}

View File

@@ -0,0 +1,31 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.syndication;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public interface Formatter {
static final ZoneId defaultZone = ZoneOffset.ofHours(0).normalized();
static String formatIso(final LocalDateTime time){
return time.atZone(ZoneId.systemDefault())
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(defaultZone));
}
static String formatRfc(final LocalDateTime time){
return time.atZone(ZoneOffset.systemDefault())
.format(DateTimeFormatter.RFC_1123_DATE_TIME.withZone(defaultZone));
}
}

View File

@@ -0,0 +1,45 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.syndication;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Locale;
@Slf4j
@Getter
@Setter
@Builder @ToString
public class Syndication {
private String title;
private String link, linkRss, linkAtom;
private String description;
private String copyright;
private String imageUrl;
@Builder.Default
private Locale language = Locale.getDefault();
@Builder.Default
private LocalDateTime lastBuildDate = LocalDateTime.now();
@Singular
protected List<SyndicationItem> items;
public void addItem(final SyndicationItem item) {
items.add(item);
}
public final String getLastBuildDateIso() {
return Formatter.formatIso(lastBuildDate);
}
public final String getLastBuildDateRfc() {
return Formatter.formatRfc(lastBuildDate);
}
}

View File

@@ -0,0 +1,41 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.syndication;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
@Slf4j
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder @ToString
public class SyndicationItem {
private String title;
private String url;
private String description;
private String authorName, authorEmail;
private LocalDateTime pubDate;
private LocalDateTime modDate;
public final String getPubDateIsoString(){
return Formatter.formatIso(pubDate);
}
public final String getModDateIsoString(){
return Formatter.formatIso(modDate);
}
public final String getPubDateRfcString(){
return Formatter.formatRfc(pubDate);
}
public final String getModDateRfcString(){
return Formatter.formatRfc(modDate);
}
}

View File

@@ -0,0 +1,8 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.syndication;

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{{#items}}
<url>
<loc>{{loc}}</loc>
{{#lastMod}}<lastmod>{{lastModIso}}</lastmod>{{/lastMod}}
{{#changeFreq}}<changefreq>{{changeFreq}}</changefreq>{{/changeFreq}}
{{#priority}}<priority>{{priority}}</priority>{{/priority}}
</url>
{{/items}}
</urlset>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://www.elex-project.com/</id>
<title>{{title}}</title>
<link rel="self" thref="{{linkAtom}}" />
<updated>{{lastBuildDateIso}}</updated>
<rights>{{copyright}}</rights>
<logo>{{imageUrl}}</logo>
{{#items}}
<entry>
<id>{{url}}</id>
<title>{{title}}</title>
<link rel="alternate" href="{{url}}" />
<updated>{{modDateIso}}</updated>
<published>{{pubDateIso}}</published>
<summary>{{description}}</summary>
<author>
<name>{{author}}</name>
<email>{{email}}</email>
</author>
</entry>
{{/items}}
</feed>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>{{title}}</title>
<link>{{link}}</link>
<atom:link href="{{linkRss}}" rel="self" type="application/rss+xml" />
<atom:link href="{{linkAtom}}" rel="alternate" type="application/atom+xml" />
<description>{{description}}</description>
<language>{{language}}</language>
<copyright>{{copyright}}</copyright>
<lastBuildDate>{{lastBuildDateRfc}}</lastBuildDate>
<image>
<url>{{imageUrl}}</url>
<title>{{title}}</title>
<link>{{link}}</link>
</image>
{{#items}}
<item>
<title>{{title}}</title>
<link>{{url}}</link>
<guid>{{url}}</guid>
<description>{{description}}</description>
<author>{{authorEmail}} ({{authorName}})</author>
<pubDate>{{pubDateRfc}}</pubDate>
</item>
{{/items}}
</channel>
</rss>

View File

@@ -0,0 +1,49 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.sitemap;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SitemapServiceTest {
private static BaseSitemapService service;
@BeforeAll
static void prepare() {
service = new BaseSitemapService() {
@Override
protected Sitemap sitemap() {
final Sitemap sitemap = new Sitemap();
sitemap.addItem(SitemapItem.builder()
.loc("https://aaa.com/1")
.lastMod(LocalDateTime.now())
.changeFreq(Frequency.HOURLY)
.build());
sitemap.addItem(SitemapItem.builder()
.loc("https://aaa.com/2")
.lastMod(LocalDateTime.now())
.build());
return sitemap;
}
};
}
@Test
void sitemap() {
final String output = service.getSitemap();
System.out.println(output);
assertTrue(output.contains("<loc>https://aaa.com/2</loc>"));
}
}

View File

@@ -0,0 +1,105 @@
/*
* Project Asgard
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package com.elex_project.asgard.syndication;
import com.elex_project.asgard.syndication.BaseSyndicationService;
import com.elex_project.asgard.syndication.Syndication;
import com.elex_project.asgard.syndication.SyndicationItem;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SyndicationServiceTest {
private static BaseSyndicationService service;
@BeforeAll
static void prepare() {
service = new BaseSyndicationService() {
@Override
protected Syndication syndication() {
final Syndication syndication = Syndication.builder()
.title("Elex Project")
.link("https://www.elex-project.com/")
.linkAtom("https://www.elex-project.com/atom")
.linkRss("https://www.elex-project.com/rss")
.description("Sample description")
.language(Locale.KOREAN)
.copyright("Copyright (c) 2021 Elex. All Rights Reserved.")
.imageUrl("https://www.elex-project.com/images/logo.png")
.lastBuildDate(LocalDateTime.now())
.item(SyndicationItem.builder()
.authorName("Elex")
.authorEmail("email@example.com")
.description("Sample article")
.modDate(LocalDateTime.now())
.pubDate(LocalDateTime.now())
.title("Sample title")
.url("https://something.com/1")
.build())
.item(SyndicationItem.builder()
.authorName("Elex")
.authorEmail("email@example.com")
.description("Sample article 2")
.modDate(LocalDateTime.now())
.pubDate(LocalDateTime.now())
.title("Sample title 2")
.url("https://something.com/2")
.build())
.build();
final List<SyndicationItem> items = new ArrayList<>();// todo
items.forEach(syndication::addItem);
return syndication;
}
};
}
@Test
void rss() {
final String out = service.getRssSyndication();
System.out.println(out);
assertTrue(out.contains("<link>https://something.com/2</link>"));
}
@Test
void atom() {
final String out = service.getAtomSyndication();
System.out.println(out);
assertTrue(out.contains("<id>https://something.com/2</id>"));
}
@Test
void convIso() {
LocalDateTime dt = LocalDateTime.now();
String t = dt.atZone(ZoneId.systemDefault())
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME
.withZone(ZoneOffset.ofHours(0)));
System.out.println(t);
}
@Test
void convRfc() {
LocalDateTime dt = LocalDateTime.now();
String t = dt.atZone(ZoneId.systemDefault())
.format(DateTimeFormatter.RFC_1123_DATE_TIME
.withZone(ZoneOffset.ofHours(0)));
System.out.println(t);
}
}