1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package eu.ehri.project.utils.fixtures.impl;
21
22 import com.google.common.collect.ArrayListMultimap;
23 import com.google.common.collect.ImmutableMap;
24 import com.google.common.collect.Lists;
25 import com.google.common.collect.Maps;
26 import com.google.common.collect.Multimap;
27 import com.tinkerpop.blueprints.Direction;
28 import com.tinkerpop.blueprints.Vertex;
29 import com.tinkerpop.frames.FramedGraph;
30 import eu.ehri.project.core.GraphManager;
31 import eu.ehri.project.core.GraphManagerFactory;
32 import eu.ehri.project.exceptions.DeserializationError;
33 import eu.ehri.project.exceptions.ValidationError;
34 import eu.ehri.project.models.EntityClass;
35 import eu.ehri.project.models.base.Entity;
36 import eu.ehri.project.persistence.Bundle;
37 import eu.ehri.project.persistence.BundleManager;
38 import eu.ehri.project.persistence.Mutation;
39 import eu.ehri.project.utils.GraphInitializer;
40 import eu.ehri.project.utils.fixtures.FixtureLoader;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.yaml.snakeyaml.Yaml;
44
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.nio.file.Files;
48 import java.nio.file.Path;
49 import java.nio.file.Paths;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Map.Entry;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public class YamlFixtureLoader implements FixtureLoader {
80
81 private static final boolean DEFAULT_INIT = true;
82 private static final String GENERATE_ID_PLACEHOLDER = "?";
83 private static final String DEFAULT_FIXTURE_FILE = "testdata.yaml";
84
85 private static final Logger logger = LoggerFactory.getLogger(YamlFixtureLoader.class);
86
87 private final FramedGraph<?> graph;
88 private final GraphManager manager;
89 private final BundleManager dao;
90 private final boolean initialize;
91 private final GraphInitializer initializer;
92
93
94
95
96
97
98
99 public YamlFixtureLoader(FramedGraph<?> graph, boolean initialize) {
100 this.graph = graph;
101 this.initialize = initialize;
102 manager = GraphManagerFactory.getInstance(graph);
103 dao = new BundleManager(graph);
104 initializer = new GraphInitializer(graph);
105 }
106
107
108
109
110
111
112 public YamlFixtureLoader(FramedGraph<?> graph) {
113 this(graph, DEFAULT_INIT);
114 }
115
116
117
118
119
120
121
122
123 public YamlFixtureLoader setInitializing(boolean initialize) {
124 return new YamlFixtureLoader(graph, initialize);
125 }
126
127
128
129
130 private void loadFixtures() {
131 try (InputStream ios = this.getClass().getClassLoader()
132 .getResourceAsStream(DEFAULT_FIXTURE_FILE)) {
133 loadTestData(ios);
134 } catch (IOException e) {
135 throw new RuntimeException(e);
136 }
137 }
138
139
140
141
142
143
144
145 public void loadTestData(String resourceNameOrPath) {
146 Path path = Paths.get(resourceNameOrPath);
147 try (InputStream stream = Files.isRegularFile(path)
148 ? Files.newInputStream(path)
149 : this.getClass().getClassLoader()
150 .getResourceAsStream(resourceNameOrPath)) {
151 loadTestData(stream);
152 } catch (IOException e) {
153 throw new RuntimeException(e);
154 }
155 }
156
157
158
159
160
161
162 public void loadTestData(InputStream stream) {
163
164 try {
165 if (initialize) {
166 initializer.initialize();
167 }
168 loadFixtureFileStream(stream);
169 } catch (Exception e) {
170 throw new RuntimeException(e);
171 }
172 }
173
174 @SuppressWarnings("unchecked")
175 private void loadFixtureFileStream(InputStream yamlStream) {
176 Yaml yaml = new Yaml();
177 try {
178 Map<Vertex, Multimap<String, String>> links = Maps.newHashMap();
179 for (Object data : yaml.loadAll(yamlStream)) {
180 for (Object node : (List<?>) data) {
181 if (node instanceof Map) {
182 logger.trace("Importing node: {}", node);
183 importNode(links, (Map<String, Object>) node);
184 }
185 }
186 }
187
188
189
190 logger.trace("Linking data...");
191 for (Entry<Vertex, Multimap<String, String>> entry : links.entrySet()) {
192 logger.trace("Setting links for: {}", entry.getKey());
193 Vertex src = entry.getKey();
194 Multimap<String, String> rels = entry.getValue();
195 for (String relname : rels.keySet()) {
196 for (String target : rels.get(relname)) {
197 Vertex dst = manager.getVertex(target);
198 addRelationship(src, dst, relname);
199 }
200 }
201 }
202 } catch (Exception e) {
203 throw new RuntimeException("Error loading YAML fixture", e);
204 }
205 }
206
207 private void addRelationship(Vertex src, Vertex dst, String relname) {
208 boolean found = false;
209 for (Vertex v : src.getVertices(Direction.OUT, relname)) {
210 if (v.equals(dst)) {
211 found = true;
212 break;
213 }
214 }
215 if (!found) {
216 logger.trace(String.format(" - %s -[%s]-> %s", src, dst, relname));
217 graph.addEdge(null, src, dst, relname);
218 }
219 }
220
221 private void importNode(Map<Vertex, Multimap<String, String>> links,
222 Map<String, Object> node) throws DeserializationError, ValidationError {
223 EntityClass isa = EntityClass.withName((String) node
224 .get(Bundle.TYPE_KEY));
225
226 String id = (String) node.get(Bundle.ID_KEY);
227
228 @SuppressWarnings("unchecked")
229 Map<String, Object> nodeData = (Map<String, Object>) node
230 .get(Bundle.DATA_KEY);
231 if (nodeData == null) {
232 nodeData = Maps.newHashMap();
233 }
234 @SuppressWarnings("unchecked")
235 Map<String, Object> nodeRels = (Map<String, Object>) node
236 .get(Bundle.REL_KEY);
237
238
239
240 Bundle entityBundle = createBundle(id, isa, nodeData,
241 getDependentRelations(nodeRels));
242 logger.trace("Creating node with id: {}", id);
243 Mutation<Entity> frame = dao.createOrUpdate(entityBundle, Entity.class);
244
245 Multimap<String, String> linkRels = getLinkedRelations(nodeRels);
246 if (!linkRels.isEmpty()) {
247 links.put(frame.getNode().asVertex(), linkRels);
248 }
249 }
250
251 private Bundle createBundle(String id, EntityClass type,
252 Map<String, Object> nodeData,
253 Multimap<String, Map<?, ?>> dependentRelations) throws DeserializationError {
254 Map<String, Object> data = ImmutableMap.of(
255 Bundle.ID_KEY, id,
256 Bundle.TYPE_KEY, type.getName(),
257 Bundle.DATA_KEY, nodeData,
258 Bundle.REL_KEY, dependentRelations.asMap()
259 );
260 Bundle b = Bundle.fromData(data);
261
262
263 if (id.trim().contentEquals(GENERATE_ID_PLACEHOLDER)) {
264 String newId = type.getIdGen().generateId(Lists.<String>newArrayList(), b);
265 b = b.withId(newId);
266 }
267 return b;
268 }
269
270 private Multimap<String, String> getLinkedRelations(Map<String, Object> data) {
271 Multimap<String, String> rels = ArrayListMultimap.create();
272 if (data != null) {
273 for (Entry<String, Object> entry : data.entrySet()) {
274 String relName = entry.getKey();
275 Object relValue = entry.getValue();
276 if (relValue instanceof List) {
277 for (Object relation : (List<?>) relValue) {
278 if (relation instanceof String) {
279 rels.put(relName, (String) relation);
280 }
281 }
282 } else if (relValue instanceof String) {
283 rels.put(relName, (String) relValue);
284 }
285 }
286 }
287 return rels;
288 }
289
290 private Multimap<String, Map<?, ?>> getDependentRelations(Map<String, Object> data) {
291 Multimap<String, Map<?, ?>> rels = ArrayListMultimap.create();
292 if (data != null) {
293 for (Entry<String, Object> entry : data.entrySet()) {
294 String relName = entry.getKey();
295 Object relValue = entry.getValue();
296 if (relValue instanceof List) {
297 for (Object relation : (List<?>) relValue) {
298 if (relation instanceof Map) {
299 rels.put(relName, (Map<?, ?>) relation);
300 }
301 }
302 } else if (relValue instanceof Map) {
303 rels.put(relName, (Map<?, ?>) relValue);
304 }
305 }
306 }
307 return rels;
308 }
309
310 public void loadTestData() {
311 loadFixtures();
312 }
313 }