started implementing poll backend

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2026-02-13 09:15:19 +01:00
parent 0db2ad8b0e
commit dbc4525e80
10 changed files with 138 additions and 21 deletions

View File

@@ -23,6 +23,7 @@ dependencies{
implementation(project(":markdown"))
implementation(project(":messages"))
implementation(project(":notes"))
implementation(project(":poll"))
implementation(project(":project"))
implementation(project(":stock"))
implementation(project(":tags"))

View File

@@ -23,6 +23,7 @@ import de.srsoftware.umbrella.markdown.MarkdownApi;
import de.srsoftware.umbrella.message.MessageSystem;
import de.srsoftware.umbrella.messagebus.MessageApi;
import de.srsoftware.umbrella.notes.NoteModule;
import de.srsoftware.umbrella.poll.PollModule;
import de.srsoftware.umbrella.project.ProjectModule;
import de.srsoftware.umbrella.stock.StockModule;
import de.srsoftware.umbrella.tags.TagModule;
@@ -81,6 +82,7 @@ public class Application {
new MarkdownApi().bindPath("/api/markdown").on(server);
new NoteModule(config).bindPath("/api/notes").on(server);
new StockModule(config).bindPath("/api/stock").on(server);
new PollModule(config).bindPath("/api/poll").on(server);
new ProjectModule(config).bindPath("/api/project").on(server);
new ProjectLegacy(config).bindPath("/legacy/project").on(server);
new TaskModule(config).bindPath("/api/task").on(server);

View File

@@ -12,6 +12,7 @@ public class ModuleRegistry {
private FileService fileService;
private MarkdownService markdownService;
private NoteService noteService;
private PollService pollService;
private PostBox postBox;
private ProjectService projectService;
private StockService stockService;
@@ -33,9 +34,10 @@ public class ModuleRegistry {
case ContactService cs: singleton.contactService = cs; break;
case DocumentService ds: singleton.documentService = ds; break;
case FileService fs: singleton.fileService = fs; break;
case StockService is: singleton.stockService = is; break;
case StockService is: singleton.stockService = is; break;
case MarkdownService ms: singleton.markdownService = ms; break;
case NoteService ns: singleton.noteService = ns; break;
case PollService ps: singleton.pollService = ps; break;
case PostBox pb: singleton.postBox = pb; break;
case ProjectService ps: singleton.projectService = ps; break;
case TagService ts: singleton.tagService = ts; break;
@@ -81,6 +83,10 @@ public class ModuleRegistry {
return singleton.noteService;
}
public static PollService pollService() {
return singleton.pollService;
}
public static PostBox postBox() {
return singleton.postBox;
}

View File

@@ -1,3 +1,4 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.core.api;
public interface PollService {

View File

@@ -0,0 +1,37 @@
package de.srsoftware.umbrella.core.model;
import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.ModuleRegistry;
import de.srsoftware.umbrella.core.Util;
import de.srsoftware.umbrella.core.api.Owner;
import de.srsoftware.umbrella.core.constants.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
public record Poll(String id, Owner owner, String name, String description, boolean isPrivate) implements Mappable {
public static Poll of(ResultSet rs) throws SQLException {
var id = rs.getString(Field.ID);
var userId = rs.getLong(Field.USER_ID);
var name = rs.getString(Field.NAME);
var description = rs.getString(Field.DESCRIPTION);
var isPrivate = rs.getBoolean(Field.PRIVATE);
var owner = ModuleRegistry.userService().loadUser(userId);
return new Poll(id,owner,name,description,isPrivate);
}
@Override
public Map<String, Object> toMap() {
return Map.of(
Field.ID, id,
Field.OWNER, owner.toMap(),
Field.NAME,name,
Field.DESCRIPTION, Map.of(
Field.SOURCE,description,
Field.RENDERED,Util.markdown(description)
),
Field.PRIVATE, isPrivate
);
}
}

View File

@@ -1,9 +1,11 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.poll;
public class Constants {
public static final String CONFIG_DATABASE = "de.umbrella.module.polls";
public static final String CONFIG_DATABASE = "umbrella.modules.poll.database";
public static final String TABLE_OPTIONS = "options";
public static final String TABLE_POLLS = "polls";
public static final String TABLE_SELECTIONS = "selections";
public static final String TABLE_SHARES = "shares";
public static final String TABLE_WEIGHTS = "weights";
}

View File

@@ -1,4 +1,11 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.poll;
import de.srsoftware.umbrella.core.model.Poll;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.util.Collection;
public interface PollDb {
Collection<Poll> listPolls(UmbrellaUser user);
}

View File

@@ -1,23 +1,56 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.poll;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
import static de.srsoftware.umbrella.core.constants.Path.LIST;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingField;
import static de.srsoftware.umbrella.poll.Constants.CONFIG_DATABASE;
import com.sun.net.httpserver.HttpExchange;
import de.srsoftware.configuration.Configuration;
import de.srsoftware.tools.Path;
import de.srsoftware.tools.SessionToken;
import de.srsoftware.umbrella.core.BaseHandler;
import de.srsoftware.umbrella.core.ModuleRegistry;
import de.srsoftware.umbrella.core.api.PollService;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Token;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingField;
import static de.srsoftware.umbrella.poll.Constants.CONFIG_DATABASE;
import java.io.IOException;
import java.util.Optional;
public class PollModule extends BaseHandler implements PollService {
private PollDb pollDb;
PollModule(Configuration config){
public PollModule(Configuration config){
super();
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingField(CONFIG_DATABASE));
pollDb = new SqliteDb(connect(dbFile));
ModuleRegistry.add(this);
}
@Override
public boolean doGet(Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> token = SessionToken.from(ex).map(Token::of);
var user = userService().loadUser(token);
if (user.isEmpty()) return unauthorized(ex);
var head = path.pop();
return switch (head) {
case LIST -> getPollList(ex,user.get());
case null, default -> super.doGet(path,ex);
};
} catch (UmbrellaException e){
return send(ex,e);
}
}
private boolean getPollList(HttpExchange ex, UmbrellaUser user) throws IOException {
var list = pollDb.listPolls(user);
return sendContent(ex,list);
}
}

View File

@@ -1,19 +1,25 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.poll;
import static de.srsoftware.tools.jdbc.Condition.equal;
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
import static de.srsoftware.tools.jdbc.Query.select;
import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.constants.Field.DESCRIPTION;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.poll.Constants.*;
import static java.text.MessageFormat.format;
import de.srsoftware.umbrella.core.BaseDb;
import de.srsoftware.umbrella.core.constants.Field;
import de.srsoftware.umbrella.core.model.Poll;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.sql.Connection;
import java.sql.SQLException;
import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.constants.Field.DESCRIPTION;
import static de.srsoftware.umbrella.core.constants.Field.SHOW_CLOSED;
import static de.srsoftware.umbrella.core.constants.Field.STATUS;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.failedToCreateTable;
import static de.srsoftware.umbrella.core.model.Status.OPEN;
import static de.srsoftware.umbrella.poll.Constants.*;
import static java.text.MessageFormat.format;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class SqliteDb extends BaseDb implements PollDb {
@@ -48,7 +54,7 @@ public class SqliteDb extends BaseDb implements PollDb {
}
private void createPollsTable() {
var sql = "CREATE TABLE IF NOT EXISTS {0} ({1} VARCHAR(255) NOT NULL PRIMARY_KEY, {2} INT NOT NULL, {3} VARCHAR(255) NOT NULL, {4} TEXT, {5} BOOLEAN)";
var sql = "CREATE TABLE IF NOT EXISTS {0} ({1} VARCHAR(255) NOT NULL PRIMARY KEY, {2} INT NOT NULL, {3} VARCHAR(255) NOT NULL, {4} TEXT, {5} BOOLEAN)";
try {
var stmt = db.prepareStatement(format(sql,TABLE_POLLS, Field.ID,Field.USER_ID,Field.NAME,Field.DESCRIPTION, Field.PRIVATE));
stmt.execute();
@@ -69,8 +75,19 @@ public class SqliteDb extends BaseDb implements PollDb {
}
}
private void createSharesTable() {
var sql = "CREATE TABLE IF NOT EXISTS {0} ({1} VARCHAR(255) NOT NULL REFERENCES {2}({3}), {4} INT NOT NULL, {5} INT, PRIMARY KEY ({1}, {4}))";
try {
var stmt = db.prepareStatement(format(sql,TABLE_SHARES,POLL_ID,TABLE_POLLS,ID,USER_ID, PERMISSION));
stmt.execute();
stmt.close();
} catch (SQLException e) {
throw failedToCreateTable(TABLE_SHARES).causedBy(e);
}
}
private void createWeightsTable(){
var sql = "CREATE TABLE {0} ( {1} INT NOT NULL, {2} VARCHAR(255) NOT NULL REFERENCES {3}({4}), {5} TEXT, PRIMARY KEY ({2}, {1}))"
var sql = "CREATE TABLE {0} ( {1} INT NOT NULL, {2} VARCHAR(255) NOT NULL REFERENCES {3}({4}), {5} TEXT, PRIMARY KEY ({2}, {1}))";
try {
var stmt = db.prepareStatement(format(sql,TABLE_WEIGHTS, WEIGHT,POLL_ID,TABLE_POLLS, ID, DESCRIPTION));
stmt.execute();
@@ -79,5 +96,18 @@ public class SqliteDb extends BaseDb implements PollDb {
throw failedToCreateTable(TABLE_WEIGHTS).causedBy(e);
}
}
@Override
public Collection<Poll> listPolls(UmbrellaUser user) {
// TODO
try {
var rs = select(ALL).from(TABLE_POLLS).where(USER_ID,equal(user.id())).exec(db);
var list = new ArrayList<Poll>();
while (rs.next()) list.add(Poll.of(rs));
return list;
} catch (SQLException sqle){
throw failedToLoadObject(TABLE_POLLS);
}
return List.of();
}
}

View File

@@ -33,12 +33,10 @@ import de.srsoftware.umbrella.core.constants.Text;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.*;
import de.srsoftware.umbrella.core.model.Location;
import java.io.IOException;
import java.util.*;
import de.srsoftware.umbrella.messagebus.events.Event;
import de.srsoftware.umbrella.messagebus.events.ItemEvent;
import java.io.IOException;
import java.util.*;
import org.json.JSONObject;
public class StockModule extends BaseHandler implements StockService {