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