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;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Optional;
32 import java.util.stream.Collectors;
34 import org.apache.commons.lang3.tuple.ImmutableTriple;
35 import org.apache.commons.lang3.tuple.Pair;
36 import org.apache.tinkerpop.gremlin.process.traversal.Path;
37 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
38 import org.apache.tinkerpop.gremlin.structure.Vertex;
39 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported;
40 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
44 public abstract class MultiFormatMapper implements FormatMapper {
46 private static final Logger logger = LoggerFactory.getLogger(MultiFormatMapper.class);
48 protected boolean isTree = false;
49 protected static final String PROPERTIES_KEY = "properties";
50 protected static final String NODE_TYPE_KEY = "node-type";
52 protected static final String RETURNED_EMPTY_JSONARRAY_MSG =
53 "Returned empty JsonArray - Could not populate nested json objects for wrapper: {}";
56 public Optional<JsonObject> formatObject(Object input)
57 throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported {
58 if (input instanceof Vertex) {
59 logger.debug("Formatting vertex object");
60 return this.getJsonFromVertex((Vertex) input);
61 } else if (input instanceof Tree) {
62 logger.debug("Formatting tree object");
64 return this.getRelatedNodesFromTree((Tree<?>) input, null);
66 return this.getJsonFromTree((Tree<?>) input);
68 } else if (input instanceof Path) {
69 logger.debug("Formatting path object");
70 return this.getJsonFromPath((Path) input);
72 throw new AAIFormatQueryResultFormatNotSupported();
77 public Optional<JsonObject> formatObject(Object input, Map<String, List<String>> properties)
78 throws AAIFormatVertexException, AAIFormatQueryResultFormatNotSupported {
79 if (input instanceof Vertex) {
80 logger.debug("Formatting vertex object with properties map filter");
81 return this.getJsonFromVertex((Vertex) input, properties);
82 } else if (input instanceof Tree) {
83 logger.debug("Formatting tree object with properties map filter");
85 return this.getRelatedNodesFromTree((Tree<?>) input, properties);
87 return this.getJsonFromTree((Tree<?>) input);
89 } else if (input instanceof Path) {
90 logger.debug("Formatting path object");
91 return this.getJsonFromPath((Path) input);
93 throw new AAIFormatQueryResultFormatNotSupported();
97 protected abstract Optional<JsonObject> getJsonFromVertex(Vertex input) throws AAIFormatVertexException;
99 protected abstract Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties)
100 throws AAIFormatVertexException;
102 protected Optional<JsonObject> getJsonFromPath(Path input) throws AAIFormatVertexException {
103 List<Object> path = input.objects();
105 JsonObject jo = new JsonObject();
106 JsonArray ja = new JsonArray();
108 for (Object o : path) {
109 if (o instanceof Vertex) {
110 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) o);
111 obj.ifPresent(ja::add);
116 return Optional.of(jo);
120 * Returns an Optional<JsonObject> object using "nodes" as a wrapper to encapsulate json objects
124 * @throws AAIFormatVertexException
126 protected Optional<JsonObject> getJsonFromTree(Tree<?> tree) throws AAIFormatVertexException {
127 if (tree.isEmpty()) {
128 return Optional.of(new JsonObject());
130 String nodeIdentifier = "nodes";
132 JsonObject t = new JsonObject();
133 JsonArray ja = this.getNodesArray(tree, null, nodeIdentifier);
137 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
140 return Optional.of(t);
144 * Returns an Optional<JsonObject> object using "related-nodes" to encapsulate nested json objects.
145 * Primarily intended to be utilized by the "as-tree" query parameter feature
150 * @throws AAIFormatVertexException
152 protected Optional<JsonObject> getRelatedNodesFromTree(Tree<?> tree, Map<String, List<String>> properties)
153 throws AAIFormatVertexException {
154 if (tree.isEmpty()) {
155 return Optional.of(new JsonObject());
157 String nodeIdentifier = "related-nodes";
159 // Creating another DS to help with calls in O(1)
160 Map<String, Set<String>> filterPropertiesMap = createFilteredPropertyMap(properties);
162 JsonObject t = new JsonObject();
163 JsonArray ja = this.getNodesArray(tree, filterPropertiesMap, nodeIdentifier);
165 t.add("results", ja);
166 return Optional.of(t);
168 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
171 return Optional.empty();
175 * Returns JsonArray Object populated with nested json wrapped by the nodeIdentifier parameter
178 * @param filterPropertiesMap
179 * @param nodeIdentifier
181 * @throws AAIFormatVertexException
183 protected JsonArray getNodesArray(Tree<?> tree, Map<String, Set<String>> filterPropertiesMap, String nodeIdentifier)
184 throws AAIFormatVertexException {
185 JsonArray nodes = new JsonArray();
186 for (Map.Entry<?, ? extends Tree<?>> entry : tree.entrySet()) {
187 JsonObject me = new JsonObject();
188 if (entry.getKey() instanceof Vertex) {
189 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) entry.getKey());
190 if (obj.isPresent()) {
191 me = getPropertyFilteredObject(obj, filterPropertiesMap);
196 JsonArray ja = this.getNodesArray(entry.getValue(), filterPropertiesMap, nodeIdentifier);
198 me.add(nodeIdentifier, ja);
200 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
208 * Returns a Map<String, Set<String>> object through converting given map parameter
213 protected Map<String, Set<String>> createFilteredPropertyMap(Map<String, List<String>> properties) {
214 if (properties == null)
215 return new HashMap<>();
217 return properties.entrySet().stream().map(entry -> {
218 Set<String> newSet = entry.getValue().stream().map(this::truncateApostrophes).collect(Collectors.toSet());
220 return Pair.of(entry.getKey(), newSet);
221 }).collect(Collectors.toMap(Pair::getKey, Pair::getValue));
225 * Returns a string with it's apostrophes truncated at the start and end.
230 protected String truncateApostrophes(String s) {
231 if (s == null || s.isEmpty()) {
234 if (s.startsWith("'") && s.endsWith("'")) {
235 s = s.substring(1, s.length() - 1);
241 * Filters the given Optional<JsonObject> with the properties under a properties field
242 * or the properties under its respective node type.
245 * @param filterPropertiesMap
248 protected JsonObject getPropertyFilteredObject(Optional<JsonObject> obj,
249 Map<String, Set<String>> filterPropertiesMap) {
250 return obj.map(jsonObj -> {
251 if (filterPropertiesMap == null || filterPropertiesMap.isEmpty()) {
254 ImmutableTriple<JsonObject, Optional<String>, Optional<JsonObject>> triple =
255 cloneObjectAndExtractNodeTypeAndProperties(jsonObj);
257 JsonObject result = triple.left;
258 Optional<String> nodeType = triple.middle;
259 Optional<JsonObject> properties = triple.right;
261 // Filter current object based on it containing fields: "node-type" and "properties"
262 if (nodeType.isPresent() && properties.isPresent()) {
263 filterByNodeTypeAndProperties(result, nodeType.get(), properties.get(), filterPropertiesMap);
265 // filter current object based on the: key - nodeType & value - JsonObject of nodes properties
266 filterByJsonObj(result, jsonObj, filterPropertiesMap);
271 }).orElseGet(JsonObject::new);
274 private ImmutableTriple<JsonObject, Optional<String>, Optional<JsonObject>> cloneObjectAndExtractNodeTypeAndProperties(
275 JsonObject jsonObj) {
276 JsonObject result = new JsonObject();
277 Optional<String> nodeType = Optional.empty();
278 Optional<JsonObject> properties = Optional.empty();
281 for (Map.Entry<String, JsonElement> mapEntry : jsonObj.entrySet()) {
282 String key = mapEntry.getKey();
283 JsonElement value = mapEntry.getValue();
285 // also, check if payload has node-type and properties fields
286 if (key.equals(NODE_TYPE_KEY) && value != null) {
287 nodeType = Optional.of(value.getAsString());
288 } else if (key.equals(PROPERTIES_KEY) && value != null && value.isJsonObject()) {
289 properties = Optional.of(value.getAsJsonObject());
291 result.add(key, value);
294 return ImmutableTriple.of(result, nodeType, properties);
298 * Returns a JsonObject with filtered properties using "node-type" and "properties"
299 * Used for formats with payloads similar to simple and raw
304 * @param filterPropertiesMap
307 private JsonObject filterByNodeTypeAndProperties(JsonObject result, String nodeType, JsonObject properties,
308 Map<String, Set<String>> filterPropertiesMap) {
309 if (result == null || nodeType == null || nodeType.isEmpty() || properties == null
310 || filterPropertiesMap == null) {
313 if (filterPropertiesMap.containsKey(nodeType)) { // filterPropertiesMap keys are nodeTypes - keys are obtained
314 // from the incoming query request
315 Set<String> filterSet = filterPropertiesMap.get(nodeType);
316 JsonObject filteredProperties = new JsonObject();
317 for (String property : filterSet) { // Each nodeType should have a set of properties to be retained in the
319 if (properties.get(property) != null) {
320 filteredProperties.add(property, properties.get(property));
323 result.remove(PROPERTIES_KEY);
324 result.add(PROPERTIES_KEY, filteredProperties);
330 * Returns a JsonObject with its properties filtered
334 * @param filterPropertiesMap
337 private JsonObject filterByJsonObj(JsonObject result, JsonObject jsonObj,
338 Map<String, Set<String>> filterPropertiesMap) {
339 if (result == null || jsonObj == null || filterPropertiesMap == null) {
343 for (Map.Entry<String, JsonElement> mapEntry : jsonObj.entrySet()) {
344 String key = mapEntry.getKey();
345 JsonElement value = mapEntry.getValue();
346 JsonObject filteredProperties = new JsonObject();
347 if (value != null && value.isJsonObject() && filterPropertiesMap.containsKey(key)) {
348 JsonObject joProperties = value.getAsJsonObject();
349 Set<String> filterSet = filterPropertiesMap.get(key);
350 for (String property : filterSet) {
351 if (joProperties.get(property) != null) {
352 filteredProperties.add(property, joProperties.get(property));
356 result.add(key, filteredProperties);
363 * Returns a filtered JsonObject with properties contained in the parameter filterPropertiesMap
366 * @param filterPropertiesMap
369 protected JsonObject filterProperties(Optional<JsonObject> properties, String nodeType,
370 Map<String, Set<String>> filterPropertiesMap) {
371 return properties.map(jo -> {
372 if (filterPropertiesMap == null || filterPropertiesMap.isEmpty()) {
373 return properties.get();
376 JsonObject result = new JsonObject();
378 for (Map.Entry<String, JsonElement> mapEntry : jo.entrySet()) {
379 String key = mapEntry.getKey();
380 JsonElement value = mapEntry.getValue();
381 result.add(key, value);
385 if (filterPropertiesMap.containsKey(nodeType)) {
386 Set<String> filterSet = filterPropertiesMap.get(nodeType);
387 for (Map.Entry<String, JsonElement> mapEntry : jo.entrySet()) {
388 String key = mapEntry.getKey();
389 if (!filterSet.contains(key)) {
395 }).orElseGet(JsonObject::new);
399 public int parallelThreshold() {