refactoring objects in preparation for:

refactoring journal database to contain object ids for:
querying journal for objects
This commit is contained in:
2026-07-27 09:22:50 +02:00
parent c8ad603fca
commit e614880d71
11 changed files with 97 additions and 74 deletions
@@ -6,6 +6,7 @@ import static java.util.Optional.*;
import de.srsoftware.tools.Diff; import de.srsoftware.tools.Diff;
import de.srsoftware.tools.Mappable; import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.model.ObjectWithId;
import de.srsoftware.umbrella.core.model.Translatable; import de.srsoftware.umbrella.core.model.Translatable;
import de.srsoftware.umbrella.core.model.UmbrellaUser; import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.util.Collection; import java.util.Collection;
@@ -14,15 +15,15 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import org.json.JSONObject; import org.json.JSONObject;
public abstract class Event<Payload extends Mappable> { public abstract class Event<Payload extends ObjectWithId> {
public enum EventType { public enum EventType {
CREATE, CREATE,
MEMBER_ADDED, MEMBER_ADDED,
UPDATE, UPDATE,
DELETE; DELETE;
}
}
private final UmbrellaUser initiator; private final UmbrellaUser initiator;
private final String module; private final String module;
private final Payload payload; private final Payload payload;
@@ -98,6 +99,10 @@ public abstract class Event<Payload extends Mappable> {
return module; return module;
} }
public long objectId() {
return payload.id();
};
protected Map<String, Object> oldData() { protected Map<String, Object> oldData() {
return oldData; return oldData;
} }
@@ -36,6 +36,10 @@ public class ItemEvent extends Event<Item>{
return t("{user} added \"{item}\" to \"{location}\"", USER,initiator().name(), ITEM, payload().name(), LOCATION, loc); return t("{user} added \"{item}\" to \"{location}\"", USER,initiator().name(), ITEM, payload().name(), LOCATION, loc);
} }
@Override
public long objectId() {
return payload().id();
}
@Override @Override
public Translatable subject() { public Translatable subject() {
@@ -78,6 +78,11 @@ public class ProjectEvent extends Event<Project>{
return t("You can view/edit this project at {base_url}/project/{id}/view",ID,payload().id()); return t("You can view/edit this project at {base_url}/project/{id}/view",ID,payload().id());
} }
@Override
public long objectId() {
return payload().id();
}
@Override @Override
public Translatable subject() { public Translatable subject() {
return switch (eventType()){ return switch (eventType()){
@@ -62,6 +62,11 @@ public class TransactionEvent extends Event<Transaction> {
return t("You can view/edit this transaction at {base_url}/account/{id}", ID, payload().accountId()); return t("You can view/edit this transaction at {base_url}/account/{id}", ID, payload().accountId());
} }
@Override
public long objectId() {
return payload().id();
}
@Override @Override
public Translatable subject() { public Translatable subject() {
var user = initiator().name(); var user = initiator().name();
@@ -68,6 +68,10 @@ public class WikiEvent extends Event<WikiPage>{
return t("You can view/edit this wiki page at {base_url}/wiki/{id}/view",ID,payload().id()); return t("You can view/edit this wiki page at {base_url}/wiki/{id}/view",ID,payload().id());
} }
@Override
public long objectId() {
return payload().id();
}
@Override @Override
public Translatable subject() { public Translatable subject() {
@@ -4,7 +4,6 @@ package de.srsoftware.umbrella.core.model;
import static de.srsoftware.umbrella.core.Util.mapMarkdown; import static de.srsoftware.umbrella.core.Util.mapMarkdown;
import static de.srsoftware.umbrella.core.constants.Field.*; import static de.srsoftware.umbrella.core.constants.Field.*;
import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.api.Owner; import de.srsoftware.umbrella.core.api.Owner;
import de.srsoftware.umbrella.core.constants.Field; import de.srsoftware.umbrella.core.constants.Field;
import java.sql.ResultSet; import java.sql.ResultSet;
@@ -12,16 +11,16 @@ import java.sql.SQLException;
import java.util.*; import java.util.*;
import org.json.JSONObject; import org.json.JSONObject;
public class Item implements Mappable { public class Item extends ObjectWithId {
private long id, ownerNumber; // id is the database key, number the owner-relative id private long ownerNumber; // id is the database key, number the owner-relative id
private Owner owner; private final Owner owner;
private String code, description, name; private String code, description, name;
private Location location; private Location location;
private Collection<Property> properties; private final Collection<Property> properties;
private Set<String> dirtyFields = new HashSet<>(); private final Set<String> dirtyFields = new HashSet<>();
public Item(long id, Owner owner, long ownerNumber, Location location, String code, String name, String description) { public Item(long id, Owner owner, long ownerNumber, Location location, String code, String name, String description) {
this.id = id; super(id);
this.owner = owner; this.owner = owner;
this.ownerNumber = ownerNumber; this.ownerNumber = ownerNumber;
this.location = location; this.location = location;
@@ -48,15 +47,6 @@ public class Item implements Mappable {
return !dirtyFields.isEmpty(); return !dirtyFields.isEmpty();
} }
public long id(){
return id;
}
public Item id(long newVal) {
id = newVal;
return this;
}
public Location location(){ public Location location(){
return location; return location;
} }
@@ -121,14 +111,13 @@ public class Item implements Mappable {
@Override @Override
public Map<String, Object> toMap() { public Map<String, Object> toMap() {
var map = new HashMap<String,Object>(); var map = super.map(
map.put(OWNER,owner.toMap()); OWNER,owner.toMap(),
map.put(ID,id); LOCATION,location.toMap(),
map.put(LOCATION,location.toMap()); Field.CODE,code,
map.put(Field.CODE,code); NAME,name,
map.put(NAME,name); DESCRIPTION,mapMarkdown(description),
map.put(DESCRIPTION,mapMarkdown(description)); OWNER_NUMBER,ownerNumber);
map.put(OWNER_NUMBER,ownerNumber);
if (properties != null) map.put(PROPERTIES,properties.stream().map(Property::toMap).toList()); if (properties != null) map.put(PROPERTIES,properties.stream().map(Property::toMap).toList());
return map; return map;
} }
@@ -0,0 +1,30 @@
package de.srsoftware.umbrella.core.model;
import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.constants.Field;
import java.security.InvalidParameterException;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class ObjectWithId implements Mappable {
private final long id;
public ObjectWithId(long id){
this.id = id;
}
public long id(){
return id;
}
protected Map<String, Object> map(Object ... keysAndValues) {
if (keysAndValues.length % 2 != 0) throw new InvalidParameterException("Expected even number of keys and parameters!");
var map = new HashMap<String, Object>();
map.put(Field.ID,id);
for (var idx = 0; idx<keysAndValues.length; idx+=2) map.put(keysAndValues[idx].toString(),keysAndValues[idx+1]);
return map;
}
}
@@ -6,27 +6,25 @@ import static de.srsoftware.umbrella.core.Util.mapMarkdown;
import static de.srsoftware.umbrella.core.constants.Field.*; import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.model.Status.PREDEFINED; import static de.srsoftware.umbrella.core.model.Status.PREDEFINED;
import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.constants.Field; import de.srsoftware.umbrella.core.constants.Field;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.*;
import org.json.JSONObject; import org.json.JSONObject;
public class Project implements Mappable { public class Project extends ObjectWithId {
private final Map<Long,Member> members; private final Map<Long,Member> members;
private final Collection<Status> allowedStates; private final Collection<Status> allowedStates;
private boolean showClosed; private boolean showClosed;
private Long companyId; private Long companyId;
private int status; private int status;
private String name; private String name;
private final long id;
private String description; private String description;
private final Set<String> dirtyFields = new HashSet<>(); private final Set<String> dirtyFields = new HashSet<>();
private final Map<String,String> tagColors = new HashMap<>(); private final Map<String,String> tagColors = new HashMap<>();
public Project(long id, String name, String description, int status, Long companyId, boolean showClosed, Map<Long,Member> members, Collection<Status> allowedStates) { public Project(long id, String name, String description, int status, Long companyId, boolean showClosed, Map<Long,Member> members, Collection<Status> allowedStates) {
this.id = id; super(id);
this.name = name; this.name = name;
this.description = description; this.description = description;
this.status = status; this.status = status;
@@ -67,10 +65,6 @@ public class Project implements Mappable {
return members.containsKey(user.id()); return members.containsKey(user.id());
} }
public long id(){
return id;
}
public boolean isDirty() { public boolean isDirty() {
return !dirtyFields.isEmpty(); return !dirtyFields.isEmpty();
} }
@@ -126,23 +120,21 @@ public class Project implements Mappable {
@Override @Override
public Map<String, Object> toMap() { public Map<String, Object> toMap() {
var map = new HashMap<String, Object>();
var memberMap = new HashMap<Long,Map<String,Object>>(); var memberMap = new HashMap<Long,Map<String,Object>>();
if (members != null) for (var entry : members.entrySet()){ if (members != null) for (var entry : members.entrySet()){
memberMap.put(entry.getKey(),entry.getValue().toMap()); memberMap.put(entry.getKey(),entry.getValue().toMap());
} }
map.put(ID,id);
map.put(NAME,name);
map.put(DESCRIPTION,mapMarkdown(description));
map.put(STATUS,status);
map.put(COMPANY_ID,companyId);
map.put(SHOW_CLOSED,showClosed);
map.put(MEMBERS,memberMap);
var stateMap = new HashMap<Integer,String>(); var stateMap = new HashMap<Integer,String>();
for (var state : allowedStates) stateMap.put(state.code(),state.name()); for (var state : allowedStates) stateMap.put(state.code(),state.name());
map.put(Field.ALLOWED_STATES,stateMap); return super.map(
map.put(TAG_COLORS,tagColors); NAME,name,
return map; DESCRIPTION,mapMarkdown(description),
STATUS,status,
COMPANY_ID,companyId,
SHOW_CLOSED,showClosed,
MEMBERS,memberMap,
Field.ALLOWED_STATES,stateMap,
TAG_COLORS,tagColors);
} }
@Override @Override
@@ -14,17 +14,17 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
public class Transaction implements Mappable { public class Transaction extends ObjectWithId {
private long accountId, id; private final long accountId;
private LocalDateTime date; private LocalDateTime date;
private IdOrString source, destination; private IdOrString source, destination;
private double amount; private double amount;
private String purpose; private String purpose;
private Set<String> tags; private final Set<String> tags;
private HashSet<String> dirtyFields = new HashSet<>(); private final HashSet<String> dirtyFields = new HashSet<>();
public Transaction(long id, long accountId, LocalDateTime date, IdOrString source, IdOrString destination, double amount, String purpose, Set<String> tags){ public Transaction(long id, long accountId, LocalDateTime date, IdOrString source, IdOrString destination, double amount, String purpose, Set<String> tags){
this.id = id; super(id);
this.accountId = accountId; this.accountId = accountId;
this.date = date; this.date = date;
this.source = source; this.source = source;
@@ -77,10 +77,6 @@ public class Transaction implements Mappable {
return this; return this;
} }
public long id(){
return id;
}
public boolean isDirty(){ public boolean isDirty(){
return !dirtyFields.isEmpty(); return !dirtyFields.isEmpty();
} }
@@ -123,8 +119,7 @@ public class Transaction implements Mappable {
@Override @Override
public Map<String, Object> toMap() { public Map<String, Object> toMap() {
return Map.of( return super.map(
Field.ID, id,
Field.ACCOUNT, accountId, Field.ACCOUNT, accountId,
Field.DATE, date.toLocalDate(), Field.DATE, date.toLocalDate(),
Field.SOURCE, source.toMap(), Field.SOURCE, source.toMap(),
@@ -10,16 +10,14 @@ import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.core.model.Translatable.t; import static de.srsoftware.umbrella.core.model.Translatable.t;
import static java.lang.String.join; import static java.lang.String.join;
import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.api.UserService; import de.srsoftware.umbrella.core.api.UserService;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.*;
import org.json.JSONObject; import org.json.JSONObject;
public class WikiPage implements Mappable { public class WikiPage extends ObjectWithId {
private final long id;
private String title; private String title;
private int version; private int version;
private final Set<Integer> versions = new TreeSet<>(); private final Set<Integer> versions = new TreeSet<>();
@@ -29,7 +27,7 @@ public class WikiPage implements Mappable {
private boolean guestAllowed = false; private boolean guestAllowed = false;
public WikiPage(long id, String title, int version, String content) { public WikiPage(long id, String title, int version, String content) {
this.id = id; super(id);
this.version = version; this.version = version;
this.content = content; this.content = content;
this.title = title; this.title = title;
@@ -60,10 +58,6 @@ public class WikiPage implements Mappable {
dirtyFields.add(GUEST_ALLOWED); dirtyFields.add(GUEST_ALLOWED);
} }
public long id(){
return id;
}
public boolean isDirty(String field) { public boolean isDirty(String field) {
return dirtyFields.contains(field); return dirtyFields.contains(field);
} }
@@ -146,8 +140,7 @@ public class WikiPage implements Mappable {
var memberMap = new HashMap<Long,Map<String,Object>>(); var memberMap = new HashMap<Long,Map<String,Object>>();
for (var entry : members.entrySet()) memberMap.put(entry.getKey(),entry.getValue().toMap()); for (var entry : members.entrySet()) memberMap.put(entry.getKey(),entry.getValue().toMap());
return Map.of( return map(
ID,id,
CONTENT,mapMarkdown(content), CONTENT,mapMarkdown(content),
GUEST_ALLOWED,guestAllowed, GUEST_ALLOWED,guestAllowed,
MEMBERS,memberMap, MEMBERS,memberMap,
@@ -38,11 +38,12 @@ public class SqliteDb extends BaseDb implements JournalDb{
{2} LONG NOT NULL, {2} LONG NOT NULL,
{3} INTEGER, {3} INTEGER,
{4} VARCHAR(255) NOT NULL, {4} VARCHAR(255) NOT NULL,
{5} VARCHAR(16) NOT NULL, {5} VARCHAR(255),
{6} TEXT {6} VARCHAR(16) NOT NULL,
{7} TEXT
); );
"""; """;
sql = format(sql,TABLE_JOURNAL,ID,TIMESTAMP,USER_ID,MODULE,ACTION,DESCRIPTION); sql = format(sql,TABLE_JOURNAL,ID,TIMESTAMP,USER_ID,MODULE,ENTITY_ID,ACTION,DESCRIPTION);
try { try {
db.prepareStatement(sql).execute(); db.prepareStatement(sql).execute();
} catch (SQLException e) { } catch (SQLException e) {
@@ -54,8 +55,8 @@ public class SqliteDb extends BaseDb implements JournalDb{
public void logEvent(Event<?> event) { public void logEvent(Event<?> event) {
try { try {
var timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC); var timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
insertInto(TABLE_JOURNAL,TIMESTAMP,USER_ID,MODULE,ACTION,DESCRIPTION) insertInto(TABLE_JOURNAL,TIMESTAMP,USER_ID,MODULE,ENTITY_ID,ACTION,DESCRIPTION)
.values(timestamp,event.initiator().id(), event.module(), event.eventType(), event.describe()) .values(timestamp,event.initiator().id(), event.module(), event.objectId(), event.eventType(), event.describe())
.execute(db).close(); .execute(db).close();
} catch (SQLException e) { } catch (SQLException e) {
throw databaseException(ERROR_WRITE_EVENT,event.eventType(),event.initiator().name()); throw databaseException(ERROR_WRITE_EVENT,event.eventType(),event.initiator().name());