2 * ============LICENSE_START=======================================================
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
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 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 package org.onap.aai.dbgraphmap;
24 import java.io.UnsupportedEncodingException;
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;
32 import java.util.Optional;
33 import java.util.stream.Stream;
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;
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;
78 import com.att.eelf.configuration.EELFLogger;
79 import com.att.eelf.configuration.EELFManager;
80 import com.google.common.base.CaseFormat;
82 import edu.emory.mathcs.backport.java.util.Collections;
85 * Database Mapping class which acts as the middle man between the REST interface objects
86 * for the Search namespace
89 public class SearchGraph {
91 private final String COMPONENT = "aaidbmap";
92 private AAIExtensionMap aaiExtMap;
93 private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(SearchGraph.class);
95 * Get the search result based on the includeNodeType and depth provided.
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
105 * @throws AAIException the AAI exception
107 public Response runGenericQuery (
109 String startNodeType,
110 List <String> startNodeKeyParams,
111 List <String> includeNodeTypes,
113 TransactionalGraphEngine dbEngine,
115 UrlBuilder urlBuilder) throws AAIException {
116 Response response = null;
117 boolean success = true;
120 dbEngine.startTransaction();
122 if( startNodeType == null ){
123 throw new AAIException("AAI_6120", "null start-node-type passed to the generic query");
126 if( startNodeKeyParams == null ){
127 throw new AAIException("AAI_6120", "no key param passed to the generic query");
130 if( includeNodeTypes == null ){
131 throw new AAIException("AAI_6120", "no include params passed to the generic query");
135 throw new AAIException("AAI_6120", "The maximum depth supported by the generic query is 6");
137 final QueryBuilder queryBuilder;
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;
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 + "]");
154 keyName = keyData.substring(0, colonIndex).split("\\.")[1];
155 keyValue = keyData.substring(colonIndex + 1);
156 builder.getVerticesByProperty(keyName, keyValue);
160 queryBuilder = builder;
162 URI uri = craftUriFromQueryParams(loader, startNodeType, startNodeKeyParams);
163 queryBuilder = dbEngine.getQueryBuilder().createQueryFromURI(uri).getQueryBuilder();
165 List<Vertex> results = queryBuilder.toList();
166 if( results.isEmpty()){
167 throw new AAIException("AAI_6114", "No Node of type " +
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);
176 Vertex startNode = results.get(0);
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") )
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();
189 if (!includeNodeTypes.contains("all")) {
190 traversal.where(__.has(AAIProperties.NODE_TYPE, P.within(includeNodeTypes)));
192 queryResults = traversal.toList();
195 if( queryResults.isEmpty()){
196 LOGGER.warn("No nodes found - apipe was null/empty");
200 Introspector searchResults = createSearchResults(loader, urlBuilder, queryResults);
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();
206 result = searchResults.marshal(properties);
207 response = Response.ok().entity(result).build();
209 LOGGER.debug(ver.size() + " node(s) traversed, " + queryResults.size() + " found");
212 } catch (AAIException e) {
215 } catch (Exception e) {
217 throw new AAIException("AAI_5105", e);
219 if (dbEngine != null) {
232 private URI craftUriFromQueryParams(Loader loader, String startNodeType, List<String> startNodeKeyParams) throws UnsupportedEncodingException, IllegalArgumentException, UriBuilderException, AAIException {
233 Introspector relationship = loader.introspectorFromName("relationship");
235 relationship.setValue("related-to", startNodeType);
236 List<Object> relationshipDataList = relationship.getValue("relationship-data");
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 + "]");
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());
251 RelationshipToURI parser = new RelationshipToURI(loader, relationship);
253 return parser.getUri();
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
266 * @throws AAIException the AAI exception
268 public Response runNodesQuery (
270 String targetNodeType,
271 List <String> edgeFilterParams,
272 List <String> filterParams,
273 TransactionalGraphEngine dbEngine,
275 UrlBuilder urlBuilder) throws AAIException {
277 Response response = null;
278 boolean success = true;
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";
286 dbEngine.startTransaction();
290 if( targetNodeType == null || targetNodeType == "" ){
291 throw new AAIException("AAI_6120", "null or empty target-node-type passed to the node query");
295 target = loader.introspectorFromName(targetNodeType);
296 } catch (AAIUnknownObjectException e) {
297 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + targetNodeType + "] passed to node query.");
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");
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 + ")");
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 + "]");
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 + "]");
322 if( pieces.length == 3 ){
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);
331 queryStringForMsg.append(".has(" + propName + "," + value + ")");
332 traversal.has(propName,value);
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 + "]");
339 if( pieces.length == 3 ){
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);
348 queryStringForMsg.append(".hasNot(" + propName + "," + value + ")");
349 traversal.not(__.has(propName,value));
351 else if( filterType.equals(EXISTS)){
352 queryStringForMsg.append(".has(" + propName + ")");
353 traversal.has(propName);
355 else if( filterType.equals(DOES_NOT_EXIST)){
356 queryStringForMsg.append(".hasNot(" + propName + ")");
357 traversal.hasNot(propName);
360 throw new AAIException("AAI_6120", "bad filterType passed: [" + filterType + "]");
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 + "]");
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 + "]");
380 otherNode = loader.introspectorFromName(nodeType);
381 } catch (AAIUnknownObjectException e) {
382 throw new AAIException("AAI_6115", "Unrecognized nodeType [" + nodeType + "] passed to node query.");
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];
390 String[] edgeLabels = getEdgeLabel(targetNodeType, nodeType);
392 GraphTraversal<Vertex, Vertex> edgeSearch = __.start();
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);
400 edgeSearch.has(propName);
404 if( filterType.equals(DOES_NOT_EXIST)){
405 traversal.where(__.not(edgeSearch));
406 } else if (filterType.equals(EXISTS)) {
407 traversal.where(edgeSearch);
412 List<Vertex> results = traversal.toList();
413 Introspector searchResults = createSearchResults(loader, urlBuilder, results);
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();
419 result = searchResults.marshal(properties);
420 response = Response.ok().entity(result).build();
423 } catch (AAIException e) {
426 } catch (Exception e) {
428 throw new AAIException("AAI_5105", e);
430 if (dbEngine != null) {
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();
450 stream = results.stream();
452 boolean isParallel = stream.isParallel();
453 stream.forEach(v -> {
454 String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null);
458 thisNodeURL = urlBuilder.pathed(v);
459 Introspector resultData = loader.introspectorFromName("result-data");
461 resultData.setValue("resource-type", nodeType);
462 resultData.setValue("resource-link", thisNodeURL);
464 synchronized (resultDataList) {
465 resultDataList.add(resultData.getUnderlyingObject());
468 resultDataList.add(resultData.getUnderlyingObject());
470 } catch (AAIException | AAIFormatVertexException e) {
471 throw new RuntimeException(e);
475 return searchResults;
478 private String findDbPropName(Introspector obj, String propName) {
480 Optional<String> result = obj.getPropertyMetadata(propName, PropertyMetadata.DB_ALIAS);
481 if (result.isPresent()) {
490 * Gets the edge label.
492 * @param targetNodeType the target node type
493 * @param nodeType the node type
494 * @return the edge label
495 * @throws AAIException the AAI exception
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]);
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
515 public Response runNamedQuery(String fromAppId, String transId, String queryParameters,
516 DBConnectionType connectionType,
517 AAIExtensionMap aaiExtMap) throws JAXBException, AAIException {
519 Introspector inventoryItems;
520 boolean success = true;
521 TransactionalGraphEngine dbEngine = null;
524 MoxyLoader loader = (MoxyLoader)LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
525 DynamicJAXBContext jaxbContext = loader.getJAXBContext();
526 dbEngine = new TitanDBEngine(
527 QueryStyle.TRAVERSAL,
530 DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId);
531 ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer);
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;
540 if (queryParameters.length() == 0) {
541 queryParameters = "{}";
544 DynamicEntity modelAndNamedQuerySearch = (DynamicEntity)loader.unmarshal("ModelAndNamedQuerySearch", queryParameters, mediaType).getUnderlyingObject();
545 if (modelAndNamedQuerySearch == null) {
546 throw new AAIException("AAI_5105");
548 HashMap<String,Object> namedQueryLookupHash = new HashMap<String,Object>();
550 DynamicEntity qp = modelAndNamedQuerySearch.get("queryParameters");
551 String namedQueryUuid = null;
552 if ((qp != null) && qp.isSet("namedQuery")) {
553 DynamicEntity namedQuery = (DynamicEntity) qp.get("namedQuery");
555 if (namedQuery.isSet("namedQueryUuid")) {
556 namedQueryUuid = namedQuery.get("namedQueryUuid");
558 if (namedQuery.isSet("namedQueryName")) {
559 namedQueryLookupHash.put("named-query-name", namedQuery.get("namedQueryName"));
561 if (namedQuery.isSet("namedQueryVersion")) {
562 namedQueryLookupHash.put("named-query-version", namedQuery.get("namedQueryVersion"));
566 if (namedQueryUuid == null) {
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
577 String secondaryFilterCutPoint = null;
579 if (modelAndNamedQuerySearch.isSet("secondaryFilterCutPoint")) {
580 secondaryFilterCutPoint = modelAndNamedQuerySearch.get("secondaryFilterCutPoint");
583 List<Map<String,Object>> startNodeFilterHash = new ArrayList<>();
585 mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"),
586 startNodeFilterHash, jaxbContext);
588 Map<String,Object> secondaryFilterHash = new HashMap<>();
590 mapSecondaryFilters((DynamicEntity)modelAndNamedQuerySearch.get("secondaryFilts"),
591 secondaryFilterHash, jaxbContext);
593 List<ResultSet> resultSet = processor.queryByNamedQuery(transId, fromAppId,
594 namedQueryUuid, startNodeFilterHash, aaiExtMap.getApiVersion(), secondaryFilterCutPoint, secondaryFilterHash);
596 inventoryItems = loader.introspectorFromName("inventory-response-items");
598 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
600 inventoryItems.setValue("inventory-response-item", invItemList);
602 } catch (AAIException e) {
605 } catch (Exception e) {
607 throw new AAIException("AAI_5105", e);
609 if (dbEngine != null) {
618 return getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders());
622 * Execute model operation.
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
635 public Response executeModelOperation(String fromAppId, String transId, String queryParameters,
636 DBConnectionType connectionType,
638 AAIExtensionMap aaiExtMap) throws JAXBException, AAIException, DynamicException, UnsupportedEncodingException {
640 boolean success = true;
641 TransactionalGraphEngine dbEngine = null;
644 MoxyLoader loader = (MoxyLoader) LoaderFactory.createLoaderForVersion(ModelType.MOXY, AAIProperties.LATEST);
645 DynamicJAXBContext jaxbContext = loader.getJAXBContext();
646 dbEngine = new TitanDBEngine(
647 QueryStyle.TRAVERSAL,
650 DBSerializer serializer = new DBSerializer(AAIProperties.LATEST, dbEngine, ModelType.MOXY, fromAppId);
651 ModelBasedProcessing processor = new ModelBasedProcessing(loader, dbEngine, serializer);
652 dbEngine.startTransaction();
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;
661 if (queryParameters.length() == 0) {
662 queryParameters = "{}";
665 DynamicEntity modelAndNamedQuerySearch = (DynamicEntity)loader.unmarshal("ModelAndNamedQuerySearch", queryParameters, mediaType).getUnderlyingObject();
666 if (modelAndNamedQuerySearch == null) {
667 throw new AAIException("AAI_5105");
670 Map<String,Object> modelQueryLookupHash = new HashMap<>();
672 String modelVersionId = null;
673 String modelName = null;
674 String modelInvariantId = null;
675 String modelVersion = null;
676 String topNodeType = null;
678 if (modelAndNamedQuerySearch.isSet("topNodeType")) {
679 topNodeType = modelAndNamedQuerySearch.get("topNodeType");
682 // the ways to get a model:
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
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");
693 if (qp.isSet("model")) {
694 DynamicEntity model = (DynamicEntity) qp.get("model");
696 // on an old-style model object, the following 4 attrs were all present
697 if (model.isSet("modelNameVersionId")) {
698 modelVersionId = model.get("modelNameVersionId");
700 if (model.isSet("modelId")) {
701 modelInvariantId = model.get("modelId");
703 if (model.isSet("modelName")) {
704 modelName = model.get("modelName");
706 if (model.isSet("modelVersion")) {
707 modelVersion = model.get("modelVersion");
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");
716 if (model.isSet("modelVers")) {
717 // we know that this is new style, because modelVers was not an option
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");
727 if (modelVer.isSet("modelVersionId")) {
728 modelVersionId = modelVer.get("modelVersionId");
730 if (modelVer.isSet("modelVersion")) {
731 modelVersion = modelVer.get("modelVersion");
738 List<Map<String,Object>> startNodeFilterHash = new ArrayList<>();
740 String resourceVersion = mapInstanceFilters((DynamicEntity)modelAndNamedQuerySearch.get("instanceFilters"),
741 startNodeFilterHash, jaxbContext);
745 List<ResultSet> resultSet = processor.queryByModel(transId, fromAppId,
746 modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() );
748 Map<Object,String> objectToVertMap = new HashMap<>();
749 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
751 ResultSet rs = resultSet.get(0);
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;
759 restURI = "/aai/" + notificationVersion + "/" + restURI;
762 Map<String,String> delResult = processor.runDeleteByModel( transId, fromAppId,
763 modelVersionId, topNodeType, startNodeFilterHash.get(0), aaiExtMap.getApiVersion(), resourceVersion );
765 String resultStr = "";
766 for (Map.Entry<String,String> ent : delResult.entrySet()) {
767 resultStr += "v[" + ent.getKey() + "] " + ent.getValue() + ",\n";
771 // Note - notifications are now done down in the individual "remove" calls done in runDeleteByModel() above.
773 response = Response.ok(resultStr).build();
776 List<ResultSet> resultSet = processor.queryByModel( transId, fromAppId,
777 modelVersionId, modelInvariantId, modelName, topNodeType, startNodeFilterHash, aaiExtMap.getApiVersion() );
779 Introspector inventoryItems = loader.introspectorFromName("inventory-response-items");
781 List<Object> invItemList = unpackResultSet(resultSet, dbEngine, loader, serializer);
783 inventoryItems.setValue("inventory-response-item", invItemList);
785 response = getResponseFromIntrospector(inventoryItems, aaiExtMap.getHttpHeaders());
788 } catch (AAIException e) {
791 } catch (Exception e) {
793 throw new AAIException("AAI_5105", e);
795 if (dbEngine != null) {
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)) {
815 org.onap.aai.introspection.MarshallerProperties properties;
818 new org.onap.aai.introspection.MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.APPLICATION_JSON_TYPE).build();
821 new org.onap.aai.introspection.MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.APPLICATION_XML_TYPE).build();
824 String marshalledObj = obj.marshal(properties);
825 return Response.ok().entity(marshalledObj).build();
829 * Map instance filters.
831 * @param instanceFilters the instance filters
832 * @param startNodeFilterHash the start node filter hash
833 * @param jaxbContext the jaxb context
836 private String mapInstanceFilters(DynamicEntity instanceFilters, List<Map<String,Object>> startNodeFilterHash, DynamicJAXBContext jaxbContext) {
838 if (instanceFilters == null || !instanceFilters.isSet("instanceFilter")) {
841 @SuppressWarnings("unchecked")
842 List<DynamicEntity> instanceFilter = (ArrayList<DynamicEntity>)instanceFilters.get("instanceFilter");
843 String resourceVersion = null;
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();
852 String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz);
854 DynamicType anyEntType = jaxbContext.getDynamicType(clazz);
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);
866 startNodeFilterHash.add(thisNodeFilterHash);
868 return resourceVersion;
872 * Map secondary filters.
874 * @param secondaryFilts the secondary filters
875 * @param secondaryFilterHash the secondary filter hash
876 * @param jaxbContext the jaxb context
879 private void mapSecondaryFilters(DynamicEntity secondaryFilts, Map<String,Object> secondaryFilterHash, DynamicJAXBContext jaxbContext) {
881 if (secondaryFilts == null || !secondaryFilts.isSet("secondaryFilt")) {
884 @SuppressWarnings("unchecked")
885 List<DynamicEntity> secondaryFilter = (ArrayList<DynamicEntity>)secondaryFilts.get("secondaryFilt");
887 for (DynamicEntity secondaryFilt : secondaryFilter) {
888 List<DynamicEntity> any = secondaryFilt.get("any");
890 for (DynamicEntity anyEnt : any) {
891 String clazz = anyEnt.getClass().getCanonicalName();
892 String simpleClazz = anyEnt.getClass().getSimpleName();
894 String nodeType = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, simpleClazz);
896 DynamicType anyEntType = jaxbContext.getDynamicType(clazz);
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));
909 * Remap inventory items.
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
918 private DynamicEntity remapInventoryItems(DynamicEntity invResultItem, DynamicJAXBContext jaxbContext,
919 Map<String,String> includeTheseVertices, Map<Object,String> objectToVertMap, AAIExtensionMap aaiExtMap) {
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"));
928 String vertexId = "";
930 if (objectToVertMap.containsKey(item)) {
931 vertexId = objectToVertMap.get(item);
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);
946 inventoryItems.set("inventoryResponseItem", invItemList);
947 inventoryItem.set("inventoryResponseItems", inventoryItems);
950 return inventoryItem;
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
965 // this should return an inventoryItem
966 private List<Object> unpackResultSet(List<ResultSet> resultSetList,
967 TransactionalGraphEngine engine,
969 DBSerializer serializer) throws AAIException {
971 List<Object> resultList = new ArrayList<>();
973 for (ResultSet resultSet : resultSetList) {
975 if( resultSet.getVert() == null ){
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());
984 Vertex vert = resultSet.getVert();
986 String aaiNodeType = vert.<String>property("aai-node-type").orElse(null);
988 if (aaiNodeType != null) {
989 Introspector thisObj = loader.introspectorFromName(aaiNodeType);
991 if (resultSet.getExtraPropertyHash() != null) {
992 Map<String,Object> extraProperties = resultSet.getExtraPropertyHash();
994 Introspector extraPropertiesEntity = loader.introspectorFromName("extra-properties");
996 List<Object> extraPropsList = extraPropertiesEntity.getValue("extra-property");
998 for (Map.Entry<String,Object> ent : extraProperties.entrySet()) {
999 String propName = ent.getKey();
1000 Object propVal = ent.getValue();
1002 Introspector extraPropEntity = loader.introspectorFromName("extra-property");
1004 extraPropEntity.setValue("property-name", propName);
1005 extraPropEntity.setValue("property-value", propVal);
1007 extraPropsList.add(extraPropEntity.getUnderlyingObject());
1010 inventoryItem.setValue("extra-properties", extraPropertiesEntity.getUnderlyingObject());
1014 serializer.dbToObject(Collections.singletonList(vert), thisObj, 0, true, "false");
1015 } catch (UnsupportedEncodingException e1) {
1016 throw new AAIException("AAI_5105");
1018 PropertyLimitDesc propertyLimitDesc = resultSet.getPropertyLimitDesc();
1020 if (propertyLimitDesc != null) {
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);
1027 filterProperties(thisObj, emptyPropertyOverRideHash);
1028 } else if (PropertyLimitDesc.SHOW_ALL.equals(propertyLimitDesc)) {
1030 } else if (PropertyLimitDesc.SHOW_NAME_AND_KEYS_ONLY.equals(propertyLimitDesc)) {
1031 HashMap<String,Object> keysAndNamesPropHash = new HashMap<String,Object>();
1033 for (String key : thisObj.getAllKeys()) {
1034 keysAndNamesPropHash.put(key, null);
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);
1043 filterProperties(thisObj, keysAndNamesPropHash);
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);
1052 for (String key : thisObj.getAllKeys()) {
1053 propertyOverRideHash.put(key, null);
1055 filterProperties(thisObj, propertyOverRideHash);
1061 if (thisObj != null) {
1062 inventoryItem.setValue("item", thisObj.getUnderlyingObject());
1064 String modelName = null;
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
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);
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
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());
1107 private void filterProperties(Introspector thisObj, Map<String, Object> override) {
1109 thisObj.getProperties().stream().filter(x -> {
1110 return !override.containsKey(x);
1111 }).forEach(prop -> {
1112 if (thisObj.isSimpleType(prop)) {
1113 thisObj.setValue(prop, null);
1120 * Gets the media type.
1122 * @param mediaTypeList the media type list
1123 * @return the media type
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;