From 10ea200a2ef74b1fe486005309bf77c0835952f1 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sat, 10 Jan 2026 01:10:24 +0100 Subject: [PATCH] refactoring Events for better journal Signed-off-by: Stephan Richter --- .../umbrella/messagebus/events/Event.java | 47 ++++++++++++++++--- .../messagebus/events/ProjectEvent.java | 7 ++- .../umbrella/messagebus/events/TaskEvent.java | 7 ++- .../srsoftware/umbrella/core/model/Task.java | 14 +++++- .../umbrella/journal/JournalDb.java | 1 + .../umbrella/journal/JournalModule.java | 17 ++++--- .../srsoftware/umbrella/journal/SqliteDb.java | 18 ++++--- .../umbrella/project/ProjectModule.java | 4 +- .../srsoftware/umbrella/task/TaskModule.java | 29 ++---------- 9 files changed, 89 insertions(+), 55 deletions(-) diff --git a/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/Event.java b/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/Event.java index 9bf1697..4abdc2a 100644 --- a/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/Event.java +++ b/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/Event.java @@ -1,35 +1,70 @@ /* © SRSoftware 2025 */ package de.srsoftware.umbrella.messagebus.events; -import static de.srsoftware.umbrella.core.Constants.USER; +import static de.srsoftware.umbrella.core.Constants.*; +import static java.text.MessageFormat.format; +import static java.util.Optional.*; import de.srsoftware.tools.Mappable; import de.srsoftware.umbrella.core.model.UmbrellaUser; +import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.json.JSONObject; + public abstract class Event { public enum EventType { CREATE, UPDATE, DELETE; - } - private UmbrellaUser initiator; - private String module; - private Payload payload; - private EventType eventType; + private final UmbrellaUser initiator; + private final String module; + private final Payload payload; + private final EventType eventType; + private final Map oldData; + public Event(UmbrellaUser initiator, String module, Payload payload, EventType type){ this.initiator = initiator; this.module = module; this.payload = payload; this.eventType = type; + this.oldData = null; + } + + public Event(UmbrellaUser initiator, String module, Payload payload, Map oldData){ + this.initiator = initiator; + this.module = module; + this.payload = payload; + this.eventType = EventType.UPDATE; + this.oldData = oldData; } public abstract String describe(); + private String diff(Map a, Map b){ + // TODO: replace by better implementation + return format("{0}\n→\n{1}",dropMarkdown(a),dropMarkdown(b)); + } + + private Map dropMarkdown(Map map) { + var result = new HashMap(); + for (var entry : map.entrySet()){ + var v = entry.getValue(); + if (v instanceof Map m && m.containsKey(RENDERED) && m.get(SOURCE) instanceof String s) v=s; + result.put(entry.getKey(),v); + } + return result; + } + + public Optional diff(){ + return oldData == null ? empty() : of(diff(oldData,payload.toMap())); + } + + public String eventType(){ return eventType.toString(); } diff --git a/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/ProjectEvent.java b/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/ProjectEvent.java index a768a52..c567480 100644 --- a/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/ProjectEvent.java +++ b/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/ProjectEvent.java @@ -5,6 +5,7 @@ import static de.srsoftware.umbrella.core.Constants.PROJECT; import de.srsoftware.umbrella.core.model.Project; import de.srsoftware.umbrella.core.model.UmbrellaUser; +import java.util.Map; public class ProjectEvent extends Event{ @@ -12,9 +13,13 @@ public class ProjectEvent extends Event{ super(initiator, PROJECT, project, type); } + public ProjectEvent(UmbrellaUser initiator, Project project, Map oldData){ + super(initiator, PROJECT, project, oldData); + } + @Override public String describe() { - return "[TODO: ProjectEvent.describe]"; + return diff().orElse("[TODO: ProjectEvent.describe]"); } @Override diff --git a/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/TaskEvent.java b/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/TaskEvent.java index 5a2090d..b2b63d1 100644 --- a/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/TaskEvent.java +++ b/bus/src/main/java/de/srsoftware/umbrella/messagebus/events/TaskEvent.java @@ -5,6 +5,7 @@ import static de.srsoftware.umbrella.core.Constants.TASK; import de.srsoftware.umbrella.core.model.Task; import de.srsoftware.umbrella.core.model.UmbrellaUser; +import java.util.Map; public class TaskEvent extends Event{ @@ -12,9 +13,13 @@ public class TaskEvent extends Event{ super(initiator, TASK, task, type); } + public TaskEvent(UmbrellaUser initiator, Task task, Map oldData){ + super(initiator, TASK, task, oldData); + } + @Override public String describe() { - return "[TODO: TaskEvent.describe()]"; + return diff().orElse("[TODO: TaskEvent.describe()]"); } @Override diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Task.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Task.java index 0e77071..7fa25ee 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Task.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Task.java @@ -27,7 +27,7 @@ public class Task implements Mappable { private boolean noIndex, showClosed; private final Map members; private final Set dirtyFields = new HashSet<>(); - + private final Set tags = new HashSet<>(); public Task (long id, long projectId, Long parentTaskId, String name, String description, int status, Double estimatedTime, LocalDate start, LocalDate dueDate, boolean showClosed, boolean noIndex, Map members, int priority){ this.id = id; @@ -218,6 +218,16 @@ public class Task implements Mappable { return status; } + public Task tags(Collection newValue){ + tags.clear(); + tags.addAll(newValue); + return this; + } + + public Set tags(){ + return tags; + } + @Override public Map toMap() { var map = new HashMap(); @@ -240,7 +250,7 @@ public class Task implements Mappable { map.put(REQUIRED_TASKS_IDS,requiredTasksIds); map.put(SHOW_CLOSED,showClosed); map.put(TOTAL_PRIO,totalPrio()); - + map.put(TAGS,tags); return map; } diff --git a/journal/src/main/java/de/srsoftware/umbrella/journal/JournalDb.java b/journal/src/main/java/de/srsoftware/umbrella/journal/JournalDb.java index 14c443c..69a08f0 100644 --- a/journal/src/main/java/de/srsoftware/umbrella/journal/JournalDb.java +++ b/journal/src/main/java/de/srsoftware/umbrella/journal/JournalDb.java @@ -1,3 +1,4 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.journal; import de.srsoftware.umbrella.messagebus.events.Event; diff --git a/journal/src/main/java/de/srsoftware/umbrella/journal/JournalModule.java b/journal/src/main/java/de/srsoftware/umbrella/journal/JournalModule.java index 066312f..39ab069 100644 --- a/journal/src/main/java/de/srsoftware/umbrella/journal/JournalModule.java +++ b/journal/src/main/java/de/srsoftware/umbrella/journal/JournalModule.java @@ -1,18 +1,17 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.journal; -import de.srsoftware.configuration.Configuration; -import de.srsoftware.umbrella.core.BaseHandler; -import de.srsoftware.umbrella.core.ModuleRegistry; -import de.srsoftware.umbrella.core.model.Message; -import de.srsoftware.umbrella.messagebus.EventListener; -import de.srsoftware.umbrella.messagebus.MessageBus; -import de.srsoftware.umbrella.messagebus.events.Event; - import static de.srsoftware.umbrella.core.ConnectionProvider.connect; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; import static de.srsoftware.umbrella.journal.Constants.CONFIG_DATABASE; import static de.srsoftware.umbrella.messagebus.MessageBus.messageBus; -import static java.lang.System.Logger.Level.INFO; +import static java.lang.System.Logger.Level.DEBUG; + +import de.srsoftware.configuration.Configuration; +import de.srsoftware.umbrella.core.BaseHandler; +import de.srsoftware.umbrella.core.ModuleRegistry; +import de.srsoftware.umbrella.messagebus.EventListener; +import de.srsoftware.umbrella.messagebus.events.Event; public class JournalModule extends BaseHandler implements EventListener { diff --git a/journal/src/main/java/de/srsoftware/umbrella/journal/SqliteDb.java b/journal/src/main/java/de/srsoftware/umbrella/journal/SqliteDb.java index 1ca82ae..7c27596 100644 --- a/journal/src/main/java/de/srsoftware/umbrella/journal/SqliteDb.java +++ b/journal/src/main/java/de/srsoftware/umbrella/journal/SqliteDb.java @@ -1,13 +1,6 @@ +/* © SRSoftware 2025 */ package de.srsoftware.umbrella.journal; -import de.srsoftware.tools.jdbc.Query; -import de.srsoftware.umbrella.core.BaseDb; -import de.srsoftware.umbrella.core.exceptions.UmbrellaException; -import de.srsoftware.umbrella.messagebus.events.Event; - -import java.sql.Connection; -import java.sql.SQLException; - import static de.srsoftware.tools.jdbc.Query.insertInto; import static de.srsoftware.umbrella.core.Constants.*; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException; @@ -15,6 +8,11 @@ import static de.srsoftware.umbrella.journal.Constants.ERROR_WRITE_EVENT; import static de.srsoftware.umbrella.journal.Constants.TABLE_JOURNAL; import static java.text.MessageFormat.format; +import de.srsoftware.umbrella.core.BaseDb; +import de.srsoftware.umbrella.messagebus.events.Event; +import java.sql.Connection; +import java.sql.SQLException; + public class SqliteDb extends BaseDb implements JournalDb{ public SqliteDb(Connection connection) { super(connection); @@ -33,8 +31,8 @@ public class SqliteDb extends BaseDb implements JournalDb{ private void createJournalTable() { var sql = """ CREATE TABLE IF NOT EXISTS {0} ( - {1} INT PRIMARY KEY, - {2} INT, + {1} INTEGER PRIMARY KEY, + {2} INTEGER, {3} VARCHAR(255) NOT NULL, {4} VARCHAR(16) NOT NULL, {5} TEXT diff --git a/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java b/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java index 31de459..43b7e5e 100644 --- a/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java +++ b/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java @@ -14,7 +14,6 @@ import static de.srsoftware.umbrella.core.model.Status.OPEN; import static de.srsoftware.umbrella.core.model.Status.PREDEFINED; import static de.srsoftware.umbrella.messagebus.MessageBus.messageBus; import static de.srsoftware.umbrella.messagebus.events.Event.EventType.CREATE; -import static de.srsoftware.umbrella.messagebus.events.Event.EventType.UPDATE; import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE; import static java.lang.Boolean.TRUE; import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; @@ -210,13 +209,14 @@ public class ProjectModule extends BaseHandler implements ProjectService { private boolean patchProject(HttpExchange ex, long projectId, UmbrellaUser user) throws IOException, UmbrellaException { var project = loadMembers(projectDb.load(projectId)); if (!project.hasMember(user)) throw forbidden("You are not a member of {0}",project.name()); + var old = project.toMap(); var json = json(ex); if (json.has(DROP_MEMBER) && json.get(DROP_MEMBER) instanceof Number id) dropMember(project,id.longValue()); if (json.has(MEMBERS) && json.get(MEMBERS) instanceof JSONObject memberJson) patchMembers(project,memberJson); if (json.has(NEW_MEMBER) && json.get(NEW_MEMBER) instanceof Number num) addMember(project,num.longValue()); project = projectDb.save(project.patch(json), user); - messageBus().dispatch(new ProjectEvent(user,project, UPDATE)); + messageBus().dispatch(new ProjectEvent(user,project, old)); return sendContent(ex,project.toMap()); } diff --git a/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java b/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java index 37d4994..39f4a8d 100644 --- a/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java +++ b/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java @@ -13,7 +13,6 @@ import static de.srsoftware.umbrella.core.model.Permission.*; import static de.srsoftware.umbrella.core.model.Permission.OWNER; import static de.srsoftware.umbrella.messagebus.MessageBus.messageBus; import static de.srsoftware.umbrella.messagebus.events.Event.EventType.CREATE; -import static de.srsoftware.umbrella.messagebus.events.Event.EventType.UPDATE; import static de.srsoftware.umbrella.project.Constants.PERMISSIONS; import static de.srsoftware.umbrella.task.Constants.*; import static java.lang.System.Logger.Level.WARNING; @@ -42,22 +41,6 @@ import org.json.JSONObject; public class TaskModule extends BaseHandler implements TaskService { - private static class TaggedTask extends Task{ - private final Collection tags; - - public TaggedTask(Task task, Collection tags) { - super(task.id(), task.projectId(), task.parentTaskId(), task.name(), task.description(), task.status(), task.estimatedTime(), task.start(), task.dueDate(), task.showClosed(), task.noIndex(), task.members(), task.priority()); - this.tags = tags; - } - - @Override - public Map toMap() { - var map = super.toMap(); - map.put(TAGS,tags); - return map; - } - } - private final TaskDb taskDb; public TaskModule(Configuration config) throws UmbrellaException { @@ -343,10 +326,8 @@ public class TaskModule extends BaseHandler implements TaskService { if (json.has(MEMBERS) && json.get(MEMBERS) instanceof JSONObject memberJson) patchMembers(task, memberJson); if (json.has(NEW_MEMBER) && json.get(NEW_MEMBER) instanceof Number num) addMember(task, num.longValue()); if (json.has(PARENT_TASK_ID) && json.get(PARENT_TASK_ID) instanceof Number ptid && newParentIsSubtask(task, ptid.longValue())) throw forbidden("Task must not be sub-task of itself."); - var curr = taskDb.save(task.patch(json)).toMap(); - var diff = diff(old,curr); - var tagList = tagService().getTags(TASK, taskId, user); - messageBus().dispatch(new TaskEvent(user,new TaggedTask(task,tagList), UPDATE)); + task = taskDb.save(task.patch(json)).tags(tagService().getTags(TASK, taskId, user)); + messageBus().dispatch(new TaskEvent(user, task, old)); return sendContent(ex, task); } @@ -407,8 +388,8 @@ public class TaskModule extends BaseHandler implements TaskService { if ((tagList == null || tagList.isEmpty())) tagList = tagService().getTags(PROJECT, projectId, user); if (tagList != null && !tagList.isEmpty()) tagService().save(TASK, task.id(), null, tagList); task = loadMembers(task); - - messageBus().dispatch(new TaskEvent(user,new TaggedTask(task,tagList), CREATE)); + task.tags(tagList); + messageBus().dispatch(new TaskEvent(user, task, CREATE)); return sendContent(ex, task); } @@ -449,6 +430,6 @@ public class TaskModule extends BaseHandler implements TaskService { } private Map addTags(Map taskList, Map> tags) { - return taskList.values().stream().map(task -> new TaggedTask(task, tags.get(task.id()))).collect(Collectors.toMap(Task::id, t -> t)); + return taskList.values().stream().map(task -> task.tags(tags.get(task.id()))).collect(Collectors.toMap(Task::id, t -> t)); } }