f4fd921fdf06a7bbb9d3768ee158a8756b8c6992
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / serialization / queryformats / Resource.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.JsonArray;
24 import com.google.gson.JsonElement;
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.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.Set;
34
35 import javax.ws.rs.core.MultivaluedMap;
36
37 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
38 import org.apache.tinkerpop.gremlin.structure.Vertex;
39 import org.onap.aai.db.props.AAIProperties;
40 import org.onap.aai.exceptions.AAIException;
41 import org.onap.aai.introspection.Introspector;
42 import org.onap.aai.introspection.Loader;
43 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
44 import org.onap.aai.serialization.db.DBSerializer;
45 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
46 import org.onap.aai.serialization.queryformats.params.AsTree;
47 import org.onap.aai.serialization.queryformats.params.Depth;
48 import org.onap.aai.serialization.queryformats.params.NodesOnly;
49 import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 public class Resource extends MultiFormatMapper {
54
55     private static final Logger logger = LoggerFactory.getLogger(Resource.class);
56
57     private final Loader loader;
58     private final DBSerializer serializer;
59     private final JsonParser parser;
60     private final UrlBuilder urlBuilder;
61     private final boolean includeUrl;
62     private final boolean nodesOnly;
63     private final int depth;
64     private final boolean isSkipRelatedTo;
65
66     public Resource(Builder builder) {
67         this.parser = new JsonParser();
68         this.loader = builder.getLoader();
69         this.serializer = builder.getSerializer();
70         this.urlBuilder = builder.getUrlBuilder();
71         this.includeUrl = builder.isIncludeUrl();
72         this.nodesOnly = builder.isNodesOnly();
73         this.depth = builder.getDepth();
74         this.isSkipRelatedTo = builder.isSkipRelatedTo();
75         this.isTree = builder.isTree();
76     }
77
78     @Override
79     protected Optional<JsonObject> getRelatedNodesFromTree(Tree<?> tree, Map<String, List<String>> properties)
80             throws AAIFormatVertexException {
81         if (tree.isEmpty()) {
82             return Optional.of(new JsonObject());
83         }
84
85         Map<String, Set<String>> filterPropertiesMap = createFilteredPropertyMap(properties);
86
87         JsonObject t = new JsonObject();
88         JsonArray ja = this.getRelatedNodesArray(tree, filterPropertiesMap, "related-nodes");
89         if (ja.size() > 0) {
90             t.add("results", ja);
91             return Optional.of(t);
92         }
93
94         return Optional.empty();
95     }
96
97     protected JsonArray getRelatedNodesArray(Tree<?> tree, Map<String, Set<String>> filterPropertiesMap,
98             String nodeIdentifier) throws AAIFormatVertexException {
99         JsonArray nodes = new JsonArray();
100         if (tree.isEmpty()) {
101             return nodes;
102         }
103         for (Map.Entry<?, ? extends Tree<?>> entry : tree.entrySet()) {
104             JsonObject me = new JsonObject();
105             if (entry.getKey() instanceof Vertex) {
106                 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) entry.getKey());
107                 if (obj.isPresent()) {
108                     me = getPropertyFilteredObject(obj, filterPropertiesMap);
109                 } else {
110                     continue;
111                 }
112             }
113             JsonArray ja = this.getRelatedNodesArray(entry.getValue(), filterPropertiesMap, nodeIdentifier);
114             if (ja.size() > 0) {
115                 try {
116                     for (Map.Entry<String, JsonElement> mapEntry : me.entrySet()) {
117                         JsonElement value = mapEntry.getValue();
118                         if (value != null && value.isJsonObject()) {
119                             value.getAsJsonObject().add(nodeIdentifier, ja);
120                         }
121                     }
122                 } catch (Exception e) {
123                     logger.debug("Failed to add related-nodes array: {}", e.getMessage());
124                     throw new AAIFormatVertexException("Failed to add related-nodes array: " + e.getMessage(), e);
125                 }
126             }
127             nodes.add(me);
128         }
129         return nodes;
130     }
131
132     @Override
133     protected Optional<JsonObject> getJsonFromVertex(Vertex v) throws AAIFormatVertexException {
134
135         JsonObject json = new JsonObject();
136
137         if (this.includeUrl) {
138             json.addProperty("url", this.urlBuilder.pathed(v));
139         }
140         Optional<JsonObject> jsonObject = this.vertexToJsonObject(v);
141         if (jsonObject.isPresent()) {
142             json.add(v.<String>property(AAIProperties.NODE_TYPE).orElse(null), jsonObject.get());
143         } else {
144             return Optional.empty();
145         }
146         return Optional.of(json);
147     }
148
149     @Override
150     protected Optional<JsonObject> getJsonFromVertex(Vertex v, Map<String, List<String>> properties)
151             throws AAIFormatVertexException {
152         JsonObject json = new JsonObject();
153
154         if (this.includeUrl) {
155             json.addProperty("url", this.urlBuilder.pathed(v));
156         }
157         Optional<JsonObject> jsonObject = this.vertexToJsonObject(v);
158         if (jsonObject.isPresent()) {
159             String nodeType = v.<String>value(AAIProperties.NODE_TYPE);
160             Map<String, Set<String>> filterPropertiesMap = createFilteredPropertyMap(properties); // this change is for
161                                                                                                   // resource_and_url
162                                                                                                   // with/out as-tree.
163                                                                                                   // and no as-tree req
164             JsonObject jo = filterProperties(jsonObject, nodeType, filterPropertiesMap);
165             json.add(v.<String>property(AAIProperties.NODE_TYPE).orElse(null), jo);
166         } else {
167             return Optional.empty();
168         }
169         return Optional.of(json);
170     }
171
172     protected Optional<JsonObject> vertexToJsonObject(Vertex v) throws AAIFormatVertexException {
173         if (v == null) {
174             return Optional.empty();
175         }
176         try {
177             final Introspector obj =
178                     getLoader().introspectorFromName(v.<String>property(AAIProperties.NODE_TYPE).orElse(null));
179
180             final List<Vertex> wrapper = new ArrayList<>();
181
182             wrapper.add(v);
183
184             try {
185                 getSerializer().dbToObject(wrapper, obj, this.depth, this.nodesOnly, "false", isSkipRelatedTo);
186             } catch (AAIException | UnsupportedEncodingException e) {
187                 throw new AAIFormatVertexException(
188                         "Failed to format vertex - error while serializing: " + e.getMessage(), e);
189             }
190
191             final String json = obj.marshal(false);
192
193             return Optional.of(getParser().parse(json).getAsJsonObject());
194         } catch (AAIUnknownObjectException e) {
195             return Optional.empty();
196         }
197     }
198
199     @Override
200     public int parallelThreshold() {
201         return 20;
202     }
203
204     private Loader getLoader() {
205         return loader;
206     }
207
208     private DBSerializer getSerializer() {
209         return serializer;
210     }
211
212     private JsonParser getParser() {
213         return parser;
214     }
215
216     public static class Builder implements NodesOnly<Builder>, Depth<Builder>, AsTree<Builder> {
217
218         private final Loader loader;
219         private final DBSerializer serializer;
220         private final UrlBuilder urlBuilder;
221         private boolean includeUrl = false;
222         private boolean nodesOnly = false;
223         private int depth = 1;
224         private MultivaluedMap<String, String> params;
225         private boolean tree = false;
226
227         public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder) {
228             this.loader = loader;
229             this.serializer = serializer;
230             this.urlBuilder = urlBuilder;
231         }
232
233         public Builder(Loader loader, DBSerializer serializer, UrlBuilder urlBuilder,
234                 MultivaluedMap<String, String> params) {
235             this.loader = loader;
236             this.serializer = serializer;
237             this.urlBuilder = urlBuilder;
238             this.params = params;
239         }
240
241         protected Loader getLoader() {
242             return this.loader;
243         }
244
245         protected DBSerializer getSerializer() {
246             return this.serializer;
247         }
248
249         protected UrlBuilder getUrlBuilder() {
250             return this.urlBuilder;
251         }
252
253         protected MultivaluedMap<String, String> getParams() {
254             return this.params;
255         }
256
257         public boolean isSkipRelatedTo() {
258             if (params != null) {
259                 boolean isSkipRelatedTo = true;
260                 if (params.containsKey("skip-related-to")) {
261                     String skipRelatedTo = params.getFirst("skip-related-to");
262                     isSkipRelatedTo = !(skipRelatedTo != null && skipRelatedTo.equals("false"));
263                 } else {
264                     // if skip-related-to param is missing, then default it to false;
265                     isSkipRelatedTo = false;
266                 }
267                 return isSkipRelatedTo;
268             }
269             return true;
270         }
271
272         protected boolean isTree() {
273             return this.tree;
274         }
275
276         public Builder isTree(Boolean tree) {
277             this.tree = tree;
278             return this;
279         }
280
281         public Builder includeUrl() {
282             this.includeUrl = true;
283             return this;
284         }
285
286         public Builder nodesOnly(Boolean nodesOnly) {
287             this.nodesOnly = nodesOnly;
288             return this;
289         }
290
291         public boolean isNodesOnly() {
292             return this.nodesOnly;
293         }
294
295         public Builder depth(Integer depth) {
296             this.depth = depth;
297             return this;
298         }
299
300         public int getDepth() {
301             return this.depth;
302         }
303
304         public boolean isIncludeUrl() {
305             return this.includeUrl;
306         }
307
308         public Resource build() {
309             return new Resource(this);
310         }
311     }
312 }