CalendarSyncService.java

/*
 * BSD 2-Clause License
 * 
 * Copyright (c) 2022, [Aleksandra Serba, Marcin Czerniak, Bartosz Wawrzyniak, Adrian Antkowiak]
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package dev.vernite.vernite.integration.calendar;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import dev.vernite.vernite.event.Event;
import dev.vernite.vernite.event.EventFilter;
import dev.vernite.vernite.event.EventService;
import dev.vernite.vernite.utils.ObjectNotFoundException;
import net.fortuna.ical4j.data.CalendarOutputter;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.property.CalScale;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.model.property.Version;

@Service
@Component
public class CalendarSyncService {
    private static final java.util.Date FROM = new java.util.Date(0);

    @Autowired
    private EventService eventService;
    @Autowired
    private CalendarIntegrationRepository repository;

    public byte[] handleCalendar(String key) {
        java.util.Date to = java.util.Date.from(Instant.now().plus(1000, ChronoUnit.DAYS));
        CalendarIntegration integration = repository.findByKey(key).orElseThrow(ObjectNotFoundException::new);
        Set<Event> events;
        Calendar calendar = new Calendar();
        if (integration.getProject() == null) {
            events = eventService.getUserEvents(integration.getUser(), FROM, to, new EventFilter());
            calendar.getProperties()
                    .add(new CalendarName("Vernite - " + integration.getUser().getUsername() + " user calendar"));
        } else {
            events = eventService.getProjectEvents(integration.getProject(), FROM, to, new EventFilter());
            calendar.getProperties()
                    .add(new CalendarName("Vernite - " + integration.getProject().getName() + " project calendar"));
        }
        calendar.getProperties().add(new ProdId("-//Vernite//EN"));
        calendar.getProperties().add(Version.VERSION_2_0);
        calendar.getProperties().add(CalScale.GREGORIAN);
        for (Event event : events) {
            VEvent calEvent;
            if (event.getStartDate() == null) {
                calEvent = new VEvent(new Date(event.getEndDate()), event.getName());
            } else {
                calEvent = new VEvent(new DateTime(event.getStartDate()), new DateTime(event.getEndDate()), event.getName());
            }
            calEvent.getProperties().add(new Uid(String.format("project_%d_event_%d_%d", event.getProjectId(),
                    event.getType().ordinal(), event.getRelatedId())));
            if (event.getDescription() != null) {
                calEvent.getProperties().add(new Description(event.getDescription()));
            }
            if (event.getLocation() != null) {
                calEvent.getProperties().add(new Location(event.getLocation()));
            }
            calendar.getComponents().add(calEvent);
        }
        CalendarOutputter calendarOutputter = new CalendarOutputter();
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        try {
            calendarOutputter.output(calendar, stream);
        } catch (IOException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "IO error");
        }
        return stream.toByteArray();
    }
}