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