2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.aai.serialization.queryformats;
23 import com.google.gson.JsonArray;
24 import com.google.gson.JsonElement;
25 import com.google.gson.JsonObject;
26 import java.util.stream.Collectors;
27 import org.apache.commons.lang3.tuple.ImmutableTriple;
28 import org.apache.commons.lang3.tuple.Pair;
29 import org.apache.tinkerpop.gremlin.process.traversal.Path;
30 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
31 import org.apache.tinkerpop.gremlin.structure.Vertex;
32 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported;
33 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
39 public abstract class MultiFormatMapper implements FormatMapper {
41 Logger logger = LoggerFactory.getLogger(MultiFormatMapper.class);
43 protected boolean isTree = false;
44 protected static final String PROPERTIES_KEY = "properties";
45 protected static final String NODE_TYPE_KEY = "node-type";
47 protected static final String RETURNED_EMPTY_JSONARRAY_MSG =
48 "Returned empty JsonArray - Could not populate nested json objects for wrapper: {}";
51 public Optional<JsonObject> formatObject(Object input)
52 throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported {
53 if (input instanceof Vertex) {
54 logger.debug("Formatting vertex object");
55 return this.getJsonFromVertex((Vertex) input);
56 } else if (input instanceof Tree) {
57 logger.debug("Formatting tree object");
59 return this.getRelatedNodesFromTree((Tree<?>) input, null);
61 return this.getJsonFromTree((Tree<?>) input);
63 } else if (input instanceof Path) {
64 logger.debug("Formatting path object");
65 return this.getJsonFromPath((Path) input);
67 throw new AAIFormatQueryResultFormatNotSupported();
72 public Optional<JsonObject> formatObject(Object input, Map<String, List<String>> properties)
73 throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported {
74 if (input instanceof Vertex) {
75 logger.debug("Formatting vertex object with properties map filter");
76 return this.getJsonFromVertex((Vertex) input, properties);
77 } else if (input instanceof Tree) {
78 logger.debug("Formatting tree object with properties map filter");
80 return this.getRelatedNodesFromTree((Tree<?>) input, properties);
82 return this.getJsonFromTree((Tree<?>) input);
84 } else if (input instanceof Path) {
85 logger.debug("Formatting path object");
86 return this.getJsonFromPath((Path) input);
88 throw new AAIFormatQueryResultFormatNotSupported();
92 protected abstract Optional<JsonObject> getJsonFromVertex(Vertex input) throws AAIFormatVertexException;
93 protected abstract Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException;
95 protected Optional<JsonObject> getJsonFromPath(Path input) throws AAIFormatVertexException {
96 List<Object> path = input.objects();
98 JsonObject jo = new JsonObject();
99 JsonArray ja = new JsonArray();
101 for (Object o : path) {
102 if (o instanceof Vertex) {
103 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) o);
104 obj.ifPresent(ja::add);
109 return Optional.of(jo);
113 * Returns an Optional<JsonObject> object using "nodes" as a wrapper to encapsulate json objects
116 * @throws AAIFormatVertexException
118 protected Optional<JsonObject> getJsonFromTree(Tree<?> tree) throws AAIFormatVertexException {
119 if (tree.isEmpty()) {
120 return Optional.of(new JsonObject());
122 String nodeIdentifier = "nodes";
124 JsonObject t = new JsonObject();
125 JsonArray ja = this.getNodesArray(tree, null, nodeIdentifier);
129 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
132 return Optional.of(t);
136 * Returns an Optional<JsonObject> object using "related-nodes" to encapsulate nested json objects.
137 * Primarily intended to be utilized by the "as-tree" query parameter feature
141 * @throws AAIFormatVertexException
143 protected Optional<JsonObject> getRelatedNodesFromTree(Tree<?> tree, Map<String, List<String>> properties) throws AAIFormatVertexException {
144 if (tree.isEmpty()) {
145 return Optional.of(new JsonObject());
147 String nodeIdentifier = "related-nodes";
149 // Creating another DS to help with calls in O(1)
150 Map<String, Set<String>> filterPropertiesMap = createFilteredPropertyMap(properties);
152 JsonObject t = new JsonObject();
153 JsonArray ja = this.getNodesArray(tree, filterPropertiesMap, nodeIdentifier);
155 t.add("results", ja);
156 return Optional.of(t);
158 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
161 return Optional.empty();
165 * Returns JsonArray Object populated with nested json wrapped by the nodeIdentifier parameter
167 * @param filterPropertiesMap
168 * @param nodeIdentifier
170 * @throws AAIFormatVertexException
172 protected JsonArray getNodesArray(Tree<?> tree, Map<String, Set<String>> filterPropertiesMap, String nodeIdentifier) throws AAIFormatVertexException {
173 JsonArray nodes = new JsonArray();
174 for (Map.Entry<?, ? extends Tree<?>> entry : tree.entrySet()) {
175 JsonObject me = new JsonObject();
176 if (entry.getKey() instanceof Vertex) {
177 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) entry.getKey());
178 if (obj.isPresent()) {
179 me = getPropertyFilteredObject(obj, filterPropertiesMap);
184 JsonArray ja = this.getNodesArray(entry.getValue(), filterPropertiesMap, nodeIdentifier);
186 me.add(nodeIdentifier, ja);
188 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
196 * Returns a Map<String, Set<String>> object through converting given map parameter
200 protected Map<String, Set<String>> createFilteredPropertyMap(Map<String, List<String>> properties) {
201 if (properties == null)
202 return new HashMap<>();
204 return properties.entrySet().stream()
206 Set<String> newSet = entry.getValue().stream()
207 .map(this::truncateApostrophes)
208 .collect(Collectors.toSet());
210 return Pair.of(entry.getKey(), newSet);
212 ).collect(Collectors.toMap(Pair::getKey, Pair::getValue));
216 * Returns a string with it's apostrophes truncated at the start and end.
220 protected String truncateApostrophes(String s) {
221 if (s == null || s.isEmpty()) {
224 if (s.startsWith("'") && s.endsWith("'")) {
225 s = s.substring(1, s.length() - 1);
231 * Filters the given Optional<JsonObject> with the properties under a properties field
232 * or the properties under its respective node type.
234 * @param filterPropertiesMap
237 protected JsonObject getPropertyFilteredObject(Optional<JsonObject> obj,
238 Map<String, Set<String>> filterPropertiesMap) {
241 if (filterPropertiesMap == null || filterPropertiesMap.isEmpty()) {
244 ImmutableTriple<JsonObject, Optional<String>, Optional<JsonObject>> triple =
245 cloneObjectAndExtractNodeTypeAndProperties(jsonObj);
247 JsonObject result = triple.left;
248 Optional<String> nodeType = triple.middle;
249 Optional<JsonObject> properties = triple.right;
251 // Filter current object based on it containing fields: "node-type" and "properties"
252 if (nodeType.isPresent() && properties.isPresent()) {
253 filterByNodeTypeAndProperties(result, nodeType.get(), properties.get(), filterPropertiesMap);
255 // filter current object based on the: key - nodeType & value - JsonObject of nodes properties
256 filterByJsonObj(result, jsonObj, filterPropertiesMap);
262 ).orElseGet(JsonObject::new);
265 private ImmutableTriple<JsonObject, Optional<String>, Optional<JsonObject>> cloneObjectAndExtractNodeTypeAndProperties(
266 JsonObject jsonObj) {
267 JsonObject result = new JsonObject();
268 Optional<String> nodeType = Optional.empty();
269 Optional<JsonObject> properties = Optional.empty();
272 for (Map.Entry<String, JsonElement> mapEntry : jsonObj.entrySet()) {
273 String key = mapEntry.getKey();
274 JsonElement value = mapEntry.getValue();
276 // also, check if payload has node-type and properties fields
277 if (key.equals(NODE_TYPE_KEY) && value != null) {
278 nodeType = Optional.of(value.getAsString());
279 } else if (key.equals(PROPERTIES_KEY) && value != null && value.isJsonObject()) {
280 properties = Optional.of(value.getAsJsonObject());
282 result.add(key, value);
285 return ImmutableTriple.of(result, nodeType, properties);
289 * Returns a JsonObject with filtered properties using "node-type" and "properties"
290 * Used for formats with payloads similar to simple and raw
294 * @param filterPropertiesMap
297 private JsonObject filterByNodeTypeAndProperties(JsonObject result, String nodeType, JsonObject properties, Map<String, Set<String>> filterPropertiesMap) {
298 if (result == null || nodeType == null || nodeType.isEmpty() || properties == null || filterPropertiesMap == null) {
301 if (filterPropertiesMap.containsKey(nodeType)) { // filterPropertiesMap keys are nodeTypes - keys are obtained from the incoming query request
302 Set<String> filterSet = filterPropertiesMap.get(nodeType);
303 JsonObject filteredProperties = new JsonObject();
304 for (String property : filterSet) { // Each nodeType should have a set of properties to be retained in the response
305 if (properties.get(property) != null) {
306 filteredProperties.add(property, properties.get(property));
309 result.remove(PROPERTIES_KEY);
310 result.add(PROPERTIES_KEY, filteredProperties);
316 * Returns a JsonObject with its properties filtered
319 * @param filterPropertiesMap
322 private JsonObject filterByJsonObj(JsonObject result, JsonObject jsonObj, Map<String, Set<String>> filterPropertiesMap) {
323 if (result == null || jsonObj == null || filterPropertiesMap == null) {
327 for (Map.Entry<String, JsonElement> mapEntry : jsonObj.entrySet()) {
328 String key = mapEntry.getKey();
329 JsonElement value = mapEntry.getValue();
330 JsonObject filteredProperties = new JsonObject();
331 if (value != null && value.isJsonObject() && filterPropertiesMap.containsKey(key)) {
332 JsonObject joProperties = value.getAsJsonObject();
333 Set<String> filterSet = filterPropertiesMap.get(key);
334 for (String property : filterSet) {
335 if (joProperties.get(property) != null) {
336 filteredProperties.add(property, joProperties.get(property));
340 result.add(key, filteredProperties);
347 * Returns a filtered JsonObject with properties contained in the parameter filterPropertiesMap
349 * @param filterPropertiesMap
352 protected JsonObject filterProperties(Optional<JsonObject> properties, String nodeType,
353 Map<String, Set<String>> filterPropertiesMap) {
354 return properties.map(jo -> {
355 if (filterPropertiesMap == null || filterPropertiesMap.isEmpty()) {
356 return properties.get();
359 JsonObject result = new JsonObject();
361 for (Map.Entry<String, JsonElement> mapEntry : jo.entrySet()) {
362 String key = mapEntry.getKey();
363 JsonElement value = mapEntry.getValue();
364 result.add(key, value);
368 if (filterPropertiesMap.containsKey(nodeType)) {
369 Set<String> filterSet = filterPropertiesMap.get(nodeType);
370 for (Map.Entry<String, JsonElement> mapEntry : jo.entrySet()) {
371 String key = mapEntry.getKey();
372 if (!filterSet.contains(key)) {
378 }).orElseGet(JsonObject::new);
382 public int parallelThreshold() {