From 5d506ac0f994047989488bb80a03da906264f7c7 Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sat, 9 Aug 2025 13:43:10 +0200 Subject: [PATCH] introduced ModuleRegistry to easy inter-module dependencies --- .../umbrella/backend/Application.java | 34 ++--- .../umbrella/bookmarks/BookmarkApi.java | 22 ++-- .../umbrella/company/CompanyModule.java | 23 ++-- .../umbrella/core/ModuleRegistry.java | 121 ++++++++++++++++++ .../umbrella/core/api/CompanyService.java | 2 - .../umbrella/core/api/DocumentService.java | 5 + .../umbrella/core/api/ItemService.java | 5 + .../umbrella/core/api/MarkdownService.java | 5 + .../umbrella/core/api/ProjectService.java | 1 - .../umbrella/core/api/TagService.java | 2 - .../umbrella/core/api/TaskService.java | 4 - .../umbrella/core/api/UserService.java | 8 +- .../umbrella/documents/DocumentApi.java | 58 ++++----- .../de/srsoftware/umbrella/items/ItemApi.java | 20 ++- .../srsoftware/umbrella/legacy/LegacyApi.java | 22 ++-- .../umbrella/markdown/MarkdownApi.java | 13 +- .../umbrella/message/MessageApi.java | 7 +- .../umbrella/message/MessageSystem.java | 28 ++-- .../srsoftware/umbrella/notes/NoteModule.java | 18 +-- .../umbrella/project/ProjectModule.java | 42 +++--- .../srsoftware/umbrella/tags/TagModule.java | 19 +-- .../srsoftware/umbrella/task/TaskModule.java | 62 +++------ .../srsoftware/umbrella/time/TimeModule.java | 27 ++-- .../umbrella/translations/Translations.java | 5 + .../srsoftware/umbrella/user/UserModule.java | 51 +++++--- 25 files changed, 348 insertions(+), 256 deletions(-) create mode 100644 core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java create mode 100644 core/src/main/java/de/srsoftware/umbrella/core/api/DocumentService.java create mode 100644 core/src/main/java/de/srsoftware/umbrella/core/api/ItemService.java create mode 100644 core/src/main/java/de/srsoftware/umbrella/core/api/MarkdownService.java diff --git a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java index c6803f6..91979d4 100644 --- a/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java +++ b/backend/src/main/java/de/srsoftware/umbrella/backend/Application.java @@ -10,13 +10,13 @@ import de.srsoftware.configuration.JsonConfig; import de.srsoftware.tools.ColorLogger; import de.srsoftware.umbrella.bookmarks.BookmarkApi; import de.srsoftware.umbrella.company.CompanyModule; +import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.Util; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.documents.DocumentApi; import de.srsoftware.umbrella.items.ItemApi; import de.srsoftware.umbrella.legacy.LegacyApi; import de.srsoftware.umbrella.markdown.MarkdownApi; -import de.srsoftware.umbrella.message.MessageApi; import de.srsoftware.umbrella.message.MessageSystem; import de.srsoftware.umbrella.notes.NoteModule; import de.srsoftware.umbrella.project.ProjectModule; @@ -57,23 +57,24 @@ public class Application { config.get("umbrella.plantuml").map(Object::toString).map(File::new).filter(File::exists).ifPresent(Util::setPlantUmlJar); - var translationModule = new Translations(); - var messageSystem = new MessageSystem(translationModule,config); var server = HttpServer.create(new InetSocketAddress(port), 0); - var userModule = new UserModule(config,messageSystem); - var tagModule = new TagModule(config,userModule); - var bookmarkApi = new BookmarkApi(config,tagModule); - var companyModule = new CompanyModule(config, userModule); - var documentApi = new DocumentApi(companyModule, translationModule, config); - var itemApi = new ItemApi(config,companyModule); - var legacyApi = new LegacyApi(userModule.userDb(),config); - var markdownApi = new MarkdownApi(userModule); - var messageApi = new MessageApi(messageSystem); - var notesModule = new NoteModule(config,userModule); - var projectModule = new ProjectModule(config,companyModule,tagModule); - var taskModule = new TaskModule(config,projectModule,tagModule,notesModule); - var timeModule = new TimeModule(config,taskModule); + var registry = new ModuleRegistry(); + var translationModule = new Translations(registry); + var postBox = new MessageSystem(registry,config); + + var userModule = new UserModule(registry,config); + var tagModule = new TagModule(registry,config); + var bookmarkApi = new BookmarkApi(registry,config); + var companyModule = new CompanyModule(registry, config); + var documentApi = new DocumentApi(registry, config); + var itemApi = new ItemApi(registry, config); + var legacyApi = new LegacyApi(registry,config); + var markdownApi = new MarkdownApi(registry); + var notesModule = new NoteModule(registry,config); + var projectModule = new ProjectModule(registry, config); + var taskModule = new TaskModule(registry, config); + var timeModule = new TimeModule(registry, config); var webHandler = new WebHandler(); bookmarkApi .bindPath("/api/bookmark") .on(server); @@ -81,7 +82,6 @@ public class Application { companyModule .bindPath("/api/company") .on(server); itemApi .bindPath("/api/items") .on(server); markdownApi .bindPath("/api/markdown") .on(server); - messageApi .bindPath("/api/messages") .on(server); notesModule .bindPath("/api/notes") .on(server); projectModule .bindPath("/api/project") .on(server); tagModule .bindPath("/api/tags") .on(server); diff --git a/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java b/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java index 073512b..47b2444 100644 --- a/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java +++ b/bookmark/src/main/java/de/srsoftware/umbrella/bookmarks/BookmarkApi.java @@ -14,8 +14,8 @@ 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.api.TagService; -import de.srsoftware.umbrella.core.api.UserService; +import de.srsoftware.umbrella.core.ModuleRegistry; +import de.srsoftware.umbrella.core.api.BookmarkService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; @@ -24,16 +24,14 @@ import java.util.ArrayList; import java.util.Optional; import org.json.JSONArray; -public class BookmarkApi extends BaseHandler { +public class BookmarkApi extends BaseHandler implements BookmarkService { private final BookmarkDb db; - private final UserService users; - private final TagService tags; + private final ModuleRegistry registry; - public BookmarkApi(Configuration config, TagService tagService) { + public BookmarkApi(ModuleRegistry registry, Configuration config) { var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); db = new SqliteDb(connect(dbFile)); - tags = tagService; - users = tagService.userService(); + this.registry = registry.add(this); } @Override @@ -41,7 +39,7 @@ public class BookmarkApi extends BaseHandler { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -61,7 +59,7 @@ public class BookmarkApi extends BaseHandler { private boolean getBookmark(UmbrellaUser user, long id, HttpExchange ex) throws IOException { var bookmark = db.load(id,user.id()); - tags.getTags(BOOKMARK, id, user).forEach(bookmark.tags()::add); + registry.tagService().getTags(BOOKMARK, id, user).forEach(bookmark.tags()::add); return sendContent(ex,bookmark); } @@ -70,7 +68,7 @@ public class BookmarkApi extends BaseHandler { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -117,7 +115,7 @@ public class BookmarkApi extends BaseHandler { if (json.has(TAGS) && json.get(TAGS) instanceof JSONArray tagList){ var list = tagList.toList().stream().map(Object::toString).toList(); - tags.save(BOOKMARK,bookmark.urlId(), userList, list); + registry.tagService().save(BOOKMARK,bookmark.urlId(), userList, list); } return sendContent(ex,bookmark); } diff --git a/company/src/main/java/de/srsoftware/umbrella/company/CompanyModule.java b/company/src/main/java/de/srsoftware/umbrella/company/CompanyModule.java index 113a76a..f779117 100644 --- a/company/src/main/java/de/srsoftware/umbrella/company/CompanyModule.java +++ b/company/src/main/java/de/srsoftware/umbrella/company/CompanyModule.java @@ -14,8 +14,8 @@ import de.srsoftware.tools.Path; import de.srsoftware.tools.SessionToken; import de.srsoftware.umbrella.company.api.CompanyDb; import de.srsoftware.umbrella.core.BaseHandler; +import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.api.CompanyService; -import de.srsoftware.umbrella.core.api.UserService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.*; import java.io.IOException; @@ -23,13 +23,13 @@ import java.util.*; public class CompanyModule extends BaseHandler implements CompanyService { - private final UserService users; private final CompanyDb companyDb; + private final ModuleRegistry registry; - public CompanyModule(Configuration config, UserService userService) throws UmbrellaException { + public CompanyModule(ModuleRegistry registry, Configuration config) throws UmbrellaException { var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); companyDb = new SqliteDb(connect(dbFile)); - users = userService; + this.registry = registry.add(this); } @Override @@ -37,7 +37,7 @@ public class CompanyModule extends BaseHandler implements CompanyService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head){ @@ -55,7 +55,7 @@ public class CompanyModule extends BaseHandler implements CompanyService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -74,7 +74,7 @@ public class CompanyModule extends BaseHandler implements CompanyService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -102,7 +102,7 @@ public class CompanyModule extends BaseHandler implements CompanyService { @Override public Collection getMembers(long companyId) throws UmbrellaException { var members = new HashSet(); - for (var userId : companyDb.getMembers(companyId)) members.add(users.loadUser(userId)); + for (var userId : companyDb.getMembers(companyId)) members.add(registry.userService().loadUser(userId)); return members; } @@ -120,7 +120,7 @@ public class CompanyModule extends BaseHandler implements CompanyService { var userMap = new HashMap(); for (var company : companyList){ for (var userId : companyDb.getMembers(company.id())){ - var user = userMap.computeIfAbsent(userId,k -> users.loadUser(userId)); + var user = userMap.computeIfAbsent(userId,k -> registry.userService().loadUser(userId)); company.members().put(userId,user); } } @@ -166,9 +166,4 @@ public class CompanyModule extends BaseHandler implements CompanyService { companyDb.addUser(company.id(),user.id()); return sendContent(ex,company); } - - @Override - public UserService userService() { - return users; - } } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java b/core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java new file mode 100644 index 0000000..3bc81df --- /dev/null +++ b/core/src/main/java/de/srsoftware/umbrella/core/ModuleRegistry.java @@ -0,0 +1,121 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.core; + +import de.srsoftware.umbrella.core.api.*; + +public class ModuleRegistry { + private Translator translator; + private PostBox postBox; + private UserService userService; + private TagService tagService; + private BookmarkService bookmarkService; + private CompanyService companyService; + private DocumentService documentService; + private ItemService itemService; + private MarkdownService markdownService; + private NoteService noteService; + private ProjectService projectService; + private TaskService taskService; + private TimeService timeService; + + public ModuleRegistry add(BookmarkService bookmarkService) { + this.bookmarkService = bookmarkService; + return this; + } + + public ModuleRegistry add(CompanyService companyService) { + this.companyService = companyService; + return this; + } + + public ModuleRegistry add(DocumentService documentService) { + this.documentService = documentService; + return this; + } + + public ModuleRegistry add(ItemService itemService) { + this.itemService = itemService; + return this; + } + + public ModuleRegistry add(MarkdownService markdownService) { + this.markdownService = markdownService; + return this; + } + + public ModuleRegistry add(NoteService noteService) { + this.noteService = noteService; + return this; + } + + public ModuleRegistry add(PostBox postBox) { + this.postBox = postBox; + return this; + } + + public ModuleRegistry add(ProjectService projectService) { + this.projectService = projectService; + return this; + } + + public ModuleRegistry add(TagService tagService) { + this.tagService = tagService; + return this; + } + + public ModuleRegistry add(TaskService taskService) { + this.taskService = taskService; + return this; + } + + public ModuleRegistry add(TimeService timeService) { + this.timeService = timeService; + return this; + } + + public ModuleRegistry add(Translator translator) { + this.translator = translator; + return this; + } + + public ModuleRegistry add(UserService userService) { + this.userService = userService; + return this; + } + + public CompanyService companyService(){ + return companyService; + } + + public DocumentService documentService(){ + return documentService; + } + + public NoteService noteService(){ + return noteService; + } + + public PostBox postBox() { + return postBox; + } + + public ProjectService projectService(){ + return projectService; + } + + public TagService tagService(){ + return tagService; + } + + public TaskService taskService(){ + return taskService; + } + + public Translator translator(){ + return translator; + } + + public UserService userService(){ + return userService; + } +} diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/CompanyService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/CompanyService.java index 838e300..32aaec4 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/CompanyService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/CompanyService.java @@ -15,6 +15,4 @@ public interface CompanyService { Map listCompaniesOf(UmbrellaUser user) throws UmbrellaException; boolean membership(long companyId, long userId) throws UmbrellaException; - - UserService userService(); } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/DocumentService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/DocumentService.java new file mode 100644 index 0000000..464d752 --- /dev/null +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/DocumentService.java @@ -0,0 +1,5 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.core.api; + +public interface DocumentService { +} diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/ItemService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/ItemService.java new file mode 100644 index 0000000..adcc0bd --- /dev/null +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/ItemService.java @@ -0,0 +1,5 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.core.api; + +public interface ItemService { +} diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/MarkdownService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/MarkdownService.java new file mode 100644 index 0000000..1cc510f --- /dev/null +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/MarkdownService.java @@ -0,0 +1,5 @@ +/* © SRSoftware 2025 */ +package de.srsoftware.umbrella.core.api; + +public interface MarkdownService { +} diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/ProjectService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/ProjectService.java index 5459c17..0d71c1c 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/ProjectService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/ProjectService.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Map; public interface ProjectService { - CompanyService companyService(); Map listCompanyProjects(long companyId, boolean includeClosed) throws UmbrellaException; Map listUserProjects(long userId, boolean includeClosed) throws UmbrellaException; Project load(long projectId); diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/TagService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/TagService.java index 562ecee..4b0e688 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/TagService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/TagService.java @@ -13,6 +13,4 @@ public interface TagService { void save(String module, long entityId, Collection userIds, Collection tags); String save(String module, long entityId, Collection userIds, String tag); - - UserService userService(); } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/TaskService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/TaskService.java index 100841e..a1a7b13 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/TaskService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/TaskService.java @@ -8,7 +8,6 @@ import java.util.HashMap; import java.util.List; public interface TaskService { - CompanyService companyService(); HashMap listCompanyTasks(long companyId) throws UmbrellaException; HashMap listProjectTasks(long projectId) throws UmbrellaException; Collection loadMembers(Collection tasks); @@ -16,7 +15,4 @@ public interface TaskService { loadMembers(List.of(task)); return task; } - ProjectService projectService(); - - UserService userService(); } diff --git a/core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java b/core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java index 193d159..fda1ef8 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/api/UserService.java @@ -3,6 +3,7 @@ package de.srsoftware.umbrella.core.api; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import de.srsoftware.umbrella.core.model.Session; import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; import java.util.Collection; @@ -10,10 +11,13 @@ import java.util.Map; import java.util.Optional; public interface UserService { - Map list(Collection ids) throws UmbrellaException; + void dropSession(Token token) throws UmbrellaException; + Session extend(Session session) throws UmbrellaException; + Map list(Integer start, Integer limit, Collection ids) throws UmbrellaException; + Session load(Token token) throws UmbrellaException; + UmbrellaUser load(Session session) throws UmbrellaException; UmbrellaUser loadUser(long userId) throws UmbrellaException; Optional loadUser(Optional sessionToken) throws UmbrellaException; Optional loadUser(HttpExchange ex) throws UmbrellaException; - PostBox postBox(); Optional refreshSession(HttpExchange ex); } diff --git a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java index 507dc42..dc8c31e 100644 --- a/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java +++ b/documents/src/main/java/de/srsoftware/umbrella/documents/DocumentApi.java @@ -42,10 +42,8 @@ import de.srsoftware.tools.Path; import de.srsoftware.tools.SessionToken; import de.srsoftware.tools.Tuple; import de.srsoftware.umbrella.core.BaseHandler; -import de.srsoftware.umbrella.core.api.CompanyService; -import de.srsoftware.umbrella.core.api.PostBox; -import de.srsoftware.umbrella.core.api.Translator; -import de.srsoftware.umbrella.core.api.UserService; +import de.srsoftware.umbrella.core.ModuleRegistry; +import de.srsoftware.umbrella.core.api.*; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.*; import de.srsoftware.umbrella.documents.model.*; @@ -61,32 +59,26 @@ import java.util.stream.Stream; import org.json.JSONArray; import org.json.JSONObject; -public class DocumentApi extends BaseHandler { +public class DocumentApi extends BaseHandler implements DocumentService { private static final Predicate ZUGFERD_FILTER = document -> document.id().equals("Zugferd"); private final DocumentRegistry registry = new DocumentRegistry(); - private final CompanyService companies; private final Configuration config; private final DocumentDb db; - private final UserService users; - private final Translator translator; - private final PostBox messages; + private final ModuleRegistry modules; - public DocumentApi(CompanyService companyService, Translator translator, Configuration config) throws UmbrellaException { + public DocumentApi(ModuleRegistry registry, Configuration config) throws UmbrellaException { this.config = config; - this.translator = translator; var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); db = new SqliteDb(connect(dbFile)); - companies = companyService; - users = companyService.userService(); - messages = users.postBox(); + modules = registry.add(this); Optional templates = config.get(CONFIG_TEMPLATES); if (templates.isEmpty()) throw missingFieldException(CONFIG_TEMPLATES); - registry.add(new DocumentDirectory(new File(templates.get()))); - registry.add(new TemplateProcessor(), new LatexFactory(), new WeasyFactory(), new ZugferdFactory()); + this.registry.add(new DocumentDirectory(new File(templates.get()))); + this.registry.add(new TemplateProcessor(), new LatexFactory(), new WeasyFactory(), new ZugferdFactory()); - registry.documents().forEach(d -> LOG.log(DEBUG,"found template: {0}",d)); + this.registry.documents().forEach(d -> LOG.log(DEBUG,"found template: {0}",d)); } @Override @@ -94,7 +86,7 @@ public class DocumentApi extends BaseHandler { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = modules.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); long docId = Long.parseLong(head); @@ -113,7 +105,7 @@ public class DocumentApi extends BaseHandler { private boolean deleteDocument(HttpExchange ex, long docId, UmbrellaUser user) throws IOException, UmbrellaException { var doc = db.loadDoc(docId); var companyId = doc.companyId(); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",doc.companyId()); + if (!modules.companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",doc.companyId()); if (doc.state() != NEW) throw new UmbrellaException(HTTP_BAD_REQUEST,"This document has already been sent"); return sendContent(ex,db.deleteDoc(docId)); } @@ -121,7 +113,7 @@ public class DocumentApi extends BaseHandler { private boolean deletePosition(HttpExchange ex, long docId, UmbrellaUser user) throws UmbrellaException, IOException { var doc = db.loadDoc(docId); var companyId = doc.companyId(); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",doc.companyId()); + if (!modules.companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",doc.companyId()); if (doc.state() != NEW) throw new UmbrellaException(HTTP_BAD_REQUEST,"This document has already been sent"); var json = json(ex); if (!(json.has(POSITION) && json.get(POSITION) instanceof Number number)) throw missingFieldException(POSITION); @@ -134,7 +126,7 @@ public class DocumentApi extends BaseHandler { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = modules.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head){ @@ -165,7 +157,7 @@ public class DocumentApi extends BaseHandler { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = modules.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); var docId = Long.parseLong(head); @@ -187,7 +179,7 @@ public class DocumentApi extends BaseHandler { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = modules.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head){ @@ -219,7 +211,7 @@ public class DocumentApi extends BaseHandler { var attachment = new Attachment(doc.number()+".pdf",rendered.mimeType(),rendered.bytes()); var message = new Message(user,subject,content,null,List.of(attachment)); var envelope = new Envelope(message,new User(doc.customer().shortName(),new EmailAddress(email),doc.customer().language())); - messages.send(envelope); + modules.postBox().send(envelope); db.save(doc.set(SENT)); return ok(ex); } @@ -244,8 +236,8 @@ public class DocumentApi extends BaseHandler { private Tuple getDocument(long docId, UmbrellaUser user) throws UmbrellaException { var doc = db.loadDoc(docId); var companyId = doc.companyId(); - var company = companies.get(companyId); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); + var company = modules.companyService().get(companyId); + if (!modules.companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); return Tuple.of(doc,company); } @@ -364,7 +356,7 @@ public class DocumentApi extends BaseHandler { .filter(filter) .findAny(); if (optDoc.isEmpty()) throw UmbrellaException.notFound("Cannot render {0} {1}: Missing template \"{2}\"",type,document.number(),template); - Function translate = text -> translator.translate(user.language(),text); + Function translate = text -> modules.translator().translate(user.language(),text); var pdfData = new HashMap(); pdfData.put(FIELD_DOCUMENT,document.renderToMap()); pdfData.put("translate",translate); @@ -413,8 +405,8 @@ public class DocumentApi extends BaseHandler { var json = json(ex); if (!json.has(COMPANY)) throw missingFieldException(COMPANY); long companyId = json.getLong(COMPANY); - var company = companies.get(companyId); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company); + var company = modules.companyService().get(companyId); + if (!modules.companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company); var docs = db.listDocs(companyId); var map = new HashMap(); for (var entry : docs.entrySet()) map.put(entry.getKey(),entry.getValue().summary()); @@ -459,8 +451,8 @@ public class DocumentApi extends BaseHandler { if (!(json.has(SENDER) && json.get(SENDER) instanceof JSONObject senderData)) throw missingFieldException(SENDER); if (!senderData.has(FIELD_COMPANY) || !(senderData.get(FIELD_COMPANY) instanceof Number companyId)) throw missingFieldException(FIELD_COMPANY); - var company = companies.get(companyId.longValue()); - if (!companies.membership(companyId.longValue(),user.id())) throw forbidden("You are mot a member of company {0}",company); + var company = modules.companyService().get(companyId.longValue()); + if (!modules.companyService().membership(companyId.longValue(),user.id())) throw forbidden("You are mot a member of company {0}",company); if (!json.has(FIELD_CUSTOMER) || !(json.get(FIELD_CUSTOMER) instanceof JSONObject customerData)) throw missingFieldException(FIELD_CUSTOMER); if (!json.has(FIELD_TYPE) || !(json.get(FIELD_TYPE) instanceof Number docTypeId)) throw missingFieldException(FIELD_TYPE); @@ -506,8 +498,8 @@ public class DocumentApi extends BaseHandler { private boolean postTemplateList(HttpExchange ex, UmbrellaUser user) throws UmbrellaException, IOException { var json = json(ex); if (!(json.has(COMPANY) && json.get(COMPANY) instanceof Number companyId)) throw missingFieldException(COMPANY); - var company = companies.get(companyId.longValue()); - if (!companies.membership(companyId.longValue(),user.id())) throw forbidden("You are not a member of {0}",company.name()); + var company = modules.companyService().get(companyId.longValue()); + if (!modules.companyService().membership(companyId.longValue(),user.id())) throw forbidden("You are not a member of {0}",company.name()); var templates = db.getCompanyTemplates(companyId.longValue()); return sendContent(ex,templates.stream().map(Template::toMap)); } diff --git a/items/src/main/java/de/srsoftware/umbrella/items/ItemApi.java b/items/src/main/java/de/srsoftware/umbrella/items/ItemApi.java index 993286b..370ed9b 100644 --- a/items/src/main/java/de/srsoftware/umbrella/items/ItemApi.java +++ b/items/src/main/java/de/srsoftware/umbrella/items/ItemApi.java @@ -13,8 +13,8 @@ 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.api.CompanyService; -import de.srsoftware.umbrella.core.api.UserService; +import de.srsoftware.umbrella.core.ModuleRegistry; +import de.srsoftware.umbrella.core.api.ItemService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; @@ -22,17 +22,15 @@ import java.io.IOException; import java.util.HashMap; import java.util.Optional; -public class ItemApi extends BaseHandler { +public class ItemApi extends BaseHandler implements ItemService { private final ItemDb itemDb; - private final CompanyService companies; - private final UserService users; + private final ModuleRegistry registry; - public ItemApi(Configuration config, CompanyService companyService) throws UmbrellaException { + public ItemApi(ModuleRegistry registry, Configuration config) throws UmbrellaException { var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); itemDb = new SqliteDb(connect(dbFile)); - companies = companyService; - users = companies.userService(); + this.registry = registry.add(this); } @Override @@ -40,7 +38,7 @@ public class ItemApi extends BaseHandler { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -56,8 +54,8 @@ public class ItemApi extends BaseHandler { var json = json(ex); if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID); var companyId = cid.longValue(); - var company = companies.get(companyId); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); + var company = registry.companyService().get(companyId); + if (!registry.companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); var items = itemDb.list(companyId) .stream() .map(Item::toMap) diff --git a/legacy/src/main/java/de/srsoftware/umbrella/legacy/LegacyApi.java b/legacy/src/main/java/de/srsoftware/umbrella/legacy/LegacyApi.java index cbe2833..6adf846 100644 --- a/legacy/src/main/java/de/srsoftware/umbrella/legacy/LegacyApi.java +++ b/legacy/src/main/java/de/srsoftware/umbrella/legacy/LegacyApi.java @@ -19,11 +19,11 @@ 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.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Session; import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; -import de.srsoftware.umbrella.user.api.UserDb; import java.io.IOException; import java.time.Instant; import java.util.*; @@ -31,12 +31,12 @@ import org.json.JSONObject; public class LegacyApi extends BaseHandler { - private final UserDb users; private final Configuration config; private final String messageUrl; + private final ModuleRegistry registry; - public LegacyApi(UserDb userDb, Configuration config) { - users = userDb; + public LegacyApi(ModuleRegistry registry, Configuration config) { + this.registry = registry; this.config = config.subset("umbrella.modules").orElseThrow(() -> new RuntimeException("Missing configuration: umbrella.modules")); this.messageUrl = null; } @@ -128,7 +128,7 @@ public class LegacyApi extends BaseHandler { throw new UmbrellaException(400,"Fetching related users not implemented, yet!"); } - Map userMap = users.list(0, null, ids); + Map userMap = registry.userService().list(0, null, ids); if (arrayPassed || userMap.size() != 1) { var userData = new HashMap>(); for (var entry : userMap.entrySet()) userData.put(entry.getKey(),entry.getValue().toMap()); @@ -180,7 +180,7 @@ public class LegacyApi extends BaseHandler { } } if (!recipients.isEmpty()){ // replace legacy user ids by user objects in receivers field - Map resp = users.list(0, null, recipients); + Map resp = registry.userService().list(0, null, recipients); data.put("receivers",resp.values().stream().map(UmbrellaUser::toMap).toList()); } @@ -203,7 +203,7 @@ public class LegacyApi extends BaseHandler { var optToken = SessionToken.from(ex).map(Token::of); if (optToken.isPresent()) try{ var token = optToken.get(); - users.dropSession(token); + registry.userService().dropSession(token); var expiredToken = new SessionToken(token.toString(),"/", Instant.now().minus(1, DAYS),true); expiredToken.addTo(ex); if (returnTo instanceof String location) return sendRedirect(ex,location); @@ -245,8 +245,8 @@ public class LegacyApi extends BaseHandler { }; protected Session requestSession(Token token) throws UmbrellaException { - var session = users.load(token); - session = users.extend(session); + var session = registry.userService().load(token); + session = registry.userService().extend(session); return session; } @@ -280,8 +280,8 @@ public class LegacyApi extends BaseHandler { var o = map.get(TOKEN); if (!(o instanceof String token)) throw new UmbrellaException(500,"Request did not contain token!"); - var session = users.load(Token.of(token)); - var user = users.load(session); + var session = registry.userService().load(Token.of(token)); + var user = registry.userService().load(session); var userMap = user.toMap(); userMap.put(TOKEN,Map.of(TOKEN,token,EXPIRATION,session.expiration().getEpochSecond())); return sendContent(ex,userMap); diff --git a/markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java b/markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java index 8f6b970..6d3475c 100644 --- a/markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java +++ b/markdown/src/main/java/de/srsoftware/umbrella/markdown/MarkdownApi.java @@ -7,19 +7,20 @@ import com.sun.net.httpserver.HttpExchange; 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.Util; -import de.srsoftware.umbrella.core.api.UserService; +import de.srsoftware.umbrella.core.api.MarkdownService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Token; import java.io.IOException; import java.util.Optional; -public class MarkdownApi extends BaseHandler { +public class MarkdownApi extends BaseHandler implements MarkdownService { - private final UserService users; + private final ModuleRegistry registry; - public MarkdownApi(UserService userService) { - users = userService; + public MarkdownApi(ModuleRegistry registry) { + this.registry = registry.add(this); } @Override @@ -27,7 +28,7 @@ public class MarkdownApi extends BaseHandler { try { addCors(ex); Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) throw UmbrellaException.forbidden("You must be logged in to use the markdown renderer!"); var rendered = Util.markdown(body(ex)); diff --git a/messages/src/main/java/de/srsoftware/umbrella/message/MessageApi.java b/messages/src/main/java/de/srsoftware/umbrella/message/MessageApi.java index 7e8a515..194d16f 100644 --- a/messages/src/main/java/de/srsoftware/umbrella/message/MessageApi.java +++ b/messages/src/main/java/de/srsoftware/umbrella/message/MessageApi.java @@ -2,12 +2,13 @@ package de.srsoftware.umbrella.message; import de.srsoftware.umbrella.core.BaseHandler; +import de.srsoftware.umbrella.core.ModuleRegistry; public class MessageApi extends BaseHandler { - private final MessageSystem messageSystem; + private final ModuleRegistry registry; - public MessageApi(MessageSystem messageSystem) { + public MessageApi(ModuleRegistry moduleRegistry) { super(); - this.messageSystem = messageSystem; + this.registry = moduleRegistry; } } diff --git a/messages/src/main/java/de/srsoftware/umbrella/message/MessageSystem.java b/messages/src/main/java/de/srsoftware/umbrella/message/MessageSystem.java index 0c87198..8c86b35 100644 --- a/messages/src/main/java/de/srsoftware/umbrella/message/MessageSystem.java +++ b/messages/src/main/java/de/srsoftware/umbrella/message/MessageSystem.java @@ -9,8 +9,8 @@ import static de.srsoftware.umbrella.message.Constants.*; import static java.lang.System.Logger.Level.*; import de.srsoftware.configuration.Configuration; +import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.api.PostBox; -import de.srsoftware.umbrella.core.api.Translator; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Envelope; import de.srsoftware.umbrella.core.model.UmbrellaUser; @@ -32,6 +32,8 @@ import java.util.function.BiFunction; public class MessageSystem implements PostBox { public static final System.Logger LOG = System.getLogger(MessageSystem.class.getSimpleName()); private final Timer timer = new Timer(); + private final ModuleRegistry registry; + private record Receiver(User user, de.srsoftware.umbrella.core.model.Message message){} private class SubmissionTask extends TimerTask{ @@ -66,22 +68,20 @@ public class MessageSystem implements PostBox { private final int port; private final SqliteMessageDb db; private Session session; - private List queue = new CopyOnWriteArrayList<>(); + private final List queue = new CopyOnWriteArrayList<>(); private String debugAddress; private final HashMap> exceptions = new HashMap<>(); - private final Translator translator; - - public MessageSystem(Translator translator, Configuration config) throws UmbrellaException { - var messageDbFile = config.get(CONFIG_DB).orElseThrow(() -> missingConfigException(CONFIG_DB)); - db = new SqliteMessageDb(connect(messageDbFile)); - this.translator = translator; + public MessageSystem(ModuleRegistry moduleRegistry, Configuration config) throws UmbrellaException { + var dbFile = config.get(CONFIG_DB).orElseThrow(() -> missingConfigException(CONFIG_DB)); + db = new SqliteMessageDb(connect(dbFile)); debugAddress = config.get(DEBUG_ADDREESS).map(Object::toString).orElse(null); - port = config.get(CONFIG_SMTP_PORT,587); - host = config.get(CONFIG_SMTP_HOST).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.host not configured!")); - user = config.get(CONFIG_SMTP_USER).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.user not configured!")); - pass = config.get(CONFIG_SMTP_PASS).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.pass not configured!")); - from = user; + port = config.get(CONFIG_SMTP_PORT,587); + host = config.get(CONFIG_SMTP_HOST).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.host not configured!")); + user = config.get(CONFIG_SMTP_USER).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.user not configured!")); + pass = config.get(CONFIG_SMTP_PASS).map(Object::toString).orElseThrow(() -> new RuntimeException("umbrella.modules.message.smtp.pass not configured!")); + from = user; + registry = moduleRegistry.add(this); new SubmissionTask(8).schedule(); new SubmissionTask(10).schedule(); new SubmissionTask(12).schedule(); @@ -117,7 +117,7 @@ public class MessageSystem implements PostBox { var date = new Date(); for (var receiver : dueRecipients){ - BiFunction,String> translateFunction = (text,fills) -> translator.translate(receiver.language(),text,fills); + BiFunction,String> translateFunction = (text,fills) -> registry.translator().translate(receiver.language(),text,fills); var combined = new CombinedMessage("Collected messages",translateFunction); var envelopes = queue.stream().filter(env -> env.isFor(receiver)).toList(); diff --git a/notes/src/main/java/de/srsoftware/umbrella/notes/NoteModule.java b/notes/src/main/java/de/srsoftware/umbrella/notes/NoteModule.java index 792789e..bdc7850 100644 --- a/notes/src/main/java/de/srsoftware/umbrella/notes/NoteModule.java +++ b/notes/src/main/java/de/srsoftware/umbrella/notes/NoteModule.java @@ -12,8 +12,8 @@ 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.NoteService; -import de.srsoftware.umbrella.core.api.UserService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Note; import de.srsoftware.umbrella.core.model.Token; @@ -26,12 +26,12 @@ import java.util.stream.Collectors; public class NoteModule extends BaseHandler implements NoteService { private final NotesDb notesDb; - private final UserService users; + private final ModuleRegistry registry; - public NoteModule(Configuration config, UserService userService) { + public NoteModule(ModuleRegistry registry, Configuration config) { var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); notesDb = new SqliteDb(connect(dbFile)); - users = userService; + this.registry = registry.add(this); } @Override @@ -44,7 +44,7 @@ public class NoteModule extends BaseHandler implements NoteService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); if (head == null) throw unprocessable("Module missing in path."); @@ -71,7 +71,7 @@ public class NoteModule extends BaseHandler implements NoteService { public boolean doGet(Path path, HttpExchange ex) throws IOException { addCors(ex); try { - var user = users.refreshSession(ex); + var user = registry.userService().refreshSession(ex); if (user.isEmpty()) return unauthorized(ex); var module = path.pop(); Map notes = null; @@ -82,7 +82,7 @@ public class NoteModule extends BaseHandler implements NoteService { long entityId = Long.parseLong(head); notes = getNotes(module, entityId); } - var authors = notes.values().stream().map(Note::authorId).distinct().map(users::loadUser).collect(Collectors.toMap(UmbrellaUser::id,UmbrellaUser::toMap)); + var authors = notes.values().stream().map(Note::authorId).distinct().map(registry.userService()::loadUser).collect(Collectors.toMap(UmbrellaUser::id,UmbrellaUser::toMap)); return sendContent(ex, Map.of("notes",mapValues(notes),"authors",authors)); } catch (NumberFormatException e){ return sendContent(ex,HTTP_UNPROCESSABLE,"Entity id missing in path."); @@ -96,7 +96,7 @@ public class NoteModule extends BaseHandler implements NoteService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); long noteId = Long.parseLong(head); @@ -119,7 +119,7 @@ public class NoteModule extends BaseHandler implements NoteService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var module = path.pop(); if (module == null) throw unprocessable("Module missing in path."); 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 831e519..45f08ac 100644 --- a/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java +++ b/project/src/main/java/de/srsoftware/umbrella/project/ProjectModule.java @@ -18,10 +18,8 @@ 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.api.CompanyService; +import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.api.ProjectService; -import de.srsoftware.umbrella.core.api.TagService; -import de.srsoftware.umbrella.core.api.UserService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.*; import java.io.IOException; @@ -32,37 +30,27 @@ import org.json.JSONObject; public class ProjectModule extends BaseHandler implements ProjectService { private final ProjectDb projects; - private final CompanyService companies; - private final UserService users; - private final TagService tags; + private final ModuleRegistry registy; - public ProjectModule(Configuration config, CompanyService companyService, TagService tagService) throws UmbrellaException { + public ProjectModule(ModuleRegistry registry, Configuration config) throws UmbrellaException { var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); projects = new SqliteDb(connect(dbFile)); - companies = companyService; - tags = tagService; - users = companies.userService(); + this.registy = registry.add(this); } private void addMember(Project project, long userId) { - var user = users.loadUser(userId); + var user = registy.userService().loadUser(userId); var member = new Member(user,READ_ONLY); project.members().put(userId,member); project.dirty(MEMBERS); } - - @Override - public CompanyService companyService() { - return companies; - } - @Override public boolean doGet(Path path, HttpExchange ex) throws IOException { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registy.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -86,7 +74,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registy.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -109,7 +97,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registy.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -141,7 +129,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { var project = loadMembers(projects.load(projectId)); if (!project.hasMember(user)) throw forbidden("You are not a member of {0}",project.name()); var map = project.toMap(); - project.companyId().map(companies::get).map(Company::toMap).ifPresent(data -> map.put(COMPANY,data)); + project.companyId().map(registy.companyService()::get).map(Company::toMap).ifPresent(data -> map.put(COMPANY,data)); return sendContent(ex,map); } @@ -152,8 +140,8 @@ public class ProjectModule extends BaseHandler implements ProjectService { } private boolean listCompanyProjects(HttpExchange ex, UmbrellaUser user, long companyId) throws IOException, UmbrellaException { - var company = companies.get(companyId); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); + var company = registy.companyService().get(companyId); + if (!registy.companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); var projects = listCompanyProjects(companyId,false); return sendContent(ex,mapValues(projects)); } @@ -182,7 +170,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { for (var entry : projects.getMembers(project).entrySet()){ var userId = entry.getKey(); var permission = entry.getValue(); - var user = userMap.computeIfAbsent(userId,k -> users.loadUser(userId)); + var user = userMap.computeIfAbsent(userId,k -> registy.userService().loadUser(userId)); project.members().put(userId,new Member(user,permission)); } } @@ -206,7 +194,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { if (member.permission() == OWNER) members.put(member.user().id(),new Member(member.user(),EDIT)); } } - members.put(userId,new Member(users.loadUser(userId),permission)); + members.put(userId,new Member(registy.userService().loadUser(userId),permission)); project.dirty(MEMBERS); } } @@ -245,7 +233,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { } Long companyId = null; if (json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number number){ - if (!companies.membership(number.longValue(), user.id())) throw forbidden("You are not a member of company {0}!",number); + if (!registy.companyService().membership(number.longValue(), user.id())) throw forbidden("You are not a member of company {0}!",number); companyId = number.longValue(); } var showClosed = false; @@ -258,7 +246,7 @@ public class ProjectModule extends BaseHandler implements ProjectService { if (json.has(TAGS) && json.get(TAGS) instanceof JSONArray arr){ var tagList = arr.toList().stream().filter(elem -> elem instanceof String).map(String.class::cast).toList(); - tags.save(PROJECT,prj.id(),null,tagList); + registy.tagService().save(PROJECT,prj.id(),null,tagList); } return sendContent(ex,prj); diff --git a/tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java b/tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java index 7c2f845..161eec6 100644 --- a/tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java +++ b/tags/src/main/java/de/srsoftware/umbrella/tags/TagModule.java @@ -14,8 +14,8 @@ 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.TagService; -import de.srsoftware.umbrella.core.api.UserService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; @@ -25,13 +25,13 @@ import org.json.JSONArray; public class TagModule extends BaseHandler implements TagService { private final SqliteDb tagDb; - private final UserService users; + private final ModuleRegistry registry; - public TagModule(Configuration config, UserService userService) { + public TagModule(ModuleRegistry registry, Configuration config) { var tagDbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); var bmDbFile = config.get(de.srsoftware.umbrella.bookmarks.Constants.CONFIG_DATABASE).orElseThrow(() -> missingFieldException(de.srsoftware.umbrella.bookmarks.Constants.CONFIG_DATABASE)); tagDb = new SqliteDb(connect(tagDbFile),connect(bmDbFile)); - users = userService; + this.registry = registry.add(this); } @Override @@ -44,7 +44,7 @@ public class TagModule extends BaseHandler implements TagService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var module = path.pop(); if (module == null) throw unprocessable("Module missing in path."); @@ -63,7 +63,7 @@ public class TagModule extends BaseHandler implements TagService { public boolean doGet(Path path, HttpExchange ex) throws IOException { addCors(ex); try { - var user = users.refreshSession(ex); + var user = registry.userService().refreshSession(ex); if (user.isEmpty()) return unauthorized(ex); var module = path.pop(); if (module == null) throw unprocessable("Module missing in path."); @@ -87,7 +87,7 @@ public class TagModule extends BaseHandler implements TagService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var module = path.pop(); if (module == null) throw unprocessable("Module missing in path."); @@ -130,9 +130,4 @@ public class TagModule extends BaseHandler implements TagService { save(module,entityId,userIds,List.of(tag)); return tag; } - - @Override - public UserService userService() { - return users; - } } 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 42fa71f..9aa359a 100644 --- a/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java +++ b/task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java @@ -20,6 +20,7 @@ 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.*; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.*; @@ -34,41 +35,28 @@ import org.json.JSONObject; public class TaskModule extends BaseHandler implements TaskService { private final TaskDb taskDb; - private final ProjectService projects; - private final UserService users; - private final CompanyService companies; - private final TagService tags; - private final NoteService notes; + private final ModuleRegistry registry; - public TaskModule(Configuration config, ProjectService projectService, TagService tagService, NoteService noteService) throws UmbrellaException { + public TaskModule(ModuleRegistry registry, Configuration config) throws UmbrellaException { var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); taskDb = new SqliteDb(connect(dbFile)); - projects = projectService; - companies = projectService.companyService(); - notes = noteService; - tags = tagService; - users = companies.userService(); + this.registry = registry.add(this); } private void addMember(Task task, long userId) { - var user = users.loadUser(userId); + var user = registry.userService().loadUser(userId); var member = new Member(user,READ_ONLY); task.members().put(userId,member); task.dirty(MEMBERS); } - @Override - public CompanyService companyService() { - return companies; - } - private boolean deleteTask(HttpExchange ex, long taskId, UmbrellaUser user) throws IOException { var task = loadMembers(taskDb.load(taskId)); var member = task.members().get(user.id()); if (member == null || !member.mayWrite()) throw forbidden("You are not allowed to delete {0}",task.name()); taskDb.delete(task); - notes.deleteEntity(TASK,taskId); - tags.deleteEntity(TASK,taskId); + registry.noteService().deleteEntity(TASK,taskId); + registry.tagService().deleteEntity(TASK,taskId); return sendContent(ex,Map.of(DELETED,taskId)); } @@ -77,7 +65,7 @@ public class TaskModule extends BaseHandler implements TaskService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -97,7 +85,7 @@ public class TaskModule extends BaseHandler implements TaskService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -119,7 +107,7 @@ public class TaskModule extends BaseHandler implements TaskService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -140,7 +128,7 @@ public class TaskModule extends BaseHandler implements TaskService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -164,9 +152,9 @@ public class TaskModule extends BaseHandler implements TaskService { var json = json(ex); if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID); var companyId = cid.longValue(); - var company = companies.get(companyId); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); - var projectMap = this.projects.listCompanyProjects(companyId,false); + var company = registry.companyService().get(companyId); + if (!registry.companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); + var projectMap = registry.projectService().listCompanyProjects(companyId,false); var taskMap = taskDb.listTasks(projectMap.keySet()); var taskTree = new HashMap>(); taskMap.values().stream().filter(task -> !is0(task.estimatedTime())).forEach(task -> placeInTree(task,taskTree,taskMap)); @@ -201,7 +189,7 @@ public class TaskModule extends BaseHandler implements TaskService { @Override public HashMap listCompanyTasks(long companyId) throws UmbrellaException { - var projectList = projects.listCompanyProjects(companyId,false); + var projectList = registry.projectService().listCompanyProjects(companyId,false); return taskDb.listTasks(projectList.keySet()); } @@ -217,7 +205,7 @@ public class TaskModule extends BaseHandler implements TaskService { for (var entry : taskDb.getMembers(task).entrySet()){ var userId = entry.getKey(); var permission = entry.getValue(); - var user = userMap.computeIfAbsent(userId,k -> users.loadUser(userId)); + var user = userMap.computeIfAbsent(userId,k -> registry.userService().loadUser(userId)); task.members().put(userId,new Member(user,permission)); } } @@ -262,7 +250,7 @@ public class TaskModule extends BaseHandler implements TaskService { if (member.permission() == ASSIGNEE) members.put(member.user().id(),new Member(member.user(),EDIT)); } } - members.put(userId,new Member(users.loadUser(userId),permission)); + members.put(userId,new Member(registry.userService().loadUser(userId),permission)); task.dirty(MEMBERS); } } @@ -296,8 +284,8 @@ public class TaskModule extends BaseHandler implements TaskService { if (!(json.has(PROJECT_ID) && json.get(PROJECT_ID) instanceof Number pid)) throw missingFieldException(PROJECT_ID); if (!(json.has(MEMBERS) && json.get(MEMBERS) instanceof JSONObject memberData)) throw missingFieldException(MEMBERS); long projectId = pid.longValue(); - var project = projects.load(projectId); - projects.loadMembers(List.of(project)); + var project = registry.projectService().load(projectId); + registry.projectService().loadMembers(List.of(project)); var member = project.members().get(user.id()); if (member == null || member.permission() == READ_ONLY) throw forbidden("You are not allowed to create new tasks in this project"); for (var key : memberData.keySet()){ @@ -326,7 +314,7 @@ public class TaskModule extends BaseHandler implements TaskService { } if (json.has(TAGS) && json.get(TAGS) instanceof JSONArray arr){ var tagList = arr.toList().stream().filter(e -> e instanceof String).map(String.class::cast).toList(); - tags.save(TASK,task.id(),null,tagList); + registry.tagService().save(TASK,task.id(),null,tagList); } return sendContent(ex,loadMembers(task)); } @@ -347,14 +335,4 @@ public class TaskModule extends BaseHandler implements TaskService { if (isSet(parentTaskId)) return sendContent(ex,mapValues(taskDb.listChildrenOf(parentTaskId,user,showClosed))); return sendEmptyResponse(HTTP_NOT_IMPLEMENTED,ex); } - - @Override - public ProjectService projectService() { - return projects; - } - - @Override - public UserService userService() { - return users; - } } diff --git a/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java b/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java index d981fe4..36bddbe 100644 --- a/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java +++ b/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java @@ -14,6 +14,7 @@ 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.*; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.*; @@ -25,27 +26,23 @@ public class TimeModule extends BaseHandler implements TimeService { private class ExtendedTime extends Time{ - private final Collection tasks; + private final Collection tasks; public ExtendedTime(long id, long userId, String subject, String description, LocalDateTime start, LocalDateTime end, State state, Collection tasks) { super(id, userId, subject, description, start, end, state, tasks.stream().map(Task::id).toList()); this.tasks = tasks; } + } - private final UserService users; + private final ModuleRegistry registry; private final TimeDb timeDb; - private final TaskService tasks; - private final CompanyService companies; - private final ProjectService projects; - - public TimeModule(Configuration config, TaskService taskService) throws UmbrellaException { - companies = taskService.companyService(); - projects = taskService.projectService(); - tasks = taskService; - users = tasks.userService(); + + + public TimeModule(ModuleRegistry registry, Configuration config) throws UmbrellaException { var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); timeDb = new SqliteDb(connect(dbFile)); + this.registry = registry.add(this); } @Override @@ -53,7 +50,7 @@ public class TimeModule extends BaseHandler implements TimeService { addCors(ex); try { Optional token = SessionToken.from(ex).map(Token::of); - var user = users.loadUser(token); + var user = registry.userService().loadUser(token); if (user.isEmpty()) return unauthorized(ex); var head = path.pop(); return switch (head) { @@ -109,11 +106,11 @@ public class TimeModule extends BaseHandler implements TimeService { var json = json(ex); if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID); var companyId = cid.longValue(); - var company = companies.get(companyId); - if (!companies.membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); + var company = registry.companyService().get(companyId); + if (!registry.companyService().membership(companyId,user.id())) throw forbidden("You are mot a member of company {0}",company.name()); if (!(json.has(PROJECT_ID) && json.get(PROJECT_ID) instanceof Number pid)) throw missingFieldException(PROJECT_ID); long projectId = pid.longValue(); - Map tasksOfProject = tasks.listProjectTasks(projectId); + Map tasksOfProject = registry.taskService().listProjectTasks(projectId); List> times = timeDb.listTimes(tasksOfProject.keySet()) .stream().filter(not(Time::isClosed)) diff --git a/translations/src/main/java/de/srsoftware/umbrella/translations/Translations.java b/translations/src/main/java/de/srsoftware/umbrella/translations/Translations.java index 3fdb970..0f2ab66 100644 --- a/translations/src/main/java/de/srsoftware/umbrella/translations/Translations.java +++ b/translations/src/main/java/de/srsoftware/umbrella/translations/Translations.java @@ -7,6 +7,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.sun.net.httpserver.HttpExchange; import de.srsoftware.tools.Path; import de.srsoftware.tools.PathHandler; +import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.api.Translator; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -20,6 +21,10 @@ public class Translations extends PathHandler implements Translator { private HashMap translations = new HashMap<>(); + public Translations(ModuleRegistry registry) { + registry.add(this); + } + @Override public boolean doGet(Path path, HttpExchange ex) throws IOException { allowOrigin(ex,"*"); diff --git a/user/src/main/java/de/srsoftware/umbrella/user/UserModule.java b/user/src/main/java/de/srsoftware/umbrella/user/UserModule.java index 71e3694..6ed15a4 100644 --- a/user/src/main/java/de/srsoftware/umbrella/user/UserModule.java +++ b/user/src/main/java/de/srsoftware/umbrella/user/UserModule.java @@ -30,7 +30,7 @@ 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.api.PostBox; +import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.api.UserService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; import de.srsoftware.umbrella.core.model.*; @@ -59,6 +59,7 @@ public class UserModule extends BaseHandler implements UserService { private record State(LoginService loginService, JSONObject config){ + public static State of(LoginService loginService, JSONObject config) { return new State(loginService,config); } @@ -68,9 +69,9 @@ public class UserModule extends BaseHandler implements UserService { private static final System.Logger LOG = System.getLogger("User"); private final UserDb users; private final LoginServiceDb logins; + private final ModuleRegistry registry; private final HashMap stateMap = new HashMap<>(); // map from state to OIDC provider name private final HashMap tokenMap = new HashMap<>(); - private final PostBox messages; static { try { @@ -80,16 +81,12 @@ public class UserModule extends BaseHandler implements UserService { } } - public UserModule(Configuration config, PostBox messageSystem) throws UmbrellaException { + public UserModule(ModuleRegistry registry, Configuration config) throws UmbrellaException { var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingConfigException(CONFIG_DATABASE)); // may be splitted in separate db files later - logins = new SqliteDB(connect(dbFile)); - messages = messageSystem; - users = new SqliteDB(connect(dbFile)); - } - - public UserDb userDb(){ - return users; + logins = new SqliteDB(connect(dbFile)); + users = new SqliteDB(connect(dbFile)); + this.registry = registry.add(this); } private boolean deleteOIDC(HttpExchange ex, UmbrellaUser user, Path path) throws IOException { @@ -137,10 +134,15 @@ public class UserModule extends BaseHandler implements UserService { } @Override - public Map list(Collection ids) throws UmbrellaException { + public Map list(Integer start, Integer limit, Collection ids) throws UmbrellaException { return users.list(0,null,ids); } + public UmbrellaUser load(Session session) throws UmbrellaException{ + return users.load(session); + } + + @Override public UmbrellaUser loadUser(long userId) throws UmbrellaException { return users.load(userId); @@ -149,8 +151,8 @@ public class UserModule extends BaseHandler implements UserService { public Optional loadUser(Optional sessionToken) throws UmbrellaException { try { if (sessionToken.isEmpty()) return empty(); - var session = users.load(sessionToken.get()); - return Optional.of(users.load(session)); + var session = load(sessionToken.get()); + return Optional.of(load(session)); } catch (UmbrellaException e) { return empty(); } @@ -256,6 +258,12 @@ public class UserModule extends BaseHandler implements UserService { } } + @Override + public void dropSession(Token token) throws UmbrellaException{ + users.dropSession(token); + } + + private boolean exchangeToken(HttpExchange ex) throws IOException { JSONObject params; try { @@ -296,6 +304,11 @@ public class UserModule extends BaseHandler implements UserService { } } + public Session extend(Session session) throws UmbrellaException{ + return users.extend(session); + } + + private boolean getConnectedServices(HttpExchange ex, UmbrellaUser user) throws IOException { if (user == null) return unauthorized(ex); try { @@ -395,6 +408,11 @@ public class UserModule extends BaseHandler implements UserService { return sendContent(ex,targetUser.toMap()); } + public Session load(Token token) throws UmbrellaException{ + return users.load(token); + } + + public boolean logout(HttpExchange ex, Optional optToken) throws IOException { if (optToken.isPresent()){ var token = optToken.get(); @@ -451,11 +469,6 @@ public class UserModule extends BaseHandler implements UserService { return sendContent(ex,service.toMap()); } - @Override - public PostBox postBox() { - return messages; - } - private boolean postCreate(HttpExchange ex) throws IOException, UmbrellaException { var optUser = loadUser(ex); if (!(optUser.isPresent() && optUser.get() instanceof DbUser dbUser)) return unauthorized(ex); @@ -491,7 +504,7 @@ public class UserModule extends BaseHandler implements UserService { var fills = Map.of("url",url); var message = new Message(user,subject,content,fills,null); var envelope = new Envelope(message,user); - messages.send(envelope); + registry.postBox().send(envelope); } catch (UmbrellaException e){ return send(ex,e); }