48c41830fceef792cd26880504a42aef151bf0b5
[aai/champ.git] / champ-service / src / main / java / org / onap / champ / service / ChampDataService.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 package org.onap.champ.service;
22
23 import org.onap.aai.champcore.ChampGraph;
24 import org.onap.aai.champcore.ChampTransaction;
25 import org.onap.aai.champcore.exceptions.ChampMarshallingException;
26 import org.onap.aai.champcore.exceptions.ChampObjectNotExistsException;
27 import org.onap.aai.champcore.exceptions.ChampRelationshipNotExistsException;
28 import org.onap.aai.champcore.exceptions.ChampSchemaViolationException;
29 import org.onap.aai.champcore.exceptions.ChampTransactionException;
30 import org.onap.aai.champcore.exceptions.ChampUnmarshallingException;
31 import org.onap.aai.champcore.model.ChampElement;
32 import org.onap.aai.champcore.model.ChampField;
33 import org.onap.aai.champcore.model.ChampObject;
34 import org.onap.aai.champcore.model.ChampObjectIndex;
35 import org.onap.aai.champcore.model.ChampRelationship;
36 import org.onap.aai.champcore.model.fluent.object.ObjectBuildOrPropertiesStep;
37 import org.onap.aai.cl.api.Logger;
38 import org.onap.aai.cl.eelf.LoggerFactory;
39 import org.onap.champ.exception.ChampServiceException;
40 import org.onap.champ.service.logging.ChampMsgs;
41 import org.onap.champ.util.ChampProperties;
42 import org.onap.champ.util.ChampServiceConstants;
43
44 import java.util.ArrayList;
45 import java.util.HashSet;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Optional;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
51 import javax.ws.rs.core.Response.Status;
52
53 public class ChampDataService {
54   private ChampUUIDService champUUIDService;
55
56   private ChampGraph graphImpl;
57   private ChampTransactionCache cache;
58   private static final String KEY_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_KEY_NAME);
59   private static final String SOT_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_SOT_NAME);
60   private static final String CREATED_TS_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_CREATED_TS_NAME);
61   private static final String LAST_MOD_TS_NAME = ChampProperties.get(ChampServiceConstants.CHAMP_LAST_MOD_TS_NAME);
62   private Logger logger = LoggerFactory.getInstance().getLogger(ChampDataService.class);
63
64
65   public ChampDataService(ChampUUIDService champUUIDService, ChampGraph graphImpl, ChampTransactionCache cache) {
66
67     this.champUUIDService = champUUIDService;
68     this.graphImpl = graphImpl;
69
70     ChampField field = new ChampField.Builder(ChampProperties.get("keyName"))
71         .type(ChampField.Type.STRING)
72         .build();
73     ChampObjectIndex index = new ChampObjectIndex.Builder(ChampProperties.get("keyName"), "STRING", field).build();
74
75     graphImpl.storeObjectIndex(index);
76
77     this.cache = cache;
78   }
79
80   public ChampObject getObject(String id, Optional<ChampTransaction> transaction) throws ChampServiceException {
81
82     Optional<ChampObject> retrieved = Optional.empty();
83     try {
84       retrieved = champUUIDService.getObjectbyUUID(id, transaction.orElse(null));
85     } catch (ChampUnmarshallingException | ChampTransactionException e) {
86       throw new ChampServiceException("Error: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR);
87     }
88     if (retrieved.isPresent()) {
89       return (ChampObject) champUUIDService.populateUUIDKey(retrieved.get());
90     } else {
91       return null;
92     }
93   }
94
95   public ChampObject storeObject(ChampObject object, Optional<ChampTransaction> transaction)
96       throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException,
97       ChampTransactionException, ChampServiceException {
98
99     if (object.getProperty(KEY_NAME).isPresent() || object.getKey().isPresent()) {
100       throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST);
101     }
102
103     champUUIDService.populateUUIDProperty(object, java.util.UUID.randomUUID().toString());
104     addTimestamps(object, null);
105     ChampObject created = graphImpl.storeObject(object, transaction);
106     return (ChampObject) champUUIDService.populateUUIDKey(created);
107   }
108
109   public ChampObject replaceObject(ChampObject object, String objectId, Optional<ChampTransaction> transaction)
110       throws ChampServiceException, ChampUnmarshallingException, ChampTransactionException, ChampMarshallingException,
111       ChampSchemaViolationException, ChampObjectNotExistsException {
112     if (object.getKey().isPresent() && (!object.getKeyValue().equals(objectId))) {
113       throw new ChampServiceException("Object Id in the URI doesn't match the body.", Status.BAD_REQUEST);
114     }
115
116     if (object.getProperty(KEY_NAME).isPresent() && !object.getProperty(KEY_NAME).get().toString().equals(objectId)) {
117       throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST);
118     }
119
120     Optional<ChampObject> retrieved = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null));
121     if (!retrieved.isPresent()) {
122       throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND);
123     }
124     ObjectBuildOrPropertiesStep payloadBuilder = ChampObject.create().from(object).withKey(retrieved.get().getKey().get())
125         .withProperty(KEY_NAME, objectId);
126     if (retrieved.get().getProperty(SOT_NAME).isPresent()){
127       payloadBuilder = payloadBuilder.withProperty(SOT_NAME, retrieved.get().getProperty(SOT_NAME).get());
128     }
129
130     if (object.getProperty(CREATED_TS_NAME).isPresent() && retrieved.get().getProperty(CREATED_TS_NAME).isPresent()) {
131       // the timestamps in object are parsed as strings regardless of how the input json is. Convert retrieved to string for easy comparison
132       if (!retrieved.get().getProperty(CREATED_TS_NAME).get().toString().equals(object.getProperty(CREATED_TS_NAME).get())) {
133         throw new ChampServiceException(CREATED_TS_NAME + " can't be updated", Status.BAD_REQUEST);
134       }
135     }
136
137     if (object.getProperty(LAST_MOD_TS_NAME).isPresent() && retrieved.get().getProperty(LAST_MOD_TS_NAME).isPresent()) {
138       if (!retrieved.get().getProperty(LAST_MOD_TS_NAME).get().toString().equals(object.getProperty(LAST_MOD_TS_NAME).get())) {
139         throw new ChampServiceException(LAST_MOD_TS_NAME + " can't be updated", Status.BAD_REQUEST);
140       }
141     }
142
143     ChampObject payload = payloadBuilder.build();
144     addTimestamps(payload, (Long)retrieved.get().getProperty(CREATED_TS_NAME).orElse(null));
145     ChampObject updated = graphImpl.replaceObject(payload, transaction);
146     return (ChampObject) champUUIDService.populateUUIDKey(updated);
147   }
148
149   public void deleteObject(String objectId, Optional<ChampTransaction> transaction) throws ChampServiceException,
150       ChampObjectNotExistsException, ChampTransactionException, ChampUnmarshallingException {
151     Optional<ChampObject> retrieved = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null));
152     if (!retrieved.isPresent()) {
153       throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND);
154     }
155     Stream<ChampRelationship> relationships = graphImpl.retrieveRelationships(retrieved.get(), transaction);
156
157     if (relationships.count() > 0) {
158       throw new ChampServiceException("Attempt to delete vertex with id " + objectId + " which has incident edges.",
159           Status.BAD_REQUEST);
160     }
161     graphImpl.deleteObject(retrieved.get().getKey().get(), transaction);
162
163   }
164
165   public ChampRelationship storeRelationship(ChampRelationship r, Optional<ChampTransaction> transaction)
166       throws ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException,
167       ChampRelationshipNotExistsException, ChampUnmarshallingException, ChampTransactionException,
168       ChampServiceException {
169
170     if (r.getSource() == null || !r.getSource().getKey().isPresent() || r.getTarget() == null
171         || !r.getTarget().getKey().isPresent()) {
172       logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "Source/Target Object key must be provided");
173       throw new ChampServiceException("Source/Target Object key must be provided", Status.BAD_REQUEST);
174     }
175
176     if (r.getProperty(KEY_NAME).isPresent() || r.getKey().isPresent()) {
177       logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "key or " + KEY_NAME + " not allowed while creating new Objects");
178       throw new ChampServiceException("key or " + KEY_NAME + " not allowed while creating new Objects", Status.BAD_REQUEST);
179
180     }
181
182     Optional<ChampObject> source = champUUIDService.getObjectbyUUID(r.getSource().getKey().get().toString(),
183         transaction.orElse(null));
184     Optional<ChampObject> target = champUUIDService.getObjectbyUUID(r.getTarget().getKey().get().toString(),
185         transaction.orElse(null));
186
187     if (!source.isPresent() || !target.isPresent()) {
188       logger.error(ChampMsgs.CHAMP_DATA_SERVICE_ERROR, "Source/Target object not found");
189       throw new ChampServiceException("Source/Target object not found", Status.BAD_REQUEST);
190     }
191
192     champUUIDService.populateUUIDProperty(r, java.util.UUID.randomUUID().toString());
193
194     ChampRelationship payload = new ChampRelationship.Builder(source.get(), target.get(), r.getType())
195         .properties(r.getProperties()).build();
196     addTimestamps(payload, null);
197     ChampRelationship created = graphImpl.storeRelationship(payload, transaction);
198     return (ChampRelationship) champUUIDService.populateUUIDKey(created);
199   }
200
201   public ChampRelationship updateRelationship(ChampRelationship r, String rId, Optional<ChampTransaction> transaction)
202       throws ChampServiceException, ChampUnmarshallingException, ChampTransactionException, ChampMarshallingException,
203       ChampSchemaViolationException, ChampRelationshipNotExistsException {
204     if (r.getKey().isPresent() && (!r.getKeyValue().equals(rId))) {
205
206       throw new ChampServiceException("Relationship Id in the URI \"" + rId + "\" doesn't match the URI in the body"
207           + " \"" + r.getKeyValue() + "\"", Status.BAD_REQUEST);
208
209     }
210
211     if (r.getProperty(KEY_NAME).isPresent() && !r.getProperty(KEY_NAME).get().toString().equals(rId)) {
212       throw new ChampServiceException(KEY_NAME + " can't be updated", Status.BAD_REQUEST);
213     }
214
215     Optional<ChampRelationship> retrieved = champUUIDService.getRelationshipbyUUID(rId, transaction.orElse(null));
216     if (!retrieved.isPresent()) {
217       throw new ChampServiceException(rId + " not found", Status.NOT_FOUND);
218     }
219     // check if key is present or if it equals the key that is in the URI
220     if (r.getSource() == null || !r.getSource().getKey().isPresent() || r.getTarget() == null
221         || !r.getTarget().getKey().isPresent()) {
222       throw new ChampServiceException("Source/Target Object key must be provided", Status.BAD_REQUEST);
223     }
224     ChampObject source = retrieved.get().getSource();
225     ChampObject target = retrieved.get().getTarget();
226
227     if (!source.getProperty(KEY_NAME).get().toString().equals(r.getSource().getKey().get().toString())
228         || !target.getProperty(KEY_NAME).get().toString().equals(r.getTarget().getKey().get().toString())) {
229       throw new ChampServiceException("Source/Target cannot be updated", Status.BAD_REQUEST);
230     }
231
232     if (r.getProperty(CREATED_TS_NAME).isPresent() && retrieved.get().getProperty(CREATED_TS_NAME).isPresent()) {
233       if (!retrieved.get().getProperty(CREATED_TS_NAME).get().toString().equals(r.getProperty(CREATED_TS_NAME).get())) {
234         throw new ChampServiceException(CREATED_TS_NAME + " can't be updated", Status.BAD_REQUEST);
235       }
236     }
237
238     if (r.getProperty(LAST_MOD_TS_NAME).isPresent() && retrieved.get().getProperty(LAST_MOD_TS_NAME).isPresent()) {
239       if (!retrieved.get().getProperty(LAST_MOD_TS_NAME).get().toString().equals(r.getProperty(LAST_MOD_TS_NAME).get())) {
240         throw new ChampServiceException(LAST_MOD_TS_NAME + " can't be updated", Status.BAD_REQUEST);
241       }
242     }
243
244     ChampRelationship payload = new ChampRelationship.Builder(source, target, r.getType())
245         .key(retrieved.get().getKey().get()).properties(r.getProperties()).property(KEY_NAME, rId).build();
246     addTimestamps(payload, (Long)retrieved.get().getProperty(CREATED_TS_NAME).orElse(null));
247     ChampRelationship updated = graphImpl.replaceRelationship(payload, transaction);
248     return (ChampRelationship) champUUIDService.populateUUIDKey(updated);
249   }
250
251   public void deleteRelationship(String relationshipId, Optional<ChampTransaction> transaction)
252       throws ChampServiceException, ChampRelationshipNotExistsException, ChampTransactionException,
253       ChampUnmarshallingException {
254     Optional<ChampRelationship> retrieved = champUUIDService.getRelationshipbyUUID(relationshipId,
255         transaction.orElse(null));
256     if (!retrieved.isPresent()) {
257       throw new ChampServiceException(relationshipId + " not found", Status.NOT_FOUND);
258     }
259
260     graphImpl.deleteRelationship(retrieved.get(), transaction);
261
262   }
263
264
265   public List<ChampRelationship> getRelationshipsByObject(String objectId, Optional<ChampTransaction> transaction)
266       throws ChampServiceException {
267     try {
268       Optional<ChampObject> retrievedObject = champUUIDService.getObjectbyUUID(objectId, transaction.orElse(null));
269       if (!retrievedObject.isPresent()) {
270         throw new ChampServiceException(objectId + " not found", Status.NOT_FOUND);
271       }
272       List<ChampRelationship> relations = new ArrayList<ChampRelationship>();
273
274       Stream<ChampRelationship> retrieved = graphImpl.retrieveRelationships(retrievedObject.get(), transaction);
275       relations = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList()));
276       return relations;
277     } catch (ChampObjectNotExistsException e) {
278       throw new ChampServiceException(" obj not found", Status.NOT_FOUND);
279     } catch (ChampUnmarshallingException | ChampTransactionException e) {
280       throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR);
281     }
282
283   }
284
285   /**
286    * Gets the ChampObjects that pass filter
287    * @param filter key/value pairs that must be present in the returned objects
288    * @param properties properties that will show up in the object
289    * @return
290    * @throws ChampServiceException
291    */
292   public List<ChampObject> queryObjects(Map<String, Object> filter, HashSet<String> properties) throws ChampServiceException {
293     try {
294
295       Stream<ChampObject> retrieved = graphImpl.queryObjects(filter);
296       List<ChampObject> objects = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList()));
297
298       if (!properties.contains("all")) {
299         for (ChampObject champObject : objects) {
300           champObject.dropProperties(properties);
301         }
302       }
303
304       return objects;
305     } catch (ChampTransactionException e) {
306       throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR);
307     }
308   }
309
310   public List<ChampRelationship> queryRelationships(Map<String, Object> filter) throws ChampServiceException {
311     try {
312       List<ChampRelationship> relations = new ArrayList<ChampRelationship>();
313       Stream<ChampRelationship> retrieved;
314
315       retrieved = graphImpl.queryRelationships(filter);
316
317       relations = champUUIDService.populateUUIDKey(retrieved.collect(Collectors.toList()));
318       return relations;
319     } catch (ChampTransactionException e) {
320       throw new ChampServiceException("Internal Error", Status.INTERNAL_SERVER_ERROR);
321     }
322   }
323
324   public ChampRelationship getRelationship(String id, Optional<ChampTransaction> transaction)
325       throws ChampServiceException {
326
327     Optional<ChampRelationship> retrieved = Optional.empty();
328     try {
329       retrieved = champUUIDService.getRelationshipbyUUID(id, transaction.orElse(null));
330     } catch (ChampUnmarshallingException | ChampTransactionException e) {
331       throw new ChampServiceException("Error: " + e.getMessage(), Status.INTERNAL_SERVER_ERROR);
332     }
333     if (retrieved.isPresent()) {
334       return (ChampRelationship) champUUIDService.populateUUIDKey(retrieved.get());
335     } else {
336       return null;
337     }
338   }
339
340   public String openTransaction() {
341     ChampTransaction transaction = graphImpl.openTransaction();
342     String transacId = transaction.id();
343     cache.put(transacId, transaction);
344     return transacId;
345
346   }
347
348   public void commitTransaction(String tId) throws ChampServiceException, ChampTransactionException {
349     ChampTransaction transaction = cache.get(tId);
350     if (transaction == null) {
351       throw new ChampServiceException("Transaction Not found: " + tId, Status.NOT_FOUND);
352     }
353     graphImpl.commitTransaction(transaction);
354     cache.invalidate(tId);
355     cache.invalidate(transaction.id());
356
357   }
358
359   public void rollbackTransaction(String tId) throws ChampServiceException, ChampTransactionException {
360     ChampTransaction transaction = cache.get(tId);
361     if (transaction == null) {
362       throw new ChampServiceException("Transaction Not found: " + tId, Status.NOT_FOUND);
363     }
364     graphImpl.rollbackTransaction(transaction);
365     cache.invalidate(tId);
366     cache.invalidate(transaction.id());
367
368   }
369
370   public ChampTransaction getTransaction(String id) {
371     return cache.get(id);
372   }
373
374   private void addTimestamps(ChampElement e, Long oldCreated) {
375     Long timestamp = System.currentTimeMillis();
376
377     if (oldCreated == null) {
378       e.getProperties().put(CREATED_TS_NAME, timestamp);
379     } else {
380       e.getProperties().put(CREATED_TS_NAME, oldCreated);
381     }
382
383     e.getProperties().put(LAST_MOD_TS_NAME, timestamp);
384   }
385 }