[AAI] Fix doc config files
[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.exceptions.AAIException;
32 import org.onap.aai.introspection.Introspector;
33 import org.onap.aai.introspection.Loader;
34 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
35 import org.onap.aai.logging.LogFormatTools;
36 import org.onap.aai.serialization.db.DBSerializer;
37 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported;
38 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
39 import org.onap.aai.serialization.queryformats.params.AsTree;
40 import org.onap.aai.serialization.queryformats.params.Depth;
41 import org.onap.aai.serialization.queryformats.params.NodesOnly;
42 import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
43
44 import java.io.UnsupportedEncodingException;
45 import java.util.*;
46 import java.util.stream.Collectors;
47 import java.util.stream.Stream;
48
49 public class Aggregate extends MultiFormatMapper {
50     private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleFormat.class);
51     protected JsonParser parser = new JsonParser();
52     protected final DBSerializer serializer;
53     protected final Loader loader;
54     protected final UrlBuilder urlBuilder;
55     protected final int depth;
56     protected final boolean nodesOnly;
57
58     protected Aggregate(Builder builder) {
59         this.urlBuilder = builder.getUrlBuilder();
60         this.loader = builder.getLoader();
61         this.serializer = builder.getSerializer();
62         this.depth = builder.getDepth();
63         this.nodesOnly = builder.isNodesOnly();
64         this.isTree = builder.isTree();
65     }
66
67     @Override
68     public Optional<JsonObject> getJsonFromVertex(Vertex v, Map<String, List<String>> selectedProps) throws AAIFormatVertexException{
69         JsonObject json = new JsonObject();
70         JsonObject outer = new JsonObject();
71         Optional<JsonObject> properties = this.createSelectedPropertiesObject(v,selectedProps);
72         if (properties.isPresent()) {
73             json.add("properties", properties.get());
74             outer.add(this.urlBuilder.pathed(v),json.getAsJsonObject());
75         } else {
76             return Optional.empty();
77         }
78         return Optional.of(outer);
79     }
80
81     @Override
82     public int parallelThreshold() {
83         return 100;
84     }
85
86     public Optional<JsonObject> createPropertiesObject(Vertex v) throws AAIFormatVertexException {
87         try {
88             final Introspector obj =
89                 loader.introspectorFromName(v.<String>property(AAIProperties.NODE_TYPE).orElse(null));
90
91             final List<Vertex> wrapper = new ArrayList<>();
92             wrapper.add(v);
93
94             try {
95                 serializer.dbToObject(wrapper, obj, 0, true, "false");
96             } catch (AAIException | UnsupportedEncodingException e) {
97                 throw new AAIFormatVertexException(
98                     "Failed to format vertex - error while serializing: " + e.getMessage(), e);
99             }
100
101             final String json = obj.marshal(false);
102             return Optional.of(parser.parse(json).getAsJsonObject());
103         } catch (AAIUnknownObjectException e) {
104             return Optional.empty();
105         }
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 Optional.empty();
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                 if(obj.isPresent()) 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 }