OpenSource Projekt-Management-Software
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

189 lines
6.8 KiB

/* © SRSoftware 2025 */
package de.srsoftware.umbrella.notes;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
import static de.srsoftware.umbrella.core.Constants.*;
import static de.srsoftware.umbrella.core.Constants.FULLTEXT;
import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
import static de.srsoftware.umbrella.core.Paths.SEARCH;
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_UNPROCESSABLE;
import static de.srsoftware.umbrella.core.Util.mapValues;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.notes.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.NoteService;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Note;
import de.srsoftware.umbrella.core.model.Token;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class NoteModule extends BaseHandler implements NoteService {
private final NotesDb notesDb;
public NoteModule(Configuration config) {
super();
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE));
notesDb = new SqliteDb(connect(dbFile));
ModuleRegistry.add(this);
}
@Override
public void deleteEntity(String module, String entityId) {
notesDb.deleteEntity(module,entityId);
}
@Override
public boolean doDelete(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();
if (head == null) throw unprocessable("Module missing in path.");
// try {
var noteId = Long.parseLong(head);
var note = notesDb.load(noteId);
if (note.authorId() != user.get().id()) throw forbidden("You are not allowed to delete notes of another user");
return sendContent(ex, notesDb.delete(noteId));
/* } catch (NumberFormatException ignored) {
var module = head;
head = path.pop();
long entityId = Long.parseLong(head);
notesDb.deleteEntity(module,entityId);
return sendContent(ex, entityId);
}*/
} catch (NumberFormatException e){
return sendContent(ex,HTTP_UNPROCESSABLE,"Entity id missing in path.");
} catch (UmbrellaException e){
return send(ex,e);
}
}
@Override
public boolean doGet(Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
var user = userService().refreshSession(ex);
if (user.isEmpty()) return unauthorized(ex);
var module = path.pop();
return switch (module){
case null -> sendContent(ex,getUserNotes(ex,user.get()));
default -> sendContent(ex,getEntityNotes(module,path.pop()));
};
} catch (NumberFormatException e){
return sendContent(ex,HTTP_UNPROCESSABLE,"Entity id missing in path.");
} catch (UmbrellaException e){
return send(ex,e);
}
}
private boolean getUserNotes(HttpExchange ex, UmbrellaUser user) throws IOException {
var param = queryParam(ex);
long offset = switch (param.get(OFFSET)){
case String s -> Long.parseLong(s);
case Number n -> n.longValue();
case null, default -> 0;
};
long limit = switch (param.get(LIMIT)){
case String s -> Long.parseLong(s);
case Number n -> n.longValue();
case null, default -> 100;
};
var notes = notesDb.list(user.id(),offset,limit);
return sendContent(ex,addUsers(notes));
}
private Map<String, Object> addUsers(Map<Long, Note> notes) {
var authors = notes.values().stream().map(Note::authorId).distinct().map(userService()::loadUser).collect(Collectors.toMap(UmbrellaUser::id,UmbrellaUser::toMap));
return Map.of("notes",mapValues(notes),"authors",authors);
}
@Override
public boolean doPatch(Path path, HttpExchange ex) throws IOException {
addCors(ex);
try {
Optional<Token> token = SessionToken.from(ex).map(Token::of);
var user = userService().refreshSession(ex);
if (user.isEmpty()) return unauthorized(ex);
var head = path.pop();
long noteId = Long.parseLong(head);
String text = body(ex);
if (text.isBlank()) throw missingFieldException("Note text");
var note = notesDb.load(noteId);
if (note.authorId() != user.get().id()) throw forbidden("You are not allowed to edit notes of another user!");
note = new Note(note.id(),note.module(),note.entityId(),note.authorId(),text,LocalDateTime.now());
note = save(note);
return sendContent(ex,note);
} catch (NumberFormatException e) {
return sendContent(ex, HTTP_UNPROCESSABLE, "Entity id missing in path.");
} catch (UmbrellaException e){
return send(ex,e);
}
}
@Override
public boolean doPost(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 module = path.pop();
if (SEARCH.equals(module)) return postSearch(ex,user.get());
if (module == null) throw unprocessable("Module missing in path.");
var entityId = path.pop();
if (entityId == null || entityId.isBlank()) throw missingFieldException(ENTITY_ID);
String text = body(ex);
if (text.isBlank()) throw missingFieldException("Note text");
var note = new Note(0,module,entityId,user.get().id(),text, LocalDateTime.now());
note = save(note);
return sendContent(ex, note);
} catch (NumberFormatException e) {
return sendContent(ex, HTTP_UNPROCESSABLE, "Entity id missing in path.");
} catch (UmbrellaException e) {
return send(ex, e);
}
}
public Map<String,Object> getEntityNotes(String module, String entityId) throws UmbrellaException{
return addUsers(getNotes(module, entityId));
}
@Override
public Map<Long, Note> getNotes(String module, String entityId) throws UmbrellaException {
return notesDb.list(module, entityId);
}
private boolean postSearch(HttpExchange ex, UmbrellaUser user) throws IOException {
var json = json(ex);
if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingFieldException(KEY);
var keys = Arrays.asList(key.split(" "));
var fulltext = json.has(FULLTEXT) && json.get(FULLTEXT) instanceof Boolean val && val;
var notes = notesDb.find(user.id(),keys,fulltext);
return sendContent(ex,mapValues(notes));
}
@Override
public Note save(Note note) {
return notesDb.save(note);
}
@Override
public void updateId(String module, Object oldId, Object newId) {
notesDb.updateId(module,oldId,newId);
}
}