diff options
author | Kajur, Harish (vk250x) <vk250x@att.com> | 2018-12-04 14:06:14 -0500 |
---|---|---|
committer | Kajur, Harish (vk250x) <vk250x@att.com> | 2018-12-05 09:38:36 -0500 |
commit | 8fb7aa6480d4d7becbbab5fcfabd6af3f57e7d74 (patch) | |
tree | e6cb6aa0e6839100a06a787c8ff3034aac11dbfb | |
parent | 7bc1735a516e56d837aa5af3d4849cdcb7af26c7 (diff) |
Improve the performance of resoures microservice1.4.0
Currently the findSubGraph is being invoked
and causing additional calls to the database by backing
a tree backed vertex and slowing the performance of GET ALLs
Original intention of both of them was to pre fetch depth 0, 1, and 2
at once so we don't have to get them at each depth but since
that was not done right there is extra amount of time
After the aai-uri migration to ensure all vertexes
have the aai-uri and also to make sure they are unique across graph
we don't need to traverse a vertex to find the parents to build aai-uri
it was done previously when there was no aai-uri to derive the uri of a
given vertex and its not necessary anymore so there would be performance
improvements of vserver when there are a lot of relationships
Currently the edge labels are retrieved for each vertex a
and b and the performance of the GET with relationships will be
propotional to the GET request and how many cousin edges it has
and the more cousin edges there are the more slower the response time
will be as for each cousin vertex its trying to get the edge in between
so the code is modified to only go to the database for the edge label
when there are multiple edge labels (cousin edges) between a and b
If there are only one edge label and its a cousin vertex then we
can use the edge rule to be able to figure out the edge label
Improve PUT on the cloud region by modifying the getEdgesBetween
method which currently was retrieving all the parent
child edges between a and b and then only using the first edge
The traversal itself was too complicated and was costly in terms of
database retrieval and calls and optimized the code to utilize the
edge information so we can modify the query to db at runtime
based on the information provided so when a 10000 vservers under a
tenant adding a new vserver under tenant would be slow because of the
old query performance as it was taking some time there but with this
optimization, its only spending at most a millisecond or 2 in that
method
Also noticed that when a PUT operation takes place, the method
calls the related objects to create a dmaap event which was in turn
calling the findParents and actually utilizing the parents to create the
dmaap event and would spend quite a lot of time here because of the
expensive call of the findParents in this case we need those vertexes
So optimized the code so based on a given vertex, we have the aai-uri
and the newly added metadata uriTemplate to break the aai-uri into
its parent aai-uri and grand parent aai-uri and so forth
With this breakdown, we can get the list of aai-uris which are parents,
grandparents and then use the aai-uri to look them up which is O(1)
lookup time due to the fact they are unique indexes
The time it takes when doing a traversal to find the parents is
propotional to the number of edges but this will be optimistic
Another area which was improved was the json path execution of the
edge rules so when the edge rules get loaded into memory it creates a
document object, it utilizes the jsonpath to query information about
the edge rules but the only thing here is each time it gets called
the query gets invoked and uses jsonpath to retrieve the edge rules when
we can cached them based on the filter so that the user executed and if
the filter is the same as before, the expected edge rules will return
the same
Too much time was spent making the query and retrieving and building the
edgerules
So a cache is a perfect way to optimize this part
Issue-ID: AAI-1987
Change-Id: Ieb8237de3fd31136ceac14bf4a8216a7ab3b7179
Signed-off-by: Kajur, Harish (vk250x) <vk250x@att.com>
34 files changed, 5911 insertions, 2365 deletions
diff --git a/aai-core/pom.xml.versionsBackup b/aai-core/pom.xml.versionsBackup new file mode 100644 index 00000000..55c5d712 --- /dev/null +++ b/aai-core/pom.xml.versionsBackup @@ -0,0 +1,1063 @@ +<?xml version="1.0"?> +<!-- + + ============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========================================================= + +--> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onap.aai.aai-common</groupId> + <artifactId>aai-common</artifactId> + <version>1.3.1-SNAPSHOT</version> + </parent> + <artifactId>aai-core</artifactId> + <name>aai-core</name> + <version>1.3.1-SNAPSHOT</version> + <packaging>jar</packaging> + <properties> + <sonar.language>java</sonar.language> + <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin> + <sonar.surefire.reportsPath>${project.build.directory}/surefire-reports</sonar.surefire.reportsPath> + <sonar.jacoco.reportPath>${project.build.directory}/coverage-reports/jacoco.exec</sonar.jacoco.reportPath> + <sonar.jacoco.reportMissing.force.zero>false</sonar.jacoco.reportMissing.force.zero> + <sonar.projectVersion>${project.version}</sonar.projectVersion> + <httpclient.version>4.5.5</httpclient.version> + <jackson.version>2.2.3</jackson.version> + <eelf.core.version>1.0.0</eelf.core.version> + <logback.version>1.2.3</logback.version> + <freemarker.version>2.3.21</freemarker.version> + <activemq.version>5.15.6</activemq.version> + <jacoco.line.coverage.limit>0.50</jacoco.line.coverage.limit> + <gremlin.version>3.2.2</gremlin.version> + <groovy.version>2.4.15</groovy.version> + <jetty.version>9.4.11.v20180605</jetty.version> + + <!-- Start of Default ONAP Schema Properties --> + <aai.wiki.link>https://wiki.onap.org/</aai.wiki.link> + <gendoc.version>v14</gendoc.version> + <aai.release>onap</aai.release> + <schema.uri.base.path>/aai</schema.uri.base.path> + <schema.configuration.location>N/A</schema.configuration.location> + <schema.nodes.location>aai-schema/src/main/resources/${aai.release}/oxm</schema.nodes.location> + <schema.edges.location>aai-schema/src/main/resources/${aai.release}/dbedgerules</schema.edges.location> + <schema.version.depth.start>v9</schema.version.depth.start> + <schema.version.related.link.start>v10</schema.version.related.link.start> + <schema.version.app.root.start>v11</schema.version.app.root.start> + <schema.version.namespace.change.start>v12</schema.version.namespace.change.start> + <schema.version.edge.label.start>v12</schema.version.edge.label.start> + <schema.version.api.default>v14</schema.version.api.default> + <schema.version.list>v8,v9,v10,v11,v12,v13,v14</schema.version.list> + <!-- End of Default ONAP Schema Properties --> + </properties> + <profiles> + <!-- Start of ONAP profile --> + <profile> + <id>onap</id> + <properties> + <aai.release>onap</aai.release> + <schema.configuration.location>N/A</schema.configuration.location> + <schema.nodes.location>aai-schema/src/main/resources/${aai.release}/oxm</schema.nodes.location> + <schema.edges.location>aai-schema/src/main/resources/${aai.release}/dbedgerules</schema.edges.location> + <schema.version.depth.start>v9</schema.version.depth.start> + <schema.version.related.link.start>v10</schema.version.related.link.start> + <schema.version.app.root.start>v11</schema.version.app.root.start> + <schema.version.edge.label.start>v12</schema.version.edge.label.start> + <schema.version.namespace.change.start>v12</schema.version.namespace.change.start> + <schema.version.api.default>v14</schema.version.api.default> + <schema.version.list>v8,v9,v10,v11,v12,v13,v14</schema.version.list> + <gendoc.version>v14</gendoc.version> + </properties> + </profile> + <!-- End of ONAP profile --> + <profile> + <id>generateXsd</id> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.1.1</version> + <executions> + <execution> + <phase>process-classes</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>org.onap.aai.util.GenerateXsd</mainClass> + <systemProperties> + <systemProperty> + <key>gen_version</key> + <value>${gendoc.version}</value> + </systemProperty> + <systemProperty> + <key>gen_type</key> + <value>XSD</value> + </systemProperty> + <systemProperty> + <key>yamlresponses_url</key> + <value></value> + </systemProperty> + <systemProperty> + <key>yamlresponses_label</key> + <value></value> + </systemProperty> + <systemProperty> + <key>schema.configuration.location</key> + <value>${schema.configuration.location}</value> + </systemProperty> + <systemProperty> + <key>schema.nodes.location</key> + <value>${schema.nodes.location}</value> + </systemProperty> + <systemProperty> + <key>schema.edges.location</key> + <value>${schema.edges.location}</value> + </systemProperty> + <systemProperty> + <key>schema.version.list</key> + <value>${schema.version.list}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.related.link.start</key> + <value>${schema.version.related.link.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.app.root.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.edge.label.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.namespace.change.start</key> + <value>${schema.version.namespace.change.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.api.default</key> + <value>${schema.version.api.default}</value> + </systemProperty> + <systemProperty> + <key>schema.uri.base.path</key> + <value>${schema.uri.base.path}</value> + </systemProperty> + <systemProperty> + <key>aai.release</key> + <value>${aai.release}</value> + </systemProperty> + </systemProperties> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>generateYaml</id> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.1.1</version> + <executions> + <execution> + <phase>process-classes</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>org.onap.aai.util.GenerateXsd</mainClass> + <systemProperties> + <systemProperty> + <key>gen_version</key> + <value>${gendoc.version}</value> + </systemProperty> + <systemProperty> + <key>gen_type</key> + <value>YAML</value> + </systemProperty> + <systemProperty> + <key>yamlresponses_url</key> + <value>https://wiki.web.att.com/pages/viewpage.action?pageId=607391054 + </value> + </systemProperty> + <systemProperty> + <key>yamlresponses_label</key> + <value>Response codes found in [response codes]</value> + </systemProperty> + <systemProperty> + <key>schema.configuration.location</key> + <value>${schema.configuration.location}</value> + </systemProperty> + <systemProperty> + <key>schema.nodes.location</key> + <value>${schema.nodes.location}</value> + </systemProperty> + <systemProperty> + <key>schema.edges.location</key> + <value>${schema.edges.location}</value> + </systemProperty> + <systemProperty> + <key>schema.version.list</key> + <value>${schema.version.list}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.related.link.start</key> + <value>${schema.version.related.link.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.app.root.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.edge.label.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.namespace.change.start</key> + <value>${schema.version.namespace.change.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.api.default</key> + <value>${schema.version.api.default}</value> + </systemProperty> + <systemProperty> + <key>schema.uri.base.path</key> + <value>${schema.uri.base.path}</value> + </systemProperty> + <systemProperty> + <key>aai.release</key> + <value>${aai.release}</value> + </systemProperty> + </systemProperties> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>generateHtml</id> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.1.1</version> + <executions> + <execution> + <phase>process-classes</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>org.onap.aai.util.swagger.GenerateSwagger</mainClass> + <systemProperties> + <property> + <key>aai.generate.version</key> + <value>${gendoc.version}</value> + </property> + <property> + <key>aai.wiki.link</key> + <value>${aai.wiki.link}</value> + </property> + <systemProperty> + <key>schema.configuration.location</key> + <value>${schema.configuration.location}</value> + </systemProperty> + <systemProperty> + <key>schema.nodes.location</key> + <value>${schema.nodes.location}</value> + </systemProperty> + <systemProperty> + <key>schema.edges.location</key> + <value>${schema.edges.location}</value> + </systemProperty> + <systemProperty> + <key>schema.version.list</key> + <value>${schema.version.list}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.related.link.start</key> + <value>${schema.version.related.link.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.app.root.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.edge.label.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.namespace.change.start</key> + <value>${schema.version.namespace.change.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.api.default</key> + <value>${schema.version.api.default}</value> + </systemProperty> + <systemProperty> + <key>schema.uri.base.path</key> + <value>${schema.uri.base.path}</value> + </systemProperty> + <systemProperty> + <key>aai.release</key> + <value>${aai.release}</value> + </systemProperty> + </systemProperties> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>autoGenerate</id> + <activation> + <property> + <name>aai.generate.schema</name> + <value>!false</value> + </property> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.1.1</version> + <executions> + <execution> + <id>autoGenerateXsd</id> + <phase>process-classes</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>org.onap.aai.util.GenerateXsd</mainClass> + <systemProperties> + <systemProperty> + <key>gen_version</key> + <value>ALL</value> + </systemProperty> + <systemProperty> + <key>gen_type</key> + <value>XSD</value> + </systemProperty> + <systemProperty> + <key>yamlresponses_url</key> + <value></value> + </systemProperty> + <systemProperty> + <key>yamlresponses_label</key> + <value></value> + </systemProperty> + <systemProperty> + <key>schema.configuration.location</key> + <value>${schema.configuration.location}</value> + </systemProperty> + <systemProperty> + <key>schema.nodes.location</key> + <value>${schema.nodes.location}</value> + </systemProperty> + <systemProperty> + <key>schema.edges.location</key> + <value>${schema.edges.location}</value> + </systemProperty> + <systemProperty> + <key>schema.version.list</key> + <value>${schema.version.list}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.related.link.start</key> + <value>${schema.version.related.link.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.app.root.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.edge.label.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.namespace.change.start</key> + <value>${schema.version.namespace.change.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.api.default</key> + <value>${schema.version.api.default}</value> + </systemProperty> + <systemProperty> + <key>schema.uri.base.path</key> + <value>${schema.uri.base.path}</value> + </systemProperty> + <systemProperty> + <key>aai.release</key> + <value>${aai.release}</value> + </systemProperty> + </systemProperties> + </configuration> + </execution> + <execution> + <id>autoGenerateYaml</id> + <phase>process-classes</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>org.onap.aai.util.GenerateXsd</mainClass> + <systemProperties> + <systemProperty> + <key>gen_version</key> + <value>ALL</value> + </systemProperty> + <systemProperty> + <key>gen_type</key> + <value>YAML</value> + </systemProperty> + <systemProperty> + <key>yamlresponses_url</key> + <value>https://wiki.web.att.com/pages/viewpage.action?pageId=607391054 + </value> + </systemProperty> + <systemProperty> + <key>yamlresponses_label</key> + <value>Response codes found in [response codes]</value> + </systemProperty> + <systemProperty> + <key>schema.configuration.location</key> + <value>${schema.configuration.location}</value> + </systemProperty> + <systemProperty> + <key>schema.nodes.location</key> + <value>${schema.nodes.location}</value> + </systemProperty> + <systemProperty> + <key>schema.edges.location</key> + <value>${schema.edges.location}</value> + </systemProperty> + <systemProperty> + <key>schema.version.list</key> + <value>${schema.version.list}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.related.link.start</key> + <value>${schema.version.related.link.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.app.root.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.edge.label.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.namespace.change.start</key> + <value>${schema.version.namespace.change.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.api.default</key> + <value>${schema.version.api.default}</value> + </systemProperty> + <systemProperty> + <key>aai.release</key> + <value>${aai.release}</value> + </systemProperty> + </systemProperties> + </configuration> + </execution> + <execution> + <id>autoGenerateHtml</id> + <phase>process-classes</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>org.onap.aai.util.AutoGenerateHtml</mainClass> + <systemProperties> + <property> + <key>aai.generate.version</key> + <value>${gendoc.version}</value> + </property> + <property> + <key>aai.wiki.link</key> + <value>${aai.wiki.link}</value> + </property> + <systemProperty> + <key>schema.configuration.location</key> + <value>${schema.configuration.location}</value> + </systemProperty> + <systemProperty> + <key>schema.nodes.location</key> + <value>${schema.nodes.location}</value> + </systemProperty> + <systemProperty> + <key>schema.edges.location</key> + <value>${schema.edges.location}</value> + </systemProperty> + <systemProperty> + <key>schema.version.list</key> + <value>${schema.version.list}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.depth.start</key> + <value>${schema.version.depth.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.related.link.start</key> + <value>${schema.version.related.link.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.app.root.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.edge.label.start</key> + <value>${schema.version.app.root.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.namespace.change.start</key> + <value>${schema.version.namespace.change.start}</value> + </systemProperty> + <systemProperty> + <key>schema.version.api.default</key> + <value>${schema.version.api.default}</value> + </systemProperty> + <systemProperty> + <key>aai.release</key> + <value>${aai.release}</value> + </systemProperty> + </systemProperties> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.12.4</version> + <configuration> + <argLine>-noverify ${argLine}</argLine> + <systemPropertyVariables> + <AJSC_HOME>.</AJSC_HOME> + <BUNDLECONFIG_DIR>bundleconfig-local</BUNDLECONFIG_DIR> + </systemPropertyVariables> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>sonar-maven-plugin</artifactId> + <version>3.2</version> + </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>0.7.7.201606060606</version> + <configuration> + <dumpOnExit>true</dumpOnExit> + </configuration> + <executions> + <execution> + <id>jacoco-initialize-unit-tests</id> + <goals> + <goal>prepare-agent</goal> + </goals> + <configuration> + <destFile>${project.build.directory}/coverage-reports/jacoco.exec</destFile> + <!-- <append>true</append> --> + </configuration> + </execution> + <execution> + <id>post-unit-test</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + <configuration> + <!-- Sets the path to the file which contains the execution data. --> + <dataFile>${project.build.directory}/coverage-reports/jacoco.exec</dataFile> + <!-- Sets the output directory for the code coverage report. --> + <outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory> + </configuration> + </execution> + <execution> + <id>default-check</id> + <goals> + <goal>check</goal> + </goals> + <configuration> + <dataFile>${project.build.directory}/coverage-reports/jacoco.exec</dataFile> + <rules> + <rule implementation="org.jacoco.maven.RuleConfiguration"> + <element>BUNDLE</element> + <limits> + <limit implementation="org.jacoco.report.check.Limit"> + <counter>LINE</counter> + <value>COVEREDRATIO</value> + <minimum>${jacoco.line.coverage.limit}</minimum> + </limit> + </limits> + </rule> + </rules> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <!-- explicitly define maven-deploy-plugin after other to force exec order --> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-deploy-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>nexus-staging-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-site-plugin</artifactId> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.onap.aai.aai-common</groupId> + <artifactId>aai-schema-ingest</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onap.aai.aai-common</groupId> + <artifactId>aai-annotations</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.6</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-text</artifactId> + <version>1.1</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>com.att.eelf</groupId> + <artifactId>eelf-core</artifactId> + <version>${eelf.core.version}</version> + </dependency> + <dependency> + <groupId>org.codehaus.jackson</groupId> + <artifactId>jackson-core-asl</artifactId> + <version>1.9.13</version> + </dependency> + <dependency> + <groupId>org.codehaus.jackson</groupId> + <artifactId>jackson-mapper-asl</artifactId> + <version>1.9.13</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-junit</artifactId> + <version>2.0.0.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-core</artifactId> + <version>1.3</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.10.19</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>1.6.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <version>1.6.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>19.0</version> + </dependency> + <dependency> + <groupId>org.janusgraph</groupId> + <artifactId>janusgraph-core</artifactId> + <version>0.2.0</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + <exclusion> + <groupId>dom4j</groupId> + <artifactId>dom4j</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.jaxrs</groupId> + <artifactId>jackson-jaxrs-json-provider</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>com.googlecode.json-simple</groupId> + <artifactId>json-simple</artifactId> + <version>1.1.1</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-web</artifactId> + <version>4.3.18.RELEASE</version> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + <version>4.3.18.RELEASE</version> + </dependency> + <dependency> + <groupId>javax.xml.bind</groupId> + <artifactId>jaxb-api</artifactId> + <version>2.2.11</version> + </dependency> + <dependency> + <groupId>org.eclipse.persistence</groupId> + <artifactId>eclipselink</artifactId> + <version>2.6.2</version> + </dependency> + <dependency> + <groupId>com.google.code.gson</groupId> + <artifactId>gson</artifactId> + <version>2.7</version> + </dependency> + <dependency> + <groupId>com.jayway.jsonpath</groupId> + <artifactId>json-path</artifactId> + <version>2.2.0</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-util</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.module</groupId> + <artifactId>jackson-module-jaxb-annotations</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-core</artifactId> + <version>1.18</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-client</artifactId> + <version>1.18</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-json</artifactId> + <version>1.18</version> + </dependency> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + <version>2.0.1</version> + </dependency> + <dependency> + <groupId>org.apache.tinkerpop</groupId> + <artifactId>gremlin-core</artifactId> + <version>${gremlin.version}</version> + </dependency> + <dependency> + <groupId>org.apache.tinkerpop</groupId> + <artifactId>tinkergraph-gremlin</artifactId> + <version>${gremlin.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.7.5</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.dataformat</groupId> + <artifactId>jackson-dataformat-yaml</artifactId> + <version>${jackson.version}</version> + </dependency> + <dependency> + <groupId>xml-apis</groupId> + <artifactId>xml-apis</artifactId> + <version>1.0.b2</version> + </dependency> + <dependency> + <groupId>commons-cli</groupId> + <artifactId>commons-cli</artifactId> + <version>1.3</version> + </dependency> + <dependency> + <groupId>com.beust</groupId> + <artifactId>jcommander</artifactId> + <version>1.48</version> + </dependency> + <dependency> + <groupId>org.json</groupId> + <artifactId>json</artifactId> + <version>20160810</version> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <version>3.1.0</version> + </dependency> + <dependency> + <groupId>com.bazaarvoice.jolt</groupId> + <artifactId>jolt-complete</artifactId> + <version>0.0.24</version> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-core</artifactId> + <version>${logback.version}</version> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>${logback.version}</version> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-access</artifactId> + <version>${logback.version}</version> + </dependency> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>activemq-broker</artifactId> + <version>${activemq.version}</version> + </dependency> + <dependency> + <groupId>com.opencsv</groupId> + <artifactId>opencsv</artifactId> + <version>3.1</version> + </dependency> + <dependency> + <groupId>org.freemarker</groupId> + <artifactId>freemarker</artifactId> + <version>${freemarker.version}</version> + </dependency> + <dependency> + <groupId>com.github.fge</groupId> + <artifactId>json-patch</artifactId> + <version>1.9</version> + </dependency> + <dependency> + <groupId>org.codehaus.groovy</groupId> + <artifactId>groovy</artifactId> + <version>${groovy.version}</version> + <classifier>indy</classifier> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <version>4.3.18.RELEASE</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-test</artifactId> + <version>1.5.1.RELEASE</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-javaagent</artifactId> + <version>1.6.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.skyscreamer</groupId> + <artifactId>jsonassert</artifactId> + <version>1.4.0</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <version>${httpclient.version}</version> + </dependency> + <dependency> + <groupId>org.apache.tinkerpop</groupId> + <artifactId>gremlin-groovy</artifactId> + <version>${gremlin.version}</version> + <exclusions> + <exclusion> + <groupId>org.codehaus.groovy</groupId> + <artifactId>groovy</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-jms</artifactId> + <version>4.3.18.RELEASE</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + <version>2.8.11</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <version>2.8.11</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + <version>2.8.11</version> + </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <version>1.2.17</version> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot</artifactId> + <version>1.5.15.RELEASE</version> + <scope>compile</scope> + </dependency> + </dependencies> + + <!-- Plugins and repositories --> + <pluginRepositories> + <pluginRepository> + <id>central</id> + <url>http://repo1.maven.org/maven2</url> + </pluginRepository> + <pluginRepository> + <id>EvoSuite</id> + <name>EvoSuite Repository</name> + <url>http://www.evosuite.org/m2</url> + </pluginRepository> + </pluginRepositories> + + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>2.10.4</version> + <configuration> + <failOnError>false</failOnError> + <doclet>org.umlgraph.doclet.UmlGraphDoc</doclet> + <docletArtifact> + <groupId>org.umlgraph</groupId> + <artifactId>umlgraph</artifactId> + <version>5.6</version> + </docletArtifact> + <additionalparam>-views</additionalparam> + <useStandardDocletOptions>true</useStandardDocletOptions> + </configuration> + </plugin> + </plugins> + </reporting> +</project> diff --git a/aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java b/aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java index aa4ec1a1..e737f08d 100644 --- a/aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java +++ b/aai-core/src/main/java/org/onap/aai/config/IntrospectionConfig.java @@ -21,14 +21,16 @@ */ package org.onap.aai.config; +import org.onap.aai.schema.enums.ObjectMetadata; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.setup.SchemaVersions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import org.onap.aai.introspection.LoaderFactory; import org.onap.aai.introspection.MoxyLoader; diff --git a/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java b/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java index c7520dd4..269d6330 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/Introspector.java @@ -36,6 +36,7 @@ import org.onap.aai.workarounds.NamingExceptions; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.util.*; +import java.util.stream.Collectors; public abstract class Introspector implements Cloneable { @@ -57,25 +58,25 @@ public abstract class Introspector implements Cloneable { protected String convertPropertyName (String name) { return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name); } - + protected abstract Object get(String name); protected abstract void set(String name, Object value); /** - * + * * @param name the property name you'd like to retrieve the value for * @return the value of the property */ public <T> T getValue(String name) { String convertedName = convertPropertyName(name); Object result = null; - + if (this.hasProperty(name)) { result = this.get(convertedName); } else { /* property not found - slightly ambiguous */ return null; } - + Class<?> clazz = this.getClass(name); if (this.isListType(name) && result == null) { try { @@ -88,18 +89,18 @@ public abstract class Introspector implements Cloneable { return (T)result; } - + public Introspector getWrappedValue(String name) { String convertedName = convertPropertyName(name); Object value = null; - + if (this.hasProperty(name)) { value = this.get(convertedName); } else { /* property not found - slightly ambiguous */ return null; } - + Class<?> clazz = this.getClass(name); if (this.isListType(name) && value == null) { try { @@ -115,9 +116,9 @@ public abstract class Introspector implements Cloneable { //no value return null; } - + } - + public List<Introspector> getWrappedListValue(String name) { String convertedName = convertPropertyName(name); Object value = null; @@ -141,17 +142,17 @@ public abstract class Introspector implements Cloneable { LOGGER.warn(e.getMessage(),e); } } - + List<Object> valueList = (List<Object>)value; - + for (Object item : valueList) { resultList.add(IntrospectorFactory.newInstance(this.getModelType(), item)); } - + return resultList; - + } - + public Object castValueAccordingToSchema(String name, Object obj) { Object result = obj; Class<?> nameClass = this.getClass(name); @@ -159,11 +160,11 @@ public abstract class Introspector implements Cloneable { throw new IllegalArgumentException("property: " + name + " does not exist on " + this.getDbName()); } if (obj != null) { - + try { if (!obj.getClass().getName().equals(nameClass.getName())) { if (nameClass.isPrimitive()) { - nameClass = ClassUtils.primitiveToWrapper(nameClass); + nameClass = ClassUtils.primitiveToWrapper(nameClass); result = nameClass.getConstructor(String.class).newInstance(obj.toString()); } if (obj instanceof String) { @@ -180,35 +181,35 @@ public abstract class Introspector implements Cloneable { } return result; } - + public List<Object> castValueAccordingToSchema(String name, List<?> objs) { List<Object> result = new ArrayList<>(); - + for (Object item : objs) { result.add(this.castValueAccordingToSchema(name, item)); } - + return result; - + } /** - * + * * @param name the property name you'd like to set the value of * @param obj the value to be set * @return */ public void setValue(String name, Object obj) throws IllegalArgumentException { Object box = this.castValueAccordingToSchema(name, obj); - + name = convertPropertyName(name); this.set(name, box); } /** - * + * * @return a list of all the properties available on the object */ public abstract Set<String> getProperties(); - + public Set<String> getProperties(PropertyPredicate<Introspector, String> p) { final Set<String> temp = new LinkedHashSet<>(); this.getProperties().stream().filter(item -> { @@ -217,22 +218,30 @@ public abstract class Introspector implements Cloneable { temp.add(item); }); final Set<String> result = Collections.unmodifiableSet(temp); - + return result; - + } + + public Set<String> getSimpleProperties(PropertyPredicate<Introspector, String> p){ + return this.getProperties() + .stream() + .filter(item -> p.test(this, item)) + .filter(this::isSimpleType) + .collect(Collectors.toSet()); + } /** - * + * * @return a list of the required properties on the object */ public abstract Set<String> getRequiredProperties(); /** - * + * * @return a list of the properties that can be used to query the object in the db */ public abstract Set<String> getKeys(); /** - * + * * @return a list of the all key properties for this object */ public Set<String> getAllKeys() { @@ -274,7 +283,7 @@ public abstract class Introspector implements Cloneable { result = this.indexedProperties; return result; } - + public Set<String> getUniqueProperties() { Set<String> result = null; if (this.uniqueProperties == null) { @@ -292,7 +301,7 @@ public abstract class Introspector implements Cloneable { result = this.uniqueProperties; return result; } - + public Set<String> getDependentOn() { String dependentOn = this.getMetadata(ObjectMetadata.DEPENDENT_ON); if (dependentOn == null) { @@ -301,21 +310,21 @@ public abstract class Introspector implements Cloneable { return new LinkedHashSet<>(Arrays.asList(dependentOn.split(","))); } /** - * + * * @param name * @return the string name of the java class of the named property */ public String getType(String name) { Class<?> resultClass = this.getClass(name); String result = ""; - + if (resultClass != null) { result = resultClass.getName(); if (result.equals("java.util.ArrayList")) { result = "java.util.List"; } } - + return result; } /** @@ -327,33 +336,33 @@ public abstract class Introspector implements Cloneable { public String getGenericType(String name) { Class<?> resultClass = this.getGenericTypeClass(name); String result = ""; - + if (resultClass != null) { result = resultClass.getName(); } - + return result; } /** - * + * * @return the string name of the java class of the underlying object */ public abstract String getJavaClassName(); - + /** - * + * * @param name the property name * @return the Class object */ public abstract Class<?> getClass(String name); - + public abstract Class<?> getGenericTypeClass(String name); /** - * + * * @param name the property name * @return a new instance of the underlying type of this property - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ public Object newInstanceOfProperty(String name) throws AAIUnknownObjectException { String type = this.getType(name); @@ -364,22 +373,22 @@ public abstract class Introspector implements Cloneable { String type = this.getGenericType(name); return loader.objectFromName(type); } - - + + public Introspector newIntrospectorInstanceOfProperty(String name) throws AAIUnknownObjectException { - + Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfProperty(name)); - + return result; - + } - + public Introspector newIntrospectorInstanceOfNestedProperty(String name) throws AAIUnknownObjectException { - + Introspector result = IntrospectorFactory.newInstance(this.getModelType(), this.newInstanceOfNestedProperty(name)); - + return result; - + } /** * Is this type not a Java String or primitive @@ -388,56 +397,56 @@ public abstract class Introspector implements Cloneable { */ public boolean isComplexType(String name) { String result = this.getType(name); - + if (result.contains("aai") || result.equals("java.lang.Object")) { return true; } else { return false; } } - + public boolean isComplexGenericType(String name) { String result = this.getGenericType(name); - + if (result.contains("aai")) { return true; } else { return false; } } - + public boolean isSimpleType(String name) { return !(this.isComplexType(name) || this.isListType(name)); } - + public boolean isSimpleGenericType(String name) { return !this.isComplexGenericType(name); } public boolean isListType(String name) { String result = this.getType(name); - + if (result.contains("java.util.List")) { return true; } else { return false; } } - + public boolean isContainer() { Set<String> props = this.getProperties(); boolean result = false; if (props.size() == 1 && this.isListType(props.iterator().next())) { result = true; } - + return result; } - + public abstract String getChildName(); public String getChildDBName() { String result = this.getChildName(); - + result = namingException.getDBName(result); return result; } @@ -447,10 +456,10 @@ public abstract class Introspector implements Cloneable { String lowerHyphen = this.getName(); lowerHyphen = namingException.getDBName(lowerHyphen); - + return lowerHyphen; } - + public abstract ModelType getModelType(); public boolean hasChild(Introspector child) { @@ -474,7 +483,7 @@ public abstract class Introspector implements Cloneable { } return result; } - + public void setURIChain(String uri) { this.uriChain = uri; } @@ -488,21 +497,21 @@ public abstract class Introspector implements Cloneable { if (this.isContainer()) { result += "/" + this.getName(); } else { - + if (container != null) { result += "/" + container; } result += "/" + this.getDbName() + "/" + this.findKey(); - + if (namespace != null && !namespace.equals("")) { result = "/" + namespace + result; } } - + return result; } - + public String getGenericURI() { String result = ""; if (this.isContainer()) { @@ -513,10 +522,10 @@ public abstract class Introspector implements Cloneable { result += "/{" + this.getDbName() + "-" + key + "}"; } } - + return result; } - + public String getFullGenericURI() { String result = ""; String namespace = this.getMetadata(ObjectMetadata.NAMESPACE); @@ -537,33 +546,33 @@ public abstract class Introspector implements Cloneable { if (namespace != null && !namespace.equals("")) { result = "/" + namespace + result; } - + } - + return result; } public abstract String preProcessKey(String key); - + protected abstract String findKey() throws UnsupportedEncodingException; - + public abstract String marshal(MarshallerProperties properties); - + public abstract Object clone(); public abstract Object getUnderlyingObject(); - + public String marshal(boolean formatted) { MarshallerProperties properties = new MarshallerProperties.Builder(MediaType.APPLICATION_JSON_TYPE).formatted(formatted).build(); - + return marshal(properties); } public String makeSingular(String word) { - + String result = word; result = result.replaceAll("(?:([ho])es|s)$", ""); - + if (result.equals("ClassesOfService")) { result = "ClassOfService"; } else if (result.equals("CvlanTag")) { @@ -573,10 +582,10 @@ public abstract class Introspector implements Cloneable { } return result; } - + protected String makePlural(String word) { String result = word; - + if (result.equals("cvlan-tag-entry")) { return "cvlan-tags"; } else if (result.equals("class-of-service")) { @@ -590,7 +599,7 @@ public abstract class Introspector implements Cloneable { if (result.equals("classes-of-services")) { result = "classes-of-service"; }*/ - + return result; } @@ -599,21 +608,22 @@ public abstract class Introspector implements Cloneable { public Optional<String> getPropertyMetadata(String propName, PropertyMetadata metadataName) { final String resultValue = this.getPropertyMetadata(propName).getOrDefault(metadataName, ""); Optional<String> result = Optional.empty(); - + if (!resultValue.isEmpty()) { result = Optional.of(resultValue); } return result; - + } public abstract SchemaVersion getVersion(); public Loader getLoader() { return this.loader; } - + public boolean isTopLevel() { - + return this.getMetadata(ObjectMetadata.NAMESPACE) != null; } + } diff --git a/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java b/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java index b1447d8f..d54a9833 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/JSONStrategy.java @@ -46,47 +46,47 @@ public class JSONStrategy extends Introspector { throw new IllegalArgumentException("This object has no named type."); } } - + protected JSONStrategy(Object o, String namedType) { super(o); json = (JSONObject)o; this.namedType = namedType; - + } - + @Override public boolean hasProperty(String name) { - //TODO + //TODO return true; } @Override public Object getValue(String name) { Object result = ""; result = json.get(name); - + return result; } @Override public void setValue(String name, Object obj) { json.put(name, obj); - + } @Override public Object getUnderlyingObject() { return this.json; } - + @Override public Set<String> getProperties() { Set<String> result = json.keySet(); return result; } - + @Override public Set<String> getRequiredProperties() { //unknowable - + return this.getProperties(); } @@ -109,11 +109,11 @@ public class JSONStrategy extends Introspector { if (resultClass != null) { result = resultClass.getName(); } - + if (result.equals("org.json.simple.JSONArray")) { result = "java.util.List"; } - + return result; } @@ -136,7 +136,7 @@ public class JSONStrategy extends Introspector { public Class<?> getClass(String name) { Class<?> result = null; result = json.get(name).getClass(); - + return result; } @@ -148,7 +148,7 @@ public class JSONStrategy extends Introspector { if (resultObject.getClass().getName().equals("org.json.simple.JSONArray")) { resultClass = ((List)resultObject).get(0).getClass(); } - + return resultClass; } @@ -169,43 +169,43 @@ public class JSONStrategy extends Introspector { return null; } } - + @Override public boolean isComplexType(String name) { String result = this.getType(name); - + if (result.contains("JSONObject")) { return true; } else { return false; } - + } - + @Override public boolean isComplexGenericType(String name) { String result = this.getGenericType(name); - + if (result.contains("JSONObject")) { return true; } else { return false; } - + } - + @Override public boolean isListType(String name) { String result = this.getType(name); - + if (result.contains("java.util.List")) { return true; } else { return false; } - + } - + @Override public boolean isContainer() { Set<String> props = this.getProperties(); @@ -213,66 +213,66 @@ public class JSONStrategy extends Introspector { if (props.size() == 1 && this.isListType(props.iterator().next())) { result = true; } - + return result; } @Override protected String findKey() { return ""; } - + @Override public String getName() { return this.namedType; } - + @Override public String getDbName() { return this.getName(); } - + @Override public String getURI() { - - // use a UUID for now + + // use a UUID for now return UUID.randomUUID().toString(); } - + @Override public String getGenericURI() { - + //there is none defined for this return ""; } - + @Override public String preProcessKey (String key) { - + // don't do anything with it return key; - + } - + @Override public String marshal(MarshallerProperties properties) { //TODO return null; } - + @Override public Object clone() { //TODO return null; } - + /*@Override public String findEdgeName(String parent, String child) { - + // Always has for now return "has"; - + }*/ - + @Override public ModelType getModelType() { return ModelType.JSON; @@ -341,7 +341,7 @@ public class JSONStrategy extends Introspector { @Override protected void set(String name, Object value) { // TODO Auto-generated method stub - + } @Override diff --git a/aai-core/src/main/java/org/onap/aai/introspection/Loader.java b/aai-core/src/main/java/org/onap/aai/introspection/Loader.java index 3a69e56e..cae4cbb7 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/Loader.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/Loader.java @@ -25,12 +25,13 @@ import org.onap.aai.restcore.MediaType; import org.onap.aai.setup.SchemaVersion; import java.util.Map; +import java.util.Set; public abstract class Loader { private final SchemaVersion version; private final ModelType modelType; - + /** * Instantiates a new loader. * @@ -41,32 +42,32 @@ public abstract class Loader { this.version = version; this.modelType = modelType; } - + /** * Process. * * @param version the version */ protected abstract void process(SchemaVersion version); - + /** * Object from name. * * @param name the name * @return the object - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ public abstract Object objectFromName(String name) throws AAIUnknownObjectException; - + /** * Introspector from name. * * @param name the name * @return the introspector - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ public abstract Introspector introspectorFromName(String name) throws AAIUnknownObjectException; - + /** * Unmarshal. * @@ -76,7 +77,7 @@ public abstract class Loader { * @return the introspector */ public abstract Introspector unmarshal(String type, String json, MediaType mediaType) throws AAIUnmarshallingException; - + /** * Unmarshal. * @@ -88,7 +89,7 @@ public abstract class Loader { return unmarshal(type, json, MediaType.APPLICATION_JSON_TYPE); } - + /** * Gets the model type. * @@ -97,7 +98,7 @@ public abstract class Loader { public ModelType getModelType() { return this.modelType; } - + /** * Gets the version. * @@ -106,6 +107,8 @@ public abstract class Loader { public SchemaVersion getVersion() { return this.version; } - + public abstract Map<String, Introspector> getAllObjects(); + + public abstract Set<String> getNamedPropNodes(); } diff --git a/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java b/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java index 82504550..fa52d62f 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/MoxyLoader.java @@ -34,6 +34,7 @@ import org.onap.aai.logging.ErrorLogHelper; import org.onap.aai.logging.LogFormatTools; import org.onap.aai.nodes.NodeIngestor; import org.onap.aai.restcore.MediaType; +import org.onap.aai.schema.enums.ObjectMetadata; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.workarounds.NamingExceptions; import org.springframework.stereotype.Component; @@ -44,6 +45,7 @@ import java.io.*; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; public class MoxyLoader extends Loader { @@ -52,9 +54,11 @@ public class MoxyLoader extends Loader { private Map<String, Introspector> allObjs = null; private Map<SchemaVersion, MoxyLoader> moxyLoaderFactory; - + private NodeIngestor nodeIngestor; + private Set<String> namedProps; + public MoxyLoader(SchemaVersion version, NodeIngestor nodeIngestor) { super(version, ModelType.MOXY); this.nodeIngestor = nodeIngestor; @@ -67,14 +71,14 @@ public class MoxyLoader extends Loader { } /** * {@inheritDoc} - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ @Override public Introspector introspectorFromName(String name) throws AAIUnknownObjectException { return IntrospectorFactory.newInstance(ModelType.MOXY, objectFromName(name)); } - + /** * {@inheritDoc} */ @@ -93,7 +97,7 @@ public class MoxyLoader extends Loader { } else { upperCamel = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, sanitizedName); } - + try { final DynamicEntity result = jaxbContext.newDynamicEntity(upperCamel); @@ -115,14 +119,14 @@ public class MoxyLoader extends Loader { * We need to have just same JaxbContext for each version */ jaxbContext = nodeIngestor.getContextForVersion(version); - + } /** * {@inheritDoc} */ @Override - public Introspector unmarshal(String type, String json, MediaType mediaType) throws AAIUnmarshallingException { + public Introspector unmarshal(String type, String json, MediaType mediaType) throws AAIUnmarshallingException { try { final Object clazz = objectFromName(type); final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); @@ -143,7 +147,7 @@ public class MoxyLoader extends Loader { throw new AAIUnmarshallingException("Could not unmarshall: " + e.getMessage(), e); } } - + @Override public Map<String, Introspector> getAllObjects() { if (this.allObjs != null) { @@ -163,7 +167,7 @@ public class MoxyLoader extends Loader { return allObjs; } } - + private Set<String> objectsInVersion() { Set<String> result = new HashSet<>(); @@ -177,7 +181,21 @@ public class MoxyLoader extends Loader { //result.remove("EdgePropNames"); return result; } - + + @Override + public Set<String> getNamedPropNodes(){ + + if(namedProps == null){ + namedProps = getAllObjects() + .entrySet() + .stream() + .filter( + (entry) -> entry.getValue().getMetadata(ObjectMetadata.NAME_PROPS) != null + ).map(entry -> entry.getKey()).collect(Collectors.toSet()); + } + + return namedProps; + } public DynamicJAXBContext getJAXBContext() { return this.jaxbContext; } diff --git a/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java b/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java index ecf31253..f0d487e8 100644 --- a/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java +++ b/aai-core/src/main/java/org/onap/aai/introspection/MoxyStrategy.java @@ -51,9 +51,10 @@ import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.util.*; import java.util.Map.Entry; +import java.util.stream.Collectors; public class MoxyStrategy extends Introspector { - + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(MoxyStrategy.class); private DynamicEntity internalObject = null; private DynamicType internalType = null; @@ -67,9 +68,9 @@ public class MoxyStrategy extends Introspector { private Set<String> requiredProperties = null; private boolean isInitialized = false; - + private NodeIngestor nodeIngestor; - + protected MoxyStrategy(Object obj) { super(obj); /* must look up the correct jaxbcontext for this object */ @@ -81,19 +82,19 @@ public class MoxyStrategy extends Introspector { jaxbContext = nodeIngestor.getContextForVersion(version); String simpleName = internalObject.getClass().getName(); internalType = jaxbContext.getDynamicType(simpleName); - + cd = internalType.getDescriptor(); try { marshaller = jaxbContext.createMarshaller(); - + unmarshaller = jaxbContext.createUnmarshaller(); - + } catch (JAXBException e) { } } - + private void init() { isInitialized = true; @@ -104,13 +105,13 @@ public class MoxyStrategy extends Introspector { } props = Collections.unmodifiableSet(props); this.properties = props; - + Set<String> requiredProps = new LinkedHashSet<>(); requiredProps = new LinkedHashSet<>(); for (DatabaseMapping dm : cd.getMappings()) { - if (dm.getField() instanceof XMLField) { + if (dm.getField() instanceof XMLField) { XMLField x = (XMLField)dm.getField(); - if (x != null) { + if (x != null) { if (x.isRequired()) { requiredProps.add(this.removeXPathDescriptor(x.getName())); } @@ -119,25 +120,25 @@ public class MoxyStrategy extends Introspector { } requiredProps = Collections.unmodifiableSet(requiredProps); this.requiredProperties = requiredProps; - + Set<String> keys = new LinkedHashSet<>(); - + for (String name : internalType.getDescriptor().getPrimaryKeyFieldNames()) { keys.add(this.removeXPathDescriptor(name)); } keys = Collections.unmodifiableSet(keys); this.keys = keys; - - + + } - + @Override public boolean hasProperty(String name) { String convertedName = convertPropertyName(name); - return internalType.containsProperty(convertedName); + return internalType.containsProperty(convertedName); } - + @Override public Object get(String name) { return internalObject.get(name); @@ -145,7 +146,7 @@ public class MoxyStrategy extends Introspector { @Override public void set(String name, Object obj) throws IllegalArgumentException { - + internalObject.set(name, obj); } @@ -157,7 +158,7 @@ public class MoxyStrategy extends Introspector { } return this.properties; - + } @Override @@ -179,7 +180,7 @@ public class MoxyStrategy extends Introspector { return this.keys; } - + @Override public Map<PropertyMetadata, String> getPropertyMetadata(String prop) { String propName = this.convertPropertyName(prop); @@ -192,7 +193,7 @@ public class MoxyStrategy extends Introspector { PropertyMetadata.valueOf(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, (String)entry.getKey())), (String)entry.getValue()); } } - + return result; } @@ -200,8 +201,8 @@ public class MoxyStrategy extends Introspector { public String getJavaClassName() { return internalObject.getClass().getName(); } - - + + @Override public Class<?> getClass(String name) { @@ -211,7 +212,7 @@ public class MoxyStrategy extends Introspector { if (internalType.getPropertyType(name) == null) { if (cd.getMappingForAttributeName(name) instanceof XMLCompositeDirectCollectionMapping) { resultClass = cd.getMappingForAttributeName(name).getContainerPolicy().getContainerClass(); - + } else if (cd.getMappingForAttributeName(name) instanceof XMLCompositeCollectionMapping) { resultClass = cd.getMappingForAttributeName(name).getContainerPolicy().getContainerClass(); } else { @@ -243,7 +244,7 @@ public class MoxyStrategy extends Introspector { resultClass = cd.getMappingForAttributeName(name).getReferenceDescriptor().getJavaClass(); } } - + return resultClass; } @@ -251,20 +252,20 @@ public class MoxyStrategy extends Introspector { public Object getUnderlyingObject() { return this.internalObject; } - + @Override public String getChildName() { - + String className = internalObject.getClass().getSimpleName(); String lowerHyphen = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, className); - + if (this.isContainer()) { lowerHyphen = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN,this.getGenericTypeClass(this.getProperties().iterator().next()).getSimpleName()); } - + return lowerHyphen; } - + @Override public String getName() { String className = internalObject.getClass().getSimpleName(); @@ -273,11 +274,11 @@ public class MoxyStrategy extends Introspector { if (this.isContainer()) { lowerHyphen = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN,this.getGenericTypeClass(this.getProperties().get(0)).getSimpleName()); }*/ - + return lowerHyphen; } - + @Override public String getObjectId() throws UnsupportedEncodingException { String result = ""; @@ -285,17 +286,17 @@ public class MoxyStrategy extends Introspector { if (this.isContainer()) { result += "/" + this.getName(); } else { - + if (container != null) { result += "/" + container; } result += "/" + this.getDbName() + "/" + this.findKey(); - + } - + return result; } - + @Override protected String findKey() throws UnsupportedEncodingException { Set<String> keys = null; @@ -305,10 +306,10 @@ public class MoxyStrategy extends Introspector { String value = UriUtils.encode(this.getValue(key).toString(), "UTF-8"); results.add(value); } - + return Joiner.on("/").join(results); } - + @Override public String preProcessKey (String key) { String result = ""; @@ -316,19 +317,19 @@ public class MoxyStrategy extends Introspector { String[] split = key.split("/"); int i = 0; for (i = split.length-1; i >= 0; i--) { - + if (jaxbContext.getDynamicType(split[i]) != null) { break; - + } - + } result = Joiner.on("/").join(Arrays.copyOfRange(split, 0, i)); - + return result; - + } - + @Override public String marshal(MarshallerProperties properties) { StringWriter result = new StringWriter(); @@ -339,7 +340,7 @@ public class MoxyStrategy extends Introspector { marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, properties.getWrapperAsArrayName()); marshaller.setProperty(org.eclipse.persistence.jaxb.MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, false); } - + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, properties.getFormatted()); marshaller.marshal(this.internalObject, result); } catch (JAXBException e) { @@ -348,7 +349,7 @@ public class MoxyStrategy extends Introspector { return result.toString(); } - + @Override public Object clone() { Object result = null; @@ -358,7 +359,7 @@ public class MoxyStrategy extends Introspector { unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json"); unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); unmarshaller.setProperty(UnmarshallerProperties.JSON_WRAPPER_AS_ARRAY_NAME, true); - + result = unmarshaller.unmarshal(new StreamSource(new StringReader(this.marshal(true))), this.internalObject.getClass()).getValue(); } catch (JAXBException e) { // TODO Auto-generated catch block @@ -371,21 +372,21 @@ public class MoxyStrategy extends Introspector { public ModelType getModelType() { return ModelType.MOXY; } - + private String removeXPathDescriptor(String name) { - + return name.replaceAll("/text\\(\\)", ""); } @Override public String getMetadata(ObjectMetadata name) { - + return (String)cd.getProperty(name.toString()); } @Override public SchemaVersion getVersion() { - + return this.version; } } diff --git a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java index dd5d42c6..bd7f1b91 100644 --- a/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java +++ b/aai-core/src/main/java/org/onap/aai/rest/db/HttpEntry.java @@ -8,7 +8,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -50,6 +50,7 @@ import com.github.fge.jsonpatch.mergepatch.JsonMergePatch; import org.apache.commons.lang.StringUtils; import org.apache.tinkerpop.gremlin.structure.Graph; import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.janusgraph.core.JanusGraphException; import org.javatuples.Pair; import org.onap.aai.db.props.AAIProperties; @@ -67,6 +68,7 @@ import org.onap.aai.nodes.NodeIngestor; import org.onap.aai.parsers.query.QueryParser; import org.onap.aai.parsers.uri.URIToExtensionInformation; +import org.onap.aai.parsers.uri.URIToObject; import org.onap.aai.rest.ueb.UEBNotification; import org.onap.aai.restcore.HttpMethod; import org.onap.aai.schema.enums.ObjectMetadata; @@ -93,15 +95,15 @@ public class HttpEntry { private static final String TARGET_ENTITY = "DB"; private ModelType introspectorFactoryType; - + private QueryStyle queryStyle; - + private SchemaVersion version; - + private Loader loader; - + private TransactionalGraphEngine dbEngine; - + private boolean processSingle = true; private int paginationBucket = -1; @@ -148,7 +150,7 @@ public class HttpEntry { } - public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType, UEBNotification notification){ + public HttpEntry setHttpEntryProperties(SchemaVersion version, DBConnectionType connectionType, UEBNotification notification){ this.version = version; this.loader = loaderFactory.createLoaderForVersion(introspectorFactoryType, version); this.dbEngine = new JanusGraphDBEngine( @@ -161,7 +163,7 @@ public class HttpEntry { getDbEngine().startTransaction(); return this; } - + /** * Gets the introspector factory type. @@ -316,7 +318,7 @@ public class HttpEntry { QueryEngine queryEngine = dbEngine.getQueryEngine(); int maxRetries = 10; int retry = 0; - + LoggingContext.save(); for (DBRequest request : requests) { response = null; @@ -358,7 +360,7 @@ public class HttpEntry { if (requestContextList != null) { requestContext = requestContextList.get(0); } - + if (cleanUp == null) { cleanUp = "false"; } @@ -426,7 +428,7 @@ public class HttpEntry { result = formatter.output(vertices.stream().map(vertex -> (Object) vertex).collect(Collectors.toList())).toString(); status = Status.OK; } - + break; case PUT: response = this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); @@ -441,9 +443,9 @@ public class HttpEntry { } obj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); } - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed "); LoggingContext.restoreIfPossible(); @@ -454,7 +456,7 @@ public class HttpEntry { serializer.touchStandardVertexProperties(v, false); this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); serializer.createEdge(obj, v); - + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs(),TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); @@ -466,7 +468,7 @@ public class HttpEntry { existingObj = this.getObjectFromDb(vertices, serializer, query, existingObj, request.getUri(), 0, false, cleanUp); String existingJson = existingObj.marshal(false); String newJson; - + if (request.getRawRequestContent().isPresent()) { newJson = request.getRawRequestContent().get(); } else { @@ -489,15 +491,15 @@ public class HttpEntry { status = Status.OK; patchedObj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, patchedObj, this.loader); } - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, patchedObj, relatedObjects, basePath); } catch (IOException | JsonPatchException e) { - + LOGGER.info ("Caught exception: " + e.getMessage()); LoggingContext.restoreIfPossible(); throw new AAIException("AAI_3000", "could not perform patch operation"); @@ -507,18 +509,18 @@ public class HttpEntry { String resourceVersion = params.getFirst("resource-version"); obj = serializer.getLatestVersionView(v); if (query.isDependent()) { - relatedObjects = this.getRelatedObjects(serializer, queryEngine, v); + relatedObjects = this.getRelatedObjects(serializer, queryEngine, v, obj, this.loader); } /* * Find all Delete-other-vertex vertices and create structure for notify - * findDeleatble also returns the startVertex v and we dont want to create + * findDeleatble also returns the startVertex v and we dont want to create * duplicate notification events for the same * So remove the startvertex first */ - + List<Vertex> deletableVertices = dbEngine.getQueryEngine().findDeletable(v); Long vId = (Long) v.id(); - + /* * I am assuming vertexId cant be null */ @@ -527,39 +529,39 @@ public class HttpEntry { Map<Vertex, Introspector> deleteObjects = new HashMap<>(); Map<String, URI> uriMap = new HashMap<>(); Map<String, HashMap<String, Introspector>> deleteRelatedObjects = new HashMap<>(); - + if(isDelVerticesPresent){ deleteObjects = this.buildIntrospectorObjects(serializer, deletableVertices); - + uriMap = this.buildURIMap(serializer, deleteObjects); deleteRelatedObjects = this.buildRelatedObjects(serializer, queryEngine, deleteObjects); } - + this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, true); serializer.delete(v, deletableVertices, resourceVersion, enableResourceVersion); this.invokeExtension(dbEngine, this.dbEngine.tx(), method, request, sourceOfTruth, version, loader, obj, uri, false); - - LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + + + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs() + (long)queryEngine.getDBTimeMsecs(), TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); status = Status.NO_CONTENT; notification.createNotificationEvent(transactionId, sourceOfTruth, status, uri, obj, relatedObjects, basePath); - + /* * Notify delete-other-v candidates */ - + if(isDelVerticesPresent){ this.buildNotificationEvent(sourceOfTruth, status, transactionId, notification, deleteObjects, uriMap, deleteRelatedObjects, basePath); } - + break; case DELETE_EDGE: serializer.touchStandardVertexProperties(v, false); serializer.deleteEdge(obj, v); - + LoggingContext.elapsedTime((long)serializer.getDBTimeMsecs(),TimeUnit.MILLISECONDS); LOGGER.info ("Completed"); LoggingContext.restoreIfPossible(); @@ -569,8 +571,8 @@ public class HttpEntry { default: break; } - - + + /* temporarily adding vertex id to the headers * to be able to use for testing the vertex id endpoint functionality * since we presently have no other way of generating those id urls @@ -606,7 +608,7 @@ public class HttpEntry { break; } catch (JanusGraphException e) { this.dbEngine.rollback(); - + LOGGER.info ("Caught exception: " + e.getMessage()); LoggingContext.restoreIfPossible(); AAIException ex = new AAIException("AAI_6142", e); @@ -655,7 +657,7 @@ public class HttpEntry { return Pair.with(success, responses); } - + /** * Gets the media type. * @@ -663,15 +665,15 @@ public class HttpEntry { * @return the media type */ private String getMediaType(List <MediaType> mediaTypeList) { - String mediaType = MediaType.APPLICATION_JSON; // json is the default + String mediaType = MediaType.APPLICATION_JSON; // json is the default for (MediaType mt : mediaTypeList) { if (MediaType.APPLICATION_XML_TYPE.isCompatible(mt)) { mediaType = MediaType.APPLICATION_XML; - } + } } return mediaType; } - + /** * Gets the object from db. * @@ -691,19 +693,19 @@ public class HttpEntry { * @throws NoSuchMethodException the no such method exception * @throws UnsupportedEncodingException the unsupported encoding exception * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException + * @throws AAIUnknownObjectException + * @throws URISyntaxException */ private Introspector getObjectFromDb(List<Vertex> results, DBSerializer serializer, QueryParser query, Introspector obj, URI uri, int depth, boolean nodeOnly, String cleanUp) throws AAIException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIUnknownObjectException, URISyntaxException { - - //nothing found - if (results.isEmpty()) { - String msg = createNotFoundMessage(query.getResultType(), uri); + + //nothing found + if (results.isEmpty()) { + String msg = createNotFoundMessage(query.getResultType(), uri); throw new AAIException("AAI_6114", msg); - } + } + + return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); - return serializer.dbToObject(results, obj, depth, nodeOnly, cleanUp); - } /** @@ -800,7 +802,7 @@ public class HttpEntry { return response; } - + /** * Creates the not found message. * @@ -809,12 +811,12 @@ public class HttpEntry { * @return the string */ private String createNotFoundMessage(String resultType, URI uri) { - - String msg = "No Node of type " + resultType + " found at: " + uri.getPath(); - return msg; + String msg = "No Node of type " + resultType + " found at: " + uri.getPath(); + + return msg; } - + /** * Sets the depth. * @@ -831,11 +833,11 @@ public class HttpEntry { return depth; } - if(depthParam == null){ + if(depthParam == null){ if(this.version.compareTo(schemaVersions.getDepthVersion()) >= 0){ depth = 0; } else { - depth = AAIProperties.MAXIMUM_DEPTH; + depth = AAIProperties.MAXIMUM_DEPTH; } } else { if (!depthParam.isEmpty() && !"all".equals(depthParam)){ @@ -847,16 +849,16 @@ public class HttpEntry { } } - String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH); - + String maxDepth = obj.getMetadata(ObjectMetadata.MAXIMUM_DEPTH); + int maximumDepth = AAIProperties.MAXIMUM_DEPTH; if(maxDepth != null){ - try { - maximumDepth = Integer.parseInt(maxDepth); - } catch(Exception ex){ - throw new AAIException("AAI_4018"); - } + try { + maximumDepth = Integer.parseInt(maxDepth); + } catch(Exception ex){ + throw new AAIException("AAI_4018"); + } } if(depth > maximumDepth){ @@ -865,7 +867,7 @@ public class HttpEntry { return depth; } - + /** * Checks if is modification method. * @@ -874,31 +876,155 @@ public class HttpEntry { */ private boolean isModificationMethod(HttpMethod method) { boolean result = false; - + if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PUT_EDGE) || method.equals(HttpMethod.DELETE_EDGE) || method.equals(HttpMethod.MERGE_PATCH)) { result = true; } - + return result; - + } - - private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIException, URISyntaxException { + + /** + * Given an uri, introspector object and loader object + * it will check if the obj is top level object if it is, + * it will return immediately returning the uri passed in + * If it isn't, it will go through, get the uriTemplate + * from the introspector object and get the count of "/"s + * and remove that part of the uri using substring + * and keep doing that until the current object is top level + * Also added the max depth just so worst case scenario + * Then keep adding aai-uri to the list include the aai-uri passed in + * Convert that list into an array and return it + * <p> + * + * Example: + * + * <blockquote> + * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 + * + * Given the uriTemplate vserver -> /vservers/vserver/{vserver-id} + * it converts to /vservers/vserver + * + * lastIndexOf /vservers/vserver in /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1/vservers/vserver/v1 + * ^ + * | + * | + * lastIndexOf + * Use substring to get the string from 0 to that lastIndexOf + * aai-uri -> /cloud-infrastructure/cloud-regions/cloud-region/cloud-owner/cloud-region-id/tenants/tenant/tenant1 + * + * From this new aai-uri, generate a introspector from the URITOObject class + * and keep doing this until you + * + * </blockquote> + * + * @param aaiUri - aai-uri of the vertex representating the unique id of a given vertex + * @param obj - introspector object of the given starting vertex + * @param loader - Type of loader which will always be MoxyLoader to support model driven + * @return an array of strings which can be used to get the vertexes of parent and grand parents from a given vertex + * @throws UnsupportedEncodingException + * @throws AAIException + */ + String[] convertIntrospectorToUriList(String aaiUri, Introspector obj, Loader loader) throws UnsupportedEncodingException, AAIException { + + List<String> uriList = new ArrayList<>(); + String template = StringUtils.EMPTY; + String truncatedUri = aaiUri; + int depth = AAIProperties.MAXIMUM_DEPTH; + uriList.add(truncatedUri); + + while (depth >= 0 && !obj.isTopLevel()) { + template = obj.getMetadata(ObjectMetadata.URI_TEMPLATE); + + if(template == null){ + LOGGER.warn("Unable to find the uriTemplate for the object {}", obj.getDbName()); + return null; + } + + int templateCount = StringUtils.countMatches(template, "/"); + int truncatedUriCount = StringUtils.countMatches(truncatedUri, "/"); + + if(templateCount > truncatedUriCount){ + LOGGER.warn("Template uri {} contains more slashes than truncatedUri {}", template, truncatedUri); + return null; + } + + int cutIndex = StringUtils.ordinalIndexOf(truncatedUri, "/", truncatedUriCount-templateCount+1); + truncatedUri = StringUtils.substring(truncatedUri, 0, cutIndex); + uriList.add(truncatedUri); + obj = new URIToObject(loader, UriBuilder.fromPath(truncatedUri).build()).getEntity(); + depth--; + } + + return uriList.toArray(new String[uriList.size()]); + } + + /** + * + * @param serializer + * @param queryEngine + * @param v + * @param obj + * @param loader + * @return + * @throws IllegalAccessException + * @throws IllegalArgumentException + * @throws InvocationTargetException + * @throws SecurityException + * @throws InstantiationException + * @throws NoSuchMethodException + * @throws UnsupportedEncodingException + * @throws AAIException + * @throws URISyntaxException + */ + private HashMap<String, Introspector> getRelatedObjects(DBSerializer serializer, QueryEngine queryEngine, Vertex v, Introspector obj, Loader loader) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, InstantiationException, NoSuchMethodException, UnsupportedEncodingException, AAIException, URISyntaxException { + HashMap<String, Introspector> relatedVertices = new HashMap<>(); - List<Vertex> vertexChain = queryEngine.findParents(v); - for (Vertex vertex : vertexChain) { + VertexProperty aaiUriProperty = v.property(AAIProperties.AAI_URI); + + if(!aaiUriProperty.isPresent()){ + if(LOGGER.isDebugEnabled()){ + LOGGER.debug("For the given vertex {}, it seems aai-uri is not present so not getting related objects", v.id().toString()); + } else { + LOGGER.info("It seems aai-uri is not present in vertex, so not getting related objects, for more info enable debug log"); + } + return relatedVertices; + } + + String aaiUri = aaiUriProperty.value().toString(); + + if(!obj.isTopLevel()){ + String[] uriList = convertIntrospectorToUriList(aaiUri, obj, loader); + List<Vertex> vertexChain = null; + // If the uriList is null then there is something wrong with converting the uri + // into a list of aai-uris so falling back to the old mechanism for finding parents + if(uriList == null){ + LOGGER.info("Falling back to the old mechanism due to unable to convert aai-uri to list of uris but this is not optimal"); + vertexChain = queryEngine.findParents(v); + } else { + vertexChain = queryEngine.findParents(uriList); + } + for(Vertex vertex : vertexChain){ + try { + final Introspector vertexObj = serializer.getVertexProperties(vertex); + relatedVertices.put(vertexObj.getObjectId(), vertexObj); + } catch (AAIUnknownObjectException e) { + LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); + } + } + } else { try { - final Introspector vertexObj = serializer.getVertexProperties(vertex); + final Introspector vertexObj = serializer.getVertexProperties(v); relatedVertices.put(vertexObj.getObjectId(), vertexObj); } catch (AAIUnknownObjectException e) { LOGGER.warn("Unable to get vertex properties, partial list of related vertices returned"); } - } - + return relatedVertices; } - + private Map<Vertex, Introspector> buildIntrospectorObjects(DBSerializer serializer, Iterable<Vertex> vertices) { Map<Vertex, Introspector> deleteObjectMap = new HashMap<>(); for (Vertex vertex : vertices) { @@ -944,7 +1070,7 @@ public class HttpEntry { for (Map.Entry<Vertex, Introspector> entry : introSpector.entrySet()) { try { HashMap<String, Introspector> relatedObjects = this.getRelatedObjects(serializer, queryEngine, - entry.getKey()); + entry.getKey(), entry.getValue(), this.loader); if (null != entry.getValue()) relatedObjectsMap.put(entry.getValue().getObjectId(), relatedObjects); } catch (IllegalAccessException | IllegalArgumentException @@ -959,7 +1085,7 @@ public class HttpEntry { return relatedObjectsMap; } - + private void buildNotificationEvent(String sourceOfTruth, Status status, String transactionId, UEBNotification notification, Map<Vertex, Introspector> deleteObjects, Map<String, URI> uriMap, Map<String, HashMap<String, Introspector>> deleteRelatedObjects, String basePath) { @@ -980,4 +1106,27 @@ public class HttpEntry { } } } + + public void setPaginationParameters(String resultIndex, String resultSize) { + if (resultIndex != null && resultIndex != "-1" && resultSize != null && resultSize != "-1") { + this.setPaginationIndex(Integer.parseInt(resultIndex)); + this.setPaginationBucket(Integer.parseInt(resultSize)); + } + } + + public List<Object> getPaginatedVertexList(List<Object> vertexList) throws AAIException{ + List<Object> vertices; + if(this.isPaginated()) { + this.setTotalsForPaging(vertexList.size(), this.getPaginationBucket()); + int startIndex = (this.getPaginationIndex() - 1) * this.getPaginationBucket(); + int endIndex = Math.min((this.getPaginationBucket() * this.getPaginationIndex()), vertexList.size()); + if(startIndex > endIndex){ + throw new AAIException("AAI_6150"," ResultIndex is not appropriate for the result set, Needs to be <= "+ endIndex); + } + vertices = vertexList.subList(startIndex, endIndex); + }else{ + vertices = vertexList; + } + return vertices; + } } 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 7a2c447e..9d107b1f 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 @@ -23,18 +23,21 @@ package org.onap.aai.serialization.db; import com.att.eelf.configuration.EELFLogger; import com.att.eelf.configuration.EELFManager; import com.google.common.base.CaseFormat; -import org.janusgraph.core.SchemaViolationException; +import com.google.common.collect.Multimap; import org.apache.commons.collections.IteratorUtils; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree; import org.apache.tinkerpop.gremlin.structure.*; -import org.javatuples.Pair; +import org.janusgraph.core.SchemaViolationException; import org.javatuples.Triplet; +import org.onap.aai.concurrent.AaiCallable; +import org.onap.aai.config.SpringContextAware; import org.onap.aai.db.props.AAIProperties; import org.onap.aai.edges.EdgeIngestor; import org.onap.aai.edges.EdgeRule; import org.onap.aai.edges.EdgeRuleQuery; +import org.onap.aai.edges.TypeAlphabetizer; import org.onap.aai.edges.enums.AAIDirection; import org.onap.aai.edges.enums.EdgeField; import org.onap.aai.edges.enums.EdgeProperty; @@ -57,6 +60,7 @@ import org.onap.aai.schema.enums.PropertyMetadata; import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException; import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException; import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.serialization.engines.query.QueryEngine; import org.onap.aai.serialization.tinkerpop.TreeBackedVertex; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.setup.SchemaVersions; @@ -64,8 +68,6 @@ import org.onap.aai.util.AAIConfig; import org.onap.aai.util.AAIConstants; import org.onap.aai.workarounds.NamingExceptions; import org.springframework.context.ApplicationContext; -import org.onap.aai.concurrent.AaiCallable; -import org.onap.aai.config.SpringContextAware; import javax.ws.rs.core.UriBuilder; import java.io.UnsupportedEncodingException; @@ -78,1790 +80,1956 @@ import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.onap.aai.serialization.engines.query.QueryEngine; +import java.util.stream.Collectors; public class DBSerializer { - - private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class); - - private final TransactionalGraphEngine engine; - private final String sourceOfTruth; - private final ModelType introspectionType; - private final SchemaVersion version; - private final Loader latestLoader; - private EdgeSerializer edgeSer; - private EdgeIngestor edgeRules; - private final Loader loader; - private final String baseURL; - private double dbTimeMsecs = 0; - private long currentTimeMillis; - - private SchemaVersions schemaVersions; - /** - * Instantiates a new DB serializer. - * - * @param version the version - * @param engine the engine - * @param introspectionType the introspection type - * @param sourceOfTruth the source of truth - * @throws AAIException - */ - public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException { - this.engine = engine; - this.sourceOfTruth = sourceOfTruth; - this.introspectionType = introspectionType; - this.schemaVersions = SpringContextAware.getBean(SchemaVersions.class); - SchemaVersion LATEST = schemaVersions.getDefaultVersion(); - this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, LATEST); - this.version = version; - this.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version); - this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE); - this.currentTimeMillis = System.currentTimeMillis(); - initBeans(); - } - - private void initBeans() { - //TODO proper spring wiring, but that requires a lot of refactoring so for now we have this - ApplicationContext ctx = SpringContextAware.getApplicationContext(); - EdgeIngestor ei = ctx.getBean(EdgeIngestor.class); - setEdgeIngestor(ei); - EdgeSerializer es = ctx.getBean(EdgeSerializer.class); - setEdgeSerializer(es); - } - - private void backupESInit() { - setEdgeSerializer(new EdgeSerializer(this.edgeRules)); - } - - public void setEdgeSerializer(EdgeSerializer edgeSer) { - this.edgeSer = edgeSer; - } - - public EdgeSerializer getEdgeSeriailizer() { - return this.edgeSer; - } - - public void setEdgeIngestor(EdgeIngestor ei) { - this.edgeRules = ei; - } - - public EdgeIngestor getEdgeIngestor(){ - return this.edgeRules; - } - - /** - * Touch standard vertex properties. - * - * @param v the v - * @param isNewVertex the is new vertex - */ - public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) { - String timeNowInSec = Long.toString(currentTimeMillis); - - if (isNewVertex) { - v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth); - v.property(AAIProperties.CREATED_TS, timeNowInSec); - v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString()); - } - v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec); - v.property(AAIProperties.LAST_MOD_TS, timeNowInSec); - v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth); - - } - - private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) { - - v.property(AAIProperties.NODE_TYPE, nodeType); - touchStandardVertexProperties(v, isNewVertex); - - } - - - - /** - * Creates the new vertex. - * - * @param wrappedObject the wrapped object - * @return the vertex - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - public Vertex createNewVertex(Introspector wrappedObject) { - Vertex v; - try { - StopWatch.conditionalStart(); - v = this.engine.tx().addVertex(); - touchStandardVertexProperties(wrappedObject.getDbName(), v, true); - } - finally { - dbTimeMsecs += StopWatch.stopIfStarted(); - } - return v; - } - - /** - * Trim class name. - * - * @param className the class name - * @return the string - */ - /* - * Removes the classpath from a class name - */ - public String trimClassName (String className) { - String returnValue = ""; - - if (className.lastIndexOf('.') == -1) { - return className; - } - returnValue = className.substring(className.lastIndexOf('.') + 1, className.length()); - - return returnValue; - } - - /** - * Serialize to db. - * - * @param obj the obj - * @param v the v - * @param uriQuery the uri query - * @param identifier the identifier - * @throws SecurityException the security exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws InterruptedException the interrupted exception - * @throws NoSuchMethodException the no such method exception - * @throws AAIException the AAI exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException { - StopWatch.conditionalStart(); - try { - if (uriQuery.isDependent()) { - //try to find the parent - List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList(); - if (!vertices.isEmpty()) { - Vertex parent = vertices.get(0); - this.reflectDependentVertex(parent, v, obj, requestContext); - } else { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier); - } - } else { - serializeSingleVertex(v, obj, requestContext); - } - - } catch (SchemaViolationException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6117", e); - } - dbTimeMsecs += StopWatch.stopIfStarted(); - } - - public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException { - StopWatch.conditionalStart(); - try { - boolean isTopLevel = obj.isTopLevel(); - if (isTopLevel) { - addUriIfNeeded(v, obj.getURI()); - } - - processObject(obj, v, requestContext); - if (!isTopLevel) { - URI uri = this.getURIForVertex(v); - URIParser parser = new URIParser(this.loader, uri); - if (parser.validate()) { - addUriIfNeeded(v, uri.toString()); - } - } - } catch (SchemaViolationException e) { - throw new AAIException("AAI_6117", e); - } - finally { - dbTimeMsecs += StopWatch.stopIfStarted(); - } - } - - private void addUriIfNeeded(Vertex v, String uri) { - VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI); - if (!uriProp.isPresent() || (uriProp.isPresent() && !uriProp.value().equals(uri))) { - v.property(AAIProperties.AAI_URI, uri); - } - } - - /** - * Process object. - * - * @param <T> the generic type - * @param obj the obj - * @param v the v - * @return the list - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws AAIException the AAI exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - /* - * Helper method for reflectToDb - * Handles all the property setting - */ - private <T> List<Vertex> processObject (Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException { - Set<String> properties = new LinkedHashSet<>(obj.getProperties()); - properties.remove(AAIProperties.RESOURCE_VERSION); - List<Vertex> dependentVertexes = new ArrayList<>(); - List<Vertex> processedVertexes = new ArrayList<>(); - boolean isComplexType = false; - boolean isListType = false; - if (!obj.isContainer()) { - this.touchStandardVertexProperties(obj.getDbName(), v, false); - } - this.executePreSideEffects(obj, v); - for (String property : properties) { - Object value = null; - final String propertyType; - propertyType = obj.getType(property); - isComplexType = obj.isComplexType(property); - isListType = obj.isListType(property); - value = obj.getValue(property); - - if (!(isComplexType || isListType)) { - boolean canModify = this.canModify(obj, property, requestContext); - - if (canModify) { - final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); - String dbProperty = property; - if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { - dbProperty = metadata.get(PropertyMetadata.DB_ALIAS); - } - if (metadata.containsKey(PropertyMetadata.DATA_LINK)) { - //data linked properties are ephemeral - //they are populated dynamically on GETs - continue; - } - if (value != null) { - if (!value.equals(v.property(dbProperty).orElse(null))) { - if (propertyType.toLowerCase().contains(".long")) { - v.property(dbProperty, new Integer(((Long)value).toString())); - } else { - v.property(dbProperty, value); - } - } - } else { - v.property(dbProperty).remove(); - } - } - } else if (isListType) { - List<Object> list = (List<Object>)value; - if (obj.isComplexGenericType(property)) { - if (list != null) { - for (Object o : list) { - Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o); - child.setURIChain(obj.getURI()); - processedVertexes.add(reflectDependentVertex(v, child, requestContext)); - } - } - } else { - //simple list case - engine.setListProperty(v, property, list); - } - } else { - //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method - if (value != null) { //effectively ignore complex properties not included in the object we're processing - if (value.getClass().isArray()) { - - int length = Array.getLength(value); - for (int i = 0; i < length; i ++) { - Object arrayElement = Array.get(value, i); - Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement); - child.setURIChain(obj.getURI()); - processedVertexes.add(reflectDependentVertex(v, child, requestContext)); - - } - } else if (!property.equals("relationship-list")) { - // container case - Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value); - if (introspector.isContainer()) { - dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName())); - introspector.setURIChain(obj.getURI()); - - processedVertexes.addAll(processObject(introspector, v, requestContext)); - - } else { - dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName())); - processedVertexes.add(reflectDependentVertex(v, introspector, requestContext)); - - } - } else if (property.equals("relationship-list")) { - handleRelationships(obj, v); - } - } - } - } - this.writeThroughDefaults(v, obj); - /* handle those vertexes not touched */ - for (Vertex toBeRemoved : processedVertexes) { - dependentVertexes.remove(toBeRemoved); - } - this.deleteItemsWithTraversal(dependentVertexes); - - this.executePostSideEffects(obj, v); - return processedVertexes; - } - - /** - * Handle relationships. - * - * @param obj the obj - * @param vertex the vertex - * @throws SecurityException the security exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - /* - * Handles the explicit relationships defined for an obj - */ - private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException { - - - - Introspector wrappedRl = obj.getWrappedValue("relationship-list"); - processRelationshipList(wrappedRl, vertex); - - - } - - - /** - * Process relationship list. - * - * @param wrapped the wrapped - * @param v the v - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException { - - List<Object> relationships = (List<Object>)wrapped.getValue("relationship"); - - List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>(); - List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader()); - - for (Object relationship : relationships) { - Edge e = null; - Vertex cousinVertex = null; - String label = null; - Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship); - QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel); - - if (wrappedRel.hasProperty("relationship-label")) { - label = wrappedRel.getValue("relationship-label"); - } - - List<Vertex> results = parser.getQueryBuilder().toList(); - if (results.isEmpty()) { - final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); - ex.getTemplateVars().add(parser.getResultType()); - ex.getTemplateVars().add(parser.getUri().toString()); - throw ex; - } else { - //still an issue if there's more than one - cousinVertex = results.get(0); - } - - if (cousinVertex != null) { - String vType = (String)v.property(AAIProperties.NODE_TYPE).value(); - String cousinType = (String)cousinVertex.property(AAIProperties.NODE_TYPE).value(); - EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label); - - - if (!edgeRules.hasRule(baseQ.build())) { - throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " - + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label):"") +"."); - } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) { - throw new AAIException("AAI_6145"); - } - - e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label); - - if (e == null) { - addEdges.add(new Triplet<>(v, cousinVertex, label)); - } else { - existingEdges.remove(e); - } - } - } - - for (Edge edge : existingEdges) { - edge.remove(); - } - for (Triplet<Vertex, Vertex, String> triplet : addEdges) { - try { - edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2()); - } catch (NoEdgeRuleFoundException e) { - throw new AAIException("AAI_6129", e); - } - } - - } - - /** - * Write through defaults. - * - * @param v the v - * @param obj the obj - * @throws AAIUnknownObjectException - */ - private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException { - Introspector latest = this.latestLoader.introspectorFromName(obj.getName()); - if (latest != null) { - Set<String> required = latest.getRequiredProperties(); - - for (String field : required) { - String defaultValue = null; - Object vertexProp = null; - defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE); - if (defaultValue != null) { - vertexProp = v.<Object>property(field).orElse(null); - if (vertexProp == null) { - v.property(field, defaultValue); - } - } - } - } - - } - - - /** - * Reflect dependent vertex. - * - * @param v the v - * @param dependentObj the dependent obj - * @return the vertex - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws AAIException the AAI exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException { - - //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI()); - //List<Vertex> items = p.getQuery().toList(); - QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v); - query.createEdgeTraversal(EdgeType.TREE, v, dependentObj); - query.createKeyQuery(dependentObj); - - List<Vertex> items = query.toList(); - - Vertex dependentVertex = null; - if (items.size() == 1) { - dependentVertex = items.get(0); - this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI()); - } else { - this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String)dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String)dependentObj.getURI()); - dependentVertex = createNewVertex(dependentObj); - } - - return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext); - - } - - /** - * Reflect dependent vertex. - * - * @param parent the parent - * @param child the child - * @param obj the obj - * @return the vertex - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws InstantiationException the instantiation exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws AAIException the AAI exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException { - - String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null); - if (parentUri != null) { - String uri; - uri = obj.getURI(); - addUriIfNeeded(child, parentUri + uri); - } - processObject(obj, child, requestContext); - - Edge e; - e = this.getEdgeBetween(EdgeType.TREE, parent, child, null); - if (e == null) { - String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED); - if (canBeLinked != null && canBeLinked.equals("true")) { - Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, getVerForContext(requestContext)); - boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext(); - if (isFirst) { - child.property(AAIProperties.LINKED, true); - } - } - edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child); - } - return child; - - } - - private SchemaVersion getVerForContext(String requestContext) { - Pattern pattern = Pattern.compile("v[0-9]+"); - Matcher m = pattern.matcher(requestContext); - if (!m.find()) { - return this.version; - } else { - return new SchemaVersion(requestContext); - } - } - - /** - * Db to object. - * - * @param vertices the vertices - * @param obj the obj - * @param depth the depth - * @param cleanUp the clean up - * @return the introspector - * @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 - */ - public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException { - final int internalDepth; - if (depth == Integer.MAX_VALUE) { - internalDepth = depth--; - } else { - internalDepth = depth; - } - StopWatch.conditionalStart(); - if (vertices.size() > 1 && !obj.isContainer()) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName()); - } else if (obj.isContainer()) { - final List getList; - String listProperty = null; - for (String property : obj.getProperties()) { - if (obj.isListType(property) && obj.isComplexGenericType(property)) { - listProperty = property; - break; - } - } - final String propertyName = listProperty; - getList = (List)obj.getValue(listProperty); - - /* This is an experimental multithreading experiment - * on get alls. - */ - ExecutorService pool = GetAllPool.getInstance().getPool(); - - List<Future<Object>> futures = new ArrayList<>(); - - QueryEngine tgEngine = this.engine.getQueryEngine(); - for (Vertex v : vertices) { - - AaiCallable<Object> task = new AaiCallable<Object>() { - @Override - public Object process() throws UnsupportedEncodingException, AAIException { - Set<Vertex> seen = new HashSet<>(); - Introspector childObject; - try { - childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName); - } catch (AAIUnknownObjectException e) { - throw e; - } - Tree<Element> tree = tgEngine.findSubGraph(v, internalDepth, nodeOnly); - TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree); - try { - dbToObject(childObject, treeVertex, seen, internalDepth, nodeOnly, cleanUp); - } catch (UnsupportedEncodingException e) { - throw e; - } catch (AAIException e) { - throw e; - } - return childObject.getUnderlyingObject(); - //getList.add(childObject.getUnderlyingObject()); - } - }; - futures.add(pool.submit(task)); - } - - for (Future<Object> future : futures) { - try { - getList.add(future.get()); - } catch (ExecutionException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_4000", e); - } catch (InterruptedException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_4000", e); - } - } - } else if (vertices.size() == 1) { - Set<Vertex> seen = new HashSet<>(); - Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(vertices.get(0), depth, nodeOnly); - TreeBackedVertex treeVertex = new TreeBackedVertex(vertices.get(0), tree); - dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp); - } else { - //obj = null; - } - - dbTimeMsecs += StopWatch.stopIfStarted(); - return obj; - } - - /** - * Db to object. - * - * @param obj the obj - * @param v the v - * @param seen the seen - * @param depth the depth - * @param cleanUp the clean up - * @return the introspector - * @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 AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException - */ - private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException { - - if (depth < 0) { - return null; - } - depth--; - seen.add(v); - - boolean modified = false; - for (String property : obj.getProperties(PropertyPredicates.isVisible())) { - List<Object> getList = null; - Vertex[] vertices = null; - - if (!(obj.isComplexType(property) || obj.isListType(property))) { - this.copySimpleProperty(property, obj, v); - modified = true; - } else { - if (obj.isComplexType(property)) { - /* container case */ - - if (!property.equals("relationship-list") && depth >= 0) { - Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); - Object result = dbToObject(argumentObject, v, seen, depth+1, nodeOnly, cleanUp); - if (result != null) { - obj.setValue(property, argumentObject.getUnderlyingObject()); - modified = true; - } - } else if (property.equals("relationship-list") && !nodeOnly){ - /* relationships need to be handled correctly */ - Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); - relationshipList = createRelationshipList(v, relationshipList, cleanUp); - if (relationshipList != null) { - modified = true; - obj.setValue(property, relationshipList.getUnderlyingObject()); - modified = true; - } - - } - } else if (obj.isListType(property)) { - - if (property.equals("any")) { - continue; - } - String genericType = obj.getGenericTypeClass(property).getSimpleName(); - if (obj.isComplexGenericType(property) && depth >= 0) { - final String childDbName = convertFromCamelCase(genericType); - String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - EdgeRule rule; - - try { - rule = edgeRules.getRule(new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build()); - } catch (EdgeRuleNotFoundException e) { - throw new NoEdgeRuleFoundException(e); - } catch (AmbiguousRuleChoiceException e) { - throw new MultipleEdgeRuleFoundException(e); - } - if (!rule.getContains().equals(AAIDirection.NONE.toString())) { - //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); - Direction ruleDirection = rule.getDirection(); - Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); - List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr); - itr = verticesList.stream().filter(item -> { - return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); - }).iterator(); - if (itr.hasNext()) { - getList = (List<Object>)obj.getValue(property); - } - int processed = 0; - int removed = 0; - while (itr.hasNext()) { - Vertex childVertex = itr.next(); - if (!seen.contains(childVertex)) { - Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); - - Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp); - if (result != null) { - getList.add(argumentObject.getUnderlyingObject()); - } - - processed++; - } else { - removed++; - LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); - } - } - if (processed == 0) { - //vertices were all seen, reset the list - getList = null; - } - if (processed > 0) { - modified = true; - } - } - } else if (obj.isSimpleGenericType(property)) { - List<Object> temp = this.engine.getListProperty(v, property); - if (temp != null) { - getList = (List<Object>)obj.getValue(property); - getList.addAll(temp); - modified = true; - } - - } - - } - - } - } - - //no changes were made to this obj, discard the instance - if (!modified) { - return null; - } - this.enrichData(obj, v); - return obj; - - } - - - public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException { - String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - if (nodeType == null) { - throw new AAIException("AAI_6143"); - } - - Introspector obj = this.latestLoader.introspectorFromName(nodeType); - Set<Vertex> seen = new HashSet<>(); - int depth = 0; - String cleanUp = "false"; - boolean nodeOnly = true; - StopWatch.conditionalStart(); - this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp); - dbTimeMsecs += StopWatch.stopIfStarted(); - return obj; - - } - public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException { - String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - if (nodeType == null) { - throw new AAIException("AAI_6143"); - } - Introspector obj = this.latestLoader.introspectorFromName(nodeType); - Set<Vertex> seen = new HashSet<>(); - int depth = AAIProperties.MAXIMUM_DEPTH; - String cleanUp = "false"; - boolean nodeOnly = false; - StopWatch.conditionalStart(); - Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly); - TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree); - this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp); - dbTimeMsecs += StopWatch.stopIfStarted(); - return obj; - } - /** - * Copy simple property. - * - * @param property the property - * @param obj the obj - * @param v the v - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - */ - private void copySimpleProperty(String property, Introspector obj, Vertex v) { - - final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); - String dbPropertyName = property; - - if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { - dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS); - } - - - - final Object temp = v.<Object>property(dbPropertyName).orElse(null); - if (temp != null) { - obj.setValue(property, temp); - } - } - - /** - * Simple db to object. - * - * @param obj the obj - * @param v the v - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - */ - private void simpleDbToObject (Introspector obj, Vertex v) { - for (String property : obj.getProperties()) { - - - if (!(obj.isComplexType(property) || obj.isListType(property))) { - this.copySimpleProperty(property, obj, v); - } - } - } - - /** - * Creates the relationship list. - * - * @param v the v - * @param obj the obj - * @param cleanUp the clean up - * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws URISyntaxException - */ - private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException { - - List<Vertex> cousins = this.engine.getQueryEngine().findCousinVertices(v); - - List<Object> relationshipObjList = obj.getValue("relationship"); - - for (Vertex cousin : cousins) { - if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) { - List<Edge> edges = this.getEdgesBetween(EdgeType.COUSIN, v, cousin); - for (Edge e : edges) { - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, e); - if (result != null) { - relationshipObjList.add(result); - } - } - } else { - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); - if (result != null) { - relationshipObjList.add(result); - } - } - - } - - if (relationshipObjList.isEmpty()) { - return null; - } else { - return obj; - } - } - - /** - * Process edge relationship. - * - * @param relationshipObj the relationship obj - * @param edge the edge - * @param cleanUp the clean up - * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws AAIUnknownObjectException - * @throws URISyntaxException - */ - private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, Edge edge) throws UnsupportedEncodingException, AAIUnknownObjectException { - - - //we must look up all parents in this case because we need to compute name-properties - //we cannot used the cached aaiUri to perform this action currently - Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(relationshipObj.getLoader(), cousin, "true".equals(cleanUp)); - //damaged vertex found, ignore - if (!tuple.isPresent()) { - return null; - } - List<Introspector> list = tuple.get().getValue1(); - URI uri = this.getURIFromList(list); - - URIToRelationshipObject uriParser = null; - Introspector result = null; - try { - uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL); - result = uriParser.getResult(); - } catch (AAIException | URISyntaxException e) { - LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + tuple.get().getValue0().id().toString() + ": " - + e.getMessage() + " " + LogFormatTools.getStackTop(e)); - if ("true".equals(cleanUp)) { - this.deleteWithTraversal(tuple.get().getValue0()); - } - return null; - } - if (!list.isEmpty()) { - this.addRelatedToProperty(result, list.get(0)); - } - - if (edge != null && result.hasProperty("relationship-label")) { - result.setValue("relationship-label", edge.label()); - } - - return result.getUnderlyingObject(); - } - - /** - * Gets the URI for vertex. - * - * @param v the v - * @return the URI for vertex - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException - */ - public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException { - - return getURIForVertex(v, false); - } - - public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException { - URI uri = UriBuilder.fromPath("/unknown-uri").build(); - - String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null); - - if (aaiUri != null && !overwrite) { - uri = UriBuilder.fromPath(aaiUri).build(); - } else { - StopWatch.conditionalStart(); - Optional<Pair<Vertex, List<Introspector>>> tuple = this.getParents(this.loader, v, false); - dbTimeMsecs += StopWatch.stopIfStarted(); - if (tuple.isPresent()) { - List<Introspector> list = tuple.get().getValue1(); - uri = this.getURIFromList(list); - } - - - } - return uri; - } - /** - * Gets the URI from list. - * - * @param list the list - * @return the URI from list - * @throws UnsupportedEncodingException the unsupported encoding exception - */ - private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException { - String uri = ""; - StringBuilder sb = new StringBuilder(); - for (Introspector i : list) { - sb.insert(0, i.getURI()); - } - - uri = sb.toString(); - return UriBuilder.fromPath(uri).build(); - } - - /** - * Gets the parents. - * - * @param start the start - * @param removeDamaged the remove damaged - * @return the parents - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws AAIUnknownObjectException - */ - private Optional<Pair<Vertex, List<Introspector>>> getParents(Loader loader, Vertex start, boolean removeDamaged) { - - List<Vertex> results = this.engine.getQueryEngine().findParents(start); - List<Introspector> objs = new ArrayList<>(); - boolean shortCircuit = false; - for (Vertex v : results) { - String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - Introspector obj = null; - //vertex on the other end of this edge is bad - if (nodeType == null) { - //log something here about what was found and that it was removed - ErrorLogHelper.logError("AAI-6143", "Found a damaged parent vertex " + v.id().toString()); - if (removeDamaged) { - this.deleteWithTraversal(v); - } - shortCircuit = true; - } else { - try { - obj = loader.introspectorFromName(nodeType); - } catch (AAIUnknownObjectException e) { - LOGGER.info("attempted to create node type " + nodeType + " but we do not understand it for version: " + loader.getVersion()); - obj = null; - } - } - - if (obj == null) { - //can't make a valid path because we don't understand this object - // don't include it - } else { - this.simpleDbToObject(obj, v); - objs.add(obj); - } - } - - //stop processing and don't return anything for this bad vertex - if (shortCircuit) { - return Optional.empty(); - } - - return Optional.of(new Pair<>(results.get(results.size()-1), objs)); - } - - /** - * Adds the r - * @throws AAIUnknownObjectException - * @throws IllegalArgumentException elated to property. - * - * @param relationship the relationship - * @param child the throws IllegalArgumentException, AAIUnknownObjectException child - */ - public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException { - String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS); - List<Introspector> relatedToProperties = new ArrayList<>(); - - if (nameProps != null) { - String[] props = nameProps.split(","); - for (String prop : props) { - Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property"); - relatedTo.setValue("property-key", child.getDbName() + "." + prop); - relatedTo.setValue("property-value", child.getValue(prop)); - relatedToProperties.add(relatedTo); - } - } - - if (!relatedToProperties.isEmpty()) { - List relatedToList = (List)relationship.getValue("related-to-property"); - for (Introspector obj : relatedToProperties) { - relatedToList.add(obj.getUnderlyingObject()); - } - } - - } - - /** - * Creates the edge. - * - * @param relationship the relationship - * @param inputVertex the input vertex - * @return true, if successful - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { - - Vertex relatedVertex = null; - StopWatch.conditionalStart(); - QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); - - String label = null; - if (relationship.hasProperty("relationship-label")) { - label = relationship.getValue("relationship-label"); - } - - List<Vertex> results = parser.getQueryBuilder().toList(); - if (results.isEmpty()) { - dbTimeMsecs += StopWatch.stopIfStarted(); - AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); - e.getTemplateVars().add(parser.getResultType()); - e.getTemplateVars().add(parser.getUri().toString()); - throw e; - } else { - //still an issue if there's more than one - relatedVertex = results.get(0); - } - - if (relatedVertex != null) { - - Edge e; - try { - e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); - if (e == null) { - edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label); - } else { - //attempted to link two vertexes already linked - } - } finally { - dbTimeMsecs += StopWatch.stopIfStarted(); - } - } - - dbTimeMsecs += StopWatch.stopIfStarted(); - return true; - } - - /** - * Gets all the edges between of the type. - * - * @param aVertex the out vertex - * @param bVertex the in vertex - * @return the edges between - * @throws AAIException the AAI exception - * @throws NoEdgeRuleFoundException - */ - private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { - - List<Edge> result = new ArrayList<>(); - - if (bVertex != null) { - GraphTraversal<Vertex, Edge> findEdgesBetween = null; - findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); - if (EdgeType.TREE.equals(type)) { - findEdgesBetween = findEdgesBetween - .not( - __.or( - __.has(EdgeProperty.CONTAINS.toString(), "NONE"), - __.has(EdgeField.PRIVATE.toString(), true) - ) - ); - } else { - findEdgesBetween = findEdgesBetween - .has(EdgeProperty.CONTAINS.toString(), "NONE") - .not( - __.has(EdgeField.PRIVATE.toString(), true) - ); - } - findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); - result = findEdgesBetween.toList(); - } - - return result; - } - /** - * Gets all the edges between the vertexes with the label and type. - * - * @param aVertex the out vertex - * @param bVertex the in vertex - * @param label - * @return the edges between - * @throws AAIException the AAI exception - */ - private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { - - List<Edge> result = new ArrayList<>(); - - if (bVertex != null) { - String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value(); - String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value(); - EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build(); - EdgeRule rule; - try { - rule = edgeRules.getRule(q); - } catch (EdgeRuleNotFoundException e) { - throw new NoEdgeRuleFoundException(e); - } catch (AmbiguousRuleChoiceException e) { - throw new MultipleEdgeRuleFoundException(e); - } - List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex); - for (Edge edge : edges) { - if (edge.label().equals(rule.getLabel())) { - result.add(edge); - } - } - } - - return result; - } - - /** - * Gets the edge between with the label and edge type. - * - * @param aVertex the out vertex - * @param bVertex the in vertex - * @param label - * @return the edge between - * @throws AAIException the AAI exception - * @throws NoEdgeRuleFoundException - */ - public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { - - StopWatch.conditionalStart(); - if (bVertex != null) { - - List<Edge> edges = this.getEdgesBetween(type, aVertex, bVertex, label); - - if (!edges.isEmpty()) { - dbTimeMsecs += StopWatch.stopIfStarted(); - return edges.get(0); - } - - } - dbTimeMsecs += StopWatch.stopIfStarted(); - return null; - } - public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { - return this.getEdgeBetween(type, aVertex, bVertex, null); - } - - - /** - * Delete edge. - * - * @param relationship the relationship - * @param inputVertex the input vertex - * @return true, if successful - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - */ - public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { - - Vertex relatedVertex = null; - StopWatch.conditionalStart(); - QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); - - List<Vertex> results = parser.getQueryBuilder().toList(); - - String label = null; - if (relationship.hasProperty("relationship-label")) { - label = relationship.getValue("relationship-label"); - } - - if (results.isEmpty()) { - dbTimeMsecs += StopWatch.stopIfStarted(); - return false; - } - - relatedVertex = results.get(0); - Edge edge; - try { - edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); - } catch (NoEdgeRuleFoundException e) { - dbTimeMsecs += StopWatch.stopIfStarted(); - throw new AAIException("AAI_6129", e); - } - if (edge != null) { - edge.remove(); - dbTimeMsecs += StopWatch.stopIfStarted(); - return true; - } else { - dbTimeMsecs += StopWatch.stopIfStarted(); - return false; - } - - } - - /** - * Delete items with traversal. - * - * @param vertexes the vertexes - * @throws IllegalStateException the illegal state exception - */ - public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException { - - for (Vertex v : vertexes) { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(DBSerializer.class); + + private final TransactionalGraphEngine engine; + private final String sourceOfTruth; + private final ModelType introspectionType; + private final SchemaVersion version; + private final Loader latestLoader; + private EdgeSerializer edgeSer; + private EdgeIngestor edgeRules; + private final Loader loader; + private final String baseURL; + private double dbTimeMsecs = 0; + private long currentTimeMillis; + + private SchemaVersions schemaVersions; + private Set<String> namedPropNodes; + /** + * Instantiates a new DB serializer. + * + * @param version the version + * @param engine the engine + * @param introspectionType the introspection type + * @param sourceOfTruth the source of truth + * @throws AAIException + */ + public DBSerializer(SchemaVersion version, TransactionalGraphEngine engine, ModelType introspectionType, String sourceOfTruth) throws AAIException { + this.engine = engine; + this.sourceOfTruth = sourceOfTruth; + this.introspectionType = introspectionType; + this.schemaVersions = SpringContextAware.getBean(SchemaVersions.class); + SchemaVersion LATEST = schemaVersions.getDefaultVersion(); + this.latestLoader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, LATEST); + this.version = version; + this.loader = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, version); + this.namedPropNodes = this.latestLoader.getNamedPropNodes(); + this.baseURL = AAIConfig.get(AAIConstants.AAI_SERVER_URL_BASE); + this.currentTimeMillis = System.currentTimeMillis(); + initBeans(); + } + + private void initBeans() { + //TODO proper spring wiring, but that requires a lot of refactoring so for now we have this + ApplicationContext ctx = SpringContextAware.getApplicationContext(); + EdgeIngestor ei = ctx.getBean(EdgeIngestor.class); + setEdgeIngestor(ei); + EdgeSerializer es = ctx.getBean(EdgeSerializer.class); + setEdgeSerializer(es); + } + + private void backupESInit() { + setEdgeSerializer(new EdgeSerializer(this.edgeRules)); + } + + public void setEdgeSerializer(EdgeSerializer edgeSer) { + this.edgeSer = edgeSer; + } + + public EdgeSerializer getEdgeSeriailizer() { + return this.edgeSer; + } + + public void setEdgeIngestor(EdgeIngestor ei) { + this.edgeRules = ei; + } + + public EdgeIngestor getEdgeIngestor() { + return this.edgeRules; + } + + /** + * Touch standard vertex properties. + * + * @param v the v + * @param isNewVertex the is new vertex + */ + public void touchStandardVertexProperties(Vertex v, boolean isNewVertex) { + String timeNowInSec = Long.toString(currentTimeMillis); + + if (isNewVertex) { + v.property(AAIProperties.SOURCE_OF_TRUTH, this.sourceOfTruth); + v.property(AAIProperties.CREATED_TS, timeNowInSec); + v.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString()); + } + v.property(AAIProperties.RESOURCE_VERSION, timeNowInSec); + v.property(AAIProperties.LAST_MOD_TS, timeNowInSec); + v.property(AAIProperties.LAST_MOD_SOURCE_OF_TRUTH, this.sourceOfTruth); + + } + + private void touchStandardVertexProperties(String nodeType, Vertex v, boolean isNewVertex) { + + v.property(AAIProperties.NODE_TYPE, nodeType); + touchStandardVertexProperties(v, isNewVertex); + + } + + + /** + * Creates the new vertex. + * + * @param wrappedObject the wrapped object + * @return the vertex + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public Vertex createNewVertex(Introspector wrappedObject) { + Vertex v; + try { + StopWatch.conditionalStart(); + v = this.engine.tx().addVertex(); + touchStandardVertexProperties(wrappedObject.getDbName(), v, true); + } finally { + dbTimeMsecs += StopWatch.stopIfStarted(); + } + return v; + } + + /** + * Trim class name. + * + * @param className the class name + * @return the string + */ + /* + * Removes the classpath from a class name + */ + public String trimClassName(String className) { + String returnValue = ""; + + if (className.lastIndexOf('.') == -1) { + return className; + } + returnValue = className.substring(className.lastIndexOf('.') + 1, className.length()); + + return returnValue; + } + + /** + * Serialize to db. + * + * @param obj the obj + * @param v the v + * @param uriQuery the uri query + * @param identifier the identifier + * @throws SecurityException the security exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws InterruptedException the interrupted exception + * @throws NoSuchMethodException the no such method exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + public void serializeToDb(Introspector obj, Vertex v, QueryParser uriQuery, String identifier, String requestContext) throws AAIException, UnsupportedEncodingException { + StopWatch.conditionalStart(); + try { + if (uriQuery.isDependent()) { + //try to find the parent + List<Vertex> vertices = uriQuery.getQueryBuilder().getParentQuery().toList(); + if (!vertices.isEmpty()) { + Vertex parent = vertices.get(0); + this.reflectDependentVertex(parent, v, obj, requestContext); + } else { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_6114", "No parent Node of type " + uriQuery.getParentResultType() + " for " + identifier); + } + } else { + serializeSingleVertex(v, obj, requestContext); + } + + } catch (SchemaViolationException e) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_6117", e); + } + dbTimeMsecs += StopWatch.stopIfStarted(); + } + + public void serializeSingleVertex(Vertex v, Introspector obj, String requestContext) throws UnsupportedEncodingException, AAIException { + StopWatch.conditionalStart(); + try { + boolean isTopLevel = obj.isTopLevel(); + if (isTopLevel) { + addUriIfNeeded(v, obj.getURI()); + } + + processObject(obj, v, requestContext); + if (!isTopLevel) { + URI uri = this.getURIForVertex(v); + URIParser parser = new URIParser(this.loader, uri); + if (parser.validate()) { + addUriIfNeeded(v, uri.toString()); + } + } + } catch (SchemaViolationException e) { + throw new AAIException("AAI_6117", e); + } finally { + dbTimeMsecs += StopWatch.stopIfStarted(); + } + } + + private void addUriIfNeeded(Vertex v, String uri) { + VertexProperty<String> uriProp = v.property(AAIProperties.AAI_URI); + if (!uriProp.isPresent() || (uriProp.isPresent() && !uriProp.value().equals(uri))) { + v.property(AAIProperties.AAI_URI, uri); + } + } + + /** + * Process object. + * + * @param <T> the generic type + * @param obj the obj + * @param v the v + * @return the list + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + /* + * Helper method for reflectToDb + * Handles all the property setting + */ + private <T> List<Vertex> processObject(Introspector obj, Vertex v, String requestContext) throws UnsupportedEncodingException, AAIException { + Set<String> properties = new LinkedHashSet<>(obj.getProperties()); + properties.remove(AAIProperties.RESOURCE_VERSION); + List<Vertex> dependentVertexes = new ArrayList<>(); + List<Vertex> processedVertexes = new ArrayList<>(); + boolean isComplexType = false; + boolean isListType = false; + if (!obj.isContainer()) { + this.touchStandardVertexProperties(obj.getDbName(), v, false); + } + this.executePreSideEffects(obj, v); + for (String property : properties) { + Object value = null; + final String propertyType; + propertyType = obj.getType(property); + isComplexType = obj.isComplexType(property); + isListType = obj.isListType(property); + value = obj.getValue(property); + + if (!(isComplexType || isListType)) { + boolean canModify = this.canModify(obj, property, requestContext); + + if (canModify) { + final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); + String dbProperty = property; + if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { + dbProperty = metadata.get(PropertyMetadata.DB_ALIAS); + } + if (metadata.containsKey(PropertyMetadata.DATA_LINK)) { + //data linked properties are ephemeral + //they are populated dynamically on GETs + continue; + } + if (value != null) { + if (!value.equals(v.property(dbProperty).orElse(null))) { + if (propertyType.toLowerCase().contains(".long")) { + v.property(dbProperty, new Integer(((Long) value).toString())); + } else { + v.property(dbProperty, value); + } + } + } else { + v.property(dbProperty).remove(); + } + } + } else if (isListType) { + List<Object> list = (List<Object>) value; + if (obj.isComplexGenericType(property)) { + if (list != null) { + for (Object o : list) { + Introspector child = IntrospectorFactory.newInstance(this.introspectionType, o); + child.setURIChain(obj.getURI()); + processedVertexes.add(reflectDependentVertex(v, child, requestContext)); + } + } + } else { + //simple list case + engine.setListProperty(v, property, list); + } + } else { + //method.getReturnType() is not 'simple' then create a vertex and edge recursively returning an edge back to this method + if (value != null) { //effectively ignore complex properties not included in the object we're processing + if (value.getClass().isArray()) { + + int length = Array.getLength(value); + for (int i = 0; i < length; i++) { + Object arrayElement = Array.get(value, i); + Introspector child = IntrospectorFactory.newInstance(this.introspectionType, arrayElement); + child.setURIChain(obj.getURI()); + processedVertexes.add(reflectDependentVertex(v, child, requestContext)); + + } + } else if (!property.equals("relationship-list")) { + // container case + Introspector introspector = IntrospectorFactory.newInstance(this.introspectionType, value); + if (introspector.isContainer()) { + dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getChildDBName())); + introspector.setURIChain(obj.getURI()); + + processedVertexes.addAll(processObject(introspector, v, requestContext)); + + } else { + dependentVertexes.addAll(this.engine.getQueryEngine().findChildrenOfType(v, introspector.getDbName())); + processedVertexes.add(reflectDependentVertex(v, introspector, requestContext)); + + } + } else if (property.equals("relationship-list")) { + handleRelationships(obj, v); + } + } + } + } + this.writeThroughDefaults(v, obj); + /* handle those vertexes not touched */ + for (Vertex toBeRemoved : processedVertexes) { + dependentVertexes.remove(toBeRemoved); + } + this.deleteItemsWithTraversal(dependentVertexes); + + this.executePostSideEffects(obj, v); + return processedVertexes; + } + + /** + * Handle relationships. + * + * @param obj the obj + * @param vertex the vertex + * @throws SecurityException the security exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + /* + * Handles the explicit relationships defined for an obj + */ + private void handleRelationships(Introspector obj, Vertex vertex) throws UnsupportedEncodingException, AAIException { + + + Introspector wrappedRl = obj.getWrappedValue("relationship-list"); + processRelationshipList(wrappedRl, vertex); + + + } + + + /** + * Process relationship list. + * + * @param wrapped the wrapped + * @param v the v + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + private void processRelationshipList(Introspector wrapped, Vertex v) throws UnsupportedEncodingException, AAIException { + + List<Object> relationships = (List<Object>) wrapped.getValue("relationship"); + + List<Triplet<Vertex, Vertex, String>> addEdges = new ArrayList<>(); + List<Edge> existingEdges = this.engine.getQueryEngine().findEdgesForVersion(v, wrapped.getLoader()); + + for (Object relationship : relationships) { + Edge e = null; + Vertex cousinVertex = null; + String label = null; + Introspector wrappedRel = IntrospectorFactory.newInstance(this.introspectionType, relationship); + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(wrappedRel); + + if (wrappedRel.hasProperty("relationship-label")) { + label = wrappedRel.getValue("relationship-label"); + } + + List<Vertex> results = parser.getQueryBuilder().toList(); + if (results.isEmpty()) { + final AAIException ex = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); + ex.getTemplateVars().add(parser.getResultType()); + ex.getTemplateVars().add(parser.getUri().toString()); + throw ex; + } else { + //still an issue if there's more than one + cousinVertex = results.get(0); + } + + if (cousinVertex != null) { + String vType = (String) v.property(AAIProperties.NODE_TYPE).value(); + String cousinType = (String) cousinVertex.property(AAIProperties.NODE_TYPE).value(); + EdgeRuleQuery.Builder baseQ = new EdgeRuleQuery.Builder(vType, cousinType).label(label); + + + if (!edgeRules.hasRule(baseQ.build())) { + throw new AAIException("AAI_6120", "No EdgeRule found for passed nodeTypes: " + v.property(AAIProperties.NODE_TYPE).value().toString() + ", " + + cousinVertex.property(AAIProperties.NODE_TYPE).value().toString() + (label != null ? (" with label " + label) : "") + "."); + } else if (edgeRules.hasRule(baseQ.edgeType(EdgeType.TREE).build()) && !edgeRules.hasRule(baseQ.edgeType(EdgeType.COUSIN).build())) { + throw new AAIException("AAI_6145"); + } + + e = this.getEdgeBetween(EdgeType.COUSIN, v, cousinVertex, label); + + if (e == null) { + addEdges.add(new Triplet<>(v, cousinVertex, label)); + } else { + existingEdges.remove(e); + } + } + } + + for (Edge edge : existingEdges) { + edge.remove(); + } + for (Triplet<Vertex, Vertex, String> triplet : addEdges) { + try { + edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), triplet.getValue0(), triplet.getValue1(), triplet.getValue2()); + } catch (NoEdgeRuleFoundException e) { + throw new AAIException("AAI_6129", e); + } + } + + } + + /** + * Write through defaults. + * + * @param v the v + * @param obj the obj + * @throws AAIUnknownObjectException + */ + private void writeThroughDefaults(Vertex v, Introspector obj) throws AAIUnknownObjectException { + Introspector latest = this.latestLoader.introspectorFromName(obj.getName()); + if (latest != null) { + Set<String> required = latest.getRequiredProperties(); + + for (String field : required) { + String defaultValue = null; + Object vertexProp = null; + defaultValue = latest.getPropertyMetadata(field).get(PropertyMetadata.DEFAULT_VALUE); + if (defaultValue != null) { + vertexProp = v.<Object>property(field).orElse(null); + if (vertexProp == null) { + v.property(field, defaultValue); + } + } + } + } + + } + + + /** + * Reflect dependent vertex. + * + * @param v the v + * @param dependentObj the dependent obj + * @return the vertex + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + private Vertex reflectDependentVertex(Vertex v, Introspector dependentObj, String requestContext) throws AAIException, UnsupportedEncodingException { + + //QueryParser p = this.engine.getQueryBuilder().createQueryFromURI(obj.getURI()); + //List<Vertex> items = p.getQuery().toList(); + QueryBuilder<Vertex> query = this.engine.getQueryBuilder(v); + query.createEdgeTraversal(EdgeType.TREE, v, dependentObj); + query.createKeyQuery(dependentObj); + + List<Vertex> items = query.toList(); + + Vertex dependentVertex = null; + if (items.size() == 1) { + dependentVertex = items.get(0); + this.verifyResourceVersion("update", dependentObj.getDbName(), dependentVertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI()); + } else { + this.verifyResourceVersion("create", dependentObj.getDbName(), "", (String) dependentObj.getValue(AAIProperties.RESOURCE_VERSION), (String) dependentObj.getURI()); + dependentVertex = createNewVertex(dependentObj); + } + + return reflectDependentVertex(v, dependentVertex, dependentObj, requestContext); + + } + + /** + * Reflect dependent vertex. + * + * @param parent the parent + * @param child the child + * @param obj the obj + * @return the vertex + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws InstantiationException the instantiation exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws AAIException the AAI exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + private Vertex reflectDependentVertex(Vertex parent, Vertex child, Introspector obj, String requestContext) throws AAIException, UnsupportedEncodingException { + + String parentUri = parent.<String>property(AAIProperties.AAI_URI).orElse(null); + if (parentUri != null) { + String uri; + uri = obj.getURI(); + addUriIfNeeded(child, parentUri + uri); + } + processObject(obj, child, requestContext); + + Edge e; + e = this.getEdgeBetween(EdgeType.TREE, parent, child, null); + + if (e == null) { + String canBeLinked = obj.getMetadata(ObjectMetadata.CAN_BE_LINKED); + if (canBeLinked != null && canBeLinked.equals("true")) { + Loader ldrForCntxt = SpringContextAware.getBean(LoaderFactory.class).createLoaderForVersion(introspectionType, getVerForContext(requestContext)); + boolean isFirst = !this.engine.getQueryBuilder(ldrForCntxt, parent).createEdgeTraversal(EdgeType.TREE, parent, obj).hasNext(); + if (isFirst) { + child.property(AAIProperties.LINKED, true); + } + } + edgeSer.addTreeEdge(this.engine.asAdmin().getTraversalSource(), parent, child); + } + return child; + + } + + private SchemaVersion getVerForContext(String requestContext) { + Pattern pattern = Pattern.compile("v[0-9]+"); + Matcher m = pattern.matcher(requestContext); + if (!m.find()) { + return this.version; + } else { + return new SchemaVersion(requestContext); + } + } + + /** + * Db to object. + * + * @param vertices the vertices + * @param obj the obj + * @param depth the depth + * @param cleanUp the clean up + * @return the introspector + * @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 + */ + public Introspector dbToObject(List<Vertex> vertices, final Introspector obj, int depth, boolean nodeOnly, String cleanUp) throws UnsupportedEncodingException, AAIException { + final int internalDepth; + if (depth == Integer.MAX_VALUE) { + internalDepth = depth--; + } else { + internalDepth = depth; + } + StopWatch.conditionalStart(); + if (vertices.size() > 1 && !obj.isContainer()) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_6136", "query object mismatch: this object cannot hold multiple items." + obj.getDbName()); + } else if (obj.isContainer()) { + final List getList; + String listProperty = null; + for (String property : obj.getProperties()) { + if (obj.isListType(property) && obj.isComplexGenericType(property)) { + listProperty = property; + break; + } + } + final String propertyName = listProperty; + getList = (List) obj.getValue(listProperty); + + /* This is an experimental multithreading experiment + * on get alls. + */ + ExecutorService pool = GetAllPool.getInstance().getPool(); + + List<Future<Object>> futures = new ArrayList<>(); + + QueryEngine tgEngine = this.engine.getQueryEngine(); + for (Vertex v : vertices) { + + AaiCallable<Object> task = new AaiCallable<Object>() { + @Override + public Object process() throws UnsupportedEncodingException, AAIException { + Set<Vertex> seen = new HashSet<>(); + Introspector childObject; + try { + childObject = obj.newIntrospectorInstanceOfNestedProperty(propertyName); + } catch (AAIUnknownObjectException e) { + throw e; + } + try { + dbToObject(childObject, v, seen, internalDepth, nodeOnly, cleanUp); + } catch (UnsupportedEncodingException e) { + throw e; + } catch (AAIException e) { + throw e; + } + return childObject.getUnderlyingObject(); + //getList.add(childObject.getUnderlyingObject()); + } + }; + futures.add(pool.submit(task)); + } + + for (Future<Object> future : futures) { + try { + getList.add(future.get()); + } catch (ExecutionException e) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_4000", e); + } catch (InterruptedException e) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_4000", e); + } + } + } else if (vertices.size() == 1) { + Set<Vertex> seen = new HashSet<>(); + dbToObject(obj, vertices.get(0), seen, depth, nodeOnly, cleanUp); + } else { + //obj = null; + } + + dbTimeMsecs += StopWatch.stopIfStarted(); + return obj; + } + + /** + * Db to object. + * + * @param obj the obj + * @param v the v + * @param seen the seen + * @param depth the depth + * @param cleanUp the clean up + * @return the introspector + * @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 AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + private Introspector dbToObject(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, String cleanUp) throws AAIException, UnsupportedEncodingException { + + if (depth < 0) { + return null; + } + depth--; + seen.add(v); + + boolean modified = false; + for (String property : obj.getProperties(PropertyPredicates.isVisible())) { + List<Object> getList = null; + Vertex[] vertices = null; + + if (!(obj.isComplexType(property) || obj.isListType(property))) { + this.copySimpleProperty(property, obj, v); + modified = true; + } else { + if (obj.isComplexType(property)) { + /* container case */ + + if (!property.equals("relationship-list") && depth >= 0) { + Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); + Object result = dbToObject(argumentObject, v, seen, depth + 1, nodeOnly, cleanUp); + if (result != null) { + obj.setValue(property, argumentObject.getUnderlyingObject()); + modified = true; + } + } else if (property.equals("relationship-list") && !nodeOnly) { + /* relationships need to be handled correctly */ + Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); + relationshipList = createRelationshipList(v, relationshipList, cleanUp); + if (relationshipList != null) { + modified = true; + obj.setValue(property, relationshipList.getUnderlyingObject()); + modified = true; + } + + } + } else if (obj.isListType(property)) { + + if (property.equals("any")) { + continue; + } + String genericType = obj.getGenericTypeClass(property).getSimpleName(); + if (obj.isComplexGenericType(property) && depth >= 0) { + final String childDbName = convertFromCamelCase(genericType); + String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + EdgeRule rule; + + try { + rule = edgeRules.getRule(new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build()); + } catch (EdgeRuleNotFoundException e) { + throw new NoEdgeRuleFoundException(e); + } catch (AmbiguousRuleChoiceException e) { + throw new MultipleEdgeRuleFoundException(e); + } + if (!rule.getContains().equals(AAIDirection.NONE.toString())) { + //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); + Direction ruleDirection = rule.getDirection(); + Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); + List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr); + itr = verticesList.stream().filter(item -> { + return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); + }).iterator(); + if (itr.hasNext()) { + getList = (List<Object>) obj.getValue(property); + } + int processed = 0; + int removed = 0; + while (itr.hasNext()) { + Vertex childVertex = itr.next(); + if (!seen.contains(childVertex)) { + Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); + + Object result = dbToObject(argumentObject, childVertex, seen, depth, nodeOnly, cleanUp); + if (result != null) { + getList.add(argumentObject.getUnderlyingObject()); + } + + processed++; + } else { + removed++; + LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); + } + } + if (processed == 0) { + //vertices were all seen, reset the list + getList = null; + } + if (processed > 0) { + modified = true; + } + } + } else if (obj.isSimpleGenericType(property)) { + List<Object> temp = this.engine.getListProperty(v, property); + if (temp != null) { + getList = (List<Object>) obj.getValue(property); + getList.addAll(temp); + modified = true; + } + + } + + } + + } + } + + //no changes were made to this obj, discard the instance + if (!modified) { + return null; + } + this.enrichData(obj, v); + return obj; + + } + + + public Introspector getVertexProperties(Vertex v) throws AAIException, UnsupportedEncodingException { + String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (nodeType == null) { + throw new AAIException("AAI_6143"); + } + + Introspector obj = this.latestLoader.introspectorFromName(nodeType); + Set<Vertex> seen = new HashSet<>(); + int depth = 0; + String cleanUp = "false"; + boolean nodeOnly = true; + StopWatch.conditionalStart(); + this.dbToObject(obj, v, seen, depth, nodeOnly, cleanUp); + dbTimeMsecs += StopWatch.stopIfStarted(); + return obj; + + } + + public Introspector getLatestVersionView(Vertex v) throws AAIException, UnsupportedEncodingException { + String nodeType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (nodeType == null) { + throw new AAIException("AAI_6143"); + } + Introspector obj = this.latestLoader.introspectorFromName(nodeType); + Set<Vertex> seen = new HashSet<>(); + int depth = AAIProperties.MAXIMUM_DEPTH; + String cleanUp = "false"; + boolean nodeOnly = false; + StopWatch.conditionalStart(); + Tree<Element> tree = this.engine.getQueryEngine().findSubGraph(v, depth, nodeOnly); + TreeBackedVertex treeVertex = new TreeBackedVertex(v, tree); + this.dbToObject(obj, treeVertex, seen, depth, nodeOnly, cleanUp); + dbTimeMsecs += StopWatch.stopIfStarted(); + return obj; + } + + /** + * Copy simple property. + * + * @param property the property + * @param obj the obj + * @param v the v + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + */ + private void copySimpleProperty(String property, Introspector obj, Vertex v) { + + final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); + String dbPropertyName = property; + + if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { + dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS); + } + + + final Object temp = v.<Object>property(dbPropertyName).orElse(null); + if (temp != null) { + obj.setValue(property, temp); + } + } + + + /** + * Load the introspector from the hashmap for the given property key + * + * @param property - vertex property + * @param obj - introspector object representing the vertex + * @param hashMap - Containing a list of pre-fetched properties for a given vertex + */ + private void copySimplePropertyFromHashMap(String property, Introspector obj, Map<String, Object> hashMap){ + + final Map<PropertyMetadata, String> metadata = obj.getPropertyMetadata(property); + String dbPropertyName = property; + + if (metadata.containsKey(PropertyMetadata.DB_ALIAS)) { + dbPropertyName = metadata.get(PropertyMetadata.DB_ALIAS); + } + + final Object temp = hashMap.getOrDefault(dbPropertyName, null); + + if (temp != null) { + obj.setValue(property, temp); + } + } + + /** + * Simple db to object. + * + * @param obj the obj + * @param v the v + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + */ + private void simpleDbToObject(Introspector obj, Vertex v) { + for(String key : obj.getProperties()){ + this.copySimpleProperty(key, obj, v); + } + } + + + public Map<String, Object> convertVertexToHashMap(Introspector obj, Vertex v){ + + long startTime = System.currentTimeMillis(); + + Set<String> simpleProperties = obj.getSimpleProperties(PropertyPredicates.isVisible()); + String[] simplePropsArray = new String[simpleProperties.size()]; + simplePropsArray = simpleProperties.toArray(simplePropsArray); + + Map<String, Object> simplePropsHashMap = new HashMap<>(simplePropsArray.length * 2); + + v.properties(simplePropsArray).forEachRemaining((vp) -> simplePropsHashMap.put(vp.key(), vp.value())); + + return simplePropsHashMap; + } + + /** + * Creates the relationship list. + * + * @param v the v + * @param obj the obj + * @param cleanUp the clean up + * @return the object + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws URISyntaxException + */ + private Introspector createRelationshipList(Vertex v, Introspector obj, String cleanUp) throws UnsupportedEncodingException, AAIException { + + String[] cousinRules = new String[0]; + + try { + cousinRules = edgeRules.retrieveCachedCousinLabels(obj.getDbName()); + } catch (ExecutionException e) { + LOGGER.warn("Encountered an execution exception while retrieving labels for the node type {} using cached", obj.getDbName(), e); + } + + List<Vertex> cousins = null; + if(cousinRules != null && cousinRules.length != 0){ + cousins = this.engine.getQueryEngine().findCousinVertices(v, cousinRules); + } else { + cousins = this.engine.getQueryEngine().findCousinVertices(v); + } + + List<Object> relationshipObjList = obj.getValue("relationship"); + String aNodeType = v.property("aai-node-type").value().toString(); + + TypeAlphabetizer alphabetizer = new TypeAlphabetizer(); + + EdgeIngestor edgeIngestor = SpringContextAware.getBean(EdgeIngestor.class); + Set<String> keysWithMultipleLabels = edgeIngestor.getMultipleLabelKeys(); + + // For the given vertex, find all the cousins + // For each cousin retrieve the node type and then + // check if the version is greater than the edge label version + // meaning is the current version equal to greater than the version + // where we introduced the edge labels into the relationship payload + // If it is, then we check if the edge key there are multiple labels + // If there are multiple labels, then we need to go to the database + // to retrieve the labels between itself and cousin vertex + // If there is only single label between the edge a and b, then + // we can retrieve what that is without going to the database + // from using the edge rules json and get the edge rule out of it + EdgeRuleQuery.Builder queryBuilder = new EdgeRuleQuery.Builder(aNodeType); + for (Vertex cousin : cousins) { + VertexProperty vertexProperty = cousin.property("aai-node-type"); + String bNodeType = null; + if(vertexProperty.isPresent()){ + bNodeType = cousin.property("aai-node-type").value().toString(); + } else { + // If the vertex is missing the aai-node-type + // Then its either a bad vertex or its in the process + // of getting deleted so we should ignore these vertexes + if(LOGGER.isDebugEnabled()){ + LOGGER.debug("For the vertex {}, unable to retrieve the aai-node-type", v.id().toString()); + } else { + LOGGER.info("Unable to retrieve the aai-node-type for vertex, for more info enable debug log"); + } + continue; + } + if (obj.getVersion().compareTo(schemaVersions.getEdgeLabelVersion()) >= 0) { + String edgeKey = alphabetizer.buildAlphabetizedKey(aNodeType, bNodeType); + if(keysWithMultipleLabels.contains(edgeKey)){ + List<String> edgeLabels = this.getEdgeLabelsBetween(EdgeType.COUSIN, v, cousin); + for(String edgeLabel: edgeLabels){ + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, edgeLabel); + if (result != null) { + relationshipObjList.add(result); + } + } + } else { + + EdgeRule edgeRule = null; + + // Create a query based on the a nodetype and b nodetype + // which is also a cousin edge and ensure the version + // is used properly so for example in order to be backwards + // compatible if we had allowed a edge between a and b + // in a previous release and we decided to remove it from + // the edge rules in the future we can display the edge + // only for the older apis and the new apis if the edge rule + // is removed will not be seen in the newer version of the API + + EdgeRuleQuery ruleQuery = queryBuilder + .to(bNodeType) + .edgeType(EdgeType.COUSIN) + .version(obj.getVersion()) + .build(); + + try { + edgeRule = edgeIngestor.getRule(ruleQuery); + } catch (EdgeRuleNotFoundException e) { + LOGGER.warn("Caught an edge rule not found exception for query {}, {}," + + " it could be the edge rule is no longer valid for the existing edge in db", + ruleQuery, LogFormatTools.getStackTop(e)); + continue; + } catch (AmbiguousRuleChoiceException e) { + LOGGER.error("Caught an ambiguous rule not found exception for query {}, {}", + ruleQuery, LogFormatTools.getStackTop(e)); + continue; + } + + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp,edgeRule.getLabel()); + if (result != null) { + relationshipObjList.add(result); + } + } + } else { + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); + if (result != null) { + relationshipObjList.add(result); + } + } + + } + + if (relationshipObjList.isEmpty()) { + return null; + } else { + return obj; + } + } + + /** + * Process edge relationship. + * + * @param relationshipObj the relationship obj + * @param edge the edge + * @param cleanUp the clean up + * @return the object + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws AAIUnknownObjectException + * @throws URISyntaxException + */ + private Object processEdgeRelationship(Introspector relationshipObj, Vertex cousin, String cleanUp, String edgeLabel) throws UnsupportedEncodingException, AAIUnknownObjectException { + + VertexProperty aaiUriProperty = cousin.property("aai-uri"); + + if(!aaiUriProperty.isPresent()){ + return null; + } + + URI uri = UriBuilder.fromUri(aaiUriProperty.value().toString()).build(); + + URIToRelationshipObject uriParser = null; + Introspector result = null; + try { + uriParser = new URIToRelationshipObject(relationshipObj.getLoader(), uri, this.baseURL); + result = uriParser.getResult(); + } catch (AAIException | URISyntaxException e) { + LOGGER.error("Error while processing edge relationship in version " + relationshipObj.getVersion() + " (bad vertex ID=" + ": " + + e.getMessage() + " " + LogFormatTools.getStackTop(e)); + return null; + } + + VertexProperty cousinVertexNodeType = cousin.property(AAIProperties.NODE_TYPE); + + if(cousinVertexNodeType.isPresent()){ + if(namedPropNodes.contains(cousinVertexNodeType.value().toString())){ + Introspector cousinObj = loader.introspectorFromName(cousinVertexNodeType.value().toString()); + this.simpleDbToObject(cousinObj, cousin); + this.addRelatedToProperty(result, cousinObj); + } + } + + if (edgeLabel != null && result.hasProperty("relationship-label")) { + result.setValue("relationship-label", edgeLabel); + } + + return result.getUnderlyingObject(); + } + + /** + * Gets the URI for vertex. + * + * @param v the v + * @return the URI for vertex + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIUnknownObjectException + */ + public URI getURIForVertex(Vertex v) throws UnsupportedEncodingException { + + return getURIForVertex(v, false); + } + + public URI getURIForVertex(Vertex v, boolean overwrite) throws UnsupportedEncodingException { + URI uri = UriBuilder.fromPath("/unknown-uri").build(); + + String aaiUri = v.<String>property(AAIProperties.AAI_URI).orElse(null); + + if (aaiUri != null && !overwrite) { + uri = UriBuilder.fromPath(aaiUri).build(); + } + + return uri; + } + + /** + * Gets the URI from list. + * + * @param list the list + * @return the URI from list + * @throws UnsupportedEncodingException the unsupported encoding exception + */ + private URI getURIFromList(List<Introspector> list) throws UnsupportedEncodingException { + String uri = ""; + StringBuilder sb = new StringBuilder(); + for (Introspector i : list) { + sb.insert(0, i.getURI()); + } + + uri = sb.toString(); + return UriBuilder.fromPath(uri).build(); + } + + /** + * Adds the r + * + * @param relationship the relationship + * @param child the throws IllegalArgumentException, AAIUnknownObjectException child + * @throws AAIUnknownObjectException + * @throws IllegalArgumentException elated to property. + */ + public void addRelatedToProperty(Introspector relationship, Introspector child) throws AAIUnknownObjectException { + String nameProps = child.getMetadata(ObjectMetadata.NAME_PROPS); + List<Introspector> relatedToProperties = new ArrayList<>(); + + if (nameProps != null) { + String[] props = nameProps.split(","); + for (String prop : props) { + Introspector relatedTo = relationship.newIntrospectorInstanceOfNestedProperty("related-to-property"); + relatedTo.setValue("property-key", child.getDbName() + "." + prop); + relatedTo.setValue("property-value", child.getValue(prop)); + relatedToProperties.add(relatedTo); + } + } + + if (!relatedToProperties.isEmpty()) { + List relatedToList = (List) relationship.getValue("related-to-property"); + for (Introspector obj : relatedToProperties) { + relatedToList.add(obj.getUnderlyingObject()); + } + } + + } + + /** + * Creates the edge. + * + * @param relationship the relationship + * @param inputVertex the input vertex + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public boolean createEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { + + Vertex relatedVertex = null; + StopWatch.conditionalStart(); + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); + + String label = null; + if (relationship.hasProperty("relationship-label")) { + label = relationship.getValue("relationship-label"); + } + + List<Vertex> results = parser.getQueryBuilder().toList(); + if (results.isEmpty()) { + dbTimeMsecs += StopWatch.stopIfStarted(); + AAIException e = new AAIException("AAI_6129", "Node of type " + parser.getResultType() + ". Could not find object at: " + parser.getUri()); + e.getTemplateVars().add(parser.getResultType()); + e.getTemplateVars().add(parser.getUri().toString()); + throw e; + } else { + //still an issue if there's more than one + relatedVertex = results.get(0); + } + + if (relatedVertex != null) { + + Edge e; + try { + e = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); + if (e == null) { + edgeSer.addEdge(this.engine.asAdmin().getTraversalSource(), inputVertex, relatedVertex, label); + } else { + //attempted to link two vertexes already linked + } + } finally { + dbTimeMsecs += StopWatch.stopIfStarted(); + } + } + + dbTimeMsecs += StopWatch.stopIfStarted(); + return true; + } + + /** + * Gets all the edges between of the type with the specified label. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private Edge getEdgeBetweenWithLabel(EdgeType type, Vertex aVertex, Vertex bVertex, EdgeRule edgeRule) { + + Edge result = null; + + if (bVertex != null) { + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + if (EdgeType.TREE.equals(type)) { + GraphTraversal<Vertex,Vertex> findVertex = this.engine.asAdmin().getTraversalSource().V(bVertex); + if(edgeRule.getDirection().equals(Direction.IN)){ + findEdgesBetween = findVertex.outE(edgeRule.getLabel()) + .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains()) + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + } else { + findEdgesBetween = findVertex.inE(edgeRule.getLabel()) + .has(EdgeProperty.CONTAINS.toString(), edgeRule.getContains()) + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + } + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(aVertex.id())).limit(1); + } else { + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(edgeRule.getLabel()); + findEdgesBetween = findEdgesBetween + .has(EdgeProperty.CONTAINS.toString(), "NONE") + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())).limit(1); + } + List<Edge> list = findEdgesBetween.toList(); + if(!list.isEmpty()){ + result = list.get(0); + } + } + + return result; + } + + /** + * Gets all the edges between of the type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private List<Edge> getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { + + List<Edge> result = new ArrayList<>(); + + if (bVertex != null) { + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); + if (EdgeType.TREE.equals(type)) { + findEdgesBetween = findEdgesBetween + .not( + __.or( + __.has(EdgeProperty.CONTAINS.toString(), "NONE"), + __.has(EdgeField.PRIVATE.toString(), true) + ) + ); + } else { + findEdgesBetween = findEdgesBetween + .has(EdgeProperty.CONTAINS.toString(), "NONE") + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + } + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); + result = findEdgesBetween.toList(); + } + + return result; + } + + /** + * Gets all the edges string between of the type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private List<String> getEdgeLabelsBetween(EdgeType type, Vertex aVertex, Vertex bVertex) { + + List<String> result = new ArrayList<>(); + + if (bVertex != null) { + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); + if (EdgeType.TREE.equals(type)) { + findEdgesBetween = findEdgesBetween + .not( + __.or( + __.has(EdgeProperty.CONTAINS.toString(), "NONE"), + __.has(EdgeField.PRIVATE.toString(), true) + ) + ); + } else { + findEdgesBetween = findEdgesBetween + .has(EdgeProperty.CONTAINS.toString(), "NONE") + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + } + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); + result = findEdgesBetween.label().toList(); + } + return result; + } + + /** + * Gets all the edges string between of the type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @return the edges between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + private Long getEdgeLabelsCount(Vertex aVertex, Vertex bVertex) { + + Long result = null; + + if (bVertex != null) { + GraphTraversal<Vertex, Edge> findEdgesBetween = null; + findEdgesBetween = this.engine.asAdmin().getTraversalSource().V(aVertex).bothE(); + findEdgesBetween = findEdgesBetween + .has(EdgeProperty.CONTAINS.toString(), "NONE") + .not( + __.has(EdgeField.PRIVATE.toString(), true) + ); + findEdgesBetween = findEdgesBetween.filter(__.otherV().hasId(bVertex.id())); + result = findEdgesBetween.count().next(); + } + return result; + } + /** + * Gets all the edges between the vertexes with the label and type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @param label + * @return the edges between + * @throws AAIException the AAI exception + */ + private Edge getEdgesBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { + + Edge edge = null; + + if (bVertex != null) { + String aType = aVertex.<String>property(AAIProperties.NODE_TYPE).value(); + String bType = bVertex.<String>property(AAIProperties.NODE_TYPE).value(); + EdgeRuleQuery q = new EdgeRuleQuery.Builder(aType, bType).edgeType(type).label(label).build(); + EdgeRule rule; + try { + rule = edgeRules.getRule(q); + } catch (EdgeRuleNotFoundException e) { + throw new NoEdgeRuleFoundException(e); + } catch (AmbiguousRuleChoiceException e) { + throw new MultipleEdgeRuleFoundException(e); + } + edge = this.getEdgeBetweenWithLabel(type, aVertex, bVertex, rule); + } + + return edge; + } + + /** + * Gets the edge between with the label and edge type. + * + * @param aVertex the out vertex + * @param bVertex the in vertex + * @param label + * @return the edge between + * @throws AAIException the AAI exception + * @throws NoEdgeRuleFoundException + */ + public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException { + + StopWatch.conditionalStart(); + if (bVertex != null) { + + Edge edge = this.getEdgesBetween(type, aVertex, bVertex, label); + if (edge != null) { + dbTimeMsecs += StopWatch.stopIfStarted(); + return edge; + } + + } + dbTimeMsecs += StopWatch.stopIfStarted(); + return null; + } + + public Edge getEdgeBetween(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException { + return this.getEdgeBetween(type, aVertex, bVertex, null); + } + + + /** + * Delete edge. + * + * @param relationship the relationship + * @param inputVertex the input vertex + * @return true, if successful + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + */ + public boolean deleteEdge(Introspector relationship, Vertex inputVertex) throws UnsupportedEncodingException, AAIException { + + Vertex relatedVertex = null; + StopWatch.conditionalStart(); + QueryParser parser = engine.getQueryBuilder().createQueryFromRelationship(relationship); + + List<Vertex> results = parser.getQueryBuilder().toList(); + + String label = null; + if (relationship.hasProperty("relationship-label")) { + label = relationship.getValue("relationship-label"); + } + + if (results.isEmpty()) { + dbTimeMsecs += StopWatch.stopIfStarted(); + return false; + } + + relatedVertex = results.get(0); + Edge edge; + try { + edge = this.getEdgeBetween(EdgeType.COUSIN, inputVertex, relatedVertex, label); + } catch (NoEdgeRuleFoundException e) { + dbTimeMsecs += StopWatch.stopIfStarted(); + throw new AAIException("AAI_6129", e); + } + if (edge != null) { + edge.remove(); + dbTimeMsecs += StopWatch.stopIfStarted(); + return true; + } else { + dbTimeMsecs += StopWatch.stopIfStarted(); + return false; + } + + } + + /** + * Delete items with traversal. + * + * @param vertexes the vertexes + * @throws IllegalStateException the illegal state exception + */ + public void deleteItemsWithTraversal(List<Vertex> vertexes) throws IllegalStateException { + + for (Vertex v : vertexes) { LOGGER.debug("About to delete the vertex with id: " + v.id()); - deleteWithTraversal(v); - } - - } - - /** - * Delete with traversal. - * - * @param startVertex the start vertex - */ - public void deleteWithTraversal(Vertex startVertex) { - StopWatch.conditionalStart(); - List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex); - - for (Vertex v : results) { - LOGGER.warn("Removing vertex " + v.id().toString()); - - v.remove(); - } - dbTimeMsecs += StopWatch.stopIfStarted(); - } - - /** - * Delete. - * - * @param v the v - * @param resourceVersion the resource version - * @throws IllegalArgumentException the illegal argument exception - * @throws AAIException the AAI exception - * @throws InterruptedException the interrupted exception - */ - public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { - - boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); - /* - * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain - * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted - * So its better to make these in 2 steps - */ - if(result && !deletableVertices.isEmpty()){ - result = verifyPreventDeleteSemantics(deletableVertices); - } - if (result) { - - try { - deleteWithTraversal(v); - } catch (IllegalStateException e) { - throw new AAIException("AAI_6110", e); - } - - } - - } - - - /** - * Delete. - * - * @param v the v - * @param resourceVersion the resource version - * @throws IllegalArgumentException the illegal argument exception - * @throws AAIException the AAI exception - * @throws InterruptedException the interrupted exception - */ - public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { - - boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); - - if (result) { - - try { - deleteWithTraversal(v); - } catch (IllegalStateException e) { - throw new AAIException("AAI_6110", e); - } - - } - - } - /** - * Verify delete semantics. - * - * @param vertex the vertex - * @param resourceVersion the resource version - * @return true, if successful - * @throws AAIException the AAI exception - */ - private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException { - boolean result = true; - String nodeType = ""; - String errorDetail = " unknown delete semantic found"; - String aaiExceptionCode = ""; - nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); - if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) { - } - List<Vertex> vertices = new ArrayList<Vertex>(); - vertices.add(vertex); - result = verifyPreventDeleteSemantics(vertices); - - return result; - } - - /** - * Verify Prevent delete semantics. - * @param vertices the list of vertices - * @return true, if successful - * @throws AAIException the AAI exception - */ - private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException { - boolean result = true; - String nodeType = ""; - String errorDetail = " unknown delete semantic found"; - String aaiExceptionCode = ""; - - StopWatch.conditionalStart(); - /* - * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a "prevent-delete" condition - * If yes - that should prevent the deletion of the vertex - * Dedup makes sure we dont capture the prevent-delete vertices twice - * The prevent-delete vertices are stored so that the error message displays what prevents the delete - */ - - List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices). - union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), - __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)) - .dedup().toList(); - - dbTimeMsecs += StopWatch.stopIfStarted(); - if (!preventDeleteVertices.isEmpty()) { - aaiExceptionCode = "AAI_6110"; - errorDetail = String.format("Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s", preventDeleteVertices); - result = false; - } - if (!result) { - throw new AAIException(aaiExceptionCode, errorDetail); - } - return result; - } - - /** - * Verify resource version. - * - * @param action the action - * @param nodeType the node type - * @param currentResourceVersion the current resource version - * @param resourceVersion the resource version - * @param uri the uri - * @return true, if successful - * @throws AAIException the AAI exception - */ - public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException { - String enabled = ""; - String errorDetail = ""; - String aaiExceptionCode = ""; - if (currentResourceVersion == null) { - currentResourceVersion = ""; - } - - if (resourceVersion == null) { - resourceVersion = ""; - } - 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("")) { - errorDetail = "resource-version passed for " + action + " of " + uri; - aaiExceptionCode = "AAI_6135"; - } else if (resourceVersion.equals("")) { - errorDetail = "resource-version not passed for " + action + " of " + uri; - aaiExceptionCode = "AAI_6130"; - } else { - errorDetail = "resource-version MISMATCH for " + action + " of " + uri; - aaiExceptionCode = "AAI_6131"; - } - - throw new AAIException(aaiExceptionCode, errorDetail); - - } - } - return true; - } - - /** - * Convert from camel case. - * - * @param name the name - * @return the string - */ - private String convertFromCamelCase (String name) { - String result = ""; - result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name); - - NamingExceptions exceptions = NamingExceptions.getInstance(); - result = exceptions.getDBName(result); - - return result; - } - - private boolean canModify(Introspector obj, String propName, String requestContext) { - final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY); - if (readOnly != null) { - final String[] items = readOnly.split(","); - for (String item : items) { - if (requestContext.equals(item)) { - return false; - } - } - } - return true; - } - - private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException { - - SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build(); - - runner.execute(obj, self); - } - - private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException { - - SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build(); - - runner.execute(obj, self); - } - - private void enrichData(Introspector obj, Vertex self) throws AAIException { - - SideEffectRunner runner = new SideEffectRunner - .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build(); - - runner.execute(obj, self); - } - - public double getDBTimeMsecs() { - return (dbTimeMsecs); - } - - /** - * Db to object With Filters - * This is for a one-time run with Tenant Isloation to only filter relationships - * TODO: Chnage the original dbToObject to take filter parent/cousins - * - * @param obj the obj - * @param v the vertex from the graph - * @param depth the depth - * @param nodeOnly specify if to exclude relationships or not - * @param filterCousinNodes - * @return the introspector - * @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 - */ - //TODO - See if you can merge the 2 dbToObjectWithFilters - public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException { - String cleanUp = "false"; - if (depth < 0) { - return null; - } - depth--; - seen.add(v); - boolean modified = false; - for (String property : obj.getProperties(PropertyPredicates.isVisible())) { - List<Object> getList = null; - Vertex[] vertices = null; - - if (!(obj.isComplexType(property) || obj.isListType(property))) { - this.copySimpleProperty(property, obj, v); - modified = true; - } else { - if (obj.isComplexType(property)) { - /* container case */ - - if (!property.equals("relationship-list") && depth >= 0) { - Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); - Object result = dbToObjectWithFilters(argumentObject, v, seen, depth+1, nodeOnly, filterCousinNodes, filterParentNodes); - if (result != null) { - obj.setValue(property, argumentObject.getUnderlyingObject()); - modified = true; - } - } else if (property.equals("relationship-list") && !nodeOnly){ - /* relationships need to be handled correctly */ - Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); - relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes); - if (relationshipList != null) { - modified = true; - obj.setValue(property, relationshipList.getUnderlyingObject()); - modified = true; - } - - } - } else if (obj.isListType(property)) { - - if (property.equals("any")) { - continue; - } - String genericType = obj.getGenericTypeClass(property).getSimpleName(); - if (obj.isComplexGenericType(property) && depth >= 0) { - final String childDbName = convertFromCamelCase(genericType); - String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); - EdgeRule rule; - - boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains); - - EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build(); - - try { - rule = edgeRules.getRule(q); - } catch (EdgeRuleNotFoundException e) { - throw new NoEdgeRuleFoundException(e); - } catch (AmbiguousRuleChoiceException e) { - throw new MultipleEdgeRuleFoundException(e); - } - if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) { - //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); - Direction ruleDirection = rule.getDirection(); - Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); - List<Vertex> verticesList = (List<Vertex>)IteratorUtils.toList(itr); - itr = verticesList.stream().filter(item -> { - return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); - }).iterator(); - if (itr.hasNext()) { - getList = (List<Object>)obj.getValue(property); - } - int processed = 0; - int removed = 0; - while (itr.hasNext()) { - Vertex childVertex = itr.next(); - if (!seen.contains(childVertex)) { - Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); - - Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes); - if (result != null) { - getList.add(argumentObject.getUnderlyingObject()); - } - - processed++; - } else { - removed++; - LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); - } - } - if (processed == 0) { - //vertices were all seen, reset the list - getList = null; - } - if (processed > 0) { - modified = true; - } - } - } else if (obj.isSimpleGenericType(property)) { - List<Object> temp = this.engine.getListProperty(v, property); - if (temp != null) { - getList = (List<Object>)obj.getValue(property); - getList.addAll(temp); - modified = true; - } - - } - - } - - } - } - - //no changes were made to this obj, discard the instance - if (!modified) { - return null; - } - this.enrichData(obj, v); - return obj; - - } - - /** - * Creates the relationship list with the filtered node types. - * - * @param v the v - * @param obj the obj - * @param cleanUp the clean up - * @return the object - * @throws InstantiationException the instantiation exception - * @throws IllegalAccessException the illegal access exception - * @throws IllegalArgumentException the illegal argument exception - * @throws InvocationTargetException the invocation target exception - * @throws NoSuchMethodException the no such method exception - * @throws SecurityException the security exception - * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIException the AAI exception - * @throws MalformedURLException the malformed URL exception - * @throws URISyntaxException - */ - private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException { - List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v); - - Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> { - String node = (String)item.property(AAIProperties.NODE_TYPE).orElse(""); - return filterNodes.parallelStream().anyMatch(node::contains); - }).iterator(); - - - List<Vertex> cousins = (List<Vertex>)IteratorUtils.toList(cousinVertices); - - //items.parallelStream().anyMatch(inputStr::contains) - List<Object> relationshipObjList = obj.getValue("relationship"); - for (Vertex cousin : cousins) { - - Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); - Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); - if (result != null) { - relationshipObjList.add(result); - } - - - } - - if (relationshipObjList.isEmpty()) { - return null; - } else { - return obj; - } - } + deleteWithTraversal(v); + } + + } + + /** + * Delete with traversal. + * + * @param startVertex the start vertex + */ + public void deleteWithTraversal(Vertex startVertex) { + StopWatch.conditionalStart(); + List<Vertex> results = this.engine.getQueryEngine().findDeletable(startVertex); + + for (Vertex v : results) { + LOGGER.warn("Removing vertex " + v.id().toString()); + + v.remove(); + } + dbTimeMsecs += StopWatch.stopIfStarted(); + } + + /** + * Delete. + * + * @param v the v + * @param resourceVersion the resource version + * @throws IllegalArgumentException the illegal argument exception + * @throws AAIException the AAI exception + * @throws InterruptedException the interrupted exception + */ + public void delete(Vertex v, List<Vertex> deletableVertices, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { + + boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); + /* + * The reason why I want to call PreventDeleteSemantics second time is to catch the prevent-deletes in a chain + * These are far-fewer than seeing a prevnt-delete on the vertex to be deleted + * So its better to make these in 2 steps + */ + if (result && !deletableVertices.isEmpty()) { + result = verifyPreventDeleteSemantics(deletableVertices); + } + if (result) { + + try { + deleteWithTraversal(v); + } catch (IllegalStateException e) { + throw new AAIException("AAI_6110", e); + } + + } + + } + + + /** + * Delete. + * + * @param v the v + * @param resourceVersion the resource version + * @throws IllegalArgumentException the illegal argument exception + * @throws AAIException the AAI exception + * @throws InterruptedException the interrupted exception + */ + public void delete(Vertex v, String resourceVersion, boolean enableResourceVersion) throws IllegalArgumentException, AAIException { + + boolean result = verifyDeleteSemantics(v, resourceVersion, enableResourceVersion); + + if (result) { + + try { + deleteWithTraversal(v); + } catch (IllegalStateException e) { + throw new AAIException("AAI_6110", e); + } + + } + + } + + /** + * Verify delete semantics. + * + * @param vertex the vertex + * @param resourceVersion the resource version + * @return true, if successful + * @throws AAIException the AAI exception + */ + private boolean verifyDeleteSemantics(Vertex vertex, String resourceVersion, boolean enableResourceVersion) throws AAIException { + boolean result = true; + String nodeType = ""; + String errorDetail = " unknown delete semantic found"; + String aaiExceptionCode = ""; + nodeType = vertex.<String>property(AAIProperties.NODE_TYPE).orElse(null); + if (enableResourceVersion && !this.verifyResourceVersion("delete", nodeType, vertex.<String>property(AAIProperties.RESOURCE_VERSION).orElse(null), resourceVersion, nodeType)) { + } + List<Vertex> vertices = new ArrayList<Vertex>(); + vertices.add(vertex); + result = verifyPreventDeleteSemantics(vertices); + + return result; + } + + /** + * Verify Prevent delete semantics. + * + * @param vertices the list of vertices + * @return true, if successful + * @throws AAIException the AAI exception + */ + private boolean verifyPreventDeleteSemantics(List<Vertex> vertices) throws AAIException { + boolean result = true; + String nodeType = ""; + String errorDetail = " unknown delete semantic found"; + String aaiExceptionCode = ""; + + StopWatch.conditionalStart(); + /* + * This takes in all the vertices in a cascade-delete-chain and checks if there is any edge with a "prevent-delete" condition + * If yes - that should prevent the deletion of the vertex + * Dedup makes sure we dont capture the prevent-delete vertices twice + * The prevent-delete vertices are stored so that the error message displays what prevents the delete + */ + + List<Object> preventDeleteVertices = this.engine.asAdmin().getReadOnlyTraversalSource().V(vertices). + union(__.inE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.IN.toString()).outV().values(AAIProperties.NODE_TYPE), + __.outE().has(EdgeProperty.PREVENT_DELETE.toString(), AAIDirection.OUT.toString()).inV().values(AAIProperties.NODE_TYPE)) + .dedup().toList(); + + dbTimeMsecs += StopWatch.stopIfStarted(); + if (!preventDeleteVertices.isEmpty()) { + aaiExceptionCode = "AAI_6110"; + errorDetail = String.format("Object is being reference by additional objects preventing it from being deleted. Please clean up references from the following types %s", preventDeleteVertices); + result = false; + } + if (!result) { + throw new AAIException(aaiExceptionCode, errorDetail); + } + return result; + } + + /** + * Verify resource version. + * + * @param action the action + * @param nodeType the node type + * @param currentResourceVersion the current resource version + * @param resourceVersion the resource version + * @param uri the uri + * @return true, if successful + * @throws AAIException the AAI exception + */ + public boolean verifyResourceVersion(String action, String nodeType, String currentResourceVersion, String resourceVersion, String uri) throws AAIException { + String enabled = ""; + String errorDetail = ""; + String aaiExceptionCode = ""; + if (currentResourceVersion == null) { + currentResourceVersion = ""; + } + + if (resourceVersion == null) { + resourceVersion = ""; + } + 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("")) { + errorDetail = "resource-version passed for " + action + " of " + uri; + aaiExceptionCode = "AAI_6135"; + } else if (resourceVersion.equals("")) { + errorDetail = "resource-version not passed for " + action + " of " + uri; + aaiExceptionCode = "AAI_6130"; + } else { + errorDetail = "resource-version MISMATCH for " + action + " of " + uri; + aaiExceptionCode = "AAI_6131"; + } + + throw new AAIException(aaiExceptionCode, errorDetail); + + } + } + return true; + } + + /** + * Convert from camel case. + * + * @param name the name + * @return the string + */ + private String convertFromCamelCase (String name) { + String result = ""; + result = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name); + + NamingExceptions exceptions = NamingExceptions.getInstance(); + result = exceptions.getDBName(result); + + return result; + } + + private boolean canModify(Introspector obj, String propName, String requestContext) { + final String readOnly = obj.getPropertyMetadata(propName).get(PropertyMetadata.READ_ONLY); + if (readOnly != null) { + final String[] items = readOnly.split(","); + for (String item : items) { + if (requestContext.equals(item)) { + return false; + } + } + } + return true; + } + + private void executePreSideEffects(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataCopy.class).addSideEffect(PrivateEdge.class).build(); + + runner.execute(obj, self); + } + + private void executePostSideEffects(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataLinkWriter.class).build(); + + runner.execute(obj, self); + } + + private void enrichData(Introspector obj, Vertex self) throws AAIException { + + SideEffectRunner runner = new SideEffectRunner + .Builder(this.engine, this).addSideEffect(DataLinkReader.class).build(); + + runner.execute(obj, self); + } + + public double getDBTimeMsecs() { + return (dbTimeMsecs); + } + + /** + * Db to object With Filters + * This is for a one-time run with Tenant Isloation to only filter relationships + * TODO: Chnage the original dbToObject to take filter parent/cousins + * + * @param obj the obj + * @param v the vertex from the graph + * @param depth the depth + * @param nodeOnly specify if to exclude relationships or not + * @param filterCousinNodes + * @return the introspector + * @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 + */ + //TODO - See if you can merge the 2 dbToObjectWithFilters + public Introspector dbToObjectWithFilters(Introspector obj, Vertex v, Set<Vertex> seen, int depth, boolean nodeOnly, List<String> filterCousinNodes, List<String> filterParentNodes) throws AAIException, UnsupportedEncodingException { + String cleanUp = "false"; + if (depth < 0) { + return null; + } + depth--; + seen.add(v); + boolean modified = false; + for (String property : obj.getProperties(PropertyPredicates.isVisible())) { + List<Object> getList = null; + Vertex[] vertices = null; + + if (!(obj.isComplexType(property) || obj.isListType(property))) { + this.copySimpleProperty(property, obj, v); + modified = true; + } else { + if (obj.isComplexType(property)) { + /* container case */ + + if (!property.equals("relationship-list") && depth >= 0) { + Introspector argumentObject = obj.newIntrospectorInstanceOfProperty(property); + Object result = dbToObjectWithFilters(argumentObject, v, seen, depth + 1, nodeOnly, filterCousinNodes, filterParentNodes); + if (result != null) { + obj.setValue(property, argumentObject.getUnderlyingObject()); + modified = true; + } + } else if (property.equals("relationship-list") && !nodeOnly) { + /* relationships need to be handled correctly */ + Introspector relationshipList = obj.newIntrospectorInstanceOfProperty(property); + relationshipList = createFilteredRelationshipList(v, relationshipList, cleanUp, filterCousinNodes); + if (relationshipList != null) { + modified = true; + obj.setValue(property, relationshipList.getUnderlyingObject()); + modified = true; + } + + } + } else if (obj.isListType(property)) { + + if (property.equals("any")) { + continue; + } + String genericType = obj.getGenericTypeClass(property).getSimpleName(); + if (obj.isComplexGenericType(property) && depth >= 0) { + final String childDbName = convertFromCamelCase(genericType); + String vType = v.<String>property(AAIProperties.NODE_TYPE).orElse(null); + EdgeRule rule; + + boolean isthisParentRequired = filterParentNodes.parallelStream().anyMatch(childDbName::contains); + + EdgeRuleQuery q = new EdgeRuleQuery.Builder(vType, childDbName).edgeType(EdgeType.TREE).build(); + + try { + rule = edgeRules.getRule(q); + } catch (EdgeRuleNotFoundException e) { + throw new NoEdgeRuleFoundException(e); + } catch (AmbiguousRuleChoiceException e) { + throw new MultipleEdgeRuleFoundException(e); + } + if (!rule.getContains().equals(AAIDirection.NONE.toString()) && isthisParentRequired) { + //vertices = this.queryEngine.findRelatedVertices(v, Direction.OUT, rule.getLabel(), childDbName); + Direction ruleDirection = rule.getDirection(); + Iterator<Vertex> itr = v.vertices(ruleDirection, rule.getLabel()); + List<Vertex> verticesList = (List<Vertex>) IteratorUtils.toList(itr); + itr = verticesList.stream().filter(item -> { + return item.property(AAIProperties.NODE_TYPE).orElse("").equals(childDbName); + }).iterator(); + if (itr.hasNext()) { + getList = (List<Object>) obj.getValue(property); + } + int processed = 0; + int removed = 0; + while (itr.hasNext()) { + Vertex childVertex = itr.next(); + if (!seen.contains(childVertex)) { + Introspector argumentObject = obj.newIntrospectorInstanceOfNestedProperty(property); + + Object result = dbToObjectWithFilters(argumentObject, childVertex, seen, depth, nodeOnly, filterCousinNodes, filterParentNodes); + if (result != null) { + getList.add(argumentObject.getUnderlyingObject()); + } + + processed++; + } else { + removed++; + LOGGER.warn("Cycle found while serializing vertex id={}", childVertex.id().toString()); + } + } + if (processed == 0) { + //vertices were all seen, reset the list + getList = null; + } + if (processed > 0) { + modified = true; + } + } + } else if (obj.isSimpleGenericType(property)) { + List<Object> temp = this.engine.getListProperty(v, property); + if (temp != null) { + getList = (List<Object>) obj.getValue(property); + getList.addAll(temp); + modified = true; + } + + } + + } + + } + } + + //no changes were made to this obj, discard the instance + if (!modified) { + return null; + } + this.enrichData(obj, v); + return obj; + + } + + /** + * Creates the relationship list with the filtered node types. + * + * @param v the v + * @param obj the obj + * @param cleanUp the clean up + * @return the object + * @throws InstantiationException the instantiation exception + * @throws IllegalAccessException the illegal access exception + * @throws IllegalArgumentException the illegal argument exception + * @throws InvocationTargetException the invocation target exception + * @throws NoSuchMethodException the no such method exception + * @throws SecurityException the security exception + * @throws UnsupportedEncodingException the unsupported encoding exception + * @throws AAIException the AAI exception + * @throws MalformedURLException the malformed URL exception + * @throws URISyntaxException + */ + private Introspector createFilteredRelationshipList(Vertex v, Introspector obj, String cleanUp, List<String> filterNodes) throws UnsupportedEncodingException, AAIException { + List<Vertex> allCousins = this.engine.getQueryEngine().findCousinVertices(v); + + Iterator<Vertex> cousinVertices = allCousins.stream().filter(item -> { + String node = (String) item.property(AAIProperties.NODE_TYPE).orElse(""); + return filterNodes.parallelStream().anyMatch(node::contains); + }).iterator(); + + + List<Vertex> cousins = (List<Vertex>) IteratorUtils.toList(cousinVertices); + + //items.parallelStream().anyMatch(inputStr::contains) + List<Object> relationshipObjList = obj.getValue("relationship"); + for (Vertex cousin : cousins) { + + Introspector relationshipObj = obj.newIntrospectorInstanceOfNestedProperty("relationship"); + Object result = processEdgeRelationship(relationshipObj, cousin, cleanUp, null); + if (result != null) { + relationshipObjList.add(result); + } + + + } + + if (relationshipObjList.isEmpty()) { + return null; + } else { + return obj; + } + } } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java index 2158c894..d072db55 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngine.java @@ -30,6 +30,7 @@ import static org.onap.aai.edges.enums.EdgeProperty.DELETE_OTHER_V; import java.util.List; import java.util.Set; +import org.apache.tinkerpop.gremlin.process.traversal.Order; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; @@ -45,12 +46,12 @@ import org.onap.aai.logging.StopWatch; /* * This class needs some big explanation despite its compact size. - * This controls all the queries performed by the CRUD API in A&AI. + * This controls all the queries performed by the CRUD API in A&AI. * findParents, findChildren, and findDeletable require special attention * These methods use 'repeat'. You cannot use 'emit' with repeat currently * as it is extremely buggy as of tinkerpop-3.0.1-incubating. The way around * it (for now) is to sideEffect all the vertices we traverse into an ArrayList. - * + * */ public class GraphTraversalQueryEngine extends QueryEngine { @@ -70,6 +71,7 @@ public class GraphTraversalQueryEngine extends QueryEngine { public List<Vertex> findParents(Vertex start) { try { StopWatch.conditionalStart(); + @SuppressWarnings("unchecked") final GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).emit(v -> true).repeat(__.union(__.inE().has(CONTAINS.toString(), OUT.toString()).outV(), __.outE().has(CONTAINS.toString(), IN.toString()).inV())); return pipe.toList(); @@ -79,19 +81,36 @@ public class GraphTraversalQueryEngine extends QueryEngine { } } + /** + * {@inheritDoc} + */ + @Override + public List<Vertex> findParents(String[] uris) { + try { + StopWatch.conditionalStart(); + final GraphTraversal<Vertex, Vertex> pipe = this.g.V() + .has(AAIProperties.AAI_URI, P.within(uris)) + .order().by(AAIProperties.AAI_URI, Order.decr); + return pipe.toList(); + } + finally { + dbTimeMsecs += StopWatch.stopIfStarted(); + } + } + /** * {@inheritDoc} */ @Override public List<Vertex> findAllChildren(Vertex start) { - + @SuppressWarnings("unchecked") GraphTraversal<Vertex, Vertex> pipe = this.g .V(start).emit(v -> true).repeat(__.union(__.outE().has(CONTAINS.toString(), OUT.toString()).inV(), __.inE().has(CONTAINS.toString(), IN.toString()).outV())); - + return pipe.toList(); - + } /** @@ -104,10 +123,10 @@ public class GraphTraversalQueryEngine extends QueryEngine { __.outE().has(CONTAINS.toString(), OUT.toString()).inV(), __.inE().has(CONTAINS.toString(), IN.toString()).outV() ).has(AAIProperties.NODE_TYPE, type).dedup(); - + return pipe.toList(); } - + /** * {@inheritDoc} */ @@ -118,7 +137,7 @@ public class GraphTraversalQueryEngine extends QueryEngine { __.outE().has(CONTAINS.toString(), OUT.toString()), __.inE().has(CONTAINS.toString(), IN.toString()) ).otherV().dedup(); - + return pipe.toList(); } @@ -135,7 +154,7 @@ public class GraphTraversalQueryEngine extends QueryEngine { __.inE().has(DELETE_OTHER_V.toString(), IN.toString()).outV() ) ).dedup(); - + return pipe.toList(); } @@ -158,11 +177,11 @@ public class GraphTraversalQueryEngine extends QueryEngine { default: break; } - + pipe.has(AAIProperties.NODE_TYPE, nodeType).dedup(); return pipe.toList(); } - + @Override public Tree<Element> findSubGraph(Vertex start, int iterations, boolean nodeOnly) { final GraphTraversal<Vertex, ?> t = this.g.V(start).emit(v -> true).times(iterations).repeat( @@ -204,13 +223,13 @@ public class GraphTraversalQueryEngine extends QueryEngine { __.has("private", true) ) .dedup(); - + return pipeline.toList(); } @Override - public List<Vertex> findCousinVertices(Vertex start) { + public List<Vertex> findCousinVertices(Vertex start, String... labels) { // Start at the given vertex // Do a union to copy the start vertex to be run against all // so for the start vertex it gets all of in edges that contains other v set to none @@ -223,8 +242,8 @@ public class GraphTraversalQueryEngine extends QueryEngine { GraphTraversal<Vertex, Vertex> pipeline = this.g .V(start) .union( - __.inE().has(CONTAINS.toString(), NONE.toString()), - __.outE().has(CONTAINS.toString(), NONE.toString()) + __.inE(labels).has(CONTAINS.toString(), NONE.toString()), + __.outE(labels).has(CONTAINS.toString(), NONE.toString()) ) .not( __.has(PRIVATE.toString(), true) @@ -234,7 +253,7 @@ public class GraphTraversalQueryEngine extends QueryEngine { return pipeline.toList(); } - + public double getDBTimeMsecs() { return (dbTimeMsecs); } diff --git a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java index c2929468..110f8628 100644 --- a/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java +++ b/aai-core/src/main/java/org/onap/aai/serialization/engines/query/QueryEngine.java @@ -42,16 +42,33 @@ public abstract class QueryEngine { public QueryEngine (GraphTraversalSource g) { this.g = g; } - + /** * Finds all the parents/grandparents/etc of the given start node. * * @param start - the start vertex whose parent chain you want * @return the list of start and start's parent, grandparent, etc, in - * order (ie {start, parent, grandparent, etc} + * order (ie {start, parent, grandparent, etc} */ public abstract List<Vertex> findParents(Vertex start); - + + /** + * Finds all the parents/grandparents/etc of the given start node. + * + * This method should be used in place of the #findParents(Vertex) + * as since the aai-uri is added the lookup for finding the parents + * using the given list of aai-uri will be much faster than using + * a traversal to follow a start vertex and keep repeating since + * as the number of different type of edges keeps growing that call + * will be more expensive than using the aai-uri's as they are fast lookup + * + * @param uris - list of the uris representing the aai-uris of + * parent, grandparent, etc + * @return the list of start and start's parent, grandparent, etc, in + * order (ie {start, parent, grandparent, etc} + */ + public abstract List<Vertex> findParents(String [] uris); + /** * Finds all children, grandchildren, etc of start * @@ -59,7 +76,7 @@ public abstract class QueryEngine { * @return the list of child/grandchild/etc vertices */ public abstract List<Vertex> findAllChildren(Vertex start); - + /** * Finds all immediate children of start (no grandchildren or so forth) of the given type * @param start - the start vertex @@ -67,14 +84,14 @@ public abstract class QueryEngine { * @return the list of immediate child vertices of given type */ public abstract List<Vertex> findChildrenOfType(Vertex start, String type); - + /** * Finds all immediate children of start (no grandchildren or so forth) * @param start - the start vertex * @return the list of immediate child vertices */ public abstract List<Vertex> findChildren(Vertex start); - + /** * Find all vertices that should be deleted in a cascade from a delete of start * @@ -82,34 +99,34 @@ public abstract class QueryEngine { * @return the list of vertices to be deleted when start is deleted */ public abstract List<Vertex> findDeletable(Vertex start); - + /** * Finds the subgraph under start, including cousins as well as start's children/grandchildren/etc. * More specifically, this includes start, all its descendants, start's cousins, and start's * descendants' cousins (but not any of the cousins' cousins or descendants), and the edges * connecting them. - * + * * @param start - the start vertex - * @return - Tree containing nodes and edges of the subgraph + * @return - Tree containing nodes and edges of the subgraph */ public Tree<Element> findSubGraph(Vertex start) { return findSubGraph(start, AAIProperties.MAXIMUM_DEPTH, false); } - + /** * Finds the subgraph under start, including cousins as well as start's children/grandchildren/etc. * More specifically, this includes start, all its descendants, start's cousins, and start's * descendants' cousins (but not any of the cousins' cousins or descendants), and the edges * connecting them. - * + * * @param start - the start vertex - * @param iterations - depth of the subgraph, this limits how many generations of + * @param iterations - depth of the subgraph, this limits how many generations of * descendants are included * @param nodeOnly - if true the subgraph will NOT include the cousins - * @return Tree containing nodes and edges of the subgraph + * @return Tree containing nodes and edges of the subgraph */ public abstract Tree<Element> findSubGraph(Vertex start, int iterations, boolean nodeOnly); - + /** * Find vertices of type nodeType related to start by edges of the given * direction and label. @@ -125,23 +142,23 @@ public abstract class QueryEngine { /** * Finds cousin edges connecting start to other vertices only of types defined in an old version. * The idea is that if a user is using an old version, they won't understand any new node types in - * subsequent versions. Thus, revealing edges to new types will cause problems. This methods + * subsequent versions. Thus, revealing edges to new types will cause problems. This methods * filters any such edges out. - * + * * @param start - the start vertex * @param loader - loader for retrieving the list of allowed node types for the desired version * (version is set when the loader was instantiated) * @return list of cousin edges between start and any node types understood by the version specified in loader */ public abstract List<Edge> findEdgesForVersion(Vertex start, Loader loader); - + /** - * Finds all cousins of start. - * + * Finds all cousins of start. + * * @param start - the start vertex * @return list of start's cousin vertices */ - public abstract List<Vertex> findCousinVertices(Vertex start); + public abstract List<Vertex> findCousinVertices(Vertex start, String... labels); public abstract double getDBTimeMsecs(); diff --git a/aai-core/src/test/java/org/onap/aai/HttpTestUtil.java b/aai-core/src/test/java/org/onap/aai/HttpTestUtil.java index 82a3efdf..92cb59c5 100644 --- a/aai-core/src/test/java/org/onap/aai/HttpTestUtil.java +++ b/aai-core/src/test/java/org/onap/aai/HttpTestUtil.java @@ -52,11 +52,11 @@ import static org.mockito.Mockito.when; public class HttpTestUtil extends RESTAPI { - - protected HttpEntry traversalHttpEntry; - - protected HttpEntry traversalUriHttpEntry; - + + protected HttpEntry traversalHttpEntry; + + protected HttpEntry traversalUriHttpEntry; + private static final EELFLogger logger = EELFManager.getInstance().getLogger(HttpTestUtil.class); protected static final MediaType APPLICATION_JSON = MediaType.valueOf("application/json"); @@ -77,7 +77,7 @@ public class HttpTestUtil extends RESTAPI { this.queryStyle = qs; traversalHttpEntry = SpringContextAware.getBean("traversalUriHttpEntry", HttpEntry.class); traversalUriHttpEntry = SpringContextAware.getBean("traversalUriHttpEntry", HttpEntry.class); - + } public void init(){ @@ -121,7 +121,10 @@ public class HttpTestUtil extends RESTAPI { try { - uri = uri.replaceAll("/aai/", ""); + if(uri.startsWith("/aai/")){ + uri = uri.substring(5); + } + logger.info("Starting the put request for the uri {} with payload {}", uri, payload); String [] arr = uri.split("/"); @@ -142,7 +145,7 @@ public class HttpTestUtil extends RESTAPI { Mockito.when(uriInfo.getPath()).thenReturn(uri); DBConnectionType type = DBConnectionType.REALTIME; - + traversalHttpEntry.setHttpEntryProperties(version, type); Loader loader = traversalHttpEntry.getLoader(); dbEngine = traversalHttpEntry.getDbEngine(); @@ -206,7 +209,7 @@ public class HttpTestUtil extends RESTAPI { return response; } - public Response doGet(String uri) throws UnsupportedEncodingException, AAIException { + public Response doGet(String uri, String depth){ this.init(); Response response = null; @@ -215,8 +218,11 @@ public class HttpTestUtil extends RESTAPI { try { - uri = uri.replaceAll("/aai/", ""); - logger.info("Starting the GET request for the uri {} with depth {}", uri, "all"); + if(uri.startsWith("/aai/")){ + uri = uri.substring(5); + } + + logger.info("Starting the GET request for the uri {} with depth {}", uri, depth); String [] arr = uri.split("/"); @@ -240,21 +246,29 @@ public class HttpTestUtil extends RESTAPI { dbEngine = traversalHttpEntry.getDbEngine(); URI uriObject = UriBuilder.fromPath(uri).build(); - URIToObject uriToObject = new URIToObject(loader, uriObject); - String objType = uriToObject.getEntityName(); - queryParameters.add("depth", "all"); + if(depth != null){ + queryParameters.add("depth", depth); + } + QueryParser uriQuery = dbEngine.getQueryBuilder().createQueryFromURI(uriObject, queryParameters); Mockito.when(uriInfo.getPath()).thenReturn(uri); + URIToObject uriToObject = new URIToObject(loader, uriObject); + String objType = ""; + if (!uriQuery.getContainerType().equals("")) { + objType = uriQuery.getContainerType(); + } else { + objType = uriQuery.getResultType(); + } logger.info("Unmarshalling the payload to this {}", objType); Introspector obj = loader.introspectorFromName(objType); DBRequest dbRequest = - new DBRequest.Builder(HttpMethod.GET, uriObject, uriQuery, obj, httpHeaders, uriInfo, "JUNIT-TRANSACTION") - .build(); + new DBRequest.Builder(HttpMethod.GET, uriObject, uriQuery, obj, httpHeaders, uriInfo, "JUNIT-TRANSACTION") + .build(); List<DBRequest> dbRequestList = new ArrayList<>(); dbRequestList.add(dbRequest); @@ -288,6 +302,10 @@ public class HttpTestUtil extends RESTAPI { return response; } + public Response doGet(String uri) throws UnsupportedEncodingException, AAIException { + return this.doGet(uri, "all"); + } + public Response doDelete(String uri, String resourceVersion) throws UnsupportedEncodingException, AAIException { this.init(); @@ -321,7 +339,7 @@ public class HttpTestUtil extends RESTAPI { Mockito.when(uriInfo.getPath()).thenReturn(uri); DBConnectionType type = DBConnectionType.REALTIME; traversalHttpEntry.setHttpEntryProperties(version, type); - + traversalHttpEntry.setHttpEntryProperties(version, type); Loader loader = traversalHttpEntry.getLoader(); dbEngine = traversalHttpEntry.getDbEngine(); diff --git a/aai-core/src/test/java/org/onap/aai/parsers/uri/URIToObjectTest.java b/aai-core/src/test/java/org/onap/aai/parsers/uri/URIToObjectTest.java index c7c1f4ca..fc2c64a1 100644 --- a/aai-core/src/test/java/org/onap/aai/parsers/uri/URIToObjectTest.java +++ b/aai-core/src/test/java/org/onap/aai/parsers/uri/URIToObjectTest.java @@ -19,6 +19,7 @@ */ package org.onap.aai.parsers.uri; +import org.onap.aai.schema.enums.ObjectMetadata; import org.onap.aai.setup.SchemaVersion; import org.junit.Ignore; @@ -65,7 +66,7 @@ public class URIToObjectTest extends AAISetup { currentVersion = schemaVersions.getDefaultVersion(); loader = loaderFactory.createLoaderForVersion(ModelType.MOXY, schemaVersions.getRelatedLinkVersion()); } - + @Test public void uri() throws JAXBException, AAIException, IllegalArgumentException, UnsupportedEncodingException { URI uri = UriBuilder.fromPath("/aai/" + loader.getVersion() + "/cloud-infrastructure/cloud-regions/cloud-region/mycloudowner/mycloudregionid/tenants/tenant/key1/vservers/vserver/key2/l-interfaces/l-interface/key3").build(); @@ -74,11 +75,11 @@ public class URIToObjectTest extends AAISetup { String expected = "{\"cloud-owner\":\"mycloudowner\",\"cloud-region-id\":\"mycloudregionid\",\"tenants\":{\"tenant\":[{\"tenant-id\":\"key1\",\"vservers\":{\"vserver\":[{\"vserver-id\":\"key2\",\"l-interfaces\":{\"l-interface\":[{\"interface-name\":\"key3\"}]}}]}}]}}"; String topEntity = "cloud-region"; String entity = "l-interface"; - + testSet(result.marshal(false), parse, expected, topEntity, entity, version); } - + /** * Uri no version. * @@ -86,7 +87,7 @@ public class URIToObjectTest extends AAISetup { * @throws AAIException the AAI exception * @throws IllegalArgumentException the illegal argument exception * @throws UnsupportedEncodingException the unsupported encoding exception - * @throws AAIUnknownObjectException + * @throws AAIUnknownObjectException */ @Test public void uriNoVersion() throws JAXBException, AAIException, IllegalArgumentException, UnsupportedEncodingException, AAIUnknownObjectException { @@ -106,12 +107,12 @@ public class URIToObjectTest extends AAISetup { String expected = "{\"cloud-owner\":\"mycloudowner\",\"cloud-region-id\":\"mycloudregionid\",\"tenants\":{\"tenant\":[{\"tenant-id\":\"key1\",\"tenant-name\":\"name1\",\"vservers\":{\"vserver\":[{\"vserver-id\":\"key2\",\"vserver-name\":\"name2\",\"l-interfaces\":{\"l-interface\":[{\"interface-name\":\"key3\"}]}}]}}]}}"; String topEntity = "cloud-region"; String entity = "l-interface"; - + testSet(result.marshal(false), parse, expected, topEntity, entity, version); - + } - + /** * Bad URI. @@ -124,13 +125,13 @@ public class URIToObjectTest extends AAISetup { @Test public void badURI() throws JAXBException, AAIException, IllegalArgumentException, UnsupportedEncodingException { URI uri = UriBuilder.fromPath("/aai/" + loader.getVersion() + "/cloud-infrastructure/cloud-regions/cloud-region/mycloudowner/mycloudregionid/tenants/tenant/key1/vservers/vserver/key2/l-interadsfaces/l-interface/key3").build(); - + thrown.expect(AAIException.class); thrown.expect(hasProperty("code", is("AAI_3000"))); - + new URIToObject(loader, uri); } - + /** * Starts with valid namespace. * @@ -147,10 +148,10 @@ public class URIToObjectTest extends AAISetup { String expected = "{\"cloud-owner\":\"mycloudowner\",\"cloud-region-id\":\"mycloudregionid\",\"tenants\":{\"tenant\":[{\"tenant-id\":\"key1\",\"vservers\":{\"vserver\":[{\"vserver-id\":\"key2\",\"l-interfaces\":{\"l-interface\":[{\"interface-name\":\"key3\"}]}}]}}]}}"; String topEntity = "cloud-region"; String entity = "l-interface"; - + testSet(result.marshal(false), parse, expected, topEntity, entity, version); } - + /** * Single top level. * @@ -165,14 +166,14 @@ public class URIToObjectTest extends AAISetup { URIToObject parse = new URIToObject(loader, uri); Introspector result = parse.getTopEntity(); String expected = "{\"vnf-id\":\"key1\"}"; - + String topEntity = "generic-vnf"; String entity = "generic-vnf"; - + testSet(result.marshal(false), parse, expected, topEntity, entity, version); } - + /** * Naming exceptions. * @@ -190,11 +191,11 @@ public class URIToObjectTest extends AAISetup { String expected = "{\"vnf-id\":\"key1\",\"port-groups\":{\"port-group\":[{\"interface-id\":\"key2\",\"cvlan-tags\":{\"cvlan-tag-entry\":[{\"cvlan-tag\":655}]}}]}}"; String topEntity = "vce"; String entity = "cvlan-tag"; - + testSet(result.marshal(false), parse, expected, topEntity, entity, version); } - + /** * No list object. * @@ -212,23 +213,23 @@ public class URIToObjectTest extends AAISetup { String entity = "l3-interface-ipv4-address-list"; String expected = "{\"equipment-name\":\"0e6189fd-9257-49b9-a3be-d7ba980ccfc9\",\"lag-interfaces\":{\"lag-interface\":[{\"interface-name\":\"8ae5aa76-d597-4382-b219-04f266fe5e37\",\"l-interfaces\":{\"l-interface\":[{\"interface-name\":\"9e141d03-467b-437f-b4eb-b3133ec1e205\",\"l3-interface-ipv4-address-list\":[{\"l3-interface-ipv4-address\":\"8f19f0ea-a81f-488e-8d5c-9b7b53696c11\"}]}]}}]}}"; testSet(result.marshal(false), parse, expected, topEntity, entity, version); - + } - + @Test public void relativePath() throws JAXBException, AAIException, IllegalArgumentException, UnsupportedEncodingException { URI uri = UriBuilder.fromPath("./l-interfaces/l-interface/key1").build(); URIToObject parse = new URIToObject(loader, uri); Introspector result = parse.getEntity(); String expected = "{\"interface-name\":\"key1\"}"; - + String topEntity = "l-interface"; String entity = "l-interface"; - + testSet(result.marshal(false), parse, expected, topEntity, entity, version); } - + /** * Test set. * @@ -241,15 +242,15 @@ public class URIToObjectTest extends AAISetup { */ public void testSet(String json, URIToObject parse, String expected, String topEntity, String entity, SchemaVersion version) { assertEquals("blah", expected, json); - + assertEquals("top entity", topEntity, parse.getTopEntityName()); assertEquals("entity", entity, parse.getEntityName()); assertEquals("entity object", entity, parse.getEntity().getDbName()); - + assertEquals("parent list object", 1, parse.getParentList().size()); - + assertEquals("object version", version, parse.getObjectVersion()); } } diff --git a/aai-core/src/test/java/org/onap/aai/rest/EdgeNotValidAnymoreTest.java b/aai-core/src/test/java/org/onap/aai/rest/EdgeNotValidAnymoreTest.java new file mode 100644 index 00000000..1a69fc58 --- /dev/null +++ b/aai-core/src/test/java/org/onap/aai/rest/EdgeNotValidAnymoreTest.java @@ -0,0 +1,120 @@ +/** + * ============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.rest; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.JanusGraphTransaction; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onap.aai.AAISetup; +import org.onap.aai.HttpTestUtil; +import org.onap.aai.PayloadUtil; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.dbmap.AAIGraph; +import org.onap.aai.edges.enums.EdgeField; +import org.onap.aai.edges.enums.EdgeProperty; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.serialization.engines.QueryStyle; + +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.UUID; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +public class EdgeNotValidAnymoreTest extends AAISetup { + + private HttpTestUtil testUtil; + + @Before + public void setupData() throws IOException, AAIException { + + String cloudRegionEndpoint = "/aai/v13/cloud-infrastructure/cloud-regions/cloud-region/junit-cloud-owner-with-vlan/junit-cloud-region-with-vlan"; + + String cloudRegionBody = PayloadUtil.getResourcePayload("cloud-region-with-vlan.json"); + testUtil = new HttpTestUtil(QueryStyle.TRAVERSAL_URI); + testUtil.doPut(cloudRegionEndpoint, cloudRegionBody); + + JanusGraphTransaction transaction = AAIGraph.getInstance().getGraph().newTransaction(); + GraphTraversalSource g = transaction.traversal(); + + Vertex configurationVertex = g.addV() + .property( AAIProperties.NODE_TYPE, "configuration") + .property( "configuration-id", "ci1") + .property( "configuration-type", "ci1") + .property( AAIProperties.AAI_URI, "/network/configurations/configuration/ci1") + .property(AAIProperties.SOURCE_OF_TRUTH, "JUNIT") + .next(); + + Vertex vlanVertex = g.V() + .has("vlan-interface", "test-vlan-interface-1") + .has(AAIProperties.NODE_TYPE, "vlan") + .next(); + + Edge edge = configurationVertex.addEdge("org.onap.relationships.inventory.PartOf", vlanVertex); + addEdge(edge); + + transaction.commit(); + } + + public void addEdge(Edge edge) { + edge.property(EdgeProperty.CONTAINS.toString(), "NONE"); + edge.property(EdgeProperty.DELETE_OTHER_V.toString(), "NONE"); + edge.property(EdgeProperty.PREVENT_DELETE.toString(), "NONE"); + edge.property(EdgeField.PRIVATE.toString(), false); + edge.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString()); + } + + @Test + public void testWhenEdgeRuleIsNoLongerValidEnsureItRetrievesVertexesWithoutOldEdges() { + + String endpoint = "/aai/v14/network/configurations"; + + Response response = testUtil.doGet(endpoint, null); + assertThat(response.getStatus(), is(200)); + + String body = response.getEntity().toString(); + + assertThat(body, containsString("configuration-id")); + assertThat(body, not(containsString("vlan"))); + } + + @After + public void teardown(){ + + JanusGraph janusGraph = AAIGraph.getInstance().getGraph(); + JanusGraphTransaction transaction = janusGraph.newTransaction(); + GraphTraversalSource g = transaction.traversal(); + + g.V() + .has(AAIProperties.SOURCE_OF_TRUTH, "JUNIT") + .toList() + .forEach((edge) -> edge.remove()); + + transaction.commit(); + } +} diff --git a/aai-core/src/test/java/org/onap/aai/rest/EntitlementTest.java b/aai-core/src/test/java/org/onap/aai/rest/EntitlementTest.java new file mode 100644 index 00000000..3559b9d6 --- /dev/null +++ b/aai-core/src/test/java/org/onap/aai/rest/EntitlementTest.java @@ -0,0 +1,96 @@ +/** + * ============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.rest; + +import com.jayway.jsonpath.JsonPath; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.onap.aai.AAISetup; +import org.onap.aai.HttpTestUtil; +import org.onap.aai.PayloadUtil; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.serialization.engines.QueryStyle; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.test.annotation.DirtiesContext; + +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; + +@RunWith(value = Parameterized.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) +public class EntitlementTest extends AAISetup { + + private HttpTestUtil httpTestUtil; + + @Parameterized.Parameter(value = 0) + public QueryStyle queryStyle; + + private String vnfPayload; + + private String vnfUri; + + @Parameterized.Parameters(name = "QueryStyle.{0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][]{ + {QueryStyle.TRAVERSAL_URI} + }); + } + + @Before + public void setUp() throws IOException { + httpTestUtil = new HttpTestUtil(queryStyle); + vnfPayload = PayloadUtil.getResourcePayload("vnf.json"); + vnfUri = "/aai/v14/network/generic-vnfs/generic-vnf/vnf1"; + } + + @Test + public void testPutGenericVnfAndThenInsertEntitlement() throws IOException, AAIException { + String entitlementPayload = PayloadUtil.getResourcePayload("entitlement.json"); + String entitlementUri = "/aai/v14/network/generic-vnfs/generic-vnf/vnf1/entitlements/entitlement/g1/r1"; + Response response = httpTestUtil.doPut(vnfUri, vnfPayload); + assertEquals("Expected the Generic Vnf to be created", 201, response.getStatus()); + + response = httpTestUtil.doGet(vnfUri); + assertEquals("Expected the Generic Vnf to be found", 200, response.getStatus()); + String jsonResponse = response.getEntity().toString(); + JSONAssert.assertEquals(vnfPayload, jsonResponse, false); + + response = httpTestUtil.doPut(entitlementUri, entitlementPayload); + assertEquals("Expected the Entitlement to be created", 201, response.getStatus()); + } + + @After + public void tearDown() throws UnsupportedEncodingException, AAIException { + Response response = httpTestUtil.doGet(vnfUri); + assertEquals("Expected the Generic Vnf to be found", 200, response.getStatus()); + String jsonResponse = response.getEntity().toString(); + String resourceVersion = JsonPath.read(jsonResponse, "$.resource-version"); + response = httpTestUtil.doDelete(vnfUri, resourceVersion); + assertEquals("Expected the cloud region to be deleted", 204, response.getStatus()); + } +} diff --git a/aai-core/src/test/java/org/onap/aai/rest/ModelElementTest.java b/aai-core/src/test/java/org/onap/aai/rest/ModelElementTest.java new file mode 100644 index 00000000..d808c993 --- /dev/null +++ b/aai-core/src/test/java/org/onap/aai/rest/ModelElementTest.java @@ -0,0 +1,98 @@ +/** + * ============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.rest; + +import com.jayway.jsonpath.JsonPath; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.onap.aai.AAISetup; +import org.onap.aai.HttpTestUtil; +import org.onap.aai.PayloadUtil; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.serialization.engines.QueryStyle; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.test.annotation.DirtiesContext; + +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; + +@RunWith(value = Parameterized.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) +public class ModelElementTest extends AAISetup { + + private HttpTestUtil httpTestUtil; + + @Parameterized.Parameter(value = 0) + public QueryStyle queryStyle; + + private String modelPayload; + private String modelElementPayload; + + private String modelUri; + private String modelElementUri; + + @Parameterized.Parameters(name = "QueryStyle.{0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][]{ + {QueryStyle.TRAVERSAL_URI} + }); + } + + @Before + public void setUp() throws IOException { + httpTestUtil = new HttpTestUtil(queryStyle); + modelPayload = PayloadUtil.getResourcePayload("model.json"); + modelElementPayload = PayloadUtil.getResourcePayload("model-element.json"); + modelUri = "/aai/v14/service-design-and-creation/models/model/24c04fc5-f3f8-43ec-8792-c6f940638676-test1"; + modelElementUri = "/aai/v14/service-design-and-creation/models/model/24c04fc5-f3f8-43ec-8792-c6f940638676-test1/model-vers/model-ver/0c4c59f0-9864-43ca-b0c2-ca38746b72a5-test1/model-elements/model-element/0dc2b8b6-af8d-4213-970b-7861a603fc86-test1/model-constraints/model-constraint/782ba24a-28ab-4fd0-8e69-da10cc5373f3-test1/constrained-element-sets/constrained-element-set/a33e65c3-1198-4d4c-9799-2b521e4c4212-test1/element-choice-sets/element-choice-set/7df27a94-06c8-46ef-9fc2-5900d8cffbb0-test1/model-elements/model-element/acf8b6cf-e051-4c1b-bcad-b24792f826cf-test1"; + } + + @Test + public void testPutModelAndThenModelElementAndItShouldSucceed() throws IOException, AAIException { + Response response = httpTestUtil.doPut(modelUri, modelPayload); + assertEquals("Expected the cloud region to be created", 201, response.getStatus()); + + response = httpTestUtil.doGet(modelUri); + assertEquals("Expected the cloud region to be found", 200, response.getStatus()); + String jsonResponse = response.getEntity().toString(); + JSONAssert.assertEquals(modelPayload, jsonResponse, false); + + response = httpTestUtil.doPut(modelElementUri, modelElementPayload); + assertEquals("Expected the cloud region to be created", 201, response.getStatus()); + } + + @After + public void tearDown() throws UnsupportedEncodingException, AAIException { + Response response = httpTestUtil.doGet(modelUri); + assertEquals("Expected the cloud region to be found", 200, response.getStatus()); + String jsonResponse = response.getEntity().toString(); + String resourceVersion = JsonPath.read(jsonResponse, "$.resource-version"); + response = httpTestUtil.doDelete(modelUri, resourceVersion); + assertEquals("Expected the cloud region to be deleted", 204, response.getStatus()); + } +} diff --git a/aai-core/src/test/java/org/onap/aai/rest/PserverDuplicateTest.java b/aai-core/src/test/java/org/onap/aai/rest/PserverDuplicateTest.java new file mode 100644 index 00000000..71e53be8 --- /dev/null +++ b/aai-core/src/test/java/org/onap/aai/rest/PserverDuplicateTest.java @@ -0,0 +1,135 @@ +/** + * ============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.rest; + +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.core.JanusGraphTransaction; +import org.junit.Test; +import org.onap.aai.AAISetup; +import org.onap.aai.HttpTestUtil; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.dbmap.AAIGraph; +import org.onap.aai.serialization.engines.QueryStyle; + +import javax.ws.rs.core.Response; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +public class PserverDuplicateTest extends AAISetup { + + private static final EELFLogger LOGGER = EELFManager.getInstance().getLogger(PserverDuplicateTest.class); + + private HttpTestUtil testUtil; + + private String hostname; + + public boolean createDuplicate() throws InterruptedException { + + hostname = getHostname(); + final String aaiUri = "/cloud-infrastructure/pservers/pserver/" + hostname; + final int threads = getNumberOfThreads(); + + ExecutorService service = Executors.newFixedThreadPool(threads); + + JanusGraph janusGraph = AAIGraph.getInstance().getGraph(); + // Due to the lazy instantiation of the graph, it needs to create a new transaction to create schema + janusGraph.newTransaction().rollback(); + + service.invokeAll( + IntStream.range(0, threads) + .mapToObj((i) -> (Callable<Void>) () -> { + JanusGraphTransaction transaction = janusGraph.newTransaction(); + GraphTraversalSource g = transaction.traversal(); + g.addV() + .property(AAIProperties.AAI_URI, aaiUri) + .property(AAIProperties.NODE_TYPE, "pserver") + .property("hostname", hostname) + .next(); + transaction.commit(); + return null; + }).collect(Collectors.toList()) + , 7, TimeUnit.SECONDS + ); + + JanusGraphTransaction readOnlyTransaction = AAIGraph.getInstance().getGraph().buildTransaction().readOnly().start(); + GraphTraversalSource g = readOnlyTransaction.traversal(); + + List<Vertex> pserverList = g.V().has(AAIProperties.AAI_URI, aaiUri).toList(); + LOGGER.debug("Number of pservers with uri {} is {}", aaiUri, pserverList.size()); + + testUtil = new HttpTestUtil(QueryStyle.TRAVERSAL_URI); + + if(pserverList.size() == 1){ + return false; + } + return true; + } + + + @Test + public void testWhenDuplicatesExistInGraphThatGetAllSuceeds() throws InterruptedException { + + int totalRetries = getNumOfRetries(); + for(int retry = 0; retry < totalRetries; retry++){ + if(!this.createDuplicate()){ + if(retry == (totalRetries-1)){ + fail("Unable to produce duplicates in the graph, " + + "please increase retry or ignore test if it becomes impossible to create duplicate this test"); + } + } else { + // Successfully created a duplicate in the janus graph + break; + } + } + + String endpoint = "/aai/v14/cloud-infrastructure/pservers"; + + Response response = testUtil.doGet(endpoint, null); + LOGGER.info("GET ALL Pservers with duplicates status code {} and body {}", response.getStatus(), response.getEntity()); + assertThat(response.getStatus(), is(200)); + } + + public String getHostname(){ + return UUID.randomUUID().toString(); + } + + public int getNumOfRetries(){ + return 10; + } + + public int getNumberOfThreads(){ + return 10; + } +} + diff --git a/aai-core/src/test/java/org/onap/aai/rest/VipAddressListTest.java b/aai-core/src/test/java/org/onap/aai/rest/VipAddressListTest.java new file mode 100644 index 00000000..0351c2c3 --- /dev/null +++ b/aai-core/src/test/java/org/onap/aai/rest/VipAddressListTest.java @@ -0,0 +1,99 @@ +/** + * ============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.rest; + +import com.jayway.jsonpath.JsonPath; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.onap.aai.AAISetup; +import org.onap.aai.HttpTestUtil; +import org.onap.aai.PayloadUtil; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.serialization.engines.QueryStyle; +import org.skyscreamer.jsonassert.JSONAssert; + +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; + +@RunWith(value = Parameterized.class) +public class VipAddressListTest extends AAISetup { + + private HttpTestUtil httpTestUtil; + + @Parameterized.Parameter(value = 0) + public QueryStyle queryStyle; + + @Parameterized.Parameters(name = "QueryStyle.{0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][]{ + {QueryStyle.TRAVERSAL_URI} + }); + } + + @Before + public void setUp(){ + httpTestUtil = new HttpTestUtil(queryStyle); + } + + @Test + public void testPutWithAllCloudRegionChildrenNodesAndCheckIfDeleteIsSuccessful() throws IOException, AAIException { + + String cloudRegionPayload = PayloadUtil.getResourcePayload("cloud-region.json"); + String cloudRegionUri = "/aai/v14/cloud-infrastructure/cloud-regions/cloud-region/cloud-region-owner-with-vip-ipv4/cloud-region-id-with-vip-ipv4"; + + Response response = httpTestUtil.doPut(cloudRegionUri, cloudRegionPayload); + assertEquals("Expected the cloud region to be created", 201, response.getStatus()); + + response = httpTestUtil.doGet(cloudRegionUri); + assertEquals("Expected the cloud region to be found", 200, response.getStatus()); + String jsonResponse = response.getEntity().toString(); + + JSONAssert.assertEquals(cloudRegionPayload, jsonResponse, false); + + String vipIpv4Uri = cloudRegionUri + "/vip-ipv4-address-list/vip-ipv4-address-list-1"; + String vipIpv4Payload = PayloadUtil.getResourcePayload("vip-ipv4-address-list.json"); + + response = httpTestUtil.doPut(vipIpv4Uri, vipIpv4Payload); + assertEquals("Expected the ipv4 address list to be created", 201, response.getStatus()); + + response = httpTestUtil.doGet(vipIpv4Uri); + assertEquals("Expected the ipv4 address list to be found", 200, response.getStatus()); + + jsonResponse = response.getEntity().toString(); + String resourceVersion = JsonPath.read(jsonResponse, "$.resource-version"); + + response = httpTestUtil.doDelete(vipIpv4Uri, resourceVersion); + assertEquals("Expected the ipv4 address list to be deleted", 204, response.getStatus()); + + response = httpTestUtil.doGet(cloudRegionUri); + jsonResponse = response.getEntity().toString(); + resourceVersion = JsonPath.read(jsonResponse, "$.resource-version"); + + response = httpTestUtil.doDelete(cloudRegionUri, resourceVersion); + assertEquals("Expected the cloud region to be deleted", 204, response.getStatus()); + } +} diff --git a/aai-core/src/test/java/org/onap/aai/serialization/db/DbSerializerTest.java b/aai-core/src/test/java/org/onap/aai/serialization/db/DbSerializerTest.java index a5b968b3..5041c7d1 100644 --- a/aai-core/src/test/java/org/onap/aai/serialization/db/DbSerializerTest.java +++ b/aai-core/src/test/java/org/onap/aai/serialization/db/DbSerializerTest.java @@ -465,16 +465,18 @@ public class DbSerializerTest extends AAISetup { public void getURIForVertexTest() throws AAIException, URISyntaxException, UnsupportedEncodingException { engine.startTransaction(); - Vertex cr = engine.tx().addVertex("aai-node-type", "cloud-region", "cloud-owner", "me", "cloud-region-id", "123"); + Vertex cr = engine.tx().addVertex("aai-node-type", "cloud-region", "cloud-owner", "me", "cloud-region-id", "123", "aai-uri", "/cloud-infrastructure/cloud-regions/cloud-region/me/123"); Vertex ten = engine.tx().addVertex("aai-node-type", "tenant", "tenant-id", "453"); edgeSer.addTreeEdge(engine.tx().traversal(), cr, ten); + ten.property("aai-uri", "/cloud-infrastructure/cloud-regions/cloud-region/me/123/tenants/tenant/453"); + URI compare = new URI("/cloud-infrastructure/cloud-regions/cloud-region/me/123/tenants/tenant/453"); assertEquals(compare, dbser.getURIForVertex(ten)); - cr.property("aai-node-type").remove(); URI compareFailure = new URI("/unknown-uri"); + ten.property("aai-uri").remove(); assertEquals(compareFailure, dbser.getURIForVertex(ten)); } @@ -619,9 +621,10 @@ public class DbSerializerTest extends AAISetup { public void serializeSingleVertexChildTest() throws AAIException, UnsupportedEncodingException { engine.startTransaction(); - Vertex cr = engine.tx().addVertex("aai-node-type", "cloud-region", "cloud-owner", "me", "cloud-region-id", "123"); + Vertex cr = engine.tx().addVertex("aai-node-type", "cloud-region", "cloud-owner", "me", "cloud-region-id", "123", "aai-uri", "/cloud-infrastructure/cloud-regions/cloud-region/me/123"); Introspector tenIn = loader.introspectorFromName("tenant"); Vertex ten = dbser.createNewVertex(tenIn); + ten.property("aai-uri", cr.property("aai-uri").value().toString() + "/tenants/tenant/453"); edgeSer.addTreeEdge(engine.tx().traversal(), cr, ten); @@ -639,8 +642,8 @@ public class DbSerializerTest extends AAISetup { public void getVertexPropertiesRelationshipHasLabelTest() throws AAIException, UnsupportedEncodingException { engine.startTransaction(); - Vertex gvnf = engine.tx().addVertex("aai-node-type","generic-vnf","vnf-id","vnf-123"); - Vertex vnfc = engine.tx().addVertex("aai-node-type","vnfc","vnfc-name","vnfc-123"); + Vertex gvnf = engine.tx().addVertex("aai-node-type","generic-vnf","vnf-id","vnf-123","aai-uri", "/network/generic-vnfs/generic-vnf/vnf-123"); + Vertex vnfc = engine.tx().addVertex("aai-node-type","vnfc","vnfc-name","vnfc-123","aai-uri", "/network/vnfcs/vnfc/vnfc-123"); edgeSer.addEdge(engine.tx().traversal(), gvnf, vnfc); @@ -672,8 +675,8 @@ public class DbSerializerTest extends AAISetup { engine.startTransaction(); - Vertex gvnf = engine.tx().addVertex("aai-node-type","generic-vnf","vnf-id","vnf-123"); - Vertex vnfc = engine.tx().addVertex("aai-node-type","vnfc","vnfc-name","vnfc-123"); + Vertex gvnf = engine.tx().addVertex("aai-node-type","generic-vnf","vnf-id","vnf-123", "aai-uri", "/network/generic-vnfs/generic-vnf/vnf-123"); + Vertex vnfc = engine.tx().addVertex("aai-node-type","vnfc","vnfc-name","vnfc-123", "aai-uri", "/network/vnfcs/vnfc/vnfc-123"); edgeSer.addEdge(engine.tx().traversal(), gvnf, vnfc); diff --git a/aai-core/src/test/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngineTest.java b/aai-core/src/test/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngineTest.java index a24855b8..b9431217 100644 --- a/aai-core/src/test/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngineTest.java +++ b/aai-core/src/test/java/org/onap/aai/serialization/engines/query/GraphTraversalQueryEngineTest.java @@ -38,6 +38,7 @@ import org.springframework.test.annotation.DirtiesContext; import java.io.InputStream; import java.util.*; +import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -53,30 +54,30 @@ public class GraphTraversalQueryEngineTest extends AAISetup { public void testFindParents() throws AAIException { //setup Graph graph = TinkerGraph.open(); - + Vertex cloudreg = graph.addVertex(T.id, "00", "aai-node-type", "cloud-region"); Vertex tenant = graph.addVertex(T.id, "10", "aai-node-type", "tenant"); Vertex vserver = graph.addVertex(T.id, "20", "aai-node-type", "vserver"); - + GraphTraversalSource g = graph.traversal(); - + edgeSer.addTreeEdge(g, cloudreg, tenant); edgeSer.addTreeEdge(g, tenant, vserver); - + //expect start vertex back plus any parents List<Vertex> crExpected = new ArrayList<>(Arrays.asList(cloudreg)); //no parents List<Vertex> tenExpected = new ArrayList<>(Arrays.asList(tenant, cloudreg)); //only has the immediate parent List<Vertex> vsExpected = new ArrayList<>(Arrays.asList(vserver, tenant, cloudreg)); //parent & grandparent - + GraphTraversalQueryEngine engine = new GraphTraversalQueryEngine(g); - + //test List<Vertex> crRes = engine.findParents(cloudreg); assertTrue(crRes.containsAll(crExpected) && crExpected.containsAll(crRes)); - + List<Vertex> tenRes = engine.findParents(tenant); assertTrue(tenRes.containsAll(tenExpected) && tenExpected.containsAll(tenRes)); - + List<Vertex> vsRes = engine.findParents(vserver); assertTrue(vsRes.containsAll(vsExpected) && vsExpected.containsAll(vsRes)); //verify expected ordering - start, parent, grandparent @@ -84,131 +85,157 @@ public class GraphTraversalQueryEngineTest extends AAISetup { assertTrue(vsRes.get(1).equals(tenant)); assertTrue(vsRes.get(2).equals(cloudreg)); } - + + @Test + public void testFindAllParentsGivenAaiUris(){ + + //setup + Graph graph = TinkerGraph.open(); + + Vertex cloudreg = graph.addVertex(T.id, "00", "aai-node-type", "cloud-region", "aai-uri", "/cloud-infrastructure/cloud-regions/cloud-region/testowner/testid"); + Vertex tenant = graph.addVertex(T.id, "10", "aai-node-type", "tenant", "aai-uri", "/cloud-infrastructure/cloud-regions/cloud-region/testowner/testid/tenants/tenant/testTenant1"); + Vertex vserver = graph.addVertex(T.id, "20", "aai-node-type", "vserver", "aai-uri", "/cloud-infrastructure/cloud-regions/cloud-region/testowner/testid/tenants/tenant/testTenant1/vservers/vserver/testVserver1"); + + String [] uris = new String[]{ + "/cloud-infrastructure/cloud-regions/cloud-region/testowner/testid/tenants/tenant/testTenant1", + "/cloud-infrastructure/cloud-regions/cloud-region/testowner/testid/tenants/tenant/testTenant1/vservers/vserver/testVserver1", + "/cloud-infrastructure/cloud-regions/cloud-region/testowner/testid" + }; + + GraphTraversalSource g = graph.traversal(); + GraphTraversalQueryEngine queryEngine = new GraphTraversalQueryEngine(g); + List<Vertex> vertices = queryEngine.findParents(uris); + + assertThat("Returned vertices should be 3 cloud region, tenant and vserver", vertices.size(), is(3)); + assertThat("Expected the first element back to be vserver", vertices.get(0), is(vserver)); + assertThat("Expected the second element back to be tenant", vertices.get(1), is(tenant)); + assertThat("Expected the element back to be cloud region", vertices.get(2), is(cloudreg)); + } + @Test public void testFindAllChildren() throws AAIException { //setup Graph graph = TinkerGraph.open(); - + Vertex cloudreg = graph.addVertex(T.id, "00", "aai-node-type", "cloud-region"); Vertex tenant = graph.addVertex(T.id, "10", "aai-node-type", "tenant"); Vertex vserver = graph.addVertex(T.id, "20", "aai-node-type", "vserver"); Vertex vserver2 = graph.addVertex(T.id, "21", "aai-node-type", "vserver"); Vertex oam = graph.addVertex(T.id, "30", "aai-node-type", "oam-network"); - + GraphTraversalSource g = graph.traversal(); - + edgeSer.addTreeEdge(g, cloudreg, tenant); edgeSer.addTreeEdge(g, tenant, vserver); edgeSer.addTreeEdge(g, tenant, vserver2); edgeSer.addTreeEdge(g, cloudreg, oam); - + List<Vertex> crExpected = new ArrayList<>(Arrays.asList(cloudreg, tenant, vserver, vserver2, oam)); List<Vertex> vsExpected = new ArrayList<>(Arrays.asList(vserver)); - + GraphTraversalQueryEngine engine = new GraphTraversalQueryEngine(g); - + //test List<Vertex> crRes = engine.findAllChildren(cloudreg); assertTrue(crRes.containsAll(crExpected) && crExpected.containsAll(crRes)); - + List<Vertex> vsRes = engine.findAllChildren(vserver); assertTrue(vsRes.containsAll(vsExpected) && vsExpected.containsAll(vsRes)); } - + @Test public void testFindChildrenOfType() throws AAIException { //setup Graph graph = TinkerGraph.open(); - + Vertex gv = graph.addVertex(T.id, "00", "aai-node-type", "generic-vnf"); Vertex lint1 = graph.addVertex(T.id, "10", "aai-node-type", "l-interface"); Vertex lint2 = graph.addVertex(T.id, "11", "aai-node-type", "l-interface"); Vertex lag = graph.addVertex(T.id, "20", "aai-node-type", "lag-interface"); Vertex lint3 = graph.addVertex(T.id, "12", "aai-node-type", "l-interface"); - + GraphTraversalSource g = graph.traversal(); - + edgeSer.addTreeEdge(g, gv, lint1); edgeSer.addTreeEdge(g, gv, lint2); edgeSer.addTreeEdge(g, gv, lag); edgeSer.addTreeEdge(g, lag, lint3); - + List<Vertex> expected = new ArrayList<>(Arrays.asList(lint1, lint2)); - + GraphTraversalQueryEngine engine = new GraphTraversalQueryEngine(g); - + //test List<Vertex> results = engine.findChildrenOfType(gv, "l-interface"); assertTrue(results.containsAll(expected) && expected.containsAll(results)); } - + @Test public void testFindChildren() throws AAIException { //setup Graph graph = TinkerGraph.open(); - + Vertex gv = graph.addVertex(T.id, "00", "aai-node-type", "generic-vnf"); Vertex lint1 = graph.addVertex(T.id, "10", "aai-node-type", "l-interface"); Vertex lint2 = graph.addVertex(T.id, "11", "aai-node-type", "l-interface"); Vertex lag = graph.addVertex(T.id, "20", "aai-node-type", "lag-interface"); Vertex lint3 = graph.addVertex(T.id, "12", "aai-node-type", "l-interface"); - + GraphTraversalSource g = graph.traversal(); - + edgeSer.addTreeEdge(g, gv, lint1); edgeSer.addTreeEdge(g, gv, lint2); edgeSer.addTreeEdge(g, gv, lag); edgeSer.addTreeEdge(g, lag, lint3); - + List<Vertex> expected = new ArrayList<>(Arrays.asList(lint1, lint2, lag)); - + GraphTraversalQueryEngine engine = new GraphTraversalQueryEngine(g); - + //test List<Vertex> results = engine.findChildren(gv); assertTrue(results.containsAll(expected) && expected.containsAll(results)); } - + @Test public void testFindRelatedVertices() throws AAIException { //setup - + Graph graph = TinkerGraph.open(); - + Vertex gv = graph.addVertex(T.id, "00", "aai-node-type", "generic-vnf"); Vertex lint = graph.addVertex(T.id, "10", "aai-node-type", "l-interface"); Vertex lint2 = graph.addVertex(T.id, "11", "aai-node-type", "l-interface"); Vertex log = graph.addVertex(T.id, "20", "aai-node-type", "logical-link"); - + GraphTraversalSource g = graph.traversal(); - + edgeSer.addTreeEdge(g, gv, lint); edgeSer.addEdge(g, lint, log); edgeSer.addEdge(g, log, lint2); - + List<Vertex> outExpected = new ArrayList<>(Arrays.asList(lint)); List<Vertex> inExpected = new ArrayList<>(Arrays.asList(lint, lint2)); List<Vertex> bothExpected = new ArrayList<>(Arrays.asList(log)); - + GraphTraversalQueryEngine engine = new GraphTraversalQueryEngine(g); - + //test List<Vertex> outRes = engine.findRelatedVertices(gv, Direction.IN, "org.onap.relationships.inventory.BelongsTo", "l-interface"); assertTrue(outRes.containsAll(outExpected) && outExpected.containsAll(outRes)); List<Vertex> inRes = engine.findRelatedVertices(log, Direction.IN, "tosca.relationships.network.LinksTo", "l-interface"); assertTrue(inRes.containsAll(inExpected) && inExpected.containsAll(inRes)); - + List<Vertex> bothRes = engine.findRelatedVertices(lint, Direction.BOTH, "tosca.relationships.network.LinksTo", "logical-link"); assertTrue(bothRes.containsAll(bothExpected) && bothExpected.containsAll(bothRes)); } - + @Test public void testFindSubGraph() throws AAIException, EdgeRuleNotFoundException, AmbiguousRuleChoiceException { //setup Graph graph = TinkerGraph.open(); - + Vertex cr = graph.addVertex(T.id, "00", "aai-node-type", "cloud-region"); Vertex ten = graph.addVertex(T.id, "10", "aai-node-type", "tenant"); Vertex ten2 = graph.addVertex(T.id, "11", "aai-node-type", "tenant"); @@ -225,7 +252,7 @@ public class GraphTraversalQueryEngineTest extends AAISetup { Vertex modelVer = graph.addVertex(T.id, "100", "aai-node-type", "model-ver"); GraphTraversalSource g = graph.traversal(); - + Edge crTen = edgeSer.addTreeEdge(g, cr, ten); Edge crTen2 = edgeSer.addTreeEdge(g, cr, ten2); Edge tenVs = edgeSer.addTreeEdge(g, ten, vs); @@ -234,14 +261,14 @@ public class GraphTraversalQueryEngineTest extends AAISetup { Edge lintLog = edgeSer.addEdge(g, lint, log); Edge vsGv = edgeSer.addEdge(g, vs, gv); edgeSer.addEdge(g, gv, vnfc); - + edgeSer.addTreeEdge(g, gv, lag); edgeSer.addTreeEdge(g, lag, lint2); Edge modelVerEdge = edgeSer.addPrivateEdge(g, gv, modelVer, null); edgeSer.addTreeEdge(g, comp, ctag); Edge crComp = edgeSer.addEdge(g, cr, comp); - + //findSubGraph(cr, 0, true) List<Element> expected1 = new ArrayList<>(Arrays.asList(cr)); //findSubGraph(cr, 2, true) @@ -249,48 +276,48 @@ public class GraphTraversalQueryEngineTest extends AAISetup { crTen, crTen2, tenVs, tenVs2)); //findSubGraph(cr) List<Element> expected3 = new ArrayList<>(Arrays.asList(cr, ten, ten2, comp, vs, vs2, lint, gv, log, - crTen, crTen2, crComp, tenVs, tenVs2, vsLInt, + crTen, crTen2, crComp, tenVs, tenVs2, vsLInt, vsGv, lintLog)); - + GraphTraversalQueryEngine engine = new GraphTraversalQueryEngine(g); - + //test Tree<Element> res1 = engine.findSubGraph(cr, 0, true); Set<Element> resList1 = treeToList(res1); assertTrue(resList1.containsAll(expected1) && expected1.containsAll(resList1)); - + Tree<Element> res2 = engine.findSubGraph(cr, 2, true); Set<Element> resList2 = treeToList(res2); assertTrue(resList2.containsAll(expected2) && expected2.containsAll(resList2)); - + Tree<Element> res3 = engine.findSubGraph(cr); Set<Element> resList3 = treeToList(res3); assertThat(resList3, containsInAnyOrder(expected3.toArray())); // assertTrue(resList3.containsAll(expected3) && expected3.containsAll(resList3)); } - + /** - * convenience helper method to make it easier to check the contents of the tree against + * convenience helper method to make it easier to check the contents of the tree against * a list of expected results * @param tree - the tree whose contents you want in collection form * @return set of the contents of the tree */ private Set<Element> treeToList(Tree<Element> tree) { Set<Element> ret = new HashSet<>(); - + for (Element key : tree.keySet()) { ret.add(key); ret.addAll(treeToList(tree.get(key))); } - + return ret; } - + @Test public void testFindEdgesForVersion() throws AAIException, EdgeRuleNotFoundException, AmbiguousRuleChoiceException { //setup Graph graph = TinkerGraph.open(); - + Vertex gv = graph.addVertex(T.id, "00", "aai-node-type", "generic-vnf"); Vertex vnfc = graph.addVertex(T.id, "10", "aai-node-type", "vnfc"); Vertex lob = graph.addVertex(T.id, "20", "aai-node-type", "line-of-business"); @@ -303,16 +330,16 @@ public class GraphTraversalQueryEngineTest extends AAISetup { .addEdge("some-edge", cr, CONTAINS.toString(), "NONE"); GraphTraversalSource g = graph.traversal(); - + edgeSer.addTreeEdge(g, gv, lint); //tree edge so shouldn't appear in results Edge gvVnfc = edgeSer.addEdge(g, gv, vnfc); edgeSer.addEdge(g, gv, lob); //v11/12 not v10 Edge gvMvEdge = edgeSer.addPrivateEdge(g, gv, mv, null); List<Edge> expected = new ArrayList<>(Arrays.asList(gvVnfc)); - + GraphTraversalQueryEngine engine = new GraphTraversalQueryEngine(g); - + //test Loader loader = loaderFactory.createLoaderForVersion(ModelType.MOXY, schemaVersions.getRelatedLinkVersion()); List<Edge> results = engine.findEdgesForVersion(gv, loader); @@ -322,27 +349,27 @@ public class GraphTraversalQueryEngineTest extends AAISetup { results = engine.findEdgesForVersion(cr, loader); assertThat(results, containsInAnyOrder(expected.toArray())); } - + @Test public void testFindCousinVertices() throws AAIException { //setup Graph graph = TinkerGraph.open(); - + Vertex gv = graph.addVertex(T.id, "00", "aai-node-type", "generic-vnf"); Vertex vnfc = graph.addVertex(T.id, "10", "aai-node-type", "vnfc"); Vertex lob = graph.addVertex(T.id, "20", "aai-node-type", "line-of-business"); Vertex lint = graph.addVertex(T.id, "30", "aai-node-type", "l-interface"); - + GraphTraversalSource g = graph.traversal(); - + edgeSer.addTreeEdge(g, gv, lint); //tree edge so shouldn't appear in results edgeSer.addEdge(g, gv, vnfc); edgeSer.addEdge(g, gv, lob); - + List<Vertex> expected = new ArrayList<>(Arrays.asList(vnfc, lob)); - + GraphTraversalQueryEngine engine = new GraphTraversalQueryEngine(g); - + //test List<Vertex> results = engine.findCousinVertices(gv); assertTrue(results.containsAll(expected) && expected.containsAll(results)); diff --git a/aai-core/src/test/resources/payloads/resource/cloud-region-with-vlan.json b/aai-core/src/test/resources/payloads/resource/cloud-region-with-vlan.json new file mode 100644 index 00000000..bad4fadf --- /dev/null +++ b/aai-core/src/test/resources/payloads/resource/cloud-region-with-vlan.json @@ -0,0 +1,50 @@ +{ + "cloud-owner" : "junit-cloud-owner-with-vlan", + "cloud-region-id" : "junit-cloud-region-with-vlan", + "cloud-type" : "P9P1X6U9eDXR", + "owner-defined-type" : "OUrR8kI6Br", + "tenants" : { + "tenant" : [ { + "tenant-id" : "tenant1-with-vlan", + "tenant-name" : "yhgVBcv3Pr", + "vservers" : { + "vserver" : [ { + "vserver-id" : "vserver1-with-vlan", + "vserver-name" : "P3SJ347Uyv", + "vserver-name2" : "1dHd", + "vserver-selflink" : "v7dU8H", + "in-maint" : false, + "is-closed-loop-disabled" : true, + "l-interfaces" : { + "l-interface" : [ { + "interface-name" : "top-linterface-with-vlan", + "interface-role" : "3W1FAJGSQ", + "v6-wan-link-ip" : "WugWw3N", + "selflink" : "Of4j0pU", + "interface-id" : "eoW", + "macaddr" : "XNbbIy33", + "network-name" : "ZETDv5sGhiS", + "management-option" : "qR3RyCxgEU", + "vlans" : { + "vlan" : [ { + "vlan-interface" : "test-vlan-interface-1", + "vlan-id-inner" : 884, + "vlan-id-outer" : 992, + "speed-value" : "RyFPm", + "speed-units" : "ZrckmLff7b", + "vlan-description" : "zjAeUFgeagpf", + "backdoor-connection" : "gANMvUquB", + "vpn-key" : "l0rV7bb2Sc", + "orchestration-status" : "WVnLpbzIx", + "in-maint" : false, + "prov-status" : "tYMRi6df", + "is-ip-unnumbered" : false + } ] + } + } ] + } + } ] + } + } ] + } +} diff --git a/aai-core/src/test/resources/payloads/resource/cloud-region.json b/aai-core/src/test/resources/payloads/resource/cloud-region.json new file mode 100644 index 00000000..0bbf6398 --- /dev/null +++ b/aai-core/src/test/resources/payloads/resource/cloud-region.json @@ -0,0 +1,4 @@ +{ + "cloud-owner": "cloud-region-owner-with-vip-ipv4", + "cloud-region-id": "cloud-region-id-with-vip-ipv4" +} diff --git a/aai-core/src/test/resources/payloads/resource/entitlement.json b/aai-core/src/test/resources/payloads/resource/entitlement.json new file mode 100644 index 00000000..09afb617 --- /dev/null +++ b/aai-core/src/test/resources/payloads/resource/entitlement.json @@ -0,0 +1,4 @@ +{ + "group-uuid":"g1", + "resource-uuid":"r1" +} diff --git a/aai-core/src/test/resources/payloads/resource/model-element.json b/aai-core/src/test/resources/payloads/resource/model-element.json new file mode 100644 index 00000000..935f1b05 --- /dev/null +++ b/aai-core/src/test/resources/payloads/resource/model-element.json @@ -0,0 +1,230 @@ +{ + "model-element-uuid": "acf8b6cf-e051-4c1b-bcad-b24792f826cf-test1", + "new-data-del-flag": "7hWigbwQCh", + "cardinality": "loWHX7E8X", + "linkage-points": [ + "Ve4JU" + ], + "model-elements": { + "model-element": [ + { + "model-element-uuid": "01b757d5-6468-48d0-a1cd-2aa8310dadab-test1", + "new-data-del-flag": "IXoM0QnE0W", + "cardinality": "THLHKl" + }, + { + "model-element-uuid": "2e5d5696-2221-4bcf-a493-e0ca92e323de-test1", + "new-data-del-flag": "icATJZ7Vbhz", + "cardinality": "tOIz" + } + ] + }, + "model-constraints": { + "model-constraint": [ + { + "model-constraint-uuid": "9f2e872b-000e-4bb9-9398-f308a997dd4f-test1", + "constrained-element-set-uuid-to-replace": "XfAVftTYu", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "103cc6f7-b648-4a3f-b5aa-0ffdac4d4bc5-test1", + "constraint-type": "FLKyNFdqbSSje", + "check-type": "IDLSYTM", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "5c063d38-d83e-4fe5-a721-86296fe39267-test1", + "element-choice-set-name": "EFc5b4k7LN8hF", + "cardinality": "xx8", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "0b7a5aa2-5061-4916-a2c6-5280e67e0a71-test1", + "new-data-del-flag": "x1Lo15W", + "cardinality": "COu" + }, + { + "model-element-uuid": "f0af348b-1701-409f-bb11-e3acc77ad718-test1", + "new-data-del-flag": "eiWkNQEj", + "cardinality": "3aju5XtI" + } + ] + } + }, + { + "element-choice-set-uuid": "2c0888a6-fb6d-4a72-b0a1-3349c90d9e33-test1", + "element-choice-set-name": "Ko4", + "cardinality": "aXjnyYfCRFTDK", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "ea8853be-8473-4f80-8229-9041864e143e-test1", + "new-data-del-flag": "4tirsCs0fjDdS", + "cardinality": "v2hbEn8u" + }, + { + "model-element-uuid": "f1267f37-5aa0-4f4a-ae66-3cf782ff4a9d-test1", + "new-data-del-flag": "mzuWZTlc9f", + "cardinality": "ysBXVL" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "a82256df-5bc8-4433-851f-9b09f63432b4-test1", + "constraint-type": "qgnLpj", + "check-type": "zFug3wfyCe5cF", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "3ce79981-c0cc-4210-9dab-89b8866797be-test1", + "element-choice-set-name": "rZ2f7Es", + "cardinality": "H1b", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "52362a88-eb7a-4fe5-a6d5-1ca414cfed64-test1", + "new-data-del-flag": "8cQmvQgeKJz", + "cardinality": "6T7GiwYlJR5" + }, + { + "model-element-uuid": "e9d00d89-a340-4ef1-ab19-63367a5e8642-test1", + "new-data-del-flag": "ec8DWfYeMJdr", + "cardinality": "Bumc8fT7" + } + ] + } + }, + { + "element-choice-set-uuid": "335bac38-9739-4ff7-8743-e186310d86a9-test1", + "element-choice-set-name": "K0MH", + "cardinality": "rL3fH1Oz", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "29244639-38ab-45b2-82b7-b91001ad56da-test1", + "new-data-del-flag": "YZGbB6", + "cardinality": "ushcYd" + }, + { + "model-element-uuid": "e36d3159-bfa7-465a-849f-de9ca6d4f304-test1", + "new-data-del-flag": "ZtzRhZbxv2i", + "cardinality": "NE4m8n5hj" + } + ] + } + } + ] + } + } + ] + } + }, + { + "model-constraint-uuid": "9e74a97c-52a9-43b4-bb9d-144cb5a2216e-test1", + "constrained-element-set-uuid-to-replace": "bZqdarfk6", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "6c4db1da-8860-4445-a0ad-3ae00709e2bb-test1", + "constraint-type": "Xzat2eVcU36g", + "check-type": "3Y5Bi32VkRHFl", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "33dc52b0-3461-4ca6-a195-c3cacc2ab426-test1", + "element-choice-set-name": "2ziwMvQGmanJ", + "cardinality": "7XXcdT5sqsOf", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "c0fb7805-dfc4-4972-9a1c-d9409560f6e3-test1", + "new-data-del-flag": "262Lx", + "cardinality": "0ZaryC" + }, + { + "model-element-uuid": "e868506d-ef7d-4843-9c1b-47712c7c1b77-test1", + "new-data-del-flag": "f7cKUCEbVuj6", + "cardinality": "6LlYciE99" + } + ] + } + }, + { + "element-choice-set-uuid": "aca6b107-ba99-4e10-aef5-ff35a5b25a60-test1", + "element-choice-set-name": "lqfZDs", + "cardinality": "9AwB7", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "527faaf0-f989-47a4-9ffd-4ea16d034be1-test1", + "new-data-del-flag": "mPra14ZfLilOw", + "cardinality": "y6mYr6" + }, + { + "model-element-uuid": "dfd7c245-0a28-494d-b388-faf27481674c-test1", + "new-data-del-flag": "muGR", + "cardinality": "TUj4Z" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "17999338-a062-4447-b8c3-b27228255ede-test1", + "constraint-type": "WhTbdWCLyA", + "check-type": "tN02MBeB", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "633c6757-2270-48fb-bff7-61145e6c943f-test1", + "element-choice-set-name": "Ith1PEe6", + "cardinality": "8sta", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "827c40a9-cabe-460a-b537-2f117ca62db5-test1", + "new-data-del-flag": "fS65rXWQY", + "cardinality": "Kq42Z8GzCh" + }, + { + "model-element-uuid": "c107a4bd-59e4-4249-bbc3-a006645dcb01-test1", + "new-data-del-flag": "EtgTeJdo19", + "cardinality": "tvzpgg" + } + ] + } + }, + { + "element-choice-set-uuid": "930260d8-4cfd-4494-ba2c-d619c143a390-test1", + "element-choice-set-name": "ugDc0", + "cardinality": "K8MXjV", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "f17f9eea-d421-4e8a-8628-32a4b56507ef-test1", + "new-data-del-flag": "rjAbq7gyZWe", + "cardinality": "VbDCFB9" + }, + { + "model-element-uuid": "ee1d800e-46c1-4bb0-8664-f257850ffff9-test1", + "new-data-del-flag": "YontPXK4sj4s", + "cardinality": "UQnNPld4sE" + } + ] + } + } + ] + } + } + ] + } + } + ] + } +} diff --git a/aai-core/src/test/resources/payloads/resource/model.json b/aai-core/src/test/resources/payloads/resource/model.json new file mode 100644 index 00000000..a86a1de8 --- /dev/null +++ b/aai-core/src/test/resources/payloads/resource/model.json @@ -0,0 +1,975 @@ +{ + "model-invariant-id": "24c04fc5-f3f8-43ec-8792-c6f940638676-test1", + "model-type": "woZ4dpF3TM", + "model-vers": { + "model-ver": [ + { + "model-version-id": "0c4c59f0-9864-43ca-b0c2-ca38746b72a5-test1", + "model-name": "vzZn3Q3h", + "model-version": "RcN2FIwUF", + "distribution-status": "qcCHkye", + "model-description": "qcPB4hOig9", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "0dc2b8b6-af8d-4213-970b-7861a603fc86-test1", + "new-data-del-flag": "OzhypC", + "cardinality": "vNpFfHn", + "linkage-points": [ + "BTDVw7pA" + ], + "model-elements": { + "model-element": [ + { + "model-element-uuid": "0ff7bfc5-e42b-474d-b728-cfdc08310971-test1", + "new-data-del-flag": "v54vhcTX", + "cardinality": "uHLsCw" + }, + { + "model-element-uuid": "4fb1b036-9963-467e-93fe-c6e499859b2e-test1", + "new-data-del-flag": "UAJAjOCe9dIiW", + "cardinality": "8PhfKI" + } + ] + }, + "model-constraints": { + "model-constraint": [ + { + "model-constraint-uuid": "782ba24a-28ab-4fd0-8e69-da10cc5373f3-test1", + "constrained-element-set-uuid-to-replace": "8vcq", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "a33e65c3-1198-4d4c-9799-2b521e4c4212-test1", + "constraint-type": "4jqozTvJSD", + "check-type": "J9aM", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "7df27a94-06c8-46ef-9fc2-5900d8cffbb0-test1", + "element-choice-set-name": "wjNT", + "cardinality": "XSPmZH", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "fcf1450b-cf5c-414a-99c0-2bd7ca247997-test1", + "new-data-del-flag": "uhwWkaDw", + "cardinality": "jeBU9v" + }, + { + "model-element-uuid": "85bfca5f-03ac-4653-a3b4-bdfad8dd1c13-test1", + "new-data-del-flag": "L2BQe2kyA", + "cardinality": "asciE2y7RWGVa" + } + ] + } + }, + { + "element-choice-set-uuid": "d20040e2-0417-4a2d-978e-e6c9f353a0c5-test1", + "element-choice-set-name": "x7vEDj5", + "cardinality": "A6f2Us", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "5cdcbb91-22eb-4c12-9a54-b8c3ff5aa591-test1", + "new-data-del-flag": "BCo", + "cardinality": "FejO1GRl" + }, + { + "model-element-uuid": "7a401382-7d45-40f9-8fe7-236629273226-test1", + "new-data-del-flag": "zFj", + "cardinality": "9DzyB7u" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "2f7a375a-135d-461b-b806-e7cd636506f4-test1", + "constraint-type": "Toy", + "check-type": "uYEWZon81Wa", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "8aab42ee-561f-4506-ab66-6432442193f8-test1", + "element-choice-set-name": "RpJJn1eRQL", + "cardinality": "q0XXZ52", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "2a35ad40-02ef-4947-b20c-94a90440ffc7-test1", + "new-data-del-flag": "gXHt5O5XyRKb", + "cardinality": "idg0cH" + }, + { + "model-element-uuid": "7aae6c34-086e-477b-845a-01cedc2a4e25-test1", + "new-data-del-flag": "Zwj", + "cardinality": "3vkT" + } + ] + } + }, + { + "element-choice-set-uuid": "f42dcd25-aa82-401f-b987-5d0f46d6b5d6-test1", + "element-choice-set-name": "Cuon", + "cardinality": "cfxfo3MAF", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "2f9f4c5c-216c-45c0-913b-5513faa6f887-test1", + "new-data-del-flag": "2ZTs", + "cardinality": "zki" + }, + { + "model-element-uuid": "6abd9eb0-59b2-45d1-bc31-51913bc9f54b-test1", + "new-data-del-flag": "9cQkkA", + "cardinality": "NGY7CgvUQDN" + } + ] + } + } + ] + } + } + ] + } + }, + { + "model-constraint-uuid": "8544de93-b647-4d79-aa55-a3ebe01664e7-test1", + "constrained-element-set-uuid-to-replace": "phbhKDPCcUjK", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "61624b66-e059-49c6-a97c-06ca4643e259-test1", + "constraint-type": "gp0nB63dH", + "check-type": "NFnVJKceCMc", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "f85f1ee8-d28f-4128-83d8-02c2e07f9e27-test1", + "element-choice-set-name": "8noi", + "cardinality": "mTFoGk", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "1e15718d-17e8-4a34-852e-2edede549b63-test1", + "new-data-del-flag": "f3p1f", + "cardinality": "c0Z6zG0iKX" + }, + { + "model-element-uuid": "4ad51c91-1e90-4683-b9ea-017cf0bc528e-test1", + "new-data-del-flag": "05r47Aldegi", + "cardinality": "aoF" + } + ] + } + }, + { + "element-choice-set-uuid": "fb2c7824-fc87-4fe9-93c6-10586a59384e-test1", + "element-choice-set-name": "EIjKr0w3tJ", + "cardinality": "qlw6EFFF", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "2395bed7-399d-41cd-81af-e5002854bc69-test1", + "new-data-del-flag": "Uj2", + "cardinality": "eLHBGqv7wz5z" + }, + { + "model-element-uuid": "77840aeb-0868-49c1-a97d-6ded16115174-test1", + "new-data-del-flag": "rOR", + "cardinality": "ddXm59kMQ01y" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "4f1f30ee-5bd5-4f19-b11d-23ebf0fcd569-test1", + "constraint-type": "Ul7rn0Gyh", + "check-type": "5KpT6x", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "d2b9a3f5-29de-45ba-ad5a-184e0635607a-test1", + "element-choice-set-name": "OWFEeT", + "cardinality": "g2wvznpf", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "93d0b10b-529a-4ad4-88fa-afc7273ec538-test1", + "new-data-del-flag": "BHqDTu", + "cardinality": "bUZd3Zj4b" + }, + { + "model-element-uuid": "fd6548f8-211a-4194-a78e-857efc443180-test1", + "new-data-del-flag": "ygDuebv", + "cardinality": "UZx1v0HH7I" + } + ] + } + }, + { + "element-choice-set-uuid": "f393b086-40b6-47d9-b28a-b830d5d7f896-test1", + "element-choice-set-name": "dxt8afBT", + "cardinality": "L3mtZIH342JVw", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "d802155e-8e16-4b9d-986a-d0f490d83e56-test1", + "new-data-del-flag": "cPapfw", + "cardinality": "4O3aiKf" + }, + { + "model-element-uuid": "64cf1800-77b7-4c1d-80b2-38fc156769a6-test1", + "new-data-del-flag": "T2dc7t3", + "cardinality": "LTrGUfoB7imS" + } + ] + } + } + ] + } + } + ] + } + } + ] + } + }, + { + "model-element-uuid": "1db3d04c-92c6-4100-b851-16a0a2318909-test1", + "new-data-del-flag": "9Gc", + "cardinality": "vtJk", + "linkage-points": [ + "NKC5OiZl" + ], + "model-elements": { + "model-element": [ + { + "model-element-uuid": "16b90f9d-d2ed-46e8-8a0d-f2907ecf723d-test1", + "new-data-del-flag": "jcjL", + "cardinality": "qxX" + }, + { + "model-element-uuid": "46afb085-7025-4a35-99e3-6170dc98b74b-test1", + "new-data-del-flag": "tXgrF6T4HrirE", + "cardinality": "55waV3oUI" + } + ] + }, + "model-constraints": { + "model-constraint": [ + { + "model-constraint-uuid": "6f6fbf96-c9a7-4393-9e81-284d194ba6a3-test1", + "constrained-element-set-uuid-to-replace": "HF81PuSuM", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "cf2d2ff1-1ffc-4ad6-b3ce-8729c3352637-test1", + "constraint-type": "XfD", + "check-type": "mVERn7luqFc", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "85fb8953-f86d-4d58-8ca2-3169cba074c6-test1", + "element-choice-set-name": "4s6Jf", + "cardinality": "KL8eVh9xZ", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "7b1fd5b5-b633-4ed6-a9e8-9c3430eaaae0-test1", + "new-data-del-flag": "uNbESfr", + "cardinality": "aEj2c5UXN2qW" + }, + { + "model-element-uuid": "c67a3856-3812-4c16-abb3-484fe68ba65e-test1", + "new-data-del-flag": "cg1GbGyps", + "cardinality": "cCPBuauJ" + } + ] + } + }, + { + "element-choice-set-uuid": "4748a324-a8aa-420a-9f48-0fd0b7418a1a-test1", + "element-choice-set-name": "U3JuW0tx", + "cardinality": "4jpXRS43G", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "daad8caa-dbfc-487a-91d4-a93021a86e0c-test1", + "new-data-del-flag": "ZoRQyflmIqEaA", + "cardinality": "Mu7PzxNg8cta" + }, + { + "model-element-uuid": "e5a640f1-1203-410c-8665-0ee325179368-test1", + "new-data-del-flag": "thsF0L", + "cardinality": "fViD" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "5d1b2701-d719-4eb5-b5bc-355f99042648-test1", + "constraint-type": "4wPy2Xd", + "check-type": "sDuGfZji", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "d01f1c5b-5ba1-4c21-b80d-901867a1d810-test1", + "element-choice-set-name": "FdrVe6o", + "cardinality": "F1MXs1fET0jr3", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "6e059bfe-5386-4491-8e84-b1b6dd8b5333-test1", + "new-data-del-flag": "DfodMIT72h7", + "cardinality": "KvRbLIJFL" + }, + { + "model-element-uuid": "ea73886a-ff64-4d3c-9b17-5ae5b2196dea-test1", + "new-data-del-flag": "n9PnoK5", + "cardinality": "0E3fM6" + } + ] + } + }, + { + "element-choice-set-uuid": "abb55d08-28b6-48d8-b19e-91b12c096649-test1", + "element-choice-set-name": "MRFYeF7", + "cardinality": "b64jIhgbYH7", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "c449a692-bd7d-4d86-b46e-1c417b17099f-test1", + "new-data-del-flag": "ApwuLwUkkw", + "cardinality": "m23c6lsF" + }, + { + "model-element-uuid": "7cb770d9-55d8-45d2-8b9b-abdff7a2c88b-test1", + "new-data-del-flag": "MeSjvno", + "cardinality": "lGFfcOOl19" + } + ] + } + } + ] + } + } + ] + } + }, + { + "model-constraint-uuid": "ce9e9365-1fec-4c29-8201-c4417ee2ea1d-test1", + "constrained-element-set-uuid-to-replace": "Buq56Km4W9BD", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "3dcea521-a227-46f3-bd4f-ef1fcff0755e-test1", + "constraint-type": "WZr", + "check-type": "pxJXuyaXvsaQy", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "19dc8b4a-c58b-4ff8-b507-877c6521d91a-test1", + "element-choice-set-name": "af5CO7sD5TWwz", + "cardinality": "uGwotewgnmls", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "5d476750-ddc3-4822-b629-5fba93445ed9-test1", + "new-data-del-flag": "mMjS6OflV3c", + "cardinality": "Gidw5" + }, + { + "model-element-uuid": "d4d4f1f6-ad60-4a94-b076-eb36db6fb362-test1", + "new-data-del-flag": "kWLrcR", + "cardinality": "AU6llmk" + } + ] + } + }, + { + "element-choice-set-uuid": "d6616e1a-3de5-4981-a216-805bdd97d65e-test1", + "element-choice-set-name": "9w67AaiJNt0", + "cardinality": "kKax01oqb", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "bb11c9c1-5013-4b51-9a06-9729addfd4af-test1", + "new-data-del-flag": "ANM8TN0a", + "cardinality": "UPtA8wc4m" + }, + { + "model-element-uuid": "71b323c0-1829-414e-85f6-99f4b5835e44-test1", + "new-data-del-flag": "okC9", + "cardinality": "1KP3eU5j8" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "77d1cc2d-6e94-4a4f-a7ec-f725605f18d3-test1", + "constraint-type": "inJa", + "check-type": "JkoIos3V", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "9893bb02-e422-4810-bccc-a594b0489ded-test1", + "element-choice-set-name": "ZIync", + "cardinality": "WZfSZe", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "b0aaa08d-a7fe-44ae-a6d8-d21645831a23-test1", + "new-data-del-flag": "RdwsDmBBz", + "cardinality": "Ry6V3zZtLOS" + }, + { + "model-element-uuid": "a2670e8e-f57e-4c0e-9522-6d16b4e2cf18-test1", + "new-data-del-flag": "aCECwDjNyauTZ", + "cardinality": "a28MCzMm24JBI" + } + ] + } + }, + { + "element-choice-set-uuid": "321ccc75-61ae-4358-af89-53809a28f956-test1", + "element-choice-set-name": "HjNWjs", + "cardinality": "bxRIxMP", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "41965df8-b42d-47ba-8bf6-cade35804311-test1", + "new-data-del-flag": "CPLVd", + "cardinality": "4Ij4hep2" + }, + { + "model-element-uuid": "db7ee384-420e-44fb-a1ca-809f94add6b8-test1", + "new-data-del-flag": "Cj5GVv", + "cardinality": "1LSdRvvzBBfa" + } + ] + } + } + ] + } + } + ] + } + } + ] + } + } + ] + }, + "metadata": { + "metadatum": [ + { + "metaname": "8b3496f1-822e-4acf-b1fc-d07a494d3a86-test1", + "metaval": "gZwqNxIDsw" + }, + { + "metaname": "d9a50737-f3df-4156-881d-e3875031b413-test1", + "metaval": "qMugMK" + } + ] + } + }, + { + "model-version-id": "1e6ec4df-ad66-40bc-8f1d-ba40c835e77e-test1", + "model-name": "wStuf", + "model-version": "HAm", + "distribution-status": "zsKxpTc5YTW1", + "model-description": "zxHpALNUvX8V", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "b1ac6004-59bd-4372-ab16-f327d04d647e-test1", + "new-data-del-flag": "j58MXR", + "cardinality": "9Xr0gNa", + "linkage-points": [ + "LmE7mpec2" + ], + "model-elements": { + "model-element": [ + { + "model-element-uuid": "38295729-dc3b-4b7b-87e3-446513a802df-test1", + "new-data-del-flag": "R5KeFH8", + "cardinality": "9wkk9Z" + }, + { + "model-element-uuid": "b1ac08c3-3047-4637-8796-6b5e43bbad19-test1", + "new-data-del-flag": "KOS9kMuahku", + "cardinality": "F6A" + } + ] + }, + "model-constraints": { + "model-constraint": [ + { + "model-constraint-uuid": "0c7f45c8-2552-400a-b029-e20cddda592b-test1", + "constrained-element-set-uuid-to-replace": "105ZUJ", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "162ea223-ae49-4e6d-ba30-8215c433a7e2-test1", + "constraint-type": "GAq", + "check-type": "ippNik", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "5caa493c-e804-4c28-aad6-3389a1801e29-test1", + "element-choice-set-name": "oLPlXWcCL", + "cardinality": "ns6", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "ff8f0de4-220f-4f7f-8447-07c67ed8a109-test1", + "new-data-del-flag": "TlLVzdU5Mz", + "cardinality": "wQI" + }, + { + "model-element-uuid": "fd6018bc-b3ce-4073-b494-848c06aaef83-test1", + "new-data-del-flag": "ZIkOtmdu8ij", + "cardinality": "XfOsai" + } + ] + } + }, + { + "element-choice-set-uuid": "45d079e2-94f8-4c75-9e58-fca00da8e032-test1", + "element-choice-set-name": "nfOh9SvgG", + "cardinality": "j5u1OHFt", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "3edbf250-7039-47cd-b31d-019e5639006b-test1", + "new-data-del-flag": "Qo5M8AB", + "cardinality": "crDs4E" + }, + { + "model-element-uuid": "566c492a-4b8d-4393-9634-5deb36114390-test1", + "new-data-del-flag": "tSl", + "cardinality": "xZK7oAuI1" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "6efd9e99-bca6-4f3b-a7dc-38ad373c38e9-test1", + "constraint-type": "v3RYRkCdTm", + "check-type": "adGnhMq6P4", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "09d59684-e8e5-4e08-93af-b2a65d0328e6-test1", + "element-choice-set-name": "Q1rPa", + "cardinality": "ENpTrUR", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "904654a2-4d7d-4d07-af26-ddb881dd05d7-test1", + "new-data-del-flag": "76D6azxVm1XR9", + "cardinality": "R0Zb34f" + }, + { + "model-element-uuid": "898cfb56-6e36-4b03-8967-9384d06b3a3e-test1", + "new-data-del-flag": "SJtXdr4", + "cardinality": "vG4JrjQ8" + } + ] + } + }, + { + "element-choice-set-uuid": "9ac87d60-51c5-4dd6-bb1e-1b8dd234b5a9-test1", + "element-choice-set-name": "azv", + "cardinality": "oJfnKsDO5k", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "6ec8304f-8159-4344-b06d-faf1001fd275-test1", + "new-data-del-flag": "3s5zQjf8Vwl4", + "cardinality": "6SogRPhZX38Ol" + }, + { + "model-element-uuid": "cfd9b055-3d8a-4429-9ea6-078a2270e469-test1", + "new-data-del-flag": "VaW81J59tqiY", + "cardinality": "t1mNuU7JnNc7L" + } + ] + } + } + ] + } + } + ] + } + }, + { + "model-constraint-uuid": "fe9d4b0d-dc65-4440-a10f-5237f135255c-test1", + "constrained-element-set-uuid-to-replace": "pWv26", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "ea1473a3-e72d-4838-b8d8-7c8e86e0b3c8-test1", + "constraint-type": "Mdbb0G5nLxcx3", + "check-type": "a8rlES", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "86726d80-74d6-4b7e-9532-65ebb76133ef-test1", + "element-choice-set-name": "fBm0", + "cardinality": "csPsKpgV", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "c0df13d1-a8be-4bc0-a572-44ca28d35d62-test1", + "new-data-del-flag": "k9x8eFMP", + "cardinality": "5xHWzWretpyAN" + }, + { + "model-element-uuid": "ef4f9a86-a1b8-48ef-a7aa-ff56a1879ef7-test1", + "new-data-del-flag": "JdujO3QQr4", + "cardinality": "N6pvsIK" + } + ] + } + }, + { + "element-choice-set-uuid": "07f1ae12-8e40-4c42-bb8c-e5931f82be84-test1", + "element-choice-set-name": "by4K", + "cardinality": "HfJt7", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "aed78a3d-de9e-4825-88b3-8251a822e87c-test1", + "new-data-del-flag": "NbAWo0r1su", + "cardinality": "GIHSc" + }, + { + "model-element-uuid": "451ef346-a43e-4d9c-ae94-4d14f12b7776-test1", + "new-data-del-flag": "o6GI", + "cardinality": "Hbbm3HRFeXw8U" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "1b3d0f60-b438-4789-b711-5d48c211e9b3-test1", + "constraint-type": "G7HBqSqa8", + "check-type": "Rky", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "c47b3c18-912b-4781-82a9-961702e58712-test1", + "element-choice-set-name": "ntxJ1ETx96", + "cardinality": "6B1XDdERKUk0f", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "d451967f-5cb9-4aba-8595-938d44bed081-test1", + "new-data-del-flag": "pwHvYjtFEvmn", + "cardinality": "X6jiiniZ24e3" + }, + { + "model-element-uuid": "0f6196d6-b3b7-451a-a3d8-2a0a9bf401b7-test1", + "new-data-del-flag": "YFY4Dmyr", + "cardinality": "CMF05rTle1" + } + ] + } + }, + { + "element-choice-set-uuid": "927a4c5d-c06f-474c-bc39-b1bf8edbd5a9-test1", + "element-choice-set-name": "C19", + "cardinality": "7xCo", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "b839b84d-4508-4aa6-8c32-0566a25d62d1-test1", + "new-data-del-flag": "QdvVPKk", + "cardinality": "QWO7FG" + }, + { + "model-element-uuid": "20df13a4-aad9-46ed-8c40-d2c9ee25c0c0-test1", + "new-data-del-flag": "aFu39uCq", + "cardinality": "wFwAw3L3wT2x5" + } + ] + } + } + ] + } + } + ] + } + } + ] + } + }, + { + "model-element-uuid": "c198c7e7-162f-4b36-ab18-1ea652940569-test1", + "new-data-del-flag": "c9VM1nU9", + "cardinality": "na6ev", + "linkage-points": [ + "Qn71", + "VqVquEM" + ], + "model-elements": { + "model-element": [ + { + "model-element-uuid": "5a3d5001-ed82-4586-9f8e-f4754c5a7a18-test1", + "new-data-del-flag": "x5unCqf", + "cardinality": "SelKZSH" + }, + { + "model-element-uuid": "b150a826-8d8e-4e76-8661-6cbc67fd0d9a-test1", + "new-data-del-flag": "9LfZWv", + "cardinality": "uMtY5vRkmCnw" + } + ] + }, + "model-constraints": { + "model-constraint": [ + { + "model-constraint-uuid": "43871728-3816-470f-9b36-4097731910a4-test1", + "constrained-element-set-uuid-to-replace": "R8Uhk", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "0fd99298-8079-4a3c-bbed-dce0ceadf790-test1", + "constraint-type": "JFVkmI9ilV", + "check-type": "9eqnm", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "22a978b9-4dcc-4c7d-b88a-6bdbab97dd7c-test1", + "element-choice-set-name": "4fHNNV", + "cardinality": "VBAiH", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "d9bef0c1-07c9-4251-b621-196be3b7870e-test1", + "new-data-del-flag": "FQen9U2BTGx", + "cardinality": "ohd" + }, + { + "model-element-uuid": "60d8b814-8035-4010-8d46-9db2e5962f17-test1", + "new-data-del-flag": "2aJgwjV3UZv", + "cardinality": "WMb" + } + ] + } + }, + { + "element-choice-set-uuid": "a5b753eb-162e-4bea-8185-43b7e96c0770-test1", + "element-choice-set-name": "k9Ud", + "cardinality": "VAL51", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "1ecfe4f4-6696-4df4-8b93-b2b3fa39c377-test1", + "new-data-del-flag": "SRFYVMGe", + "cardinality": "muxuy0DZBBW" + }, + { + "model-element-uuid": "96acb4b2-f178-4119-8c32-6c5901896417-test1", + "new-data-del-flag": "wbzd", + "cardinality": "M0Y7Meota" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "b22dc84b-5568-4b7d-9b58-0bc90c969619-test1", + "constraint-type": "waPRf1NGHeFy", + "check-type": "YziABbwA4", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "fb4d5b28-2083-4bb6-a87d-e53eadd14d54-test1", + "element-choice-set-name": "VnX7QOx", + "cardinality": "qk4aMYBkw", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "1b5481f2-66c9-4cd4-89ee-492e333570fe-test1", + "new-data-del-flag": "ba6QIyym", + "cardinality": "zUJ9gpkRq" + }, + { + "model-element-uuid": "c9918ba3-dcb3-46e8-870f-316251f459a3-test1", + "new-data-del-flag": "X2MIS2y", + "cardinality": "u3xIyaL" + } + ] + } + }, + { + "element-choice-set-uuid": "f5a14c3b-6536-4803-9660-4a2f2263dfdd-test1", + "element-choice-set-name": "KiVnNej1flt", + "cardinality": "AwImrctM5", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "e5e3b7db-7336-498b-8eca-f43f979dda68-test1", + "new-data-del-flag": "v3JnIH7JEK", + "cardinality": "pzypSipEOA" + }, + { + "model-element-uuid": "24426103-516b-4634-83f6-0b02db4d10ed-test1", + "new-data-del-flag": "lNx", + "cardinality": "OFMiiE1w4K" + } + ] + } + } + ] + } + } + ] + } + }, + { + "model-constraint-uuid": "310f083b-2d76-4e2d-ab82-b4c6b2a49637-test1", + "constrained-element-set-uuid-to-replace": "70UQQ", + "constrained-element-sets": { + "constrained-element-set": [ + { + "constrained-element-set-uuid": "f6b24791-facd-41d8-8b4e-b76d750ef3e8-test1", + "constraint-type": "eBTDI1Xx", + "check-type": "uyscxXaNEt", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "efc328ed-3302-4808-b25a-adbbe88af7d9-test1", + "element-choice-set-name": "HwpVVTGb6e", + "cardinality": "dZ78oR", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "1f20c582-952d-4489-a45b-b7b898062bc4-test1", + "new-data-del-flag": "Vq8f3STu8ez", + "cardinality": "5lN" + }, + { + "model-element-uuid": "77f61b0a-ecf4-43ed-84b8-478372922557-test1", + "new-data-del-flag": "OrbNIIvUvTQ", + "cardinality": "CwT1hp9uUn" + } + ] + } + }, + { + "element-choice-set-uuid": "7561f91e-5d71-4871-b745-5974a29fa4f0-test1", + "element-choice-set-name": "JyMxtTdSzEYI", + "cardinality": "CU5", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "7857bb38-b373-4f32-bfed-fb698b38f288-test1", + "new-data-del-flag": "qGYsqE", + "cardinality": "m1wtW502" + }, + { + "model-element-uuid": "27097500-1e6a-4692-bcfc-8c5751094416-test1", + "new-data-del-flag": "e3LD9ESy1h2we", + "cardinality": "a0jEiwQq" + } + ] + } + } + ] + } + }, + { + "constrained-element-set-uuid": "a63f212b-0608-4676-a801-eae9704ca6f8-test1", + "constraint-type": "1s642QAY3emE", + "check-type": "3sR23Vt", + "element-choice-sets": { + "element-choice-set": [ + { + "element-choice-set-uuid": "e6f1f3eb-61c5-4e00-877b-253d41dc8bc1-test1", + "element-choice-set-name": "EBhZUZSbs", + "cardinality": "1LrK8RM4o33LD", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "323c6b90-7aba-41ad-8313-5a4a2b2c56ae-test1", + "new-data-del-flag": "R3oKjC8hz87", + "cardinality": "LT8Phj" + }, + { + "model-element-uuid": "6a4f5cbe-6251-402e-aa0c-d42284acf4f3-test1", + "new-data-del-flag": "XeIPIYLqyIL", + "cardinality": "pGK" + } + ] + } + }, + { + "element-choice-set-uuid": "4937bd81-08f1-4cb7-b3bd-9c9db142a791-test1", + "element-choice-set-name": "VXcx", + "cardinality": "hIKH", + "model-elements": { + "model-element": [ + { + "model-element-uuid": "db4ddcd0-6a57-47af-b72d-f10e806bd779-test1", + "new-data-del-flag": "2lsYwLYukGh43", + "cardinality": "zhcfiBQ" + }, + { + "model-element-uuid": "7f0823ec-87f7-4f0a-b6ed-d19571917f3d-test1", + "new-data-del-flag": "8xUASuYyIym", + "cardinality": "ktmQThTtgu" + } + ] + } + } + ] + } + } + ] + } + } + ] + } + } + ] + }, + "metadata": { + "metadatum": [ + { + "metaname": "a4f78e88-e09f-4ca1-b26a-093141044aea-test1", + "metaval": "kfsTQsxZk" + }, + { + "metaname": "1d153c8b-ba38-4566-876b-53795a71b8f9-test1", + "metaval": "xr0Z" + } + ] + } + } + ] + } +} diff --git a/aai-core/src/test/resources/payloads/resource/vip-ipv4-address-list.json b/aai-core/src/test/resources/payloads/resource/vip-ipv4-address-list.json new file mode 100644 index 00000000..4978d2af --- /dev/null +++ b/aai-core/src/test/resources/payloads/resource/vip-ipv4-address-list.json @@ -0,0 +1,6 @@ +{ + "vip-ipv4-address": "vip-ipv4-address-list-1", + "vip-ipv4-prefix-length": 3333, + "vlan-id-inner": 3333, + "vlan-id-outer": 3333 +} diff --git a/aai-core/src/test/resources/payloads/resource/vnf.json b/aai-core/src/test/resources/payloads/resource/vnf.json new file mode 100644 index 00000000..e3e7a35f --- /dev/null +++ b/aai-core/src/test/resources/payloads/resource/vnf.json @@ -0,0 +1,4 @@ +{ + "vnf-id":"vnf1", + "vnf-type":"someval" +} diff --git a/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeIngestor.java b/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeIngestor.java index dfcd0db3..c2c58d5f 100644 --- a/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeIngestor.java +++ b/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeIngestor.java @@ -1,4 +1,4 @@ -/** +/** * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ @@ -20,11 +20,16 @@ package org.onap.aai.edges; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import org.apache.tinkerpop.gremlin.structure.Direction; import org.onap.aai.edges.enums.DirectionNotation; import org.onap.aai.edges.enums.EdgeField; @@ -34,6 +39,8 @@ import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException; import org.onap.aai.setup.ConfigTranslator; import org.onap.aai.setup.SchemaVersion; import org.onap.aai.setup.SchemaVersions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -51,15 +58,25 @@ import static com.jayway.jsonpath.Criteria.where; */ @Component public class EdgeIngestor { - private Map<SchemaVersion, List<DocumentContext>> versionJsonFilesMap; + + private static final Logger LOG = LoggerFactory.getLogger(EdgeIngestor.class); + + private Map<SchemaVersion, List<DocumentContext>> versionJsonFilesMap; private static final String READ_START = "$.rules.[?]"; private static final String READ_ALL_START = "$.rules.*"; private SchemaVersions schemaVersions; + + private Set<String> multipleLabelKeys; + + private final LoadingCache<SchemaFilter,Multimap<String,EdgeRule>> cacheFilterStore; + + private final LoadingCache<String, String[]> cousinLabelStore; + //-----ingest-----// /** * Instantiates the EdgeIngestor bean. - * + * * @param translator - ConfigTranslator autowired in by Spring framework which * contains the configuration information needed to ingest the desired files. */ @@ -69,20 +86,41 @@ public class EdgeIngestor { JsonIngestor ji = new JsonIngestor(); this.schemaVersions = schemaVersions; versionJsonFilesMap = ji.ingest(filesToIngest); + this.cacheFilterStore = CacheBuilder.newBuilder() + .maximumSize(2000) + .build( + new CacheLoader<SchemaFilter, Multimap<String, EdgeRule>>() { + @Override + public Multimap<String, EdgeRule> load(SchemaFilter key) { + return extractRules(key); + } + } + ); + + this.cousinLabelStore = CacheBuilder.newBuilder() + .maximumSize(50) + .build( + new CacheLoader<String, String[]>() { + @Override + public String[] load(String key) throws Exception { + return retrieveCousinLabels(key); + } + } + ); } - + //-----methods for getting rule info-----// - + /** * Gets list of all edge rules defined in the latest version's schema - * + * * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules associated with those types - * where the key takes the form of + * where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple * rules for a pair of node types but the from/to value in the json is flipped for some of them. * @throws EdgeRuleNotFoundException if none found @@ -90,17 +128,47 @@ public class EdgeIngestor { public Multimap<String, EdgeRule> getAllCurrentRules() throws EdgeRuleNotFoundException { return getAllRules(schemaVersions.getDefaultVersion()); } - + + /** + * Retrieves all the nodes that contain multiple edge labels + * + * A lazy instantiation to retrieve all this info on first call + * + * @return a set containing a list of strings where each string is + * concatenated by a pipe (|) character such as aNodeType|bNodeType + */ + public Set<String> getMultipleLabelKeys(){ + + if(multipleLabelKeys == null){ + multipleLabelKeys = new HashSet<>(); + try { + final Multimap<String, EdgeRule> edges = this.getAllCurrentRules(); + if(edges == null || edges.isEmpty()){ + LOG.warn("Unable to find any edge rules for the latest version"); + } + edges.keySet().forEach((key) -> { + Collection<EdgeRule> rules = edges.get(key); + if(rules.size() > 1){ + multipleLabelKeys.add(key); + } + }); + } catch (EdgeRuleNotFoundException e) { + LOG.info("For the latest schema version, unable to find any edges with multiple keys"); + } + } + + return multipleLabelKeys; + } /** * Gets list of all edge rules defined in the given version's schema - * + * * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules associated with those types - * where the key takes the form of + * where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple * rules for a pair of node types but the from/to value in the json is flipped for some of them. * @throws EdgeRuleNotFoundException if none found @@ -113,19 +181,19 @@ public class EdgeIngestor { return found; } } - + /** * Finds the rules (if any) matching the given query criteria. If none, the returned Multimap * will be empty. - * + * * @param q - EdgeRuleQuery with filter criteria set - * - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple * rules for a pair of node types but the from/to value in the json is flipped for some of them. * @throws EdgeRuleNotFoundException if none found @@ -140,36 +208,40 @@ public class EdgeIngestor { if (found.isEmpty()) { throw new EdgeRuleNotFoundException("No rules found for " + q.toString()); } else { - for (EdgeRule rule : found.values()) { - if (!q.getFromType().equals(rule.getFrom())) { - /* To maintain backwards compatibility with old EdgeRules API, - * where the direction of the returned EdgeRule would be - * flipped (if necessary) to match the directionality of - * the input params. - * ie, If the rule is from=A,to=B,direction=OUT, - * if the user asked (A,B) the direction would be OUT, - * if they asked (B,A), it would be IN to match. - */ - rule.flipDirection(); - } - } - return found; + Multimap<String, EdgeRule> copy = ArrayListMultimap.create(); + found.entries().stream().forEach((entry) -> { + EdgeRule rule = new EdgeRule(entry.getValue()); + if(!q.getFromType().equals(rule.getFrom())){ + /* To maintain backwards compatibility with old EdgeRules API, + * where the direction of the returned EdgeRule would be + * flipped (if necessary) to match the directionality of + * the input params. + * ie, If the rule is from=A,to=B,direction=OUT, + * if the user asked (A,B) the direction would be OUT, + * if they asked (B,A), it would be IN to match. + */ + rule.flipDirection(); + } + copy.put(entry.getKey(), rule); + }); + + return copy; } } - + /** * Gets the rule satisfying the given filter criteria. If there are more than one - * that match, return the default rule. If there is no clear default to return, or + * that match, return the default rule. If there is no clear default to return, or * no rules match at all, error. - * + * * @param q - EdgeRuleQuery with filter criteria set * @return EdgeRule satisfying given criteria * @throws EdgeRuleNotFoundException if none found that match * @throws AmbiguousRuleChoiceException if multiple match but no way to choice one from them - * Specifically, if multiple node type pairs come back (ie bar|foo and asdf|foo, + * Specifically, if multiple node type pairs come back (ie bar|foo and asdf|foo, * no way to know which is appropriate over the others), * or if there is a mix of Tree and Cousin edges because again there is no way to - * know which is "defaulter" than the other. + * know which is "defaulter" than the other. * The default property only clarifies among multiple cousin edges of the same node pair, * ex: which l-interface|logical-link rule to default to. */ @@ -184,7 +256,7 @@ public class EdgeIngestor { if (found.isEmpty()) { throw new EdgeRuleNotFoundException("No rule found for " + q.toString() + "."); } - + EdgeRule rule = null; if (found.keys().size() == 1) { //only one found, cool we're done for (Entry<String, EdgeRule> e : found.entries()) { @@ -193,10 +265,12 @@ public class EdgeIngestor { } else { rule = getDefaultRule(found); } - + + if (rule == null) { //should never get here though throw new EdgeRuleNotFoundException("No rule found for " + q.toString() + "."); } else { + rule = new EdgeRule(rule); if (!q.getFromType().equals(rule.getFrom())) { /* To maintain backwards compatibility with old EdgeRules API, * where the direction of the returned EdgeRule would be @@ -211,7 +285,9 @@ public class EdgeIngestor { return rule; } } - + + + private EdgeRule getDefaultRule(Multimap<String, EdgeRule> found) throws AmbiguousRuleChoiceException { if (found.keySet().size() > 1) { //ie multiple node pairs (a|c and b|c not just all a|c) case StringBuilder sb = new StringBuilder(); @@ -220,7 +296,7 @@ public class EdgeIngestor { } throw new AmbiguousRuleChoiceException("No way to select single rule from these pairs: " + sb.toString() + "."); } - + int defaultCount = 0; EdgeRule defRule = null; for (Entry<String, EdgeRule> e : found.entries()) { @@ -235,13 +311,13 @@ public class EdgeIngestor { } else if (defaultCount == 0) { throw new AmbiguousRuleChoiceException("No default found."); } - + return defRule; } - + /** * Checks if there exists any rule that satisfies the given filter criteria. - * + * * @param q - EdgeRuleQuery with filter criteria set * @return boolean */ @@ -252,42 +328,59 @@ public class EdgeIngestor { return !extractRules(q.getFilter(), schemaVersions.getDefaultVersion()).isEmpty(); } } - + /** * Gets all cousin rules for the given node type in the latest schema version. - * + * * @param nodeType - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple * rules for a pair of node types but the from/to value in the json is flipped for some of them. */ public Multimap<String, EdgeRule> getCousinRules(String nodeType) { return getCousinRules(nodeType, schemaVersions.getDefaultVersion()); //default to latest } - + + + public String[] retrieveCousinLabels(String nodeType){ + + Multimap<String, EdgeRule> cousinRules = getCousinRules(nodeType); + String[] cousinLabels = new String[cousinRules.size()]; + + return cousinRules.entries() + .stream() + .map((entry) -> entry.getValue().getLabel()) + .collect(Collectors.toList()) + .toArray(cousinLabels); + } + + public String[] retrieveCachedCousinLabels(String nodeType) throws ExecutionException { + return cousinLabelStore.get(nodeType); + } + /** * Gets all cousin rules for the given node type in the given schema version. - * + * * @param nodeType * @param v - the version of the edge rules to query - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple - * rules for a pair of node types but the from/to value in the json is flipped for some of them. + * rules for a pair of node types but the from/to value in the json is flipped for some of them. */ public Multimap<String, EdgeRule> getCousinRules(String nodeType, SchemaVersion v) { return extractRules(new EdgeRuleQuery.Builder(nodeType).edgeType(EdgeType.COUSIN).build().getFilter(), v); } - + /** * Returns if the given node type has any cousin relationships in the current version. * @param nodeType @@ -296,7 +389,7 @@ public class EdgeIngestor { public boolean hasCousinRule(String nodeType) { return hasCousinRule(nodeType, schemaVersions.getDefaultVersion()); } - + /** * Returns if the given node type has any cousin relationships in the given version. * @param nodeType @@ -305,45 +398,45 @@ public class EdgeIngestor { public boolean hasCousinRule(String nodeType, SchemaVersion v) { return !getCousinRules(nodeType, v).isEmpty(); } - + /** * Gets all rules where "{given nodeType} contains {otherType}" in the latest schema version. - * + * * @param nodeType - node type that is the container in the returned relationships - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple - * rules for a pair of node types but the from/to value in the json is flipped for some of them. + * rules for a pair of node types but the from/to value in the json is flipped for some of them. */ public Multimap<String, EdgeRule> getChildRules(String nodeType) { return getChildRules(nodeType, schemaVersions.getDefaultVersion()); } - + /** * Gets all rules where "{given nodeType} contains {otherType}" in the given schema version. - * + * * @param nodeType - node type that is the container in the returned relationships - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple - * rules for a pair of node types but the from/to value in the json is flipped for some of them. + * rules for a pair of node types but the from/to value in the json is flipped for some of them. */ public Multimap<String, EdgeRule> getChildRules(String nodeType, SchemaVersion v) { Filter from = assembleFilterSegments(where(EdgeField.FROM.toString()).is(nodeType), getSameDirectionContainmentCriteria()); Filter to = assembleFilterSegments(where(EdgeField.TO.toString()).is(nodeType), getOppositeDirectionContainmentCriteria()); Filter total = from.or(to); - + return extractRules(total, v); } - + /** * Returns if the given node type has any child relationships (ie it contains another node type) in the current version. * @param nodeType @@ -352,7 +445,7 @@ public class EdgeIngestor { public boolean hasChildRule(String nodeType) { return hasChildRule(nodeType, schemaVersions.getDefaultVersion()); } - + /** * Returns if the given node type has any child relationships (ie it contains another node type) in the given version. * @param nodeType @@ -361,45 +454,45 @@ public class EdgeIngestor { public boolean hasChildRule(String nodeType, SchemaVersion v) { return !getChildRules(nodeType, v).isEmpty(); } - + /** * Gets all rules where "{given nodeType} is contained by {otherType}" in the latest schema version. - * + * * @param nodeType - node type that is the containee in the returned relationships - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple * rules for a pair of node types but the from/to value in the json is flipped for some of them. */ public Multimap<String, EdgeRule> getParentRules(String nodeType) { return getParentRules(nodeType, schemaVersions.getDefaultVersion()); } - + /** * Gets all rules where "{given nodeType} is contained by {otherType}" in the given schema version. - * + * * @param nodeType - node type that is the containee in the returned relationships - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple - * rules for a pair of node types but the from/to value in the json is flipped for some of them. + * rules for a pair of node types but the from/to value in the json is flipped for some of them. */ public Multimap<String, EdgeRule> getParentRules(String nodeType, SchemaVersion v) { Filter from = assembleFilterSegments(where(EdgeField.FROM.toString()).is(nodeType), getOppositeDirectionContainmentCriteria()); Filter to = assembleFilterSegments(where(EdgeField.TO.toString()).is(nodeType), getSameDirectionContainmentCriteria()); Filter total = from.or(to); - + return extractRules(total, v); } - + /** * Returns if the given node type has any parent relationships (ie it is contained by another node type) in the current version. * @param nodeType @@ -408,7 +501,7 @@ public class EdgeIngestor { public boolean hasParentRule(String nodeType) { return hasParentRule(nodeType, schemaVersions.getDefaultVersion()); } - + /** * Returns if the given node type has any parent relationships (ie it is contained by another node type) in the given version. * @param nodeType @@ -417,47 +510,57 @@ public class EdgeIngestor { public boolean hasParentRule(String nodeType, SchemaVersion v) { return !getParentRules(nodeType, v).isEmpty(); } - + /** * Applies the given filter to the DocumentContext(s) for the given version to extract * edge rules, and converts this extracted information into the Multimap form - * + * * @param filter - JsonPath filter to read the DocumentContexts with. May be null * to denote no filter, ie get all. * @param v - The schema version to extract from - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Map will be empty if * no rules are found. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple - * rules for a pair of node types but the from/to value in the json is flipped for some of them. + * rules for a pair of node types but the from/to value in the json is flipped for some of them. */ private Multimap<String, EdgeRule> extractRules(Filter filter, SchemaVersion v) { - List<Map<String, String>> foundRules = new ArrayList<>(); - List<DocumentContext> docs = versionJsonFilesMap.get(v); - if (docs != null) { - for (DocumentContext doc : docs) { - if (filter == null) { - foundRules.addAll(doc.read(READ_ALL_START)); - } else { - foundRules.addAll(doc.read(READ_START, filter)); - } - } - } - - return convertToEdgeRules(foundRules); - } - + SchemaFilter schemaFilter = new SchemaFilter(filter, v); + try { + return cacheFilterStore.get(schemaFilter); + } catch (ExecutionException e) { + LOG.info("Encountered exception during the retrieval of the rules"); + return ArrayListMultimap.create(); + } + } + + public Multimap<String, EdgeRule> extractRules(SchemaFilter schemaFilter){ + List<Map<String, String>> foundRules = new ArrayList<>(); + List<DocumentContext> docs = versionJsonFilesMap.get(schemaFilter.getSchemaVersion()); + if (docs != null) { + for (DocumentContext doc : docs) { + if (schemaFilter.getFilter() == null) { + foundRules.addAll(doc.read(READ_ALL_START)); + } else { + foundRules.addAll(doc.read(READ_START, Filter.parse(schemaFilter.getFilter()))); + } + } + } + + return convertToEdgeRules(foundRules); + } + //-----filter building helpers-----// /** * ANDs together the given start criteria with each criteria in the pieces list, and * then ORs together these segments into one filter. - * + * * JsonPath doesn't have an OR method on Criteria, only on Filters, so assembling * a complete filter requires this sort of roundabout construction. - * + * * @param start - Criteria of the form where(from/to).is(nodeType) * (ie the start of any A&AI edge rule query) * @param pieces - Other Criteria to be applied @@ -474,91 +577,91 @@ public class EdgeIngestor { } return assembled; } - + /** * Builds the sub-Criteria for a containment edge rule query where the direction * and containment fields must match. - * + * * Used for getChildRules() where the container node type is in the "from" position and * for getParentRules() where the containee type is in the "to" position. - * + * * @return List<Criteria> covering property permutations defined with either notation or explicit direction */ private List<Criteria> getSameDirectionContainmentCriteria() { List<Criteria> crits = new ArrayList<>(); - + crits.add(where(EdgeField.CONTAINS.toString()).is(DirectionNotation.DIRECTION.toString())); - + crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.OUT.toString()) .and(EdgeField.CONTAINS.toString()).is(Direction.OUT.toString())); - + crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.IN.toString()) .and(EdgeField.CONTAINS.toString()).is(Direction.IN.toString())); - + return crits; } - + /** * Builds the sub-Criteria for a containment edge rule query where the direction * and containment fields must not match. - * + * * Used for getChildRules() where the container node type is in the "to" position and * for getParentRules() where the containee type is in the "from" position. - * + * * @return List<Criteria> covering property permutations defined with either notation or explicit direction */ private List<Criteria> getOppositeDirectionContainmentCriteria() { List<Criteria> crits = new ArrayList<>(); - + crits.add(where(EdgeField.CONTAINS.toString()).is(DirectionNotation.OPPOSITE.toString())); - + crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.OUT.toString()) .and(EdgeField.CONTAINS.toString()).is(Direction.IN.toString())); - + crits.add(where(EdgeField.DIRECTION.toString()).is(Direction.IN.toString()) .and(EdgeField.CONTAINS.toString()).is(Direction.OUT.toString())); - + return crits; } - + //-----rule packaging helpers-----// /** * Converts the raw output from reading the json file to the Multimap<String key, EdgeRule> format - * + * * @param allFound - raw edge rule output read from json file(s) * (could be empty if none found) - * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of + * @return Multimap<String, EdgeRule> of node names keys to the EdgeRules where the key takes the form of * {alphabetically first nodetype}|{alphabetically second nodetype}. Will be empty if input * was empty. * ex: buildAlphabetizedKey("l-interface", "logical-link") -> "l-interface|logical-link" * buildAlphabetizedKey("logical-link", "l-interface") -> "l-interface|logical-link" - * + * * This is alphabetical order to normalize the keys, as sometimes there will be multiple - * rules for a pair of node types but the from/to value in the json is flipped for some of them. + * rules for a pair of node types but the from/to value in the json is flipped for some of them. */ private Multimap<String, EdgeRule> convertToEdgeRules(List<Map<String, String>> allFound) { Multimap<String, EdgeRule> rules = ArrayListMultimap.create(); - + TypeAlphabetizer alpher = new TypeAlphabetizer(); - + for (Map<String, String> raw : allFound) { EdgeRule converted = new EdgeRule(raw); if (converted.getFrom().equals(converted.getTo())) { - /* the way the code worked in the past was with outs and - * when we switched it to in the same-node-type to - * same-node-type parent child edges were failing because all - * of the calling code would pass the parent as the left argument, - * so it was either in that method swap the parent/child, - * flip the edge rule or make all callers swap. the last seemed - * like a bad idea. and felt like the edge flip was the better + /* the way the code worked in the past was with outs and + * when we switched it to in the same-node-type to + * same-node-type parent child edges were failing because all + * of the calling code would pass the parent as the left argument, + * so it was either in that method swap the parent/child, + * flip the edge rule or make all callers swap. the last seemed + * like a bad idea. and felt like the edge flip was the better * of the remaining 2 */ converted.flipDirection(); } String alphabetizedKey = alpher.buildAlphabetizedKey(raw.get(EdgeField.FROM.toString()), raw.get(EdgeField.TO.toString())); rules.put(alphabetizedKey, converted); } - + return rules; } - + } diff --git a/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeRule.java b/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeRule.java index e1cb240e..f914f6cb 100644 --- a/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeRule.java +++ b/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeRule.java @@ -26,7 +26,9 @@ import org.onap.aai.edges.enums.EdgeField; import org.onap.aai.edges.enums.EdgeProperty; import org.onap.aai.edges.enums.MultiplicityRule; +import java.util.Collections; import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; /** @@ -43,16 +45,16 @@ public class EdgeRule { private String description; private boolean isPrivateEdge = false; - /** + /** * Instantiates a new edge rule. - * + * * @param fieldVals - Map<String, String> where first string is - * an EdgeField value and second string is the + * an EdgeField value and second string is the * value of that field */ public EdgeRule(Map<String, String> fieldVals) { edgeFields = new EnumMap<>(EdgeProperty.class); - + from = fieldVals.get(EdgeField.FROM.toString()); to = fieldVals.get(EdgeField.TO.toString()); label = fieldVals.get(EdgeField.LABEL.toString()); @@ -63,19 +65,32 @@ public class EdgeRule { String rawVal = fieldVals.get(prop.toString()); edgeFields.put(prop, convertNotation(direction, rawVal)); } - + isDefaultEdge = Boolean.valueOf(fieldVals.get(EdgeField.DEFAULT.toString())); - + description = fieldVals.get(EdgeField.DESCRIPTION.toString()); if (description == null) { //bc description is optional and not in v12 and earlier description = ""; } } + // Copy Constructor + public EdgeRule(EdgeRule edgeRule){ + this.from = edgeRule.from; + this.to = edgeRule.to; + this.label = edgeRule.label; + this.direction = Direction.valueOf(edgeRule.direction.toString()); + this.multiplicityRule = MultiplicityRule.valueOf(edgeRule.multiplicityRule.toString()); + this.edgeFields = new HashMap<>(edgeRule.edgeFields); + this.isDefaultEdge = edgeRule.isDefaultEdge; + this.description = edgeRule.description; + this.isPrivateEdge = edgeRule.isPrivateEdge; + } + /** * Converts whatever string was in the json for an edge property value into * the appropriate AAIDirection - * + * * @param Direction dir - the edge direction * @param String rawVal - property value from the json, may be * IN, OUT, BOTH, NONE, ${direction}, or !${direction} @@ -92,7 +107,7 @@ public class EdgeRule { } else if (AAIDirection.IN.toString().equalsIgnoreCase(rawVal)) { return AAIDirection.IN; } - + DirectionNotation rawDN = DirectionNotation.getValue(rawVal); if (DirectionNotation.DIRECTION.equals(rawDN)) { return AAIDirection.getValue(dir); @@ -100,7 +115,7 @@ public class EdgeRule { return AAIDirection.getValue(dir.opposite()); } } - + /** * Gets the name of the node type in the "from" field * @return String nodetype @@ -125,7 +140,7 @@ public class EdgeRule { public String getLabel() { return label; } - + /** * Gets the multiplicity rule. * @@ -134,7 +149,7 @@ public class EdgeRule { public MultiplicityRule getMultiplicityRule() { return multiplicityRule; } - + /** * Gets the edge direction * @@ -143,7 +158,7 @@ public class EdgeRule { public Direction getDirection() { return direction; } - + /** * Gets the value of contains-other-v * @@ -152,7 +167,7 @@ public class EdgeRule { public String getContains() { return edgeFields.get(EdgeProperty.CONTAINS).toString(); } - + /** * Gets the value of delete-other-v * @@ -161,10 +176,10 @@ public class EdgeRule { public String getDeleteOtherV() { return edgeFields.get(EdgeProperty.DELETE_OTHER_V).toString(); } - + /** * Gets the value of the prevent-delete property - * + * * @return String prevent-delete property value */ public String getPreventDelete() { @@ -173,13 +188,13 @@ public class EdgeRule { /** * Returns if this rule is a default or not - * + * * @return boolean */ public boolean isDefault() { return isDefaultEdge; } - + /** * Gets the description on the edge rule (if there is one) * @return String description @@ -187,7 +202,7 @@ public class EdgeRule { public String getDescription() { return this.description; } - + /** * Flips the direction value * IN -> OUT diff --git a/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeRuleQuery.java b/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeRuleQuery.java index 5801e816..58c8c546 100644 --- a/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeRuleQuery.java +++ b/aai-schema-ingest/src/main/java/org/onap/aai/edges/EdgeRuleQuery.java @@ -1,4 +1,4 @@ -/** +/** * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ @@ -36,6 +36,7 @@ import static com.jayway.jsonpath.Filter.filter; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import static com.jayway.jsonpath.Criteria.where; @@ -90,6 +91,12 @@ public class EdgeRuleQuery { private String getSecondNodeType() { return nodeB; } + + public Builder to(String nodeB){ + this.nodeB = nodeB; + return this; + } + public Builder toOnly() { //Allows this to be used with single parameter constructor Builder(String nodeA) if(StringUtils.isEmpty(this.nodeB) && StringUtils.isNotEmpty(this.nodeA) ) { @@ -312,6 +319,30 @@ public class EdgeRuleQuery { } return sb.toString(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + EdgeRuleQuery ruleQuery = (EdgeRuleQuery) o; + return isPrivate == ruleQuery.isPrivate && + Objects.equals(v, ruleQuery.v) && + Objects.equals(nodeA, ruleQuery.nodeA) && + Objects.equals(nodeB, ruleQuery.nodeB) && + Objects.equals(label, ruleQuery.label) && + direction == ruleQuery.direction && + type == ruleQuery.type; + } + + @Override + public int hashCode() { + if(v.isPresent()){ + return Objects.hash(v.get(), nodeA, nodeB, label, direction, type, isPrivate); + } else { + return Objects.hash(nodeA, nodeB, label, direction, type, isPrivate); + } + } + } diff --git a/aai-schema-ingest/src/main/java/org/onap/aai/edges/SchemaFilter.java b/aai-schema-ingest/src/main/java/org/onap/aai/edges/SchemaFilter.java new file mode 100644 index 00000000..792e3c51 --- /dev/null +++ b/aai-schema-ingest/src/main/java/org/onap/aai/edges/SchemaFilter.java @@ -0,0 +1,69 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-18 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.edges; + +import com.jayway.jsonpath.Filter; +import org.onap.aai.setup.SchemaVersion; + +import java.util.Objects; + +public class SchemaFilter { + + private String filter; + + private SchemaVersion schemaVersion; + + public SchemaFilter(Filter filter, SchemaVersion schemaVersion){ + if(filter != null){ + this.filter = filter.toString(); + } + this.schemaVersion = schemaVersion; + } + + public SchemaVersion getSchemaVersion() { + return schemaVersion; + } + + public String getFilter() { + return filter; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SchemaFilter that = (SchemaFilter) o; + return Objects.equals(filter, that.filter) && + Objects.equals(schemaVersion, that.schemaVersion); + } + + @Override + public int hashCode() { + return Objects.hash(filter, schemaVersion); + } + + @Override + public String toString() { + return "SchemaFilter{" + + "filter='" + filter + '\'' + + ", schemaVersion=" + schemaVersion + + '}'; + } +} diff --git a/aai-schema-ingest/src/main/java/org/onap/aai/edges/exceptions/AmbiguousRuleChoiceException.java b/aai-schema-ingest/src/main/java/org/onap/aai/edges/exceptions/AmbiguousRuleChoiceException.java index b218cecd..3ddced9a 100644 --- a/aai-schema-ingest/src/main/java/org/onap/aai/edges/exceptions/AmbiguousRuleChoiceException.java +++ b/aai-schema-ingest/src/main/java/org/onap/aai/edges/exceptions/AmbiguousRuleChoiceException.java @@ -1,4 +1,4 @@ -/** +/** * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ @@ -24,4 +24,8 @@ public class AmbiguousRuleChoiceException extends Exception { public AmbiguousRuleChoiceException(String msg) { super(msg); } + + public AmbiguousRuleChoiceException(Throwable throwable){ + super(throwable); + } } diff --git a/aai-schema-ingest/src/main/java/org/onap/aai/edges/exceptions/EdgeRuleNotFoundException.java b/aai-schema-ingest/src/main/java/org/onap/aai/edges/exceptions/EdgeRuleNotFoundException.java index 4d339de2..de022bc5 100644 --- a/aai-schema-ingest/src/main/java/org/onap/aai/edges/exceptions/EdgeRuleNotFoundException.java +++ b/aai-schema-ingest/src/main/java/org/onap/aai/edges/exceptions/EdgeRuleNotFoundException.java @@ -1,4 +1,4 @@ -/** +/** * ============LICENSE_START======================================================= * org.onap.aai * ================================================================================ @@ -24,4 +24,8 @@ public class EdgeRuleNotFoundException extends Exception { public EdgeRuleNotFoundException(String msg) { super(msg); } + + public EdgeRuleNotFoundException(Throwable throwable){ + super(throwable); + } } diff --git a/aai-schema/src/main/resources/onap/oxm/v15/aai_oxm_v15.xml b/aai-schema/src/main/resources/onap/oxm/v15/aai_oxm_v15.xml index f068b009..ce7f2d52 100644 --- a/aai-schema/src/main/resources/onap/oxm/v15/aai_oxm_v15.xml +++ b/aai-schema/src/main/resources/onap/oxm/v15/aai_oxm_v15.xml @@ -5704,8 +5704,8 @@ <java-type name="ReservedPropNames"> <xml-properties> <xml-property name="description" value="Internal map to define some reserved properties of a vertex"/> - <xml-property name="uniqueProps" value="aai-unique-key"/> - <xml-property name="indexedProps" value="aai-unique-key,source-of-truth,aai-node-type,aai-uri"/> + <xml-property name="uniqueProps" value="aai-unique-key,aai-uri,aai-uuid"/> + <xml-property name="indexedProps" value="aai-unique-key,source-of-truth,aai-node-type,aai-uri,aai-uuid"/> </xml-properties> <xml-root-element name="reserved-prop-names"/> <java-attributes> |