Performance Improvements for Gizmo bulk API
[aai/gizmo.git] / src / main / java / org / onap / crud / dao / champ / ChampBulkPayload.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017-2018 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.crud.dao.champ;
23
24 import com.google.gson.Gson;
25 import com.google.gson.GsonBuilder;
26 import com.google.gson.JsonElement;
27
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32
33 import javax.ws.rs.core.HttpHeaders;
34 import javax.ws.rs.core.Response.Status;
35
36 import org.onap.aai.restclient.client.OperationResult;
37 import org.onap.crud.dao.GraphDao;
38 import org.onap.crud.entity.Edge;
39 import org.onap.crud.entity.Vertex;
40 import org.onap.crud.exception.CrudException;
41 import org.onap.crud.service.BulkPayload;
42 import org.onap.crud.service.EdgePayload;
43 import org.onap.crud.service.VertexPayload;
44 import org.onap.crud.util.CrudServiceUtil;
45 import org.onap.schema.OxmModelValidator;
46 import org.onap.schema.RelationshipSchemaValidator;
47
48 public class ChampBulkPayload {
49
50   public static String ADD_OP = "add";
51   public static String UPDATE_OP = "modify";
52   public static String DELETE_OP = "delete";
53   public static String PATCH_OP = "patch";
54
55   private List<ChampBulkOp> edgeDeleteOps = new ArrayList<ChampBulkOp>();
56   private List<ChampBulkOp> vertexDeleteOps = new ArrayList<ChampBulkOp>();
57   private List<ChampBulkOp> vertexAddModifyOps = new ArrayList<ChampBulkOp>();
58   private List<ChampBulkOp> edgeAddModifyOps = new ArrayList<ChampBulkOp>();
59
60   private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
61
62   public String toJson() {
63     return  gson.toJson(this);
64   }
65
66   public static ChampBulkPayload fromJson(String payload) {
67     return gson.fromJson(payload, ChampBulkPayload.class);
68   }
69
70   public List<ChampBulkOp> getEdgeDeleteOps() {
71     return edgeDeleteOps;
72   }
73
74   public void setEdgeDeleteOps(List<ChampBulkOp> ops) {
75     this.edgeDeleteOps = ops;
76   }
77
78   public List<ChampBulkOp> getVertexDeleteOps() {
79     return vertexDeleteOps;
80   }
81
82   public void setVertexDeleteOps(List<ChampBulkOp> ops) {
83     this.vertexDeleteOps = ops;
84   }
85
86   public List<ChampBulkOp> getVertexAddModifyOps() {
87     return vertexAddModifyOps;
88   }
89
90   public void setVertexAddModifyOps(List<ChampBulkOp> ops) {
91     this.vertexAddModifyOps = ops;
92   }
93
94   public List<ChampBulkOp> getEdgeAddModifyOps() {
95     return edgeAddModifyOps;
96   }
97
98   public void setEdgeAddModifyOps(List<ChampBulkOp> ops) {
99     this.edgeAddModifyOps = ops;
100   }
101
102   public void fromGizmoPayload(BulkPayload gizmoPayload, String version, HttpHeaders headers, GraphDao champDao) throws CrudException {
103     edgeDeleteOps = new ArrayList<ChampBulkOp>();
104     vertexDeleteOps = new ArrayList<ChampBulkOp>();
105     vertexAddModifyOps = new ArrayList<ChampBulkOp>();
106     edgeAddModifyOps = new ArrayList<ChampBulkOp>();
107
108     Map<String,String> addedVertexes = new HashMap<String,String>();
109
110     // Step 1. Extract edge deletes
111     for (JsonElement v : gizmoPayload.getRelationships()) {
112       List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
113               v.getAsJsonObject().entrySet());
114
115       if (entries.size() != 2) {
116         throw new CrudException("", Status.BAD_REQUEST);
117       }
118       Map.Entry<String, JsonElement> opr = entries.get(0);
119       Map.Entry<String, JsonElement> item = entries.get(1);
120       EdgePayload edgePayload = EdgePayload.fromJson(item.getValue().getAsJsonObject().toString());
121
122       if (opr.getValue().getAsString().equalsIgnoreCase("delete")) {
123         ChampBulkOp champOp = new ChampBulkOp();
124         champOp.setId(edgePayload.getId());
125         champOp.setOperation(DELETE_OP);
126         edgeDeleteOps.add(champOp);
127       }
128     }
129
130     // Step 2: Extract vertex deletes
131     for (JsonElement v : gizmoPayload.getObjects()) {
132       List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
133               v.getAsJsonObject().entrySet());
134
135       if (entries.size() != 2) {
136         throw new CrudException("", Status.BAD_REQUEST);
137       }
138
139       Map.Entry<String, JsonElement> opr = entries.get(0);
140       Map.Entry<String, JsonElement> item = entries.get(1);
141       VertexPayload vertexPayload = VertexPayload.fromJson(item.getValue().getAsJsonObject().toString());
142
143       if (opr.getValue().getAsString().equalsIgnoreCase("delete")) {
144         String type = OxmModelValidator.resolveCollectionType(version, vertexPayload.getType());
145         ChampBulkOp champOp = new ChampBulkOp();
146         champOp.setId(vertexPayload.getId());
147         champOp.setOperation(DELETE_OP);
148         champOp.setType(type);
149         vertexDeleteOps.add(champOp);
150       }
151     }
152
153     // Step 3: Extract vertex add/modify
154     for (JsonElement v : gizmoPayload.getObjects()) {
155       List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
156               v.getAsJsonObject().entrySet());
157
158       if (entries.size() != 2) {
159         throw new CrudException("", Status.BAD_REQUEST);
160       }
161       Map.Entry<String, JsonElement> opr = entries.get(0);
162       Map.Entry<String, JsonElement> item = entries.get(1);
163       VertexPayload vertexPayload = VertexPayload.fromJson(item.getValue().getAsJsonObject().toString());
164
165       // Add vertex
166       if (opr.getValue().getAsString().equalsIgnoreCase("add")) {
167         vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(),
168                 headers, true));
169         Vertex validatedVertex = OxmModelValidator.validateIncomingUpsertPayload(null, version, vertexPayload.getType(),
170                 vertexPayload.getProperties());
171         validatedVertex.getProperties().put(OxmModelValidator.Metadata.NODE_TYPE.propertyName(), vertexPayload.getType());
172
173         ChampBulkOp champOp = new ChampBulkOp();
174         champOp.setLabel(item.getKey());
175         champOp.setOperation(ADD_OP);
176         champOp.setType(vertexPayload.getType());
177         champOp.setProperties(validatedVertex.getProperties());
178         vertexAddModifyOps.add(champOp);
179       }
180
181       // Update vertex
182       else if (opr.getValue().getAsString().equalsIgnoreCase("modify")) {
183         vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(),
184                 headers, false));
185         Vertex validatedVertex = OxmModelValidator.validateIncomingUpsertPayload(vertexPayload.getId(), version,
186                 vertexPayload.getType(), vertexPayload.getProperties());
187         validatedVertex.getProperties().put(OxmModelValidator.Metadata.NODE_TYPE.propertyName(), vertexPayload.getType());
188
189         ChampBulkOp champOp = new ChampBulkOp();
190         champOp.setLabel(item.getKey());
191         champOp.setId(vertexPayload.getId());
192         champOp.setOperation(UPDATE_OP);
193         champOp.setType(vertexPayload.getType());
194         champOp.setProperties(validatedVertex.getProperties());
195         vertexAddModifyOps.add(champOp);
196       }
197
198       // Patch vertex
199       else if (opr.getValue().getAsString().equalsIgnoreCase("patch")) {
200         if ( (vertexPayload.getId() == null) || (vertexPayload.getType() == null) ) {
201           throw new CrudException("id and type must be specified for patch request", Status.BAD_REQUEST);
202         }
203
204         vertexPayload.setProperties(CrudServiceUtil.mergeHeaderInFoToPayload(vertexPayload.getProperties(),
205                 headers, false));
206
207         OperationResult existingVertexOpResult =
208                 champDao.getVertex(vertexPayload.getId(),
209                         OxmModelValidator.resolveCollectionType(version, vertexPayload.getType()),
210                         version, new HashMap<String, String>());
211
212         Vertex existingVertex = Vertex.fromJson(existingVertexOpResult.getResult(), version);
213         Vertex validatedVertex = OxmModelValidator.validateIncomingPatchPayload(vertexPayload.getId(),
214                 version, vertexPayload.getType(), vertexPayload.getProperties(), existingVertex);
215         validatedVertex.getProperties().put(OxmModelValidator.Metadata.NODE_TYPE.propertyName(), vertexPayload.getType());
216
217         ChampBulkOp champOp = new ChampBulkOp();
218         champOp.setLabel(item.getKey());
219         champOp.setId(vertexPayload.getId());
220         champOp.setOperation(UPDATE_OP);
221         champOp.setType(vertexPayload.getType());
222         champOp.setProperties(validatedVertex.getProperties());
223         vertexAddModifyOps.add(champOp);
224       }
225
226       addedVertexes.put(item.getKey(), "services/inventory/" + version + "/" + vertexPayload.getType()+ "/" + item.getKey());
227     }
228
229     // Step 4: Extract edge add/modify
230     for (JsonElement v : gizmoPayload.getRelationships()) {
231       List<Map.Entry<String, JsonElement>> entries = new ArrayList<Map.Entry<String, JsonElement>>(
232               v.getAsJsonObject().entrySet());
233
234       if (entries.size() != 2) {
235         throw new CrudException("", Status.BAD_REQUEST);
236       }
237       Map.Entry<String, JsonElement> opr = entries.get(0);
238       Map.Entry<String, JsonElement> item = entries.get(1);
239       EdgePayload edgePayload = EdgePayload.fromJson(item.getValue().getAsJsonObject().toString());
240
241       // Add/Update edge
242       if (opr.getValue().getAsString().equalsIgnoreCase("add")
243               || opr.getValue().getAsString().equalsIgnoreCase("modify")
244               || opr.getValue().getAsString().equalsIgnoreCase("patch")) {
245
246
247
248
249         ChampBulkOp champOp = new ChampBulkOp();
250         champOp.setLabel(item.getKey());
251
252         if (opr.getValue().getAsString().equalsIgnoreCase("add")) {
253           // If the source/target is a vertex that hasn't been created yet, get the types from the map
254           String sourceUrl = edgePayload.getSource();
255           String targetUrl = edgePayload.getTarget();
256           edgePayload.setSource(resolveUrl(edgePayload.getSource(), addedVertexes));
257           edgePayload.setTarget(resolveUrl(edgePayload.getTarget(), addedVertexes));
258
259           // If the type isn't set, resolve it based on on the source and target vertex types
260           if (edgePayload.getType() == null || edgePayload.getType().isEmpty()) {
261             edgePayload.setType(CrudServiceUtil.determineEdgeType(edgePayload, version));
262           }
263
264           champOp.setType(edgePayload.getType());
265           Edge validatedEdge = RelationshipSchemaValidator.validateIncomingAddPayload(version, edgePayload.getType(), edgePayload);
266           champOp.setOperation(ADD_OP);
267           champOp.setProperties(validatedEdge.getProperties());
268           champOp.setSource(sourceUrl.substring(sourceUrl.lastIndexOf('/') + 1));
269           champOp.setTarget(targetUrl.substring(targetUrl.lastIndexOf('/') + 1));
270         } else if (opr.getValue().getAsString().equalsIgnoreCase("modify")) {
271           Edge edge = champDao.getEdge(edgePayload.getId());
272           if (edgePayload.getType() == null || edgePayload.getType().isEmpty()) {
273             edgePayload.setType(edge.getType());
274           }
275           champOp.setType(edgePayload.getType());
276           Edge validatedEdge = RelationshipSchemaValidator.validateIncomingUpdatePayload(edge, version, edgePayload);
277           champOp.setOperation(UPDATE_OP);
278           champOp.setId(edgePayload.getId());
279           champOp.setProperties(validatedEdge.getProperties());
280           champOp.setSource(edge.getSource().getId().get());
281           champOp.setTarget(edge.getTarget().getId().get());
282         } else {
283           if (edgePayload.getId() == null) {
284             throw new CrudException("id must be specified for patch request", Status.BAD_REQUEST);
285           }
286           Edge existingEdge = champDao.getEdge(edgePayload.getId());
287           if (edgePayload.getType() == null || edgePayload.getType().isEmpty()) {
288             edgePayload.setType(existingEdge.getType());
289           }
290           champOp.setType(edgePayload.getType());
291           Edge patchedEdge = RelationshipSchemaValidator.validateIncomingPatchPayload(existingEdge, version, edgePayload);
292           champOp.setOperation(UPDATE_OP);
293           champOp.setId(edgePayload.getId());
294           champOp.setProperties(patchedEdge.getProperties());
295           champOp.setSource(existingEdge.getSource().getId().get());
296           champOp.setTarget(existingEdge.getTarget().getId().get());
297         }
298
299         edgeAddModifyOps.add(champOp);
300       }
301     }
302   }
303
304   private String resolveUrl(String vertexUrl, Map<String, String> addedVertexes) throws CrudException {
305     if (vertexUrl.startsWith("$")) {
306       String key = vertexUrl.substring(1);
307       if (addedVertexes.get(key) != null) {
308         return addedVertexes.get(key);
309       }
310
311       throw new CrudException("Unable to resolve vertex " + key, Status.BAD_REQUEST);
312     }
313
314     return vertexUrl;
315   }
316 }