1

add sql-export

This commit is contained in:
Christoph
2020-04-17 18:53:09 +02:00
parent 0d79467e0b
commit 95272075d4
10 changed files with 227 additions and 142 deletions

View File

@ -5,8 +5,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.exception.BadPayloadException;
import mops.gruppen2.domain.exception.GroupNotFoundException;
import mops.gruppen2.domain.service.helper.JsonHelper;
import mops.gruppen2.domain.service.helper.FileHelper;
import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO;
import org.springframework.stereotype.Service;
@ -55,15 +54,14 @@ public class EventStoreService {
*/
private static EventDTO getDTOFromEvent(Event event) {
try {
String payload = JsonHelper.serializeEvent(event);
String payload = FileHelper.serializeEventJson(event);
return new EventDTO(null,
event.getGroupid().toString(),
event.getVersion(),
event.getExec(),
event.getTarget(),
event.type(),
payload,
Timestamp.valueOf(event.getDate()));
Timestamp.valueOf(event.getDate()),
payload);
} catch (JsonProcessingException e) {
log.error("Event ({}) konnte nicht serialisiert werden!", event, e);
throw new BadPayloadException(EventStoreService.class.toString());
@ -85,7 +83,7 @@ public class EventStoreService {
private static Event getEventFromDTO(EventDTO dto) {
try {
return JsonHelper.deserializeEvent(dto.getEvent_payload());
return FileHelper.deserializeEventJson(dto.getEvent_payload());
} catch (JsonProcessingException e) {
log.error("Payload {} konnte nicht deserialisiert werden!", dto.getEvent_payload(), e);
throw new BadPayloadException(EventStoreService.class.toString());
@ -104,10 +102,11 @@ public class EventStoreService {
return getEventsFromDTOs(eventStore.findGroupEvents(groupId.toString()));
}
public String findGroupPayloads(UUID groupId) {
return eventStore.findGroupPayloads(groupId.toString()).stream()
.map(payload -> payload + "\n")
.reduce((String payloadA, String payloadB) -> payloadA + payloadB)
.orElseThrow(() -> new GroupNotFoundException("Keine Payloads gefunden."));
public List<String> findGroupPayloads(UUID groupId) {
return eventStore.findGroupPayloads(groupId.toString());
}
public List<EventDTO> findGroupDTOs(UUID groupid) {
return eventStore.findGroupEvents(groupid.toString());
}
}

View File

@ -1,64 +0,0 @@
package mops.gruppen2.domain.service.helper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.WrongFileException;
import mops.gruppen2.domain.model.group.User;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class CsvHelper {
public static List<User> readCsvFile(MultipartFile file) throws EventException {
if (file == null || file.isEmpty()) {
return Collections.emptyList();
}
try {
List<User> userList = read(file.getInputStream());
return userList.stream()
.distinct()
.collect(Collectors.toList()); //filter duplicates from list
} catch (IOException e) {
log.error("File konnte nicht gelesen werden!", e);
throw new WrongFileException(file.getOriginalFilename());
}
}
private static List<User> read(InputStream stream) throws IOException {
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(User.class).withHeader().withColumnReordering(true);
ObjectReader reader = mapper.readerFor(User.class).with(schema);
return reader.<User>readValues(stream).readAll();
}
public static String writeCsvUserList(List<User> members) {
StringBuilder builder = new StringBuilder();
builder.append("id,givenname,familyname,email\n");
members.forEach(user -> builder.append(user.getId())
.append(",")
.append(user.getGivenname())
.append(",")
.append(user.getFamilyname())
.append(",")
.append(user.getEmail())
.append("\n"));
return builder.toString();
}
}

View File

@ -0,0 +1,148 @@
package mops.gruppen2.domain.service.helper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.exception.EventException;
import mops.gruppen2.domain.exception.GroupNotFoundException;
import mops.gruppen2.domain.exception.WrongFileException;
import mops.gruppen2.domain.model.group.User;
import mops.gruppen2.persistance.dto.EventDTO;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class FileHelper {
// ######################################## CSV #############################################
public static List<User> readCsvFile(MultipartFile file) throws EventException {
if (file == null || file.isEmpty()) {
return Collections.emptyList();
}
try {
List<User> userList = readCsv(file.getInputStream());
return userList.stream()
.distinct()
.collect(Collectors.toList()); //filter duplicates from list
} catch (IOException e) {
log.error("File konnte nicht gelesen werden!", e);
throw new WrongFileException(file.getOriginalFilename());
}
}
private static List<User> readCsv(InputStream stream) throws IOException {
CsvMapper mapper = new CsvMapper();
CsvSchema schema = mapper.schemaFor(User.class).withHeader().withColumnReordering(true);
ObjectReader reader = mapper.readerFor(User.class).with(schema);
return reader.<User>readValues(stream).readAll();
}
public static String writeCsvUserList(List<User> members) {
StringBuilder builder = new StringBuilder();
builder.append("id,givenname,familyname,email\n");
members.forEach(user -> builder.append(user.getId())
.append(",")
.append(user.getGivenname())
.append(",")
.append(user.getFamilyname())
.append(",")
.append(user.getEmail())
.append("\n"));
return builder.toString();
}
// ########################################## JSON ###########################################
/**
* Übersetzt eine Java-Event-Repräsentation zu einem JSON-Event-Payload.
*
* @param event Java-Event-Repräsentation
*
* @return JSON-Event-Payload als String
*
* @throws JsonProcessingException Bei JSON Fehler
*/
public static String serializeEventJson(Event event) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
String payload = mapper.writeValueAsString(event);
log.trace(payload);
return payload;
}
/**
* Übersetzt eine JSON-Event-Payload zu einer Java-Event-Repräsentation.
*
* @param json JSON-Event-Payload als String
*
* @return Java-Event-Repräsentation
*
* @throws JsonProcessingException Bei JSON Fehler
*/
public static Event deserializeEventJson(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
Event event = mapper.readValue(json, Event.class);
log.trace(event);
return event;
}
// ############################################### TXT #######################################
public static String payloadsToPlain(List<String> payloads) {
return payloads.stream()
.map(payload -> payload + "\n")
.reduce((String payloadA, String payloadB) -> payloadA + payloadB)
.orElseThrow(() -> new GroupNotFoundException("Keine Payloads gefunden."));
}
public static String eventDTOsToSql(List<EventDTO> dtos) {
StringBuilder builder = new StringBuilder();
builder.append("INSERT INTO event(group_id, group_version, exec_id, target_id, event_date, event_payload)\nVALUES\n");
dtos.forEach(dto -> builder.append("('")
.append(dto.getGroup_id())
.append("','")
.append(dto.getGroup_version())
.append("','")
.append(dto.getExec_id())
.append("','")
.append(dto.getTarget_id())
.append("','")
.append(dto.getEvent_date())
.append("','")
.append(dto.getEvent_payload())
.append("'),\n"));
builder.replace(builder.length() - 2, builder.length(), ";");
return builder.toString();
}
// ############################################### SQL #######################################
}

View File

@ -1,50 +0,0 @@
package mops.gruppen2.domain.service.helper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.event.Event;
/**
* Übersetzt JSON-Event-Payloads zu Java-Event-Repräsentationen und zurück.
*/
@Log4j2
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class JsonHelper {
/**
* Übersetzt eine Java-Event-Repräsentation zu einem JSON-Event-Payload.
*
* @param event Java-Event-Repräsentation
*
* @return JSON-Event-Payload als String
*
* @throws JsonProcessingException Bei JSON Fehler
*/
public static String serializeEvent(Event event) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
String payload = mapper.writeValueAsString(event);
log.trace(payload);
return payload;
}
/**
* Übersetzt eine JSON-Event-Payload zu einer Java-Event-Repräsentation.
*
* @param json JSON-Event-Payload als String
*
* @return Java-Event-Repräsentation
*
* @throws JsonProcessingException Bei JSON Fehler
*/
public static Event deserializeEvent(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
Event event = mapper.readValue(json, Event.class);
log.trace(event);
return event;
}
}

View File

@ -11,7 +11,7 @@ import mops.gruppen2.domain.model.group.wrapper.Limit;
import mops.gruppen2.domain.model.group.wrapper.Parent;
import mops.gruppen2.domain.model.group.wrapper.Title;
import mops.gruppen2.domain.service.GroupService;
import mops.gruppen2.domain.service.helper.CsvHelper;
import mops.gruppen2.domain.service.helper.FileHelper;
import mops.gruppen2.domain.service.helper.ValidationHelper;
import mops.gruppen2.infrastructure.GroupCache;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@ -67,7 +67,7 @@ public class GroupCreationController {
// ROLE_studentin kann kein CSV importieren
if (token.getAccount().getRoles().contains("orga")) {
groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file));
groupService.addUsersToGroup(group, principal, FileHelper.readCsvFile(file));
}
return "redirect:/gruppen2/details/" + group.getId();

View File

@ -10,7 +10,7 @@ import mops.gruppen2.domain.model.group.wrapper.Limit;
import mops.gruppen2.domain.model.group.wrapper.Title;
import mops.gruppen2.domain.service.EventStoreService;
import mops.gruppen2.domain.service.GroupService;
import mops.gruppen2.domain.service.helper.CsvHelper;
import mops.gruppen2.domain.service.helper.FileHelper;
import mops.gruppen2.domain.service.helper.ValidationHelper;
import mops.gruppen2.infrastructure.GroupCache;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
@ -112,9 +112,9 @@ public class GroupDetailsController {
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping(value = "details/{id}/export/history", produces = "text/plain;charset=UTF-8")
public void getDetailsExportHistory(HttpServletResponse response,
@PathVariable("id") String groupId) {
@GetMapping(value = "details/{id}/export/history/plain", produces = "text/plain;charset=UTF-8")
public void getDetailsExportHistoryPlain(HttpServletResponse response,
@PathVariable("id") String groupId) {
String filename = "eventlog-" + groupId + ".txt";
@ -123,7 +123,29 @@ public class GroupDetailsController {
"attachment; filename=\"" + filename + "\"");
try {
response.getWriter().write(eventStoreService.findGroupPayloads(UUID.fromString(groupId)));
response.getWriter()
.write(FileHelper.payloadsToPlain(
eventStoreService.findGroupPayloads(UUID.fromString(groupId))));
} catch (IOException e) {
log.error("Payloads konnten nicht geschrieben werden.", e);
}
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping(value = "details/{id}/export/history/sql", produces = "application/sql;charset=UTF-8")
public void getDetailsExportHistorySql(HttpServletResponse response,
@PathVariable("id") String groupId) {
String filename = "data.sql";
response.setContentType("application/sql;charset=UTF-8");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"");
try {
response.getWriter()
.write(FileHelper.eventDTOsToSql(
eventStoreService.findGroupDTOs(UUID.fromString(groupId))));
} catch (IOException e) {
log.error("Payloads konnten nicht geschrieben werden.", e);
}
@ -142,7 +164,7 @@ public class GroupDetailsController {
try {
response.getWriter()
.print(CsvHelper.writeCsvUserList(groupCache.group(UUID.fromString(groupId)).getMembers()));
.print(FileHelper.writeCsvUserList(groupCache.group(UUID.fromString(groupId)).getMembers()));
} catch (IOException e) {
log.error("Teilnehmerliste konnte nicht geschrieben werden.", e);
}
@ -209,7 +231,7 @@ public class GroupDetailsController {
String principal = token.getName();
Group group = groupCache.group(UUID.fromString(groupId));
groupService.addUsersToGroup(group, principal, CsvHelper.readCsvFile(file));
groupService.addUsersToGroup(group, principal, FileHelper.readCsvFile(file));
return "redirect:/gruppen2/details/" + groupId + "/edit";
}

View File

@ -21,8 +21,6 @@ public class EventDTO {
String exec_id;
String target_id;
String event_type;
Timestamp event_date;
String event_payload;
Timestamp timestamp;
}

View File

@ -0,0 +1,27 @@
INSERT INTO event(group_id, group_version, exec_id, target_id, event_date, event_payload)
VALUES ('e65dd5f1-b252-4512-8db4-0407b31d199f', '1', 'orga', 'null', '2020-04-17 18:52:01.259555',
'{"class":"CREATEGROUP","date":[2020,4,17,18,52,1,259555000],"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":1,"exec":"orga","target":null}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '2', 'orga', 'orga', '2020-04-17 18:52:01.291448',
'{"class":"ADDMEMBER","user":{"id":"orga","givenname":"Thomas","familyname":"Organisator","email":"orga@hhu.de"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":2,"exec":"orga","target":"orga","date":[2020,4,17,18,52,1,291448000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '3', 'orga', 'orga', '2020-04-17 18:52:01.301972',
'{"class":"UPDATEROLE","role":"ADMIN","groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":3,"exec":"orga","target":"orga","date":[2020,4,17,18,52,1,301972000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '4', 'orga', 'null', '2020-04-17 18:52:01.3053',
'{"class":"SETLIMIT","limit":{"value":1000},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":4,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,305300000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '5', 'orga', 'null', '2020-04-17 18:52:01.310406',
'{"class":"SETTYPE","type":"LECTURE","groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":5,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,310406000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '6', 'orga', 'null', '2020-04-17 18:52:01.313421',
'{"class":"SETPARENT","parent":{"id":"00000000-0000-0000-0000-000000000000"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":6,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,313421000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '7', 'orga', 'null', '2020-04-17 18:52:01.317931',
'{"class":"SETTITLE","title":{"value":"Programmierung SS2020"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":7,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,317931000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '8', 'orga', 'null', '2020-04-17 18:52:01.320693',
'{"class":"SETDESCRIPTION","desc":{"value":"Einführung in die objektorientierte Programmierung mit Java."},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":8,"exec":"orga","target":null,"date":[2020,4,17,18,52,1,320693000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '9', 'orga', 'A5ggd', '2020-04-17 18:52:01.330879',
'{"class":"ADDMEMBER","user":{"id":"A5ggd","givenname":"peter","familyname":"pan","email":"peter@pan.com"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":9,"exec":"orga","target":"A5ggd","date":[2020,4,17,18,52,1,330879000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '10', 'orga', 'Affs', '2020-04-17 18:52:01.333756',
'{"class":"ADDMEMBER","user":{"id":"Affs","givenname":"olaf","familyname":"pomodoro","email":"ol@f-99.de"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":10,"exec":"orga","target":"Affs","date":[2020,4,17,18,52,1,333756000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '11', 'orga', '55fdd', '2020-04-17 18:52:01.336206',
'{"class":"ADDMEMBER","user":{"id":"55fdd","givenname":"dieter","familyname":"niemöller","email":"pfarrer@erde.de"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":11,"exec":"orga","target":"55fdd","date":[2020,4,17,18,52,1,336206000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '12', 'orga', '22ööl', '2020-04-17 18:52:01.338582',
'{"class":"ADDMEMBER","user":{"id":"22ööl","givenname":"thomas","familyname":"müller","email":"thot@scheisse.de"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":12,"exec":"orga","target":"22ööl","date":[2020,4,17,18,52,1,338582000]}'),
('e65dd5f1-b252-4512-8db4-0407b31d199f', '13', 'orga', 'tdsd8', '2020-04-17 18:52:01.341216',
'{"class":"ADDMEMBER","user":{"id":"tdsd8","givenname":"tobidignouserandingdong","familyname":"abraham","email":"g@gmail.mail"},"groupid":"e65dd5f1-b252-4512-8db4-0407b31d199f","version":13,"exec":"orga","target":"tdsd8","date":[2020,4,17,18,52,1,341216000]}');

View File

@ -7,7 +7,6 @@ CREATE TABLE event
group_version INT NOT NULL,
exec_id VARCHAR(50) NOT NULL,
target_id VARCHAR(50),
event_type VARCHAR(32) NOT NULL,
timestamp DATETIME NOT NULL,
event_date DATETIME NOT NULL,
event_payload VARCHAR(2500) NOT NULL
);

View File

@ -100,9 +100,15 @@
<span class="col"></span>
<a class="btn btn-info btn-bar mr-2"
th:href="@{/gruppen2/details/{id}/export/history(id=${group.getId()})}"
th:href="@{/gruppen2/details/{id}/export/history/plain(id=${group.getId()})}"
title="Exportiert die gesamte Event-Historie dieser Gruppe. Kann beim erstellen importiert werden.">
Event-Log exportieren
Event-Log exportieren (TXT)
</a>
<a class="btn btn-info btn-bar mr-2"
th:href="@{/gruppen2/details/{id}/export/history/sql(id=${group.getId()})}"
title="Exportiert die gesamte Event-Historie dieser Gruppe. Kann als data.sql verwendet werden.">
Event-Log exportieren (SQL)
</a>
<a class="btn btn-info btn-bar"