Update Janusgraph to 0.5.0 in traversal 36/138336/3
authorFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Thu, 27 Jun 2024 12:12:01 +0000 (14:12 +0200)
committerFiete Ostkamp <Fiete.Ostkamp@telekom.de>
Thu, 27 Jun 2024 17:39:25 +0000 (19:39 +0200)
- update Janusgraph to 0.5.0 + tinkerpop to 3.4.13
- do not use pagination methods from HttpEntry [1]
- introduce WebTestClient as a way to more elegantly declare http requests and assertions

[1] pagination is still done on the client-side in this service.
This change just removes the usage of methods of HttpEntry that effectively do a List.subList
and that will be removed from HttpEntry shortly after

Issue-ID: AAI-3902
Change-Id: I446f092048b9e2be7314ed32ab3de5d5e1e1689d
Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de>
15 files changed:
aai-traversal/pom.xml
aai-traversal/src/main/java/org/onap/aai/rest/CQ2Gremlin.java
aai-traversal/src/main/java/org/onap/aai/rest/CQ2GremlinTest.java
aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java
aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java
aai-traversal/src/main/java/org/onap/aai/rest/dsl/v1/DslListener.java
aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryTestDTO.java
aai-traversal/src/main/java/org/onap/aai/rest/util/PaginationUtil.java [new file with mode: 0644]
aai-traversal/src/test/java/org/onap/aai/PayloadUtil.java
aai-traversal/src/test/java/org/onap/aai/WebClientConfiguration.java [new file with mode: 0644]
aai-traversal/src/test/java/org/onap/aai/rest/DslConsumerTest.java
aai-traversal/src/test/java/org/onap/aai/rest/GfpVserverDataStoredQueryTest.java
aai-traversal/src/test/java/org/onap/aai/rest/QueryConsumerTest.java
pom.xml
version.properties

index 59527d0..99e5c2d 100644 (file)
@@ -26,7 +26,7 @@
        <parent>
                <groupId>org.onap.aai.traversal</groupId>
                <artifactId>traversal</artifactId>
-               <version>1.14.2-SNAPSHOT</version>
+               <version>1.14.3-SNAPSHOT</version>
        </parent>
        <groupId>org.onap.aai.traversal</groupId>
        <artifactId>aai-traversal</artifactId>
 
                <!-- versions -->
                <spring.boot.version>2.4.13</spring.boot.version>
-               <spring.version>5.3.13</spring.version>
+               <janusgraph.version>0.5.0</janusgraph.version>
+               <gremlin.version>3.4.13</gremlin.version>
+
                <spring-cloud.version>2020.0.2</spring-cloud.version>
-               <spring.test.version>${spring.version}</spring.test.version>
-               <spring.jms.version>${spring.version}</spring.jms.version>
+
                <javax.servlet.version>4.0.1</javax.servlet.version>
                <antlr.version>4.9.3</antlr.version>
                <keycloak.version>11.0.2</keycloak.version>
-               <micrometer-spring-legacy.version>1.3.19</micrometer-spring-legacy.version>
-               <micrometer-core.version>1.6.6</micrometer-core.version>
-               <micrometer-jersey2>1.6.6</micrometer-jersey2>
+
+               <micrometer.version>1.6.6</micrometer.version>
                <mockito.core.version>3.4.0</mockito.core.version>
-               <powermock.version>2.0.9</powermock.version>
                <testcontainers.version>1.6.1</testcontainers.version>
                <netty.handler.version>4.1.63.Final</netty.handler.version>
                <netty.version>4.1.63.Final</netty.version>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-hateoas</artifactId>
                </dependency>
-               <dependency>
-                       <groupId>io.micrometer</groupId>
-                       <artifactId>micrometer-spring-legacy</artifactId>
-                       <version>${micrometer-spring-legacy.version}</version>
-               </dependency>
                <dependency>
                        <groupId>io.micrometer</groupId>
                        <artifactId>micrometer-core</artifactId>
-                       <version>${micrometer-core.version}</version>
+                       <version>${micrometer.version}</version>
                </dependency>
                <dependency>
                        <groupId>io.micrometer</groupId>
                        <artifactId>micrometer-registry-prometheus</artifactId>
-                       <!-- <version>${micrometer-registry-prometheus.version}</version> -->
+                       <version>${micrometer.version}</version>
                </dependency>
                <dependency>
                        <groupId>io.micrometer</groupId>
                        <artifactId>micrometer-jersey2</artifactId>
-                       <version>${micrometer-jersey2}</version>
+                       <version>${micrometer.version}</version>
                </dependency>
                <dependency>
                        <groupId>javax.jms</groupId>
                                </exclusion>
                        </exclusions>
                </dependency>
+               <dependency>
+                       <groupId>org.janusgraph</groupId>
+                       <artifactId>janusgraph-inmemory</artifactId>
+                       <version>${janusgraph.version}</version>
+       </dependency>
                <dependency>
                        <groupId>com.github.jnr</groupId>
                        <artifactId>jnr-ffi</artifactId>
                        <groupId>com.fasterxml.jackson.dataformat</groupId>
                        <artifactId>jackson-dataformat-xml</artifactId>
                </dependency>
