1 package de.dlr.shepard.context.export;
2
3 import com.fasterxml.jackson.databind.ObjectMapper;
4 import de.dlr.shepard.auth.users.entities.User;
5 import de.dlr.shepard.common.neo4j.io.AbstractDataObjectIO;
6 import de.dlr.shepard.common.neo4j.io.BasicEntityIO;
7 import de.dlr.shepard.context.collection.entities.Collection;
8 import de.dlr.shepard.context.collection.entities.DataObject;
9 import de.dlr.shepard.context.collection.io.CollectionIO;
10 import de.dlr.shepard.context.collection.io.DataObjectIO;
11 import de.dlr.shepard.context.labJournal.io.LabJournalEntryIO;
12 import de.dlr.shepard.context.references.basicreference.io.BasicReferenceIO;
13 import edu.kit.datamanager.ro_crate.RoCrate.RoCrateBuilder;
14 import edu.kit.datamanager.ro_crate.entities.contextual.PersonEntity;
15 import edu.kit.datamanager.ro_crate.entities.data.FileEntity;
16 import edu.kit.datamanager.ro_crate.entities.data.FileEntity.FileEntityBuilder;
17 import io.quarkus.logging.Log;
18 import java.io.ByteArrayInputStream;
19 import java.io.ByteArrayOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.time.LocalDateTime;
23 import java.time.ZoneId;
24 import java.time.format.DateTimeFormatter;
25 import java.util.ArrayList;
26 import java.util.Date;
27 import java.util.List;
28 import java.util.zip.ZipEntry;
29 import java.util.zip.ZipOutputStream;
30 import lombok.AccessLevel;
31 import lombok.Getter;
32
33 public class ExportBuilder {
34
35 private ObjectMapper objectMapper = new ObjectMapper();
36
37 @Getter(value = AccessLevel.PROTECTED)
38 private RoCrateBuilder roCrateBuilder;
39
40 private ByteArrayOutputStream baos;
41 private ZipOutputStream zos;
42 private List<String> entries;
43
44 public ExportBuilder(Collection collection) throws IOException {
45 baos = new ByteArrayOutputStream();
46 zos = new ZipOutputStream(baos);
47 entries = new ArrayList<>();
48
49 var roCrateName = collection.getName() + " Research Object Crate";
50 var roCrateDescription = "Research Object Crate representing the shepard Collection " + collection.getName();
51
52 roCrateBuilder = new RoCrateBuilder().addName(roCrateName).addDescription(roCrateDescription);
53
54 var collectionEntity = createFileEntity(new CollectionIO(collection));
55 roCrateBuilder.addDataEntity(collectionEntity);
56 addPersonEntity(collection.getCreatedBy());
57 }
58
59 public ExportBuilder addDataObject(DataObject dataObject) throws IOException {
60 var dataObjectEntity = createFileEntity(new DataObjectIO(dataObject));
61 roCrateBuilder.addDataEntity(dataObjectEntity);
62 addPersonEntity(dataObject.getCreatedBy());
63 return this;
64 }
65
66 public ExportBuilder addReference(BasicReferenceIO reference, User author) throws IOException {
67 var referenceEntity = createFileEntity(reference);
68 roCrateBuilder.addDataEntity(referenceEntity);
69 addPersonEntity(author);
70 return this;
71 }
72
73 public ExportBuilder addLabJournalEntry(LabJournalEntryIO entry, User author) throws IOException {
74 var referenceEntity = createFileEntity(entry);
75 roCrateBuilder.addDataEntity(referenceEntity);
76 addPersonEntity(author);
77 return this;
78 }
79
80 public ExportBuilder addPayload(byte[] payload, String filename, String name) throws IOException {
81 addToZip(filename, payload);
82 roCrateBuilder.addDataEntity(createFileEntityBuilder(filename, name).build());
83 return this;
84 }
85
86 public ExportBuilder addPayload(byte[] payload, String filename, String name, String encodingFormat)
87 throws IOException {
88 addToZip(filename, payload);
89 roCrateBuilder.addDataEntity(createFileEntityBuilder(filename, name, encodingFormat).build());
90 return this;
91 }
92
93 private void addPersonEntity(User user) {
94 if (user == null) return;
95
96 var entity = new PersonEntity.PersonEntityBuilder()
97 .setId(user.getUsername())
98 .setEmail(user.getEmail())
99 .setGivenName(user.getFirstName())
100 .setFamilyName(user.getLastName())
101 .build();
102
103 roCrateBuilder.addContextualEntity(entity);
104 }
105
106 public InputStream build() throws IOException {
107 var roCrate = roCrateBuilder.build();
108 Object jsonObject = objectMapper.readValue(roCrate.getJsonMetadata(), Object.class);
109 byte[] bytes = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(jsonObject);
110 addToZip(ExportConstants.ROCRATE_METADATA, bytes);
111 zos.close();
112
113 return new ByteArrayInputStream(baos.toByteArray());
114 }
115
116 private FileEntity createFileEntity(AbstractDataObjectIO entity) throws IOException {
117 var filename = entity.getId() + ExportConstants.JSON_FILE_EXTENSION;
118 writeEntityToZip(filename, entity);
119 var fileEntityBuilder = createFileEntityBuilder(entity, filename);
120 fileEntityBuilder.addProperty(ExportConstants.DESCRIPTION_PROP, entity.getDescription());
121 return fileEntityBuilder.build();
122 }
123
124 private FileEntity createFileEntity(LabJournalEntryIO entry) throws IOException {
125 var filename = entry.getId() + ExportConstants.JSON_FILE_EXTENSION;
126 writeEntityToZip(filename, entry);
127 var fileEntityBuilder = createFileEntityBuilder(entry, filename);
128 return fileEntityBuilder.build();
129 }
130
131 private FileEntity createFileEntity(BasicReferenceIO entity) throws IOException {
132 var filename = entity.getId() + ExportConstants.JSON_FILE_EXTENSION;
133 writeEntityToZip(filename, entity);
134 var fileEntityBuilder = createFileEntityBuilder(entity, filename);
135 fileEntityBuilder.addProperty(ExportConstants.TYPE_PROP, entity.getType());
136 return fileEntityBuilder.build();
137 }
138
139 private void writeEntityToZip(String filename, Object entity) throws IOException {
140 var bytes = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(entity);
141 addToZip(filename, bytes);
142 }
143
144 private static FileEntityBuilder createFileEntityBuilder(String filename, String name) {
145 return new FileEntity.FileEntityBuilder().setId(filename).addProperty(ExportConstants.NAME_PROP, name);
146 }
147
148 private static FileEntityBuilder createFileEntityBuilder(String filename, String name, String encodingFormat) {
149 var fileEntityBuilder = createFileEntityBuilder(filename, name);
150 fileEntityBuilder.addProperty("encodingFormat", encodingFormat);
151 return fileEntityBuilder;
152 }
153
154 private static FileEntityBuilder createFileEntityBuilder(LabJournalEntryIO entry, String filename) {
155 var createdAt = convertToIsoDate(entry.getCreatedAt());
156 var type = entry.getClass().getSimpleName().replace("IO", "");
157 var fileEntityBuilder = new FileEntity.FileEntityBuilder().setId(filename);
158 fileEntityBuilder.addProperty("encodingFormat", "application/json");
159 fileEntityBuilder
160 .addAuthor(entry.getCreatedBy())
161 .addProperty(ExportConstants.CREATED_PROP, createdAt)
162 .addProperty(ExportConstants.TYPE_PROP, type);
163 return fileEntityBuilder;
164 }
165
166 private static FileEntityBuilder createFileEntityBuilder(BasicEntityIO entity, String filename) {
167 var createdAt = convertToIsoDate(entity.getCreatedAt());
168 var type = entity.getClass().getSimpleName().replace("IO", "");
169 var fileEntityBuilder = createFileEntityBuilder(filename, entity.getName(), "application/json");
170 fileEntityBuilder
171 .addAuthor(entity.getCreatedBy())
172 .addProperty(ExportConstants.CREATED_PROP, createdAt)
173 .addProperty(ExportConstants.TYPE_PROP, type);
174 if (entity.getUpdatedAt() != null) fileEntityBuilder.addProperty(
175 ExportConstants.UPDATED_PROP,
176 convertToIsoDate(entity.getUpdatedAt())
177 );
178
179 return fileEntityBuilder;
180 }
181
182 private static String convertToIsoDate(Date dateToConvert) {
183 var localDateTime = LocalDateTime.ofInstant(dateToConvert.toInstant(), ZoneId.systemDefault());
184 return DateTimeFormatter.ISO_DATE_TIME.format(localDateTime);
185 }
186
187 private void addToZip(String filename, byte[] bytes) throws IOException {
188 if (entries.contains(filename)) {
189 Log.warnf("%s already in zip file, skipping", filename);
190 return;
191 }
192
193 zos.putNextEntry(new ZipEntry(filename));
194 zos.write(bytes);
195 zos.closeEntry();
196 entries.add(filename);
197 }
198 }