diff options
15 files changed, 330 insertions, 148 deletions
diff --git a/aai-traversal/pom.xml b/aai-traversal/pom.xml index 59527d0..99e5c2d 100644 --- a/aai-traversal/pom.xml +++ b/aai-traversal/pom.xml @@ -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> @@ -94,18 +94,17 @@ <!-- 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> @@ -324,23 +323,18 @@ </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> @@ -472,6 +466,11 @@ </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> </dependency> @@ -494,34 +493,6 @@ <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> </dependency> @@ -682,6 +653,11 @@ </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> </dependency> <dependency> diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/CQ2Gremlin.java b/aai-traversal/src/main/java/org/onap/aai/rest/CQ2Gremlin.java index c8f1f0b..6b8382b 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/CQ2Gremlin.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/CQ2Gremlin.java @@ -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(); diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/CQ2GremlinTest.java b/aai-traversal/src/main/java/org/onap/aai/rest/CQ2GremlinTest.java index 8c6477f..bade2cd 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/CQ2GremlinTest.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/CQ2GremlinTest.java @@ -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; diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java b/aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java index 78ca452..8226ddd 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/DslConsumer.java @@ -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<>(); diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java b/aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java index 5a2e19f..5aad81e 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/QueryConsumer.java @@ -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(); diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/dsl/v1/DslListener.java b/aai-traversal/src/main/java/org/onap/aai/rest/dsl/v1/DslListener.java index 1ad1292..ce326df 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/dsl/v1/DslListener.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/dsl/v1/DslListener.java @@ -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; /** diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryTestDTO.java b/aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryTestDTO.java index b64f07f..5c2129a 100644 --- a/aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryTestDTO.java +++ b/aai-traversal/src/main/java/org/onap/aai/rest/search/CustomQueryTestDTO.java @@ -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 index 0000000..5e8c567 --- /dev/null +++ b/aai-traversal/src/main/java/org/onap/aai/rest/util/PaginationUtil.java @@ -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; + } +} diff --git a/aai-traversal/src/test/java/org/onap/aai/PayloadUtil.java b/aai-traversal/src/test/java/org/onap/aai/PayloadUtil.java index 5a86d92..75ecb1f 100644 --- a/aai-traversal/src/test/java/org/onap/aai/PayloadUtil.java +++ b/aai-traversal/src/test/java/org/onap/aai/PayloadUtil.java @@ -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 index 0000000..73239c6 --- /dev/null +++ b/aai-traversal/src/test/java/org/onap/aai/WebClientConfiguration.java @@ -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(); + } +} diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/DslConsumerTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/DslConsumerTest.java index a4fb0a7..ed4a6e6 100644 --- a/aai-traversal/src/test/java/org/onap/aai/rest/DslConsumerTest.java +++ b/aai-traversal/src/test/java/org/onap/aai/rest/DslConsumerTest.java @@ -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() { @@ -208,6 +210,46 @@ public class DslConsumerTest extends AbstractSpringRestTest { } @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 { String endpoint = "/aai/v14/dsl?format=console"; diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/GfpVserverDataStoredQueryTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/GfpVserverDataStoredQueryTest.java index f305e14..f43c91f 100644 --- a/aai-traversal/src/test/java/org/onap/aai/rest/GfpVserverDataStoredQueryTest.java +++ b/aai-traversal/src/test/java/org/onap/aai/rest/GfpVserverDataStoredQueryTest.java @@ -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(); diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/QueryConsumerTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/QueryConsumerTest.java index cfb81d4..a6da4fc 100644 --- a/aai-traversal/src/test/java/org/onap/aai/rest/QueryConsumerTest.java +++ b/aai-traversal/src/test/java/org/onap/aai/rest/QueryConsumerTest.java @@ -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)); @@ -162,6 +166,55 @@ public class QueryConsumerTest { } @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"; Map<String, String> cloudRegionMap = new HashMap<>(); @@ -26,11 +26,11 @@ <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> diff --git a/version.properties b/version.properties index 466ec95..a9c3f22 100644 --- a/version.properties +++ b/version.properties @@ -5,7 +5,7 @@ major_version=1 minor_version=14 -patch_version=2 +patch_version=3 base_version=${major_version}.${minor_version}.${patch_version} |