feat(webcal): Finished main features of webcal module

This commit is contained in:
Zihlu Wang
2023-08-06 18:32:16 +08:00
parent 171b7f3639
commit 3cb6805185
7 changed files with 850 additions and 0 deletions
+1
View File
@@ -20,6 +20,7 @@
<module>devkit-core</module> <module>devkit-core</module>
<module>devkit-utils</module> <module>devkit-utils</module>
<module>guid</module> <module>guid</module>
<module>webcal</module>
<module>simple-jwt-facade</module> <module>simple-jwt-facade</module>
<module>simple-jwt-authzero</module> <module>simple-jwt-authzero</module>
<module>simple-jwt-jjwt</module> <module>simple-jwt-jjwt</module>
+37
View File
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2023 CodeCraftersCN.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.org.codecrafters</groupId>
<artifactId>jdevkit</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>webcal</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
@@ -0,0 +1,166 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.codecrafters.webcal;
import java.util.ArrayList;
import java.util.List;
/**
* WebCalendar
*
* @author Zihlu Wang
*/
public final class WebCalendar {
//
// Constants
//
// Tag
private final static String TAG = "VCALENDAR";
//
// Fields
//
private String name;
/**
* Company name. This value is to specify the {@code productIdentifier}
* property.
*/
private String companyName;
/**
* Product name. This value is to specify the {@code productIdentifier}
* property.
*/
private String productName;
private String domainName;
/**
* Calendar scale, referenced from <a href="https://icalendar.org/iCalendar-RFC-5545/3-7-1-calendar-scale.html"
* >RFC 5545 - 3.7.1. Calendar Scale</a>.
*/
private final String scale = "GREGORIAN";
/**
* Method, referenced from <a href="https://icalendar.org/iCalendar-RFC-5545/3-7-2-method.html"
* >RFC 5545 - 3.7.2. Method</a>.
*/
private String method;
private final String version = "2.0";
private List<WebCalendarNode> nodes;
//
// Constructors
//
public WebCalendar() {
this.nodes = new ArrayList<>();
}
//
// Methods
//
/**
* Set the name for this calendar.
*
* @param name The name for the calendar.
* @return The calendar instance.
*/
public WebCalendar setName(String name) {
this.name = name;
return this;
}
/**
* Set the company name for this calendar.
*
* @param companyName The company name for the calendar.
* @return The calendar instance.
*/
public WebCalendar setCompanyName(String companyName) {
this.companyName = companyName;
return this;
}
public WebCalendar setDomainName(String domainName) {
this.domainName = domainName;
return this;
}
/**
* Set the product name for this calendar.
*
* @param productName The product name for the calendar.
* @return The calendar instance.
*/
public WebCalendar setProductName(String productName) {
this.productName = productName;
return this;
}
/**
* Set the method for this calendar.
*
* @param method The product name for the calendar.
* @return The calendar instance.
*/
public WebCalendar setMethod(String method) {
this.method = method;
return this;
}
/**
* Add a calendar node to this calendar.
*
* @param node Any calendar node.
* @return The calendar instance.
*/
public WebCalendar addEvent(WebCalendarNode node) {
this.nodes.add(node);
return this;
}
/**
* Resolve the calendar instance to a text that implements RFC-5545.
*
* @return A string includes all events in this calendar.
*/
public String resolve() {
var events = new StringBuilder();
if (nodes != null && !nodes.isEmpty()) {
nodes.forEach(item ->
events.append(item.setDomainName(domainName)
.resolve()));
}
return "BEGIN:" + TAG + "\n" +
"PRODID:-//" + companyName + "//" + productName + "//EN\n" +
"VERSION:" + version + "\n" +
"X-WR-CALNAME:" + name + "\n" +
events + "\n" +
"END:" + TAG;
}
}
@@ -0,0 +1,265 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.codecrafters.webcal;
import cn.org.codecrafters.webcal.config.Classification;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import java.util.UUID;
/**
* WebCalendarEvent
*
* @author Zihlu Wang
*/
public final class WebCalendarEvent extends WebCalendarNode {
private final static String TAG = "VEVENT";
/**
* Add a batch of categories.
*
* @param categories A batch of categories.
* @return The Event instance.
*/
public WebCalendarEvent addCategories(String... categories) {
this.categories.addAll(Arrays.asList(categories));
return this;
}
/**
* Add a batch of categories.
*
* @param categories A batch of categories.
* @return The Event instance.
*/
public WebCalendarEvent addCategories(Collection<String> categories) {
this.categories.addAll(categories);
return this;
}
/**
* Add a category.
*
* @param category A category.
* @return The Event instance.
*/
public WebCalendarEvent addCategory(String category) {
this.categories.add(category);
return this;
}
/**
* Set the classification.
*
* @param classification The specified classification value.
* @return The Event instance.
*/
public WebCalendarEvent setClassification(Classification classification) {
this.classification = classification;
return this;
}
/**
* Set the comment.
*
* @param comment The comment.
* @return The Event instance.
*/
public WebCalendarEvent setComment(String comment) {
this.comment = comment;
return this;
}
/**
* Set the description.
*
* @param description The description.
* @return The Event instance.
*/
public WebCalendarEvent setDescription(String description) {
this.description = description;
return this;
}
/**
* Set the location.
*
* @param location The location.
* @return The Event instance.
*/
public WebCalendarEvent setLocation(String location) {
this.location = location;
return this;
}
/**
* Set the percent complete value.
*
* @param percentComplete The percent complete value.
* @return The Event instance.
*/
public WebCalendarEvent setPercentComplete(Integer percentComplete) {
if (percentComplete < 0 || percentComplete > 100) {
throw new IllegalArgumentException("Percent out of range (0 ~ 100)");
}
this.percentComplete = percentComplete;
return this;
}
/**
* Set a priority.
*
* @param priority The priority to be set.
* @return The Event instance.
*/
public WebCalendarEvent setPriority(Integer priority) {
if (priority < 0 || priority > 9) {
throw new IllegalArgumentException("The priority you provide is out of range (0 ~ 9).");
}
this.priority = priority;
return this;
}
/**
* Set the summary.
*
* @param summary The summary (you can also call it as a title).
* @return The Event instance.
*/
public WebCalendarEvent setSummary(String summary) {
this.summary = summary;
return this;
}
/**
* Set the end of this node.
*
* @param end The end time of this event.
* @return The Event instance.
*/
public WebCalendarEvent setEnd(LocalDateTime end) {
if (this.duration != null) {
throw new IllegalStateException("You have set the field DURATION before, please remove it or remove setEnd.");
}
this.end = end;
return this;
}
/**
* Set the start of this event.
*
* @param start The date time specify the start time of this event.
* @return The Event instance.
*/
public WebCalendarEvent setStart(LocalDateTime start) {
this.start = start;
return this;
}
/**
* Set the duration of this event.
*
* @param duration The duration of this event.
* @return The Event instance.
*/
public WebCalendarEvent setDuration(Duration duration) {
if (this.end != null) {
throw new IllegalStateException("You have set the field END before, please remove it or remove setDuration.");
}
this.duration = duration;
return this;
}
/**
* Set the URL.
*
* @param url The URL.
* @return The Event instance.
*/
public WebCalendarEvent setUrl(String url) {
this.url = url;
return this;
}
/**
* Set the uid of this event.
*
* @param uid The uid.
* @return The Event instance.
*/
public WebCalendarEvent setUid(String uid) {
this.uid = uid;
return this;
}
/**
* Set the domain name of this event.
*
* @param domainName The domain name.
* @return The Event instance.
*/
public WebCalendarEvent setDomainName(String domainName) {
this.domainName = domainName;
return this;
}
/**
* Set the timezone of this event.
*
* @param timezone The time zone to set.
* @return The Event instance.
*/
public WebCalendarEvent setTimezone(String timezone) {
this.timezone = timezone;
return this;
}
@Override
public String resolve() {
return "\nBEGIN:" + TAG + "\n" +
"UID:" + Optional.ofNullable(uid).orElse(UUID.randomUUID().toString()) + "@" + domainName + "\n" +
Optional.ofNullable(summary).map((item) -> "SUMMARY:" + item + "\n").orElse("") +
"DTSTART" + Optional.ofNullable(timezone).map(item -> ";TZID=" + item).orElse("") + ":" + start.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "\n" +
Optional.ofNullable(categories)
.map((item) -> {
if (!item.isEmpty()) {
return "CATEGORIES:" + resolveCategories() + "\n";
}
return null;
}).orElse("") +
Optional.ofNullable(duration)
.map((item) -> "DURATION:PT" + item.getSeconds() + "S\n").orElse("") +
Optional.ofNullable(end)
.map((item) -> "DTEND:" + end.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "\n").orElse("") +
Optional.ofNullable(classification)
.map((item) -> "CLASS:" + item.getClassification() + "\n").orElse("") +
Optional.ofNullable(comment).map((item) -> "COMMENT:" + item + "\n").orElse("") +
Optional.ofNullable(description).map((item) -> "DESCRIPTION:" + item + "\n").orElse("") +
Optional.ofNullable(location).map((item) -> "LOCATION:" + item + "\n").orElse("") +
Optional.ofNullable(percentComplete).map((item) -> "PERCENT-COMPLETE:" + item + "\n").orElse("") +
Optional.ofNullable(priority).map((item) -> "PRIORITY:" + item + "\n").orElse("") +
"END:" + TAG + "\n";
}
}
@@ -0,0 +1,282 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.codecrafters.webcal;
import cn.org.codecrafters.webcal.config.Classification;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* WebCalendarNode
*
* @author Zihlu Wang
*/
public abstract sealed class WebCalendarNode
permits WebCalendarEvent {
/**
* <p>
* Categories, is used to specify categories or subtypes of the calendar component. The categories are useful in
* searching for a calendar component of a particular type and category.
* </p>
*
* Template:
* <pre>CATEGORIES:APPOINTMENT,EDUCATION</pre>
* <pre>CATEGORIES:MEETING</pre>
*
* <p>Referenced from <a href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-2-categories.html"
* >RFC-5545 - 3.8.1.2. Categories</a></p>
*/
protected List<String> categories;
/**
* <p>
* Classification, An access classification is only one component of the general security system within a calendar
* application. It provides a method of capturing the scope of the access the calendar owner intends for information
* within an individual calendar entry.
* </p>
*
* Template:
* <pre>CLASS:protected</pre>
*
* <p>Referenced from <a href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-3-classification.html"
* >RFC-5545 - 3.8.1.3. Classification</a></p>
*/
protected Classification classification;
/**
* <p>
* Comment, is used to specify a comment to the calendar user.
* </p>
*
* Template:
* <pre>
* COMMENT:The meeting really needs to include both ourselves
* and the customer. We can't hold this meeting without them.
* As a matter of fact\, the venue for the meeting ought to be at
* their site. - - John
* </pre>
*
* <p>Referenced from <a href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-4-comment.html"
* >RFC-5545 - 3.8.1.4. Comment</a></p>
*/
protected String comment;
/**
* <p>
* Description is used in the {@link WebCalendarEvent} and <em>to-do</em> to capture lengthy extual descriptions associated with
* the activity.
* </p>
* <p>
* Description is used in the <em>Journal</em> calendar component to capture one or more textual journal entries.
* </p>
* <p>
* Description is used in the <em>Alarm</em> calendar component to capture the display text for a DISPLAY category of
* alarm, and to capture the body text for an EMAIL category of alarm.
* </p>
*
* Template:
* <pre>
* DESCRIPTION:Meeting to provide technical review for "Phoenix"
* design.\nHappy Face Conference Room. Phoenix design team
* MUST attend this meeting.\nRSVP to team leader.
* </pre>
*
* <p>Referenced from <a href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-5-description.html"
* >RFC-5545 - 3.8.1.5. Description</a></p>
*/
protected String description;
/**
* <p>
* Location, Specific venues such as conference or meeting rooms may be explicitly specified using this property.
* </p>
*
* <p>
* <b>Note</b><br>
* This location has not implement the URI of the location.
* </p>
*
* Template:
* <pre>LOCATION:Conference Room - F123\, Bldg. 002</pre>
* <pre>LOCATION;ALTREP="http://xyzcorp.com/conf-rooms/f123.vcf":
* Conference Room - F123\, Bldg. 002</pre>
*
* <p>Referenced from <a href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-7-location.html"
* >RFC-5545 - 3.8.1.7. Location</a></p>
*/
protected String location;
/**
* <p>
* Percent Complete, is a positive integer between 0 and 100. A value of "0" indicates the to-do has not yet been
* started. A value of "100" indicates that the to-do has been completed. Integer values in between indicate the
* percent partially complete.
* </p>
*
* Template:
* <pre>PERCENT-COMPLETE:39</pre>
*
* <p>Referenced from <a href="https://icalendar.org/iCalendar-RFC-5545/3-8-1-8-percent-complete.html"
* >RFC-5545 - 3.8.1.8. Percent Complete</a></p>
*/
protected Integer percentComplete;
/**
* <p>
* Priority, is specified as an integer in the range 0 to 9. A value of 0 specifies an undefined priority. A value
* of 1 is the highest priority. A value of 2 is the second-highest priority. Subsequent numbers specify a
* decreasing ordinal priority. A value of 9 is the lowest priority.
* </p>
*
* Example:
* <ul style="list-style-type: none;">
* <li>
* The following is an example of a property with the highest priority:
* <pre>PRIORITY:1</pre>
* </li>
* <li>
* The following is an example of a property with a next highest priority:
* <pre>PRIORITY:2</pre>
* </li>
* </ul>
*/
protected Integer priority;
/**
* <p>
* Summary, is used to capture a short, one-line summary about the activity or journal entry.
* </p>
*
* Example:
* <pre>SUMMARY:Department Party</pre>
*/
protected String summary;
// /**
// * Completed defines the date and time that a to-do was actually completed.
// *
// * <p>
// * Example:
// * <pre>COMPLETED:19960401T150000Z</pre>
// * </p>
// */
// protected DateTime completed;
//
// /**
// * Set the specific time of the completion of this to-do event.
// *
// * @param completed The complete time.
// */
// protected void setCompleted(DateTime completed) {
// this.completed = completed;
// }
/**
* End defines the date and time by which the event ends.
*
* Example:
* <pre>DTEND:19960401T150000Z</pre>
* <pre>DTEND;VALUE=DATE:19980704</pre>
*/
protected LocalDateTime end;
/**
* Start defines the start date and time for the event.
*
* Example:
* <pre>DTSTART:19980118T073000Z</pre>
* <pre>DTSTART;VALUE=DATE:19980118</pre>
*/
protected LocalDateTime start;
/**
* Duration may be used to specify a duration of the event, instead of an explicit end DATE-TIME.
*
* Example:
* <pre>DURATION:PT100000S</pre>
*/
protected Duration duration;
/**
* URL may be used in a calendar component to convey a location where a more dynamic rendition of the calendar
* information associated with the calendar component can be found.
*
* Example:
* <pre>URL:http://example.com/pub/calendars/jsmith/mytime.ics</pre>
*/
protected String url;
protected String uid;
protected String domainName;
protected String timezone;
//
// Constructors
//
protected WebCalendarNode() {
this.categories = new ArrayList<>();
}
public WebCalendarNode setDomainName(String domainName) {
this.domainName = domainName;
return this;
}
//
// Protected methods
//
protected String resolveCategories() {
var builder = new StringBuilder();
if (categories != null && !categories.isEmpty()) {
categories.forEach(item -> builder.append(item).append(","));
return builder.substring(0, builder.length() - 1);
}
return builder.toString();
}
//
// Abstract methods
//
public abstract String resolve();
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.codecrafters.webcal.config;
/**
* WebCalClassification
*
* @author Zihlu Wang
*/
public enum Classification {
PUBLIC("PUBLIC"),
PRIVATE("PRIVATE"),
CONFIDENTIAL("CONFIDENTIAL"),
;
private final String classification;
Classification(String classification) {
this.classification = classification;
}
public String getClassification() {
return classification;
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2023 CodeCraftersCN.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.org.codecrafters.webcal.test;
import cn.org.codecrafters.webcal.WebCalendar;
import cn.org.codecrafters.webcal.WebCalendarEvent;
import cn.org.codecrafters.webcal.config.Classification;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* TestWebCalendar
*
* @author Zihlu Wang
*/
@Slf4j
public class TestWebCalendar {
@Test
void testWebCalendar() {
var calendar = new WebCalendar();
calendar.setCompanyName("Code Crafters")
.setDomainName("codecrafters.org.cn")
.setName("Code Crafters SPECIAL EVENT")
.setProductName("Code Crafters SPECIAL EVENT");
calendar.addEvent(new WebCalendarEvent()
.setClassification(Classification.PUBLIC)
.setStart(LocalDateTime.of(2023, 8, 6, 0, 0, 0))
.setEnd(LocalDateTime.of(2023, 8, 6, 8, 0, 0))
.setLocation("湖南省长沙市天心区碧云路60号")
.setUid(UUID.randomUUID().toString())
.setTimezone("Asia/Hong_Kong"));
System.out.println(calendar.resolve());
}
}