Merge "Release 1.14.0 maven artifact"
[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 com.google.gson.Gson;
24 import com.google.gson.JsonArray;
25 import com.google.gson.JsonObject;
26 import com.google.gson.JsonParser;
27
28 import java.io.UnsupportedEncodingException;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Optional;
35 import java.util.Set;
36 import java.util.stream.Collectors;
37 import java.util.stream.Stream;
38
39 import org.apache.tinkerpop.gremlin.structure.Vertex;
40 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
41 import org.onap.aai.db.props.AAIProperties;
42 import org.onap.aai.exceptions.AAIException;
43 import org.onap.aai.introspection.Introspector;
44 import org.onap.aai.introspection.Loader;
45 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
46 import org.onap.aai.logging.LogFormatTools;
47 import org.onap.aai.serialization.db.DBSerializer;
48 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported;
49 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
50 import org.onap.aai.serialization.queryformats.params.AsTree;
51 import org.onap.aai.serialization.queryformats.params.Depth;
52 import org.onap.aai.serialization.queryformats.params.NodesOnly;
53 import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 public class Aggregate extends MultiFormatMapper {
58     private static final Logger LOGGER = LoggerFactory.getLogger(LifecycleFormat.class);
59     protected final DBSerializer serializer;
60     protected final Loader loader;
61     protected final UrlBuilder urlBuilder;
62     protected final int depth;
63     protected final boolean nodesOnly;
64
65     protected Aggregate(Builder builder) {
66         this.urlBuilder = builder.getUrlBuilder();
67         this.loader = builder.getLoader();
68         this.serializer = builder.getSerializer();
69         this.depth = builder.getDepth();
70         this.nodesOnly = builder.isNodesOnly();
71         this.isTree = builder.isTree();
72     }
73
74     @Override
75     public Optional<JsonObject> getJsonFromVertex(Vertex v, Map<String, List<String>> selectedProps)
76             throws AAIFormatVertexException {
77         JsonObject json = new JsonObject();
78         JsonObject outer = new JsonObject();
79         Optional<JsonObject> properties = this.createSelectedPropertiesObject(v, selectedProps);
80         if (properties.isPresent()) {
81             json.add("properties", properties.get());
82             outer.add(this.urlBuilder.pathed(v), json.getAsJsonObject());
83         } else {
84             return Optional.empty();
85         }
86         return Optional.of(outer);
87     }
88
89     @Override
90     public int parallelThreshold() {
91         return 100;
92     }
93
94     public Optional<JsonObject> createPropertiesObject(Vertex v) throws AAIFormatVertexException {
95         try {
96             final Introspector obj =
97                     loader.introspectorFromName(v.<String>property(AAIProperties.NODE_TYPE).orElse(null));
98
99             final List<Vertex> wrapper = new ArrayList<>();
100             wrapper.add(v);
101
102             try {
103                 serializer.dbToObject(wrapper, obj, 0, true, "false");
104             } catch (AAIException | UnsupportedEncodingException e) {
105                 throw new AAIFormatVertexException(
106                         "Failed to format vertex - error while serializing: " + e.getMessage(), e);
107             }
108
109             final String json = obj.marshal(false);
110             return Optional.of(JsonParser.parseString(json).getAsJsonObject());
111         } catch (AAIUnknownObjectException e) {
112             return Optional.empty();
113         }
114     }
115
116     public Optional<JsonObject> createSelectedPropertiesObject(Vertex v, Map<String, List<String>> selectedProps)
117             throws AAIFormatVertexException {
118         JsonObject json = new JsonObject();
119         Set<String> propList = null;
120         String nodeType = v.<String>value(AAIProperties.NODE_TYPE);
121         if (selectedProps != null && !selectedProps.isEmpty() && selectedProps.containsKey(nodeType)) {
122             propList = removeSingleQuotesForProperties(selectedProps.get(nodeType));
123         }
124         Iterator<VertexProperty<Object>> iter = v.properties();
125
126         Gson gson = new Gson();
127         while (iter.hasNext()) {
128             VertexProperty<Object> prop = iter.next();
129             if (propList != null && !propList.isEmpty()) {
130                 if (propList.contains(prop.label())) {
131                     if (prop.value() instanceof String) {
132                         json.addProperty(prop.key(), (String) prop.value());
133                     } else if (prop.value() instanceof Boolean) {
134                         json.addProperty(prop.key(), (Boolean) prop.value());
135                     } else if (prop.value() instanceof Number) {
136                         json.addProperty(prop.key(), (Number) prop.value());
137                     } else if (prop.value() instanceof List) {
138                         json.addProperty(prop.key(), gson.toJson(prop.value()));
139                     } else {
140                         // throw exception?
141                         return Optional.empty();
142                     }
143                 }
144             } else {
145                 return this.createPropertiesObject(v);
146             }
147         }
148
149         return Optional.of(json);
150     }
151
152     private Set<String> removeSingleQuotesForProperties(List<String> props) {
153         if (props != null && !props.isEmpty()) {
154             return props.stream().map(e -> e.substring(1, e.length() - 1)).collect(Collectors.toSet());
155         } else {
156             return Collections.emptySet();
157         }
158     }
159
160     public JsonArray process(List<Object> queryResults, Map<String, List<String>> properties) {
161         JsonArray body = new JsonArray();
162         Stream<Object> stream;
163         if (queryResults.size() >= this.parallelThreshold()) {
164             stream = queryResults.parallelStream();
165         } else {
166             stream = queryResults.stream();
167         }
168         final boolean isParallel = stream.isParallel();
169
170         stream.map(o -> {
171             try {
172                 return this.formatObject(o, properties);
173             } catch (AAIFormatVertexException e) {
174                 LOGGER.warn("Failed to format vertex, returning a partial list " + LogFormatTools.getStackTop(e));
175             } catch (AAIFormatQueryResultFormatNotSupported e) {
176                 LOGGER.warn("Failed to format result type of the query " + LogFormatTools.getStackTop(e));
177             }
178
179             return Optional.<JsonObject>empty();
180         }).filter(Optional::isPresent).map(Optional::get).forEach(json -> {
181             if (isParallel) {
182                 synchronized (body) {
183                     body.add(json);
184                 }
185             } else {
186                 body.add(json);
187             }
188         });
189         return body;
190     }
191
192     @Override
193     public Optional<JsonObject> formatObject(Object input, Map<String, List<String>> properties)
194             throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported {
195         JsonObject json = new JsonObject();
196         if (input instanceof ArrayList) {
197             Optional<JsonArray> ja = processInput(input, properties);
198             json.add("results", ja.get());
199         } else {
200             throw new AAIFormatQueryResultFormatNotSupported();
201         }
202         return Optional.of(json);
203     }
204
205     private Optional<JsonArray> processInput(Object input, Map<String, List<String>> properties)
206             throws AAIFormatVertexException {
207         JsonArray json = new JsonArray();
208         for (Object l : (ArrayList) input) {
209             if (l instanceof ArrayList) {
210                 JsonArray inner = new JsonArray();
211                 for (Vertex o : (ArrayList<Vertex>) l) {
212                     if (o instanceof Vertex) {
213                         Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) o, properties);
214                         if (obj.isPresent()) {
215                             inner.add(obj.get());
216                         } else {
217                             continue;
218                         }
219                     }
220                 }
221                 json.add(inner);
222             } else {
223                 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) l, properties);
224                 if (obj.isPresent())
225                     json.add(obj.get());
226             }
227         }
228         return Optional.of(json);
229     }
230
231     public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> {
232
233         protected final Loader loader;
234         protected final DBSerializer serializer;
235         protected final UrlBuilder urlBuilder;
236         protected boolean includeUrl = false;
237         protected boolean nodesOnly = false;
238         protected int depth = 1;
239         protected boolean modelDriven = false;
240         private boolean tree = false;
241
242         public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) {
243             this.loader = loader;
244             this.serializer = serializer;
245             this.urlBuilder = urlBuilder;
246         }
247
248         protected boolean isTree() {
249             return this.tree;
250         }
251
252         public Builder isTree(Boolean tree) {
253             this.tree = tree;
254             return this;
255         }
256
257         protected Loader getLoader() {
258             return this.loader;
259         }
260
261         protected DBSerializer getSerializer() {
262             return this.serializer;
263         }
264
265         protected UrlBuilder getUrlBuilder() {
266             return this.urlBuilder;
267         }
268
269         public Builder includeUrl() {
270             this.includeUrl = true;
271             return this;
272         }
273
274         public Builder nodesOnly(Boolean nodesOnly) {
275             this.nodesOnly = nodesOnly;
276             return this;
277         }
278
279         public boolean isNodesOnly() {
280             return this.nodesOnly;
281         }
282
283         public Builder depth(Integer depth) {
284             this.depth = depth;
285             return this;
286         }
287
288         public int getDepth() {
289             return this.depth;
290         }
291
292         public boolean isIncludeUrl() {
293             return this.includeUrl;
294         }
295
296         public Builder modelDriven() {
297             this.modelDriven = true;
298             return this;
299         }
300
301         public boolean getModelDriven() {
302             return this.modelDriven;
303         }
304
305         public Aggregate build() {
306             return new Aggregate(this);
307         }
308     }
309
310     @Override
311     protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException {
312
313         JsonObject json = new JsonObject();
314         json.addProperty("url", this.urlBuilder.pathed(v));
315         Optional<JsonObject> properties = this.createPropertiesObject(v);
316         if (properties.isPresent()) {
317             json.add("properties", properties.get());
318         } else {
319             return Optional.empty();
320         }
321         return Optional.of(json);
322     }
323 }