diff options
Diffstat (limited to 'aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java')
-rw-r--r-- | aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java | 299 |
1 files changed, 224 insertions, 75 deletions
diff --git a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java index dd5d42c6..bd7f1b91 100644 --- a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java +++ b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java @@ -8,7 +8,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -50,6 +50,7 @@ import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; import org.apache.commons.lang.StringUtils; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.janusgraph.core.JanusGraphException; import org.javatuples.Pair; import org.onap.aai.db.props.AAIProperties; @@ -67,6 +68,7 @@ import org.onap.aai.nodes.NodeIngestor; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.parsers.uri.URIToExtensionInformation; +import org.onap.aai.parsers.uri.URIToObject; import org.onap.aai.rest.ueb.UEBNotification; import org.onap.aai.restcore.HttpMethod; import org.onap.aai.schema.enums.ObjectMetadata; @@ -93,15 +95,15 @@ public class HttpEntry { private static final String TARGET_ENTITY = "DB"; private ModelType introspectorFactoryType; - + private QueryStyle queryStyle; - + private SchemaVersion version; - + private Loader loader; - + private TransactionalGraphEngine dbEngine; - + private boolean processSingle = true; private int paginationBucket = -1; @@ -148,7 +150,7 @@ public class HttpEntry { } - public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType, UEBNotification notification){ + public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType, UEBNotification notification){ this.version = version; this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version); this.dbEngine = new JanusGraphDBEngine( @@ -161,7 +163,7 @@ public class HttpEntry { getDbEngine().startTransaction(); return this; } - + /** * Gets the introspector factory type. @@ -316,7 +318,7 @@ public class HttpEntry { QueryEngine queryEngine = dbEngine.getQueryEngine(); int maxRetries = 10; int retry = 0; - + LoggingContext.save(); for (DBRequest request : requests) { response = null; @@ -358,7 +360,7 @@ public class HttpEntry { if (requestContextList != null) { requestContext = requestContextList.get(0); } - + if (cleanUp == null) { cleanUp = "false"; } @@ -426,7 +428,7 @@ public class HttpEntry { result = formatter.output(vertices.stream().map(vertex -> (Object) vertex).collect(Collectors.toList())).toString(); status = Status.OK; } - + break; case PUT: response = this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); @@ -441,9 +443,9 @@ public class HttpEntry { } obj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); } - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed "); LoggingContext.restoreIfPossible(); @@ -454,7 +456,7 @@ public class HttpEntry { serializer.touchStandardVertexProperties(v, false); this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); serializer.createEdge(obj, v); - + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs(),TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); @@ -466,7 +468,7 @@ public class HttpEntry { existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj, request.getUri(), 0, false, cleanUp); String existingJson = existingObj.marshal(false); String newJson; - + if (request.getRawRequestContent().isPresent()) { newJson = request.getRawRequestContent().get(); } else { @@ -489,15 +491,15 @@ public class HttpEntry { status = Status.OK; patchedObj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, patchedObj, this.loader); } - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, patchedObj, relatedObjects, basePath); } catch (IOException | JsonPatchException e) { - + LOGGER.info ("Caught exception: " + e.getMessage()); LoggingContext.restoreIfPossible(); throw new AAIException("AAI_3000", "could not perform patch operation"); @@ -507,18 +509,18 @@ public class HttpEntry { String resourceVersion = params.getFirst("resource-version"); obj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); } /* * Find all Delete-other-vertex vertices and create structure for notify - * findDeleatble also returns the startVertex v and we dont want to create + * findDeleatble also returns the startVertex v and we dont want to create * duplicate notification events for the same * So remove the startvertex first */ - + List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v); Long vId = (Long) v.id(); - + /* * I am assuming vertexId cant be null */ @@ -527,39 +529,39 @@ public class HttpEntry { Map<Vertex, Introspector> deleteObjects = new HashMap<>(); Map<String, URI> uriMap = new HashMap<>(); Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>(); - + if(isDelVerticesPresent){ deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices); - + uriMap = this.buildURIMap(serializer, deleteObjects); deleteRelatedObjects = this.buildRelatedObjects(serializer, queryEngine, deleteObjects); } - + this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion); this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, false); - - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); status = Status.NO_CONTENT; notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects, basePath); - + /* * Notify delete-other-v candidates */ - + if(isDelVerticesPresent){ this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification, deleteObjects, uriMap, deleteRelatedObjects, basePath); } - + break; case DELETE_EDGE: serializer.touchStandardVertexProperties(v, false); serializer.deleteEdge(obj, v); - + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs(),TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); @@ -569,8 +571,8 @@ public class HttpEntry { default: break; } - - + + /* temporarily adding vertex id to the headers * to be able to use for testing the vertex id endpoint functionality * since we presently have no other way of generating those id urls @@ -606,7 +608,7 @@ public class HttpEntry { break; } catch (JanusGraphException e) { this.dbEngine.rollback(); - + LOGGER.info ("Caught exception: " + e.getMessage()); LoggingContext.restoreIfPossible(); AAIException ex = new AAIException("AAI_6142", e); @@ -655,7 +657,7 @@ public class HttpEntry { return Pair.with(success, responses); } - + /** * Gets the media type. * @@ -663,15 +665,15 @@ public class HttpEntry { * @return the media type */ private String getMediaType(List <MediaType> mediaTypeList) { - String mediaType = MediaType.APPLICATION_JSON; // json is the default + String mediaType = MediaType.APPLICATION_JSON; // json is the default for (MediaType mt : mediaTypeList) { if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) { mediaType = MediaType.APPLICATION_XML; - } + } } return mediaType; } - + /** * Gets the object from db. * @@ -691,19 +693,19 @@ public class HttpEntry { * @throws NoSuchMethodException the no such method exception * @throws UnsupportedEncodingException the unsupported encoding exception * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException + * @throws AAIUnknownObjectException + * @throws URISyntaxException */ private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException { - - //nothing found - if (results.isEmpty()) { - String msg = createNotFoundMessage(query.getResultType(), uri); + + //nothing found + if (results.isEmpty()) { + String msg = createNotFoundMessage(query.getResultType(), uri); throw new AAIException("AAI_6114", msg); - } + } + + return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); - return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); - } /** @@ -800,7 +802,7 @@ public class HttpEntry { return response; } - + /** * Creates the not found message. * @@ -809,12 +811,12 @@ public class HttpEntry { * @return the string */ private String createNotFoundMessage(String resultType, URI uri) { - - String msg = "No Node of type " + resultType + " found at: " + uri.getPath(); - return msg; + String msg = "No Node of type " + resultType + " found at: " + uri.getPath(); + + return msg; } - + /** * Sets the depth. * @@ -831,11 +833,11 @@ public class HttpEntry { return depth; } - if(depthParam == null){ + if(depthParam == null){ if(this.version.compareTo(schemaVersions.getDepthVersion()) >= 0){ depth = 0; } else { - depth = AAIProperties.MAXIMUM_DEPTH; + depth = AAIProperties.MAXIMUM_DEPTH; } } else { if (!depthParam.isEmpty() && !"all".equals(depthParam)){ @@ -847,16 +849,16 @@ public class HttpEntry { } } - String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH); - + String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH); + int maximumDepth = AAIProperties.MAXIMUM_DEPTH; if(maxDepth != null){ - try { - maximumDepth = Integer.parseInt(maxDepth); - } catch(Exception ex){ - throw new AAIException("AAI_4018"); - } + try { + maximumDepth = Integer.parseInt(maxDepth); + } catch(Exception ex){ + throw new AAIException("AAI_4018"); + } } if(depth > maximumDepth){ @@ -865,7 +867,7 @@ public class HttpEntry { return depth; } - + /** * Checks if is modification method. * @@ -874,31 +876,155 @@ public class HttpEntry { */ private boolean isModificationMethod(HttpMethod method) { boolean result = false; - + if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PUT_EDGE) || method.equals(HttpMethod.DELETE_EDGE) || method.equals(HttpMethod.MERGE_PATCH)) { result = true; } - + return result; - + } - - private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIException, URISyntaxException { + + /** + * Given an uri, introspector object and loader object + * it will check if the obj is top level object if it is, + * it will return immediately returning the uri passed in + * If it isn't, it will go through, get the uriTemplate + * from the introspector object and get the count of "/"s + * and remove that part of the uri using substring + * and keep doing that until the current object is top level + * Also added the max depth just so worst case scenario + * Then keep adding aai-uri to the list include the aai-uri passed in + * Convert that list into an array and return it + * <p> + * + * Example: + * + * <blockquote> + * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 + * + * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id} + * it converts to /vservers/vserver + * + * lastIndexOf /vservers/vserver in /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 + * ^ + * | + * | + * lastIndexOf + * Use substring to get the string from 0 to that lastIndexOf + * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1 + * + * From this new aai-uri, generate a introspector from the URITOObject class + * and keep doing this until you + * + * </blockquote> + * + * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex + * @param obj - introspector object of the given starting vertex + * @param loader - Type of loader which will always be MoxyLoader to support model driven + * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex + * @throws UnsupportedEncodingException + * @throws AAIException + */ + String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader) throws UnsupportedEncodingException, AAIException { + + List<String> uriList = new ArrayList<>(); + String template = StringUtils.EMPTY; + String truncatedUri = aaiUri; + int depth = AAIProperties.MAXIMUM_DEPTH; + uriList.add(truncatedUri); + + while (depth >= 0 && !obj.isTopLevel()) { + template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE); + + if(template == null){ + LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName()); + return null; + } + + int templateCount = StringUtils.countMatches(template, "/"); + int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/"); + + if(templateCount > truncatedUriCount){ + LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri); + return null; + } + + int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount-templateCount+1); + truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex); + uriList.add(truncatedUri); + obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity(); + depth--; + } + + return uriList.toArray(new String[uriList.size()]); + } + + /** + * + * @param serializer + * @param queryEngine + * @param v + * @param obj + * @param loader + * @return + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + * @throws SecurityException + * @throws InstantiationException + * @throws NoSuchMethodException + * @throws UnsupportedEncodingException + * @throws AAIException + * @throws URISyntaxException + */ + private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v, Introspector obj, Loader loader) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIException, URISyntaxException { + HashMap<String, Introspector> relatedVertices = new HashMap<>(); - List<Vertex> vertexChain = queryEngine.findParents(v); - for (Vertex vertex : vertexChain) { + VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI); + + if(!aaiUriProperty.isPresent()){ + if(LOGGER.isDebugEnabled()){ + LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects", v.id().toString()); + } else { + LOGGER.info("It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log"); + } + return relatedVertices; + } + + String aaiUri = aaiUriProperty.value().toString(); + + if(!obj.isTopLevel()){ + String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader); + List<Vertex> vertexChain = null; + // If the uriList is null then there is something wrong with converting the uri + // into a list of aai-uris so falling back to the old mechanism for finding parents + if(uriList == null){ + LOGGER.info("Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal"); + vertexChain = queryEngine.findParents(v); + } else { + vertexChain = queryEngine.findParents(uriList); + } + for(Vertex vertex : vertexChain){ + try { + final Introspector vertexObj = serializer.getVertexProperties(vertex); + relatedVertices.put(vertexObj.getObjectId(), vertexObj); + } catch (AAIUnknownObjectException e) { + LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); + } + } + } else { try { - final Introspector vertexObj = serializer.getVertexProperties(vertex); + final Introspector vertexObj = serializer.getVertexProperties(v); relatedVertices.put(vertexObj.getObjectId(), vertexObj); } catch (AAIUnknownObjectException e) { LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); } - } - + return relatedVertices; } - + private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) { Map<Vertex, Introspector> deleteObjectMap = new HashMap<>(); for (Vertex vertex : vertices) { @@ -944,7 +1070,7 @@ public class HttpEntry { for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) { try { HashMap<String, Introspector> relatedObjects = this.getRelatedObjects(serializer, queryEngine, - entry.getKey()); + entry.getKey(), entry.getValue(), this.loader); if (null != entry.getValue()) relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects); } catch (IllegalAccessException | IllegalArgumentException @@ -959,7 +1085,7 @@ public class HttpEntry { return relatedObjectsMap; } - + private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId, UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap, Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) { @@ -980,4 +1106,27 @@ public class HttpEntry { } } } + + public void setPaginationParameters(String resultIndex, String resultSize) { + if (resultIndex != null && resultIndex != "-1" && resultSize != null && resultSize != "-1") { + this.setPaginationIndex(Integer.parseInt(resultIndex)); + this.setPaginationBucket(Integer.parseInt(resultSize)); + } + } + + public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException{ + List<Object> vertices; + if(this.isPaginated()) { + this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket()); + int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket(); + int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size()); + if(startIndex > endIndex){ + throw new AAIException("AAI_6150"," ResultIndex is not appropriate for the result set, Needs to be <= "+ endIndex); + } + vertices = vertexList.subList(startIndex, endIndex); + }else{ + vertices = vertexList; + } + return vertices; + } } |