f140bcd48850a4334d22298ef47f5b9171a0c368
[aai/traversal.git] / aai-traversal / src / main / java / org / onap / aai / dbgraphmap / SearchGraph.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 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  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22 package org.onap.aai.dbgraphmap;
23
24 import java.io.UnsupportedEncodingException;
25 import java.net.URI;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.stream.Stream;
34
35 import javax.ws.rs.core.HttpHeaders;
36 import javax.ws.rs.core.MediaType;
37 import javax.ws.rs.core.Response;
38 import javax.ws.rs.core.UriBuilderException;
39 import javax.xml.bind.JAXBException;
40
41 import org.apache.tinkerpop.gremlin.process.traversal.P;
42 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
43 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
44 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
45 import org.apache.tinkerpop.gremlin.structure.Vertex;
46 import org.eclipse.persistence.dynamic.DynamicEntity;
47 import org.eclipse.persistence.dynamic.DynamicType;
48 import org.eclipse.persistence.exceptions.DynamicException;
49 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
50 import org.onap.aai.db.DbMethHelper;
51 import org.onap.aai.db.props.AAIProperties;
52 import org.onap.aai.dbgen.PropertyLimitDesc;
53 import org.onap.aai.dbgraphgen.ModelBasedProcessing;
54 import org.onap.aai.dbgraphgen.ResultSet;
55 import org.onap.aai.dbmap.DBConnectionType;
56 import org.onap.aai.exceptions.AAIException;
57 import org.onap.aai.extensions.AAIExtensionMap;
58 import org.onap.aai.introspection.Introspector;
59 import org.onap.aai.introspection.Loader;
60 import org.onap.aai.introspection.LoaderFactory;
61 import org.onap.aai.introspection.ModelType;
62 import org.onap.aai.introspection.MoxyLoader;
63 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
64 import org.onap.aai.parsers.relationship.RelationshipToURI;
65 import org.onap.aai.query.builder.QueryBuilder;
66 import org.onap.aai.schema.enums.ObjectMetadata;
67 import org.onap.aai.schema.enums.PropertyMetadata;
68 import org.onap.aai.serialization.db.DBSerializer;
69 import org.onap.aai.serialization.db.EdgeRule;
70 import org.onap.aai.serialization.db.EdgeRules;
71 import org.onap.aai.serialization.engines.QueryStyle;
72 import org.onap.aai.serialization.engines.TitanDBEngine;
73 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
74 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
75 import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
76 import org.onap.aai.util.StoreNotificationEvent;
77
78 import com.att.eelf.configuration.EELFLogger;
79 import com.att.eelf.configuration.EELFManager;
80 import com.google.common.base.CaseFormat;
81
82 import edu.emory.mathcs.backport.java.util.Collections;
83
84 /**
85  * Database Mapping class which acts as the middle man between the REST interface objects 
86  * for the Search namespace 
87
88  */
89 public class SearchGraph {
90
91         private final String COMPONENT = "aaidbmap";
92         private AAIExtensionMap aaiExtMap;
93         private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(SearchGraph.class);
94         /**
95          * Get the search result based on the includeNodeType and depth provided.
96          *
97          * @param fromAppId the from app id
98          * @param transId the trans id
99          * @param startNodeType the start node type
100          * @param startNodeKeyParams the start node key params
101          * @param includeNodeTypes the include node types
102          * @param depth the depth
103          * @param aaiExtMap the aai ext map
104          * @return Response
105          * @throws AAIException the AAI exception
106          */
107         public Response runGenericQuery (
108                         HttpHeaders headers,
109                         String startNodeType,
110                         List <String> startNodeKeyParams,
111                         List <String> includeNodeTypes,
112                         final int depth,
113                         TransactionalGraphEngine dbEngine,
114                         Loader loader,
115                         UrlBuilder urlBuilder) throws AAIException {
116                 Response response = null;
117                 boolean success = true;
118                 String result = "";
119                 try {                   
120                         dbEngine.startTransaction();
121
122                         if( startNodeType == null ){
123                                 throw new AAIException("AAI_6120", "null start-node-type passed to the generic query"); 
124                         }
125
126                         if( startNodeKeyParams == null ){
127                                 throw new AAIException("AAI_6120", "no key param passed to the generic query"); 
128                         }
129
130                         if( includeNodeTypes == null ){
131                                 throw new AAIException("AAI_6120", "no include params passed to the generic query"); 
132                         }
133
134                         if (depth > 6) {
135                                 throw new AAIException("AAI_6120", "The maximum depth supported by the generic query is 6");
136                         }
137                         final QueryBuilder queryBuilder;
138                         
139                         // there is an issue with service-instance - it is a unique node but still dependent
140                         // for now query it directly without attempting to craft a valid URI    
141                         if (startNodeType.equalsIgnoreCase("service-instance") && startNodeKeyParams.size() == 1) {
142                                 Introspector obj = loader.introspectorFromName(startNodeType);
143                                 // Build a hash with keys to uniquely identify the start Node
144                                 String keyName = null;
145                                 String keyValue = null;
146
147                                 QueryBuilder builder = dbEngine.getQueryBuilder().getVerticesByIndexedProperty(AAIProperties.NODE_TYPE, "service-instance");
148                                 for( String keyData : startNodeKeyParams ){ 
149                                         int colonIndex = keyData.indexOf(":");
150                                         if( colonIndex <= 0 ){
151                                                 throw new AAIException("AAI_6120", "Bad key param passed in: [" + keyData + "]"); 
152                                         }
153                                         else {
154                                                 keyName = keyData.substring(0, colonIndex).split("\\.")[1];
155                                                 keyValue = keyData.substring(colonIndex + 1);
156                                                 builder.getVerticesByProperty(keyName, keyValue);
157                                         }
158                                 }
159                                 
160                                 queryBuilder = builder;
161                         } else {
162                                 URI uri = craftUriFromQueryParams(loader, startNodeType, startNodeKeyParams);
163                                 queryBuilder = dbEngine.getQueryBuilder().createQueryFromURI(uri).getQueryBuilder();
164                         }
165                         List<Vertex> results = queryBuilder.toList();
166                         if( results.isEmpty()){
167                                 throw new AAIException("AAI_6114", "No Node of type " + 
168                                                 startNodeType + 
169                                                 " found for properties: " + 
170                                                 startNodeKeyParams.toString()); 
171                         } else if (results.size() > 1) {
172                                 String detail = "More than one Node found by getUniqueNode for params: " + startNodeKeyParams.toString() + "\n";
173                                 throw new AAIException("AAI_6112", detail); 
174                         }
175
176                         Vertex startNode = results.get(0);
177
178                         Collection <Vertex> ver = new HashSet <>();
179                         List<Vertex> queryResults = new ArrayList<>();
180                         GraphTraversalSource traversalSource = dbEngine.asAdmin().getReadOnlyTraversalSource();
181                         GraphTraversal<Vertex, Vertex> traversal;
182                         if (includeNodeTypes.contains(startNodeType) || depth == 0 || includeNodeTypes.contains("all") )
183                                 ver.add(startNode);
184
185                         // Now look for a node of includeNodeType within a given depth
186                         traversal = traversalSource.withSideEffect("x", ver).V(startNode)
187                         .times(depth).repeat(__.both().store("x")).cap("x").unfold();
188                         
189                         if (!includeNodeTypes.contains("all")) {
190                                 traversal.where(__.has(AAIProperties.NODE_TYPE, P.within(includeNodeTypes)));
191                         }
192                         queryResults = traversal.toList();
193                         
194
195                         if( queryResults.isEmpty()){
196                                 LOGGER.warn("No nodes found - apipe was null/empty");
197                         }
198                         else {                                          
199                                 
200                                 Introspector searchResults = createSearchResults(loader, urlBuilder, queryResults);
201
202                                 String outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
203                                 org.onap.aai.introspection.MarshallerProperties properties = new org.onap.aai.introspection.MarshallerProperties.Builder(
204                                                 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
205
206                                 result = searchResults.marshal(properties);
207                                 response = Response.ok().entity(result).build();
208
209                                 LOGGER.debug(ver.size() + " node(s) traversed, " + queryResults.size() + " found");
210                         }
211                         success = true;
212                 } catch (AAIException e) { 
213                         success = false;
214                         throw e;
215                 } catch (Exception e) {
216                         success = false;
217                         throw new AAIException("AAI_5105", e);
218                 } finally {
219                         if (dbEngine != null) {
220                                 if (success) {
221                                         dbEngine.commit();
222                                 } else {
223                                         dbEngine.rollback();
224                                 }
225                         }
226
227                 }
228
229                 return response;        
230         }       
231
232         private URI craftUriFromQueryParams(Loader loader, String startNodeType, List<String> startNodeKeyParams) throws UnsupportedEncodingException, IllegalArgumentException, UriBuilderException, AAIException {
233                 Introspector relationship = loader.introspectorFromName("relationship");
234                 
235                 relationship.setValue("related-to", startNodeType);
236                 List<Object> relationshipDataList = relationship.getValue("relationship-data");
237
238                 for( String keyData : startNodeKeyParams ){ 
239                         int colonIndex = keyData.indexOf(":");
240                         if( colonIndex <= 0 ){
241                                 throw new AAIException("AAI_6120", "Bad key param passed in: [" + keyData + "]"); 
242                         }
243                         else {
244                                 Introspector data = loader.introspectorFromName("relationship-data");
245                                 data.setValue("relationship-key", keyData.substring(0, colonIndex));
246                                 data.setValue("relationship-value", keyData.substring(colonIndex + 1));
247                                 relationshipDataList.add(data.getUnderlyingObject());
248                         }
249                 }
250                 
251                 RelationshipToURI parser = new RelationshipToURI(loader, relationship);
252
253                 return parser.getUri();
254         }
255
256         /**
257          * Run nodes query.
258          *
259          * @param fromAppId the from app id
260          * @param transId the trans id
261          * @param targetNodeType the target node type
262          * @param edgeFilterParams the edge filter params
263          * @param filterParams the filter params
264          * @param aaiExtMap the aai ext map
265          * @return Response
266          * @throws AAIException the AAI exception
267          */
268         public Response runNodesQuery (
269                         HttpHeaders headers,
270                         String targetNodeType,
271                         List <String> edgeFilterParams,
272                         List <String> filterParams,
273                         TransactionalGraphEngine dbEngine,
274                         Loader loader,
275                         UrlBuilder urlBuilder) throws AAIException {
276                 
277                 Response response = null;
278                 boolean success = true;
279         String result = "";
280                 final String EQUALS = "EQUALS";
281                 final String DOES_NOT_EQUAL = "DOES-NOT-EQUAL";
282                 final String EXISTS = "EXISTS";
283                 final String DOES_NOT_EXIST = "DOES-NOT-EXIST";
284                 try {
285                         
286                         dbEngine.startTransaction();
287                         
288                         Introspector target;
289                         
290                         if( targetNodeType == null || targetNodeType == "" ){
291                                 throw new AAIException("AAI_6120", "null or empty target-node-type passed to the node query"); 
292                         }
293
294                         try {
295                                 target = loader.introspectorFromName(targetNodeType);
296                         } catch (AAIUnknownObjectException e) {
297                                 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + targetNodeType + "] passed to node query."); 
298                         }
299                         
300                         if( filterParams.isEmpty()  && edgeFilterParams.isEmpty()){
301                                 // For now, it's ok to pass no filter params.  We'll just return ALL the nodes of the requested type.
302                                 LOGGER.warn("No filters passed to the node query");
303                         }
304
305                         StringBuilder queryStringForMsg = new StringBuilder();  
306                         GraphTraversal<Vertex, Vertex> traversal  = dbEngine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE, targetNodeType);
307                         queryStringForMsg.append("has(\"aai-node-type\"," + targetNodeType + ")");
308                         
309                         for( String filter : filterParams ) {
310                                 String [] pieces = filter.split(":");
311                                 if( pieces.length < 2 ){
312                                         throw new AAIException("AAI_6120", "bad filter passed to node query: [" + filter + "]"); 
313                                 }
314                                 else {
315                                         String propName = this.findDbPropName(target, pieces[0]);
316                                         String filterType = pieces[1];
317                                         if( filterType.equals(EQUALS)){
318                                                 if( pieces.length < 3 ){ 
319                                                         throw new AAIException("AAI_6120", "No value passed for filter: [" + filter + "]"); 
320                                                 }
321                                                 String value = "?";
322                                                 if( pieces.length == 3 ){
323                                                         value = pieces[2];
324                                                 }
325                                                 else if( pieces.length > 3 ){
326                                                         // When a ipv6 address comes in as a value, it has colons in it which require us to 
327                                                         // pull the "value" off the end of the filter differently
328                                                         int startPos4Value = propName.length() + filterType.length() + 3;
329                                                         value = filter.substring(startPos4Value);
330                                                 }
331                                                 queryStringForMsg.append(".has(" + propName + "," + value + ")");
332                                                 traversal.has(propName,value);
333                                         }
334                                         else if( filterType.equals(DOES_NOT_EQUAL)){
335                                                 if( pieces.length < 3 ){
336                                                         throw new AAIException("AAI_6120", "No value passed for filter: [" + filter + "]"); 
337                                                 }
338                                                 String value = "?";
339                                                 if( pieces.length == 3 ){
340                                                         value = pieces[2];
341                                                 }
342                                                 else if( pieces.length > 3 ){
343                                                         // When a ipv6 address comes in as a value, it has colons in it which require us to 
344                                                         // pull the "value" off the end of the filter differently
345                                                         int startPos4Value = propName.length() + filterType.length() + 3;
346                                                         value = filter.substring(startPos4Value);
347                                                 }
348                                                 queryStringForMsg.append(".hasNot(" + propName + "," + value + ")");
349                                                 traversal.not(__.has(propName,value));
350                                         }
351                                         else if( filterType.equals(EXISTS)){
352                                                 queryStringForMsg.append(".has(" + propName + ")");
353                                                 traversal.has(propName);
354                                         }
355                                         else if( filterType.equals(DOES_NOT_EXIST)){
356                                                 queryStringForMsg.append(".hasNot(" + propName + ")");
357                                                 traversal.hasNot(propName);
358                                         }
359                                         else {
360                                                 throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]"); 
361                                         }
362                                 }
363                         }
364
365                         if (!edgeFilterParams.isEmpty()) {
366                                 // edge-filter=pserver:EXISTS: OR pserver:EXISTS:hostname:XXX
367                                 // edge-filter=pserver:DOES-NOT-EXIST: OR pserver:DOES-NOT-EXIST:hostname:XXX
368                                 String filter = edgeFilterParams.get(0); // we process and allow only one edge filter for now
369                                 String [] pieces = filter.split(":");
370                                 if( pieces.length < 2 || pieces.length == 3 || pieces.length > 4){
371                                         throw new AAIException("AAI_6120", "bad edge-filter passed: [" + filter + "]"); 
372                                 } else {
373                                         String nodeType = pieces[0].toLowerCase();
374                                         String filterType = pieces[1].toUpperCase();
375                                         Introspector otherNode;
376                                         if (!filterType.equals(EXISTS) && !filterType.equals(DOES_NOT_EXIST)) {
377                                                 throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]"); 
378                                         }
379                                         try {
380                                                 otherNode = loader.introspectorFromName(nodeType);
381                                         } catch (AAIUnknownObjectException e) {
382                                                 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + nodeType + "] passed to node query."); 
383                                         }
384                                         String propName = null;
385                                         String propValue = null;
386                                         if ( pieces.length >= 3) {
387                                                 propName = this.findDbPropName(otherNode, pieces[2].toLowerCase());
388                                                 propValue = pieces[3];
389                                         }
390                                         String[] edgeLabels = getEdgeLabel(targetNodeType, nodeType);
391                                         
392                                         GraphTraversal<Vertex, Vertex> edgeSearch = __.start();
393                                         
394                                         edgeSearch.both(edgeLabels).has(AAIProperties.NODE_TYPE, nodeType);
395                                         if (propName != null) {
396                                                 // check for matching property
397                                                 if (propValue != null) {
398                                                         edgeSearch.has(propName, propValue);
399                                                 } else {
400                                                         edgeSearch.has(propName);
401                                                 }
402                                         }
403                                         
404                                         if( filterType.equals(DOES_NOT_EXIST)){
405                                                 traversal.where(__.not(edgeSearch));
406                                         } else if (filterType.equals(EXISTS)) {
407                                                 traversal.where(edgeSearch);
408                                         }
409                                 }
410                         }
411
412                         List<Vertex> results = traversal.toList();
413                         Introspector searchResults = createSearchResults(loader, urlBuilder, results);
414
415                         String outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
416                         org.onap.aai.introspection.MarshallerProperties properties = new org.onap.aai.introspection.MarshallerProperties.Builder(
417                                         org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
418
419                         result = searchResults.marshal(properties);
420                         response = Response.ok().entity(result).build();
421
422                         success = true;
423                 } catch (AAIException e) { 
424                         success = false;
425                         throw e;
426                 } catch (Exception e) {
427                         success = false;
428                         throw new AAIException("AAI_5105", e);
429                 } finally {
430                         if (dbEngine != null) {
431                                 if (success) {
432                                         dbEngine.commit();
433                                 } else {
434                                         dbEngine.rollback();
435                                 }
436                         }
437                 }
438
439                 return response;        
440         }
441
442         protected Introspector createSearchResults(Loader loader, UrlBuilder urlBuilder, List<Vertex> results)
443                         throws AAIUnknownObjectException {
444                 Introspector searchResults = loader.introspectorFromName("search-results");
445                 List<Object> resultDataList = searchResults.getValue("result-data");
446                 Stream<Vertex> stream;
447                 if (results.size() >= 50) {
448                         stream = results.parallelStream();
449                 } else {
450                         stream = results.stream();
451                 }
452                 boolean isParallel = stream.isParallel();
453                 stream.forEach(v -> {
454                         String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
455                         
456                         String thisNodeURL;
457                         try {
458                                 thisNodeURL = urlBuilder.pathed(v);
459                                 Introspector resultData = loader.introspectorFromName("result-data");
460
461                                 resultData.setValue("resource-type", nodeType);
462                                 resultData.setValue("resource-link", thisNodeURL);
463                                 if (isParallel) {
464                                         synchronized (resultDataList) {
465                                                 resultDataList.add(resultData.getUnderlyingObject());
466                                         }
467                                 } else {
468                                         resultDataList.add(resultData.getUnderlyingObject());
469                                 }
470                         } catch (AAIException | AAIFormatVertexException e) {
471                                 throw new RuntimeException(e);
472                         }
473                         
474                 });
475                 return searchResults;
476         }
477
478         private String findDbPropName(Introspector obj, String propName) {
479                 
480                 Optional<String> result = obj.getPropertyMetadata(propName, PropertyMetadata.DB_ALIAS);
481                 if (result.isPresent()) {
482                         return result.get();
483                 } else {
484                         return propName;
485                 }
486         }
487
488
489         /**
490          * Gets the edge label.
491          *
492          * @param targetNodeType the target node type
493          * @param nodeType the node type
494          * @return the edge label
495          * @throws AAIException the AAI exception
496          */
497         public static String[] getEdgeLabel(String targetNodeType, String nodeType) throws AAIException{
498                 Map<String, EdgeRule> rules = EdgeRules.getInstance().getEdgeRules(targetNodeType, nodeType);
499                 String[] results = rules.keySet().toArray(new String[0]);
500                 return results;
501         }
502
503
504         /**
505          * Run named query.
506          *
507          * @param fromAppId the from app id
508          * @param transId the trans id
509          * @param queryParameters the query parameters
510          * @param aaiExtMap the aai ext map
511          * @return the response
512          * @throws JAXBException the JAXB exception
513          * @throws AAIException the AAI exception
514          */
515         public Response runNamedQuery(String fromAppId, String transId, String queryParameters,
516                         DBConnectionType connectionType,
517                         AAIExtensionMap aaiExtMap) throws JAXBException, AAIException {
518
519                 Introspector inventoryItems;
520                 boolean success = true;
521                 TransactionalGraphEngine dbEngine = null;
522                 try {
523                         
524                         MoxyLoader loader = (MoxyLoader)LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
525                         DynamicJAXBContext jaxbContext = loader.getJAXBContext();
526                         dbEngine = new TitanDBEngine(
527                                         QueryStyle.TRAVERSAL,
528                                         connectionType,
529                                         loader);
530                         DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId);
531                         ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer);
532
533                         dbEngine.startTransaction();
534                         org.onap.aai.restcore.MediaType mediaType = org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE;
535                         String contentType = aaiExtMap.getHttpServletRequest().getContentType();
536                         if (contentType != null && contentType.contains("application/xml")) {
537                                 mediaType = org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE;
538                         }
539
540                         if (queryParameters.length() == 0) { 
541                                 queryParameters = "{}";
542                         }
543
544                         DynamicEntity modelAndNamedQuerySearch = (DynamicEntity)loader.unmarshal("ModelAndNamedQuerySearch", queryParameters, mediaType).getUnderlyingObject();
545                         if (modelAndNamedQuerySearch == null) { 
546                                 throw new AAIException("AAI_5105");
547                         }
548                         HashMap<String,Object> namedQueryLookupHash = new HashMap<String,Object>();
549
550                         DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters");
551                         String namedQueryUuid = null;
552                         if ((qp != null) && qp.isSet("namedQuery")) {    
553                                 DynamicEntity namedQuery = (DynamicEntity) qp.get("namedQuery");
554
555                                 if (namedQuery.isSet("namedQueryUuid")) { 
556                                         namedQueryUuid = namedQuery.get("namedQueryUuid");
557                                 }
558                                 if (namedQuery.isSet("namedQueryName")) { 
559                                         namedQueryLookupHash.put("named-query-name",  namedQuery.get("namedQueryName"));
560                                 }
561                                 if (namedQuery.isSet("namedQueryVersion")) { 
562                                         namedQueryLookupHash.put("named-query-version", namedQuery.get("namedQueryVersion"));
563                                 }
564                         }
565
566                         if (namedQueryUuid == null) { 
567
568                                 DbMethHelper dbMethHelper = new DbMethHelper(loader, dbEngine);
569                                 List<Vertex> namedQueryVertices = dbMethHelper.locateUniqueVertices("named-query", namedQueryLookupHash);
570                                 for (Vertex vert : namedQueryVertices) { 
571                                         namedQueryUuid = vert.<String>property("named-query-uuid").orElse(null); 
572                                         // there should only be one, we'll pick the first if not
573                                         break;
574                                 }
575                         }
576                         
577                         String secondaryFilterCutPoint = null;
578                         
579                         if (modelAndNamedQuerySearch.isSet("secondaryFilterCutPoint")) { 
580                                 secondaryFilterCutPoint = modelAndNamedQuerySearch.get("secondaryFilterCutPoint");
581                         }
582                         
583                         List<Map<String,Object>> startNodeFilterHash = new ArrayList<>();
584
585                         mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"), 
586                                         startNodeFilterHash, jaxbContext);                      
587
588                         Map<String,Object> secondaryFilterHash = new HashMap<>();
589                         
590                         mapSecondaryFilters((DynamicEntity)modelAndNamedQuerySearch.get("secondaryFilts"), 
591                                         secondaryFilterHash, jaxbContext);                      
592                         
593                         List<ResultSet> resultSet = processor.queryByNamedQuery(transId, fromAppId,
594                                         namedQueryUuid, startNodeFilterHash, aaiExtMap.getApiVersion(), secondaryFilterCutPoint, secondaryFilterHash);
595
596                         inventoryItems = loader.introspectorFromName("inventory-response-items");
597
598                         List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
599
600                         inventoryItems.setValue("inventory-response-item", invItemList);
601                         success = true;
602                 } catch (AAIException e) {
603                         success = false;
604                         throw e;
605                 } catch (Exception e) {
606                         success = false;
607                         throw new AAIException("AAI_5105", e);
608                 } finally {
609                         if (dbEngine != null) {
610                                 if (success) {
611                                         dbEngine.commit();
612                                 } else {
613                                         dbEngine.rollback();
614                                 }
615                         }
616                 }
617
618                 return getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders());
619         }
620
621         /**
622          * Execute model operation.
623          *
624          * @param fromAppId the from app id
625          * @param transId the trans id
626          * @param queryParameters the query parameters
627          * @param isDelete the is delete
628          * @param aaiExtMap the aai ext map
629          * @return the response
630          * @throws JAXBException the JAXB exception
631          * @throws AAIException the AAI exception
632          * @throws DynamicException the dynamic exception
633          * @throws UnsupportedEncodingException the unsupported encoding exception
634          */
635         public Response executeModelOperation(String fromAppId, String transId, String queryParameters,
636                         DBConnectionType connectionType,
637                         boolean isDelete,
638                         AAIExtensionMap aaiExtMap) throws JAXBException, AAIException, DynamicException, UnsupportedEncodingException {
639                 Response response;
640                 boolean success = true;
641                 TransactionalGraphEngine dbEngine = null;
642                 try {
643                         
644                         MoxyLoader loader = (MoxyLoader) LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
645                         DynamicJAXBContext jaxbContext = loader.getJAXBContext();
646                         dbEngine = new TitanDBEngine(
647                                         QueryStyle.TRAVERSAL,
648                                         connectionType,
649                                         loader);
650                         DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId);
651                         ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer);
652                         dbEngine.startTransaction();
653
654
655                         org.onap.aai.restcore.MediaType mediaType = org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE;
656                         String contentType = aaiExtMap.getHttpServletRequest().getContentType();
657                         if (contentType != null && contentType.contains("application/xml")) {
658                                 mediaType = org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE;
659                         }
660
661                         if (queryParameters.length() == 0) { 
662                                 queryParameters = "{}";
663                         }
664
665                         DynamicEntity modelAndNamedQuerySearch = (DynamicEntity)loader.unmarshal("ModelAndNamedQuerySearch", queryParameters, mediaType).getUnderlyingObject();
666                         if (modelAndNamedQuerySearch == null) { 
667                                 throw new AAIException("AAI_5105");
668                         }
669
670                         Map<String,Object> modelQueryLookupHash = new HashMap<>();
671                         
672                         String modelVersionId = null;
673                         String modelName = null;
674                         String modelInvariantId = null;
675                         String modelVersion = null;
676                         String topNodeType = null;
677
678                         if (modelAndNamedQuerySearch.isSet("topNodeType")) { 
679                                 topNodeType = modelAndNamedQuerySearch.get("topNodeType");
680                         }
681                         
682                         // the ways to get a model:
683                         
684                         // 1.  model-version-id (previously model-name-version-id
685                         // 2.  model-invariant-id (previously model-id) + model-version
686                         // 3.  model-name + model-version
687                                         
688                         // we will support both using the OverloadedModel object in the v9 oxm.  This allows us to unmarshal
689                         // either an old-style model or new-style model + model-ver object
690                         if (modelAndNamedQuerySearch.isSet("queryParameters")) { 
691                                 DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters");
692
693                                 if (qp.isSet("model")) { 
694                                         DynamicEntity model = (DynamicEntity) qp.get("model");
695
696                                         // on an old-style model object, the following 4 attrs were all present
697                                         if (model.isSet("modelNameVersionId")) { 
698                                                 modelVersionId = model.get("modelNameVersionId");
699                                         }
700                                         if (model.isSet("modelId")) { 
701                                                 modelInvariantId =  model.get("modelId");
702                                         }
703                                         if (model.isSet("modelName")) {
704                                                 modelName = model.get("modelName");
705                                         }
706                                         if (model.isSet("modelVersion")) { 
707                                                 modelVersion =  model.get("modelVersion");
708                                         }
709
710                                         // new style splits model-invariant-id from the other 3 attrs.  This is 
711                                         // the only way to directly look up the model object
712                                         if (model.isSet("modelInvariantId")) { 
713                                                 modelInvariantId =  model.get("modelInvariantId");
714                                         }
715                                                                 
716                                         if (model.isSet("modelVers")) {
717                                                 // we know that this is new style, because modelVers was not an option
718                                                 // before v9
719                                                 DynamicEntity modelVers = (DynamicEntity) model.get("modelVers");
720                                                 if (modelVers.isSet("modelVer")) {
721                                                         List<DynamicEntity> modelVerList = modelVers.get("modelVer");
722                                                         // if they send more than one, too bad, they get the first one
723                                                         DynamicEntity modelVer = modelVerList.get(0);
724                                                         if (modelVer.isSet("modelName")) {
725                                                                 modelName = modelVer.get("modelName");
726                                                         }
727                                                         if (modelVer.isSet("modelVersionId")) { 
728                                                                 modelVersionId =  modelVer.get("modelVersionId");
729                                                         }
730                                                         if (modelVer.isSet("modelVersion")) { 
731                                                                 modelVersion =  modelVer.get("modelVersion");
732                                                         }
733                                                 }
734                                         }
735                                 }
736                         }
737                         
738                         List<Map<String,Object>> startNodeFilterHash = new ArrayList<>();
739
740                         String resourceVersion = mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"), 
741                                         startNodeFilterHash, jaxbContext);      
742
743                         if (isDelete) {
744
745                                 List<ResultSet> resultSet = processor.queryByModel(transId, fromAppId,
746                                                 modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() );
747
748                                 Map<Object,String> objectToVertMap = new HashMap<>();
749                                 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
750
751                                 ResultSet rs = resultSet.get(0);
752
753                                 Vertex firstVert = rs.getVert();
754                                 String restURI = serializer.getURIForVertex(firstVert).toString();
755                                 String notificationVersion = AAIProperties.LATEST.toString();
756                                 if (restURI.startsWith("/")) {
757                                         restURI = "/aai/" + notificationVersion + restURI;
758                                 } else {
759                                         restURI = "/aai/" + notificationVersion + "/" + restURI;
760                                 }
761                                 
762                                 Map<String,String> delResult = processor.runDeleteByModel( transId, fromAppId,
763                                                 modelVersionId, topNodeType, startNodeFilterHash.get(0), aaiExtMap.getApiVersion(), resourceVersion );
764
765                                 String resultStr = "";
766                                 for (Map.Entry<String,String> ent : delResult.entrySet()) { 
767                                         resultStr += "v[" + ent.getKey() + "] " + ent.getValue() + ",\n";
768                                 }
769                                 resultStr.trim();
770
771                                 // Note - notifications are now done down in the individual "remove" calls done in runDeleteByModel() above.
772
773                                 response = Response.ok(resultStr).build();
774
775                         } else {
776                                 List<ResultSet> resultSet = processor.queryByModel( transId, fromAppId,
777                                                 modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() );
778
779                                 Introspector inventoryItems = loader.introspectorFromName("inventory-response-items");
780
781                                 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
782
783                                 inventoryItems.setValue("inventory-response-item", invItemList);
784
785                                 response = getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders());
786                         }
787                         success = true;
788                 } catch (AAIException e) {
789                         success = false;
790                         throw e;
791                 } catch (Exception e) {
792                         success = false;
793                         throw new AAIException("AAI_5105", e);
794                 } finally {
795                         if (dbEngine != null) {
796                                 if (success) {
797                                         dbEngine.commit();
798                                 } else {
799                                         dbEngine.rollback();
800                                 }
801                         }
802                 }
803
804                 return response;
805         }
806         
807         private Response getResponseFromIntrospector(Introspector obj, HttpHeaders headers) {
808                 boolean isJson = false;
809                 for (MediaType mt : headers.getAcceptableMediaTypes()) {
810                         if (MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
811                                 isJson = true;
812                                 break;
813                         }
814                 }
815                 org.onap.aai.introspection.MarshallerProperties properties;
816                 if (isJson) {
817                         properties = 
818                                         new org.onap.aai.introspection.MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE).build();
819                 } else {
820                         properties = 
821                                         new org.onap.aai.introspection.MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE).build();
822                 }
823                 
824                 String marshalledObj = obj.marshal(properties);
825                 return Response.ok().entity(marshalledObj).build();
826         }
827
828         /**
829          * Map instance filters.
830          *
831          * @param instanceFilters the instance filters
832          * @param startNodeFilterHash the start node filter hash
833          * @param jaxbContext the jaxb context
834          * @return the string
835          */
836         private String mapInstanceFilters(DynamicEntity instanceFilters, List<Map<String,Object>> startNodeFilterHash, DynamicJAXBContext jaxbContext) {                        
837
838                 if (instanceFilters == null || !instanceFilters.isSet("instanceFilter")) {
839                         return null;
840                 }
841                 @SuppressWarnings("unchecked")
842                 List<DynamicEntity> instanceFilter = (ArrayList<DynamicEntity>)instanceFilters.get("instanceFilter");
843                 String resourceVersion = null;
844
845                 for (DynamicEntity instFilt : instanceFilter) { 
846                         List<DynamicEntity> any = instFilt.get("any");
847                         HashMap<String,Object> thisNodeFilterHash = new HashMap<String,Object>();
848                         for (DynamicEntity anyEnt : any) { 
849                                 String clazz = anyEnt.getClass().getCanonicalName();
850                                 String simpleClazz = anyEnt.getClass().getSimpleName();
851
852                                 String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz);
853
854                                 DynamicType anyEntType = jaxbContext.getDynamicType(clazz);
855
856                                 for (String propName : anyEntType.getPropertiesNames()) {
857                                         // hyphencase the prop and throw it on the hash
858                                         if (anyEnt.isSet(propName)) {
859                                                 thisNodeFilterHash.put(nodeType + "." + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName), anyEnt.get(propName));
860                                                 if (propName.equals("resourceVersion") && resourceVersion == null) { 
861                                                         resourceVersion = (String)anyEnt.get(propName);
862                                                 }
863                                         }
864                                 }
865                         }
866                         startNodeFilterHash.add(thisNodeFilterHash);
867                 }
868                 return resourceVersion;
869         }
870
871         /**
872          * Map secondary filters.
873          *
874          * @param secondaryFilts the secondary filters
875          * @param secondaryFilterHash the secondary filter hash
876          * @param jaxbContext the jaxb context
877          * @return the string
878          */
879         private void mapSecondaryFilters(DynamicEntity secondaryFilts, Map<String,Object> secondaryFilterHash, DynamicJAXBContext jaxbContext) {                        
880
881                 if (secondaryFilts == null || !secondaryFilts.isSet("secondaryFilt")) {
882                         return;
883                 }
884                 @SuppressWarnings("unchecked")
885                 List<DynamicEntity> secondaryFilter = (ArrayList<DynamicEntity>)secondaryFilts.get("secondaryFilt");
886                 
887                 for (DynamicEntity secondaryFilt : secondaryFilter) { 
888                         List<DynamicEntity> any = secondaryFilt.get("any");
889                         
890                         for (DynamicEntity anyEnt : any) { 
891                                 String clazz = anyEnt.getClass().getCanonicalName();
892                                 String simpleClazz = anyEnt.getClass().getSimpleName();
893
894                                 String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz);
895
896                                 DynamicType anyEntType = jaxbContext.getDynamicType(clazz);
897
898                                 for (String propName : anyEntType.getPropertiesNames()) {
899                                         // hyphencase the prop and throw it on the hash
900                                         if (anyEnt.isSet(propName)) {
901                                                 secondaryFilterHash.put(nodeType + "." + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName), anyEnt.get(propName));
902                                         }
903                                 }
904                         }
905                 }
906         }
907         
908         /**
909          * Remap inventory items.
910          *
911          * @param invResultItem the inv result item
912          * @param jaxbContext the jaxb context
913          * @param includeTheseVertices the include these vertices
914          * @param objectToVertMap the object to vert map
915          * @param aaiExtMap the aai ext map
916          * @return the dynamic entity
917          */
918         private DynamicEntity remapInventoryItems(DynamicEntity invResultItem, DynamicJAXBContext jaxbContext, 
919                         Map<String,String> includeTheseVertices, Map<Object,String> objectToVertMap, AAIExtensionMap aaiExtMap) { 
920
921
922                 DynamicEntity inventoryItem = jaxbContext.newDynamicEntity("inventory.aai.onap.org." + aaiExtMap.getApiVersion() + ".InventoryResponseItem");
923                 Object item = invResultItem.get("item");
924                 inventoryItem.set("modelName",                  invResultItem.get("modelName"));
925                 inventoryItem.set("item",                               item);
926                 inventoryItem.set("extraProperties",    invResultItem.get("extraProperties"));
927
928                 String vertexId = "";
929
930                 if (objectToVertMap.containsKey(item)) {
931                         vertexId = objectToVertMap.get(item);
932                 }
933
934                 if (includeTheseVertices.containsKey(vertexId)) { 
935                         if (invResultItem.isSet("inventoryResponseItems")) {
936                                 List<DynamicEntity> invItemList = new ArrayList<DynamicEntity>();
937                                 DynamicEntity inventoryItems = jaxbContext.newDynamicEntity("inventory.aai.att.com." + aaiExtMap.getApiVersion() + ".InventoryResponseItems");
938                                 DynamicEntity subInventoryResponseItems = invResultItem.get("inventoryResponseItems");
939                                 List<DynamicEntity> subInventoryResponseItemList = subInventoryResponseItems.get("inventoryResponseItem");
940                                 for (DynamicEntity ent : subInventoryResponseItemList) { 
941                                         DynamicEntity invItem = remapInventoryItems(ent, jaxbContext, includeTheseVertices, objectToVertMap, aaiExtMap);
942                                         if (invItem != null) { 
943                                                 invItemList.add(invItem);
944                                         }
945                                 }
946                                 inventoryItems.set("inventoryResponseItem", invItemList);
947                                 inventoryItem.set("inventoryResponseItems",  inventoryItems);
948                         }
949                 }
950                 return inventoryItem;
951         }
952
953         /**
954          * Unpack result set.
955          *
956          * @param g the g
957          * @param resultSetList the result set list
958          * @param jaxbContext the jaxb context
959          * @param aaiResources the aai resources
960          * @param objectToVertMap the object to vert map
961          * @param aaiExtMap the aai ext map
962          * @return the array list
963          * @throws AAIException the AAI exception
964          */
965         // this should return an inventoryItem
966         private List<Object> unpackResultSet(List<ResultSet> resultSetList,
967                         TransactionalGraphEngine engine,
968                         Loader loader,
969                         DBSerializer serializer) throws AAIException {
970
971                 List<Object> resultList = new ArrayList<>();
972
973                 for (ResultSet resultSet : resultSetList) { 
974                         
975                         if( resultSet.getVert() == null ){
976                                 continue;
977                         }
978
979                         Introspector inventoryItem = loader.introspectorFromName("inventory-response-item");
980                         Introspector inventoryItems = loader.introspectorFromName("inventory-response-items");
981                         // add this inventoryItem to the resultList for this level
982                         resultList.add(inventoryItem.getUnderlyingObject());
983
984                         Vertex vert = resultSet.getVert();
985
986                         String aaiNodeType = vert.<String>property("aai-node-type").orElse(null);
987
988                         if (aaiNodeType != null) {
989                                 Introspector thisObj = loader.introspectorFromName(aaiNodeType);
990
991                                 if (resultSet.getExtraPropertyHash() != null) { 
992                                         Map<String,Object> extraProperties = resultSet.getExtraPropertyHash();  
993
994                                         Introspector extraPropertiesEntity = loader.introspectorFromName("extra-properties");
995
996                                         List<Object> extraPropsList = extraPropertiesEntity.getValue("extra-property");
997
998                                         for (Map.Entry<String,Object> ent : extraProperties.entrySet()) {
999                                                 String propName = ent.getKey();
1000                                                 Object propVal = ent.getValue();
1001
1002                                                 Introspector extraPropEntity = loader.introspectorFromName("extra-property");
1003
1004                                                 extraPropEntity.setValue("property-name",  propName);
1005                                                 extraPropEntity.setValue("property-value", propVal);
1006
1007                                                 extraPropsList.add(extraPropEntity.getUnderlyingObject());
1008
1009                                         }
1010                                         inventoryItem.setValue("extra-properties", extraPropertiesEntity.getUnderlyingObject());
1011                                 }
1012                                 
1013                                 try {
1014                                         serializer.dbToObject(Collections.singletonList(vert), thisObj, 0, true, "false");
1015                                 } catch (UnsupportedEncodingException  e1) {
1016                                         throw new AAIException("AAI_5105");
1017                                 }
1018                                 PropertyLimitDesc propertyLimitDesc = resultSet.getPropertyLimitDesc();
1019
1020                                 if (propertyLimitDesc != null) {
1021
1022                                         if (PropertyLimitDesc.SHOW_NONE.equals(propertyLimitDesc)) {
1023                                                 HashMap<String,Object> emptyPropertyOverRideHash = new HashMap<String,Object>();
1024                                                 for (String key : thisObj.getAllKeys()) {
1025                                                         emptyPropertyOverRideHash.put(key, null);
1026                                                 }
1027                                                 filterProperties(thisObj, emptyPropertyOverRideHash);
1028                                         } else if (PropertyLimitDesc.SHOW_ALL.equals(propertyLimitDesc)) { 
1029                                                 //keep everything
1030                                         } else if (PropertyLimitDesc.SHOW_NAME_AND_KEYS_ONLY.equals(propertyLimitDesc)) {
1031                                                 HashMap<String,Object> keysAndNamesPropHash = new HashMap<String,Object>();
1032                                                 
1033                                                 for (String key : thisObj.getAllKeys()) {
1034                                                         keysAndNamesPropHash.put(key, null);
1035                                                 }
1036                                                 String namePropMetaData = thisObj.getMetadata(ObjectMetadata.NAME_PROPS);
1037                                                 if (namePropMetaData != null) {
1038                                                         String[] nameProps = namePropMetaData.split(",");
1039                                                         for (String names : nameProps) {
1040                                                                 keysAndNamesPropHash.put(names, null);
1041                                                         }
1042                                                 }
1043                                                 filterProperties(thisObj, keysAndNamesPropHash);
1044                                         }
1045                                 } else { 
1046                                         if (resultSet.getPropertyOverRideHash() != null && resultSet.getPropertyOverRideHash().size() > 0) { 
1047                                                 Map<String,Object> propertyOverRideHash = resultSet.getPropertyOverRideHash();
1048                                                 if (propertyOverRideHash.containsKey("persona-model-id")) {
1049                                                         propertyOverRideHash.remove("persona-model-id");
1050                                                         propertyOverRideHash.put("model-invariant-id", null);
1051                                                 }
1052                                                 for (String key : thisObj.getAllKeys()) {
1053                                                         propertyOverRideHash.put(key, null);
1054                                                 }
1055                                                 filterProperties(thisObj, propertyOverRideHash);
1056                                         } else {
1057                                                 //keep everything
1058                                         }
1059                                 }
1060
1061                                 if (thisObj != null) { 
1062                                         inventoryItem.setValue("item", thisObj.getUnderlyingObject());
1063
1064                                         String modelName = null;
1065                                         try { 
1066                                                 // Try to get the modelName if we can.  Otherwise, do not fail, just return what we have already.
1067                                                 String modelInvariantIdLocal = (String)vert.<String>property("model-invariant-id-local").orElse(null); // this one points at a model
1068                                                 String modelVersionIdLocal = (String)vert.<String>property("model-version-id-local").orElse(null); // this one points at a model-ver
1069                                                 
1070                                                 if ( (modelInvariantIdLocal != null && modelVersionIdLocal != null) 
1071                                                                 && (modelInvariantIdLocal.length() > 0 && modelVersionIdLocal.length() > 0) ) {
1072                                                         Introspector modelVer = loader.introspectorFromName("model-ver");
1073                                                         modelVer.setValue("model-version-id", modelVersionIdLocal);
1074                                                         QueryBuilder builder = engine.getQueryBuilder().createDBQuery(modelVer);
1075                                                         List<Vertex> modelVerVerts = builder.toList();
1076                                                         if( (modelVerVerts != null) && (modelVerVerts.size() == 1) ) {
1077                                                                 Vertex modelVerVert = modelVerVerts.get(0);
1078                                                                 modelName = modelVerVert.<String>property("model-name").orElse(null); 
1079                                                                 if (modelName != null && modelName.length() > 0) { 
1080                                                                         inventoryItem.setValue("model-name", modelName);
1081                                                                 }
1082                                                         }
1083                                                 }
1084                                         } catch (DynamicException e) { 
1085                                                 ; // it's ok, dynamic object might not have these fields
1086                                         } catch (Exception e) { 
1087                                                 ; // it's ok, couldn't find a matching model
1088                                         }
1089                                         
1090                                         if (resultSet.getSubResultSet() != null) { 
1091                                                 List<ResultSet> subResultSet = resultSet.getSubResultSet();
1092                                                 if (subResultSet != null && subResultSet.size() > 0 ) { 
1093                                                         List<Object> res = unpackResultSet(subResultSet, engine, loader, serializer);
1094                                                         if (res.size() > 0) { 
1095                                                                 inventoryItems.setValue("inventory-response-item", res);
1096                                                                 inventoryItem.setValue("inventory-response-items", inventoryItems.getUnderlyingObject());
1097                                                         }
1098                                                 }
1099                                         }
1100                                 }
1101                         }
1102                 }
1103
1104                 return resultList;
1105         }
1106         
1107         private void filterProperties(Introspector thisObj, Map<String, Object> override) {
1108                 
1109                 thisObj.getProperties().stream().filter(x -> {
1110                         return !override.containsKey(x);
1111                 }).forEach(prop -> {
1112                         if (thisObj.isSimpleType(prop)) {
1113                                 thisObj.setValue(prop, null);
1114                         }
1115                 });
1116
1117         }
1118
1119         /**
1120          * Gets the media type.
1121          *
1122          * @param mediaTypeList the media type list
1123          * @return the media type
1124          */
1125         protected String getMediaType(List <MediaType> mediaTypeList) {
1126                 String mediaType = MediaType.APPLICATION_JSON;  // json is the default
1127                 for (MediaType mt : mediaTypeList) {
1128                         if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
1129                                 mediaType = MediaType.APPLICATION_XML;
1130                         }
1131                 }
1132                 return mediaType;
1133         }
1134 }