1 package eu.ehri.project.tools;
2
3 import com.fasterxml.jackson.databind.JsonNode;
4 import com.fasterxml.jackson.databind.ObjectMapper;
5 import com.fasterxml.jackson.databind.node.ArrayNode;
6 import com.fasterxml.jackson.databind.node.ObjectNode;
7 import com.google.common.base.Preconditions;
8 import com.google.common.collect.ImmutableMap;
9 import com.tinkerpop.blueprints.Vertex;
10 import com.tinkerpop.frames.FramedGraph;
11 import eu.ehri.project.core.GraphManager;
12 import eu.ehri.project.core.GraphManagerFactory;
13 import eu.ehri.project.definitions.Entities;
14 import eu.ehri.project.definitions.Ontology;
15 import eu.ehri.project.exceptions.ItemNotFound;
16 import eu.ehri.project.models.annotations.EntityType;
17 import eu.ehri.project.models.idgen.GenericIdGenerator;
18 import eu.ehri.project.persistence.ActionManager;
19 import eu.ehri.project.persistence.Bundle;
20 import org.joda.time.DateTime;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import java.io.IOException;
25 import java.util.Iterator;
26 import java.util.Map;
27 import java.util.UUID;
28
29
30
31
32
33
34
35
36
37
38
39
40 public class DbUpgrader1to2 {
41 public static final Map<String, String> changeMap = ImmutableMap.<String, String>builder()
42 .put("historicalAgent", "HistoricalAgent")
43 .put("repository", "Repository")
44 .put("documentaryUnit", "DocumentaryUnit")
45 .put("virtualUnit", "VirtualUnit")
46 .put("systemEvent", "SystemEvent")
47 .put("documentDescription", "DocumentaryUnitDescription")
48 .put("repositoryDescription", "RepositoryDescription")
49 .put("historicalAgentDescription", "HistoricalAgentDescription")
50 .put("group", "Group")
51 .put("country", "Country")
52 .put("userProfile", "UserProfile")
53 .put("datePeriod", "DatePeriod")
54 .put("annotation", "Annotation")
55 .put("address", "Address")
56 .put("property", "UnknownProperty")
57 .put("permission", "Permission")
58 .put("permissionGrant", "PermissionGrant")
59 .put("contentType", "ContentType")
60 .put("version", "Version")
61 .put("link", "Link")
62 .put("cvocVocabulary", "CvocVocabulary")
63 .put("cvocConcept", "CvocConcept")
64 .put("cvocConceptDescription", "CvocConceptDescription")
65 .put("system", "System")
66 .put("maintenanceEvent", "MaintenanceEvent")
67 .put("relationship", "AccessPoint")
68 .put("authoritativeSet", "AuthoritativeSet")
69 .put("eventLink", "EventLink")
70 .build();
71
72 public static final String OLD_ID_KEY = "__ID__";
73 public static final String OLD_TYPE_KEY = "__ISA__";
74
75 private static final ObjectMapper jsonMapper = new ObjectMapper();
76 private static final Logger logger = LoggerFactory.getLogger(DbUpgrader1to2.class);
77
78 private final FramedGraph<?> graph;
79 private final GraphManager manager;
80 private final OnChange onChange;
81
82 public DbUpgrader1to2(FramedGraph<?> graph, OnChange onChange) {
83 this.graph = graph;
84 this.manager = GraphManagerFactory.getInstance(graph);
85 this.onChange = onChange;
86 }
87
88 public interface OnChange {
89 void changed();
90 }
91
92 public DbUpgrader1to2 setDbSchemaVersion() {
93 try {
94 Vertex vertex = manager.getVertex(ActionManager.GLOBAL_EVENT_ROOT);
95 vertex.setProperty("DB_SCHEMA", "2");
96 vertex.setProperty("DB_SCHEMA_DATE", DateTime.now().toString());
97 onChange.changed();
98 return this;
99 } catch (ItemNotFound e) {
100 throw new RuntimeException("Global Event Root not found!", e);
101 }
102 }
103
104 public DbUpgrader1to2 upgradeIdAndTypeKeys() {
105 for (Vertex v : graph.getVertices()) {
106 Object oldId = v.getProperty(OLD_ID_KEY);
107 if (oldId != null) {
108 v.setProperty(EntityType.ID_KEY, oldId);
109 v.removeProperty(OLD_ID_KEY);
110 }
111 Object oldType = v.getProperty(OLD_TYPE_KEY);
112 if (oldType != null) {
113 v.setProperty(EntityType.TYPE_KEY, oldType);
114 v.removeProperty(OLD_TYPE_KEY);
115 }
116
117 onChange.changed();
118 }
119 return this;
120 }
121
122 public DbUpgrader1to2 setIdAndTypeOnEventLinks() {
123 for (Vertex v : graph.getVertices()) {
124 String dt = v.getProperty(ActionManager.DEBUG_TYPE);
125 if (ActionManager.EVENT_LINK.equalsIgnoreCase(dt)) {
126 String id = manager.getId(v);
127 if (id == null) {
128 UUID timeBasedUUID = GenericIdGenerator.getTimeBasedUUID();
129 v.setProperty(EntityType.ID_KEY, timeBasedUUID.toString());
130 v.setProperty(EntityType.TYPE_KEY, Entities.EVENT_LINK);
131 onChange.changed();
132 }
133 }
134 }
135 return this;
136 }
137
138 public DbUpgrader1to2 upgradeTypeValues() throws IOException {
139 for (Vertex v : graph.getVertices()) {
140 String oldType = v.getProperty(EntityType.TYPE_KEY);
141 if (changeMap.containsKey(oldType)) {
142 String newType = changeMap.get(oldType);
143 v.setProperty(EntityType.TYPE_KEY, newType);
144
145
146
147 if (newType.equals(Entities.CONTENT_TYPE)) {
148 String oldTypeId = v.getProperty(EntityType.ID_KEY);
149 String newTypeId = changeMap.get(oldTypeId);
150 v.setProperty(EntityType.ID_KEY, newTypeId);
151 } else if (newType.equals(Entities.VERSION)) {
152 String entityData = v.getProperty(Ontology.VERSION_ENTITY_DATA);
153 JsonNode node = jsonMapper.readTree(entityData);
154 if (!node.isObject()) {
155 throw new RuntimeException("Unexpected JSON object: " + node.getNodeType());
156 }
157 ObjectNode before = jsonMapper.valueToTree(node);
158 ObjectNode updated = upgradeNode(before);
159 String after = jsonMapper.writeValueAsString(updated);
160
161 manager.setProperty(v, Ontology.VERSION_ENTITY_CLASS, newType);
162 manager.setProperty(v, Ontology.VERSION_ENTITY_DATA, after);
163 }
164
165 onChange.changed();
166 }
167 }
168 return this;
169 }
170
171
172
173
174
175
176
177
178 public static ObjectNode upgradeNode(ObjectNode beforeNode) {
179 ObjectNode node = beforeNode.deepCopy();
180 JsonNode typeNode = node.path(Bundle.TYPE_KEY);
181 String typeText = typeNode.asText();
182 if (typeNode.isTextual() && changeMap.containsKey(typeText)) {
183 logger.trace("Renaming type key: {} -> {}", typeText, changeMap.get(typeText));
184 node.set(Bundle.TYPE_KEY, jsonMapper.valueToTree(changeMap.get(typeText)));
185 JsonNode rels = node.path(Bundle.REL_KEY);
186 if (!rels.isMissingNode()) {
187 Preconditions.checkState(rels.isObject(), "Relations is not a map: " + rels.getNodeType());
188 ObjectNode relObject = jsonMapper.valueToTree(rels);
189 Iterator<Map.Entry<String, JsonNode>> fields = relObject.fields();
190 while (fields.hasNext()) {
191 Map.Entry<String, JsonNode> next = fields.next();
192 JsonNode listNode = next.getValue();
193 Preconditions.checkState(listNode.isArray(), "Relations contains a list");
194 ArrayNode relList = jsonMapper.valueToTree(listNode);
195 for (int i = 0; i < relList.size(); i++) {
196 ObjectNode out = upgradeNode(jsonMapper.<ObjectNode>valueToTree(relList.path(i)));
197 relList.set(i, out);
198 }
199 relObject.set(next.getKey(), relList);
200 }
201 node.set(Bundle.REL_KEY, relObject);
202 }
203 }
204 return node;
205 }
206 }