4c37aaa1888a0bc475370bf3c7f0b465b17726f1
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / serialization / queryformats / Aggregate.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.aai.serialization.queryformats;
22
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import com.google.gson.*;
26 import org.apache.tinkerpop.gremlin.structure.Direction;
27 import org.apache.tinkerpop.gremlin.structure.Edge;
28 import org.apache.tinkerpop.gremlin.structure.Vertex;
29 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
30 import org.onap.aai.db.props.AAIProperties;
31 import org.onap.aai.introspection.Loader;
32 import org.onap.aai.logging.LogFormatTools;
33 import org.onap.aai.serialization.db.DBSerializer;
34 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported;
35 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
36 import org.onap.aai.serialization.queryformats.params.AsTree;
37 import org.onap.aai.serialization.queryformats.params.Depth;
38 import org.onap.aai.serialization.queryformats.params.NodesOnly;
39 import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
40
41 import java.util.*;
42 import java.util.stream.Collectors;
43 import java.util.stream.Stream;
44
45 public class Aggregate extends MultiFormatMapper {
46     private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleFormat.class);
47     protected JsonParser parser = new JsonParser();
48     protected final DBSerializer serializer;
49     protected final Loader loader;
50     protected final UrlBuilder urlBuilder;
51     protected final int depth;
52     protected final boolean nodesOnly;
53
54     protected Aggregate(Builder builder) {
55         this.urlBuilder = builder.getUrlBuilder();
56         this.loader = builder.getLoader();
57         this.serializer = builder.getSerializer();
58         this.depth = builder.getDepth();
59         this.nodesOnly = builder.isNodesOnly();
60         this.isTree = builder.isTree();
61     }
62
63     @Override
64     public Optional<JsonObject> getJsonFromVertex(Vertex v, Map<String, List<String>> selectedProps) throws AAIFormatVertexException{
65         JsonObject json = new JsonObject();
66         JsonObject outer = new JsonObject();
67         Optional<JsonObject> properties = this.createSelectedPropertiesObject(v,selectedProps);
68         if (properties.isPresent()) {
69             json.add("properties", properties.get());
70             outer.add(this.urlBuilder.pathed(v),json.getAsJsonObject());
71         } else {
72             return Optional.empty();
73         }
74         return Optional.of(outer);
75     }
76
77     @Override
78     public int parallelThreshold() {
79         return 100;
80     }
81
82     public Optional<JsonObject> createPropertiesObject(Vertex v) throws AAIFormatVertexException {
83         JsonObject json = new JsonObject();
84         Iterator<VertexProperty<Object>> iter = v.properties();
85
86         while (iter.hasNext()) {
87             VertexProperty<Object> prop = iter.next();
88             if (prop.value() instanceof String) {
89                 json.addProperty(prop.key(), (String) prop.value());
90             } else if (prop.value() instanceof Boolean) {
91                 json.addProperty(prop.key(), (Boolean) prop.value());
92             } else if (prop.value() instanceof Number) {
93                 json.addProperty(prop.key(), (Number) prop.value());
94             } else if (prop.value() instanceof List) {
95                 Gson gson = new Gson();
96                 String list = gson.toJson(prop.value());
97
98                 json.addProperty(prop.key(), list);
99             } else {
100                 // throw exception?
101                 return null;
102             }
103         }
104
105         return Optional.of(json);
106     }
107
108     public Optional<JsonObject> createSelectedPropertiesObject(Vertex v, Map<String, List<String>> selectedProps) throws AAIFormatVertexException {
109         JsonObject json = new JsonObject();
110         Set<String> propList = null;
111         String nodeType = v.<String>value(AAIProperties.NODE_TYPE);
112         if (selectedProps != null && !selectedProps.isEmpty() && selectedProps.containsKey(nodeType)) {
113             propList = removeSingleQuotesForProperties(selectedProps.get(nodeType));
114         }
115         Iterator<VertexProperty<Object>> iter = v.properties();
116
117         Gson gson = new Gson();
118         while (iter.hasNext()) {
119             VertexProperty<Object> prop = iter.next();
120             if (propList != null && !propList.isEmpty()) {
121                 if (propList.contains(prop.label())) {
122                     if (prop.value() instanceof String) {
123                         json.addProperty(prop.key(), (String) prop.value());
124                     } else if (prop.value() instanceof Boolean) {
125                         json.addProperty(prop.key(), (Boolean) prop.value());
126                     } else if (prop.value() instanceof Number) {
127                         json.addProperty(prop.key(), (Number) prop.value());
128                     } else if (prop.value() instanceof List) {
129                         json.addProperty(prop.key(), gson.toJson(prop.value()));
130                     } else {
131                         // throw exception?
132                         return null;
133                     }
134                 }
135             } else {
136                 return this.createPropertiesObject(v);
137             }
138         }
139
140         return Optional.of(json);
141     }
142
143     private Set<String> removeSingleQuotesForProperties(List<String> props){
144         if (props != null && !props.isEmpty()) {
145             return props.stream().map(
146                 e -> e.substring(1, e.length()-1)).collect(Collectors.toSet());
147         } else {
148             return Collections.emptySet();
149         }
150     }
151
152     public JsonArray process(List<Object> queryResults, Map<String, List<String>> properties) {
153         JsonArray body = new JsonArray();
154         Stream<Object> stream;
155         if (queryResults.size() >= this.parallelThreshold()) {
156             stream = queryResults.parallelStream();
157         } else {
158             stream = queryResults.stream();
159         }
160         final boolean isParallel = stream.isParallel();
161
162         stream.map(o -> {
163             try {
164                 return this.formatObject(o, properties);
165             } catch (AAIFormatVertexException e) {
166                 LOGGER.warn("Failed to format vertex, returning a partial list " + LogFormatTools.getStackTop(e));
167             } catch (AAIFormatQueryResultFormatNotSupported e) {
168                 LOGGER.warn("Failed to format result type of the query " + LogFormatTools.getStackTop(e));
169             }
170
171             return Optional.<JsonObject>empty();
172         }).filter(Optional::isPresent)
173             .map(Optional::get)
174             .forEach(json -> {
175                 if (isParallel) {
176                     synchronized (body) {
177                         body.add(json);
178                     }
179                 } else {
180                     body.add(json);
181                 }
182             });
183         return body;
184     }
185
186     @Override
187     public Optional<JsonObject> formatObject(Object input, Map<String, List<String>> properties)
188         throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported {
189         JsonObject json = new JsonObject();
190         if (input instanceof ArrayList) {
191             Optional<JsonArray> ja = processInput(input, properties);
192             json.add("results", ja.get());
193         } else {
194             throw new AAIFormatQueryResultFormatNotSupported();
195         }
196         return Optional.of(json);
197     }
198
199     private Optional<JsonArray> processInput(Object input, Map properties) throws AAIFormatVertexException {
200         JsonArray json = new JsonArray();
201         for (Object l  : (ArrayList) input) {
202             if (l instanceof ArrayList) {
203                 JsonArray inner = new JsonArray();
204                 for (Vertex o : (ArrayList<Vertex>) l) {
205                     if (o instanceof Vertex) {
206                         Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) o, properties);
207                         if (obj.isPresent()) {
208                             inner.add(obj.get());
209                         } else {
210                             continue;
211                         }
212                     }
213                 }
214                 json.add(inner);
215             } else {
216                 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex)l, properties);
217                 json.add(obj.get());
218             }
219         }
220         return Optional.of(json);
221     }
222
223     public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> {
224
225         protected final Loader loader;
226         protected final DBSerializer serializer;
227         protected final UrlBuilder urlBuilder;
228         protected boolean includeUrl = false;
229         protected boolean nodesOnly = false;
230         protected int depth = 1;
231         protected boolean modelDriven = false;
232         private boolean tree = false;
233
234         public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) {
235             this.loader = loader;
236             this.serializer = serializer;
237             this.urlBuilder = urlBuilder;
238         }
239
240         protected boolean isTree() { return this.tree; }
241
242         public Builder isTree(Boolean tree) {
243             this.tree = tree;
244             return this;
245         }
246
247         protected Loader getLoader() {
248             return this.loader;
249         }
250
251         protected DBSerializer getSerializer() {
252             return this.serializer;
253         }
254
255         protected UrlBuilder getUrlBuilder() {
256             return this.urlBuilder;
257         }
258
259         public Builder includeUrl() {
260             this.includeUrl = true;
261             return this;
262         }
263
264         public Builder nodesOnly(Boolean nodesOnly) {
265             this.nodesOnly = nodesOnly;
266             return this;
267         }
268
269         public boolean isNodesOnly() {
270             return this.nodesOnly;
271         }
272
273         public Builder depth(Integer depth) {
274             this.depth = depth;
275             return this;
276         }
277
278         public int getDepth() {
279             return this.depth;
280         }
281
282         public boolean isIncludeUrl() {
283             return this.includeUrl;
284         }
285
286         public Builder modelDriven() {
287             this.modelDriven = true;
288             return this;
289         }
290
291         public boolean getModelDriven() {
292             return this.modelDriven;
293         }
294
295         public Aggregate build() {
296                 return new Aggregate(this);
297         }
298     }
299
300     @Override
301     protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException {
302
303         JsonObject json = new JsonObject();
304         json.addProperty("url", this.urlBuilder.pathed(v));
305         Optional<JsonObject> properties = this.createPropertiesObject(v);
306         if (properties.isPresent()) {
307             json.add("properties", properties.get());
308         } else {
309             return Optional.empty();
310         }
311         return Optional.of(json);
312     }
313 }