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 java.util.HashMap;
24 import java.util.List;
26 import java.util.Optional;
28 import java.util.stream.Collectors;
30 import org.apache.commons.lang3.tuple.ImmutableTriple;
31 import org.apache.commons.lang3.tuple.Pair;
32 import org.apache.tinkerpop.gremlin.process.traversal.Path;
33 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
34 import org.apache.tinkerpop.gremlin.structure.Vertex;
35 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatQueryResultFormatNotSupported;
36 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import com.google.gson.JsonArray;
41 import com.google.gson.JsonElement;
42 import com.google.gson.JsonObject;
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;
98 protected abstract Optional<JsonObject> getJsonFromVertex(Vertex input, Map<String, List<String>> properties) throws AAIFormatVertexException;
100 protected Optional<JsonObject> getJsonFromPath(Path input) throws AAIFormatVertexException {
101 List<Object> path = input.objects();
103 JsonObject jo = new JsonObject();
104 JsonArray ja = new JsonArray();
106 for (Object o : path) {
107 if (o instanceof Vertex) {
108 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) o);
109 obj.ifPresent(ja::add);
114 return Optional.of(jo);
118 * Returns an Optional<JsonObject> object using "nodes" as a wrapper to encapsulate json objects
121 * @throws AAIFormatVertexException
123 protected Optional<JsonObject> getJsonFromTree(Tree<?> tree) throws AAIFormatVertexException {
124 if (tree.isEmpty()) {
125 return Optional.of(new JsonObject());
127 String nodeIdentifier = "nodes";
129 JsonObject t = new JsonObject();
130 JsonArray ja = this.getNodesArray(tree, null, nodeIdentifier);
134 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
137 return Optional.of(t);
141 * Returns an Optional<JsonObject> object using "related-nodes" to encapsulate nested json objects.
142 * Primarily intended to be utilized by the "as-tree" query parameter feature
146 * @throws AAIFormatVertexException
148 protected Optional<JsonObject> getRelatedNodesFromTree(Tree<?> tree, Map<String, List<String>> properties) throws AAIFormatVertexException {
149 if (tree.isEmpty()) {
150 return Optional.of(new JsonObject());
152 String nodeIdentifier = "related-nodes";
154 // Creating another DS to help with calls in O(1)
155 Map<String, Set<String>> filterPropertiesMap = createFilteredPropertyMap(properties);
157 JsonObject t = new JsonObject();
158 JsonArray ja = this.getNodesArray(tree, filterPropertiesMap, nodeIdentifier);
160 t.add("results", ja);
161 return Optional.of(t);
163 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
166 return Optional.empty();
170 * Returns JsonArray Object populated with nested json wrapped by the nodeIdentifier parameter
172 * @param filterPropertiesMap
173 * @param nodeIdentifier
175 * @throws AAIFormatVertexException
177 protected JsonArray getNodesArray(Tree<?> tree, Map<String, Set<String>> filterPropertiesMap, String nodeIdentifier) throws AAIFormatVertexException {
178 JsonArray nodes = new JsonArray();
179 for (Map.Entry<?, ? extends Tree<?>> entry : tree.entrySet()) {
180 JsonObject me = new JsonObject();
181 if (entry.getKey() instanceof Vertex) {
182 Optional<JsonObject> obj = this.getJsonFromVertex((Vertex) entry.getKey());
183 if (obj.isPresent()) {
184 me = getPropertyFilteredObject(obj, filterPropertiesMap);
189 JsonArray ja = this.getNodesArray(entry.getValue(), filterPropertiesMap, nodeIdentifier);
191 me.add(nodeIdentifier, ja);
193 logger.debug(RETURNED_EMPTY_JSONARRAY_MSG, nodeIdentifier);
201 * Returns a Map<String, Set<String>> object through converting given map parameter
205 protected Map<String, Set<String>> createFilteredPropertyMap(Map<String, List<String>> properties) {
206 if (properties == null)
207 return new HashMap<>();
209 return properties.entrySet().stream()
211 Set<String> newSet = entry.getValue().stream()
212 .map(this::truncateApostrophes)
213 .collect(Collectors.toSet());
215 return Pair.of(entry.getKey(), newSet);
217 ).collect(Collectors.toMap(Pair::getKey, Pair::getValue));
221 * Returns a string with it's apostrophes truncated at the start and end.
225 protected String truncateApostrophes(String s) {
226 if (s == null || s.isEmpty()) {
229 if (s.startsWith("'") && s.endsWith("'")) {
230 s = s.substring(1, s.length() - 1);
236 * Filters the given Optional<JsonObject> with the properties under a properties field
237 * or the properties under its respective node type.
239 * @param filterPropertiesMap
242 protected JsonObject getPropertyFilteredObject(Optional<JsonObject> obj,
243 Map<String, Set<String>> filterPropertiesMap) {
246 if (filterPropertiesMap == null || filterPropertiesMap.isEmpty()) {
249 ImmutableTriple<JsonObject, Optional<String>, Optional<JsonObject>> triple =
250 cloneObjectAndExtractNodeTypeAndProperties(jsonObj);
252 JsonObject result = triple.left;
253 Optional<String> nodeType = triple.middle;
254 Optional<JsonObject> properties = triple.right;
256 // Filter current object based on it containing fields: "node-type" and "properties"
257 if (nodeType.isPresent() && properties.isPresent()) {
258 filterByNodeTypeAndProperties(result, nodeType.get(), properties.get(), filterPropertiesMap);
260 // filter current object based on the: key - nodeType & value - JsonObject of nodes properties
261 filterByJsonObj(result, jsonObj, filterPropertiesMap);
267 ).orElseGet(JsonObject::new);
270 private ImmutableTriple<JsonObject, Optional<String>, Optional<JsonObject>> cloneObjectAndExtractNodeTypeAndProperties(
271 JsonObject jsonObj) {
272 JsonObject result = new JsonObject();
273 Optional<String> nodeType = Optional.empty();
274 Optional<JsonObject> properties = Optional.empty();
277 for (Map.Entry<String, JsonElement> mapEntry : jsonObj.entrySet()) {
278 String key = mapEntry.getKey();
279 JsonElement value = mapEntry.getValue();
281 // also, check if payload has node-type and properties fields
282 if (key.equals(NODE_TYPE_KEY) && value != null) {
283 nodeType = Optional.of(value.getAsString());
284 } else if (key.equals(PROPERTIES_KEY) && value != null && value.isJsonObject()) {
285 properties = Optional.of(value.getAsJsonObject());
287 result.add(key, value);
290 return ImmutableTriple.of(result, nodeType, properties);
294 * Returns a JsonObject with filtered properties using "node-type" and "properties"
295 * Used for formats with payloads similar to simple and raw
299 * @param filterPropertiesMap
302 private JsonObject filterByNodeTypeAndProperties(JsonObject result, String nodeType, JsonObject properties, Map<String, Set<String>> filterPropertiesMap) {
303 if (result == null || nodeType == null || nodeType.isEmpty() || properties == null || filterPropertiesMap == null) {
306 if (filterPropertiesMap.containsKey(nodeType)) { // filterPropertiesMap keys are nodeTypes - keys are obtained from the incoming query request
307 Set<String> filterSet = filterPropertiesMap.get(nodeType);
308 JsonObject filteredProperties = new JsonObject();
309 for (String property : filterSet) { // Each nodeType should have a set of properties to be retained in the response
310 if (properties.get(property) != null) {
311 filteredProperties.add(property, properties.get(property));
314 result.remove(PROPERTIES_KEY);
315 result.add(PROPERTIES_KEY, filteredProperties);
321 * Returns a JsonObject with its properties filtered
324 * @param filterPropertiesMap
327 private JsonObject filterByJsonObj(JsonObject result, JsonObject jsonObj, Map<String, Set<String>> filterPropertiesMap) {
328 if (result == null || jsonObj == null || filterPropertiesMap == null) {
332 for (Map.Entry<String, JsonElement> mapEntry : jsonObj.entrySet()) {
333 String key = mapEntry.getKey();
334 JsonElement value = mapEntry.getValue();
335 JsonObject filteredProperties = new JsonObject();
336 if (value != null && value.isJsonObject() && filterPropertiesMap.containsKey(key)) {
337 JsonObject joProperties = value.getAsJsonObject();
338 Set<String> filterSet = filterPropertiesMap.get(key);
339 for (String property : filterSet) {
340 if (joProperties.get(property) != null) {
341 filteredProperties.add(property, joProperties.get(property));
345 result.add(key, filteredProperties);
352 * Returns a filtered JsonObject with properties contained in the parameter filterPropertiesMap
354 * @param filterPropertiesMap
357 protected JsonObject filterProperties(Optional<JsonObject> properties, String nodeType,
358 Map<String, Set<String>> filterPropertiesMap) {
359 return properties.map(jo -> {
360 if (filterPropertiesMap == null || filterPropertiesMap.isEmpty()) {
361 return properties.get();
364 JsonObject result = new JsonObject();
366 for (Map.Entry<String, JsonElement> mapEntry : jo.entrySet()) {
367 String key = mapEntry.getKey();
368 JsonElement value = mapEntry.getValue();
369 result.add(key, value);
373 if (filterPropertiesMap.containsKey(nodeType)) {
374 Set<String> filterSet = filterPropertiesMap.get(nodeType);
375 for (Map.Entry<String, JsonElement> mapEntry : jo.entrySet()) {
376 String key = mapEntry.getKey();
377 if (!filterSet.contains(key)) {
383 }).orElseGet(JsonObject::new);
387 public int parallelThreshold() {