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=========================================================
20 package org.onap.aai.dbgraphmap;
22 import java.io.UnsupportedEncodingException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
30 import java.util.Optional;
31 import java.util.stream.Stream;
33 import javax.ws.rs.core.HttpHeaders;
34 import javax.ws.rs.core.MediaType;
35 import javax.ws.rs.core.Response;
36 import javax.ws.rs.core.UriBuilderException;
37 import javax.xml.bind.JAXBException;
39 import org.apache.tinkerpop.gremlin.process.traversal.P;
40 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
41 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
42 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
43 import org.apache.tinkerpop.gremlin.structure.Vertex;
44 import org.eclipse.persistence.dynamic.DynamicEntity;
45 import org.eclipse.persistence.dynamic.DynamicType;
46 import org.eclipse.persistence.exceptions.DynamicException;
47 import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
48 import org.onap.aai.db.DbMethHelper;
49 import org.onap.aai.db.props.AAIProperties;
50 import org.onap.aai.dbgen.PropertyLimitDesc;
51 import org.onap.aai.dbgraphgen.ModelBasedProcessing;
52 import org.onap.aai.dbgraphgen.ResultSet;
53 import org.onap.aai.dbmap.DBConnectionType;
54 import org.onap.aai.exceptions.AAIException;
55 import org.onap.aai.extensions.AAIExtensionMap;
56 import org.onap.aai.introspection.Introspector;
57 import org.onap.aai.introspection.Loader;
58 import org.onap.aai.introspection.LoaderFactory;
59 import org.onap.aai.introspection.ModelType;
60 import org.onap.aai.introspection.MoxyLoader;
61 import org.onap.aai.introspection.exceptions.AAIUnknownObjectException;
62 import org.onap.aai.parsers.relationship.RelationshipToURI;
63 import org.onap.aai.query.builder.QueryBuilder;
64 import org.onap.aai.schema.enums.ObjectMetadata;
65 import org.onap.aai.schema.enums.PropertyMetadata;
66 import org.onap.aai.serialization.db.DBSerializer;
67 import org.onap.aai.serialization.db.EdgeRule;
68 import org.onap.aai.serialization.db.EdgeRules;
69 import org.onap.aai.serialization.engines.QueryStyle;
70 import org.onap.aai.serialization.engines.TitanDBEngine;
71 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
72 import org.onap.aai.serialization.queryformats.exceptions.AAIFormatVertexException;
73 import org.onap.aai.serialization.queryformats.utils.UrlBuilder;
74 import org.onap.aai.util.StoreNotificationEvent;
76 import com.att.eelf.configuration.EELFLogger;
77 import com.att.eelf.configuration.EELFManager;
78 import com.google.common.base.CaseFormat;
80 import edu.emory.mathcs.backport.java.util.Collections;
83 * Database Mapping class which acts as the middle man between the REST interface objects
84 * for the Search namespace
87 public class SearchGraph {
89 private final String COMPONENT = "aaidbmap";
90 private AAIExtensionMap aaiExtMap;
91 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(SearchGraph.class);
93 * Get the search result based on the includeNodeType and depth provided.
95 * @param fromAppId the from app id
96 * @param transId the trans id
97 * @param startNodeType the start node type
98 * @param startNodeKeyParams the start node key params
99 * @param includeNodeTypes the include node types
100 * @param depth the depth
101 * @param aaiExtMap the aai ext map
103 * @throws AAIException the AAI exception
105 public Response runGenericQuery (
107 String startNodeType,
108 List <String> startNodeKeyParams,
109 List <String> includeNodeTypes,
111 TransactionalGraphEngine dbEngine,
113 UrlBuilder urlBuilder) throws AAIException {
114 Response response = null;
115 boolean success = true;
118 dbEngine.startTransaction();
120 if( startNodeType == null ){
121 throw new AAIException("AAI_6120", "null start-node-type passed to the generic query");
124 if( startNodeKeyParams == null ){
125 throw new AAIException("AAI_6120", "no key param passed to the generic query");
128 if( includeNodeTypes == null ){
129 throw new AAIException("AAI_6120", "no include params passed to the generic query");
133 throw new AAIException("AAI_6120", "The maximum depth supported by the generic query is 6");
135 final QueryBuilder queryBuilder;
137 // there is an issue with service-instance - it is a unique node but still dependent
138 // for now query it directly without attempting to craft a valid URI
139 if (startNodeType.equalsIgnoreCase("service-instance") && startNodeKeyParams.size() == 1) {
140 Introspector obj = loader.introspectorFromName(startNodeType);
141 // Build a hash with keys to uniquely identify the start Node
142 String keyName = null;
143 String keyValue = null;
145 QueryBuilder builder = dbEngine.getQueryBuilder().getVerticesByIndexedProperty(AAIProperties.NODE_TYPE, "service-instance");
146 for( String keyData : startNodeKeyParams ){
147 int colonIndex = keyData.indexOf(":");
148 if( colonIndex <= 0 ){
149 throw new AAIException("AAI_6120", "Bad key param passed in: [" + keyData + "]");
152 keyName = keyData.substring(0, colonIndex).split("\\.")[1];
153 keyValue = keyData.substring(colonIndex + 1);
154 builder.getVerticesByProperty(keyName, keyValue);
158 queryBuilder = builder;
160 URI uri = craftUriFromQueryParams(loader, startNodeType, startNodeKeyParams);
161 queryBuilder = dbEngine.getQueryBuilder().createQueryFromURI(uri).getQueryBuilder();
163 List<Vertex> results = queryBuilder.toList();
164 if( results.isEmpty()){
165 throw new AAIException("AAI_6114", "No Node of type " +
167 " found for properties: " +
168 startNodeKeyParams.toString());
169 } else if (results.size() > 1) {
170 String detail = "More than one Node found by getUniqueNode for params: " + startNodeKeyParams.toString() + "\n";
171 throw new AAIException("AAI_6112", detail);
174 Vertex startNode = results.get(0);
176 Collection <Vertex> ver = new HashSet <>();
177 List<Vertex> queryResults = new ArrayList<>();
178 GraphTraversalSource traversalSource = dbEngine.asAdmin().getReadOnlyTraversalSource();
179 GraphTraversal<Vertex, Vertex> traversal;
180 if (includeNodeTypes.contains(startNodeType) || depth == 0 || includeNodeTypes.contains("all") )
183 // Now look for a node of includeNodeType within a given depth
184 traversal = traversalSource.withSideEffect("x", ver).V(startNode)
185 .times(depth).repeat(__.both().store("x")).cap("x").unfold();
187 if (!includeNodeTypes.contains("all")) {
188 traversal.where(__.has(AAIProperties.NODE_TYPE, P.within(includeNodeTypes)));
190 queryResults = traversal.toList();
193 if( queryResults.isEmpty()){
194 LOGGER.warn("No nodes found - apipe was null/empty");
198 Introspector searchResults = createSearchResults(loader, urlBuilder, queryResults);
200 String outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
201 org.onap.aai.introspection.MarshallerProperties properties = new org.onap.aai.introspection.MarshallerProperties.Builder(
202 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
204 result = searchResults.marshal(properties);
205 response = Response.ok().entity(result).build();
207 LOGGER.debug(ver.size() + " node(s) traversed, " + queryResults.size() + " found");
210 } catch (AAIException e) {
213 } catch (Exception e) {
215 throw new AAIException("AAI_5105", e);
217 if (dbEngine != null) {
230 private URI craftUriFromQueryParams(Loader loader, String startNodeType, List<String> startNodeKeyParams) throws UnsupportedEncodingException, IllegalArgumentException, UriBuilderException, AAIException {
231 Introspector relationship = loader.introspectorFromName("relationship");
233 relationship.setValue("related-to", startNodeType);
234 List<Object> relationshipDataList = relationship.getValue("relationship-data");
236 for( String keyData : startNodeKeyParams ){
237 int colonIndex = keyData.indexOf(":");
238 if( colonIndex <= 0 ){
239 throw new AAIException("AAI_6120", "Bad key param passed in: [" + keyData + "]");
242 Introspector data = loader.introspectorFromName("relationship-data");
243 data.setValue("relationship-key", keyData.substring(0, colonIndex));
244 data.setValue("relationship-value", keyData.substring(colonIndex + 1));
245 relationshipDataList.add(data.getUnderlyingObject());
249 RelationshipToURI parser = new RelationshipToURI(loader, relationship);
251 return parser.getUri();
257 * @param fromAppId the from app id
258 * @param transId the trans id
259 * @param targetNodeType the target node type
260 * @param edgeFilterParams the edge filter params
261 * @param filterParams the filter params
262 * @param aaiExtMap the aai ext map
264 * @throws AAIException the AAI exception
266 public Response runNodesQuery (
268 String targetNodeType,
269 List <String> edgeFilterParams,
270 List <String> filterParams,
271 TransactionalGraphEngine dbEngine,
273 UrlBuilder urlBuilder) throws AAIException {
275 Response response = null;
276 boolean success = true;
278 final String EQUALS = "EQUALS";
279 final String DOES_NOT_EQUAL = "DOES-NOT-EQUAL";
280 final String EXISTS = "EXISTS";
281 final String DOES_NOT_EXIST = "DOES-NOT-EXIST";
284 dbEngine.startTransaction();
288 if( targetNodeType == null || targetNodeType == "" ){
289 throw new AAIException("AAI_6120", "null or empty target-node-type passed to the node query");
293 target = loader.introspectorFromName(targetNodeType);
294 } catch (AAIUnknownObjectException e) {
295 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + targetNodeType + "] passed to node query.");
298 if( filterParams.isEmpty() && edgeFilterParams.isEmpty()){
299 // For now, it's ok to pass no filter params. We'll just return ALL the nodes of the requested type.
300 LOGGER.warn("No filters passed to the node query");
303 StringBuilder queryStringForMsg = new StringBuilder();
304 GraphTraversal<Vertex, Vertex> traversal = dbEngine.asAdmin().getReadOnlyTraversalSource().V().has(AAIProperties.NODE_TYPE, targetNodeType);
305 queryStringForMsg.append("has(\"aai-node-type\"," + targetNodeType + ")");
307 for( String filter : filterParams ) {
308 String [] pieces = filter.split(":");
309 if( pieces.length < 2 ){
310 throw new AAIException("AAI_6120", "bad filter passed to node query: [" + filter + "]");
313 String propName = this.findDbPropName(target, pieces[0]);
314 String filterType = pieces[1];
315 if( filterType.equals(EQUALS)){
316 if( pieces.length < 3 ){
317 throw new AAIException("AAI_6120", "No value passed for filter: [" + filter + "]");
320 if( pieces.length == 3 ){
323 else if( pieces.length > 3 ){
324 // When a ipv6 address comes in as a value, it has colons in it which require us to
325 // pull the "value" off the end of the filter differently
326 int startPos4Value = propName.length() + filterType.length() + 3;
327 value = filter.substring(startPos4Value);
329 queryStringForMsg.append(".has(" + propName + "," + value + ")");
330 traversal.has(propName,value);
332 else if( filterType.equals(DOES_NOT_EQUAL)){
333 if( pieces.length < 3 ){
334 throw new AAIException("AAI_6120", "No value passed for filter: [" + filter + "]");
337 if( pieces.length == 3 ){
340 else if( pieces.length > 3 ){
341 // When a ipv6 address comes in as a value, it has colons in it which require us to
342 // pull the "value" off the end of the filter differently
343 int startPos4Value = propName.length() + filterType.length() + 3;
344 value = filter.substring(startPos4Value);
346 queryStringForMsg.append(".hasNot(" + propName + "," + value + ")");
347 traversal.not(__.has(propName,value));
349 else if( filterType.equals(EXISTS)){
350 queryStringForMsg.append(".has(" + propName + ")");
351 traversal.has(propName);
353 else if( filterType.equals(DOES_NOT_EXIST)){
354 queryStringForMsg.append(".hasNot(" + propName + ")");
355 traversal.hasNot(propName);
358 throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]");
363 if (!edgeFilterParams.isEmpty()) {
364 // edge-filter=pserver:EXISTS: OR pserver:EXISTS:hostname:XXX
365 // edge-filter=pserver:DOES-NOT-EXIST: OR pserver:DOES-NOT-EXIST:hostname:XXX
366 String filter = edgeFilterParams.get(0); // we process and allow only one edge filter for now
367 String [] pieces = filter.split(":");
368 if( pieces.length < 2 || pieces.length == 3 || pieces.length > 4){
369 throw new AAIException("AAI_6120", "bad edge-filter passed: [" + filter + "]");
371 String nodeType = pieces[0].toLowerCase();
372 String filterType = pieces[1].toUpperCase();
373 Introspector otherNode;
374 if (!filterType.equals(EXISTS) && !filterType.equals(DOES_NOT_EXIST)) {
375 throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]");
378 otherNode = loader.introspectorFromName(nodeType);
379 } catch (AAIUnknownObjectException e) {
380 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + nodeType + "] passed to node query.");
382 String propName = null;
383 String propValue = null;
384 if ( pieces.length >= 3) {
385 propName = this.findDbPropName(otherNode, pieces[2].toLowerCase());
386 propValue = pieces[3];
388 String[] edgeLabels = getEdgeLabel(targetNodeType, nodeType);
390 GraphTraversal<Vertex, Vertex> edgeSearch = __.start();
392 edgeSearch.both(edgeLabels).has(AAIProperties.NODE_TYPE, nodeType);
393 if (propName != null) {
394 // check for matching property
395 if (propValue != null) {
396 edgeSearch.has(propName, propValue);
398 edgeSearch.has(propName);
402 if( filterType.equals(DOES_NOT_EXIST)){
403 traversal.where(__.not(edgeSearch));
404 } else if (filterType.equals(EXISTS)) {
405 traversal.where(edgeSearch);
410 List<Vertex> results = traversal.toList();
411 Introspector searchResults = createSearchResults(loader, urlBuilder, results);
413 String outputMediaType = getMediaType(headers.getAcceptableMediaTypes());
414 org.onap.aai.introspection.MarshallerProperties properties = new org.onap.aai.introspection.MarshallerProperties.Builder(
415 org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build();
417 result = searchResults.marshal(properties);
418 response = Response.ok().entity(result).build();
421 } catch (AAIException e) {
424 } catch (Exception e) {
426 throw new AAIException("AAI_5105", e);
428 if (dbEngine != null) {
440 protected Introspector createSearchResults(Loader loader, UrlBuilder urlBuilder, List<Vertex> results)
441 throws AAIUnknownObjectException {
442 Introspector searchResults = loader.introspectorFromName("search-results");
443 List<Object> resultDataList = searchResults.getValue("result-data");
444 Stream<Vertex> stream;
445 if (results.size() >= 50) {
446 stream = results.parallelStream();
448 stream = results.stream();
450 boolean isParallel = stream.isParallel();
451 stream.forEach(v -> {
452 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
456 thisNodeURL = urlBuilder.pathed(v);
457 Introspector resultData = loader.introspectorFromName("result-data");
459 resultData.setValue("resource-type", nodeType);
460 resultData.setValue("resource-link", thisNodeURL);
462 synchronized (resultDataList) {
463 resultDataList.add(resultData.getUnderlyingObject());
466 resultDataList.add(resultData.getUnderlyingObject());
468 } catch (AAIException | AAIFormatVertexException e) {
469 throw new RuntimeException(e);
473 return searchResults;
476 private String findDbPropName(Introspector obj, String propName) {
478 Optional<String> result = obj.getPropertyMetadata(propName, PropertyMetadata.DB_ALIAS);
479 if (result.isPresent()) {
488 * Gets the edge label.
490 * @param targetNodeType the target node type
491 * @param nodeType the node type
492 * @return the edge label
493 * @throws AAIException the AAI exception
495 public static String[] getEdgeLabel(String targetNodeType, String nodeType) throws AAIException{
496 Map<String, EdgeRule> rules = EdgeRules.getInstance().getEdgeRules(targetNodeType, nodeType);
497 String[] results = rules.keySet().toArray(new String[0]);
505 * @param fromAppId the from app id
506 * @param transId the trans id
507 * @param queryParameters the query parameters
508 * @param aaiExtMap the aai ext map
509 * @return the response
510 * @throws JAXBException the JAXB exception
511 * @throws AAIException the AAI exception
513 public Response runNamedQuery(String fromAppId, String transId, String queryParameters,
514 DBConnectionType connectionType,
515 AAIExtensionMap aaiExtMap) throws JAXBException, AAIException {
517 Introspector inventoryItems;
518 boolean success = true;
519 TransactionalGraphEngine dbEngine = null;
522 MoxyLoader loader = (MoxyLoader)LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
523 DynamicJAXBContext jaxbContext = loader.getJAXBContext();
524 dbEngine = new TitanDBEngine(
525 QueryStyle.TRAVERSAL,
528 DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId);
529 ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer);
531 dbEngine.startTransaction();
532 org.onap.aai.restcore.MediaType mediaType = org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE;
533 String contentType = aaiExtMap.getHttpServletRequest().getContentType();
534 if (contentType != null && contentType.contains("application/xml")) {
535 mediaType = org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE;
538 if (queryParameters.length() == 0) {
539 queryParameters = "{}";
542 DynamicEntity modelAndNamedQuerySearch = (DynamicEntity)loader.unmarshal("ModelAndNamedQuerySearch", queryParameters, mediaType).getUnderlyingObject();
543 if (modelAndNamedQuerySearch == null) {
544 throw new AAIException("AAI_5105");
546 HashMap<String,Object> namedQueryLookupHash = new HashMap<String,Object>();
548 DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters");
549 String namedQueryUuid = null;
550 if ((qp != null) && qp.isSet("namedQuery")) {
551 DynamicEntity namedQuery = (DynamicEntity) qp.get("namedQuery");
553 if (namedQuery.isSet("namedQueryUuid")) {
554 namedQueryUuid = namedQuery.get("namedQueryUuid");
556 if (namedQuery.isSet("namedQueryName")) {
557 namedQueryLookupHash.put("named-query-name", namedQuery.get("namedQueryName"));
559 if (namedQuery.isSet("namedQueryVersion")) {
560 namedQueryLookupHash.put("named-query-version", namedQuery.get("namedQueryVersion"));
564 if (namedQueryUuid == null) {
566 DbMethHelper dbMethHelper = new DbMethHelper(loader, dbEngine);
567 List<Vertex> namedQueryVertices = dbMethHelper.locateUniqueVertices("named-query", namedQueryLookupHash);
568 for (Vertex vert : namedQueryVertices) {
569 namedQueryUuid = vert.<String>property("named-query-uuid").orElse(null);
570 // there should only be one, we'll pick the first if not
575 String secondaryFilterCutPoint = null;
577 if (modelAndNamedQuerySearch.isSet("secondaryFilterCutPoint")) {
578 secondaryFilterCutPoint = modelAndNamedQuerySearch.get("secondaryFilterCutPoint");
581 List<Map<String,Object>> startNodeFilterHash = new ArrayList<>();
583 mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"),
584 startNodeFilterHash, jaxbContext);
586 Map<String,Object> secondaryFilterHash = new HashMap<>();
588 mapSecondaryFilters((DynamicEntity)modelAndNamedQuerySearch.get("secondaryFilts"),
589 secondaryFilterHash, jaxbContext);
591 List<ResultSet> resultSet = processor.queryByNamedQuery(transId, fromAppId,
592 namedQueryUuid, startNodeFilterHash, aaiExtMap.getApiVersion(), secondaryFilterCutPoint, secondaryFilterHash);
594 inventoryItems = loader.introspectorFromName("inventory-response-items");
596 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
598 inventoryItems.setValue("inventory-response-item", invItemList);
600 } catch (AAIException e) {
603 } catch (Exception e) {
605 throw new AAIException("AAI_5105", e);
607 if (dbEngine != null) {
616 return getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders());
620 * Execute model operation.
622 * @param fromAppId the from app id
623 * @param transId the trans id
624 * @param queryParameters the query parameters
625 * @param isDelete the is delete
626 * @param aaiExtMap the aai ext map
627 * @return the response
628 * @throws JAXBException the JAXB exception
629 * @throws AAIException the AAI exception
630 * @throws DynamicException the dynamic exception
631 * @throws UnsupportedEncodingException the unsupported encoding exception
633 public Response executeModelOperation(String fromAppId, String transId, String queryParameters,
634 DBConnectionType connectionType,
636 AAIExtensionMap aaiExtMap) throws JAXBException, AAIException, DynamicException, UnsupportedEncodingException {
638 boolean success = true;
639 TransactionalGraphEngine dbEngine = null;
642 MoxyLoader loader = (MoxyLoader) LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
643 DynamicJAXBContext jaxbContext = loader.getJAXBContext();
644 dbEngine = new TitanDBEngine(
645 QueryStyle.TRAVERSAL,
648 DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId);
649 ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer);
650 dbEngine.startTransaction();
653 org.onap.aai.restcore.MediaType mediaType = org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE;
654 String contentType = aaiExtMap.getHttpServletRequest().getContentType();
655 if (contentType != null && contentType.contains("application/xml")) {
656 mediaType = org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE;
659 if (queryParameters.length() == 0) {
660 queryParameters = "{}";
663 DynamicEntity modelAndNamedQuerySearch = (DynamicEntity)loader.unmarshal("ModelAndNamedQuerySearch", queryParameters, mediaType).getUnderlyingObject();
664 if (modelAndNamedQuerySearch == null) {
665 throw new AAIException("AAI_5105");
668 Map<String,Object> modelQueryLookupHash = new HashMap<>();
670 String modelVersionId = null;
671 String modelName = null;
672 String modelInvariantId = null;
673 String modelVersion = null;
674 String topNodeType = null;
676 if (modelAndNamedQuerySearch.isSet("topNodeType")) {
677 topNodeType = modelAndNamedQuerySearch.get("topNodeType");
680 // the ways to get a model:
682 // 1. model-version-id (previously model-name-version-id
683 // 2. model-invariant-id (previously model-id) + model-version
684 // 3. model-name + model-version
686 // we will support both using the OverloadedModel object in the v9 oxm. This allows us to unmarshal
687 // either an old-style model or new-style model + model-ver object
688 if (modelAndNamedQuerySearch.isSet("queryParameters")) {
689 DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters");
691 if (qp.isSet("model")) {
692 DynamicEntity model = (DynamicEntity) qp.get("model");
694 // on an old-style model object, the following 4 attrs were all present
695 if (model.isSet("modelNameVersionId")) {
696 modelVersionId = model.get("modelNameVersionId");
698 if (model.isSet("modelId")) {
699 modelInvariantId = model.get("modelId");
701 if (model.isSet("modelName")) {
702 modelName = model.get("modelName");
704 if (model.isSet("modelVersion")) {
705 modelVersion = model.get("modelVersion");
708 // new style splits model-invariant-id from the other 3 attrs. This is
709 // the only way to directly look up the model object
710 if (model.isSet("modelInvariantId")) {
711 modelInvariantId = model.get("modelInvariantId");
714 if (model.isSet("modelVers")) {
715 // we know that this is new style, because modelVers was not an option
717 DynamicEntity modelVers = (DynamicEntity) model.get("modelVers");
718 if (modelVers.isSet("modelVer")) {
719 List<DynamicEntity> modelVerList = modelVers.get("modelVer");
720 // if they send more than one, too bad, they get the first one
721 DynamicEntity modelVer = modelVerList.get(0);
722 if (modelVer.isSet("modelName")) {
723 modelName = modelVer.get("modelName");
725 if (modelVer.isSet("modelVersionId")) {
726 modelVersionId = modelVer.get("modelVersionId");
728 if (modelVer.isSet("modelVersion")) {
729 modelVersion = modelVer.get("modelVersion");
736 List<Map<String,Object>> startNodeFilterHash = new ArrayList<>();
738 String resourceVersion = mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"),
739 startNodeFilterHash, jaxbContext);
743 List<ResultSet> resultSet = processor.queryByModel(transId, fromAppId,
744 modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() );
746 Map<Object,String> objectToVertMap = new HashMap<>();
747 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
749 ResultSet rs = resultSet.get(0);
751 Vertex firstVert = rs.getVert();
752 String restURI = serializer.getURIForVertex(firstVert).toString();
753 String notificationVersion = AAIProperties.LATEST.toString();
754 if (restURI.startsWith("/")) {
755 restURI = "/aai/" + notificationVersion + restURI;
757 restURI = "/aai/" + notificationVersion + "/" + restURI;
760 Map<String,String> delResult = processor.runDeleteByModel( transId, fromAppId,
761 modelVersionId, topNodeType, startNodeFilterHash.get(0), aaiExtMap.getApiVersion(), resourceVersion );
763 String resultStr = "";
764 for (Map.Entry<String,String> ent : delResult.entrySet()) {
765 resultStr += "v[" + ent.getKey() + "] " + ent.getValue() + ",\n";
769 // Note - notifications are now done down in the individual "remove" calls done in runDeleteByModel() above.
771 response = Response.ok(resultStr).build();
774 List<ResultSet> resultSet = processor.queryByModel( transId, fromAppId,
775 modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() );
777 Introspector inventoryItems = loader.introspectorFromName("inventory-response-items");
779 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
781 inventoryItems.setValue("inventory-response-item", invItemList);
783 response = getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders());
786 } catch (AAIException e) {
789 } catch (Exception e) {
791 throw new AAIException("AAI_5105", e);
793 if (dbEngine != null) {
805 private Response getResponseFromIntrospector(Introspector obj, HttpHeaders headers) {
806 boolean isJson = false;
807 for (MediaType mt : headers.getAcceptableMediaTypes()) {
808 if (MediaType.APPLICATION_JSON_TYPE.isCompatible(mt)) {
813 org.onap.aai.introspection.MarshallerProperties properties;
816 new org.onap.aai.introspection.MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE).build();
819 new org.onap.aai.introspection.MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE).build();
822 String marshalledObj = obj.marshal(properties);
823 return Response.ok().entity(marshalledObj).build();
827 * Map instance filters.
829 * @param instanceFilters the instance filters
830 * @param startNodeFilterHash the start node filter hash
831 * @param jaxbContext the jaxb context
834 private String mapInstanceFilters(DynamicEntity instanceFilters, List<Map<String,Object>> startNodeFilterHash, DynamicJAXBContext jaxbContext) {
836 if (instanceFilters == null || !instanceFilters.isSet("instanceFilter")) {
839 @SuppressWarnings("unchecked")
840 List<DynamicEntity> instanceFilter = (ArrayList<DynamicEntity>)instanceFilters.get("instanceFilter");
841 String resourceVersion = null;
843 for (DynamicEntity instFilt : instanceFilter) {
844 List<DynamicEntity> any = instFilt.get("any");
845 HashMap<String,Object> thisNodeFilterHash = new HashMap<String,Object>();
846 for (DynamicEntity anyEnt : any) {
847 String clazz = anyEnt.getClass().getCanonicalName();
848 String simpleClazz = anyEnt.getClass().getSimpleName();
850 String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz);
852 DynamicType anyEntType = jaxbContext.getDynamicType(clazz);
854 for (String propName : anyEntType.getPropertiesNames()) {
855 // hyphencase the prop and throw it on the hash
856 if (anyEnt.isSet(propName)) {
857 thisNodeFilterHash.put(nodeType + "." + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName), anyEnt.get(propName));
858 if (propName.equals("resourceVersion") && resourceVersion == null) {
859 resourceVersion = (String)anyEnt.get(propName);
864 startNodeFilterHash.add(thisNodeFilterHash);
866 return resourceVersion;
870 * Map secondary filters.
872 * @param secondaryFilts the secondary filters
873 * @param secondaryFilterHash the secondary filter hash
874 * @param jaxbContext the jaxb context
877 private void mapSecondaryFilters(DynamicEntity secondaryFilts, Map<String,Object> secondaryFilterHash, DynamicJAXBContext jaxbContext) {
879 if (secondaryFilts == null || !secondaryFilts.isSet("secondaryFilt")) {
882 @SuppressWarnings("unchecked")
883 List<DynamicEntity> secondaryFilter = (ArrayList<DynamicEntity>)secondaryFilts.get("secondaryFilt");
885 for (DynamicEntity secondaryFilt : secondaryFilter) {
886 List<DynamicEntity> any = secondaryFilt.get("any");
888 for (DynamicEntity anyEnt : any) {
889 String clazz = anyEnt.getClass().getCanonicalName();
890 String simpleClazz = anyEnt.getClass().getSimpleName();
892 String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz);
894 DynamicType anyEntType = jaxbContext.getDynamicType(clazz);
896 for (String propName : anyEntType.getPropertiesNames()) {
897 // hyphencase the prop and throw it on the hash
898 if (anyEnt.isSet(propName)) {
899 secondaryFilterHash.put(nodeType + "." + CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, propName), anyEnt.get(propName));
907 * Remap inventory items.
909 * @param invResultItem the inv result item
910 * @param jaxbContext the jaxb context
911 * @param includeTheseVertices the include these vertices
912 * @param objectToVertMap the object to vert map
913 * @param aaiExtMap the aai ext map
914 * @return the dynamic entity
916 private DynamicEntity remapInventoryItems(DynamicEntity invResultItem, DynamicJAXBContext jaxbContext,
917 Map<String,String> includeTheseVertices, Map<Object,String> objectToVertMap, AAIExtensionMap aaiExtMap) {
920 DynamicEntity inventoryItem = jaxbContext.newDynamicEntity("inventory.aai.onap.org." + aaiExtMap.getApiVersion() + ".InventoryResponseItem");
921 Object item = invResultItem.get("item");
922 inventoryItem.set("modelName", invResultItem.get("modelName"));
923 inventoryItem.set("item", item);
924 inventoryItem.set("extraProperties", invResultItem.get("extraProperties"));
926 String vertexId = "";
928 if (objectToVertMap.containsKey(item)) {
929 vertexId = objectToVertMap.get(item);
932 if (includeTheseVertices.containsKey(vertexId)) {
933 if (invResultItem.isSet("inventoryResponseItems")) {
934 List<DynamicEntity> invItemList = new ArrayList<DynamicEntity>();
935 DynamicEntity inventoryItems = jaxbContext.newDynamicEntity("inventory.aai.att.com." + aaiExtMap.getApiVersion() + ".InventoryResponseItems");
936 DynamicEntity subInventoryResponseItems = invResultItem.get("inventoryResponseItems");
937 List<DynamicEntity> subInventoryResponseItemList = subInventoryResponseItems.get("inventoryResponseItem");
938 for (DynamicEntity ent : subInventoryResponseItemList) {
939 DynamicEntity invItem = remapInventoryItems(ent, jaxbContext, includeTheseVertices, objectToVertMap, aaiExtMap);
940 if (invItem != null) {
941 invItemList.add(invItem);
944 inventoryItems.set("inventoryResponseItem", invItemList);
945 inventoryItem.set("inventoryResponseItems", inventoryItems);
948 return inventoryItem;
955 * @param resultSetList the result set list
956 * @param jaxbContext the jaxb context
957 * @param aaiResources the aai resources
958 * @param objectToVertMap the object to vert map
959 * @param aaiExtMap the aai ext map
960 * @return the array list
961 * @throws AAIException the AAI exception
963 // this should return an inventoryItem
964 private List<Object> unpackResultSet(List<ResultSet> resultSetList,
965 TransactionalGraphEngine engine,
967 DBSerializer serializer) throws AAIException {
969 List<Object> resultList = new ArrayList<>();
971 for (ResultSet resultSet : resultSetList) {
973 if( resultSet.getVert() == null ){
977 Introspector inventoryItem = loader.introspectorFromName("inventory-response-item");
978 Introspector inventoryItems = loader.introspectorFromName("inventory-response-items");
979 // add this inventoryItem to the resultList for this level
980 resultList.add(inventoryItem.getUnderlyingObject());
982 Vertex vert = resultSet.getVert();
984 String aaiNodeType = vert.<String>property("aai-node-type").orElse(null);
986 if (aaiNodeType != null) {
987 Introspector thisObj = loader.introspectorFromName(aaiNodeType);
989 if (resultSet.getExtraPropertyHash() != null) {
990 Map<String,Object> extraProperties = resultSet.getExtraPropertyHash();
992 Introspector extraPropertiesEntity = loader.introspectorFromName("extra-properties");
994 List<Object> extraPropsList = extraPropertiesEntity.getValue("extra-property");
996 for (Map.Entry<String,Object> ent : extraProperties.entrySet()) {
997 String propName = ent.getKey();
998 Object propVal = ent.getValue();
1000 Introspector extraPropEntity = loader.introspectorFromName("extra-property");
1002 extraPropEntity.setValue("property-name", propName);
1003 extraPropEntity.setValue("property-value", propVal);
1005 extraPropsList.add(extraPropEntity.getUnderlyingObject());
1008 inventoryItem.setValue("extra-properties", extraPropertiesEntity.getUnderlyingObject());
1012 serializer.dbToObject(Collections.singletonList(vert), thisObj, 0, true, "false");
1013 } catch (UnsupportedEncodingException e1) {
1014 throw new AAIException("AAI_5105");
1016 PropertyLimitDesc propertyLimitDesc = resultSet.getPropertyLimitDesc();
1018 if (propertyLimitDesc != null) {
1020 if (PropertyLimitDesc.SHOW_NONE.equals(propertyLimitDesc)) {
1021 HashMap<String,Object> emptyPropertyOverRideHash = new HashMap<String,Object>();
1022 for (String key : thisObj.getAllKeys()) {
1023 emptyPropertyOverRideHash.put(key, null);
1025 filterProperties(thisObj, emptyPropertyOverRideHash);
1026 } else if (PropertyLimitDesc.SHOW_ALL.equals(propertyLimitDesc)) {
1028 } else if (PropertyLimitDesc.SHOW_NAME_AND_KEYS_ONLY.equals(propertyLimitDesc)) {
1029 HashMap<String,Object> keysAndNamesPropHash = new HashMap<String,Object>();
1031 for (String key : thisObj.getAllKeys()) {
1032 keysAndNamesPropHash.put(key, null);
1034 String namePropMetaData = thisObj.getMetadata(ObjectMetadata.NAME_PROPS);
1035 if (namePropMetaData != null) {
1036 String[] nameProps = namePropMetaData.split(",");
1037 for (String names : nameProps) {
1038 keysAndNamesPropHash.put(names, null);
1041 filterProperties(thisObj, keysAndNamesPropHash);
1044 if (resultSet.getPropertyOverRideHash() != null && resultSet.getPropertyOverRideHash().size() > 0) {
1045 Map<String,Object> propertyOverRideHash = resultSet.getPropertyOverRideHash();
1046 if (propertyOverRideHash.containsKey("persona-model-id")) {
1047 propertyOverRideHash.remove("persona-model-id");
1048 propertyOverRideHash.put("model-invariant-id", null);
1050 for (String key : thisObj.getAllKeys()) {
1051 propertyOverRideHash.put(key, null);
1053 filterProperties(thisObj, propertyOverRideHash);
1059 if (thisObj != null) {
1060 inventoryItem.setValue("item", thisObj.getUnderlyingObject());
1062 String modelName = null;
1064 // Try to get the modelName if we can. Otherwise, do not fail, just return what we have already.
1065 String modelInvariantIdLocal = (String)vert.<String>property("model-invariant-id-local").orElse(null); // this one points at a model
1066 String modelVersionIdLocal = (String)vert.<String>property("model-version-id-local").orElse(null); // this one points at a model-ver
1068 if ( (modelInvariantIdLocal != null && modelVersionIdLocal != null)
1069 && (modelInvariantIdLocal.length() > 0 && modelVersionIdLocal.length() > 0) ) {
1070 Introspector modelVer = loader.introspectorFromName("model-ver");
1071 modelVer.setValue("model-version-id", modelVersionIdLocal);
1072 QueryBuilder builder = engine.getQueryBuilder().createDBQuery(modelVer);
1073 List<Vertex> modelVerVerts = builder.toList();
1074 if( (modelVerVerts != null) && (modelVerVerts.size() == 1) ) {
1075 Vertex modelVerVert = modelVerVerts.get(0);
1076 modelName = modelVerVert.<String>property("model-name").orElse(null);
1077 if (modelName != null && modelName.length() > 0) {
1078 inventoryItem.setValue("model-name", modelName);
1082 } catch (DynamicException e) {
1083 ; // it's ok, dynamic object might not have these fields
1084 } catch (Exception e) {
1085 ; // it's ok, couldn't find a matching model
1088 if (resultSet.getSubResultSet() != null) {
1089 List<ResultSet> subResultSet = resultSet.getSubResultSet();
1090 if (subResultSet != null && subResultSet.size() > 0 ) {
1091 List<Object> res = unpackResultSet(subResultSet, engine, loader, serializer);
1092 if (res.size() > 0) {
1093 inventoryItems.setValue("inventory-response-item", res);
1094 inventoryItem.setValue("inventory-response-items", inventoryItems.getUnderlyingObject());
1105 private void filterProperties(Introspector thisObj, Map<String, Object> override) {
1107 thisObj.getProperties().stream().filter(x -> {
1108 return !override.containsKey(x);
1109 }).forEach(prop -> {
1110 if (thisObj.isSimpleType(prop)) {
1111 thisObj.setValue(prop, null);
1118 * Gets the media type.
1120 * @param mediaTypeList the media type list
1121 * @return the media type
1123 protected String getMediaType(List <MediaType> mediaTypeList) {
1124 String mediaType = MediaType.APPLICATION_JSON; // json is the default
1125 for (MediaType mt : mediaTypeList) {
1126 if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) {
1127 mediaType = MediaType.APPLICATION_XML;