-               <dependency>
-                       <groupId>org.powermock</groupId>
-                       <artifactId>powermock-module-junit4</artifactId>
-                       <version>${powermock.version}</version>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.powermock</groupId>
-                       <artifactId>powermock-api-mockito2</artifactId>
-                       <version>${powermock.version}</version>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.powermock</groupId>
-                       <artifactId>powermock-module-javaagent</artifactId>
-                       <version>${powermock.version}</version>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.powermock</groupId>
-                       <artifactId>powermock-module-junit4-rule-agent</artifactId>
-                       <version>${powermock.version}</version>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>com.beust</groupId>
-                       <artifactId>jcommander</artifactId>
-               </dependency>
                <dependency>
                        <groupId>org.json</groupId>
                        <artifactId>json</artifactId>
                                </exclusion>
                        </exclusions>
                </dependency>
+               <dependency>
+                       <groupId>org.springframework.boot</groupId>
+                       <artifactId>spring-boot-starter-webflux</artifactId>
+                       <scope>test</scope>
+               </dependency>
                <dependency>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-jetty</artifactId>
index c8f1f0b..6b8382b 100644 (file)
@@ -103,7 +103,6 @@ public class CQ2Gremlin extends RESTAPI {
 
             SchemaVersions schemaVersions = SpringContextAware.getBean(SchemaVersions.class);
             traversalUriHttpEntry.setHttpEntryProperties(schemaVersions.getDefaultVersion());
-            traversalUriHttpEntry.setPaginationParameters("-1", "-1");
 
             TransactionalGraphEngine dbEngine = traversalUriHttpEntry.getDbEngine();
 
index 8c6477f..bade2cd 100644 (file)
@@ -66,9 +66,6 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.RequestBody;
 
-import com.beust.jcommander.internal.Lists;
-import com.beust.jcommander.internal.Maps;
-
 @Path("/cq2gremlintest")
 public class CQ2GremlinTest extends RESTAPI {
 
@@ -106,7 +103,6 @@ public class CQ2GremlinTest extends RESTAPI {
         String realTime = headers.getRequestHeaders().getFirst("Real-Time");
         SchemaVersions schemaVersions = SpringContextAware.getBean(SchemaVersions.class);
         traversalUriHttpEntry.setHttpEntryProperties(schemaVersions.getDefaultVersion());
-        traversalUriHttpEntry.setPaginationParameters("-1", "-1");
         return processC2UnitTest(content);
     }
 
@@ -159,11 +155,11 @@ public class CQ2GremlinTest extends RESTAPI {
     }
 
     private List<Vertex> createGraph(CustomQueryTestDTO content, Graph graph) {
-        Map<String, Vertex> verticesMap = Maps.newLinkedHashMap();
+        Map<String, Vertex> verticesMap = new LinkedHashMap<>();
         // Creating all the Vertices
         content.getVerticesDtos().forEach(vertex -> {
             StringBuilder vertexIdentifier = new StringBuilder();
-            List<String> keyValues = Lists.newArrayList();
+            List<String> keyValues = new ArrayList<>();
             keyValues.add(T.id.toString());
             keyValues.add(String.format("%02d", verticesMap.size() * 10));
             AtomicInteger index = new AtomicInteger(0);
@@ -199,7 +195,7 @@ public class CQ2GremlinTest extends RESTAPI {
 
         });
 
-        List<Vertex> expectedVertices = Lists.newArrayList();
+        List<Vertex> expectedVertices = new ArrayList<>();
         content.getExpectedResultsDtos().getIds()
             .forEach(vertexId -> expectedVertices.add(verticesMap.get(vertexId)));
         return expectedVertices;
index 78ca452..8226ddd 100644 (file)
@@ -22,8 +22,6 @@ package org.onap.aai.rest;
 
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -36,10 +34,10 @@ import javax.ws.rs.core.MultivaluedHashMap;
 import javax.ws.rs.core.MultivaluedMap;
 
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
-import org.onap.aai.edges.EdgeIngestor;
+import org.javatuples.Pair;
 import org.onap.aai.exceptions.AAIException;
-import org.onap.aai.introspection.LoaderFactory;
 import org.onap.aai.introspection.ModelType;
+import org.onap.aai.query.builder.Pageable;
 import org.onap.aai.rest.db.HttpEntry;
 import org.onap.aai.rest.dsl.DslQueryProcessor;
 import org.onap.aai.rest.dsl.V1DslQueryProcessor;
@@ -49,6 +47,7 @@ import org.onap.aai.rest.enums.QueryVersion;
 import org.onap.aai.rest.search.GenericQueryProcessor;
 import org.onap.aai.rest.search.GremlinServerSingleton;
 import org.onap.aai.rest.search.QueryProcessorType;
+import org.onap.aai.rest.util.PaginationUtil;
 import org.onap.aai.serialization.db.DBSerializer;
 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
 import org.onap.aai.serialization.queryformats.Format;
@@ -122,19 +121,19 @@ public class DslConsumer extends TraversalConsumer {
                                                @RequestParam(defaultValue = "graphson") String format,
                                                @RequestParam(defaultValue = "no_op") String subgraph,
                                                @RequestParam(defaultValue = "all") String validate,
-                                               @RequestParam(defaultValue = "-1") String resultIndex,
-                                               @RequestParam(defaultValue = "-1") String resultSize,
+                                               @RequestParam(defaultValue = "-1") int resultIndex,
+                                               @RequestParam(defaultValue = "-1") int resultSize,
                                                @RequestHeader HttpHeaders headers,
                                                HttpServletRequest request) throws FileNotFoundException, AAIException {
         Set<String> roles = this.getRoles(request.getUserPrincipal());
 
         return processExecuteQuery(dslQuery, request, versionParam, format, subgraph,
-                validate, headers, resultIndex, resultSize, roles);
+                validate, headers, new Pageable(resultIndex, resultSize), roles);
     }
 
     public ResponseEntity<String> processExecuteQuery(String dslQuery, HttpServletRequest request, String versionParam,
             String queryFormat, String subgraph, String validate, HttpHeaders headers,
-            String resultIndex, String resultSize, Set<String> roles) throws FileNotFoundException, AAIException {
+           Pageable pageable, Set<String> roles) throws FileNotFoundException, AAIException {
 
         final SchemaVersion version = new SchemaVersion(versionParam);
         final String sourceOfTruth = headers.getFirst("X-FromAppId");
@@ -151,8 +150,49 @@ public class DslConsumer extends TraversalConsumer {
             }
         }
 
-        String result = executeQuery(dslQuery, request, queryFormat, subgraph, validate, queryParams, resultIndex, resultSize,
+        Pair<List<Object>,Map<String,List<String>>> executionResult = executeQuery(dslQuery, request, queryFormat, subgraph, validate, queryParams, pageable,
                 roles, version, sourceOfTruth, dslOverride);
+        List<Object> vertices = executionResult.getValue0();
+
+        String result = serializeResponse(request, queryFormat, headers, version, sourceOfTruth, queryParams, executionResult.getValue1(), vertices);
+
+        if (PaginationUtil.hasValidPaginationParams(pageable)) {
+            int totalCount = vertices.size();
+            long totalPages = PaginationUtil.getTotalPages(pageable, totalCount);
+            return ResponseEntity.ok()
+                .header("total-results", String.valueOf(totalCount))
+                .header("total-pages", String.valueOf(totalPages))
+                .body(result);
+        } else {
+            return ResponseEntity.ok(result);
+        }
+    }
+
+    private String serializeResponse(HttpServletRequest request, String queryFormat, HttpHeaders headers,
+            final SchemaVersion version, final String sourceOfTruth, MultivaluedMap<String, String> queryParameters, final Map<String, List<String>> propertiesMap,
+            List<Object> vertices) throws AAIException {
+        DBSerializer serializer =
+            new DBSerializer(version, httpEntry.getDbEngine(), ModelType.MOXY, sourceOfTruth);
+        String serverBase = request.getRequestURL().toString().replaceAll("/(v[0-9]+|latest)/.*", "/");
+        FormatFactory ff = new FormatFactory(httpEntry.getLoader(), serializer,
+                schemaVersions, this.basePath, serverBase);
+
+        MultivaluedMap<String, String> mvm = new MultivaluedHashMap<>();
+        mvm.putAll(queryParameters);
+        Format format = Format.getFormat(queryFormat);
+        if (isHistory(format)) {
+            mvm.putSingle("startTs", Long.toString(getStartTime(format, mvm)));
+            mvm.putSingle("endTs", Long.toString(getEndTime(mvm)));
+        }
+        Formatter formatter = ff.get(format, mvm);
+
+        String result = "";
+        if (propertiesMap != null && !propertiesMap.isEmpty()) {
+            result = formatter.output(vertices, propertiesMap).toString();
+        } else {
+            result = formatter.output(vertices).toString();
+        }
+
         MediaType acceptType = headers.getAccept().stream()
             .filter(Objects::nonNull)
             .filter(header -> !header.equals(MediaType.ALL))
@@ -162,25 +202,16 @@ public class DslConsumer extends TraversalConsumer {
         if (MediaType.APPLICATION_XML.isCompatibleWith(acceptType)) {
             result = xmlFormatTransformer.transform(result);
         }
-
-        if (httpEntry.isPaginated()) {
-            return ResponseEntity.ok()
-                .header("total-results", String.valueOf(httpEntry.getTotalVertices()))
-                .header("total-pages", String.valueOf(httpEntry.getTotalPaginationBuckets()))
-                .body(result);
-        } else {
-            return ResponseEntity.ok(result);
-        }
+        return result;
     }
 
-    private String executeQuery(String content, HttpServletRequest req, String queryFormat, String subgraph,
-            String validate, MultivaluedMap<String, String> queryParameters, String resultIndex, String resultSize, Set<String> roles,
+    private Pair<List<Object>,Map<String,List<String>>> executeQuery(String content, HttpServletRequest req, String queryFormat, String subgraph,
+            String validate, MultivaluedMap<String, String> queryParameters, Pageable pageable, Set<String> roles,
             final SchemaVersion version, final String sourceOfTruth, final String dslOverride)
             throws AAIException, FileNotFoundException {
         final String serverBase =
             req.getRequestURL().toString().replaceAll("/(v[0-9]+|latest)/.*", "/");
         httpEntry.setHttpEntryProperties(version, serverBase);
-        httpEntry.setPaginationParameters(resultIndex, resultSize);
 
         JsonObject input = JsonParser.parseString(content).getAsJsonObject();
         JsonElement dslElement = input.get("dsl");
@@ -215,7 +246,7 @@ public class DslConsumer extends TraversalConsumer {
         final TransactionalGraphEngine dbEngine = httpEntry.getDbEngine();
         GraphTraversalSource traversalSource =
             getTraversalSource(dbEngine, format, queryParameters, roles);
-        
+
         GenericQueryProcessor processor =
             new GenericQueryProcessor.Builder(dbEngine, gremlinServerSingleton)
                 .queryFrom(dsl, "dsl").queryProcessor(dslQueryProcessor).version(dslApiVersion)
@@ -229,34 +260,18 @@ public class DslConsumer extends TraversalConsumer {
         if (isAggregate(format)) {
             // Dedup if duplicate objects are returned in each array in the aggregate format
             // scenario.
-            List<Object> vertTempDedupedObjectList = dedupObjectInAggregateFormatResult(vertTemp);
-            vertices = httpEntry
-                    .getPaginatedVertexListForAggregateFormat(vertTempDedupedObjectList);
+            List<Object> vertTempDedupedObjectList = dedupObjectInAggregateFormatResultStreams(vertTemp);
+            vertices = PaginationUtil.hasValidPaginationParams(pageable)
+                ? vertices = PaginationUtil.getPaginatedVertexListForAggregateFormat(vertTempDedupedObjectList, pageable)
+                : vertTempDedupedObjectList;
         } else {
-            vertices = httpEntry.getPaginatedVertexList(vertTemp);
+            int startIndex = pageable.getPage() * pageable.getPageSize();
+            vertices = PaginationUtil.hasValidPaginationParams(pageable)
+                ? vertTemp.subList(startIndex, startIndex + pageable.getPageSize())
+                : vertTemp;
         }
 
-        DBSerializer serializer =
-            new DBSerializer(version, dbEngine, ModelType.MOXY, sourceOfTruth);
-        FormatFactory ff = new FormatFactory(httpEntry.getLoader(), serializer,
-                schemaVersions, this.basePath, serverBase);
-
-        MultivaluedMap<String, String> mvm = new MultivaluedHashMap<>();
-        mvm.putAll(queryParameters);
-        if (isHistory(format)) {
-            mvm.putSingle("startTs", Long.toString(getStartTime(format, mvm)));
-            mvm.putSingle("endTs", Long.toString(getEndTime(mvm)));
-        }
-        Formatter formatter = ff.get(format, mvm);
-
-        final Map<String, List<String>> propertiesMap = processor.getPropertiesMap();
-        String result = "";
-        if (propertiesMap != null && !propertiesMap.isEmpty()) {
-            result = formatter.output(vertices, propertiesMap).toString();
-        } else {
-            result = formatter.output(vertices).toString();
-        }
-        return result;
+        return Pair.with(vertices, processor.getPropertiesMap());
     }
 
     private List<Object> dedupObjectInAggregateFormatResultStreams(List<Object> vertTemp) {
@@ -265,18 +280,6 @@ public class DslConsumer extends TraversalConsumer {
             .map(o -> ((ArrayList<?>) o).stream().distinct().collect(Collectors.toList()))
             .collect(Collectors.toList());
     }
-    private List<Object> dedupObjectInAggregateFormatResult(List<Object> vertTemp) {
-        List<Object> vertTempDedupedObjectList = new ArrayList<Object>();
-        Iterator<Object> itr = vertTemp.listIterator();
-        while (itr.hasNext()) {
-            Object o = itr.next();
-            if (o instanceof ArrayList) {
-                vertTempDedupedObjectList
-                        .add(((ArrayList) o).stream().distinct().collect(Collectors.toList()));
-            }
-        }
-        return vertTempDedupedObjectList;
-    }
 
     private MultivaluedMap<String, String> toMultivaluedMap(Map<String, String[]> map) {
         MultivaluedMap<String, String> multivaluedMap = new MultivaluedHashMap<>();
index 5a2e19f..5aad81e 100644 (file)
@@ -51,11 +51,13 @@ import org.onap.aai.exceptions.AAIException;
 import org.onap.aai.introspection.ModelType;
 import org.onap.aai.logging.ErrorLogHelper;
 import org.onap.aai.parsers.query.QueryParser;
+import org.onap.aai.query.builder.Pageable;
 import org.onap.aai.rest.db.HttpEntry;
 import org.onap.aai.rest.search.CustomQueryConfig;
 import org.onap.aai.rest.search.GenericQueryProcessor;
 import org.onap.aai.rest.search.GremlinServerSingleton;
 import org.onap.aai.rest.search.QueryProcessorType;
+import org.onap.aai.rest.util.PaginationUtil;
 import org.onap.aai.restcore.HttpMethod;
 import org.onap.aai.restcore.util.URITools;
 import org.onap.aai.serialization.db.DBSerializer;
@@ -80,8 +82,8 @@ import com.google.gson.JsonParser;
 
 import io.micrometer.core.annotation.Timed;
 
-@Path("{version: v[1-9][0-9]*|latest}/query")
 @Timed
+@Path("{version: v[1-9][0-9]*|latest}/query")
 public class QueryConsumer extends TraversalConsumer {
 
     private QueryProcessorType processorType = QueryProcessorType.LOCAL_GROOVY;
@@ -112,12 +114,16 @@ public class QueryConsumer extends TraversalConsumer {
     @PUT
     @Consumes({MediaType.APPLICATION_JSON})
     @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
-    public Response executeQuery(String content, @PathParam("version") String versionParam,
+    public Response executeQuery(
+        String content,
+        @PathParam("version") String versionParam,
         @DefaultValue("graphson") @QueryParam("format") String queryFormat,
         @DefaultValue("no_op") @QueryParam("subgraph") String subgraph,
-        @Context HttpHeaders headers, @Context HttpServletRequest req, @Context UriInfo info,
-        @DefaultValue("-1") @QueryParam("resultIndex") String resultIndex,
-        @DefaultValue("-1") @QueryParam("resultSize") String resultSize) {
+        @DefaultValue("-1") @QueryParam("resultIndex") int resultIndex,
+        @DefaultValue("-1") @QueryParam("resultSize") int resultSize,
+        @Context HttpHeaders headers,
+        @Context HttpServletRequest req,
+        @Context UriInfo info) {
         Set<String> roles = this.getRoles(req.getUserPrincipal());
 
         return runner(TraversalConstants.AAI_TRAVERSAL_TIMEOUT_ENABLED,
@@ -127,14 +133,13 @@ public class QueryConsumer extends TraversalConsumer {
                 @Override
                 public Response process() {
                     return processExecuteQuery(content, req, versionParam, queryFormat, subgraph,
-                        headers, info, resultIndex, resultSize, roles);
+                        headers, info, new Pageable(resultIndex, resultSize), roles);
                 }
             });
     }
 
     public Response processExecuteQuery(String content, HttpServletRequest req, String versionParam,
-        String queryFormat, String subgraph, HttpHeaders headers, UriInfo info, String resultIndex,
-        String resultSize, Set<String> roles) {
+        String queryFormat, String subgraph, HttpHeaders headers, UriInfo info, Pageable pageable, Set<String> roles) {
 
         String sourceOfTruth = headers.getRequestHeaders().getFirst("X-FromAppId");
         String queryProcessor = headers.getRequestHeaders().getFirst("QueryProcessor");
@@ -166,7 +171,6 @@ public class QueryConsumer extends TraversalConsumer {
              * Changes for Pagination
              */
 
-            traversalUriHttpEntry.setPaginationParameters(resultIndex, resultSize);
             dbEngine = traversalUriHttpEntry.getDbEngine();
 
             if (startElement != null) {
@@ -243,7 +247,10 @@ public class QueryConsumer extends TraversalConsumer {
                     .traversalSource(isHistory(format), traversalSource).create();
             }
             List<Object> vertTemp = processor.execute(subGraphStyle);
-            List<Object> vertices = traversalUriHttpEntry.getPaginatedVertexList(vertTemp);
+            int fromIndex = pageable.getPage() * pageable.getPageSize();
+            List<Object> vertices = PaginationUtil.hasValidPaginationParams(pageable)
+                ? vertTemp.subList(fromIndex, fromIndex + pageable.getPageSize())
+                : vertTemp;
 
             DBSerializer serializer =
                 new DBSerializer(version, dbEngine, ModelType.MOXY, sourceOfTruth);
@@ -270,10 +277,12 @@ public class QueryConsumer extends TraversalConsumer {
                 result = xmlFormatTransformer.transform(result);
             }
 
-            if (traversalUriHttpEntry.isPaginated()) {
+            if (PaginationUtil.hasValidPaginationParams(pageable)) {
+                int totalCount = vertTemp.size();
+                long totalPages = PaginationUtil.getTotalPages(pageable, totalCount);
                 response = Response.status(Status.OK).type(acceptType)
-                    .header("total-results", traversalUriHttpEntry.getTotalVertices())
-                    .header("total-pages", traversalUriHttpEntry.getTotalPaginationBuckets())
+                    .header("total-results", totalCount)
+                    .header("total-pages", totalPages)
                     .entity(result).build();
             } else {
                 response = Response.status(Status.OK).type(acceptType).entity(result).build();
index 1ad1292..ce326df 100644 (file)
@@ -40,7 +40,6 @@ import org.onap.aai.setup.SchemaVersions;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.annotation.Scope;
-import org.springframework.context.annotation.ScopedProxyMode;
 import org.springframework.stereotype.Component;
 
 /**
index b64f07f..5c2129a 100644 (file)
@@ -28,9 +28,9 @@ package org.onap.aai.rest.search;
  * Licensed under the Apache License, Version 2.0 (the "License");
  * 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
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -39,9 +39,9 @@ package org.onap.aai.rest.search;
  * ============LICENSE_END=========================================================
  */
 
-import com.beust.jcommander.internal.Maps;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -58,10 +58,10 @@ public class CustomQueryTestDTO {
     private List<LinkedHashMap<String, String>> edgesDtos;
 
     @JsonProperty("optional-properties")
-    private Map<String, String> queryOptionalProperties = Maps.newHashMap();
+    private Map<String, String> queryOptionalProperties = new HashMap<>();
 
     @JsonProperty("required-properties")
-    private Map<String, String> queryRequiredProperties = Maps.newHashMap();
+    private Map<String, String> queryRequiredProperties = new HashMap<>();
 
     @JsonProperty("expected-result")
     private ExpectedResultsDto expectedResultsDtos;
diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/util/PaginationUtil.java b/aai-traversal/src/main/java/org/onap/aai/rest/util/PaginationUtil.java
new file mode 100644 (file)
index 0000000..5e8c567
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2024 Deutsche Telekom. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.rest.util;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.onap.aai.exceptions.AAIException;
+import org.onap.aai.query.builder.Pageable;
+
+public class PaginationUtil {
+
+    public static List<Object> getPaginatedVertexListForAggregateFormat(List<Object> aggregateVertexList, Pageable pageable) throws AAIException {
+        if (aggregateVertexList != null && !aggregateVertexList.isEmpty() && aggregateVertexList.size() == 1) {
+            List<Object> vertexList = (List<Object>) aggregateVertexList.get(0);
+            int fromIndex = pageable.getPage() * pageable.getPageSize();
+            List<Object> page = vertexList.subList(fromIndex, fromIndex + pageable.getPageSize());
+            return Collections.singletonList(page);
+        }
+        // If the list size is greater than 1 or if pagination is not needed, return the original list.
+        return aggregateVertexList;
+    }
+
+    public static boolean hasValidPaginationParams(Pageable pageable) {
+      return pageable.getPage() >= 0 && pageable.getPageSize() > 0;
+    }
+
+    public static long getTotalPages(Pageable pageable, long totalCount) {
+        int pageSize = pageable.getPageSize();
+        long totalPages = totalCount / pageSize;
+        // conditionally add a page for the remainder
+        if (totalCount % pageSize > 0) {
+            totalPages++;
+        }
+        return totalPages;
+    }
+}
index 5a86d92..75ecb1f 100644 (file)
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertNotNull;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.regex.Matcher;
@@ -43,7 +44,7 @@ public class PayloadUtil {
         String message = String.format("Unable to find the %s in src/test/resources", fileName);
         assertNotNull(message, inputStream);
 
-        String resource = IOUtils.toString(inputStream);
+        String resource = IOUtils.toString(inputStream, Charset.defaultCharset());
 
         inputStream.close();
         return resource;
@@ -57,7 +58,7 @@ public class PayloadUtil {
         String message = String.format("Unable to find the %s in src/test/resources", fileName);
         assertNotNull(message, inputStream);
 
-        String resource = IOUtils.toString(inputStream);
+        String resource = IOUtils.toString(inputStream, Charset.defaultCharset());
 
         inputStream.close();
         return resource;
@@ -77,7 +78,7 @@ public class PayloadUtil {
         if (cache.containsKey(fileName)) {
             resource = cache.get(fileName);
         } else {
-            resource = IOUtils.toString(inputStream);
+            resource = IOUtils.toString(inputStream, Charset.defaultCharset());
             cache.put(fileName, resource);
         }
 
@@ -112,7 +113,7 @@ public class PayloadUtil {
             .format("Unable to find the %s in src/test/resources/payloads/named-queries", fileName);
         assertNotNull(message, inputStream);
 
-        String resource = IOUtils.toString(inputStream);
+        String resource = IOUtils.toString(inputStream, Charset.defaultCharset());
 
         inputStream.close();
         return resource;
diff --git a/aai-traversal/src/test/java/org/onap/aai/WebClientConfiguration.java b/aai-traversal/src/test/java/org/onap/aai/WebClientConfiguration.java
new file mode 100644 (file)
index 0000000..73239c6
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * ============LICENSE_START=======================================================
+ * org.onap.aai
+ * ================================================================================
+ * Copyright © 2024 Deutsche Telekom. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * 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
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aai;
+
+import java.time.Duration;
+import java.util.Collections;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+@TestConfiguration
+public class WebClientConfiguration {
+
+  @Lazy
+  @Bean
+  WebTestClient webTestClient(@LocalServerPort int port) {
+    return WebTestClient.bindToServer()
+      .baseUrl("http://localhost:" + port)
+      .responseTimeout(Duration.ofSeconds(300))
+      .defaultHeaders(headers -> {
+        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+        headers.set("Real-Time", "true");
+        headers.set("X-FromAppId", "JUNIT");
+        headers.set("X-TransactionId", "JUNIT");
+        headers.setBasicAuth("AAI", "AAI");
+      })
+      .build();
+  }
+}
index a4fb0a7..ed4a6e6 100644 (file)
@@ -29,11 +29,9 @@ import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
@@ -42,6 +40,7 @@ import org.janusgraph.core.JanusGraphTransaction;
 import org.junit.Assert;
 import org.junit.Test;
 import org.onap.aai.PayloadUtil;
+import org.onap.aai.WebClientConfiguration;
 import org.onap.aai.dbmap.AAIGraph;
 import org.onap.aai.entities.AAIErrorResponse;
 import org.onap.aai.entities.ServiceException;
@@ -49,23 +48,26 @@ import org.onap.aai.util.AAIConfig;
 import org.onap.aai.util.TraversalConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Import;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.test.web.reactive.server.WebTestClient;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParser;
 
+@Import(WebClientConfiguration.class)
 public class DslConsumerTest extends AbstractSpringRestTest {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(DslConsumerTest.class);
-    private static final ObjectMapper objectMapper = new ObjectMapper();
 
+    @Autowired WebTestClient webClient;
 
     @Override
     public void createTestGraph() {
@@ -207,6 +209,46 @@ public class DslConsumerTest extends AbstractSpringRestTest {
         assertThat(responseEntity.getBody(), is(containsString("<results><result>")));
     }
 
+    @Test
+    public void thatResultsCanBePaginated() throws Exception {
+        String path = "/aai/v14/dsl";
+        Map<String, String> dslQueryMap = Collections.singletonMap("dsl-query", "pserver*('hostname','test-pserver-dsl')");
+
+        String payload = PayloadUtil.getTemplatePayload("dsl-query.json", dslQueryMap);
+
+        webClient.put()
+            .uri(uriBuilder -> uriBuilder
+                .path(path)
+                .queryParam("format", "console")
+                .build())
+            .contentType(MediaType.APPLICATION_JSON)
+            .bodyValue(payload)
+            .exchange()
+            .expectStatus().isOk();
+
+        String responseEntity = webClient.put()
+            .uri(uriBuilder -> uriBuilder
+                .path(path)
+                .queryParam("format", "console")
+                .queryParam("resultIndex", 0)
+                .queryParam("resultSize", 1)
+                .build())
+            .contentType(MediaType.APPLICATION_JSON)
+            .accept(MediaType.APPLICATION_XML)
+            .bodyValue(payload)
+            .exchange()
+            .expectStatus().isOk()
+            .expectHeader().valueEquals("total-results", 1)
+            .expectHeader().valueEquals("total-pages", 1)
+            .returnResult(String.class)
+            .getResponseBody()
+            .blockFirst();
+
+        assertThat(responseEntity,
+            is(not(containsString("<result><result>"))));
+        assertThat(responseEntity, is(containsString("<results><result>")));
+    }
+
     @Test
     public void thatWildcardContentTypeCanBeUsed() throws Exception {
 
index f305e14..f43c91f 100644 (file)
@@ -182,7 +182,7 @@ public class GfpVserverDataStoredQueryTest extends AAISetup {
             .thenReturn(new StringBuffer("https://localhost:8446" + query));
 
         Response response = queryConsumer.executeQuery(payload, version.toString(),
-            "resource_and_url", "" + "no_op", httpHeaders, mockRequest, uriInfo, "-1", "-1");
+            "resource_and_url", "" + "no_op", -1, -1, httpHeaders, mockRequest, uriInfo);
 
         String entity = response.getEntity().toString();
         assertEquals("Expected the response to be 200 but got this returned: "
@@ -214,7 +214,7 @@ public class GfpVserverDataStoredQueryTest extends AAISetup {
             .thenReturn(new StringBuffer("https://localhost:8446" + query));
 
         Response response = queryConsumer.executeQuery(payload, version.toString(),
-            "resource_and_url", "" + "no_op", httpHeaders, mockRequest, uriInfo, "-1", "-1");
+            "resource_and_url", "" + "no_op", -1, -1, httpHeaders, mockRequest, uriInfo);
 
         String entity = response.getEntity().toString();
 
@@ -247,7 +247,7 @@ public class GfpVserverDataStoredQueryTest extends AAISetup {
             .thenReturn(new StringBuffer("https://localhost:8446" + query));
 
         Response response = queryConsumer.executeQuery(payload, version.toString(),
-            "resource_and_url", "" + "no_op", httpHeaders, mockRequest, uriInfo, "-1", "-1");
+            "resource_and_url", "" + "no_op", -1, -1, httpHeaders, mockRequest, uriInfo);
 
         String entity = response.getEntity().toString();
 
index cfb81d4..a6da4fc 100644 (file)
@@ -42,6 +42,7 @@ import org.onap.aai.HttpTestUtil;
 import org.onap.aai.PayloadUtil;
 import org.onap.aai.TraversalApp;
 import org.onap.aai.TraversalTestConfiguration;
+import org.onap.aai.WebClientConfiguration;
 import org.onap.aai.config.PropertyPasswordConfiguration;
 import org.onap.aai.dbmap.AAIGraph;
 import org.onap.aai.exceptions.AAIException;
@@ -55,6 +56,7 @@ import org.springframework.http.*;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
 import org.springframework.web.client.RestTemplate;
 
 @RunWith(SpringRunner.class)
@@ -63,7 +65,7 @@ import org.springframework.web.client.RestTemplate;
     classes = TraversalApp.class)
 @TestPropertySource(locations = "classpath:application-test.properties")
 @ContextConfiguration(initializers = PropertyPasswordConfiguration.class)
-@Import(TraversalTestConfiguration.class)
+@Import({TraversalTestConfiguration.class, WebClientConfiguration.class})
 public class QueryConsumerTest {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(QueryConsumerTest.class);
@@ -71,6 +73,8 @@ public class QueryConsumerTest {
 
     private String pserverUri;
 
+    @Autowired WebTestClient webTestClient;
+
     @Autowired
     RestTemplate restTemplate;
 
@@ -124,7 +128,7 @@ public class QueryConsumerTest {
         Response response = httpTestUtil.doPut(complexUri, complexPayload);
     }
 
-    // @Test
+    @Test
     public void testRequiredAGood() throws Exception {
         String endpoint = "/aai/v14/query?format=pathed";
         Map<String, String> cloudRegionMap = new HashMap<>();
@@ -152,7 +156,7 @@ public class QueryConsumerTest {
 
         String payload = PayloadUtil.getTemplatePayload("custom-query.json", customQueryMap);
         httpEntity = new HttpEntity(payload, headers);
-        ResponseEntity responseEntity =
+        ResponseEntity<String> responseEntity =
             restTemplate.exchange(baseUrl + endpoint, HttpMethod.PUT, httpEntity, String.class);
         LOGGER.info("Response of custom query : {}", responseEntity.getBody().toString());
         assertThat(responseEntity.getStatusCode(), is(HttpStatus.OK));
@@ -161,6 +165,55 @@ public class QueryConsumerTest {
         // containsString(customerUri2));
     }
 
+    @Test
+    public void thatPaginatedResponseCanBeRetrieved() throws Exception {
+        Map<String, String> cloudRegionMap = new HashMap<>();
+        cloudRegionMap.put("cloud-owner", "test-owner-id1111");
+        cloudRegionMap.put("cloud-region-id", "test-region-id1111");
+        cloudRegionMap.put("tenant-id", "test-tenant-id1111");
+        cloudRegionMap.put("tenant-name", "test-tenant-name-id1111");
+        cloudRegionMap.put("vserver-id", "some-vserver-id-id1111");
+        cloudRegionMap.put("vserver-name", "test-vserver-name-id1111");
+        cloudRegionMap.put("pserver-uri", pserverUri);
+        cloudRegionUri =
+            "/aai/v14/cloud-infrastructure/cloud-regions/cloud-region/test-owner-id1111/test-region-id1111";
+        addCloudRegion(cloudRegionMap, cloudRegionUri);
+
+        Map<String, String> complexMap = new HashMap<>();
+        complexMap.put("physical-location-id", "location-1111");
+        complexMap.put("cloud-region-uri", cloudRegionUri);
+        String complexUri = "/aai/v14/cloud-infrastructure/complexes/complex/location-1111";
+        addComplex(complexMap, complexUri);
+
+        Map<String, String> customQueryMap = new HashMap<>();
+        customQueryMap.put("start", "cloud-infrastructure/cloud-regions");
+        customQueryMap.put("query", "cloud-region-sites?owner=test-owner-id1111");
+
+        String payload = PayloadUtil.getTemplatePayload("custom-query.json", customQueryMap);
+        String path = "/aai/v14/query";
+
+        String response = webTestClient.put()
+            .uri(uriBuilder -> uriBuilder
+                .path(path)
+                .queryParam("format", "pathed")
+                .queryParam("resultIndex", 0)
+                .queryParam("resultSize", 1)
+                .build())
+            .contentType(MediaType.APPLICATION_JSON)
+            .bodyValue(payload)
+            .exchange()
+            .expectStatus().isOk()
+            .expectHeader().valueEquals("total-results", 2)
+            .expectHeader().valueEquals("total-pages", 2)
+            .returnResult(String.class)
+            .getResponseBody()
+            .blockFirst();
+
+        String expectedResponse = "{\"results\":[{\"resource-type\":\"cloud-region\",\"resource-link\":\"/aai/v14/cloud-infrastructure/cloud-regions/cloud-region/test-owner-id1111/test-region-id1111\"}]}";
+        assertEquals(expectedResponse, response);
+
+    }
+
     @Test
     public void testRequiredBad() throws Exception {
         String endpoint = "/aai/v14/query?format=pathed";
diff --git a/pom.xml b/pom.xml
index 6643722..6ac42c4 100644 (file)
--- a/pom.xml
+++ b/pom.xml
     <parent>
         <groupId>org.onap.aai.aai-common</groupId>
         <artifactId>aai-parent</artifactId>
-        <version>1.14.2-SNAPSHOT</version>
+        <version>1.14.3-SNAPSHOT</version>
     </parent>
     <groupId>org.onap.aai.traversal</groupId>
     <artifactId>traversal</artifactId>
-    <version>1.14.2-SNAPSHOT</version>
+    <version>1.14.3-SNAPSHOT</version>
     <name>aai-traversal</name>
     <packaging>pom</packaging>
     <modules>
@@ -41,7 +41,7 @@
             Nexus Proxy Properties and Snapshot Locations
             Ideally this can be overwritten at runtime per internal environment specific values at runtime
         -->
-        <aai.common.version>1.14.2</aai.common.version>
+        <aai.common.version>1.14.3-SNAPSHOT</aai.common.version>
         <nexusproxy>https://nexus.onap.org</nexusproxy>
         <site.path>/content/sites/site/org/onap/aai/traversal/${project.artifactId}/${project.version}</site.path>
         <release.path>/content/repositories/releases/</release.path>
index 466ec95..a9c3f22 100644 (file)
@@ -5,7 +5,7 @@
 
 major_version=1
 minor_version=14
-patch_version=2
+patch_version=3
 
 base_version=${major_version}.${minor_version}.${patch_version}