|
|
|
@ -2,6 +2,7 @@
@@ -2,6 +2,7 @@
|
|
|
|
|
package de.srsoftware.cal.db; |
|
|
|
|
|
|
|
|
|
import static de.srsoftware.tools.Optionals.*; |
|
|
|
|
import static de.srsoftware.tools.Result.transform; |
|
|
|
|
import static de.srsoftware.tools.Strings.camelCase; |
|
|
|
|
import static de.srsoftware.tools.jdbc.Condition.equal; |
|
|
|
|
import static de.srsoftware.tools.jdbc.Condition.moreThan; |
|
|
|
@ -10,20 +11,35 @@ import static java.lang.System.Logger.Level.*;
@@ -10,20 +11,35 @@ import static java.lang.System.Logger.Level.*;
|
|
|
|
|
|
|
|
|
|
import de.srsoftware.cal.BaseAppointment; |
|
|
|
|
import de.srsoftware.cal.api.Appointment; |
|
|
|
|
import de.srsoftware.cal.api.Attachment; |
|
|
|
|
import de.srsoftware.cal.api.Link; |
|
|
|
|
import de.srsoftware.tools.Calc; |
|
|
|
|
import de.srsoftware.tools.Error; |
|
|
|
|
import de.srsoftware.tools.Payload; |
|
|
|
|
import de.srsoftware.tools.Result; |
|
|
|
|
import de.srsoftware.tools.jdbc.Query; |
|
|
|
|
import java.net.MalformedURLException; |
|
|
|
|
import java.net.URI; |
|
|
|
|
import java.sql.*; |
|
|
|
|
import java.time.LocalDateTime; |
|
|
|
|
import java.util.*; |
|
|
|
|
|
|
|
|
|
public class MariaDB implements Database { |
|
|
|
|
private static final System.Logger LOG = System.getLogger(MariaDB.class.getSimpleName()); |
|
|
|
|
private static final String ADD_SLUG = "ALTER TABLE appointments ADD slug VARCHAR(255) UNIQUE"; |
|
|
|
|
private static final String APPOINTMENTS = "appointments"; |
|
|
|
|
private static final String AID = "aid"; |
|
|
|
|
private static final System.Logger LOG = System.getLogger(MariaDB.class.getSimpleName()); |
|
|
|
|
private static final String ADD_SLUG = "ALTER TABLE appointments ADD slug VARCHAR(255) UNIQUE"; |
|
|
|
|
private static final String APPOINTMENTS = "appointments"; |
|
|
|
|
private static final String AID = "aid"; |
|
|
|
|
private static final String ALL = "*"; |
|
|
|
|
private static final String KEYWORD = "keyword"; |
|
|
|
|
private static final String APPOINTMENT_TAGS = "appointment_tags"; |
|
|
|
|
private static final String URL = "url"; |
|
|
|
|
private static final String APPOINTMENT_URLS = "appointment_urls"; |
|
|
|
|
private static final String DESCRIPTION = "description"; |
|
|
|
|
private static final String UID = "uid"; |
|
|
|
|
private static final String URLS = "urls"; |
|
|
|
|
private static final String TID = "tid"; |
|
|
|
|
private static final String MIME = "mime"; |
|
|
|
|
private static final String APPOINTMENT_ATTACHMENTS = "appointment_attachments"; |
|
|
|
|
private static Connection connection; |
|
|
|
|
|
|
|
|
|
private MariaDB(Connection conn) throws SQLException { |
|
|
|
@ -57,7 +73,7 @@ public class MariaDB implements Database {
@@ -57,7 +73,7 @@ public class MariaDB implements Database {
|
|
|
|
|
connection.prepareStatement(ADD_SLUG).execute(); |
|
|
|
|
var slugMap = new HashMap<Long, String>(); |
|
|
|
|
LOG.log(DEBUG, "Reading existing appointments…"); |
|
|
|
|
var rs = Query.select("*").from("appointments").exec(connection); |
|
|
|
|
var rs = Query.select(ALL).from("appointments").exec(connection); |
|
|
|
|
while (rs.next()) { |
|
|
|
|
var id = rs.getLong(AID); |
|
|
|
|
var location = nullable(nullIfEmpty(rs.getString("location"))); |
|
|
|
@ -101,7 +117,7 @@ public class MariaDB implements Database {
@@ -101,7 +117,7 @@ public class MariaDB implements Database {
|
|
|
|
|
@Override |
|
|
|
|
public List<Appointment> list(Integer count, Integer offset) throws SQLException { |
|
|
|
|
var list = new ArrayList<Appointment>(); |
|
|
|
|
var results = Query.select("*").from(APPOINTMENTS).sort("start").exec(connection); |
|
|
|
|
var results = Query.select(ALL).from(APPOINTMENTS).sort("start").exec(connection); |
|
|
|
|
while (results.next()) createAppointmentOf(results).optional().ifPresent(list::add); |
|
|
|
|
results.close(); |
|
|
|
|
return list; |
|
|
|
@ -110,21 +126,80 @@ public class MariaDB implements Database {
@@ -110,21 +126,80 @@ public class MariaDB implements Database {
|
|
|
|
|
@Override |
|
|
|
|
public Result<Appointment> loadEvent(long id) { |
|
|
|
|
try { |
|
|
|
|
var rs = Query //
|
|
|
|
|
.select("%s.*".formatted(APPOINTMENTS), "GROUP_CONCAT(keyword) AS tags") |
|
|
|
|
.from(APPOINTMENTS) |
|
|
|
|
.leftJoin(AID, "appointment_tags", AID) |
|
|
|
|
.leftJoin("tid", "tags", "tid") |
|
|
|
|
.groupBy(AID) |
|
|
|
|
.where("%s.%s".formatted(APPOINTMENTS, AID), equal(id)) |
|
|
|
|
.exec(connection); |
|
|
|
|
return rs.next() ? createAppointmentOf(rs) : Error.format("Failed to find appointment with id %s", id); |
|
|
|
|
var rs = Query.select(ALL).from(APPOINTMENTS).where(AID, equal(id)).exec(connection); |
|
|
|
|
Result<Appointment> result = rs.next() ? createAppointmentOf(rs).map(MariaDB::loadExtra) : Error.format("Failed to find appointment with id %s", id); |
|
|
|
|
rs.close(); |
|
|
|
|
return result; |
|
|
|
|
} catch (SQLException e) { |
|
|
|
|
return Error.of("Failed to load appointment with id = %s".formatted(id), e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Result<Appointment> createAppointmentOf(ResultSet results) throws SQLException { |
|
|
|
|
private static Result<Appointment> loadExtra(Result<BaseAppointment> res) { |
|
|
|
|
return loadTags(res).map(MariaDB::loadLinks).map(MariaDB::loadAttachments); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static Result<BaseAppointment> loadTags(Result<BaseAppointment> res) { |
|
|
|
|
if (res.optional().isEmpty()) return transform(res); |
|
|
|
|
BaseAppointment event = res.optional().get(); |
|
|
|
|
var id = event.id(); |
|
|
|
|
try { |
|
|
|
|
var rs = Query.select(KEYWORD).from(APPOINTMENT_TAGS).leftJoin(TID, "tags", TID).where(AID, equal(id)).exec(connection); |
|
|
|
|
while (rs.next()) event.tags(rs.getString(1)); |
|
|
|
|
rs.close(); |
|
|
|
|
return Payload.of(event); |
|
|
|
|
} catch (SQLException e) { |
|
|
|
|
return Error.of("Failed to load tags for appointment %s".formatted(id), e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static Result<BaseAppointment> loadLinks(Result<BaseAppointment> res) { |
|
|
|
|
if (res.optional().isEmpty()) return transform(res); |
|
|
|
|
BaseAppointment event = res.optional().get(); |
|
|
|
|
var id = event.id(); |
|
|
|
|
try { |
|
|
|
|
var rs = Query.select(URL, DESCRIPTION).from(APPOINTMENT_URLS).leftJoin(UID, URLS, UID).where(AID, equal(id)).exec(connection); |
|
|
|
|
while (rs.next()) { |
|
|
|
|
var u = rs.getString(URL); |
|
|
|
|
try { |
|
|
|
|
var url = URI.create(u).toURL(); |
|
|
|
|
var description = rs.getString(DESCRIPTION); |
|
|
|
|
event.addLinks(new Link(url, description)); |
|
|
|
|
} catch (MalformedURLException e) { |
|
|
|
|
LOG.log(WARNING, () -> "Failed to convert %s to URI!".formatted(u)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
rs.close(); |
|
|
|
|
return Payload.of(event); |
|
|
|
|
} catch (SQLException e) { |
|
|
|
|
return Error.of("Failed to load tags for appointment %s".formatted(id), e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static Result<Appointment> loadAttachments(Result<BaseAppointment> res) { |
|
|
|
|
if (res.optional().isEmpty()) return transform(res); |
|
|
|
|
BaseAppointment event = res.optional().get(); |
|
|
|
|
var id = event.id(); |
|
|
|
|
try { |
|
|
|
|
var rs = Query.select(URL, MIME).from(APPOINTMENT_ATTACHMENTS).leftJoin(UID, URLS, UID).where(AID, equal(id)).exec(connection); |
|
|
|
|
while (rs.next()) { |
|
|
|
|
var u = rs.getString(URL); |
|
|
|
|
try { |
|
|
|
|
var url = URI.create(u).toURL(); |
|
|
|
|
var mime = rs.getString(MIME); |
|
|
|
|
event.add(new Attachment(url, mime)); |
|
|
|
|
} catch (MalformedURLException e) { |
|
|
|
|
LOG.log(WARNING, () -> "Failed to convert %s to URI!".formatted(u)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
rs.close(); |
|
|
|
|
return Payload.of(event); |
|
|
|
|
} catch (SQLException e) { |
|
|
|
|
return Error.of("Failed to load tags for appointment %s".formatted(id), e); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Result<BaseAppointment> createAppointmentOf(ResultSet results) throws SQLException { |
|
|
|
|
var id = results.getInt(AID); |
|
|
|
|
var title = results.getString("title"); |
|
|
|
|
var description = results.getString("description"); |
|
|
|
@ -133,10 +208,13 @@ public class MariaDB implements Database {
@@ -133,10 +208,13 @@ public class MariaDB implements Database {
|
|
|
|
|
var end = nullable(results.getTimestamp("end")).map(Timestamp::toLocalDateTime).orElse(null); |
|
|
|
|
var location = results.getString("location"); |
|
|
|
|
var slug = results.getString("slug"); |
|
|
|
|
var tags = nullIfEmpty(results.getString("tags")); |
|
|
|
|
if (slug == null) slug = Calc.hash(start + "@" + location).orElse(null); |
|
|
|
|
var appointment = new BaseAppointment(id, title, description, start, end, location, slug); |
|
|
|
|
if (tags != null) appointment.tags(tags.split(",")); |
|
|
|
|
try { |
|
|
|
|
var tags = nullIfEmpty(results.getString("tags")); |
|
|
|
|
if (tags != null) appointment.tags(tags.split(",")); |
|
|
|
|
} catch (SQLException e) { |
|
|
|
|
} |
|
|
|
|
return Payload.of(appointment); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|