diff options
Diffstat (limited to 'aai-core/src/main')
26 files changed, 1673 insertions, 474 deletions
diff --git a/aai-core/src/main/java/org/onap/aai/config/SwaggerGenerationConfiguration.java b/aai-core/src/main/java/org/onap/aai/config/SwaggerGenerationConfiguration.java index 9aae5a62..dea284c8 100644 --- a/aai-core/src/main/java/org/onap/aai/config/SwaggerGenerationConfiguration.java +++ b/aai-core/src/main/java/org/onap/aai/config/SwaggerGenerationConfiguration.java @@ -37,31 +37,28 @@ import org.springframework.context.annotation.Scope; @Configuration public class SwaggerGenerationConfiguration { - @Value("${schema.uri.base.path}") - private String basePath; + @Value("${schema.uri.base.path}") + private String basePath; - @Value("${schema.xsd.maxoccurs:5000}") - private String maxOccurs; - - @Bean - @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public NodesYAMLfromOXM nodesYamlFromOXM(SchemaVersions schemaVersions, NodeIngestor nodeIngestor, EdgeIngestor edgeIngestor) { - NodesYAMLfromOXM nodesYamlFromOXM = new NodesYAMLfromOXM(basePath, schemaVersions, nodeIngestor, edgeIngestor); - return nodesYamlFromOXM; - } - - @Bean - @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public HTMLfromOXM htmlFromOXM(SchemaVersions schemaVersions, NodeIngestor nodeIngestor, EdgeIngestor edgeIngestor) { - HTMLfromOXM htmlFromOXM = new HTMLfromOXM(maxOccurs, schemaVersions, nodeIngestor, edgeIngestor); - return htmlFromOXM; - } - - @Bean - @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) - public YAMLfromOXM yamlFromOXM(SchemaVersions schemaVersions, NodeIngestor nodeIngestor, EdgeIngestor edgeIngestor) { - YAMLfromOXM yamlFromOXM = new YAMLfromOXM(basePath, schemaVersions, nodeIngestor, edgeIngestor); - return yamlFromOXM; - } + @Value("${schema.xsd.maxoccurs:5000}") + private String maxOccurs; + + @Bean + @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public NodesYAMLfromOXM nodesYamlFromOXM(SchemaVersions schemaVersions, NodeIngestor nodeIngestor, EdgeIngestor edgeIngestor) { + return new NodesYAMLfromOXM(basePath, schemaVersions, nodeIngestor, edgeIngestor); + } + + @Bean + @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public HTMLfromOXM htmlFromOXM(SchemaVersions schemaVersions, NodeIngestor nodeIngestor, EdgeIngestor edgeIngestor) { + return new HTMLfromOXM(maxOccurs, schemaVersions, nodeIngestor, edgeIngestor); + } + + @Bean + @Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE) + public YAMLfromOXM yamlFromOXM(SchemaVersions schemaVersions, NodeIngestor nodeIngestor, EdgeIngestor edgeIngestor) { + return new YAMLfromOXM(basePath, schemaVersions, nodeIngestor, edgeIngestor); + } } diff --git a/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialIO.java b/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialIO.java new file mode 100644 index 00000000..915db69c --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialIO.java @@ -0,0 +1,158 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.dbgen; + +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.io.Io; +import org.apache.tinkerpop.gremlin.structure.io.IoRegistry; +import org.apache.tinkerpop.gremlin.structure.io.Mapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONReader; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONWriter; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Optional; +import java.util.function.Consumer; + +/** + * Constructs GraphSON IO implementations given a {@link Graph} and {@link IoRegistry}. Implementers of the {@link Graph} + * interfaces should see the {@link GraphSONMapper} for information on the expectations for the {@link IoRegistry}. + * + * @author Stephen Mallette (http://stephen.genoprime.com) + */ +public final class GraphSONPartialIO implements Io<GraphSONPartialReader.Builder, GraphSONWriter.Builder, GraphSONMapper.Builder> { + private final IoRegistry registry; + private final Graph graph; + private final Optional<Consumer<Mapper.Builder>> onMapper; + private final GraphSONVersion version; + + private GraphSONPartialIO(final Builder builder) { + this.registry = builder.registry; + this.graph = builder.graph; + this.onMapper = Optional.ofNullable(builder.onMapper); + this.version = builder.version; + } + + /** + * {@inheritDoc} + */ + @Override + public GraphSONPartialReader.Builder reader() { + return GraphSONPartialReader.build().mapper(mapper().create()); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphSONWriter.Builder writer() { + return GraphSONWriter.build().mapper(mapper().create()); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphSONMapper.Builder mapper() { + final GraphSONMapper.Builder builder = (null == this.registry) ? + GraphSONMapper.build().version(version) : GraphSONMapper.build().version(version).addRegistry(this.registry); + onMapper.ifPresent(c -> c.accept(builder)); + return builder; + } + + /** + * {@inheritDoc} + */ + @Override + public void writeGraph(final String file) throws IOException { + try (final OutputStream out = new FileOutputStream(file)) { + writer().create().writeGraph(out, graph); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void readGraph(final String file) throws IOException { + try (final InputStream in = new FileInputStream(file)) { + reader().create().readGraph(in, graph); + } + } + + /** + * Create a new builder using the default version of GraphSON. + */ + public static Io.Builder<GraphSONPartialIO> build() { + return build(GraphSONVersion.V1_0); + } + + /** + * Create a new builder using the specified version of GraphSON. + */ + public static Io.Builder<GraphSONPartialIO> build(final GraphSONVersion version) { + return new Builder(version); + } + + public final static class Builder implements Io.Builder<GraphSONPartialIO> { + + private IoRegistry registry = null; + private Graph graph; + private Consumer<Mapper.Builder> onMapper = null; + private final GraphSONVersion version; + + Builder(final GraphSONVersion version) { + this.version = version; + } + + /** + * @deprecated As of release 3.2.2, replaced by {@link #onMapper(Consumer)}. + */ + @Deprecated + @Override + public Io.Builder<GraphSONPartialIO> registry(final IoRegistry registry) { + this.registry = registry; + return this; + } + + @Override + public Io.Builder<? extends Io> onMapper(final Consumer<Mapper.Builder> onMapper) { + this.onMapper = onMapper; + return this; + } + + @Override + public Io.Builder<GraphSONPartialIO> graph(final Graph g) { + this.graph = g; + return this; + } + + @Override + public GraphSONPartialIO create() { + if (null == graph) throw new IllegalArgumentException("The graph argument was not specified"); + return new GraphSONPartialIO(this); + } + } +} diff --git a/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialReader.java b/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialReader.java new file mode 100644 index 00000000..2088286d --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/dbgen/GraphSONPartialReader.java @@ -0,0 +1,354 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.dbgen; + +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Property; +import org.apache.tinkerpop.gremlin.structure.T; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; +import org.apache.tinkerpop.gremlin.structure.io.GraphReader; +import org.apache.tinkerpop.gremlin.structure.io.GraphWriter; +import org.apache.tinkerpop.gremlin.structure.io.Mapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONMapper; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONReader; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens; +import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONVersion; +import org.apache.tinkerpop.gremlin.structure.io.gryo.GryoWriter; +import org.apache.tinkerpop.gremlin.structure.util.Attachable; +import org.apache.tinkerpop.gremlin.structure.util.Host; +import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph; +import org.apache.tinkerpop.gremlin.util.function.FunctionUtils; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; +import org.apache.tinkerpop.shaded.jackson.core.type.TypeReference; +import org.apache.tinkerpop.shaded.jackson.databind.JsonNode; +import org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper; +import org.apache.tinkerpop.shaded.jackson.databind.node.JsonNodeType; +import org.onap.aai.dbmap.InMemoryGraph; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * This is a Wrapper around the GraphsonReader class + * The idea is to rewrite methods that are customized for A&AI + * GraphsonReader is a final class . hence the use of the Wrapper + * instead of inheriting-overwriting + * + * + */ +public final class GraphSONPartialReader implements GraphReader { + private final ObjectMapper mapper ; + private final long batchSize ; + private final GraphSONVersion version ; + private boolean unwrapAdjacencyList = false; + private final GraphSONReader reader; + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(InMemoryGraph.class); + + final TypeReference<Map<String, Object>> mapTypeReference = new TypeReference<Map<String, Object>>() { + }; + + private GraphSONPartialReader(final Builder builder) { + mapper = builder.mapper.createMapper(); + batchSize = builder.batchSize; + unwrapAdjacencyList = builder.unwrapAdjacencyList; + version = ((GraphSONMapper)builder.mapper).getVersion(); + reader = GraphSONReader.build().create(); + } + + /** + * Read data into a {@link Graph} from output generated by any of the {@link GraphSONWriter} {@code writeVertex} or + * {@code writeVertices} methods or by {@link GryoWriter#writeGraph(OutputStream, Graph)}. + * + * @param inputStream a stream containing an entire graph of vertices and edges as defined by the accompanying + * {@link GraphSONWriter#writeGraph(OutputStream, Graph)}. + * @param graphToWriteTo the graph to write to when reading from the stream. + */ + @Override + public void readGraph(final InputStream inputStream, final Graph graphToWriteTo) throws IOException { + // dual pass - create all vertices and store to cache the ids. then create edges. as long as we don't + // have vertex labels in the output we can't do this single pass + LOGGER.info("Read the Partial Graph"); + final Map<StarGraph.StarVertex,Vertex> cache = new HashMap<>(); + final AtomicLong counter = new AtomicLong(0); + + final boolean supportsTx = graphToWriteTo.features().graph().supportsTransactions(); + final Graph.Features.EdgeFeatures edgeFeatures = graphToWriteTo.features().edge(); + + readVertexStrings(inputStream).<Vertex>map(FunctionUtils.wrapFunction(line -> readVertex(new ByteArrayInputStream(line.getBytes()), null, null, Direction.IN))).forEach(vertex -> { + try{ + final Attachable<Vertex> attachable = (Attachable<Vertex>) vertex; + cache.put((StarGraph.StarVertex) attachable.get(), attachable.attach(Attachable.Method.create(graphToWriteTo))); + if (supportsTx && counter.incrementAndGet() % batchSize == 0) + graphToWriteTo.tx().commit(); + } + catch(Exception ex){ + LOGGER.info("Error in reading vertex from graphson"+vertex.toString()); + } + }); + + cache.entrySet().forEach(kv -> kv.getKey().edges(Direction.IN).forEachRemaining(e -> { + try{ + // can't use a standard Attachable attach method here because we have to use the cache for those + // graphs that don't support userSuppliedIds on edges. note that outVertex/inVertex methods return + // StarAdjacentVertex whose equality should match StarVertex. + final Vertex cachedOutV = cache.get(e.outVertex()); + final Vertex cachedInV = cache.get(e.inVertex()); + + if(cachedOutV != null && cachedInV != null){ + + final Edge newEdge = edgeFeatures.willAllowId(e.id()) ? cachedOutV.addEdge(e.label(), cachedInV, T.id, e.id()) : cachedOutV.addEdge(e.label(), cachedInV); + e.properties().forEachRemaining(p -> newEdge.property(p.key(), p.value())); + } + else{ + LOGGER.debug("Ghost edges from "+ cachedOutV + " to "+ cachedInV); + + } + if (supportsTx && counter.incrementAndGet() % batchSize == 0) + graphToWriteTo.tx().commit(); + } + catch(Exception ex){ + LOGGER.info("Error in writing vertex into graph"+e.toString()); + } + })); + + if (supportsTx) graphToWriteTo.tx().commit(); + } + + /** + * Read {@link Vertex} objects from output generated by any of the {@link GraphSONWriter} {@code writeVertex} or + * {@code writeVertices} methods or by {@link GraphSONWriter#writeGraph(OutputStream, Graph)}. + * + * @param inputStream a stream containing at least one {@link Vertex} as defined by the accompanying + * {@link GraphWriter#writeVertices(OutputStream, Iterator, Direction)} or + * {@link GraphWriter#writeVertices(OutputStream, Iterator)} methods. + * @param vertexAttachMethod a function that creates re-attaches a {@link Vertex} to a {@link Host} object. + * @param edgeAttachMethod a function that creates re-attaches a {@link Edge} to a {@link Host} object. + * @param attachEdgesOfThisDirection only edges of this direction are passed to the {@code edgeMaker}. + */ + @Override + public Iterator<Vertex> readVertices(final InputStream inputStream, + final Function<Attachable<Vertex>, Vertex> vertexAttachMethod, + final Function<Attachable<Edge>, Edge> edgeAttachMethod, + final Direction attachEdgesOfThisDirection) throws IOException { + // return readVertexStrings(inputStream).<Vertex>map(FunctionUtils.wrapFunction(line -> readVertex(new ByteArrayInputStream(line.getBytes()), vertexAttachMethod, edgeAttachMethod, attachEdgesOfThisDirection))).iterator(); + return reader.readVertices(inputStream, vertexAttachMethod, edgeAttachMethod, attachEdgesOfThisDirection); + + } + + /** + * Read a {@link Vertex} from output generated by any of the {@link GraphSONWriter} {@code writeVertex} or + * {@code writeVertices} methods or by {@link GraphSONWriter#writeGraph(OutputStream, Graph)}. + * + * @param inputStream a stream containing at least a single vertex as defined by the accompanying + * {@link GraphWriter#writeVertex(OutputStream, Vertex)}. + * @param vertexAttachMethod a function that creates re-attaches a {@link Vertex} to a {@link Host} object. + */ + @Override + public Vertex readVertex(final InputStream inputStream, final Function<Attachable<Vertex>, Vertex> vertexAttachMethod) throws IOException { + return reader.readVertex(inputStream, vertexAttachMethod); + } + + /** + * Read a {@link Vertex} from output generated by any of the {@link GraphSONWriter} {@code writeVertex} or + * {@code writeVertices} methods or by {@link GraphSONWriter#writeGraph(OutputStream, Graph)}. + * + * @param inputStream a stream containing at least one {@link Vertex} as defined by the accompanying + * {@link GraphWriter#writeVertices(OutputStream, Iterator, Direction)} method. + * @param vertexAttachMethod a function that creates re-attaches a {@link Vertex} to a {@link Host} object. + * @param edgeAttachMethod a function that creates re-attaches a {@link Edge} to a {@link Host} object. + * @param attachEdgesOfThisDirection only edges of this direction are passed to the {@code edgeMaker}. + */ + @Override + public Vertex readVertex(final InputStream inputStream, + final Function<Attachable<Vertex>, Vertex> vertexAttachMethod, + final Function<Attachable<Edge>, Edge> edgeAttachMethod, + final Direction attachEdgesOfThisDirection) throws IOException { + + return reader.readVertex(inputStream, vertexAttachMethod, edgeAttachMethod, attachEdgesOfThisDirection); + } + + /** + * Read an {@link Edge} from output generated by {@link GraphSONWriter#writeEdge(OutputStream, Edge)} or via + * an {@link Edge} passed to {@link GraphSONWriter#writeObject(OutputStream, Object)}. + * + * @param inputStream a stream containing at least one {@link Edge} as defined by the accompanying + * {@link GraphWriter#writeEdge(OutputStream, Edge)} method. + * @param edgeAttachMethod a function that creates re-attaches a {@link Edge} to a {@link Host} object. + */ + @Override + public Edge readEdge(final InputStream inputStream, final Function<Attachable<Edge>, Edge> edgeAttachMethod) throws IOException { + /*if (version == GraphSONVersion.v1_0) { + final Map<String, Object> edgeData = mapper.readValue(inputStream, mapTypeReference); + + final Map<String, Object> edgeProperties = edgeData.containsKey(GraphSONTokens.PROPERTIES) ? + (Map<String, Object>) edgeData.get(GraphSONTokens.PROPERTIES) : Collections.EMPTY_MAP; + final DetachedEdge edge = new DetachedEdge(edgeData.get(GraphSONTokens.ID), + edgeData.get(GraphSONTokens.LABEL).toString(), + edgeProperties, + Pair.with(edgeData.get(GraphSONTokens.OUT), edgeData.get(GraphSONTokens.OUT_LABEL).toString()), + Pair.with(edgeData.get(GraphSONTokens.IN), edgeData.get(GraphSONTokens.IN_LABEL).toString())); + + return edgeAttachMethod.apply(edge); + } else { + return edgeAttachMethod.apply((DetachedEdge) mapper.readValue(inputStream, Edge.class)); + }*/ + return reader.readEdge(inputStream, edgeAttachMethod); + } + + /** + * Read a {@link VertexProperty} from output generated by + * {@link GraphSONWriter#writeVertexProperty(OutputStream, VertexProperty)} or via an {@link VertexProperty} passed + * to {@link GraphSONWriter#writeObject(OutputStream, Object)}. + * + * @param inputStream a stream containing at least one {@link VertexProperty} as written by the accompanying + * {@link GraphWriter#writeVertexProperty(OutputStream, VertexProperty)} method. + * @param vertexPropertyAttachMethod a function that creates re-attaches a {@link VertexProperty} to a + * {@link Host} object. + */ + @Override + public VertexProperty readVertexProperty(final InputStream inputStream, + final Function<Attachable<VertexProperty>, VertexProperty> vertexPropertyAttachMethod) throws IOException { + /*if (version == GraphSONVersion.v1_0) { + final Map<String, Object> vpData = mapper.readValue(inputStream, mapTypeReference); + final Map<String, Object> metaProperties = (Map<String, Object>) vpData.get(GraphSONTokens.PROPERTIES); + final DetachedVertexProperty vp = new DetachedVertexProperty(vpData.get(GraphSONTokens.ID), + vpData.get(GraphSONTokens.LABEL).toString(), + vpData.get(GraphSONTokens.VALUE), metaProperties); + return vertexPropertyAttachMethod.apply(vp); + } else { + return vertexPropertyAttachMethod.apply((DetachedVertexProperty) mapper.readValue(inputStream, VertexProperty.class)); + }*/ + return reader.readVertexProperty(inputStream, vertexPropertyAttachMethod); + } + + /** + * Read a {@link Property} from output generated by {@link GraphSONWriter#writeProperty(OutputStream, Property)} or + * via an {@link Property} passed to {@link GraphSONWriter#writeObject(OutputStream, Object)}. + * + * @param inputStream a stream containing at least one {@link Property} as written by the accompanying + * {@link GraphWriter#writeProperty(OutputStream, Property)} method. + * @param propertyAttachMethod a function that creates re-attaches a {@link Property} to a {@link Host} object. + */ + @Override + public Property readProperty(final InputStream inputStream, + final Function<Attachable<Property>, Property> propertyAttachMethod) throws IOException { + /*if (version == GraphSONVersion.v1_0) { + final Map<String, Object> propertyData = mapper.readValue(inputStream, mapTypeReference); + final DetachedProperty p = new DetachedProperty(propertyData.get(GraphSONTokens.KEY).toString(), propertyData.get(GraphSONTokens.VALUE)); + return propertyAttachMethod.apply(p); + } else { + return propertyAttachMethod.apply((DetachedProperty) mapper.readValue(inputStream, Property.class)); + }*/ + return reader.readProperty(inputStream, propertyAttachMethod); + } + + /** + * {@inheritDoc} + */ + @Override + public <C> C readObject(final InputStream inputStream, final Class<? extends C> clazz) throws IOException { + return mapper.readValue(inputStream, clazz); + } + + private Stream<String> readVertexStrings(final InputStream inputStream) throws IOException { + if (unwrapAdjacencyList) { + final JsonNode root = mapper.readTree(inputStream); + final JsonNode vertices = root.get(GraphSONTokens.VERTICES); + if (!vertices.getNodeType().equals(JsonNodeType.ARRAY)) throw new IOException(String.format("The '%s' key must be an array", GraphSONTokens.VERTICES)); + return IteratorUtils.stream(vertices.elements()).map(Object::toString); + } else { + final BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); + return br.lines(); + } + + } + + + public static Builder build() { + return new Builder(); + } + + public final static class Builder implements ReaderBuilder<GraphSONPartialReader> { + private long batchSize = 10000; + + private Mapper<ObjectMapper> mapper = GraphSONMapper.build().create(); + private boolean unwrapAdjacencyList = false; + + + private Builder() {} + + /** + * Number of mutations to perform before a commit is executed when using + * {@link GraphSONPartialReader#readGraph(InputStream, Graph)}. + */ + public Builder batchSize(final long batchSize) { + this.batchSize = batchSize; + return this; + } + + /** + * Override all of the {@link GraphSONMapper} builder + * options with this mapper. If this value is set to something other than null then that value will be + * used to construct the writer. + */ + public Builder mapper(final Mapper<ObjectMapper> mapper) { + this.mapper = mapper; + return this; + } + + /** + * If the adjacency list is wrapped in a JSON object, as is done when writing a graph with + * {@link GraphSONWriter.Builder#wrapAdjacencyList} set to {@code true}, this setting needs to be set to + * {@code true} to properly read it. By default, this value is {@code false} and the adjacency list is + * simply read as line delimited vertices. + * <p/> + * By setting this value to {@code true}, the generated JSON is no longer "splittable" by line and thus not + * suitable for OLAP processing. Furthermore, reading this format of the JSON with + * {@link GraphSONPartialReader#readGraph(InputStream, Graph)} or + * {@link GraphSONPartialReader#readVertices(InputStream, Function, Function, Direction)} requires that the + * entire JSON object be read into memory, so it is best saved for "small" graphs. + */ + public Builder unwrapAdjacencyList(final boolean unwrapAdjacencyList) { + this.unwrapAdjacencyList = unwrapAdjacencyList; + return this; + } + + public GraphSONPartialReader create() { + return new GraphSONPartialReader(this); + } + } +} diff --git a/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator.java b/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator.java index d2f60f11..1f7f5dbc 100644 --- a/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator.java +++ b/aai-core/src/main/java/org/onap/aai/dbgen/SchemaGenerator.java @@ -132,8 +132,8 @@ public class SchemaGenerator { if (alias.isPresent()) { dbPropName = alias.get(); } - if (graphMgmt.containsRelationType(propName)) { - String dmsg = " PropertyKey [" + propName + "] already existed in the DB. "; + if (graphMgmt.containsRelationType(dbPropName)) { + String dmsg = " PropertyKey [" + dbPropName + "] already existed in the DB. "; LOGGER.debug(dmsg); } else { Class<?> type = obj.getClass(propName); @@ -164,8 +164,8 @@ public class SchemaGenerator { String dmsg = " Index [" + dbPropName + "] already existed in the DB. "; LOGGER.debug(dmsg); } else { - if (obj.getIndexedProperties().contains(propName)) { - if (obj.getUniqueProperties().contains(propName)) { + if (obj.getIndexedProperties().contains(dbPropName)) { + if (obj.getUniqueProperties().contains(dbPropName)) { imsg = "Add Unique index for PropertyKey: [" + dbPropName + "]"; LOGGER.info(imsg); graphMgmt.buildIndex(dbPropName, Vertex.class).addKey(propK).unique() diff --git a/aai-core/src/main/java/org/onap/aai/dbmap/InMemoryGraph.java b/aai-core/src/main/java/org/onap/aai/dbmap/InMemoryGraph.java new file mode 100644 index 00000000..84e15479 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/dbmap/InMemoryGraph.java @@ -0,0 +1,149 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.dbmap; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import java.util.Properties; + +import org.apache.commons.lang.exception.ExceptionUtils; +import org.apache.tinkerpop.gremlin.structure.io.IoCore; +import org.onap.aai.dbgen.GraphSONPartialIO; +import org.onap.aai.dbgen.SchemaGenerator; +import org.onap.aai.logging.LogFormatTools; + +import org.janusgraph.core.JanusGraphFactory; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.JanusGraphTransaction; +import org.janusgraph.core.schema.JanusGraphManagement; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +public class InMemoryGraph { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(InMemoryGraph.class); + private JanusGraph graph = null; + + + public InMemoryGraph(Builder builder) throws IOException { + /* + * Create a In-memory graph + */ + InputStream is = new FileInputStream(builder.propertyFile); + try { + graph = JanusGraphFactory.open(builder.propertyFile); + + Properties graphProps = new Properties(); + graphProps.load(is); + JanusGraphManagement graphMgt = graph.openManagement(); + if(builder.isSchemaEnabled){ + LOGGER.info("Schema Enabled"); + SchemaGenerator.loadSchemaIntoJanusGraph(graph, graphMgt, graphProps.getProperty("storage.backend")); + } + JanusGraphTransaction transaction = graph.newTransaction(); + LOGGER.info("Loading snapshot"); + if(builder.isPartialGraph){ + if ( (builder.graphsonLocation != null) && (builder.graphsonLocation.length() > 0) ) { + transaction.io(GraphSONPartialIO.build()).readGraph(builder.graphsonLocation); + } + else { + transaction.io(GraphSONPartialIO.build()).reader().create().readGraph(builder.seqInputStream, graph); + } + } + else{ + if ( (builder.graphsonLocation != null) && (builder.graphsonLocation.length() > 0) ) { + transaction.io(IoCore.graphson()).readGraph(builder.graphsonLocation); + } + else { + transaction.io(IoCore.graphson()).reader().create().readGraph(builder.seqInputStream, graph); + } + } + transaction.commit(); + + } catch (Exception e) { + // TODO : Changesysout to logger + e.printStackTrace(); + LOGGER.error( + "ERROR: Could not load datasnapshot to in memory graph. \n" + LogFormatTools.getStackTop(e)); + throw new IllegalStateException("Could not load datasnapshot to in memory graph"); + + } + finally{ + is.close(); + } + + } + + public static class Builder { + private String graphsonLocation = ""; + private String propertyFile = ""; + private boolean isSchemaEnabled = false; + private InputStream seqInputStream = null; + private boolean isPartialGraph = false; + + + /* + * Builder constructor doesnt do anything + */ + public Builder() { + //Do nothing + } + + public InMemoryGraph build(String graphsonFile, String propertyFile, boolean isSchemaEnabled) throws IOException { + this.graphsonLocation = graphsonFile; + this.propertyFile = propertyFile; + this.isSchemaEnabled = isSchemaEnabled; + return new InMemoryGraph(this); + } + + public InMemoryGraph build(InputStream sis, String propertyFile, boolean isSchemaEnabled) throws IOException { + this.graphsonLocation = null; + this.propertyFile = propertyFile; + this.isSchemaEnabled = isSchemaEnabled; + this.seqInputStream = sis; + return new InMemoryGraph(this); + } + + public InMemoryGraph build(String graphsonFile, String propertyFile, boolean isSchemaEnabled, boolean isPartialGraph) throws IOException { + this.graphsonLocation = graphsonFile; + this.propertyFile = propertyFile; + this.isSchemaEnabled = isSchemaEnabled; + this.isPartialGraph = isPartialGraph; + return new InMemoryGraph(this); + } + + public InMemoryGraph build(InputStream sis, String propertyFile, boolean isSchemaEnabled, boolean isPartialGraph) throws IOException { + this.graphsonLocation = null; + this.propertyFile = propertyFile; + this.isSchemaEnabled = isSchemaEnabled; + this.seqInputStream = sis; + this.isPartialGraph = isPartialGraph; + return new InMemoryGraph(this); + } + } + + public JanusGraph getGraph() { + return graph; + } + +} diff --git a/aai-core/src/main/java/org/onap/aai/extensions/OrphanLInterfaceHandler.java b/aai-core/src/main/java/org/onap/aai/extensions/OrphanLInterfaceHandler.java new file mode 100644 index 00000000..128945d3 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/extensions/OrphanLInterfaceHandler.java @@ -0,0 +1,110 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.extensions; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Introspector; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.exceptions.AAIUnknownObjectException; +import org.onap.aai.parsers.query.QueryParser; +import org.onap.aai.query.builder.QueryBuilder; +import org.onap.aai.rest.db.DBRequest; +import org.onap.aai.restcore.HttpMethod; +import org.onap.aai.edges.enums.EdgeType; + +public class OrphanLInterfaceHandler { + + private QueryBuilder<Vertex> createLInterfaceQuery(AAIExtensionMap aaiReqMap, Introspector newvceObj) throws AAIException { + Introspector uplinkLInterfaceTraversalIntro = aaiReqMap.getLoader().introspectorFromName("l-interface"); + + Introspector customerUplinkLInterfaceTraversalIntro = aaiReqMap.getLoader().introspectorFromName("l-interface"); + + Introspector logLinkIntroForTraversal = aaiReqMap.getLoader().introspectorFromName("logical-link"); + + QueryBuilder<Vertex> query = aaiReqMap.getTransactionalGraphEngine().getQueryBuilder() + .exactMatchQuery(newvceObj) + .createEdgeTraversal(EdgeType.TREE, newvceObj, uplinkLInterfaceTraversalIntro) + .getVerticesByProperty("interface-role", "UPLINK") + .createEdgeTraversal(EdgeType.COUSIN, uplinkLInterfaceTraversalIntro, logLinkIntroForTraversal) + .createEdgeTraversal(EdgeType.COUSIN, logLinkIntroForTraversal, customerUplinkLInterfaceTraversalIntro) + .getVerticesByProperty("interface-role", "CUSTOMER-UPLINK").dedup(); + return query; + } + + private URI buildLInterfaceURI(Vertex linterface, AAIExtensionMap aaiReqMap) throws UnsupportedEncodingException, AAIException, URISyntaxException { + Loader loader = aaiReqMap.getLoader(); + Introspector lint = loader.introspectorFromName("l-interface"); + lint.setValue("interface-name", (String)linterface.property("interface-name").value()); + String lintSegment = lint.getURI(); + + Introspector lagInterfaceForTrav = loader.introspectorFromName("lag-interface"); + QueryBuilder<Vertex> lagIntQuery = aaiReqMap.getTransactionalGraphEngine().getQueryBuilder() + .exactMatchQuery(lint) + .createEdgeTraversal(EdgeType.TREE, linterface, lagInterfaceForTrav).dedup(); + List<Vertex> lagInterfaces = lagIntQuery.toList(); + if (lagInterfaces.isEmpty()) { + throw new AAIException("AAI_6114"); + } else if (lagInterfaces.size() > 1) { + throw new AAIException("AAI_6140"); + } + Vertex lagInt = lagInterfaces.get(0); + lagInterfaceForTrav.setValue("interface-name", (String)lagInt.property("interface-name").value()); + String lagSegment = lagInterfaceForTrav.getURI(); + + Introspector gvVPEforTrav = loader.introspectorFromName("generic-vnf"); + QueryBuilder<Vertex> gvVPEquery = aaiReqMap.getTransactionalGraphEngine().getQueryBuilder() + .exactMatchQuery(lagInterfaceForTrav) + .createEdgeTraversal(EdgeType.TREE, lagInterfaceForTrav, gvVPEforTrav).dedup(); + List<Vertex> genvnfs = gvVPEquery.toList(); + if (genvnfs.isEmpty()) { + throw new AAIException("AAI_6114"); + } else if (genvnfs.size() > 1) { + throw new AAIException("AAI_6140"); + } + Vertex genvnf = genvnfs.get(0); + gvVPEforTrav.setValue("vnf-id", (String)genvnf.property("vnf-id").value()); + String gvSegment = gvVPEforTrav.getURI(); + + return new URI(gvSegment + lagSegment + lintSegment); + } + + public List<DBRequest> createOrphanLInterfaceDelRequests(AAIExtensionMap aaiReqMap, Introspector newvce) throws AAIException, UnsupportedEncodingException, URISyntaxException{ + List<DBRequest> requests = new ArrayList<>(); + QueryBuilder<Vertex> query = createLInterfaceQuery(aaiReqMap, newvce); + List<Vertex> linterfaces = query.toList(); + + for (Vertex lint : linterfaces) { + URI lintURI = buildLInterfaceURI(lint, aaiReqMap); + QueryParser parser = createLInterfaceQuery(aaiReqMap, newvce).createQueryFromObjectName("l-interface"); + DBRequest originalDbRequest = aaiReqMap.getDbRequest(); + DBRequest request = new DBRequest.Builder(HttpMethod.DELETE, lintURI, parser, newvce, originalDbRequest.getHeaders(), originalDbRequest.getInfo(), originalDbRequest.getTransactionId()).build(); + requests.add(request); + } + + return requests; + } +} diff --git a/aai-core/src/main/java/org/onap/aai/ingestModel/CreateWidgetModels.java b/aai-core/src/main/java/org/onap/aai/ingestModel/CreateWidgetModels.java index d5921c19..5f641025 100644 --- a/aai-core/src/main/java/org/onap/aai/ingestModel/CreateWidgetModels.java +++ b/aai-core/src/main/java/org/onap/aai/ingestModel/CreateWidgetModels.java @@ -24,6 +24,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; +import java.util.Set; import java.util.UUID; import javax.xml.transform.stream.StreamSource; @@ -36,6 +37,7 @@ import org.onap.aai.introspection.ModelType; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.util.AAIConfig; import org.onap.aai.util.AAIConstants; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; /** * The Class CreateWidgetModels. @@ -74,7 +76,13 @@ public class CreateWidgetModels System.exit(0); } - Loader loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(ModelType.MOXY, new SchemaVersion(_apiVersion)); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext( + "org.onap.aai.config", + "org.onap.aai.setup" + ); + + LoaderFactory loaderFactory = ctx.getBean(LoaderFactory.class); + Loader loader = loaderFactory.createLoaderForVersion(ModelType.MOXY, new SchemaVersion(_apiVersion)); // iterate the collection of resources @@ -91,6 +99,12 @@ public class CreateWidgetModels if (processedWidgets.contains(resource)) { continue; } + + Set<String> introspectorProperties = aaiRes.getProperties(); + + if(!(introspectorProperties.contains("model-version-id") && introspectorProperties.contains("model-invariant-id"))){ + System.out.println(aaiRes.getDbName() + " does not contain model properties so skipping"); + } processedWidgets.add(resource); String widgetName = resource; diff --git a/aai-core/src/main/java/org/onap/aai/parsers/uri/URIParser.java b/aai-core/src/main/java/org/onap/aai/parsers/uri/URIParser.java index 6e6ba844..5aece21c 100644 --- a/aai-core/src/main/java/org/onap/aai/parsers/uri/URIParser.java +++ b/aai-core/src/main/java/org/onap/aai/parsers/uri/URIParser.java @@ -66,14 +66,7 @@ public class URIParser { public URIParser(Loader loader, URI uri) { this.uri = uri; - String currentVersion = "v7"; this.originalLoader = loader; - try { - currentVersion = AAIConfig.get("aai.default.api.version"); - } catch (AAIException e) { - ErrorLogHelper.logException(e); - } - //Load the latest version because we need it for cloud region this.loader = loader; @@ -156,10 +149,13 @@ public class URIParser { } if (introspector.isContainer()) { boolean isFinalContainer = i == parts.length-2; - p.processContainer(introspector, EdgeType.COUSIN, uriKeys, isFinalContainer); + /* + * Related-to could be COUSIN OR TREE and in some cases BOTH. So Let EdgeRuleBuilder use all the edgeTypes + */ + p.processContainer(introspector, EdgeType.ALL, uriKeys, isFinalContainer); } previousObj = introspector; - type = EdgeType.COUSIN; + type = EdgeType.ALL; i+=2; continue; } @@ -215,7 +211,6 @@ public class URIParser { } } p.processContainer(introspector, type, uriKeys, isFinalContainer); - i++; } else { p.processNamespace(introspector); diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java index d5faf052..80122296 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GraphTraversalBuilder.java @@ -63,11 +63,11 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { */ public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) { super(loader, source); - + traversal = (GraphTraversal<Vertex, E>) __.<E>start(); - + } - + /** * Instantiates a new graph traversal builder. * @@ -76,42 +76,42 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { */ public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) { super(loader, source, start); - + traversal = (GraphTraversal<Vertex, E>) __.__(start); - + } - + /** * @{inheritDoc} */ @Override public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) { - + // correct value call because the index is registered as an Integer traversal.has(key, this.correctObjectType(value)); - + stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @Override public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) { - + //this is because the index is registered as an Integer List<Object> correctedValues = new ArrayList<>(); for (Object item : values) { correctedValues.add(this.correctObjectType(item)); } - + traversal.has(key, P.within(correctedValues)); - + stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @@ -124,79 +124,79 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @Override public QueryBuilder<Vertex> getVerticesByProperty(String key) { - + traversal.has(key); stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @Override public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) { - + traversal.hasNot(key); stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @Override public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) { - + // correct value call because the index is registered as an Integer traversal.has(key, P.neq(this.correctObjectType(value))); - + stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @Override public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) { - + //this is because the index is registered as an Integer List<Object> correctedValues = new ArrayList<>(); for (Object item : values) { correctedValues.add(this.correctObjectType(item)); } - + traversal.has(key, P.without(correctedValues)); - + stepIndex++; return (QueryBuilder<Vertex>) this; } @Override public QueryBuilder<Vertex> getVerticesGreaterThanProperty(final String key, Object value) { - + traversal.has(key, P.gte(this.correctObjectType(value))); - + stepIndex++; return (QueryBuilder<Vertex>) this; } - + @Override public QueryBuilder<Vertex> getVerticesLessThanProperty(final String key, Object value) { - + traversal.has(key, P.lte(this.correctObjectType(value))); - + stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} @@ -213,7 +213,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { */ @Override public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) { - + for (Map.Entry<String, String> es : map.entrySet()) { traversal.has(es.getKey(), es.getValue()); stepIndex++; @@ -273,7 +273,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { this.createContainerQuery(obj); return (QueryBuilder<Vertex>) this; } - + private void allPropertiesQuery(Introspector obj) { Set<String> props = obj.getProperties(); Set<String> keys = obj.getKeys(); @@ -298,12 +298,12 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { } } } - + /** * @{inheritDoc} */ @Override - + public QueryBuilder<Vertex> createContainerQuery(Introspector obj) { String type = obj.getChildDBName(); String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT); @@ -319,8 +319,8 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { } /** - * @throws NoEdgeRuleFoundException - * @throws AAIException + * @throws NoEdgeRuleFoundException + * @throws AAIException * @{inheritDoc} */ @Override @@ -423,10 +423,10 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { } this.traversal.union(traversals); stepIndex++; - + return this; } - + /** * @{inheritDoc} */ @@ -454,101 +454,101 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { return this; } - + @Override public QueryBuilder<E> store(String name) { - + this.traversal.store(name); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> cap(String name) { this.traversal.cap(name); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> unfold() { this.traversal.unfold(); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> dedup() { - + this.traversal.dedup(); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> emit() { - + this.traversal.emit(); stepIndex++; - + return this; - + } - + @Override public QueryBuilder<E> repeat(QueryBuilder<E> builder) { - + this.traversal.repeat((GraphTraversal<Vertex, E>)builder.getQuery()); stepIndex++; return this; } - + @Override public QueryBuilder<E> until(QueryBuilder<E> builder) { this.traversal.until((GraphTraversal<Vertex,E>)builder.getQuery()); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> groupCount() { this.traversal.groupCount(); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> both() { this.traversal.both(); stepIndex++; - + return this; } - + @Override public QueryBuilder<Tree> tree() { - + this.traversal.tree(); stepIndex++; - + return (QueryBuilder<Tree>)this; } - + @Override public QueryBuilder<E> by(String name) { this.traversal.by(name); stepIndex++; - + return this; } - + /** * {@inheritDoc} */ @@ -568,72 +568,90 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Path>)this; } - + @Override public QueryBuilder<Edge> outE() { this.traversal.outE(); stepIndex++; return (QueryBuilder<Edge>)this; } - + @Override public QueryBuilder<Edge> inE() { this.traversal.inE(); stepIndex++; return (QueryBuilder<Edge>)this; } - + @Override public QueryBuilder<Vertex> outV() { this.traversal.outV(); stepIndex++; return (QueryBuilder<Vertex>)this; } - + @Override public QueryBuilder<Vertex> inV() { this.traversal.inV(); stepIndex++; return (QueryBuilder<Vertex>)this; } - + @Override public QueryBuilder<E> as(String name) { this.traversal.as(name); - + stepIndex++; return this; } - + @Override public QueryBuilder<E> not(QueryBuilder<E> builder) { this.traversal.not(builder.getQuery()); - + stepIndex++; return this; } - + @Override public QueryBuilder<E> select(String name) { this.traversal.select(name); - + + stepIndex++; + + return this; + } + + @Override + public QueryBuilder<E> select(String... names) { + if(names.length == 1) { + this.traversal.select(names[0]); + } + else if(names.length == 2) { + this.traversal.select(names[0], names[1]); + } + else if(names.length > 2){ + String[] otherNames = Arrays.copyOfRange(names, 2, names.length); + this.traversal.select(names[0], names[1], otherNames); + } + stepIndex++; - + return this; } - + /** * Edge query. * * @param outObj the out type * @param inObj the in type - * @throws NoEdgeRuleFoundException - * @throws AAIException + * @throws NoEdgeRuleFoundException + * @throws AAIException */ private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException { String outType = outObj.getDbName(); String inType = inObj.getDbName(); - + if (outObj.isContainer()) { outType = outObj.getChildDBName(); } @@ -685,35 +703,35 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { traversal.out(outLabels.toArray(new String[outLabels.size()])); } else if (outLabels.isEmpty() && !inLabels.isEmpty()) { traversal.in(inLabels.toArray(new String[inLabels.size()])); - } else { + } else { traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()]))); } - + stepIndex++; this.createContainerQuery(inObj); - + } - + /** * Edge query. * * @param outObj the out type * @param inObj the in type - * @throws NoEdgeRuleFoundException - * @throws AAIException + * @throws NoEdgeRuleFoundException + * @throws AAIException */ private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException { String outType = outObj.getDbName(); String inType = inObj.getDbName(); - + if (outObj.isContainer()) { outType = outObj.getChildDBName(); } if (inObj.isContainer()) { inType = inObj.getChildDBName(); } - + markParentBoundary(); Multimap<String, EdgeRule> rules = ArrayListMultimap.create(); EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type); @@ -729,7 +747,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { } catch (EdgeRuleNotFoundException e) { throw new NoEdgeRuleFoundException(e); } - + final List<String> inLabels = new ArrayList<>(); final List<String> outLabels = new ArrayList<>(); @@ -749,11 +767,11 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { traversal.outE(outLabels.toArray(new String[outLabels.size()])); } else if (outLabels.isEmpty() && !inLabels.isEmpty()) { traversal.inE(inLabels.toArray(new String[inLabels.size()])); - } else { + } else { traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])), __.inE(inLabels.toArray(new String[inLabels.size()]))); } } - + @Override public QueryBuilder<E> limit(long amount) { traversal.limit(amount); @@ -767,7 +785,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { public <E2> E2 getQuery() { return (E2)this.traversal; } - + /** * @{inheritDoc} */ @@ -776,17 +794,17 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { return cloneQueryAtStep(parentStepIndex); } - + @Override public QueryBuilder<E> getContainerQuery() { - + if (this.parentStepIndex == 0) { return removeQueryStepsBetween(0, containerStepIndex); } else { return cloneQueryAtStep(containerStepIndex); } } - + /** * @{inheritDoc} */ @@ -794,13 +812,13 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { public void markParentBoundary() { parentStepIndex = stepIndex; } - + @Override public void markContainer() { containerStepIndex = stepIndex; } - - + + /** * @{inheritDoc} */ @@ -823,7 +841,7 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { /** * end is exclusive - * + * * @param start * @param end * @return @@ -850,19 +868,19 @@ public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> { if (this.completeTraversal == null) { executeQuery(); } - + return this.completeTraversal.hasNext(); } - + @Override public E next() { if (this.completeTraversal == null) { executeQuery(); } - + return this.completeTraversal.next(); } - + @Override public List<E> toList() { if (this.completeTraversal == null) { diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java index 2cc78f44..43925f49 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/GremlinQueryBuilder.java @@ -36,7 +36,7 @@ import org.onap.aai.db.props.AAIProperties; import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Introspector; import org.onap.aai.introspection.Loader; -import org.onap.aai.restcore.search.GremlinGroovyShellSingleton; +import org.onap.aai.restcore.search.GremlinGroovyShell; import org.onap.aai.schema.enums.ObjectMetadata; import org.onap.aai.edges.EdgeRule; import org.onap.aai.edges.EdgeRuleQuery; @@ -53,7 +53,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { private static final String ARGUMENT2 = "#!#argument#!#"; private static final String HAS = ".has('"; - private GremlinGroovyShellSingleton gremlinGroovy = GremlinGroovyShellSingleton.getInstance(); + private GremlinGroovyShell gremlinGroovy = new GremlinGroovyShell(); private GraphTraversal<?, ?> completeTraversal = null; protected List<String> list = null; @@ -103,6 +103,16 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { return (QueryBuilder<Vertex>) this; } + /** + * @{inheritDoc} + */ + @Override + public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) { + list.add(HAS + key + "', " + value + ")"); + stepIndex++; + return (QueryBuilder<Vertex>) this; + } + @Override public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) { boolean bValue = false; @@ -139,7 +149,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @@ -150,7 +160,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @@ -199,7 +209,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @@ -221,7 +231,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Vertex>) this; } - + @Override public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) { String predicate = "P.gte(#!#argument1#!#)"; @@ -236,7 +246,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Vertex>) this; } - + @Override public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) { String predicate = "P.lte(#!#argument1#!#)"; @@ -251,10 +261,10 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Vertex>) this; } - - + + /** * @{inheritDoc} */ @@ -263,13 +273,13 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { //TODO return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @Override public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) { - + for (Map.Entry<String, String> es : map.entrySet()) { list.add(HAS + es.getKey() + "', '" + es.getValue() + "')"); stepIndex++; @@ -278,7 +288,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Vertex>) this; } - + /** * @{inheritDoc} */ @@ -287,16 +297,16 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { Set<String> keys = obj.getKeys(); for (String key : keys) { - + this.getVerticesByProperty(key, obj.<Object>getValue(key)); - - } + + } return (QueryBuilder<Vertex>) this; } - + /** - * @throws NoEdgeRuleFoundException - * @throws AAIException + * @throws NoEdgeRuleFoundException + * @throws AAIException * @{inheritDoc} */ @Override @@ -311,7 +321,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { } this.edgeQueryToVertex(type, parentName, childName, null); return this; - + } @Override @@ -390,8 +400,14 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { } else { if (Direction.IN.equals(rule.getDirection())) { inLabels.add(rule.getLabel()); + if(inType.equals(outType)) {//code to handle when a type edges to itself, to add both in and out + outLabels.add(rule.getLabel()); + } } else { outLabels.add(rule.getLabel()); + if(inType.equals(outType)) {//code to handle when a type edges to itself, to add both in and out + inLabels.add(rule.getLabel()); + } } } } @@ -408,16 +424,16 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')"); stepIndex++; - + } - + /** * Edge query. * * @param outType the out type * @param inType the in type - * @throws NoEdgeRuleFoundException - * @throws AAIException + * @throws NoEdgeRuleFoundException + * @throws AAIException */ private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException { markParentBoundary(); @@ -434,7 +450,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { } catch (EdgeRuleNotFoundException e) { throw new NoEdgeRuleFoundException(e); } - + final List<String> inLabels = new ArrayList<>(); final List<String> outLabels = new ArrayList<>(); @@ -459,9 +475,9 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { } else { list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('" + String.join("','", outLabels) + "'))"); } - + stepIndex++; - + } @Override public QueryBuilder<E> limit(long amount) { @@ -486,7 +502,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { command.append(Joiner.on(",").join(wrapped)); command.append(")"); list.add(".has('aai-node-type', " + command + ")"); - + } else { list.add(".has('aai-node-type', '" + type + "')"); } @@ -494,7 +510,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { this.markContainer(); return (QueryBuilder<Vertex>) this; } - + @Override public QueryBuilder<E> union(QueryBuilder<E>... builder) { markParentBoundary(); @@ -508,10 +524,10 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { command.append(")"); list.add(command.toString()); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> where(QueryBuilder<E>... builder) { markParentBoundary(); @@ -521,8 +537,8 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; } list.addAll(traversals); - - + + return this; } @@ -542,95 +558,95 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { return this; } - + @Override public QueryBuilder<E> store(String name) { this.list.add(".store('"+ name + "')"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> cap(String name) { this.list.add(".cap('"+ name + "')"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> unfold() { this.list.add(".unfold()"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> dedup() { this.list.add(".dedup()"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> emit() { this.list.add(".emit()"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> repeat(QueryBuilder<E> builder) { this.list.add(".repeat(__" + builder.getQuery() + ")"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> until(QueryBuilder<E> builder) { this.list.add(".until(__" + builder.getQuery() + ")"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> groupCount() { this.list.add(".groupCount()"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> both() { this.list.add(".both()"); stepIndex++; - + return this; } - + @Override public QueryBuilder<Tree> tree() { this.list.add(".tree()"); stepIndex++; - + return (QueryBuilder<Tree>)this; } - + @Override public QueryBuilder<E> by(String name) { this.list.add(".by('"+ name + "')"); stepIndex++; - + return this; } - + /** * {@inheritDoc} */ @@ -650,60 +666,76 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { stepIndex++; return (QueryBuilder<Path>)this; } - + @Override public QueryBuilder<Edge> outE() { this.list.add(".outE()"); stepIndex++; - + return (QueryBuilder<Edge>)this; } - + @Override public QueryBuilder<Edge> inE() { this.list.add(".inE()"); stepIndex++; - + return (QueryBuilder<Edge>)this; } - + @Override public QueryBuilder<Vertex> outV() { this.list.add(".outV()"); stepIndex++; - + return (QueryBuilder<Vertex>)this; } - + @Override public QueryBuilder<Vertex> inV() { this.list.add(".inV()"); stepIndex++; - + return (QueryBuilder<Vertex>)this; } - + @Override public QueryBuilder<E> not(QueryBuilder<E> builder) { this.list.add(".not(" + "__" + builder.getQuery() + ")"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> as(String name) { this.list.add(".as('" + name + "')"); stepIndex++; - + return this; } - + @Override public QueryBuilder<E> select(String name) { this.list.add(".select('" + name + "')"); stepIndex++; - + + return this; + } + + @Override + public QueryBuilder<E> select(String... names) { + String stepString = ".select('"; + for(int i = 0; i<names.length; i++) { + stepString = stepString + names[i] +"'"; + if(i!=(names.length-1)) { + stepString = stepString + ",'"; + } + } + stepString = stepString + ")"; + this.list.add(stepString); + stepIndex++; + return this; } /** @@ -713,26 +745,26 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { public QueryBuilder<E> getParentQuery() { return cloneQueryAtStep(parentStepIndex); } - + @Override public QueryBuilder<E> getContainerQuery() { return cloneQueryAtStep(containerStepIndex); } - + /** * @{inheritDoc} */ @Override public <T2> T2 getQuery() { StringBuilder sb = new StringBuilder(); - + for (String piece : this.list) { sb.append(piece); } - + return (T2)sb.toString(); } - + /** * @{inheritDoc} */ @@ -740,12 +772,12 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { public void markParentBoundary() { parentStepIndex = stepIndex; } - + @Override public void markContainer() { this.containerStepIndex = stepIndex; } - + /** * @{inheritDoc} */ @@ -765,7 +797,7 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { protected int getStepIndex() { return stepIndex; } - + private void executeQuery() { String queryString = "g" + Joiner.on("").join(list); Map<String, Object> params = new HashMap<>(); @@ -781,25 +813,25 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { if (this.completeTraversal == null) { executeQuery(); } - + return this.completeTraversal.hasNext(); } - + @Override public E next() { if (this.completeTraversal == null) { executeQuery(); } - + return (E)this.completeTraversal.next(); } - + @Override public List<E> toList() { if (this.completeTraversal == null) { executeQuery(); } - + return (List<E>)this.completeTraversal.toList(); } @@ -808,5 +840,5 @@ public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> { return (QueryBuilder<Edge>)this; } - + } diff --git a/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java b/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java index f4ffac0e..777204f2 100644 --- a/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java +++ b/aai-core/src/main/java/org/onap/aai/query/builder/QueryBuilder.java @@ -293,6 +293,40 @@ public abstract class QueryBuilder<E> implements Iterator<E> { return createEdgeTraversal(type, out, in); } + /** + * + * @param edgeType + * @param outNodeType + * @param inNodeType + * @return + * @throws AAIException + */ + public QueryBuilder<Vertex> createEdgeTraversal(String edgeType, String outNodeType, String inNodeType) throws AAIException { + /* + * When the optional parameter edgetype is sent it is a string that needs to be converted to Enum + */ + EdgeType type = EdgeType.valueOf(edgeType); + Introspector out = loader.introspectorFromName(outNodeType); + Introspector in = loader.introspectorFromName(inNodeType); + + return createEdgeTraversal(type, out, in); + } + + /** + * + * @param MissingOptionalParameter + * @param outNodeType + * @param inNodeType + * @return + * @throws AAIException + */ + public QueryBuilder<Vertex> createEdgeTraversal(MissingOptionalParameter edgeType, String outNodeType, String inNodeType) throws AAIException { + /* + * When no optional parameter edgetype is sent get all edges between the 2 nodetypes + */ + return this.createEdgeTraversal(outNodeType, inNodeType); + } + public QueryBuilder<Vertex> createEdgeTraversal(String outNodeType, String inNodeType) throws AAIException { Introspector out = loader.introspectorFromName(outNodeType); @@ -506,6 +540,7 @@ public abstract class QueryBuilder<E> implements Iterator<E> { public abstract QueryBuilder<E> not(QueryBuilder<E> builder); public abstract QueryBuilder<E> as(String name); public abstract QueryBuilder<E> select(String name); + public abstract QueryBuilder<E> select(String... names); public abstract QueryBuilder<E> until(QueryBuilder<E> builder); public abstract QueryBuilder<E> groupCount(); public abstract QueryBuilder<E> by(String name); @@ -574,4 +609,16 @@ public abstract class QueryBuilder<E> implements Iterator<E> { protected void setEdgeIngestor(EdgeIngestor ei) { this.edgeRules = ei; } + + public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) { + return getVerticesByProperty(key, value); + } + + public QueryBuilder<Vertex> getVerticesByNumberProperty(String key) { + return getVerticesByProperty(key); + } + + public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, MissingOptionalParameter value) { + return getVerticesByProperty(key, value); + } } 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 bd7f1b91..6f551de7 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, @@ -53,6 +53,8 @@ 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.json.JSONException; +import org.json.JSONObject; import org.onap.aai.db.props.AAIProperties; import org.onap.aai.dbmap.DBConnectionType; import org.onap.aai.domain.responseMessage.AAIResponseMessage; @@ -94,9 +96,9 @@ public class HttpEntry { private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(HttpEntry.class); private static final String TARGET_ENTITY = "DB"; - private ModelType introspectorFactoryType; + private ModelType introspectorFactoryType; - private QueryStyle queryStyle; + private QueryStyle queryStyle; private SchemaVersion version; @@ -123,7 +125,7 @@ public class HttpEntry { @Value("${schema.uri.base.path}") private String basePath; - private UEBNotification notification; + private UEBNotification notification; /** * Instantiates a new http entry. @@ -136,13 +138,13 @@ public class HttpEntry { this.queryStyle = queryStyle; } - public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType){ + public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType) { this.version = version; this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version); this.dbEngine = new JanusGraphDBEngine( - queryStyle, - connectionType, - loader); + queryStyle, + connectionType, + loader); getDbEngine().startTransaction(); this.notification = new UEBNotification(loader, loaderFactory, schemaVersions); @@ -150,13 +152,13 @@ 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( - queryStyle, - connectionType, - loader); + queryStyle, + connectionType, + loader); this.notification = notification; //start transaction on creation @@ -210,7 +212,7 @@ public class HttpEntry { return dbEngine; } - public Pair<Boolean, List<Pair<URI, Response>>> process (List<DBRequest> requests, String sourceOfTruth) throws AAIException { + public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth) throws AAIException { return this.process(requests, sourceOfTruth, true); } @@ -221,24 +223,32 @@ public class HttpEntry { * * @return a boolean true/false of whether the user requested paginated results */ - public boolean isPaginated(){ + public boolean isPaginated() { return this.paginationBucket > -1 && this.paginationIndex > -1; } /** + * Returns the pagination size + * @return integer of the size of results to be returned when paginated + */ + public int getPaginationBucket() { + return this.paginationBucket; + } + + /** * Setter for the pagination bucket variable which stores in this object the size of results to return * @param pb */ - public void setPaginationBucket(int pb){ + public void setPaginationBucket(int pb) { this.paginationBucket = pb; } /** - * Returns the pagination size - * @return integer of the size of results to be returned when paginated + * Getter to return the pagination index requested by the user when requesting paginated results + * @return */ - public int getPaginationBucket(){ - return this.paginationBucket; + public int getPaginationIndex() { + return this.paginationIndex; } /** @@ -246,32 +256,24 @@ public class HttpEntry { * paginated * @param pi */ - public void setPaginationIndex(int pi){ - if(pi == 0){ + public void setPaginationIndex(int pi) { + if (pi == 0) { pi = 1; } this.paginationIndex = pi; } /** - * Getter to return the pagination index requested by the user when requesting paginated results - * @return - */ - public int getPaginationIndex(){ - return this.paginationIndex; - } - - /** * Sets the total vertices variables and calculates the amount of pages based on size and total vertices * @param totalVertices * @param paginationBucketSize */ - public void setTotalsForPaging(int totalVertices, int paginationBucketSize){ + public void setTotalsForPaging(int totalVertices, int paginationBucketSize) { this.totalVertices = totalVertices; //set total number of buckets equal to full pages - this.totalPaginationBuckets = totalVertices/paginationBucketSize; + this.totalPaginationBuckets = totalVertices / paginationBucketSize; //conditionally add a page for the remainder - if(totalVertices % paginationBucketSize > 0){ + if (totalVertices % paginationBucketSize > 0) { this.totalPaginationBuckets++; } } @@ -279,7 +281,7 @@ public class HttpEntry { /** * @return the total amount of pages */ - public int getTotalPaginationBuckets(){ + public int getTotalPaginationBuckets() { return this.totalPaginationBuckets; } @@ -287,7 +289,7 @@ public class HttpEntry { * * @return the total number of vertices when paginated */ - public int getTotalVertices(){ + public int getTotalVertices() { return this.totalVertices; } @@ -299,7 +301,7 @@ public class HttpEntry { * @return the pair * @throws AAIException the AAI exception */ - public Pair<Boolean, List<Pair<URI, Response>>> process (List<DBRequest> requests, String sourceOfTruth, boolean enableResourceVersion) throws AAIException { + public Pair<Boolean, List<Pair<URI, Response>>> process(List<DBRequest> requests, String sourceOfTruth, boolean enableResourceVersion) throws AAIException { DBSerializer serializer = new DBSerializer(version, dbEngine, introspectorFactoryType, sourceOfTruth); String methodName = "process"; @@ -310,7 +312,7 @@ public class HttpEntry { String transactionId = null; int depth = AAIProperties.MAXIMUM_DEPTH; Format format = null; - List<Pair<URI,Response>> responses = new ArrayList<>(); + List<Pair<URI, Response>> responses = new ArrayList<>(); MultivaluedMap<String, String> params = null; HttpMethod method = null; String uriTemp = ""; @@ -339,11 +341,11 @@ public class HttpEntry { LoggingContext.startTime(); List<Vertex> vertTemp; List<Vertex> vertices; - if(this.isPaginated()) { + if (this.isPaginated()) { vertTemp = query.getQueryBuilder().toList(); this.setTotalsForPaging(vertTemp.size(), this.paginationBucket); vertices = vertTemp.subList(((this.paginationIndex - 1) * this.paginationBucket), Math.min((this.paginationBucket * this.paginationIndex), vertTemp.size())); - }else{ + } else { vertices = query.getQueryBuilder().toList(); } boolean isNewVertex = false; @@ -364,7 +366,7 @@ public class HttpEntry { if (cleanUp == null) { cleanUp = "false"; } - if (vertices.size() > 1 && processSingle && !method.equals(HttpMethod.GET)) { + if (vertices.size() > 1 && processSingle && !(method.equals(HttpMethod.GET) || method.equals(HttpMethod.GET_RELATIONSHIP))) { if (method.equals(HttpMethod.DELETE)) { LoggingContext.restoreIfPossible(); throw new AAIException("AAI_6138"); @@ -374,7 +376,7 @@ public class HttpEntry { } } if (method.equals(HttpMethod.PUT)) { - String resourceVersion = (String)obj.getValue("resource-version"); + String resourceVersion = (String) obj.getValue("resource-version"); if (vertices.isEmpty()) { if (enableResourceVersion) { serializer.verifyResourceVersion("create", query.getResultType(), "", resourceVersion, obj.getURI()); @@ -399,17 +401,17 @@ public class HttpEntry { v = vertices.get(0); } HashMap<String, Introspector> relatedObjects = new HashMap<>(); + String nodeOnly = params.getFirst("nodes-only"); + boolean isNodeOnly = nodeOnly != null; switch (method) { case GET: - String nodeOnly = params.getFirst("nodes-only"); - boolean isNodeOnly = nodeOnly != null; if (format == null) { obj = this.getObjectFromDb(vertices, serializer, query, obj, request.getUri(), depth, isNodeOnly, cleanUp); LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS); - LOGGER.info ("Completed"); + LOGGER.info("Completed"); LoggingContext.restoreIfPossible(); if (obj != null) { @@ -424,12 +426,40 @@ public class HttpEntry { } } else { FormatFactory ff = new FormatFactory(loader, serializer, schemaVersions, basePath + "/"); - Formatter formatter = ff.get(format, params); + Formatter formatter = ff.get(format, params); result = formatter.output(vertices.stream().map(vertex -> (Object) vertex).collect(Collectors.toList())).toString(); status = Status.OK; } break; + case GET_RELATIONSHIP: + if (format == null) { + obj = this.getRelationshipObjectFromDb(vertices, serializer, query, request.getInfo().getRequestUri()); + + LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS); + LOGGER.info("Completed"); + LoggingContext.restoreIfPossible(); + + if (obj != null) { + status = Status.OK; + MarshallerProperties properties; + if (!request.getMarshallerProperties().isPresent()) { + properties = new MarshallerProperties.Builder(org.onap.aai.restcore.MediaType.getEnum(outputMediaType)).build(); + } else { + properties = request.getMarshallerProperties().get(); + } + result = obj.marshal(properties); + } else { + String msg = createRelationshipNotFoundMessage(query.getResultType(), request.getUri()); + throw new AAIException("AAI_6149", msg); + } + } else { + FormatFactory ff = new FormatFactory(loader, serializer, schemaVersions, basePath + "/"); + Formatter formatter = ff.get(format, params); + 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); if (isNewVertex) { @@ -445,9 +475,9 @@ public class HttpEntry { if (query.isDependent()) { relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); } - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + - (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); - LOGGER.info ("Completed "); + LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs() + + (long) queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); + LOGGER.info("Completed "); LoggingContext.restoreIfPossible(); notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects, basePath); @@ -457,14 +487,14 @@ public class HttpEntry { 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.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS); + LOGGER.info("Completed"); LoggingContext.restoreIfPossible(); status = Status.OK; notification.createNotificationEvent(transactionId, sourceOfTruth, status, new URI(uri.toString().replace("/relationship-list/relationship", "")), serializer.getLatestVersionView(v), relatedObjects, basePath); break; case MERGE_PATCH: - Introspector existingObj = loader.introspectorFromName(obj.getDbName()); + Introspector existingObj = loader.introspectorFromName(obj.getDbName()); existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj, request.getUri(), 0, false, cleanUp); String existingJson = existingObj.marshal(false); String newJson; @@ -493,14 +523,14 @@ public class HttpEntry { if (query.isDependent()) { relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, patchedObj, this.loader); } - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + - (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); - LOGGER.info ("Completed"); + 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()); + LOGGER.info("Caught exception: " + e.getMessage()); LoggingContext.restoreIfPossible(); throw new AAIException("AAI_3000", "could not perform patch operation"); } @@ -530,7 +560,7 @@ public class HttpEntry { Map<String, URI> uriMap = new HashMap<>(); Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>(); - if(isDelVerticesPresent){ + if (isDelVerticesPresent) { deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices); uriMap = this.buildURIMap(serializer, deleteObjects); @@ -541,9 +571,9 @@ public class HttpEntry { 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() + - (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); - LOGGER.info ("Completed"); + 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); @@ -552,9 +582,9 @@ public class HttpEntry { * Notify delete-other-v candidates */ - if(isDelVerticesPresent){ + if (isDelVerticesPresent) { this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification, deleteObjects, - uriMap, deleteRelatedObjects, basePath); + uriMap, deleteRelatedObjects, basePath); } break; @@ -562,8 +592,8 @@ public class HttpEntry { serializer.touchStandardVertexProperties(v, false); serializer.deleteEdge(obj, v); - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs(),TimeUnit.MILLISECONDS); - LOGGER.info ("Completed"); + LoggingContext.elapsedTime((long) serializer.getDBTimeMsecs(), TimeUnit.MILLISECONDS); + LOGGER.info("Completed"); LoggingContext.restoreIfPossible(); status = Status.NO_CONTENT; notification.createNotificationEvent(transactionId, sourceOfTruth, Status.OK, new URI(uri.toString().replace("/relationship-list/relationship", "")), serializer.getLatestVersionView(v), relatedObjects, basePath); @@ -576,40 +606,42 @@ public class HttpEntry { /* 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 - */ + */ if (response == null && v != null && ( method.equals(HttpMethod.PUT) - || method.equals(HttpMethod.GET) - || method.equals(HttpMethod.MERGE_PATCH)) - ) { + || method.equals(HttpMethod.GET) + || method.equals(HttpMethod.MERGE_PATCH) + || method.equals(HttpMethod.GET_RELATIONSHIP)) + + ) { String myvertid = v.id().toString(); - if(this.isPaginated()){ + if (this.isPaginated()) { response = Response.status(status) - .header("vertex-id", myvertid) - .header("total-results", this.getTotalVertices()) - .header("total-pages", this.getTotalPaginationBuckets()) - .entity(result) - .type(outputMediaType).build(); - }else { + .header("vertex-id", myvertid) + .header("total-results", this.getTotalVertices()) + .header("total-pages", this.getTotalPaginationBuckets()) + .entity(result) + .type(outputMediaType).build(); + } else { response = Response.status(status) - .header("vertex-id", myvertid) - .entity(result) - .type(outputMediaType).build(); + .header("vertex-id", myvertid) + .entity(result) + .type(outputMediaType).build(); } } else if (response == null) { response = Response.status(status) - .type(outputMediaType).build(); + .type(outputMediaType).build(); } else { //response already set to something } - Pair<URI,Response> pairedResp = Pair.with(request.getUri(), response); + Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response); responses.add(pairedResp); //break out of retry loop break; } catch (JanusGraphException e) { this.dbEngine.rollback(); - LOGGER.info ("Caught exception: " + e.getMessage()); + LOGGER.info("Caught exception: " + e.getMessage()); LoggingContext.restoreIfPossible(); AAIException ex = new AAIException("AAI_6142", e); ErrorLogHelper.logException(ex); @@ -630,10 +662,10 @@ public class HttpEntry { templateVars.addAll(e.getTemplateVars()); ErrorLogHelper.logException(e); response = Response - .status(e.getErrorObject().getHTTPResponseCode()) - .entity(ErrorLogHelper.getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars)) - .build(); - Pair<URI,Response> pairedResp = Pair.with(request.getUri(), response); + .status(e.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), e, templateVars)) + .build(); + Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response); responses.add(pairedResp); continue; } catch (Exception e) { @@ -645,9 +677,9 @@ public class HttpEntry { templateVars.add(request.getUri().getPath().toString()); ErrorLogHelper.logException(ex); response = Response - .status(ex.getErrorObject().getHTTPResponseCode()) - .entity(ErrorLogHelper.getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars)) - .build(); + .status(ex.getErrorObject().getHTTPResponseCode()) + .entity(ErrorLogHelper.getRESTAPIErrorResponse(request.getHeaders().getAcceptableMediaTypes(), ex, templateVars)) + .build(); Pair<URI, Response> pairedResp = Pair.with(request.getUri(), response); responses.add(pairedResp); continue; @@ -664,7 +696,7 @@ public class HttpEntry { * @param mediaTypeList the media type list * @return the media type */ - private String getMediaType(List <MediaType> mediaTypeList) { + private String getMediaType(List<MediaType> mediaTypeList) { String mediaType = MediaType.APPLICATION_JSON; // json is the default for (MediaType mt : mediaTypeList) { if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) { @@ -709,6 +741,44 @@ public class HttpEntry { } /** + * Gets the object from db. + * + * @param serializer the serializer + * @param query the query + * @param obj the obj + * @param uri the uri + * @param depth the depth + * @param cleanUp the clean up + * @return the object from db + * @throws AAIException the AAI exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws SecurityException the security exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + private Introspector getRelationshipObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, URI uri) throws AAIException, IllegalArgumentException, SecurityException, UnsupportedEncodingException, AAIUnknownObjectException { + + //nothing found + if (results.isEmpty()) { + String msg = createNotFoundMessage(query.getResultType(), uri); + throw new AAIException("AAI_6114", msg); + } + + if (results.size() > 1) { + throw new AAIException("AAI_6148", uri.getPath()); + } + + Vertex v = results.get(0); + return serializer.dbToRelationshipObject(v); + } + + /** * Invoke extension. * * @param dbEngine the db engine @@ -748,11 +818,11 @@ public class HttpEntry { ExtensionController ext = new ExtensionController(); ext.runExtension(aaiExtMap.getApiVersion(), - extensionInformation.getNamespace(), - extensionInformation.getTopObject(), - extensionInformation.getMethodName(httpMethod, isPreprocess), - aaiExtMap, - isPreprocess); + extensionInformation.getNamespace(), + extensionInformation.getTopObject(), + extensionInformation.getMethodName(httpMethod, isPreprocess), + aaiExtMap, + isPreprocess); if (aaiExtMap.getPrecheckAddedList().size() > 0) { response = notifyOnSkeletonCreation(aaiExtMap, obj, request.getHeaders()); @@ -793,12 +863,12 @@ public class HttpEntry { } templateVars.add(StringUtils.join(keys, ", ")); exceptionList.put(new AAIException("AAI_0004", msg.getAaiResponseMessageResourceType()), - templateVars); + templateVars); } response = Response - .status(Status.ACCEPTED).entity(ErrorLogHelper - .getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList)) - .build(); + .status(Status.ACCEPTED).entity(ErrorLogHelper + .getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList)) + .build(); return response; } @@ -818,6 +888,20 @@ public class HttpEntry { } /** + * Creates the not found message. + * + * @param resultType the result type + * @param uri the uri + * @return the string + */ + private String createRelationshipNotFoundMessage(String resultType, URI uri) { + + String msg = "No relationship found of type " + resultType + " at the given URI: " + uri.getPath() + "/relationship-list"; + + return msg; + } + + /** * Sets the depth. * * @param depthParam the depth param @@ -829,18 +913,18 @@ public class HttpEntry { String getAllRandomStr = AAIConfig.get("aai.rest.getall.depthparam", ""); if (depthParam != null && getAllRandomStr != null && !getAllRandomStr.isEmpty() - && getAllRandomStr.equals(depthParam)) { + && getAllRandomStr.equals(depthParam)) { return depth; } - if(depthParam == null){ - if(this.version.compareTo(schemaVersions.getDepthVersion()) >= 0){ + if (depthParam == null) { + if (this.version.compareTo(schemaVersions.getDepthVersion()) >= 0) { depth = 0; } else { depth = AAIProperties.MAXIMUM_DEPTH; } } else { - if (!depthParam.isEmpty() && !"all".equals(depthParam)){ + if (!depthParam.isEmpty() && !"all".equals(depthParam)) { try { depth = Integer.parseInt(depthParam); } catch (Exception e) { @@ -853,15 +937,15 @@ public class HttpEntry { int maximumDepth = AAIProperties.MAXIMUM_DEPTH; - if(maxDepth != null){ + if (maxDepth != null) { try { maximumDepth = Integer.parseInt(maxDepth); - } catch(Exception ex){ + } catch (Exception ex) { throw new AAIException("AAI_4018"); } } - if(depth > maximumDepth){ + if (depth > maximumDepth) { throw new AAIException("AAI_3303"); } @@ -937,7 +1021,7 @@ public class HttpEntry { while (depth >= 0 && !obj.isTopLevel()) { template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE); - if(template == null){ + if (template == null) { LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName()); return null; } @@ -945,12 +1029,12 @@ public class HttpEntry { int templateCount = StringUtils.countMatches(template, "/"); int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/"); - if(templateCount > truncatedUriCount){ + if (templateCount > truncatedUriCount) { LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri); return null; } - int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount-templateCount+1); + 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(); @@ -983,8 +1067,8 @@ public class HttpEntry { HashMap<String, Introspector> relatedVertices = new HashMap<>(); VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI); - if(!aaiUriProperty.isPresent()){ - if(LOGGER.isDebugEnabled()){ + 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"); @@ -994,18 +1078,18 @@ public class HttpEntry { String aaiUri = aaiUriProperty.value().toString(); - if(!obj.isTopLevel()){ + 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){ + 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){ + for (Vertex vertex : vertexChain) { try { final Introspector vertexObj = serializer.getVertexProperties(vertex); relatedVertices.put(vertexObj.getObjectId(), vertexObj); @@ -1050,8 +1134,9 @@ public class HttpEntry { URI uri; try { uri = serializer.getURIForVertex(entry.getKey()); - if (null != entry.getValue()) + if (null != entry.getValue()) { uriMap.put(entry.getValue().getObjectId(), uri); + } } catch (UnsupportedEncodingException e) { LOGGER.warn("Unable to get URIs, Just continue"); continue; @@ -1064,18 +1149,19 @@ public class HttpEntry { } private Map<String, HashMap<String, Introspector>> buildRelatedObjects(DBSerializer serializer, - QueryEngine queryEngine, Map<Vertex, Introspector> introSpector) { + QueryEngine queryEngine, Map<Vertex, Introspector> introSpector) { Map<String, HashMap<String, Introspector>> relatedObjectsMap = new HashMap<>(); for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) { try { HashMap<String, Introspector> relatedObjects = this.getRelatedObjects(serializer, queryEngine, - entry.getKey(), entry.getValue(), this.loader); - if (null != entry.getValue()) + entry.getKey(), entry.getValue(), this.loader); + if (null != entry.getValue()) { relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects); + } } catch (IllegalAccessException | IllegalArgumentException - | InvocationTargetException | SecurityException | InstantiationException | NoSuchMethodException - | UnsupportedEncodingException | AAIException | URISyntaxException e) { + | InvocationTargetException | SecurityException | InstantiationException | NoSuchMethodException + | UnsupportedEncodingException | AAIException | URISyntaxException e) { LOGGER.warn("Unable to get realted Objects, Just continue"); continue; } @@ -1087,8 +1173,8 @@ public class HttpEntry { } 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) { + UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap, + Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) { for (Map.Entry<Vertex, Introspector> entry : deleteObjects.entrySet()) { try { String vertexObjectId = ""; @@ -1096,9 +1182,10 @@ public class HttpEntry { if (null != entry.getValue()) { vertexObjectId = entry.getValue().getObjectId(); - if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) + if (uriMap.containsKey(vertexObjectId) && deleteRelatedObjects.containsKey(vertexObjectId)) { notification.createNotificationEvent(transactionId, sourceOfTruth, status, - uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId), basePath); + uriMap.get(vertexObjectId), entry.getValue(), deleteRelatedObjects.get(vertexObjectId), basePath); + } } } catch (UnsupportedEncodingException | AAIException e) { @@ -1114,17 +1201,17 @@ public class HttpEntry { } } - public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException{ + public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException { List<Object> vertices; - if(this.isPaginated()) { + 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); + 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{ + } else { vertices = vertexList; } return vertices; diff --git a/aai-core/src/main/java/org/onap/aai/restcore/HttpMethod.java b/aai-core/src/main/java/org/onap/aai/restcore/HttpMethod.java index 85c7dfa9..54215d6a 100644 --- a/aai-core/src/main/java/org/onap/aai/restcore/HttpMethod.java +++ b/aai-core/src/main/java/org/onap/aai/restcore/HttpMethod.java @@ -28,5 +28,6 @@ public enum HttpMethod { DELETE, PUT_EDGE, DELETE_EDGE, - GET; + GET, + GET_RELATIONSHIP; } diff --git a/aai-core/src/main/java/org/onap/aai/restcore/search/GremlinGroovyShell.java b/aai-core/src/main/java/org/onap/aai/restcore/search/GremlinGroovyShell.java new file mode 100644 index 00000000..1d074dd4 --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/restcore/search/GremlinGroovyShell.java @@ -0,0 +1,58 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.restcore.search; + +import groovy.lang.Binding; +import groovy.lang.Script; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; + +import java.util.Map; + +/** + * Creates and returns a groovy shell with the + * configuration to statically import graph classes + * + */ +public class GremlinGroovyShell extends AAIAbstractGroovyShell { + + public GremlinGroovyShell() { + super(); + } + + /** + * {@inheritDoc} + */ + @Override + public GraphTraversal<?, ?> executeTraversal (String traversal, Map<String, Object> params) { + Binding binding = new Binding(params); + Script script = shell.parse(traversal); + script.setBinding(binding); + return (GraphTraversal<?, ?>) script.run(); + } + + /** + * @throws UnsupportedOperationException + */ + @Override + public String executeTraversal(TransactionalGraphEngine engine, String traversal, Map<String, Object> params) { + throw new UnsupportedOperationException(); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/restcore/search/GroovyQueryBuilder.java b/aai-core/src/main/java/org/onap/aai/restcore/search/GroovyQueryBuilder.java new file mode 100644 index 00000000..48d4ba8c --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/restcore/search/GroovyQueryBuilder.java @@ -0,0 +1,74 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.restcore.search; + +import groovy.lang.Binding; +import groovy.lang.Script; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.onap.aai.config.SpringContextAware; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.LoaderFactory; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.query.builder.QueryBuilder; +import org.onap.aai.serialization.engines.QueryStyle; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.setup.SchemaVersions; + +import java.util.Map; + +/** + * Creates and returns a groovy shell with the + * configuration to statically import graph classes + * + */ +public class GroovyQueryBuilder extends AAIAbstractGroovyShell { + + public GroovyQueryBuilder() { + super(); + } + + /** + * {@inheritDoc} + */ + @Override + public String executeTraversal (TransactionalGraphEngine engine, String traversal, Map<String, Object> params) { + QueryBuilder<Vertex> builder = engine.getQueryBuilder(QueryStyle.GREMLIN_TRAVERSAL); + SchemaVersions schemaVersions = SpringContextAware.getBean(SchemaVersions.class); + Loader loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(ModelType.MOXY, schemaVersions.getDefaultVersion()); + + builder.changeLoader(loader); + Binding binding = new Binding(params); + binding.setVariable("builder", builder); + Script script = shell.parse(traversal); + script.setBinding(binding); + script.run(); + + return builder.getQuery(); + } + + /** + * @throws UnsupportedOperationException + */ + @Override + public GraphTraversal<?, ?> executeTraversal(String traversal, Map<String, Object> params) { + throw new UnsupportedOperationException(); + } +} diff --git a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java index 95582b98..a23ff1f9 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/db/DBSerializer.java @@ -992,6 +992,11 @@ public class DBSerializer { return simplePropsHashMap; } + public Introspector dbToRelationshipObject(Vertex v) throws UnsupportedEncodingException, AAIException { + Introspector relationshipList = this.latestLoader.introspectorFromName("relationship-list"); + relationshipList = createRelationshipList(v, relationshipList, "false"); + return relationshipList; + } /** * Creates the relationship list. * @@ -1235,7 +1240,7 @@ public class DBSerializer { } public void addRelatedToProperty(Introspector relationship, Vertex cousinVertex, String cousinType) throws AAIUnknownObjectException { - Introspector obj = loader.introspectorFromName(cousinType); + Introspector obj = this.latestLoader.introspectorFromName(cousinType); String nameProps = obj.getMetadata(ObjectMetadata.NAME_PROPS); List<Introspector> relatedToProperties = new ArrayList<>(); @@ -1734,11 +1739,11 @@ public class DBSerializer { /** * Verify resource version. * - * @param action the action - * @param nodeType the node type + * @param action the action + * @param nodeType the node type * @param currentResourceVersion the current resource version - * @param resourceVersion the resource version - * @param uri the uri + * @param resourceVersion the resource version + * @param uri the uri * @return true, if successful * @throws AAIException the AAI exception */ @@ -1746,6 +1751,7 @@ public class DBSerializer { String enabled = ""; String errorDetail = ""; String aaiExceptionCode = ""; + boolean isDeleteResourceVersionOk = true; if (currentResourceVersion == null) { currentResourceVersion = ""; } @@ -1755,12 +1761,16 @@ public class DBSerializer { } try { enabled = AAIConfig.get(AAIConstants.AAI_RESVERSION_ENABLEFLAG); + } catch (AAIException e) { ErrorLogHelper.logException(e); } if (enabled.equals("true")) { - if (!currentResourceVersion.equals(resourceVersion)) { - if (action.equals("create") && !resourceVersion.equals("")) { + if ("delete".equals(action)) { + isDeleteResourceVersionOk = verifyResourceVersionForDelete(currentResourceVersion, resourceVersion); + } + if ((!isDeleteResourceVersionOk) || ((!"delete".equals(action)) && (!currentResourceVersion.equals(resourceVersion)))) { + if ("create".equals(action) && !resourceVersion.equals("")) { errorDetail = "resource-version passed for " + action + " of " + uri; aaiExceptionCode = "AAI_6135"; } else if (resourceVersion.equals("")) { @@ -1779,12 +1789,31 @@ public class DBSerializer { } /** + * Verify resource version for delete. + * + * @param currentResourceVersion the current resource version + * @param resourceVersion the resource version + * @return true, if successful or false if there is a mismatch + */ + private boolean verifyResourceVersionForDelete(String currentResourceVersion, String resourceVersion) { + + boolean isDeleteResourceVersionOk = true; + String resourceVersionDisabledUuid = AAIConfig.get(AAIConstants.AAI_RESVERSION_DISABLED_UUID, + AAIConstants.AAI_RESVERSION_DISABLED_UUID_DEFAULT); + + if ((!currentResourceVersion.equals(resourceVersion)) && (!resourceVersion.equals(resourceVersionDisabledUuid))) { + isDeleteResourceVersionOk = false; + } + return isDeleteResourceVersionOk; + } + + /** * Convert from camel case. * * @param name the name * @return the string */ - private String convertFromCamelCase (String name) { + private String convertFromCamelCase(String name) { String result = ""; result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name); @@ -1810,7 +1839,7 @@ public class DBSerializer { private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException { SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build(); + .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build(); runner.execute(obj, self); } @@ -1818,15 +1847,15 @@ public class DBSerializer { private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException { SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build(); + .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build(); runner.execute(obj, self); } - private void enrichData(Introspector obj, Vertex self) throws AAIException { + private void enrichData(Introspector obj, Vertex self) throws AAIException { SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build(); + .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build(); runner.execute(obj, self); } diff --git a/aai-core/src/main/java/org/onap/aai/service/NodeValidationService.java b/aai-core/src/main/java/org/onap/aai/service/NodeValidationService.java new file mode 100644 index 00000000..c7ef769b --- /dev/null +++ b/aai-core/src/main/java/org/onap/aai/service/NodeValidationService.java @@ -0,0 +1,56 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2018 AT&T Intellectual Property. 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.service; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.onap.aai.validation.nodes.NodeValidator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; + +@Service +@ConditionalOnProperty(name = "schema.translator.list", havingValue = "config", matchIfMissing = true) +@PropertySource(value = "classpath:schema-ingest.properties", ignoreResourceNotFound = true) +@PropertySource(value = "file:${schema.ingest.file}", ignoreResourceNotFound = true) +public class NodeValidationService { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(NodeValidationService.class); + + @Autowired(required=false) + private NodeValidator nodeValidator; + + public NodeValidationService(NodeValidator nodeValidator){ + this.nodeValidator = nodeValidator; + } + + @PostConstruct + public void initialize(){ + if(!nodeValidator.validate()){ + LOGGER.warn(nodeValidator.getErrorMsg()); + } else { + LOGGER.info("Node validation check passed"); + } + } +} + diff --git a/aai-core/src/main/java/org/onap/aai/util/AAIConstants.java b/aai-core/src/main/java/org/onap/aai/util/AAIConstants.java index c7a7b4a1..b735f722 100644 --- a/aai-core/src/main/java/org/onap/aai/util/AAIConstants.java +++ b/aai-core/src/main/java/org/onap/aai/util/AAIConstants.java @@ -29,9 +29,9 @@ public final class AAIConstants { public static final String AAI_FILESEP = (System.getProperty("file.separator") == null) ? "/" : System.getProperty("file.separator"); // /** Default to opt aai if system property aai.home is null, using file.separator */ - public static final String AAI_HOME = (System.getProperty(AJSC_HOME) == null) ? AAI_FILESEP + "opt" + AAI_FILESEP + "app" + AAI_FILESEP +"aai" : System.getProperty(AJSC_HOME); + public static final String AAI_HOME = (System.getProperty(AJSC_HOME) == null) ? AAI_FILESEP + "opt" + AAI_FILESEP + "app" + AAI_FILESEP +"aai" : System.getProperty(AJSC_HOME); public static final String AAI_BUNDLECONFIG_NAME = (System.getProperty("BUNDLECONFIG_DIR") == null) ? "bundleconfig" : System.getProperty("BUNDLECONFIG_DIR"); - public static final String AAI_HOME_BUNDLECONFIG = (System.getProperty(AJSC_HOME) == null) ? AAI_FILESEP + "opt" + AAI_FILESEP + "app" + AAI_FILESEP + "aai" + AAI_FILESEP + AAI_BUNDLECONFIG_NAME : System.getProperty(AJSC_HOME)+ AAI_FILESEP + AAI_BUNDLECONFIG_NAME; + public static final String AAI_HOME_BUNDLECONFIG = (System.getProperty(AJSC_HOME) == null) ? AAI_FILESEP + "opt" + AAI_FILESEP + "app" + AAI_FILESEP + "aai" + AAI_FILESEP + AAI_BUNDLECONFIG_NAME : System.getProperty(AJSC_HOME)+ AAI_FILESEP + AAI_BUNDLECONFIG_NAME; /** etc directory, relative to AAI_HOME */ public static final String AAI_HOME_ETC = AAI_HOME_BUNDLECONFIG + AAI_FILESEP + "etc" + AAI_FILESEP; @@ -63,10 +63,10 @@ public final class AAIConstants { public static final int AAI_QUERY_PORT = 8446; public static final int AAI_LEGACY_PORT = 8443; - public static final String AAI_DEFAULT_API_VERSION = "v7"; + public static final String AAI_DEFAULT_API_VERSION = "v10"; public static final String AAI_DEFAULT_API_VERSION_PROP = "aai.default.api.version"; public static final String AAI_NOTIFICATION_CURRENT_VERSION = "aai.notification.current.version"; - + public static final String AAI_NODENAME = "aai.config.nodename"; public static final String AAI_BULKCONSUMER_LIMIT = "aai.bulkconsumer.payloadlimit"; @@ -85,15 +85,12 @@ public final class AAIConstants { public static final String AAI_CRUD_TIMEOUT_APP = "aai.crud.timeout.appspecific"; public static final String AAI_RESVERSION_ENABLEFLAG = "aai.resourceversion.enableflag"; + public static final String AAI_RESVERSION_DISABLED_UUID = "aai.resourceversion.disabled.uuid"; + public static final String AAI_RESVERSION_DISABLED_UUID_DEFAULT = "38cf3090-6a0c-4e9d-8142-4332a7352846"; + - public static final int AAI_GROOMING_DEFAULT_MAX_FIX = 150; - public static final int AAI_GROOMING_DEFAULT_SLEEP_MINUTES = 7; - - public static final int AAI_DUPETOOL_DEFAULT_MAX_FIX = 25; - public static final int AAI_DUPETOOL_DEFAULT_SLEEP_MINUTES = 7; - public static final long HISTORY_MAX_HOURS = 192; - + public static final String LOGGING_MAX_STACK_TRACE_ENTRIES = "aai.logging.maxStackTraceEntries"; /*** UEB ***/ diff --git a/aai-core/src/main/java/org/onap/aai/util/GenerateXsd.java b/aai-core/src/main/java/org/onap/aai/util/GenerateXsd.java index 84f06064..97803653 100644 --- a/aai-core/src/main/java/org/onap/aai/util/GenerateXsd.java +++ b/aai-core/src/main/java/org/onap/aai/util/GenerateXsd.java @@ -42,8 +42,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GenerateXsd { - - private static final Logger logger = LoggerFactory.getLogger("GenerateXsd.class"); + + private static final Logger logger = LoggerFactory.getLogger("GenerateXsd.class"); protected static String apiVersion = null; public static AnnotationConfigApplicationContext ctx = null; static String apiVersionFmt = null; @@ -76,18 +76,18 @@ public class GenerateXsd { public static final int VALUE_DESCRIPTION = 1; public static final int VALUE_INDEXED_PROPS = 2; public static final int VALUE_CONTAINER = 3; - + private static final String generateTypeXSD = "xsd"; private static final String generateTypeYAML = "yaml"; - + private final static String nodeDir = System.getProperty("nodes.configuration.location"); private final static String edgeDir = System.getProperty("edges.configuration.location"); private static final String baseRoot = "aai-schema/"; private static final String baseAutoGenRoot = "aai-schema/"; - + private static final String root = baseRoot + "src/main/resources"; private static final String autoGenRoot = baseAutoGenRoot + "src/main/resources"; - + private static final String normalStartDir = "aai-core"; private static final String xsd_dir = root + "/" + RELEASE +"/aai_schema"; @@ -97,9 +97,9 @@ public class GenerateXsd { private static int swaggerSupportStartsVersion = 1; // minimum version to support swagger documentation - + private static boolean validVersion(String versionToGen) { - + if ("ALL".equalsIgnoreCase(versionToGen)) { return true; } @@ -113,14 +113,14 @@ public class GenerateXsd { return false; } - + private static boolean versionSupportsSwagger( String version) { if (new Integer(version.substring(1)).intValue() >= swaggerSupportStartsVersion ) { return true; } return false; } - + public static String getAPIVersion() { return apiVersion; } @@ -146,19 +146,19 @@ public class GenerateXsd { if ( fileTypeToGen == null ) { fileTypeToGen = generateTypeXSD; } - + if ( !fileTypeToGen.equals( generateTypeXSD ) && !fileTypeToGen.equals( generateTypeYAML )) { System.err.println("Invalid gen_type passed. " + fileTypeToGen); System.exit(1); } - + String responsesLabel = System.getProperty("yamlresponses_url"); responsesUrl = responsesLabel; - + List<SchemaVersion> versionsToGen = new ArrayList<>(); if ( versionToGen == null ) { System.err.println("Version is required, ie v<n> or ALL."); - System.exit(1); + System.exit(1); } else if (!"ALL".equalsIgnoreCase(versionToGen) && !versionToGen.matches("v\\d+") && !validVersion(versionToGen)) { System.err.println("Invalid version passed. " + versionToGen); @@ -171,14 +171,14 @@ public class GenerateXsd { } else { versionsToGen.add(new SchemaVersion(versionToGen)); } - + //process file type System property fileTypeToGen = (fileTypeToGen == null ? generateTypeXSD : fileTypeToGen.toLowerCase()); if ( !fileTypeToGen.equals( generateTypeXSD ) && !fileTypeToGen.equals( generateTypeYAML )) { System.err.println("Invalid gen_type passed. " + fileTypeToGen); System.exit(1); } else if ( fileTypeToGen.equals(generateTypeYAML) ) { - if ( responsesUrl == null || responsesUrl.length() < 1 + if ( responsesUrl == null || responsesUrl.length() < 1 || responsesLabel == null || responsesLabel.length() < 1 ) { System.err.println("generating swagger yaml file requires yamlresponses_url and yamlresponses_label properties" ); System.exit(1); @@ -217,11 +217,11 @@ public class GenerateXsd { logger.debug("user.dir = "+System.getProperty("user.dir")); if(System.getProperty("user.dir") != null && !System.getProperty("user.dir").contains(normalStartDir)) { fileName = baseAutoGenRoot + fileName; - + } else { fileName = baseRoot + fileName; - + } edgeRuleFile = new File( fileName); // Document doc = ni.getSchema(translateVersion(v)); @@ -232,6 +232,9 @@ public class GenerateXsd { HTMLfromOXM swagger = ctx.getBean(HTMLfromOXM.class); swagger.setVersion(v); fileContent = swagger.process(); + if ( fileContent.startsWith("Schema format issue")) { + throw new Exception(fileContent); + } } catch(Exception e) { logger.error( "Exception creating output file " + outfileName); logger.error( e.getMessage()); @@ -241,7 +244,7 @@ public class GenerateXsd { } else if ( versionSupportsSwagger(apiVersion )) { outfileName = yaml_dir + "/aai_swagger_" + apiVersion + "." + generateTypeYAML; nodesfileName = yaml_dir + "/aai_swagger_" + apiVersion + "." + "nodes"+"."+generateTypeYAML; - try { + try { YAMLfromOXM swagger = (YAMLfromOXM) ctx.getBean(YAMLfromOXM.class); swagger.setVersion(v); fileContent = swagger.process(); @@ -259,13 +262,13 @@ public class GenerateXsd { } outfile = new File(outfileName); File parentDir = outfile.getParentFile(); - if(! parentDir.exists()) + if(! parentDir.exists()) parentDir.mkdirs(); if(nodesfileName != null) { BufferedWriter nodesBW = null; nodesfile = new File(nodesfileName); parentDir = nodesfile.getParentFile(); - if(! parentDir.exists()) + if(! parentDir.exists()) parentDir.mkdirs(); try { nodesfile.createNewFile(); @@ -287,7 +290,7 @@ public class GenerateXsd { } } } - + try { outfile.createNewFile(); } catch (IOException e) { @@ -310,9 +313,8 @@ public class GenerateXsd { } logger.debug( "GeneratedXSD successful, saved in " + outfileName); } - + } } -
\ No newline at end of file diff --git a/aai-core/src/main/java/org/onap/aai/util/genxsd/NodesYAMLfromOXM.java b/aai-core/src/main/java/org/onap/aai/util/genxsd/NodesYAMLfromOXM.java index 0b89ce10..5abd8f48 100644 --- a/aai-core/src/main/java/org/onap/aai/util/genxsd/NodesYAMLfromOXM.java +++ b/aai-core/src/main/java/org/onap/aai/util/genxsd/NodesYAMLfromOXM.java @@ -131,8 +131,7 @@ public class NodesYAMLfromOXM extends OxmFileProcessor { } XSDElement javaTypeElement = new XSDElement(elem); - - + logger.debug("External: "+javaTypeElement.getAttribute("name")+"/"+getXmlRootElementName(javaTypeName)); if ( javaTypeName == null ) { String msg = "Invalid OXM file: <java-type> has no name attribute in " + oxmFile; @@ -140,7 +139,6 @@ public class NodesYAMLfromOXM extends OxmFileProcessor { throw new AAIException(msg); } namespaceFilter.add(getXmlRootElementName(javaTypeName)); - processJavaTypeElementSwagger( javaTypeName, javaTypeElement, pathSb, definitionsSb, null, null, null, null, null, null); } @@ -274,7 +272,11 @@ public class NodesYAMLfromOXM extends OxmFileProcessor { } if ( indexedProps != null && indexedProps.contains(xmlElementElement.getAttribute("name") ) ) { - containerProps.add(xmlElementElement.getQueryParamYAML()); + containerProps.add(xmlElementElement.getQueryParamYAML(elementDescription)); + NodeGetOperation.addContainerProps(container, containerProps); + } else if ( indexedProps == null || indexedProps.isEmpty() && "true".equals(xmlElementElement.getAttribute("required")) ){ + // no indexedProps and element is required, also considered using xml-key in this case + containerProps.add(xmlElementElement.getPathParamYAMLRqd(elementDescription, false)); NodeGetOperation.addContainerProps(container, containerProps); } if ( xmlElementElement.isStandardType()) { @@ -500,8 +502,7 @@ public class NodesYAMLfromOXM extends OxmFileProcessor { } Path path = Paths.get(outfileName); Charset charset = Charset.forName("UTF-8"); - try { - BufferedWriter bw = Files.newBufferedWriter(path, charset); + try(BufferedWriter bw = Files.newBufferedWriter(path, charset);) { bw.write(fileContent); if ( bw != null ) { bw.close(); diff --git a/aai-core/src/main/java/org/onap/aai/util/genxsd/OxmFileProcessor.java b/aai-core/src/main/java/org/onap/aai/util/genxsd/OxmFileProcessor.java index a758be50..8edab757 100644 --- a/aai-core/src/main/java/org/onap/aai/util/genxsd/OxmFileProcessor.java +++ b/aai-core/src/main/java/org/onap/aai/util/genxsd/OxmFileProcessor.java @@ -43,7 +43,6 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; - import org.onap.aai.edges.EdgeIngestor; import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException; import org.onap.aai.exceptions.AAIException; @@ -65,7 +64,7 @@ public abstract class OxmFileProcessor { public static final String LINE_SEPARATOR = System.getProperty("line.separator"); public static final String DOUBLE_LINE_SEPARATOR = System.getProperty("line.separator") + System.getProperty("line.separator"); - + EdgeIngestor ei; NodeIngestor ni; protected Set<String> namespaceFilter; @@ -75,17 +74,17 @@ public abstract class OxmFileProcessor { protected Document doc = null; protected String apiVersion = null; protected SchemaVersions schemaVersions; - - protected Map combinedJavaTypes; - - + + protected static int annotationsStartVersion = 9; // minimum version to support annotations in xsd protected static int annotationsMinVersion = 6; // lower versions support annotations in xsd protected static int swaggerSupportStartsVersion = 1; // minimum version to support swagger documentation protected static int swaggerDiffStartVersion = 1; // minimum version to support difference protected static int swaggerMinBasepath = 6; // minimum version to support difference - - + + protected Map combinedJavaTypes; + + protected String apiVersionFmt = null; protected HashMap<String, String> generatedJavaType = new HashMap<String, String>(); protected HashMap<String, String> appliedPaths = new HashMap<String, String>(); @@ -125,8 +124,8 @@ public abstract class OxmFileProcessor { this.ni = ni; this.ei = ei; } - - + + public void setOxmVersion(File oxmFile, SchemaVersion v) { this.oxmFile = oxmFile; @@ -137,16 +136,16 @@ public abstract class OxmFileProcessor { this.xml = xml; this.v = v; } - + public void setVersion(SchemaVersion v) { this.oxmFile = null; this.v = v; } - + public void setNodeIngestor(NodeIngestor ni) { this.ni = ni; } - + public void setEdgeIngestor(EdgeIngestor ei) { this.ei = ei; } @@ -158,25 +157,25 @@ public abstract class OxmFileProcessor { public void setSchemaVersions(SchemaVersions schemaVersions) { this.schemaVersions = schemaVersions; } - + protected void init() throws ParserConfigurationException, SAXException, IOException, AAIException, EdgeRuleNotFoundException { - if(this.xml != null || this.oxmFile != null ) { + if(this.xml != null || this.oxmFile != null ) { createDocument(); } if(this.doc == null) { this.doc = ni.getSchema(v); } namespaceFilter = new HashSet<>(); - + NodeList bindingsNodes = doc.getElementsByTagName("xml-bindings"); Element bindingElement; NodeList javaTypesNodes; Element javaTypesElement; - + if ( bindingsNodes == null || bindingsNodes.getLength() == 0 ) { throw new AAIException("OXM file error: missing <binding-nodes> in " + oxmFile); - } - + } + bindingElement = (Element) bindingsNodes.item(0); javaTypesNodes = bindingElement.getElementsByTagName("java-types"); if ( javaTypesNodes.getLength() < 1 ) { @@ -192,14 +191,14 @@ public abstract class OxmFileProcessor { private void createDocument() throws ParserConfigurationException, SAXException, IOException, AAIException { DocumentBuilder dBuilder = null; - try { + try { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); dbFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); dBuilder = dbFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { throw e; } - try { + try { if ( xml == null ) { doc = dBuilder.parse(oxmFile); } else { @@ -215,11 +214,11 @@ public abstract class OxmFileProcessor { } public abstract String getDocumentHeader(); public abstract String process() throws ParserConfigurationException, SAXException, IOException, AAIException, FileNotFoundException, EdgeRuleNotFoundException ; - + public String getXMLRootElementName(Element javaTypeElement) { String xmlRootElementName=null; NamedNodeMap attributes; - + NodeList valNodes = javaTypeElement.getElementsByTagName("xml-root-element"); Element valElement = (Element) valNodes.item(0); attributes = valElement.getAttributes(); @@ -233,7 +232,7 @@ public abstract class OxmFileProcessor { } return xmlRootElementName; } - + public String getXmlRootElementName( String javaTypeName ) { String attrName, attrValue; @@ -263,22 +262,22 @@ public abstract class OxmFileProcessor { } return null; } - + public Map getCombinedJavaTypes() { return combinedJavaTypes; } - + public void setCombinedJavaTypes(Map combinedJavaTypes) { this.combinedJavaTypes = combinedJavaTypes; } - + public Element getJavaTypeElementSwagger( String javaTypeName ) { - + String attrName, attrValue; Attr attr; Element javaTypeElement; - + List<Element> combineElementList = new ArrayList<Element>(); for ( int i = 0; i < javaTypeNodes.getLength(); ++ i ) { javaTypeElement = (Element) javaTypeNodes.item(i); @@ -295,12 +294,11 @@ public abstract class OxmFileProcessor { if ( combineElementList.size() == 0 ) { return (Element) null; } else if ( combineElementList.size() > 1 ) { - // need to combine java-attributes return combineElements( javaTypeName, combineElementList); } return combineElementList.get(0); } - + public boolean versionSupportsSwaggerDiff( String version) { int ver = new Integer(version.substring(1)).intValue(); if ( ver >= HTMLfromOXM.swaggerDiffStartVersion ) { @@ -308,7 +306,7 @@ public abstract class OxmFileProcessor { } return false; } - + public boolean versionSupportsBasePathProperty( String version) { int ver = new Integer(version.substring(1)).intValue(); if ( ver <= HTMLfromOXM.swaggerMinBasepath ) { @@ -340,11 +338,11 @@ public abstract class OxmFileProcessor { for ( int i = 0; i < moreXmlElementNodes.getLength(); ++i ) { xmlElement = (Element)moreXmlElementNodes.item(i); - childNode = xmlElement.cloneNode(true); + childNode = xmlElement.cloneNode(true); parentElement.insertBefore(childNode, refChild); } } - + protected Node getXmlPropertiesNode(Element javaTypeElement ) { NodeList nl = javaTypeElement.getChildNodes(); Node child; @@ -356,7 +354,7 @@ public abstract class OxmFileProcessor { } return null; } - + protected Node merge( NodeList nl, Node mergeNode ) { NamedNodeMap nnm = mergeNode.getAttributes(); Node childNode; @@ -390,7 +388,7 @@ public abstract class OxmFileProcessor { childNode = mergeNode.cloneNode(true); return childNode; } - + protected void mergeXmlProperties(Node useChildProperties, NodeList propertiesToMerge ) { NodeList nl = useChildProperties.getChildNodes(); Node childNode; @@ -403,10 +401,10 @@ public abstract class OxmFileProcessor { useChildProperties.appendChild(newNode); } } - + } } - + protected void combineXmlProperties(int useElement, List<Element> combineElementList) { // add or update xml-properties to the referenced element from the combined list Element javaTypeElement = combineElementList.get(useElement); @@ -443,7 +441,7 @@ public abstract class OxmFileProcessor { } } - + protected Element combineElements( String javaTypeName, List<Element> combineElementList ) { Element javaTypeElement; NodeList parentNodes; @@ -486,7 +484,7 @@ public abstract class OxmFileProcessor { parentNodes = javaTypeElement.getElementsByTagName("java-attributes"); if ( parentNodes.getLength() == 0 ) { continue; - } + } otherParentElement = (Element)parentNodes.item(0); xmlElementNodes = otherParentElement.getElementsByTagName("xml-element"); if ( xmlElementNodes.getLength() <= 0 ) { @@ -494,7 +492,7 @@ public abstract class OxmFileProcessor { } // xml-element that are not present updateParentXmlElements( parentElement, xmlElementNodes); - + } } // need to combine xml-properties @@ -502,7 +500,7 @@ public abstract class OxmFileProcessor { combinedJavaTypes.put( javaTypeName, useElement); return combineElementList.get(useElement); } - + private static void prettyPrint(Node node, String tab) { diff --git a/aai-core/src/main/java/org/onap/aai/util/genxsd/PutOperation.java b/aai-core/src/main/java/org/onap/aai/util/genxsd/PutOperation.java index cb5e779e..6597d034 100644 --- a/aai-core/src/main/java/org/onap/aai/util/genxsd/PutOperation.java +++ b/aai-core/src/main/java/org/onap/aai/util/genxsd/PutOperation.java @@ -84,14 +84,14 @@ public class PutOperation { pathSb.append(" operationId: createOrUpdate" + useOpId + "\n"); pathSb.append(" consumes:\n"); pathSb.append(" - application/json\n"); - pathSb.append(" - application/xml\n"); + pathSb.append(" - application/xml\n"); pathSb.append(" produces:\n"); pathSb.append(" - application/json\n"); pathSb.append(" - application/xml\n"); pathSb.append(" responses:\n"); pathSb.append(" \"default\":\n"); pathSb.append(" " + GenerateXsd.getResponsesUrl()); - + pathSb.append(" parameters:\n"); pathSb.append(pathParams); // for nesting pathSb.append(" - name: body\n"); @@ -99,7 +99,11 @@ public class PutOperation { pathSb.append(" description: " + xmlRootElementName + " object that needs to be created or updated. "+relationshipExamplesSb.toString()+"\n"); pathSb.append(" required: true\n"); pathSb.append(" schema:\n"); - pathSb.append(" $ref: \"#/definitions/" + xmlRootElementName + "\"\n"); + String useElement = xmlRootElementName; + if ( xmlRootElementName.equals("relationship")) { + useElement += "-dict"; + } + pathSb.append(" $ref: \"#/definitions/" + useElement + "\"\n"); this.tagRelationshipPathMapEntry(); return pathSb.toString(); } @@ -110,5 +114,5 @@ public class PutOperation { } return ""; } - - }
\ No newline at end of file + + } diff --git a/aai-core/src/main/java/org/onap/aai/util/genxsd/XSDElement.java b/aai-core/src/main/java/org/onap/aai/util/genxsd/XSDElement.java index 9de7967e..db7c54c3 100644 --- a/aai-core/src/main/java/org/onap/aai/util/genxsd/XSDElement.java +++ b/aai-core/src/main/java/org/onap/aai/util/genxsd/XSDElement.java @@ -52,13 +52,13 @@ public class XSDElement implements Element { this.xmlElementElement = xmlElementElement; this.maxOccurs = maxOccurs; } - + public XSDElement(Element xmlElementElement) { super(); this.xmlElementElement = xmlElementElement; this.maxOccurs = null; } - + public String name() { return this.getAttribute("name"); } @@ -235,13 +235,23 @@ public class XSDElement implements Element { } public String getQueryParamYAML() { + return getQueryParamYAML(null); + } + + public String getQueryParamYAML(String elementDescription ) { + // when elementDescription is not null, return parameters with description and example StringBuffer sbParameter = new StringBuffer(); sbParameter.append((" - name: " + this.getAttribute("name") + "\n")); sbParameter.append((" in: query\n")); - if ( this.getAttribute("description") != null && this.getAttribute("description").length() > 0 ) - sbParameter.append((" description: " + this.getAttribute("description") + "\n")); - else + String useDescription = elementDescription; + if ( elementDescription == null ) { + useDescription = this.getAttribute("description"); + } + if ( useDescription != null && useDescription.length() > 0 ) { + sbParameter.append((" description: " + useDescription + "\n")); + } else { sbParameter.append((" description:\n")); + } sbParameter.append((" required: false\n")); if ( ("java.lang.String").equals(this.getAttribute("type"))) sbParameter.append(" type: string\n"); @@ -256,16 +266,28 @@ public class XSDElement implements Element { if ( ("java.lang.Boolean").equals(this.getAttribute("type"))) { sbParameter.append(" type: boolean\n"); } + if ( elementDescription != null && StringUtils.isNotBlank(this.getAttribute("name"))) { + sbParameter.append(" example: "+"__"+this.getAttribute("name").toUpperCase()+"__"+"\n"); + } return sbParameter.toString(); } public String getPathParamYAML(String elementDescription) { + return getPathParamYAMLRqd( elementDescription, true); + } + + public String getPathParamYAMLRqd(String elementDescription, boolean isRqd) { StringBuffer sbParameter = new StringBuffer(); sbParameter.append((" - name: " + this.getAttribute("name") + "\n")); sbParameter.append((" in: path\n")); if ( elementDescription != null && elementDescription.length() > 0 ) sbParameter.append((" description: " + elementDescription + "\n")); - sbParameter.append((" required: true\n")); + if ( isRqd ) { + sbParameter.append((" required: true\n")); + } else { + // used by Nodes API for query params + sbParameter.append((" required: false\n")); + } if ( ("java.lang.String").equals(this.getAttribute("type"))) sbParameter.append(" type: string\n"); if ( ("java.lang.Long").equals(this.getAttribute("type"))) { @@ -306,7 +328,7 @@ public class XSDElement implements Element { sbElement.append(" type=\"xs:int\""); if ( elementType.equals("java.lang.Boolean")) sbElement.append(" type=\"xs:boolean\""); - if ( addType != null || elementType.startsWith("java.lang.") ) { + if ( addType != null || elementType.startsWith("java.lang.") ) { sbElement.append(" minOccurs=\"0\""); } if ( elementContainerType != null && elementContainerType.equals("java.util.ArrayList")) { diff --git a/aai-core/src/main/java/org/onap/aai/util/genxsd/YAMLfromOXM.java b/aai-core/src/main/java/org/onap/aai/util/genxsd/YAMLfromOXM.java index a14a6977..f807bcb1 100644 --- a/aai-core/src/main/java/org/onap/aai/util/genxsd/YAMLfromOXM.java +++ b/aai-core/src/main/java/org/onap/aai/util/genxsd/YAMLfromOXM.java @@ -112,7 +112,7 @@ public class YAMLfromOXM extends OxmFileProcessor { try { init(); } catch(Exception e) { - logger.error( "Error initializing " + this.getClass()); + logger.error( "Error initializing " + this.getClass(),e); throw e; } pathSb.append(getDocumentHeader()); @@ -138,7 +138,6 @@ public class YAMLfromOXM extends OxmFileProcessor { throw new AAIException(msg); } namespaceFilter.add(getXmlRootElementName(javaTypeName)); - processJavaTypeElementSwagger( javaTypeName, javaTypeElement, pathSb, definitionsSb, null, null, null, null, null, null); } @@ -235,7 +234,7 @@ public class YAMLfromOXM extends OxmFileProcessor { private String processJavaTypeElementSwagger( String javaTypeName, Element javaTypeElement, StringBuffer pathSb, StringBuffer definitionsSb, String path, String tag, String opId, String getItemName, StringBuffer pathParams, String validEdges) { - + String xmlRootElementName = getXMLRootElementName(javaTypeElement); StringBuilder definitionsLocalSb = new StringBuilder(256); @@ -468,7 +467,7 @@ public class YAMLfromOXM extends OxmFileProcessor { results.get(key).stream().filter((i) -> (i.getFrom().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("OUT")))).forEach((i) ->{ preventDelete.add(i.getTo().toUpperCase());} ); } } catch(Exception e) { - logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e); + logger.debug("xmlRootElementName: "+xmlRootElementName+" from edge exception\n", e); } try { EdgeRuleQuery q1 = new EdgeRuleQuery.Builder(xmlRootElementName).version(v).toOnly().build(); @@ -481,7 +480,7 @@ public class YAMLfromOXM extends OxmFileProcessor { results.get(key).stream().filter((i) -> (i.getTo().equals(xmlRootElementName) && (! i.isPrivateEdge() && i.getPreventDelete().equals("IN")))).forEach((i) ->{ preventDelete.add(i.getFrom().toUpperCase());} ); } } catch(Exception e) { - logger.debug("xmlRootElementName: "+xmlRootElementName+"\n"+e); + logger.debug("xmlRootElementName: "+xmlRootElementName+" to edge exception\n", e); } if(preventDelete.size() > 0) { prevent = xmlRootElementName.toUpperCase()+" cannot be deleted if related to "+String.join(",",preventDelete); @@ -536,6 +535,7 @@ public class YAMLfromOXM extends OxmFileProcessor { } } catch (Exception e) { e.printStackTrace(); + logger.error("Exception adding in javaTypeDefinitions",e); } if ( xmlRootElementName.equals("inventory") ) { logger.trace("skip xmlRootElementName(2)="+xmlRootElementName); @@ -568,15 +568,10 @@ public class YAMLfromOXM extends OxmFileProcessor { logger.error( "Exception creating output file " + outfileName); e.printStackTrace(); } - BufferedWriter bw = null; Charset charset = Charset.forName("UTF-8"); Path path = Paths.get(outfileName); - try { - bw = Files.newBufferedWriter(path, charset); + try(BufferedWriter bw = Files.newBufferedWriter(path, charset)){ bw.write(fileContent); - if ( bw != null ) { - bw.close(); - } } catch ( IOException e) { logger.error( "Exception writing output file " + outfileName); e.printStackTrace(); @@ -600,4 +595,4 @@ public class YAMLfromOXM extends OxmFileProcessor { return false; } -}
\ No newline at end of file +} diff --git a/aai-core/src/main/java/org/onap/aai/web/EventClientPublisher.java b/aai-core/src/main/java/org/onap/aai/web/EventClientPublisher.java index ac897273..94d8f4cd 100644 --- a/aai-core/src/main/java/org/onap/aai/web/EventClientPublisher.java +++ b/aai-core/src/main/java/org/onap/aai/web/EventClientPublisher.java @@ -37,7 +37,7 @@ public class EventClientPublisher { private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(EventClientPublisher.class); - @Value("${dmaap.ribbon.listOfServers}") + @Value("${dmaap.ribbon.listOfServers:}") private String hosts; @Value("${dmaap.ribbon.username:}") diff --git a/aai-core/src/main/resources/schema-ingest.properties b/aai-core/src/main/resources/schema-ingest.properties new file mode 100644 index 00000000..4bae01db --- /dev/null +++ b/aai-core/src/main/resources/schema-ingest.properties @@ -0,0 +1 @@ +schema.translator.list=config |