aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/openecomp/aai/champ/ChampAPI.java51
-rw-r--r--src/main/java/org/openecomp/aai/champ/ChampCapabilities.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/ChampGraph.java306
-rw-r--r--src/main/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraph.java496
-rw-r--r--src/main/java/org/openecomp/aai/champ/event/ChampEvent.java186
-rw-r--r--src/main/java/org/openecomp/aai/champ/exceptions/ChampIndexNotExistsException.java41
-rw-r--r--src/main/java/org/openecomp/aai/champ/exceptions/ChampMarshallingException.java41
-rw-r--r--src/main/java/org/openecomp/aai/champ/exceptions/ChampObjectNotExistsException.java41
-rw-r--r--src/main/java/org/openecomp/aai/champ/exceptions/ChampRelationshipNotExistsException.java41
-rw-r--r--src/main/java/org/openecomp/aai/champ/exceptions/ChampSchemaViolationException.java41
-rw-r--r--src/main/java/org/openecomp/aai/champ/exceptions/ChampUnmarshallingException.java41
-rw-r--r--src/main/java/org/openecomp/aai/champ/graph/impl/AbstractGremlinChampGraph.java350
-rw-r--r--src/main/java/org/openecomp/aai/champ/graph/impl/AbstractTinkerpopChampGraph.java753
-rw-r--r--src/main/java/org/openecomp/aai/champ/graph/impl/AbstractValidatingChampGraph.java176
-rw-r--r--src/main/java/org/openecomp/aai/champ/graph/impl/ChampAPIImpl.java87
-rw-r--r--src/main/java/org/openecomp/aai/champ/graph/impl/InMemoryChampGraphImpl.java182
-rw-r--r--src/main/java/org/openecomp/aai/champ/graph/impl/TitanChampGraphImpl.java461
-rw-r--r--src/main/java/org/openecomp/aai/champ/ie/Exporter.java31
-rw-r--r--src/main/java/org/openecomp/aai/champ/ie/GraphMLImporterExporter.java460
-rw-r--r--src/main/java/org/openecomp/aai/champ/ie/Importer.java31
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampCardinality.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampConnectionConstraint.java96
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampConnectionMultiplicity.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampElement.java31
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampField.java129
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampObject.java235
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampObjectConstraint.java107
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampObjectIndex.java87
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampPartition.java144
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampPropertyConstraint.java110
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampRelationship.java217
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampRelationshipConstraint.java139
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampRelationshipIndex.java87
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/ChampSchema.java148
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/BuildStep.java26
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/KeyStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/PropertiesStep.java30
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateObjectIndexable.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateRelationshipIndexable.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexFieldStep.java30
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexTypeStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexFieldStep.java30
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexTypeStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateObjectIndexableImpl.java77
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateRelationshipIndexableImpl.java58
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/object/CreateChampObjectable.java30
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStep.java30
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStepImpl.java53
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectKeyStepImpl.java47
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/object/impl/CreateChampObjectableImpl.java42
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/partition/CreateChampPartitionable.java34
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/partition/impl/CreateChampPartionableImpl.java53
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/CreateChampRelationshipable.java30
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/RelationshipBuildOrPropertiesStep.java29
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceBuildOrPropertiesStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceFromStep.java29
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceKeyStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceStep.java26
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeOrFromStep.java26
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetBuildOrPropertiesStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetFromStep.java29
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetKeyStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetStep.java26
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeOrFromStep.java26
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/ChampRelationshipKeyStepImpl.java53
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/CreateChampRelationshipableImpl.java53
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/RelationshipBuildOrPropertiesStepImpl.java54
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceBuildOrPropertiesStepImpl.java55
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceKeyStepImpl.java56
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceStepImpl.java45
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceTypeOrFromStepImpl.java53
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetBuildOrPropertiesStepImpl.java65
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetKeyStepImpl.java56
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetStepImpl.java49
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetTypeOrFromStepImpl.java55
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/CreateChampSchemable.java31
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintBuildStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldTypeStep.java29
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintPropertyStep.java29
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintRequiredOptionalStep.java31
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintSubStep.java26
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintTypeStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintBuildStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldTypeStep.java29
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintMultiplicityStep.java30
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintPropertyOptionalsStep.java25
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintRequiredOptionalStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSourceStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSubStep.java30
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTargetStep.java28
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTypeStep.java27
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/CreateChampSchemableImpl.java74
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintBuildStepImpl.java43
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintFieldStepImpl.java45
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintPropertyStepImpl.java50
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintSubStepImpl.java111
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintBuildStepImpl.java45
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintPropertyOptionalsStepImpl.java68
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintRequiredOptionalStepImpl.java64
-rw-r--r--src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintSubStepImpl.java205
-rw-r--r--src/main/java/org/openecomp/aai/champ/perf/ChampAPIPerformanceTest.java430
-rw-r--r--src/main/java/org/openecomp/aai/champ/schema/AlwaysValidChampSchemaEnforcer.java45
-rw-r--r--src/main/java/org/openecomp/aai/champ/schema/ChampSchemaEnforcer.java37
-rw-r--r--src/main/java/org/openecomp/aai/champ/schema/DefaultChampSchemaEnforcer.java144
-rw-r--r--src/main/java/org/openecomp/aai/champ/transform/Champformer.java34
-rw-r--r--src/main/java/org/openecomp/aai/champ/transform/TinkerpopChampformer.java94
-rw-r--r--src/test/java/org/openecomp/aai/champ/concurrency/ConcurrencyTest.java72
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/BaseChampAPITest.java50
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampAPITest.java239
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampElementTest.java62
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampFieldTest.java53
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampObjectIndexTest.java142
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampObjectTest.java331
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampPartitionTest.java195
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampPropertyConstraintTest.java57
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampRelationshipIndexTest.java175
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampRelationshipTest.java261
-rw-r--r--src/test/java/org/openecomp/aai/champ/core/ChampSchemaTest.java761
-rw-r--r--src/test/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraphTest.java1029
-rw-r--r--src/test/java/org/openecomp/aai/champ/exceptions/ChampExceptionTest.java150
-rw-r--r--src/test/java/org/openecomp/aai/champ/ie/ExportTest.java42
-rw-r--r--src/test/java/org/openecomp/aai/champ/ie/ImportTest.java93
-rwxr-xr-xsrc/test/resources/import-test.graphml49
-rw-r--r--src/test/resources/logback.xml43
128 files changed, 12887 insertions, 0 deletions
diff --git a/src/main/java/org/openecomp/aai/champ/ChampAPI.java b/src/main/java/org/openecomp/aai/champ/ChampAPI.java
new file mode 100644
index 0000000..1eb3758
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/ChampAPI.java
@@ -0,0 +1,51 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ;
+
+import org.openecomp.aai.champ.graph.impl.ChampAPIImpl;
+
+public interface ChampAPI {
+
+ /**
+ * A factory for constructing basic ChampAPI implementations (minimal).
+ * If finer control is needed, you should consider accessing an implementation's
+ * constructors/builders.
+ */
+ public static final class Factory {
+ private Factory() { throw new RuntimeException("Cannot instantiate ChampAPI.Factory"); }
+
+ public static ChampAPI newInstance(ChampGraph.Type type) {
+ return new ChampAPIImpl(type);
+ }
+ }
+
+ public ChampGraph getGraph(String graphName);
+
+ public ChampGraph.Type getType();
+
+ /**
+ * Shutdown the ChampAPI. It is up to the caller to synchronize access to the ChampAPI
+ * so that shutting it down does not interfere with concurrent operations.
+ */
+ public void shutdown();
+}
+
diff --git a/src/main/java/org/openecomp/aai/champ/ChampCapabilities.java b/src/main/java/org/openecomp/aai/champ/ChampCapabilities.java
new file mode 100644
index 0000000..31624c7
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/ChampCapabilities.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ;
+
+public interface ChampCapabilities {
+
+ public boolean canDeleteObjectIndices();
+ public boolean canDeleteRelationshipIndices();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/ChampGraph.java b/src/main/java/org/openecomp/aai/champ/ChampGraph.java
new file mode 100644
index 0000000..20b9a17
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/ChampGraph.java
@@ -0,0 +1,306 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.InMemoryChampGraphImpl;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public interface ChampGraph {
+
+ /**
+ * Types that the Factory is capable of constructing
+ */
+ public enum Type {
+ IN_MEMORY,
+ TITAN/*,
+ DSE //DSE is still in beta, so leave it out for now
+ */
+ }
+
+ /**
+ * A factory for constructing basic ChampAPI implementations (minimal).
+ * If finer control is needed, you should consider accessing an implementation's
+ * constructors/builders.
+ */
+ public static class Factory {
+ public static ChampGraph newInstance(ChampGraph.Type type, String graphName) {
+ switch (type) {
+ case IN_MEMORY:
+ return new InMemoryChampGraphImpl.Builder().build();
+ case TITAN:
+ return new TitanChampGraphImpl.Builder(graphName)
+ .property("storage.backend", "inmemory")
+ .build();
+ /*
+ case DSE: //See above, DSE still in beta
+ return new DseChampGraphImpl.Builder("localhost").build();
+ */
+ default:
+ throw new RuntimeException("Unknown type of ChampAPI implementation");
+ }
+ }
+ }
+
+ /**
+ * Create/Update an object. If the ChampObject key is present, an update will be attempted,
+ * otherwise a create will be attempted. Each implementation has different guarantees on
+ * validation - see the specific implementation for more details on this.
+ * @param object - The ChampObject that you wish to store in the graph
+ * @return The ChampObject as it was stored
+ * @throws ChampMarshallingException If the {@code object} is not able to be marshalled into the backend representation
+ * @throws ChampSchemaViolationException If the {@code object} violates the constraints specifed by {@link ChampGraph#retrieveSchema}
+ * @throws ChampObjectNotExistsException If {@link org.openecomp.aai.champ.model.ChampObject#getKey}.isPresent() but the object cannot be found in the graph
+ */
+ public ChampObject storeObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException;
+
+ /**
+ * Replace an object. ChampObject key is mandatory
+ * Each implementation has different guarantees on
+ * validation - see the specific implementation for more details on this.
+ * @param object - The ChampObject that you wish to replace in the graph
+ * @return The ChampObject as it was stored
+ * @throws ChampMarshallingException If the {@code object} is not able to be marshalled into the backend representation
+ * @throws ChampSchemaViolationException If the {@code object} violates the constraints specifed by {@link ChampGraph#retrieveSchema}
+ * @throws ChampObjectNotExistsException If {@link org.openecomp.aai.champ.model.ChampObject#getKey} is not present or object not found in the graph
+ */
+ public ChampObject replaceObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException;
+
+ /**
+ * Retrieve an object by its key.
+ * @param key The key of the ChampObject in the graph {@link org.openecomp.aai.champ.model.ChampObject#getKey()}
+ * @return The {@link org.openecomp.aai.champ.model.ChampObject} if it was present, otherwise {@link Optional#empty()}
+ * @throws ChampUnmarshallingException If the object was found, but could not be unmarshalled
+ */
+ public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException;
+
+ /**
+ * Delete an object by its key.
+ * @param key The key of the ChampObject in the graph {@link ChampObject#getKey}
+ * @throws ChampObjectNotExistsException If the object did not exist in the graph
+ */
+ public void deleteObject(Object key) throws ChampObjectNotExistsException;
+
+ /**
+ * Retrieve all the objects whose properties match the given {@code queryParams}
+ * @param queryParams The key/value pairs which are found in {@link ChampObject#getProperties}
+ * @return A {@link Stream} where each {@link ChampObject#getProperties} contains the {@code queryParams}
+ */
+ public Stream<ChampObject> queryObjects(Map<String, Object> queryParams);
+
+ /**
+ * Create/Update a relationship. If the ChampRelationship key is present, an update will be attempted,
+ * otherwise a create will be attempted. Each implementation has different guarantees on
+ * validation - see the specific implementation for more details on this.
+ * @param relationship - The ChampRelationship that you wish to store in the graph
+ * @return The ChampRelationship as it was stored
+ * @throws ChampMarshallingException If the {@code relationship} is not able to be marshalled into the backend representation
+ * @throws ChampSchemaViolationException If the {@code relationship} violates the constraints specifed by {@link ChampGraph#retrieveSchema}
+ * @throws ChampObjectNotExistsException If either the source or target object referenced by this relationship does not exist in the graph
+ * @throws ChampRelationshipNotExistsException If {@link org.openecomp.aai.champ.model.ChampRelationship#getKey}.isPresent() but the object cannot be found in the graph
+ * @throws ChampUnmarshallingException If the edge which was created could not be unmarshalled into a ChampRelationship
+ */
+ public ChampRelationship storeRelationship(ChampRelationship relationship) throws ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampUnmarshallingException;
+
+ /**
+ * Replace a relationship. ChampRelationship key is mandatory .The main purpose of this method is to replace the entire properties of an existing relationship .Source/Target can't be updated with this method
+ * Each implementation has different guarantees on
+ * validation - see the specific implementation for more details on this.
+ * @param relationship - The ChampRelationship that you wish to replace in the graph
+ * @return The ChampRelationship as it was stored
+ * @throws ChampMarshallingException If the {@code relationship} is not able to be marshalled into the backend representation
+ * @throws ChampSchemaViolationException If the {@code relationship} violates the constraints specifed by {@link ChampGraph#retrieveSchema}
+ * @throws ChampRelationshipNotExistsException If {@link org.openecomp.aai.champ.model.ChampRelationship#getKey} is not present or object not found in the graph
+ * @throws ChampUnmarshallingException If the edge which was created could not be unmarshalled into a ChampRelationship
+ */
+ public ChampRelationship replaceRelationship(ChampRelationship relationship) throws ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampUnmarshallingException;
+
+
+ /**
+ * Retrieve a relationship by its key.
+ * @param key The key of the ChampRelationship in the graph {@link org.openecomp.aai.champ.model.ChampRelationship#getKey()}
+ * @return The {@link org.openecomp.aai.champ.model.ChampRelationship} if it was present, otherwise {@link Optional#empty()}
+ * @throws ChampUnmarshallingException If the relationship was found, but could not be unmarshalled
+ */
+ public Optional<ChampRelationship> retrieveRelationship(Object key) throws ChampUnmarshallingException;
+
+ /**
+ * Delete a relationship by its key.
+ * @param relationship The ChampRelationship in the graph ({@link ChampRelationship#getKey must be present})
+ * @throws ChampRelationshipNotExistsException If the object did not exist in the graph
+ */
+ public void deleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException;
+
+ /**
+ * Retrieve the relationships which are incident to the {@code object}
+ * @param object The object you wish to find incident relationships for
+ * @return A {@link Stream} where each {@link ChampRelationship} has this {@code object} as either a source or target object
+ * @throws ChampUnmarshallingException If any of the ChampRelationship objects could not be unmarshalled
+ * @throws ChampObjectNotExistsException If the {@code object} does not exist in this graph
+ */
+ public Stream<ChampRelationship> retrieveRelationships(ChampObject object) throws ChampUnmarshallingException, ChampObjectNotExistsException;
+
+ /**
+ * Retrieve the relationships whose properties match the given {@code queryParams}
+ * @param queryParams The key/value pairs to search for in the {@link ChampRelationship#getProperties}
+ * @return A {@link Stream} where each {@link ChampRelationship#getProperties} contains the {@code queryParams}
+ */
+ public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams);
+
+ /**
+ * Create/Update a {@link ChampPartition}. If any of the ChampObjects or ChampRelationships
+ * present in this ChampPartition already exist, an update will be attempted, otherwise a create
+ * will be attempted. Each implementation has different guarantees on validation -
+ * see the specific implementation details for more information on this.
+ * @param partition The ChampPartition you wish to store in this graph
+ * @throws ChampMarshallingException If any of the objects or relationships contained in this
+ * partition could not be marshalled into its backed representation
+ * @throws ChampObjectNotExistsException If any of the objects being updated do not exist, or if a relationship
+ * contain objects which do not exist in the graph.
+ * @throws ChampSchemaViolationException If any of the objects or relationships violate the schema provided by {@link retrieveSchema}
+ * @throws ChampRelationshipNotExistsException If any of the relationships which are being updated do not exist
+ * @return The ChampPartition as is was stored in the graph (contains keys for each newly created object)
+ */
+ public ChampPartition storePartition(ChampPartition partition) throws ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException;
+
+ /**
+ * Delete the {@code partition} from the graph
+ * @param partition The partition to delete from the graph
+ */
+ public void deletePartition(ChampPartition partition);
+
+ /**
+ * Create/Update an object index on the graph
+ * @param index The index to create on this {@code graph}
+ */
+ public void storeObjectIndex(ChampObjectIndex index);
+
+ /**
+ * Retrieve an object index on the graph by its {@code indexName}
+ * @param indexName The name of the index to retrieve from the graph
+ * @return The {@link ChampObjectIndex} which matches the given @{code indexName} in the graph
+ */
+ public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName);
+
+ /**
+ * Retrieve the object indices on the graph
+ * @return A {@link Stream} where each {@link ChampObjectIndex} exists in the graph
+ */
+ public Stream<ChampObjectIndex> retrieveObjectIndices();
+
+ /**
+ * Delete the object index on the graph by its {@code indexName}
+ * @param indexName The name of the index to delete from the graph
+ * @throws ChampIndexNotExistsException If an index does not exist with the given {@code indexName} in the graph
+ */
+ public void deleteObjectIndex(String indexName) throws ChampIndexNotExistsException;
+
+ /**
+ * Create/Update a relationship index on the graph
+ * @param index The relationship index to create on the graph
+ */
+ public void storeRelationshipIndex(ChampRelationshipIndex index);
+
+ /**
+ * Retrieve a relationship index from the graph
+ * @param indexName The name of the relationship index to retrieve from the graph
+ * @return The {@link ChampRelationshipIndex} which matches the given {@code indexName} in the graph
+ * or {@link Optional#empty} if no such relationship index exists
+ */
+ public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName);
+
+ /**
+ * Retrieve the relationship indices from the graph
+ * @return A {@link Stream} where each {@link ChampRelationshipIndex} exists in the graph
+ */
+ public Stream<ChampRelationshipIndex> retrieveRelationshipIndices();
+
+ /**
+ * Delete a relationship index from the graph
+ * @param indexName THe name of the index to delete from the graph
+ * @throws ChampIndexNotExistsException If an index does not exist with the give {@code indexName} in the graph
+ */
+ public void deleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException;
+
+ /**
+ * Create/Update the schema for a graph
+ * @param schema The {@link ChampSchema} to create or update on the graph
+ * @throws ChampSchemaViolationException If this schema update would violate the current schema
+ */
+ public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException;
+
+ /**
+ * Retrieve the schema for a graph
+ * @return The {@link ChampSchema} for the graph
+ */
+ public ChampSchema retrieveSchema();
+
+ /**
+ * Create/Update an object constraint on a schema
+ * @param objectConstraint The object constraint you wish to create/update for the graph
+ * @throws ChampSchemaViolationException If this schema update would violate the current schema
+ */
+ public void updateSchema(ChampObjectConstraint objectConstraint) throws ChampSchemaViolationException;
+
+ /**
+ * Create/Update a relationship constraint on a schema
+ * @param schema The relationship constraint you wish to create/update for the graph
+ * @throws ChampSchemaViolationException If this schema update would violate the current schema
+ */
+ public void updateSchema(ChampRelationshipConstraint schema) throws ChampSchemaViolationException;
+
+ /**
+ * Delete the schema for a graph
+ */
+ public void deleteSchema();
+
+ /**
+ * Shutdown the ChampAPI. It is up to the caller to synchronize access to the ChampAPI
+ * so that shutting it down does not interfere with concurrent operations.
+ */
+ public void shutdown();
+
+ /**
+ * Used to determine what the outcome of certain ChampGraph operations will be. For example,
+ * if this graph is not capable of deleting object indices, you can expect those calls to fail.
+ * @see ChampCapabilities
+ * @return What this graph is capable of performing
+ */
+ public ChampCapabilities capabilities();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraph.java b/src/main/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraph.java
new file mode 100644
index 0000000..a8e0e2f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraph.java
@@ -0,0 +1,496 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.event;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.security.GeneralSecurityException;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import org.openecomp.aai.champ.ChampCapabilities;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.event.ChampEvent.ChampOperation;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.att.nsa.cambria.client.CambriaClientBuilders;
+import com.att.nsa.cambria.client.CambriaPublisher;
+
+
+/**
+ * This class provides the hooks to allow Champ operations to be logged to an event
+ * stream.
+ */
+public abstract class AbstractLoggingChampGraph implements ChampGraph {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractLoggingChampGraph.class);
+
+ public abstract ChampObject executeStoreObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException;
+ public abstract ChampObject executeReplaceObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException;
+ public abstract Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException;
+ public abstract void executeDeleteObject(Object key) throws ChampObjectNotExistsException;
+ public abstract Stream<ChampObject> queryObjects(Map<String, Object> queryParams);
+ public abstract ChampRelationship executeStoreRelationship(ChampRelationship relationship) throws ChampUnmarshallingException, ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException;
+ public abstract ChampRelationship executeReplaceRelationship(ChampRelationship relationship) throws ChampUnmarshallingException, ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException;
+ public abstract Optional<ChampRelationship> retrieveRelationship(Object key) throws ChampUnmarshallingException;
+ public abstract void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException;
+ public abstract Stream<ChampRelationship> retrieveRelationships(ChampObject object) throws ChampUnmarshallingException, ChampObjectNotExistsException;
+ public abstract Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams);
+ public abstract ChampPartition executeStorePartition(ChampPartition partition) throws ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampMarshallingException, ChampObjectNotExistsException;
+ public abstract void executeDeletePartition(ChampPartition graph);
+ public abstract void executeStoreObjectIndex(ChampObjectIndex index);
+ public abstract Optional<ChampObjectIndex> retrieveObjectIndex(String indexName);
+ public abstract Stream<ChampObjectIndex> retrieveObjectIndices();
+ public abstract void executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException;
+ public abstract void executeStoreRelationshipIndex(ChampRelationshipIndex index);
+ public abstract Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName);
+ public abstract Stream<ChampRelationshipIndex> retrieveRelationshipIndices();
+ public abstract void executeDeleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException;
+ public abstract void storeSchema(ChampSchema schema) throws ChampSchemaViolationException;
+ public abstract ChampSchema retrieveSchema();
+ public abstract void updateSchema(ChampObjectConstraint objectConstraint) throws ChampSchemaViolationException;
+ public abstract void updateSchema(ChampRelationshipConstraint schema) throws ChampSchemaViolationException;
+ public abstract void deleteSchema();
+ public abstract ChampCapabilities capabilities();
+
+
+ /** Configuration property for setting the comma-separated list of servers to use for
+ * communicating with the event bus. */
+ public final static String PARAM_EVENT_STREAM_HOSTS = "champ.event.stream.hosts";
+
+ /** Configuration property for setting the number of events that we will try to 'batch'
+ * up before sending them to the event bus. */
+ public final static String PARAM_EVENT_STREAM_BATCH_SIZE = "champ.event.stream.batch-size";
+ public final static Integer DEFAULT_EVENT_STREAM_BATCH_SIZE = 1;
+
+ /** Configuration property for setting the maximum amount of time to wait for a batch of
+ * outgoing messages to fill up before sending the batch. */
+ public final static String PARAM_EVENT_STREAM_BATCH_TIMEOUT = "champ.event.stream.batch-timeout";
+ public final static Integer DEFAULT_EVENT_STREAM_BATCH_TIMEOUT_MS = 500;
+
+ public final static String PARAM_EVENT_STREAM_PUBLISHER_POOL_SIZE = "champ.event.stream.publisher-pool-size";
+ public final static Integer DEFAULT_EVENT_STREAM_PUBLISHER_POOL_SIZE = 100;
+
+ /** The event stream topic that we will publish Champ events to. */
+ public final static String EVENT_TOPIC = "champRawEvents";
+
+ /** Number of events to 'batch up' before actually publishing them to the event bus. */
+ private Integer eventStreamBatchSize;
+
+ private Integer eventStreamBatchTimeout;
+
+ private Integer eventStreamPublisherPoolSize;
+
+ /** Comma-separated list of hosts for connecting to the event bus. */
+ private String eventStreamHosts = null;
+
+ /** Client used for publishing messages to the event bus. */
+ protected CambriaPublisher producer;
+
+ /** Pool of worker threads that do the work of publishing the events to the event bus. */
+ protected ThreadPoolExecutor publisherPool;
+
+
+ /**
+ * Create a new instance of the AbstractLoggingChampGraph.
+ *
+ * @param properties - Set of configuration properties for this graph instance.
+ */
+ protected AbstractLoggingChampGraph(Map<String, Object> properties) {
+
+ // Extract the necessary parameters from the configuration properties.
+ configure(properties);
+
+ // Create the executor pool that will do the work of publishing events to the event bus.
+ publisherPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(eventStreamPublisherPoolSize);
+
+ // Make sure that we are actually provided a list of hosts for connecting to the event
+ // bus before we actually try to do anything useful.
+ if(eventStreamHosts == null) {
+
+ // We were not supplied a list of event bus hosts, so just bail.
+ logger.error("Cannot initialize event stream publisher without at least one event bus host.");
+ logger.error("NOTE!! Champ events will NOT be published to the event stream!");
+ return;
+ }
+
+ try {
+
+ // Instantiate the producer that we will use to publish events to the event stream.
+ setProducer(new CambriaClientBuilders.PublisherBuilder()
+ .usingHosts(eventStreamHosts)
+ .onTopic(EVENT_TOPIC)
+ .limitBatch(eventStreamBatchSize, eventStreamBatchTimeout)
+ .build());
+
+ } catch (MalformedURLException | GeneralSecurityException e) {
+
+ logger.error("Could not instantiate event stream producer due to: " + e.getMessage());
+ logger.error("NOTE: Champ events will NOT be published to the event stream");
+ producer = null;
+ }
+ }
+
+
+ /**
+ * Process the configuration properties supplied for this graph instance.
+ *
+ * @param properties - Configuration parameters.
+ */
+ private void configure(Map<String, Object> properties) {
+
+ eventStreamBatchSize =
+ (Integer) getProperty(properties, PARAM_EVENT_STREAM_BATCH_SIZE, DEFAULT_EVENT_STREAM_BATCH_SIZE);
+ eventStreamBatchTimeout =
+ (Integer) getProperty(properties, PARAM_EVENT_STREAM_BATCH_TIMEOUT, DEFAULT_EVENT_STREAM_BATCH_TIMEOUT_MS);
+ eventStreamPublisherPoolSize =
+ (Integer) getProperty(properties, PARAM_EVENT_STREAM_PUBLISHER_POOL_SIZE, DEFAULT_EVENT_STREAM_PUBLISHER_POOL_SIZE);
+
+ if(properties.containsKey(PARAM_EVENT_STREAM_HOSTS)) {
+ eventStreamHosts = (String) properties.get(PARAM_EVENT_STREAM_HOSTS);
+ }
+ }
+
+ public void setProducer(CambriaPublisher aProducer) {
+
+ producer = aProducer;
+ }
+
+ private Object getProperty(Map<String, Object> properties, String property, Object defaultValue) {
+
+ if(properties.containsKey(property)) {
+ return properties.get(property);
+ } else {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public void shutdown() {
+
+ if(publisherPool != null) {
+ publisherPool.shutdown();
+
+ try {
+ publisherPool.awaitTermination(1000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {}
+ }
+
+ if(producer != null) {
+ producer.close();
+ }
+ }
+
+ public ChampObject storeObject(ChampObject object) throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException {
+
+ ChampObject storedObject = executeStoreObject(object);
+
+ if(storedObject != null) {
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.STORE)
+ .entity(storedObject)
+ .build());
+ }
+
+ return storedObject;
+ }
+
+
+ public ChampObject replaceObject(ChampObject object)
+ throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException {
+
+ ChampObject replacedObject = executeReplaceObject(object);
+
+ if(replacedObject != null) {
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.REPLACE)
+ .entity(replacedObject)
+ .build());
+ }
+
+ return replacedObject;
+ }
+
+
+ public void deleteObject(Object key) throws ChampObjectNotExistsException {
+
+ // Retrieve the object that we are deleting before it's gone, so that we can
+ // report it to the event stream.
+ Optional<ChampObject> objectToDelete = Optional.empty();
+ try {
+ objectToDelete = retrieveObject(key);
+
+ } catch (ChampUnmarshallingException e) {
+ logger.error("Unable to generate delete object log: " + e.getMessage());
+ }
+
+ executeDeleteObject(key);
+
+ if(objectToDelete.isPresent()) {
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.DELETE)
+ .entity(objectToDelete.get())
+ .build());
+ }
+ }
+
+
+ public ChampRelationship storeRelationship(ChampRelationship relationship)
+ throws ChampUnmarshallingException, ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException {
+
+ ChampRelationship storedRelationship = executeStoreRelationship(relationship);
+
+ if(storedRelationship != null) {
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.STORE)
+ .entity(storedRelationship)
+ .build());
+ }
+
+ return storedRelationship;
+ }
+
+
+ public ChampRelationship replaceRelationship(ChampRelationship relationship)
+ throws ChampUnmarshallingException, ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException {
+
+ ChampRelationship replacedRelationship = executeReplaceRelationship(relationship);
+
+ if(replacedRelationship != null) {
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.REPLACE)
+ .entity(replacedRelationship)
+ .build());
+ }
+
+ return replacedRelationship;
+ }
+
+
+ public void deleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
+
+ executeDeleteRelationship(relationship);
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.DELETE)
+ .entity(relationship)
+ .build());
+ }
+
+
+ public ChampPartition storePartition(ChampPartition partition) throws ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampMarshallingException, ChampObjectNotExistsException {
+
+ ChampPartition storedPartition = executeStorePartition(partition);
+
+ if(storedPartition != null) {
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.STORE)
+ .entity(storedPartition)
+ .build());
+ }
+
+ return storedPartition;
+ }
+
+
+ public void deletePartition(ChampPartition graph) {
+
+ executeDeletePartition(graph);
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.DELETE)
+ .entity(graph)
+ .build());
+ }
+
+
+ public void storeObjectIndex(ChampObjectIndex index) {
+
+ executeStoreObjectIndex(index);
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.STORE)
+ .entity(index)
+ .build());
+ }
+
+
+ public void deleteObjectIndex(String indexName) throws ChampIndexNotExistsException {
+
+ // Retrieve the index that we are deleting before it's gone, so that we can
+ // report it to the event stream.
+ Optional<ChampObjectIndex> indexToDelete = retrieveObjectIndex(indexName);
+
+ executeDeleteObjectIndex(indexName);
+
+ if(indexToDelete.isPresent()) {
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.DELETE)
+ .entity(indexToDelete.get())
+ .build());
+ }
+ }
+
+
+ public void storeRelationshipIndex(ChampRelationshipIndex index) {
+
+ executeStoreRelationshipIndex(index);
+
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.STORE)
+ .entity(index)
+ .build());
+ }
+
+
+ public void deleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException {
+
+ // Retrieve the index that we are deleting before it's gone, so that we can
+ // report it to the event stream.
+ Optional<ChampRelationshipIndex> indexToDelete = retrieveRelationshipIndex(indexName);
+
+ executeDeleteRelationshipIndex(indexName);
+
+ if(indexToDelete.isPresent()) {
+ // Update the event stream with the current operation.
+ logEvent(ChampEvent.builder()
+ .operation(ChampOperation.DELETE)
+ .entity(indexToDelete.get())
+ .build());
+ }
+ }
+
+
+ /**
+ * Submits an event to be published to the event stream.
+ *
+ * @param anEvent - The event to be published.
+ */
+ public void logEvent(ChampEvent anEvent) {
+
+ if(logger.isDebugEnabled()) {
+ logger.debug("Submitting event to be published to the event bus: " + anEvent.toString());
+ }
+
+ try {
+
+ // Try submitting the event to be published to the event bus.
+ publisherPool.execute(new EventPublisher(anEvent));
+
+ } catch (RejectedExecutionException re) {
+ logger.error("Event could not be published to the event bus due to: " + re.getMessage());
+
+ } catch (NullPointerException npe) {
+ logger.error("Can not publish null event to event bus.");
+ }
+ }
+
+
+ /**
+ * This class runs as a background thread and is responsible for pulling Champ events off
+ * of the internal queue and publishing them to the event stream.
+ */
+ private class EventPublisher implements Runnable {
+
+ /** Partition key to use when publishing events to the event stream. We WANT all events
+ * to go to a single partition, so we are just using a hard-coded key for every event. */
+ private static final String EVENTS_PARTITION_KEY = "champEventKey";
+
+ private ChampEvent event;
+
+ public EventPublisher(ChampEvent event) {
+ this.event = event;
+ }
+
+
+ @Override
+ public void run() {
+
+ boolean done = false;
+ while(!done && !Thread.currentThread().isInterrupted()) {
+ try {
+
+ // Make sure that we actually have a producer instance to use to publish
+ // the event with.
+ if(producer != null) {
+
+ // Try publishing the event to the event bus.
+ producer.send(EVENTS_PARTITION_KEY, event.toJson());
+
+ } else if (logger.isDebugEnabled()) {
+ logger.debug("Event bus producer is not instantiated - will not attempt to publish event");
+ }
+
+ done = true;
+
+ } catch (IOException e) {
+
+ // We were unable to publish to the event bus, so wait a bit and then try
+ // again.
+ try {
+ Thread.sleep(500);
+
+ } catch (InterruptedException e1) {
+ logger.info("Stopping event publisher worker thread.");
+ return;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/event/ChampEvent.java b/src/main/java/org/openecomp/aai/champ/event/ChampEvent.java
new file mode 100644
index 0000000..f56601d
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/event/ChampEvent.java
@@ -0,0 +1,186 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.event;
+
+
+import java.io.IOException;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+
+public class ChampEvent {
+
+ public enum ChampOperation {
+ STORE,
+ REPLACE,
+ DELETE
+ }
+
+ private static ObjectMapper mapper = new ObjectMapper();
+
+ private ChampOperation operation;
+ private long timestamp;
+ private ChampObject vertex = null;
+ private ChampRelationship relationship = null;
+ private ChampPartition partition = null;
+ private ChampObjectIndex objectIndex = null;
+ private ChampRelationshipIndex relationshipIndex = null;
+
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public ChampOperation getOperation() {
+ return operation;
+ }
+
+ public void setOperation(ChampOperation operation) {
+ this.operation = operation;
+ }
+
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public ChampObject getVertex() {
+ return vertex;
+ }
+
+ public void setVertex(ChampObject vertex) {
+ this.vertex = vertex;
+ }
+
+ public ChampRelationship getRelationship() {
+ return relationship;
+ }
+
+ public void setRelationship(ChampRelationship relationship) {
+ this.relationship = relationship;
+ }
+
+ public ChampPartition getPartition() {
+ return partition;
+ }
+
+ public void setPartition(ChampPartition partition) {
+ this.partition = partition;
+ }
+
+ public ChampObjectIndex getObjectIndex() {
+ return objectIndex;
+ }
+
+ public void setObjectIndex(ChampObjectIndex index) {
+ this.objectIndex = index;
+ }
+
+ public ChampRelationshipIndex getRelationshipIndex() {
+ return relationshipIndex;
+ }
+
+ public void setRelationshipIndex(ChampRelationshipIndex relationshipIndex) {
+ this.relationshipIndex = relationshipIndex;
+ }
+
+ public String toJson() {
+
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.setSerializationInclusion(Include.NON_NULL);
+
+ try {
+ return mapper.writeValueAsString(this);
+ } catch (JsonProcessingException e) {
+ return "Unmarshallable: " + e.getMessage();
+ }
+ }
+
+ public static ChampEvent fromJson(String json) throws JsonParseException, JsonMappingException, IOException {
+
+ mapper.setSerializationInclusion(Include.NON_NULL);
+ return mapper.readValue(json, ChampEvent.class);
+ }
+ @Override
+ public String toString() {
+ return toJson();
+ }
+
+ public static class Builder {
+
+ ChampEvent event = null;
+
+
+ public Builder() {
+ event = new ChampEvent();
+ }
+
+ public Builder operation(ChampOperation operation) {
+ event.setOperation(operation);
+ return this;
+ }
+
+ public Builder entity(ChampObject entity) {
+ event.setVertex(entity);
+ return this;
+ }
+
+ public Builder entity(ChampRelationship relationship) {
+ event.relationship = relationship;
+ return this;
+ }
+
+ public Builder entity(ChampPartition partition) {
+ event.partition = partition;
+ return this;
+ }
+
+ public Builder entity(ChampObjectIndex index) {
+ event.objectIndex = index;
+ return this;
+ }
+
+ public Builder entity(ChampRelationshipIndex relationshipIndex) {
+ event.relationshipIndex = relationshipIndex;
+ return this;
+ }
+
+ public ChampEvent build() {
+
+ event.setTimestamp(System.currentTimeMillis());
+ return event;
+ }
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampIndexNotExistsException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampIndexNotExistsException.java
new file mode 100644
index 0000000..806d91a
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/exceptions/ChampIndexNotExistsException.java
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.exceptions;
+
+public final class ChampIndexNotExistsException extends Exception {
+
+ private static final long serialVersionUID = 1690478892404278379L;
+
+ public ChampIndexNotExistsException() {}
+
+ public ChampIndexNotExistsException(String message) {
+ super(message);
+ }
+
+ public ChampIndexNotExistsException(Throwable cause) {
+ super(cause);
+ }
+
+ public ChampIndexNotExistsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampMarshallingException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampMarshallingException.java
new file mode 100644
index 0000000..33797ab
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/exceptions/ChampMarshallingException.java
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.exceptions;
+
+public final class ChampMarshallingException extends Exception {
+
+ private static final long serialVersionUID = 7962962670920382670L;
+
+ public ChampMarshallingException() {}
+
+ public ChampMarshallingException(String message) {
+ super(message);
+ }
+
+ public ChampMarshallingException(Throwable cause) {
+ super(cause);
+ }
+
+ public ChampMarshallingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampObjectNotExistsException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampObjectNotExistsException.java
new file mode 100644
index 0000000..1651a45
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/exceptions/ChampObjectNotExistsException.java
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.exceptions;
+
+public final class ChampObjectNotExistsException extends Exception {
+
+ private static final long serialVersionUID = -4365365939154593814L;
+
+ public ChampObjectNotExistsException() {}
+
+ public ChampObjectNotExistsException(String message) {
+ super(message);
+ }
+
+ public ChampObjectNotExistsException(Throwable cause) {
+ super(cause);
+ }
+
+ public ChampObjectNotExistsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampRelationshipNotExistsException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampRelationshipNotExistsException.java
new file mode 100644
index 0000000..f4c40d7
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/exceptions/ChampRelationshipNotExistsException.java
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.exceptions;
+
+public final class ChampRelationshipNotExistsException extends Exception {
+
+ private static final long serialVersionUID = -3006050460369110202L;
+
+ public ChampRelationshipNotExistsException() {}
+
+ public ChampRelationshipNotExistsException(String message) {
+ super(message);
+ }
+
+ public ChampRelationshipNotExistsException(Throwable cause) {
+ super(cause);
+ }
+
+ public ChampRelationshipNotExistsException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampSchemaViolationException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampSchemaViolationException.java
new file mode 100644
index 0000000..7521b2f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/exceptions/ChampSchemaViolationException.java
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.exceptions;
+
+public final class ChampSchemaViolationException extends Exception {
+
+ private static final long serialVersionUID = -6650058224577965021L;
+
+ public ChampSchemaViolationException() {}
+
+ public ChampSchemaViolationException(String message) {
+ super(message);
+ }
+
+ public ChampSchemaViolationException(Throwable cause) {
+ super(cause);
+ }
+
+ public ChampSchemaViolationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/exceptions/ChampUnmarshallingException.java b/src/main/java/org/openecomp/aai/champ/exceptions/ChampUnmarshallingException.java
new file mode 100644
index 0000000..619c991
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/exceptions/ChampUnmarshallingException.java
@@ -0,0 +1,41 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.exceptions;
+
+public final class ChampUnmarshallingException extends Exception {
+
+ private static final long serialVersionUID = 8162385108397238865L;
+
+ public ChampUnmarshallingException() {}
+
+ public ChampUnmarshallingException(String message) {
+ super(message);
+ }
+
+ public ChampUnmarshallingException(Throwable cause) {
+ super(cause);
+ }
+
+ public ChampUnmarshallingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractGremlinChampGraph.java b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractGremlinChampGraph.java
new file mode 100644
index 0000000..130693f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractGremlinChampGraph.java
@@ -0,0 +1,350 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.graph.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+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.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl.Builder;
+import org.openecomp.aai.champ.model.ChampElement;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.partition.CreateChampPartitionable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractGremlinChampGraph extends AbstractValidatingChampGraph {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGremlinChampGraph.class);
+
+ protected abstract GraphTraversalSource startTraversal();
+ protected abstract Stream<ChampElement> runTraversal(GraphTraversal<?, ?> traversal);
+
+ protected AbstractGremlinChampGraph(Map<String, Object> properties) {
+ super(properties);
+ }
+
+ @Override
+ public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Stream<ChampElement> elements = runTraversal(startTraversal().V(key).limit(1));
+
+ if (elements.count() == 0) {
+ return Optional.empty();
+ }
+
+ return Optional.of(elements.findFirst().get().asObject());
+ }
+
+ public void executeDeleteObject(Object key) throws ChampObjectNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Stream<ChampElement> elements = runTraversal(startTraversal().V(key).limit(1));
+
+ if (elements.count() == 0) {
+ throw new ChampObjectNotExistsException();
+ }
+
+ runTraversal(startTraversal().V(key).drop());
+ }
+
+ @Override
+ public Stream<ChampObject> queryObjects(Map<String, Object> queryParams) {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ //If they provided the object key, do this the quick way rather than creating a traversal
+ if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString())) {
+ try {
+ final Optional<ChampObject> object = retrieveObject(queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString()));
+
+ if (object.isPresent()) return Stream.of(object.get());
+ else return Stream.empty();
+ } catch (ChampUnmarshallingException e) {
+ LOGGER.warn("Failed to unmarshall object", e);
+ return Stream.empty();
+ }
+ }
+
+ final GraphTraversal<?, Vertex> traversal = startTraversal().V();
+
+ for (Entry<String, Object> filter : queryParams.entrySet()) {
+ if (filter.getKey().equals(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
+ continue; //For performance reasons, the label is the last thing to be added
+ } else {
+ traversal.has(filter.getKey(), filter.getValue());
+ }
+ }
+
+ if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
+ traversal.hasLabel((String) queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString()));
+ }
+
+
+ return runTraversal(traversal).map(element -> {
+ return element.asObject(); //Safe, since we're only operating on vertices
+ });
+ }
+
+ @Override
+ public Optional<ChampRelationship> retrieveRelationship(Object key) throws ChampUnmarshallingException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Stream<ChampElement> elements = runTraversal(startTraversal().E(key).limit(1));
+
+ if (elements.count() == 0) return Optional.empty();
+
+ return Optional.of(elements.findFirst().get().asRelationship());
+ }
+
+ public void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+ if (!relationship.getKey().isPresent()) throw new IllegalArgumentException("Key must be provided when deleting a relationship");
+
+ final Stream<ChampElement> elements = runTraversal(startTraversal().E(relationship.getKey().get()).limit(1));
+
+ if (elements.count() == 0) {
+ throw new ChampRelationshipNotExistsException();
+ }
+
+ runTraversal(startTraversal().E(relationship.getKey().get()).drop());
+ }
+
+ @Override
+ public Stream<ChampRelationship> retrieveRelationships(ChampObject object)
+ throws ChampUnmarshallingException, ChampObjectNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Stream<ChampElement> elements = runTraversal(startTraversal().V(object.getKey().get()).limit(1).bothE());
+
+ return elements.map(element -> {
+ return element.asRelationship(); //Safe, since we're only operating on edges
+ });
+ }
+
+ @Override
+ public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams) {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ //If they provided the relationship key, do this the quick way rather than creating a traversal
+ if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString())) {
+ try {
+ final Optional<ChampRelationship> relationship = retrieveRelationship(queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString()));
+
+ if (relationship.isPresent()) return Stream.of(relationship.get());
+ else return Stream.empty();
+ } catch (ChampUnmarshallingException e) {
+ LOGGER.warn("Failed to unmarshall relationship", e);
+ return Stream.empty();
+ }
+ }
+
+ final GraphTraversal<Edge, Edge> traversal = startTraversal().E();
+
+ for (Entry<String, Object> filter : queryParams.entrySet()) {
+ if (filter.getKey().equals(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
+ continue; //Add the label last for performance reasons
+ } else {
+ traversal.has(filter.getKey(), filter.getValue());
+ }
+ }
+
+ if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
+ traversal.hasLabel((String) queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString()));
+ }
+
+ return runTraversal(traversal).map(element -> {
+ return element.asRelationship(); //Safe, since we are only operating on edges
+ });
+ }
+
+ @Override
+ public void executeDeletePartition(ChampPartition graph) {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Object[] objectIds = graph.getChampObjects()
+ .stream()
+ .filter(o -> o.getKey().isPresent())
+ .map(o -> { return o.getKey().get(); })
+ .collect(Collectors.toList())
+ .toArray();
+
+ final Object[] relationshipIds = graph.getChampRelationships()
+ .stream()
+ .filter(o -> o.getKey().isPresent())
+ .map(o -> { return o.getKey().get(); })
+ .collect(Collectors.toList())
+ .toArray();
+
+ runTraversal(startTraversal().V(objectIds).drop());
+ runTraversal(startTraversal().E(relationshipIds).drop());
+ }
+
+ @Override
+ protected ChampObject doStoreObject(ChampObject object)
+ throws ChampMarshallingException, ChampObjectNotExistsException {
+ final GraphTraversal<Vertex, Vertex> traversal;
+
+ if (object.getKey().isPresent()) {
+ traversal = startTraversal().V(object.getKey().get());
+ } else {
+ traversal = startTraversal().addV(object.getType());
+ }
+
+ for (Entry<String, Object> property : object.getProperties().entrySet()) {
+
+ if (property.getValue() instanceof List) {
+ for (Object subPropertyValue : (List<?>) property.getValue()) {
+ traversal.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
+ }
+ } else if (property.getValue() instanceof Set) {
+ for (Object subPropertyValue : (Set<?>) property.getValue()) {
+ traversal.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
+ }
+ } else {
+ traversal.property(property.getKey(), property.getValue());
+ }
+ }
+
+ return runTraversal(traversal).findFirst().get().asObject(); //TODO check if this return the updated vertices
+ }
+
+ @Override
+ protected ChampObject doReplaceObject(ChampObject object)
+ throws ChampMarshallingException, ChampObjectNotExistsException {
+ //TODO: implement the replace method when required
+ throw new UnsupportedOperationException("Method not implemented");
+ }
+
+ @Override
+ protected ChampRelationship doReplaceRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException, ChampMarshallingException {
+ //TODO: implement the replace method when required
+ throw new UnsupportedOperationException("Method not implemented");
+ }
+
+ @Override
+ protected ChampRelationship doStoreRelationship(ChampRelationship relationship) throws ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException {
+
+ /* FIXME: Only compatible with Tinkerpop 3.2.3 (Titan uses 3.0.1-incubating).
+
+ final GraphTraversal<?, Vertex> sourceBuilder;
+
+ if (relationship.getSource().getKey().isPresent()) {
+ sourceBuilder = startTraversal().V(relationship.getSource().getKey().get()).as("source");
+ } else {
+ sourceBuilder = startTraversal().addV(relationship.getSource().getType());
+ }
+
+ for (Entry<String, Object> sourceProperty : relationship.getSource().getProperties().entrySet()) {
+ sourceBuilder.property(sourceProperty.getKey(), sourceProperty.getValue());
+ }
+
+ final GraphTraversal<?, Vertex> targetBuilder;
+
+ if (relationship.getTarget().getKey().isPresent()) {
+ targetBuilder = sourceBuilder.V(relationship.getTarget().getKey().get()).as("target");
+ } else {
+ targetBuilder = sourceBuilder.addV(relationship.getTarget().getType());
+ }
+
+ for (Entry<String, Object> targetProperty : relationship.getTarget().getProperties().entrySet()) {
+ targetBuilder.property(targetProperty.getKey(), targetProperty.getValue());
+ }
+
+ final GraphTraversal<?, Edge> edgeBuilder = targetBuilder.addE(relationship.getType()).from("source");
+
+ for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
+ edgeBuilder.property(property.getKey(), property.getValue());
+ }
+
+ return runTraversal(edgeBuilder).filter(e -> e.isRelationship()).findFirst().get().asRelationship();
+ */
+
+ throw new UnsupportedOperationException("Cannot store relationships because of project setup (Incompatible Tinkerpop version in use)");
+ }
+
+ @Override
+ protected ChampPartition doStorePartition(ChampPartition submittedPartition) throws ChampObjectNotExistsException, ChampMarshallingException, ChampRelationshipNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ try {
+ final HashMap<ChampObject, ChampObject> objectsWithKeys = new HashMap<ChampObject, ChampObject> ();
+ final CreateChampPartitionable storedPartition = ChampPartition.create();
+
+ for (ChampObject champObject : submittedPartition.getChampObjects()) {
+
+ final ChampObject objectWithKey = doStoreObject(champObject);
+ objectsWithKeys.put(champObject, objectWithKey);
+ }
+
+ for (ChampRelationship champRelationship : submittedPartition.getChampRelationships()) {
+ if (!objectsWithKeys.containsKey(champRelationship.getSource())) {
+ final ChampObject objectWithKey = doStoreObject(champRelationship.getSource());
+
+ objectsWithKeys.put(champRelationship.getSource(), objectWithKey);
+ }
+
+ if (!objectsWithKeys.containsKey(champRelationship.getTarget())) {
+ final ChampObject objectWithKey = doStoreObject(champRelationship.getTarget());
+
+ objectsWithKeys.put(champRelationship.getTarget(), objectWithKey);
+ }
+
+ final ChampRelationship.Builder relWithKeysBuilder = new ChampRelationship.Builder(objectsWithKeys.get(champRelationship.getSource()),
+ objectsWithKeys.get(champRelationship.getTarget()),
+ champRelationship.getType());
+
+ if (champRelationship.getKey().isPresent()) relWithKeysBuilder.key(champRelationship.getKey().get());
+
+ relWithKeysBuilder.properties(champRelationship.getProperties());
+
+ final ChampRelationship relationship = doStoreRelationship(relWithKeysBuilder.build());
+
+ storedPartition.withRelationship(relationship);
+ }
+
+ for (ChampObject object : objectsWithKeys.values()) {
+ storedPartition.withObject(object);
+ }
+
+ return storedPartition.build();
+ } catch (ChampObjectNotExistsException | ChampMarshallingException e) {
+ throw e;
+ }
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractTinkerpopChampGraph.java b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractTinkerpopChampGraph.java
new file mode 100644
index 0000000..8128ec6
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractTinkerpopChampGraph.java
@@ -0,0 +1,753 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.graph.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.aai.champ.event.ChampEvent;
+import org.openecomp.aai.champ.event.ChampEvent.ChampOperation;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl.Builder;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.partition.CreateChampPartitionable;
+import org.openecomp.aai.champ.transform.TinkerpopChampformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public abstract class AbstractTinkerpopChampGraph extends AbstractValidatingChampGraph {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractTinkerpopChampGraph.class);
+ private static final TinkerpopChampformer TINKERPOP_CHAMPFORMER = new TinkerpopChampformer();
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ private static final int COMMIT_RETRY_COUNT = 3;
+
+ private volatile AtomicBoolean isShutdown;
+
+ protected AbstractTinkerpopChampGraph(Map<String, Object> properties) {
+ super(properties);
+
+ isShutdown = new AtomicBoolean(false);
+ Runtime.getRuntime().addShutdownHook(shutdownHook);
+ }
+
+ private static final TinkerpopChampformer getChampformer() {
+ return TINKERPOP_CHAMPFORMER;
+ }
+
+ private static final ObjectMapper getObjectMapper() {
+ return OBJECT_MAPPER;
+ }
+
+ private Vertex writeVertex(ChampObject object) throws ChampObjectNotExistsException, ChampMarshallingException {
+ final Vertex vertex;
+
+ if (object.getKey().isPresent()) {
+ final Iterator<Vertex> vertexIter = getGraph().vertices(object.getKey().get());
+
+ if (vertexIter.hasNext()) {
+ vertex = vertexIter.next();
+ } else throw new ChampObjectNotExistsException();
+ } else {
+ vertex = getGraph().addVertex(object.getType());
+ }
+
+ for (Entry<String, Object> property : object.getProperties().entrySet()) {
+
+ if (property.getValue() instanceof List) {
+ for (Object subPropertyValue : (List<?>) property.getValue()) {
+ vertex.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
+ }
+ } else if (property.getValue() instanceof Set) {
+ for (Object subPropertyValue : (Set<?>) property.getValue()) {
+ vertex.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
+ }
+ } else {
+ vertex.property(property.getKey(), property.getValue());
+ }
+ }
+
+ return vertex;
+ }
+
+ private Vertex replaceVertex(ChampObject object) throws ChampObjectNotExistsException, ChampMarshallingException {
+ Vertex vertex;
+
+ if (object.getKey().isPresent()) {
+ final Iterator<Vertex> vertexIter = getGraph().vertices(object.getKey().get());
+
+ if (vertexIter.hasNext()) {
+ vertex = vertexIter.next();
+ } else throw new ChampObjectNotExistsException();
+ } else {
+ throw new ChampObjectNotExistsException();
+ }
+
+ //clear all the existing properties
+ Iterator<VertexProperty<Object>> it = vertex.properties();
+ while (it.hasNext()) {
+ it.next().remove();
+ }
+
+ for (Entry<String, Object> property : object.getProperties().entrySet()) {
+
+ if (property.getValue() instanceof List) {
+ for (Object subPropertyValue : (List<?>) property.getValue()) {
+ vertex.property(VertexProperty.Cardinality.list, property.getKey(), subPropertyValue);
+ }
+ } else if (property.getValue() instanceof Set) {
+ for (Object subPropertyValue : (Set<?>) property.getValue()) {
+ vertex.property(VertexProperty.Cardinality.set, property.getKey(), subPropertyValue);
+ }
+ } else {
+ vertex.property(property.getKey(), property.getValue());
+ }
+ }
+
+ return vertex;
+ }
+
+ private Edge writeEdge(ChampRelationship relationship) throws ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException {
+
+ final Vertex source = writeVertex(relationship.getSource());
+ final Vertex target = writeVertex(relationship.getTarget());
+ final Edge edge;
+
+ if (relationship.getKey().isPresent()) {
+ final Iterator<Edge> edgeIter = getGraph().edges(relationship.getKey().get());
+
+ if (edgeIter.hasNext()) {
+ edge = edgeIter.next();
+ } else throw new ChampRelationshipNotExistsException();
+ } else {
+ edge = source.addEdge(relationship.getType(), target);
+ }
+
+ for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
+ edge.property(property.getKey(), property.getValue());
+ }
+
+ return edge;
+ }
+
+ private Edge replaceEdge(ChampRelationship relationship) throws ChampRelationshipNotExistsException, ChampMarshallingException {
+ final Edge edge;
+
+ if(!relationship.getSource().getKey().isPresent() || !relationship.getTarget().getKey().isPresent()){
+ throw new IllegalArgumentException("Invalid source/target");
+ }
+
+ if (relationship.getKey().isPresent()) {
+ final Iterator<Edge> edgeIter = getGraph().edges(relationship.getKey().get());
+
+ if (edgeIter.hasNext()) {
+ edge = edgeIter.next();
+ //validate if the source/target are the same as before. Throw error if not the same
+ if (!edge.outVertex().id().equals(relationship.getSource().getKey().get())
+ || !edge.inVertex().id().equals(relationship.getTarget().getKey().get())) {
+ throw new IllegalArgumentException("source/target can't be updated");
+ }
+
+ } else throw new ChampRelationshipNotExistsException();
+ } else {
+ throw new ChampRelationshipNotExistsException();
+ }
+
+ // clear all the existing properties
+ Iterator<Property<Object>> it = edge.properties();
+ while (it.hasNext()) {
+ it.next().remove();
+ }
+
+ for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
+ edge.property(property.getKey(), property.getValue());
+ }
+
+ return edge;
+ }
+
+ private void tryRollback() {
+ if (getGraph().features().graph().supportsTransactions()) {
+ getGraph().tx().rollback();
+ }
+ }
+
+ private void tryCommit() {
+
+ if (getGraph().features().graph().supportsTransactions()) {
+
+ final long initialBackoff = (int) (Math.random() * 50);
+
+ for (int i = 0; i < COMMIT_RETRY_COUNT; i++) {
+ try {
+ getGraph().tx().commit();
+ return;
+ } catch (Throwable e) {
+ if (i == COMMIT_RETRY_COUNT - 1) {
+ LOGGER.error("Maxed out commit attempt retries, client must handle exception and retry", e);
+ getGraph().tx().rollback();
+ throw e;
+ }
+
+ final long backoff = (long) Math.pow(2, i) * initialBackoff;
+ LOGGER.warn("Caught exception while retrying transaction commit, retrying in " + backoff + " ms");
+
+ try {
+ Thread.sleep(backoff);
+ } catch (InterruptedException ie) {
+ LOGGER.info("Interrupted while backing off on transaction commit");
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ protected abstract Graph getGraph();
+
+ private Thread shutdownHook = new Thread() {
+ @Override
+ public void run() {
+ try {
+ shutdown();
+ } catch (IllegalStateException e) {
+ //Suppress, because shutdown() has already been called
+ }
+ }
+ };
+
+ protected boolean isShutdown() {
+ return isShutdown.get();
+ }
+
+ @Override
+ public Stream<ChampObject> queryObjects(Map<String, Object> queryParams) {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ //If they provided the object key, do this the quick way rather than creating a traversal
+ if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString())) {
+ try {
+ final Optional<ChampObject> object = retrieveObject(queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString()));
+
+ if (object.isPresent()) return Stream.of(object.get());
+ else return Stream.empty();
+ } catch (ChampUnmarshallingException e) {
+ LOGGER.warn("Failed to unmarshall object", e);
+ return Stream.empty();
+ }
+ }
+
+ final GraphTraversal<Vertex, Vertex> query = getGraph().traversal().V();
+
+ for (Entry<String, Object> filter : queryParams.entrySet()) {
+ if (filter.getKey().equals(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
+ continue; //For performance reasons, the label is the last thing to be added
+ } else {
+ query.has(filter.getKey(), filter.getValue());
+ }
+ }
+
+ if (queryParams.containsKey(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString())) {
+ query.hasLabel((String) queryParams.get(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString()));
+ }
+
+ final Iterator<ChampObject> objIter = new Iterator<ChampObject> () {
+
+ private ChampObject next;
+
+
+ @Override
+ public boolean hasNext() {
+ while (query.hasNext()) {
+ try {
+ next = getChampformer().unmarshallObject(query.next());
+ return true;
+ } catch (ChampUnmarshallingException e) {
+ LOGGER.warn("Failed to unmarshall tinkerpop vertex during query, returning partial results", e);
+ }
+ }
+
+ tryCommit(); //Danger ahead if this iterator is not completely consumed
+ //then the transaction cache will hold stale values
+
+ next = null;
+ return false;
+ }
+
+ @Override
+ public ChampObject next() {
+ if (next == null) throw new NoSuchElementException();
+
+ return next;
+ }
+ };
+
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+ objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+ }
+
+ @Override
+ public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Iterator<Vertex> vertices = getGraph().vertices(key);
+ final Optional<ChampObject> optionalObject;
+
+ if (!vertices.hasNext()) optionalObject = Optional.empty();
+ else optionalObject = Optional.of(getChampformer().unmarshallObject(vertices.next()));
+
+ tryCommit();
+
+ return optionalObject;
+ }
+
+ @Override
+ public Stream<ChampRelationship> retrieveRelationships(ChampObject source) throws ChampUnmarshallingException, ChampObjectNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Vertex sourceVertex;
+
+ try {
+ sourceVertex = getGraph().vertices(source.getKey().get()).next();
+ } catch (NoSuchElementException e) {
+ tryRollback();
+
+ throw new ChampObjectNotExistsException();
+ }
+
+ final Iterator<Edge> edges = sourceVertex.edges(Direction.BOTH);
+ final Iterator<ChampRelationship> relIter = new Iterator<ChampRelationship> () {
+
+ private ChampRelationship next;
+
+ @Override
+ public boolean hasNext() {
+ while (edges.hasNext()) {
+ try {
+ next = getChampformer().unmarshallRelationship(edges.next());
+ return true;
+ } catch (ChampUnmarshallingException e) {
+ LOGGER.warn("Failed to unmarshall tinkerpop edge during query, returning partial results", e);
+ }
+ }
+
+ tryCommit();//Danger ahead if this iterator is not completely
+ //consumed, then the transaction cache will be stale
+ next = null;
+ return false;
+ }
+
+ @Override
+ public ChampRelationship next() {
+ if (next == null) throw new NoSuchElementException();
+
+ return next;
+ }
+ };
+
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+ relIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+ }
+
+ @Override
+ public ChampObject doStoreObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException {
+
+ try {
+ final Vertex vertex = writeVertex(object);
+
+ tryCommit();
+
+ return ChampObject.create()
+ .from(object)
+ .withKey(vertex.id())
+ .build();
+
+ } catch (ChampObjectNotExistsException e) {
+ tryRollback();
+
+ throw e;
+ }
+ }
+
+ @Override
+ public ChampObject doReplaceObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException {
+
+ try {
+ final Vertex vertex = replaceVertex(object);
+
+ tryCommit();
+
+ return ChampObject.create()
+ .from(object)
+ .withKey(vertex.id())
+ .build();
+
+ } catch (ChampObjectNotExistsException e) {
+ tryRollback();
+
+ throw e;
+ }
+ }
+
+ public void executeDeleteObject(Object key) throws ChampObjectNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Iterator<Vertex> vertex = getGraph().vertices(key);
+
+ if (!vertex.hasNext()) {
+ tryRollback();
+
+ throw new ChampObjectNotExistsException();
+ }
+
+ vertex.next().remove();
+
+ tryCommit();
+ }
+
+ @Override
+ public ChampRelationship doStoreRelationship(ChampRelationship relationship)
+ throws ChampUnmarshallingException, ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException {
+
+ try {
+ final Edge edge = writeEdge(relationship);
+
+ tryCommit();
+
+ return getChampformer().unmarshallRelationship(edge);
+
+ } catch (ChampObjectNotExistsException | ChampRelationshipNotExistsException | ChampUnmarshallingException | ChampMarshallingException e) {
+ tryRollback();
+
+ throw e;
+ }
+ }
+
+ @Override
+ public ChampRelationship doReplaceRelationship(ChampRelationship relationship)
+ throws ChampUnmarshallingException, ChampRelationshipNotExistsException, ChampMarshallingException {
+
+ try {
+ final Edge edge = replaceEdge(relationship);
+
+ tryCommit();
+
+ return getChampformer().unmarshallRelationship(edge);
+
+ } catch ( ChampRelationshipNotExistsException | ChampUnmarshallingException | ChampMarshallingException e) {
+ tryRollback();
+
+ throw e;
+ }
+ }
+
+ @Override
+ public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams) {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ //If they provided the relationship key, do this the quick way rather than creating a traversal
+ if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString())) {
+ try {
+ final Optional<ChampRelationship> relationship = retrieveRelationship(queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_KEY.toString()));
+
+ if (relationship.isPresent()) return Stream.of(relationship.get());
+ else return Stream.empty();
+ } catch (ChampUnmarshallingException e) {
+ LOGGER.warn("Failed to unmarshall relationship", e);
+ return Stream.empty();
+ }
+ }
+
+ final GraphTraversal<Edge, Edge> query = getGraph().traversal().E();
+
+ for (Entry<String, Object> filter : queryParams.entrySet()) {
+ if (filter.getKey().equals(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
+ continue; //Add the label last for performance reasons
+ } else {
+ query.has(filter.getKey(), filter.getValue());
+ }
+ }
+
+ if (queryParams.containsKey(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString())) {
+ query.hasLabel((String) queryParams.get(ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString()));
+ }
+
+ final Iterator<ChampRelationship> objIter = new Iterator<ChampRelationship> () {
+
+ private ChampRelationship next;
+
+
+ @Override
+ public boolean hasNext() {
+ while (query.hasNext()) {
+ try {
+ next = getChampformer().unmarshallRelationship(query.next());
+ return true;
+ } catch (ChampUnmarshallingException e) {
+ LOGGER.warn("Failed to unmarshall tinkerpop vertex during query, returning partial results", e);
+ }
+ }
+
+ tryCommit(); //Danger ahead if this iterator is not completely
+ //consumed, then the transaction cache will be stale
+
+ next = null;
+ return false;
+ }
+
+ @Override
+ public ChampRelationship next() {
+ if (next == null) throw new NoSuchElementException();
+
+ return next;
+ }
+ };
+
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+ objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+ }
+
+ @Override
+ public Optional<ChampRelationship> retrieveRelationship(Object key)
+ throws ChampUnmarshallingException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ final Iterator<Edge> edge = getGraph().edges(key);
+ final Optional<ChampRelationship> optionalRelationship;
+
+ if (!edge.hasNext()) optionalRelationship = Optional.empty();
+ else optionalRelationship = Optional.of(getChampformer().unmarshallRelationship(edge.next()));
+
+ tryCommit();
+
+ return optionalRelationship;
+ }
+
+ public void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+ if (!relationship.getKey().isPresent()) throw new IllegalArgumentException("Key must be provided when deleting a relationship");
+
+ final Iterator<Edge> edge = getGraph().edges(relationship.getKey().get());
+
+ if (!edge.hasNext()) {
+ tryRollback();
+
+ throw new ChampRelationshipNotExistsException();
+ }
+
+ edge.next().remove();
+
+ tryCommit();
+ }
+
+ @Override
+ public ChampPartition doStorePartition(ChampPartition submittedPartition) throws ChampMarshallingException, ChampObjectNotExistsException, ChampRelationshipNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ try {
+ final HashMap<ChampObject, ChampObject> objectsWithKeys = new HashMap<ChampObject, ChampObject> ();
+ final CreateChampPartitionable storedPartition = ChampPartition.create();
+
+ for (ChampObject champObject : submittedPartition.getChampObjects()) {
+ final Vertex vertex = writeVertex(champObject);
+ objectsWithKeys.put(champObject, ChampObject.create()
+ .from(champObject)
+ .withKey(vertex.id())
+ .build());
+ }
+
+ for (ChampRelationship champRelationship : submittedPartition.getChampRelationships()) {
+ if (!objectsWithKeys.containsKey(champRelationship.getSource())) {
+ final Vertex vertex = writeVertex(champRelationship.getSource());
+
+ objectsWithKeys.put(champRelationship.getSource(), ChampObject.create()
+ .from(champRelationship.getSource())
+ .withKey(vertex.id())
+ .build());
+ }
+
+ if (!objectsWithKeys.containsKey(champRelationship.getTarget())) {
+ final Vertex vertex = writeVertex(champRelationship.getTarget());
+
+ objectsWithKeys.put(champRelationship.getTarget(), ChampObject.create()
+ .from(champRelationship.getTarget())
+ .withKey(vertex.id())
+ .build());
+ }
+
+ final ChampRelationship.Builder relWithKeysBuilder = new ChampRelationship.Builder(objectsWithKeys.get(champRelationship.getSource()),
+ objectsWithKeys.get(champRelationship.getTarget()),
+ champRelationship.getType());
+
+ if (champRelationship.getKey().isPresent()) relWithKeysBuilder.key(champRelationship.getKey().get());
+
+ relWithKeysBuilder.properties(champRelationship.getProperties());
+
+ final Edge edge = writeEdge(relWithKeysBuilder.build());
+
+ storedPartition.withRelationship(ChampRelationship.create()
+ .from(champRelationship)
+ .withKey(edge.id())
+ .build());
+ }
+
+ for (ChampObject object : objectsWithKeys.values()) {
+ storedPartition.withObject(object);
+ }
+
+ tryCommit();
+
+ return storedPartition.build();
+
+ } catch (ChampObjectNotExistsException | ChampMarshallingException e) {
+ tryRollback();
+
+ throw e;
+ }
+ }
+
+ public void executeDeletePartition(ChampPartition graph) {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ for (ChampObject champObject : graph.getChampObjects()) {
+ try {
+ final Object vertexId = champObject.getKey().get();
+ final Iterator<Vertex> vertex = getGraph().vertices(vertexId);
+
+ if (vertex.hasNext()) {
+ vertex.next().remove();
+ }
+ } catch (NoSuchElementException e) {
+ tryRollback();
+
+ throw new IllegalArgumentException("Must pass a key to delete an object");
+ }
+ }
+
+ for (ChampRelationship champRelationship : graph.getChampRelationships()) {
+ try {
+ final Iterator<Edge> edge = getGraph().edges(champRelationship.getKey().get());
+
+ if (edge.hasNext()) {
+ edge.next().remove();
+ }
+ } catch (NoSuchElementException e) {
+ tryRollback();
+
+ throw new IllegalArgumentException("Must pass a key to delete a relationship");
+ }
+ }
+
+ tryCommit();
+
+ }
+
+ @Override
+ public void shutdown() {
+
+ if (isShutdown.compareAndSet(false, true)) {
+ super.shutdown();
+ try {
+ getGraph().close();
+ } catch (Throwable t) {
+ LOGGER.error("Exception while shutting down graph", t);
+ }
+ } else {
+ throw new IllegalStateException("Cannot call shutdown() after shutdown() was already initiated");
+ }
+ }
+
+ @Override
+ public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeSchema() after shutdown has been initiated");
+
+ if (getGraph().features().graph().variables().supportsVariables()) {
+ try {
+ getGraph().variables().set("schema", getObjectMapper().writeValueAsBytes(schema));
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ super.storeSchema(schema);
+ }
+ }
+
+ @Override
+ public ChampSchema retrieveSchema() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveSchema() after shutdown has been initiated");
+
+ if (getGraph().features().graph().variables().supportsVariables()) {
+ final Optional<byte[]> schema = getGraph().variables().get("schema");
+
+ if (schema.isPresent()) {
+ try {
+ return getObjectMapper().readValue(schema.get(), ChampSchema.class);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ return super.retrieveSchema();
+ }
+
+ @Override
+ public void deleteSchema() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call deleteSchema() after shutdown has been initiated");
+
+ if (getGraph().features().graph().variables().supportsVariables()) {
+ getGraph().variables().remove("schema");
+ } else {
+ super.deleteSchema();
+ }
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractValidatingChampGraph.java b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractValidatingChampGraph.java
new file mode 100644
index 0000000..35d56f0
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/graph/impl/AbstractValidatingChampGraph.java
@@ -0,0 +1,176 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.graph.impl;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.event.AbstractLoggingChampGraph;
+import org.openecomp.aai.champ.event.ChampEvent;
+import org.openecomp.aai.champ.event.ChampEvent.ChampOperation;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl.Builder;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+
+public abstract class AbstractValidatingChampGraph extends AbstractLoggingChampGraph {
+
+ private ChampSchema schema = ChampSchema.emptySchema();
+
+ protected abstract ChampSchemaEnforcer getSchemaEnforcer();
+ protected abstract boolean isShutdown();
+
+ protected abstract ChampObject doReplaceObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException;
+ protected abstract ChampObject doStoreObject(ChampObject object) throws ChampMarshallingException, ChampObjectNotExistsException;
+ protected abstract ChampRelationship doReplaceRelationship(ChampRelationship relationship) throws ChampUnmarshallingException, ChampRelationshipNotExistsException, ChampMarshallingException;
+ protected abstract ChampRelationship doStoreRelationship(ChampRelationship relationship) throws ChampUnmarshallingException, ChampObjectNotExistsException, ChampRelationshipNotExistsException, ChampMarshallingException;
+ protected abstract ChampPartition doStorePartition(ChampPartition partition) throws ChampRelationshipNotExistsException, ChampMarshallingException, ChampObjectNotExistsException;
+
+ protected AbstractValidatingChampGraph(Map<String, Object> properties) {
+ super(properties);
+ }
+
+ public ChampObject executeStoreObject(ChampObject object)
+ throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ validate(object);
+
+ return doStoreObject(object);
+ }
+
+ public ChampObject executeReplaceObject(ChampObject object)
+ throws ChampMarshallingException, ChampSchemaViolationException, ChampObjectNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ validate(object);
+
+ return doReplaceObject(object);
+ }
+
+ public ChampRelationship executeStoreRelationship(ChampRelationship relationship)
+ throws ChampUnmarshallingException, ChampMarshallingException, ChampObjectNotExistsException, ChampSchemaViolationException, ChampRelationshipNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ validate(relationship);
+
+ return doStoreRelationship(relationship);
+ }
+
+ public ChampRelationship executeReplaceRelationship(ChampRelationship relationship)
+ throws ChampUnmarshallingException, ChampMarshallingException, ChampSchemaViolationException, ChampRelationshipNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ validate(relationship);
+
+ return doReplaceRelationship(relationship);
+ }
+
+ public ChampPartition executeStorePartition(ChampPartition partition) throws ChampSchemaViolationException, ChampRelationshipNotExistsException, ChampMarshallingException, ChampObjectNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot use ChampAPI after calling shutdown()");
+
+ validate(partition);
+
+ return doStorePartition(partition);
+ }
+
+ protected void validate(ChampObject object) throws ChampSchemaViolationException {
+ final Optional<ChampObjectConstraint> objectConstraint = retrieveSchema().getObjectConstraint(object.getType());
+
+ if (objectConstraint.isPresent()) getSchemaEnforcer().validate(object, objectConstraint.get());
+ }
+
+ protected void validate(ChampRelationship relationship) throws ChampSchemaViolationException {
+ final ChampSchema graphSchema = retrieveSchema();
+ final Optional<ChampRelationshipConstraint> relationshipConstraint = graphSchema.getRelationshipConstraint(relationship.getType());
+ final Optional<ChampObjectConstraint> sourceObjConstraint = graphSchema.getObjectConstraint(relationship.getSource().getType());
+ final Optional<ChampObjectConstraint> targetObjConstraint = graphSchema.getObjectConstraint(relationship.getTarget().getType());
+
+ if (relationshipConstraint.isPresent()) getSchemaEnforcer().validate(relationship, relationshipConstraint.get());
+ if (sourceObjConstraint.isPresent()) getSchemaEnforcer().validate(relationship.getSource(), sourceObjConstraint.get());
+ if (targetObjConstraint.isPresent()) getSchemaEnforcer().validate(relationship.getTarget(), targetObjConstraint.get());
+ }
+
+ protected void validate(ChampPartition partition) throws ChampSchemaViolationException {
+ for (ChampObject object : partition.getChampObjects()) {
+ validate(object);
+ }
+
+ for (ChampRelationship relationship : partition.getChampRelationships()) {
+ validate(relationship);
+ }
+ }
+
+ @Override
+ public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeSchema() after shutdown has been initiated");
+
+ this.schema = schema;
+ }
+
+ @Override
+ public ChampSchema retrieveSchema() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveSchema() after shutdown has been initiated");
+
+ return schema;
+ }
+
+ @Override
+ public void updateSchema(ChampObjectConstraint objectConstraint) throws ChampSchemaViolationException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call updateSchema() after shutdown has been initiated");
+
+ final ChampSchema currentSchema = retrieveSchema();
+ final ChampSchema updatedSchema = new ChampSchema.Builder(currentSchema)
+ .constraint(objectConstraint)
+ .build();
+
+ storeSchema(updatedSchema);
+ }
+
+ @Override
+ public void updateSchema(ChampRelationshipConstraint relationshipConstraint) throws ChampSchemaViolationException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call updateSchema() after shutdown has been initiated");
+
+ final ChampSchema currentSchema = retrieveSchema();
+ final ChampSchema updatedSchema = new ChampSchema.Builder(currentSchema)
+ .constraint(relationshipConstraint)
+ .build();
+
+ storeSchema(updatedSchema);
+ }
+
+ @Override
+ public void deleteSchema() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call deleteSchema() after shutdown has been initiated");
+ this.schema = ChampSchema.emptySchema();
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/ChampAPIImpl.java b/src/main/java/org/openecomp/aai/champ/graph/impl/ChampAPIImpl.java
new file mode 100644
index 0000000..34453fc
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/graph/impl/ChampAPIImpl.java
@@ -0,0 +1,87 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.graph.impl;
+
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ChampAPIImpl implements ChampAPI {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChampAPIImpl.class);
+
+ private final AtomicBoolean shutdown;
+ private final ChampGraph.Type type;
+ private final ConcurrentHashMap<String, ChampGraph> graphs;
+
+ public ChampAPIImpl(ChampGraph.Type type) {
+ this.type = type;
+ this.graphs = new ConcurrentHashMap<String, ChampGraph> ();
+ this.shutdown = new AtomicBoolean(false);
+ }
+
+ private ConcurrentHashMap<String, ChampGraph> getGraphs() {
+ return graphs;
+ }
+
+ @Override
+ public ChampGraph getGraph(String graphName) {
+ if (shutdown.get()) throw new IllegalStateException("Cannot call getGraph() after shutdown() has been initiated");
+
+ if (getGraphs().containsKey(graphName)) return getGraphs().get(graphName);
+
+ final ChampGraph graph = ChampGraph.Factory.newInstance(type, graphName);
+
+ final ChampGraph existingGraph = getGraphs().putIfAbsent(graphName, graph);
+
+ if (existingGraph == null) return graph;
+
+ return existingGraph;
+ }
+
+ @Override
+ public void shutdown() {
+ if (shutdown.compareAndSet(false, true)) {
+ for (Entry<String, ChampGraph> graphEntry : graphs.entrySet()) {
+ LOGGER.info("Shutting down graph {}", graphEntry.getKey());
+
+ try {
+ graphEntry.getValue().shutdown();
+ LOGGER.info("Graph {} shutdown successfully", graphEntry.getKey());
+ } catch (Throwable t) {
+ LOGGER.warn("Caught exception while shutting down graph " + graphEntry.getKey(), t);
+ }
+ }
+ }
+ }
+
+ @Override
+ public ChampGraph.Type getType() {
+ return type;
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/InMemoryChampGraphImpl.java b/src/main/java/org/openecomp/aai/champ/graph/impl/InMemoryChampGraphImpl.java
new file mode 100644
index 0000000..ab59e64
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/graph/impl/InMemoryChampGraphImpl.java
@@ -0,0 +1,182 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.graph.impl;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
+import org.openecomp.aai.champ.ChampCapabilities;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+import org.openecomp.aai.champ.schema.DefaultChampSchemaEnforcer;
+import org.slf4j.Logger;
+
+import com.att.nsa.cambria.client.CambriaPublisher;
+
+public final class InMemoryChampGraphImpl extends AbstractTinkerpopChampGraph {
+
+ private static final ChampCapabilities CAPABILITIES = new ChampCapabilities() {
+
+ @Override
+ public boolean canDeleteObjectIndices() {
+ return true;
+ }
+
+ @Override
+ public boolean canDeleteRelationshipIndices() {
+ return true;
+ }
+ };
+
+ private final ConcurrentHashMap<String, ChampObjectIndex> objectIndices;
+ private final ConcurrentHashMap<String, ChampRelationshipIndex> relationshipIndices;
+
+ private final ChampSchemaEnforcer schemaEnforcer;
+ private final TinkerGraph graph;
+
+ private InMemoryChampGraphImpl(Builder builder) {
+ super(builder.graphConfiguration);
+ this.graph = TinkerGraph.open();
+
+ this.objectIndices = new ConcurrentHashMap<String, ChampObjectIndex> ();
+ this.relationshipIndices = new ConcurrentHashMap<String, ChampRelationshipIndex> ();
+
+ this.schemaEnforcer = builder.schemaEnforcer;
+ }
+
+ public static class Builder {
+ private final Map<String, Object> graphConfiguration = new HashMap<String, Object> ();
+ private ChampSchemaEnforcer schemaEnforcer = new DefaultChampSchemaEnforcer();
+
+ public Builder() {}
+
+ public Builder schemaEnforcer(ChampSchemaEnforcer schemaEnforcer) {
+ this.schemaEnforcer = schemaEnforcer;
+ return this;
+ }
+
+ public InMemoryChampGraphImpl build() {
+ return new InMemoryChampGraphImpl(this);
+ }
+ }
+
+ protected ChampSchemaEnforcer getSchemaEnforcer() {
+ return schemaEnforcer;
+ }
+
+ @Override
+ protected TinkerGraph getGraph() {
+ return graph;
+ }
+
+ private ConcurrentHashMap<String, ChampObjectIndex> getObjectIndices() {
+ return objectIndices;
+ }
+
+ private ConcurrentHashMap<String, ChampRelationshipIndex> getRelationshipIndices() {
+ return relationshipIndices;
+ }
+
+ public void executeStoreObjectIndex(ChampObjectIndex index) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeObjectIndex() after shutdown has been initiated");
+
+ getGraph().createIndex(index.getField().getName(), Vertex.class);
+ getObjectIndices().put(index.getName(), index);
+ }
+
+ @Override
+ public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndex() after shutdown has been initiated");
+
+ if (getObjectIndices().containsKey(indexName))
+ return Optional.of(getObjectIndices().get(indexName));
+
+ return Optional.empty();
+ }
+
+ @Override
+ public Stream<ChampObjectIndex> retrieveObjectIndices() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndices() after shutdown has been initiated");
+
+ return getObjectIndices().values().stream();
+ }
+
+ public void executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call deleteObjectIndex() after shutdown has been initiated");
+
+ final ChampObjectIndex objectIndex = getObjectIndices().remove(indexName);
+
+ if (objectIndex == null) throw new ChampIndexNotExistsException();
+
+ getGraph().dropIndex(objectIndex.getField().getName(), Vertex.class);
+ }
+
+ public void executeStoreRelationshipIndex(ChampRelationshipIndex index) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeRelationshipIndex() after shutdown has been initiated");
+
+ getGraph().createIndex(index.getField().getName(), Edge.class);
+ getRelationshipIndices().put(index.getName(), index);
+ }
+
+ @Override
+ public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndex() after shutdown has been initiated");
+
+ if (getRelationshipIndices().containsKey(indexName)) {
+ return Optional.of(getRelationshipIndices().get(indexName));
+ }
+
+ return Optional.empty();
+ }
+
+ @Override
+ public Stream<ChampRelationshipIndex> retrieveRelationshipIndices() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndices() after shutdown has been initiated");
+
+ return getRelationshipIndices().values().stream();
+ }
+
+ public void executeDeleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call deleteRelationshipIndex() after shutdown has been initiated");
+
+ final ChampRelationshipIndex relationshipIndex = getRelationshipIndices().remove(indexName);
+
+ if (relationshipIndex == null) throw new ChampIndexNotExistsException();
+
+ getGraph().dropIndex(relationshipIndex.getField().getName(), Edge.class);
+ }
+
+ @Override
+ public ChampCapabilities capabilities() {
+ return CAPABILITIES;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/graph/impl/TitanChampGraphImpl.java b/src/main/java/org/openecomp/aai/champ/graph/impl/TitanChampGraphImpl.java
new file mode 100644
index 0000000..2bb8d2e
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/graph/impl/TitanChampGraphImpl.java
@@ -0,0 +1,461 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.graph.impl;
+
+import java.time.temporal.ChronoUnit;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.openecomp.aai.champ.ChampCapabilities;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.model.ChampCardinality;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+import org.openecomp.aai.champ.schema.DefaultChampSchemaEnforcer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thinkaurelius.titan.core.Cardinality;
+import com.thinkaurelius.titan.core.EdgeLabel;
+import com.thinkaurelius.titan.core.PropertyKey;
+import com.thinkaurelius.titan.core.SchemaViolationException;
+import com.thinkaurelius.titan.core.TitanEdge;
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.TitanVertex;
+import com.thinkaurelius.titan.core.schema.SchemaAction;
+import com.thinkaurelius.titan.core.schema.SchemaStatus;
+import com.thinkaurelius.titan.core.schema.TitanGraphIndex;
+import com.thinkaurelius.titan.core.schema.TitanManagement;
+import com.thinkaurelius.titan.graphdb.database.management.ManagementSystem;
+
+public final class TitanChampGraphImpl extends AbstractTinkerpopChampGraph {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(TitanChampGraphImpl.class);
+ private static final String TITAN_CASSANDRA_KEYSPACE = "storage.cassandra.keyspace";
+ private static final String TITAN_HBASE_TABLE = "storage.hbase.table";
+ private static final ChampSchemaEnforcer SCHEMA_ENFORCER = new DefaultChampSchemaEnforcer();
+ private static final int REGISTER_OBJECT_INDEX_TIMEOUT_SECS = 30;
+
+ private static final ChampCapabilities CAPABILITIES = new ChampCapabilities() {
+
+ @Override
+ public boolean canDeleteObjectIndices() {
+ return false;
+ }
+
+ @Override
+ public boolean canDeleteRelationshipIndices() {
+ return false;
+ }
+ };
+
+ private final TitanGraph graph;
+
+ private TitanChampGraphImpl(Builder builder) {
+ super(builder.graphConfiguration);
+ final TitanFactory.Builder titanGraphBuilder = TitanFactory.build();
+
+ for (Entry<String, Object> titanGraphProperty : builder.graphConfiguration.entrySet()) {
+ titanGraphBuilder.set(titanGraphProperty.getKey(), titanGraphProperty.getValue());
+ }
+
+ final Object storageBackend = builder.graphConfiguration.get("storage.backend");
+
+ if (storageBackend.equals("cassandra") ||
+ storageBackend.equals("cassandrathrift") ||
+ storageBackend.equals("astyanax") ||
+ storageBackend.equals("embeddedcassandra")) {
+ titanGraphBuilder.set(TITAN_CASSANDRA_KEYSPACE, builder.graphName);
+ } else if (storageBackend.equals("hbase")) {
+ titanGraphBuilder.set(TITAN_HBASE_TABLE, builder.graphName);
+ } else if (storageBackend.equals("berkleyje")) {
+ throw new RuntimeException("storage.backend=berkleyje cannot handle multiple graphs on a single DB, not usable");
+ } else if (storageBackend.equals("inmemory")) {
+ } else {
+ throw new RuntimeException("Unknown storage.backend=" + storageBackend);
+ }
+
+ this.graph = titanGraphBuilder.open();
+ }
+
+ public static class Builder {
+ private final String graphName;
+
+ private final Map<String, Object> graphConfiguration = new HashMap<String, Object> ();
+
+ public Builder(String graphName) {
+ this.graphName = graphName;
+ }
+
+ public Builder properties(Map<String, Object> properties) {
+ if (properties.containsKey(TITAN_CASSANDRA_KEYSPACE))
+ throw new IllegalArgumentException("Cannot use path " + TITAN_CASSANDRA_KEYSPACE
+ + " in initial configuration - this path is used"
+ + " to specify graph names");
+
+ this.graphConfiguration.putAll(properties);
+ return this;
+ }
+
+ public Builder property(String path, Object value) {
+ if (path.equals(TITAN_CASSANDRA_KEYSPACE))
+ throw new IllegalArgumentException("Cannot use path " + TITAN_CASSANDRA_KEYSPACE
+ + " in initial configuration - this path is used"
+ + " to specify graph names");
+ graphConfiguration.put(path, value);
+ return this;
+ }
+
+ public TitanChampGraphImpl build() {
+ return new TitanChampGraphImpl(this);
+ }
+ }
+
+ @Override
+ protected TitanGraph getGraph() {
+ return graph;
+ }
+
+ @Override
+ protected ChampSchemaEnforcer getSchemaEnforcer() {
+ return SCHEMA_ENFORCER;
+ }
+
+ public void executeStoreObjectIndex(ChampObjectIndex index) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeObjectIndex() after shutdown has been initiated");
+
+ final TitanGraph graph = getGraph();
+ final TitanManagement createIndexMgmt = graph.openManagement();
+ final PropertyKey pk = createIndexMgmt.getOrCreatePropertyKey(index.getField().getName());
+
+ if (createIndexMgmt.getGraphIndex(index.getName()) != null) {
+ createIndexMgmt.rollback();
+ return; //Ignore, index already exists
+ }
+
+ createIndexMgmt.buildIndex(index.getName(), Vertex.class).addKey(pk).buildCompositeIndex();
+
+ createIndexMgmt.commit();
+ graph.tx().commit();
+
+ awaitIndexCreation(index.getName());
+ }
+
+ @Override
+ public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndex() after shutdown has been initiated");
+
+ final TitanManagement retrieveIndexMgmt = getGraph().openManagement();
+ final TitanGraphIndex index = retrieveIndexMgmt.getGraphIndex(indexName);
+
+ if (index == null) return Optional.empty();
+ if (index.getIndexedElement() != TitanVertex.class) return Optional.empty();
+
+ return Optional.of(ChampObjectIndex.create()
+ .ofName(indexName)
+ .onType(ChampObject.ReservedTypes.ANY.toString())
+ .forField(index.getFieldKeys()[0].name())
+ .build());
+ }
+
+ @Override
+ public Stream<ChampObjectIndex> retrieveObjectIndices() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveObjectIndices() after shutdown has been initiated");
+
+ final TitanManagement createIndexMgmt = getGraph().openManagement();
+ final Iterator<TitanGraphIndex> indices = createIndexMgmt.getGraphIndexes(Vertex.class).iterator();
+
+ final Iterator<ChampObjectIndex> objIter = new Iterator<ChampObjectIndex> () {
+
+ private ChampObjectIndex next;
+
+ @Override
+ public boolean hasNext() {
+ if (indices.hasNext()) {
+ final TitanGraphIndex index = indices.next();
+
+ next = ChampObjectIndex.create()
+ .ofName(index.name())
+ .onType(ChampObject.ReservedTypes.ANY.toString())
+ .forField(index.getFieldKeys()[0].name())
+ .build();
+ return true;
+ }
+
+ next = null;
+ return false;
+ }
+
+ @Override
+ public ChampObjectIndex next() {
+ if (next == null) throw new NoSuchElementException();
+
+ return next;
+ }
+ };
+
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+ objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+ }
+
+ public void executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call deleteObjectIndex() after shutdown has been initiated");
+
+ throw new UnsupportedOperationException("Cannot delete indices using the TitanChampImpl");
+ }
+
+ public void executeStoreRelationshipIndex(ChampRelationshipIndex index) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeRelationshipIndex() after shutdown has been initiated");
+
+ final TitanGraph graph = getGraph();
+ final TitanManagement createIndexMgmt = graph.openManagement();
+ final PropertyKey pk = createIndexMgmt.getOrCreatePropertyKey(index.getField().getName());
+
+ if (createIndexMgmt.getGraphIndex(index.getName()) != null) return; //Ignore, index already exists
+ createIndexMgmt.buildIndex(index.getName(), Edge.class).addKey(pk).buildCompositeIndex();
+
+ createIndexMgmt.commit();
+ graph.tx().commit();
+
+ awaitIndexCreation(index.getName());
+ }
+
+ @Override
+ public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName) {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndex() after shutdown has been initiated");
+
+ final TitanManagement retrieveIndexMgmt = getGraph().openManagement();
+ final TitanGraphIndex index = retrieveIndexMgmt.getGraphIndex(indexName);
+
+ if (index == null) return Optional.empty();
+ if (index.getIndexedElement() != TitanEdge.class) return Optional.empty();
+
+ return Optional.of(ChampRelationshipIndex.create()
+ .ofName(indexName)
+ .onType(ChampObject.ReservedTypes.ANY.toString())
+ .forField(index.getFieldKeys()[0].name())
+ .build());
+ }
+
+ @Override
+ public Stream<ChampRelationshipIndex> retrieveRelationshipIndices() {
+ if (isShutdown()) throw new IllegalStateException("Cannot call retrieveRelationshipIndices() after shutdown has been initiated");
+
+ final TitanManagement createIndexMgmt = getGraph().openManagement();
+ final Iterator<TitanGraphIndex> indices = createIndexMgmt.getGraphIndexes(Edge.class).iterator();
+
+ final Iterator<ChampRelationshipIndex> objIter = new Iterator<ChampRelationshipIndex> () {
+
+ private ChampRelationshipIndex next;
+
+ @Override
+ public boolean hasNext() {
+ if (indices.hasNext()) {
+ final TitanGraphIndex index = indices.next();
+
+ next = ChampRelationshipIndex.create()
+ .ofName(index.name())
+ .onType(ChampRelationship.ReservedTypes.ANY.toString())
+ .forField(index.getFieldKeys()[0].name())
+ .build();
+ return true;
+ }
+
+ next = null;
+ return false;
+ }
+
+ @Override
+ public ChampRelationshipIndex next() {
+ if (next == null) throw new NoSuchElementException();
+
+ return next;
+ }
+ };
+
+ return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+ objIter, Spliterator.ORDERED | Spliterator.NONNULL), false);
+ }
+
+ public void executeDeleteRelationshipIndex(String indexName) throws ChampIndexNotExistsException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call deleteRelationshipIndex() after shutdown has been initiated");
+
+ throw new UnsupportedOperationException("Cannot delete indices using the TitanChampImpl");
+ }
+
+ private Cardinality getTitanCardinality(ChampCardinality cardinality) {
+ switch (cardinality) {
+ case LIST:
+ return Cardinality.LIST;
+ case SET:
+ return Cardinality.SET;
+ case SINGLE:
+ return Cardinality.SINGLE;
+ default:
+ throw new RuntimeException("Unknown ChampCardinality " + cardinality);
+ }
+ }
+
+ private void awaitIndexCreation(String indexName) {
+ //Wait for the index to become available
+ try {
+ if (ManagementSystem.awaitGraphIndexStatus(graph, indexName)
+ .status(SchemaStatus.ENABLED)
+ .timeout(1, ChronoUnit.SECONDS)
+ .call()
+ .getSucceeded()) {
+ return; //Empty graphs immediately ENABLE indices
+ }
+
+ if (!ManagementSystem.awaitGraphIndexStatus(graph, indexName)
+ .status(SchemaStatus.REGISTERED)
+ .timeout(REGISTER_OBJECT_INDEX_TIMEOUT_SECS, ChronoUnit.SECONDS)
+ .call()
+ .getSucceeded()) {
+ LOGGER.warn("Object index was created, but timed out while waiting for it to be registered");
+ return;
+ }
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while waiting for object index creation status");
+ return;
+ }
+
+ //Reindex the existing data
+
+ try {
+ final TitanManagement updateIndexMgmt = graph.openManagement();
+ updateIndexMgmt.updateIndex(updateIndexMgmt.getGraphIndex(indexName),SchemaAction.REINDEX).get();
+ updateIndexMgmt.commit();
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while reindexing for object index");
+ return;
+ } catch (ExecutionException e) {
+ LOGGER.warn("Exception occurred during reindexing procedure for creating object index " + indexName, e);
+ }
+
+ try {
+ ManagementSystem.awaitGraphIndexStatus(graph, indexName)
+ .status(SchemaStatus.ENABLED)
+ .timeout(10, ChronoUnit.MINUTES)
+ .call();
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while waiting for index to transition to ENABLED state");
+ return;
+ }
+ }
+
+ @Override
+ public ChampCapabilities capabilities() {
+ return CAPABILITIES;
+ }
+
+ @Override
+ public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
+ if (isShutdown()) throw new IllegalStateException("Cannot call storeSchema() after shutdown has been initiated");
+
+ final ChampSchema currentSchema = retrieveSchema();
+ final TitanManagement mgmt = getGraph().openManagement();
+
+ try {
+ for (ChampObjectConstraint objConstraint : schema.getObjectConstraints().values()) {
+ for (ChampPropertyConstraint propConstraint : objConstraint.getPropertyConstraints()) {
+ final Optional<ChampObjectConstraint> currentObjConstraint = currentSchema.getObjectConstraint(objConstraint.getType());
+
+ if (currentObjConstraint.isPresent()) {
+ final Optional<ChampPropertyConstraint> currentPropConstraint = currentObjConstraint.get().getPropertyConstraint(propConstraint.getField().getName());
+
+ if (currentPropConstraint.isPresent() && currentPropConstraint.get().compareTo(propConstraint) != 0) {
+ throw new ChampSchemaViolationException("Cannot update already existing property on object type " + objConstraint.getType() + ": " + propConstraint);
+ }
+ }
+
+ final String newPropertyKeyName = propConstraint.getField().getName();
+
+ if (mgmt.getPropertyKey(newPropertyKeyName) != null) continue; //Check Titan to see if another node created this property key
+
+ mgmt.makePropertyKey(newPropertyKeyName)
+ .dataType(propConstraint.getField().getJavaType())
+ .cardinality(getTitanCardinality(propConstraint.getCardinality()))
+ .make();
+ }
+ }
+
+ for (ChampRelationshipConstraint relConstraint : schema.getRelationshipConstraints().values()) {
+
+ final Optional<ChampRelationshipConstraint> currentRelConstraint = currentSchema.getRelationshipConstraint(relConstraint.getType());
+
+ for (ChampPropertyConstraint propConstraint : relConstraint.getPropertyConstraints()) {
+
+ if (currentRelConstraint.isPresent()) {
+ final Optional<ChampPropertyConstraint> currentPropConstraint = currentRelConstraint.get().getPropertyConstraint(propConstraint.getField().getName());
+
+ if (currentPropConstraint.isPresent() && currentPropConstraint.get().compareTo(propConstraint) != 0) {
+ throw new ChampSchemaViolationException("Cannot update already existing property on relationship type " + relConstraint.getType());
+ }
+ }
+
+ final String newPropertyKeyName = propConstraint.getField().getName();
+
+ if (mgmt.getPropertyKey(newPropertyKeyName) != null) continue; //Check Titan to see if another node created this property key
+
+ mgmt.makePropertyKey(newPropertyKeyName)
+ .dataType(propConstraint.getField().getJavaType())
+ .cardinality(getTitanCardinality(propConstraint.getCardinality()))
+ .make();
+ }
+
+ final EdgeLabel edgeLabel = mgmt.getEdgeLabel(relConstraint.getType());
+
+ if (edgeLabel != null) mgmt.makeEdgeLabel(relConstraint.getType())
+ .directed()
+ .make();
+ }
+
+ mgmt.commit();
+
+ super.storeSchema(schema);
+ } catch (SchemaViolationException | ChampSchemaViolationException e) {
+ mgmt.rollback();
+ throw new ChampSchemaViolationException(e);
+ }
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/ie/Exporter.java b/src/main/java/org/openecomp/aai/champ/ie/Exporter.java
new file mode 100644
index 0000000..e897e2b
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/ie/Exporter.java
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.ie;
+
+import java.io.OutputStream;
+
+import org.openecomp.aai.champ.ChampGraph;
+
+public interface Exporter {
+
+ public void exportData(ChampGraph graph, OutputStream os);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/ie/GraphMLImporterExporter.java b/src/main/java/org/openecomp/aai/champ/ie/GraphMLImporterExporter.java
new file mode 100644
index 0000000..05b4919
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/ie/GraphMLImporterExporter.java
@@ -0,0 +1,460 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.ie;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class GraphMLImporterExporter implements Importer, Exporter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GraphMLImporterExporter.class);
+
+ private static class GraphMLKey {
+ private final String id;
+ private final String attrName;
+ private final String attrType;
+
+ public GraphMLKey(String id, String attrName, Class<?> attrType) {
+ this.id = id;
+ this.attrName = attrName;
+
+ if (attrType.equals(Boolean.class)) {
+ this.attrType = "boolean";
+ } else if (attrType.equals(Integer.class)) {
+ this.attrType = "int";
+ } else if (attrType.equals(Long.class)) {
+ this.attrType = "long";
+ } else if (attrType.equals(Float.class)) {
+ this.attrType = "float";
+ } else if (attrType.equals(Double.class)) {
+ this.attrType = "double";
+ } else if (attrType.equals(String.class)) {
+ this.attrType = "string";
+ } else {
+ throw new RuntimeException("Cannot handle type " + attrType + " in GraphML");
+ }
+ }
+ }
+
+ public void importData(ChampAPI api, InputStream is) {
+
+ try {
+ final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ final DocumentBuilder builder = factory.newDocumentBuilder();
+ final InputSource inputSource = new InputSource(is);
+ final Document doc = builder.parse(inputSource);
+
+ final Map<String, Map<String, String>> nodePropertyDefinitions = new HashMap<String, Map<String, String>> ();
+ final Map<String, Map<String, String>> edgePropertyDefinitions = new HashMap<String, Map<String, String>> ();
+ final Set<Map<String, String>> nodeDefaults = new HashSet<Map<String, String>> ();
+ final Set<Map<String, String>> edgeDefaults = new HashSet<Map<String, String>> ();
+
+ final NodeList keys = doc.getElementsByTagName("key");
+
+ for (int i = 0; i < keys.getLength(); i++) {
+ final Node key = keys.item(i);
+ final String id = key.getAttributes().getNamedItem("id").getNodeValue();
+ final String attrName = key.getAttributes().getNamedItem("attr.name").getNodeValue();
+ final String attrType = key.getAttributes().getNamedItem("attr.type").getNodeValue();
+ final String elementType = key.getAttributes().getNamedItem("for").getNodeValue();
+ final Map<String, String> propertyDefinitions = new HashMap<String, String> ();
+
+ propertyDefinitions.put("attr.name", attrName);
+ propertyDefinitions.put("attr.type", attrType);
+
+ final NodeList keyChildren = key.getChildNodes();
+
+ for (int j = 0; j < keyChildren.getLength(); j++) {
+ final Node keyChild = keyChildren.item(j);
+
+ if (keyChild.getNodeType() != Node.ELEMENT_NODE) continue;
+
+ if (keyChild.getNodeName().equals("default")) {
+ propertyDefinitions.put("default", keyChild.getFirstChild().getNodeValue());
+
+ if (elementType.equals("node")) nodeDefaults.add(propertyDefinitions);
+ else if (elementType.equals("edge")) edgeDefaults.add(propertyDefinitions);
+ }
+ }
+
+ if (elementType.equals("node")) {
+ nodePropertyDefinitions.put(id, propertyDefinitions);
+ } else if (elementType.equals("edge")) {
+ edgePropertyDefinitions.put(id, propertyDefinitions);
+ } else {
+ LOGGER.warn("Unknown element type {}, skipping", elementType);
+ }
+ }
+
+ final NodeList graphs = doc.getElementsByTagName("graph");
+
+ for (int i = 0; i < graphs.getLength(); i++) {
+ final Node graph = graphs.item(i);
+ final String graphName = graph.getAttributes().getNamedItem("id").getNodeValue();
+ final NodeList nodesAndEdges = graph.getChildNodes();
+
+ api.getGraph(graphName).storeObjectIndex(ChampObjectIndex.create()
+ .ofName("importAssignedId")
+ .onAnyType()
+ .forField("importAssignedId")
+ .build());
+
+ for (int j = 0; j < nodesAndEdges.getLength(); j++) {
+ final Node nodeOrEdge = nodesAndEdges.item(j);
+
+ if (nodeOrEdge.getNodeType() != Node.ELEMENT_NODE) continue;
+
+ if (nodeOrEdge.getNodeName().equals("node")) {
+ writeNode(api.getGraph(graphName), nodeOrEdge, nodePropertyDefinitions, nodeDefaults);
+ } else if (nodeOrEdge.getNodeName().equals("edge")) {
+ writeEdge(api.getGraph(graphName), nodeOrEdge, edgePropertyDefinitions, edgeDefaults);
+ } else {
+ LOGGER.warn("Unknown object {} found in graphML, skipping", nodeOrEdge.getNodeName());
+ }
+ }
+ }
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException("Failed to setup DocumentBuilder", e);
+ } catch (SAXException e) {
+ throw new RuntimeException("Failed to parse input stream", e);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to parse input stream", e);
+ }
+ }
+
+ private void writeEdge(ChampGraph graph, Node edge, Map<String, Map<String, String>> edgePropertyDefinitions, Set<Map<String, String>> edgeDefaults) {
+ final NamedNodeMap edgeAttributes = edge.getAttributes();
+ final NodeList data = edge.getChildNodes();
+ final Object sourceKey = edgeAttributes.getNamedItem("source").getNodeValue();
+ final Object targetKey = edgeAttributes.getNamedItem("target").getNodeValue();
+ final ChampObject sourceObject;
+ final ChampObject targetObject;
+
+ try {
+ final Optional<ChampObject> source = graph.queryObjects(Collections.singletonMap("importAssignedId", sourceKey)).findFirst();
+ final Optional<ChampObject> target = graph.queryObjects(Collections.singletonMap("importAssignedId", targetKey)).findFirst();
+
+ if (!source.isPresent()) {
+ sourceObject = graph.storeObject(ChampObject.create()
+ .ofType("undefined")
+ .withoutKey()
+ .build());
+ } else sourceObject = source.get();
+
+ if (!target.isPresent()) {
+ targetObject = graph.storeObject(ChampObject.create()
+ .ofType("undefined")
+ .withoutKey()
+ .build());
+ } else targetObject = target.get();
+
+ } catch (ChampMarshallingException e) {
+ LOGGER.error("Failed to marshall object to backend type, skipping this edge", e);
+ return;
+ } catch (ChampSchemaViolationException e) {
+ LOGGER.error("Source/target object violates schema constraint(s)", e);
+ return;
+ } catch (ChampObjectNotExistsException e) {
+ LOGGER.error("Failed to update existing source/target ChampObject", e);
+ return;
+ }
+
+ final ChampRelationship.Builder champRelBuilder = new ChampRelationship.Builder(sourceObject, targetObject, "undefined");
+
+ for (Map<String, String> defaultProperty : edgeDefaults) {
+ champRelBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
+ }
+
+ for (int k = 0; k < data.getLength(); k++) {
+ final Node datum = data.item(k);
+
+ if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
+
+ final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
+ final Map<String, String> nodePropertyDefinition = edgePropertyDefinitions.get(nodeProperty);
+
+ switch (nodePropertyDefinition.get("attr.type")) {
+ case "boolean":
+ champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "int":
+ champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "long":
+ champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "float":
+ champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "double":
+ champRelBuilder.property(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "string":
+ champRelBuilder.property(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
+ break;
+ default:
+ throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
+ }
+ }
+
+ final ChampRelationship relToStore = champRelBuilder.build();
+
+ try {
+ graph.storeRelationship(relToStore);
+ } catch (ChampMarshallingException e) {
+ LOGGER.warn("Failed to marshall ChampObject to backend type", e);
+ } catch (ChampSchemaViolationException e) {
+ LOGGER.error("Failed to store object (schema violated): " + relToStore, e);
+ } catch (ChampRelationshipNotExistsException e) {
+ LOGGER.error("Failed to update existing ChampRelationship", e);
+ } catch (ChampObjectNotExistsException e) {
+ LOGGER.error("Objects bound to relationship do not exist (should never happen)");
+ } catch (ChampUnmarshallingException e) {
+ LOGGER.error("Failed to unmarshall ChampObject to backend type");
+ }
+ }
+
+ private void writeNode(ChampGraph graph, Node node, Map<String, Map<String, String>> nodePropertyDefinitions, Set<Map<String, String>> nodeDefaults) {
+ final NamedNodeMap nodeAttributes = node.getAttributes();
+ final Object importAssignedId = nodeAttributes.getNamedItem("id").getNodeValue();
+ final NodeList data = node.getChildNodes();
+ final Map<String, Object> properties = new HashMap<String, Object> ();
+
+ for (int k = 0; k < data.getLength(); k++) {
+ final Node datum = data.item(k);
+
+ if (datum.getNodeType() != Node.ELEMENT_NODE) continue;
+
+ final String nodeProperty = datum.getAttributes().getNamedItem("key").getNodeValue();
+ final Map<String, String> nodePropertyDefinition = nodePropertyDefinitions.get(nodeProperty);
+
+ switch (nodePropertyDefinition.get("attr.type")) {
+ case "boolean":
+ properties.put(nodePropertyDefinition.get("attr.name"), Boolean.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "int":
+ properties.put(nodePropertyDefinition.get("attr.name"), Integer.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "long":
+ properties.put(nodePropertyDefinition.get("attr.name"), Long.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "float":
+ properties.put(nodePropertyDefinition.get("attr.name"), Float.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "double":
+ properties.put(nodePropertyDefinition.get("attr.name"), Double.valueOf(datum.getFirstChild().getNodeValue()));
+ break;
+ case "string":
+ properties.put(nodePropertyDefinition.get("attr.name"), datum.getFirstChild().getNodeValue());
+ break;
+ default:
+ throw new RuntimeException("Unknown node property attr.type " + nodePropertyDefinition.get("attr.type"));
+ }
+ }
+
+ if (!properties.containsKey("type")) throw new RuntimeException("No type provided for object (was this GraphML exported by Champ?)");
+
+ final ChampObject.Builder champObjBuilder = new ChampObject.Builder((String) properties.get("type"));
+
+ for (Map<String, String> defaultProperty : nodeDefaults) {
+ champObjBuilder.property(defaultProperty.get("attr.name"), defaultProperty.get("default"));
+ }
+
+ properties.remove("type");
+
+ champObjBuilder.properties(properties)
+ .property("importAssignedId", importAssignedId);
+
+ final ChampObject objectToStore = champObjBuilder.build();
+
+ try {
+ graph.storeObject(objectToStore);
+ } catch (ChampMarshallingException e) {
+ LOGGER.warn("Failed to marshall ChampObject to backend type", e);
+ } catch (ChampSchemaViolationException e) {
+ LOGGER.error("Failed to store object (schema violated): " + objectToStore, e);
+ } catch (ChampObjectNotExistsException e) {
+ LOGGER.error("Failed to update existing ChampObject", e);
+ }
+ }
+
+ @Override
+ public void exportData(ChampGraph graph, OutputStream os) {
+
+ final XMLOutputFactory output = XMLOutputFactory.newInstance();
+
+ try {
+ final XMLStreamWriter writer = output.createXMLStreamWriter(os);
+
+ writer.writeStartDocument();
+ writer.writeStartElement("graphml");
+ writer.writeDefaultNamespace("http://graphml.graphdrawing.org/xmlns");
+ writer.writeNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ writer.writeAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd");
+
+ final List<ChampObject> nodes = new LinkedList<ChampObject> ();
+ final List<ChampRelationship> edges = new LinkedList<ChampRelationship> ();
+ final Map<String, GraphMLKey> nodeKeys = new HashMap<String, GraphMLKey> ();
+ final Map<String, GraphMLKey> edgeKeys = new HashMap<String, GraphMLKey> ();
+ final AtomicInteger elementCount = new AtomicInteger();
+
+ graph.queryObjects(Collections.emptyMap()).forEach(object -> {
+ nodes.add(object);
+
+ for (Map.Entry<String, Object> property : object.getProperties().entrySet()) {
+ if (nodeKeys.containsKey(property.getKey())) continue;
+
+ nodeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
+ }
+
+ nodeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
+ });
+
+ graph.queryRelationships(Collections.emptyMap()).forEach(relationship -> {
+ edges.add(relationship);
+
+ for (Map.Entry<String, Object> property : relationship.getProperties().entrySet()) {
+ if (nodeKeys.containsKey(property.getKey())) continue;
+
+ edgeKeys.put(property.getKey(), new GraphMLKey("d" + elementCount.incrementAndGet(), property.getKey(), property.getValue().getClass()));
+ }
+
+ edgeKeys.put("type", new GraphMLKey("d" + elementCount.incrementAndGet(), "type", String.class));
+ });
+
+ for (Entry<String, GraphMLKey> nodeKey : nodeKeys.entrySet()) {
+ final GraphMLKey graphMlKey = nodeKey.getValue();
+
+ writer.writeStartElement("key");
+ writer.writeAttribute("id", graphMlKey.id);
+ writer.writeAttribute("for", "node");
+ writer.writeAttribute("attr.name", graphMlKey.attrName);
+ writer.writeAttribute("attr.type", graphMlKey.attrType);
+ writer.writeEndElement();
+ }
+
+ for (Entry<String, GraphMLKey> edgeKey : edgeKeys.entrySet()) {
+ final GraphMLKey graphMlKey = edgeKey.getValue();
+
+ writer.writeStartElement("key");
+ writer.writeAttribute("id", graphMlKey.id);
+ writer.writeAttribute("for", "edge");
+ writer.writeAttribute("attr.name", graphMlKey.attrName);
+ writer.writeAttribute("attr.type", graphMlKey.attrType);
+ writer.writeEndElement();
+ }
+
+ for (ChampObject object : nodes) {
+ try {
+ writer.writeStartElement("node");
+ writer.writeAttribute("id", String.valueOf(object.getKey().get()));
+
+ writer.writeStartElement("data");
+ writer.writeAttribute("key", nodeKeys.get("type").id);
+ writer.writeCharacters(object.getType());
+ writer.writeEndElement();
+
+ for (Entry<String, Object> property : object.getProperties().entrySet()) {
+ final GraphMLKey key = nodeKeys.get(property.getKey());
+
+ writer.writeStartElement("data");
+ writer.writeAttribute("key", key.id);
+ writer.writeCharacters(String.valueOf(property.getValue()));
+ writer.writeEndElement();
+ }
+
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new RuntimeException("Failed to write edge to output stream", e);
+ }
+ }
+
+ for (ChampRelationship relationship : edges) {
+ try {
+ writer.writeStartElement("edge");
+ writer.writeAttribute("id", String.valueOf(relationship.getKey().get()));
+
+ writer.writeStartElement("data");
+ writer.writeAttribute("key", edgeKeys.get("type").id);
+ writer.writeCharacters(relationship.getType());
+ writer.writeEndElement();
+
+ for (Entry<String, Object> property : relationship.getProperties().entrySet()) {
+ final GraphMLKey key = edgeKeys.get(property.getKey());
+
+ writer.writeStartElement("data");
+ writer.writeAttribute("key", key.id);
+ writer.writeCharacters(String.valueOf(property.getValue()));
+ writer.writeEndElement();
+ }
+
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new RuntimeException("Failed to write edge to output stream", e);
+ }
+ }
+
+ writer.writeEndElement();
+ writer.writeEndDocument();
+ writer.flush();
+ } catch (XMLStreamException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/ie/Importer.java b/src/main/java/org/openecomp/aai/champ/ie/Importer.java
new file mode 100644
index 0000000..c7aa6a1
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/ie/Importer.java
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.ie;
+
+import java.io.InputStream;
+
+import org.openecomp.aai.champ.ChampAPI;
+
+public interface Importer {
+
+ public void importData(ChampAPI api, InputStream is);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampCardinality.java b/src/main/java/org/openecomp/aai/champ/model/ChampCardinality.java
new file mode 100644
index 0000000..b9735ea
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampCardinality.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+public enum ChampCardinality {
+ SINGLE,
+ LIST,
+ SET
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampConnectionConstraint.java b/src/main/java/org/openecomp/aai/champ/model/ChampConnectionConstraint.java
new file mode 100644
index 0000000..c16e659
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampConnectionConstraint.java
@@ -0,0 +1,96 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampConnectionConstraint.Builder.class)
+public final class ChampConnectionConstraint {
+
+ private final String sourceType;
+ private final String targetType;
+ private final ChampConnectionMultiplicity cardinality;
+
+ private ChampConnectionConstraint() {
+ throw new RuntimeException("Cannot call ConnectionConstraint() constructor");
+ }
+
+ private ChampConnectionConstraint(Builder builder) {
+ this.sourceType = builder.sourceType;
+ this.targetType = builder.targetType;
+ this.cardinality = builder.multiplicity;
+ }
+
+ public String getSourceType() { return sourceType; }
+ public String getTargetType() { return targetType; }
+ public ChampConnectionMultiplicity getMultiplicity() { return cardinality; }
+
+ @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+ public static class Builder {
+ private final String sourceType;
+ private final String targetType;
+
+ private ChampConnectionMultiplicity multiplicity = ChampConnectionMultiplicity.MANY;
+
+ public Builder(@JsonProperty("sourceType") String sourceType, @JsonProperty("targetType") String targetType) {
+ this.sourceType = sourceType;
+ this.targetType = targetType;
+ }
+
+ public Builder multiplicity(ChampConnectionMultiplicity multiplicity) {
+ this.multiplicity = multiplicity;
+ return this;
+ }
+
+ public ChampConnectionConstraint build() {
+ return new ChampConnectionConstraint(this);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * (getSourceType().hashCode() + getTargetType().hashCode());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ChampConnectionConstraint) {
+ final ChampConnectionConstraint connConstraint = (ChampConnectionConstraint) o;
+
+ if (connConstraint.getSourceType().equals(getSourceType()) &&
+ connConstraint.getTargetType().equals(getTargetType()) &&
+ connConstraint.getMultiplicity().equals(getMultiplicity())) return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "{sourceType: " + getSourceType() +
+ ", targetType: " + getTargetType() +
+ ", multiplicity: " + getMultiplicity()
+ + "}";
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampConnectionMultiplicity.java b/src/main/java/org/openecomp/aai/champ/model/ChampConnectionMultiplicity.java
new file mode 100644
index 0000000..337c03c
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampConnectionMultiplicity.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+public enum ChampConnectionMultiplicity {
+ NONE, //Cannot have any relationships of a type between two object types
+ ONE, //Can have zero or one relationship of a type between two object types
+ MANY //Can have zero or more relationships of a type between two object types
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampElement.java b/src/main/java/org/openecomp/aai/champ/model/ChampElement.java
new file mode 100644
index 0000000..329bbfd
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampElement.java
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+public interface ChampElement {
+
+ public boolean isObject();
+ public ChampObject asObject();
+
+ public boolean isRelationship();
+ public ChampRelationship asRelationship();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampField.java b/src/main/java/org/openecomp/aai/champ/model/ChampField.java
new file mode 100644
index 0000000..6d2df69
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampField.java
@@ -0,0 +1,129 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampField.Builder.class)
+public final class ChampField implements Comparable<ChampField> {
+
+ private final String name;
+ private final ChampField.Type type;
+
+ private ChampField() {
+ throw new RuntimeException("Cannot use ChampField() constructor");
+ }
+
+ public String getName() { return name; }
+ public ChampField.Type getType() { return type; }
+
+ @JsonIgnore
+ public Class<?> getJavaType() {
+ switch (type) {
+ case BOOLEAN:
+ return Boolean.class;
+ case DOUBLE:
+ return Double.class;
+ case FLOAT:
+ return Float.class;
+ case INTEGER:
+ return Integer.class;
+ case LONG:
+ return Long.class;
+ case STRING:
+ return String.class;
+ default:
+ throw new RuntimeException("Unknown ChampField.Type " + type);
+ }
+ }
+
+ private ChampField(Builder builder) {
+ this.name = builder.name;
+ this.type = builder.type;
+ }
+
+ public static enum Type {
+ STRING,
+ INTEGER,
+ LONG,
+ DOUBLE,
+ FLOAT,
+ BOOLEAN
+ }
+
+ @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+ public static class Builder {
+ private final String name;
+
+ private ChampField.Type type = ChampField.Type.STRING;
+
+ public Builder(@JsonProperty("name") String name) {
+ this.name = name;
+ }
+
+ public Builder type(ChampField.Type type) {
+ this.type = type;
+ return this;
+ }
+
+ public ChampField build() {
+ return new ChampField(this);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * (getName().hashCode());
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof ChampField) {
+ final ChampField champField = (ChampField) object;
+
+ if (champField.getName().equals(getName())) return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "{name: " + getName() +
+ ", type: " + getType() +
+ "}";
+ }
+
+ @Override
+ public int compareTo(ChampField o) {
+ final int nameComparison = getName().compareTo(o.getName());
+
+ if (nameComparison == 0) {
+ return getType().compareTo(o.getType());
+ }
+
+ return nameComparison;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampObject.java b/src/main/java/org/openecomp/aai/champ/model/ChampObject.java
new file mode 100644
index 0000000..f83d8ca
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampObject.java
@@ -0,0 +1,235 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.openecomp.aai.champ.model.fluent.object.CreateChampObjectable;
+import org.openecomp.aai.champ.model.fluent.object.impl.CreateChampObjectableImpl;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public final class ChampObject implements ChampElement {
+
+ private final String type;
+ private final Optional<Object> key;
+ private final Map<String, Object> properties;
+
+ public static CreateChampObjectable create() {
+ return new CreateChampObjectableImpl();
+ }
+
+ private ChampObject() {
+ throw new RuntimeException("Attempted to call private AAIObject() constructor");
+ } //Not instantiable
+
+ private ChampObject(Builder builder) {
+ this.type = builder.type;
+ this.key = builder.key;
+ this.properties = builder.properties;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> Optional<T> getProperty(String key) {
+ if (!properties.containsKey(key)) return Optional.empty();
+
+ return Optional.of((T) properties.get(key));
+ }
+
+ public String getType() { return type; }
+
+ @JsonIgnore
+ public Optional<Object> getKey() { return key; }
+ public Map<String, Object> getProperties() { return properties; }
+
+ @JsonProperty("key")
+ public Object getKeyValue() {
+ return key.orElse("");
+
+ }
+
+ public static class Builder {
+ private final String type;
+ private final Map<String, Object> properties = new HashMap<String, Object> ();
+
+ private Optional<Object> key = Optional.empty();
+
+ public Builder(String type) {
+ if (type == null) throw new IllegalArgumentException("Type cannot be null");
+
+ this.type = type;
+ }
+
+ public Builder(ChampObject object) {
+ type = object.getType();
+ key = object.getKey();
+ properties(object.getProperties());
+ }
+
+ public Builder key(Object key) {
+ if (key == null) throw new IllegalArgumentException("Key cannot be set to null");
+
+ this.key = Optional.of(key);
+ return this;
+ }
+
+ public Builder property(String key, Object value) {
+ if (key == null) throw new IllegalArgumentException("Property key cannot be null");
+ if (value == null) throw new IllegalArgumentException("Property value cannot be null");
+
+ if (ReservedPropertyKeys.contains(key)) throw new IllegalArgumentException("Property key " + key + " is reserved");
+
+ properties.put(key, value);
+ return this;
+ }
+
+ public Builder properties(Map<String, Object> properties) {
+ for (Entry<String, Object> property : properties.entrySet()) {
+ property(property.getKey(), property.getValue());
+ }
+
+ return this;
+ }
+
+ public ChampObject build() {
+ return new ChampObject(this);
+ }
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) return true;
+
+ if (object instanceof ChampObject) {
+ final ChampObject champObj = (ChampObject) object;
+
+ if (getKey().isPresent() && champObj.getKey().isPresent()) {
+
+ if (getKey().get().equals(champObj.getKey().get())) return true;
+
+ } else if (!getKey().isPresent() && !champObj.getKey().isPresent()) {
+ if (getType().equals(champObj.getType()) &&
+ getProperties().equals(champObj.getProperties())) return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ if (getKey().isPresent()) return getKey().get().hashCode();
+
+ final int returnValue = 31 * (getType().hashCode() + getProperties().hashCode());
+ return returnValue;
+ }
+
+ @Override
+ public String toString() {
+ return "{key: " + (getKey().isPresent() ? getKey().get() : "")
+ + ", type: " + getType()
+ + ", properties: " + getProperties() + "}";
+ }
+
+ public enum ReservedPropertyKeys {
+ CHAMP_OBJECT_TYPE ("aai_node_type"),
+ CHAMP_OBJECT_KEY ("key");
+
+ private final String text;
+
+ private ReservedPropertyKeys(final String text) {
+ this.text = text;
+ }
+
+ public String toString() {
+ return text;
+ }
+
+ public static boolean contains(String key) {
+ for (ReservedPropertyKeys choice : ReservedPropertyKeys.values()) {
+ if (choice.toString().equals(key)) return true;
+ }
+
+ return false;
+ }
+ }
+
+ public enum IgnoreOnReadPropertyKeys {
+ CHAMP_IMPORT_ASSIGNED_ID ("importAssignedId");
+
+ private final String text;
+
+ private IgnoreOnReadPropertyKeys(final String text) {
+ this.text = text;
+ }
+
+ public String toString() {
+ return text;
+ }
+
+ public static boolean contains(String key) {
+ for (IgnoreOnReadPropertyKeys choice : IgnoreOnReadPropertyKeys.values()) {
+ if (choice.toString().equals(key)) return true;
+ }
+
+ return false;
+ }
+ }
+
+ public enum ReservedTypes {
+ ANY ("ANY");
+
+ private final String text;
+
+ private ReservedTypes(final String text) {
+ this.text = text;
+ }
+
+ public String toString() {
+ return text;
+ }
+ }
+
+ @Override
+ public boolean isObject() {
+ return true;
+ }
+
+ @Override
+ public ChampObject asObject() {
+ return this;
+ }
+
+ @Override
+ public boolean isRelationship() {
+ return false;
+ }
+
+ @Override
+ public ChampRelationship asRelationship() {
+ throw new UnsupportedOperationException("Cannot call asRelationship() on ChampObject");
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampObjectConstraint.java b/src/main/java/org/openecomp/aai/champ/model/ChampObjectConstraint.java
new file mode 100644
index 0000000..5afd803
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampObjectConstraint.java
@@ -0,0 +1,107 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampObjectConstraint.Builder.class)
+public final class ChampObjectConstraint {
+
+ private final String type;
+ private final Map<String, ChampPropertyConstraint> propertyConstraints;
+
+ private ChampObjectConstraint() {
+ throw new RuntimeException("Cannot call ChampObjectConstraint() constructor");
+ }
+
+ private ChampObjectConstraint(Builder builder) {
+ this.type = builder.type;
+ this.propertyConstraints = builder.propertyConstraints;
+ }
+
+ public String getType() { return type; }
+ public Set<ChampPropertyConstraint> getPropertyConstraints() { return new HashSet<ChampPropertyConstraint> (propertyConstraints.values()); }
+
+ public Optional<ChampPropertyConstraint> getPropertyConstraint(String fieldName) {
+ if (!propertyConstraints.containsKey(fieldName)) return Optional.empty();
+
+ return Optional.of(propertyConstraints.get(fieldName));
+ }
+
+ @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+ public static class Builder {
+ private final String type;
+ private final Map<String, ChampPropertyConstraint> propertyConstraints;
+
+ public Builder(@JsonProperty("type") String type) {
+ this.type = type;
+ this.propertyConstraints = new HashMap<String, ChampPropertyConstraint> ();
+ }
+
+ @JsonProperty("propertyConstraints")
+ public Builder constraints(Set<ChampPropertyConstraint> propertyConstraints) {
+
+ for (ChampPropertyConstraint propConstraint : propertyConstraints) {
+ constraint(propConstraint);
+ }
+
+ return this;
+ }
+
+ @JsonIgnore
+ public Builder constraint(ChampPropertyConstraint propConstraint) {
+ propertyConstraints.put(propConstraint.getField().getName(), propConstraint);
+ return this;
+ }
+
+ public ChampObjectConstraint build() {
+ return new ChampObjectConstraint(this);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ChampObjectConstraint) {
+ final ChampObjectConstraint objectConstraint = (ChampObjectConstraint) o;
+
+ if (objectConstraint.getType().equals(getType())) return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "{type: " + getType() +
+ ", propertyConstraints: " + getPropertyConstraints() +
+ "}";
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampObjectIndex.java b/src/main/java/org/openecomp/aai/champ/model/ChampObjectIndex.java
new file mode 100644
index 0000000..9b588c7
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampObjectIndex.java
@@ -0,0 +1,87 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import org.openecomp.aai.champ.model.fluent.index.CreateObjectIndexable;
+import org.openecomp.aai.champ.model.fluent.index.impl.CreateObjectIndexableImpl;
+
+public final class ChampObjectIndex {
+
+ private final String name;
+ private final String type;
+ private final ChampField field;
+
+ public static CreateObjectIndexable create() {
+ return new CreateObjectIndexableImpl();
+ }
+
+ private ChampObjectIndex() {
+ throw new RuntimeException("Cannot call ChampObjectIndex() constructor");
+ }
+
+ private ChampObjectIndex(Builder builder) {
+ this.name = builder.name;
+ this.type = builder.type;
+ this.field = builder.field;
+ }
+
+ public String getName() { return name; }
+ public String getType() { return type; }
+ public ChampField getField() { return field; }
+
+ public static class Builder {
+ private final String name;
+ private final String type;
+ private final ChampField field;
+
+ public Builder(String name, String type, ChampField field) {
+ this.name = name;
+ this.type = type;
+ this.field = field;
+ }
+
+ public ChampObjectIndex build() {
+ return new ChampObjectIndex(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{name: " + getName()
+ + ", type: " + getType()
+ + ", field: " + getField() + "}";
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) return true;
+
+ if (object instanceof ChampObjectIndex) {
+ final ChampObjectIndex objectIndex = (ChampObjectIndex) object;
+
+ if (getName().equals(objectIndex.getName()) &&
+ getField().getName().equals(objectIndex.getField().getName())) return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampPartition.java b/src/main/java/org/openecomp/aai/champ/model/ChampPartition.java
new file mode 100644
index 0000000..ca7de74
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampPartition.java
@@ -0,0 +1,144 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.openecomp.aai.champ.model.fluent.partition.CreateChampPartitionable;
+import org.openecomp.aai.champ.model.fluent.partition.impl.CreateChampPartionableImpl;
+
+public final class ChampPartition {
+
+ private final Set<ChampObject> champObjects;
+ private final Set<ChampRelationship> champRelationships;
+
+ private ChampPartition() {
+ throw new RuntimeException("Cannot call ChampGraph() constructor");
+ }
+
+ private ChampPartition(Builder builder) {
+ this.champObjects = builder.champObjects;
+ this.champRelationships = builder.champRelationships;
+ }
+
+ public static CreateChampPartitionable create() {
+ return new CreateChampPartionableImpl();
+ }
+
+ public Set<ChampObject> getChampObjects() { return champObjects; }
+ public Set<ChampRelationship> getChampRelationships() { return champRelationships; }
+
+ public Set<ChampRelationship> getIncidentRelationships(ChampObject source) {
+ final Set<ChampRelationship> incidentRelationships = new HashSet<ChampRelationship> ();
+
+ for (ChampRelationship relationship : getChampRelationships()) {
+ if (relationship.getSource().equals(source) ||
+ relationship.getTarget().equals(source)) {
+ incidentRelationships.add(relationship);
+ }
+ }
+
+ return incidentRelationships;
+ }
+
+ public Map<String, Set<ChampRelationship>> getIncidentRelationshipsByType(ChampObject source) {
+ final Map<String, Set<ChampRelationship>> incidentRelationships = new HashMap<String, Set<ChampRelationship>> ();
+
+ for (ChampRelationship relationship : getChampRelationships()) {
+ if (relationship.getSource().equals(source) ||
+ relationship.getTarget().equals(source)) {
+ if (!incidentRelationships.containsKey(relationship.getType())) {
+ incidentRelationships.put(relationship.getType(), new HashSet<ChampRelationship> ());
+ }
+
+ incidentRelationships.get(relationship.getType()).add(relationship);
+ }
+ }
+
+ return incidentRelationships;
+ }
+
+ public static class Builder {
+ private final Set<ChampObject> champObjects;
+ private final Set<ChampRelationship> champRelationships;
+
+ public Builder() {
+ this.champObjects = new HashSet<ChampObject> ();
+ this.champRelationships = new HashSet<ChampRelationship> ();
+ }
+
+ public Builder object(ChampObject object) {
+ champObjects.add(object);
+ return this;
+ }
+
+ public Builder relationship(ChampRelationship relationship) {
+ champRelationships.add(relationship);
+ return this;
+ }
+
+ public Builder objects(Set<ChampObject> objects) {
+ champObjects.addAll(objects);
+ return this;
+ }
+
+ public Builder relationships(Set<ChampRelationship> relationships) {
+ champRelationships.addAll(relationships);
+ return this;
+ }
+
+ public ChampPartition build() {
+ return new ChampPartition(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+
+ final StringBuilder sb = new StringBuilder();
+
+ sb.append("{objects: [");
+
+ for (ChampObject object : champObjects) {
+ sb.append(object.toString());
+ sb.append(",");
+ }
+
+ if (sb.charAt(sb.length() - 1) == ',') sb.deleteCharAt(sb.length() - 1); //Delete last comma
+
+ sb.append("], relationships: [");
+
+ for (ChampRelationship relationship : champRelationships) {
+ sb.append(relationship.toString());
+ sb.append(",");
+ }
+
+ if (sb.charAt(sb.length() - 1) == ',') sb.deleteCharAt(sb.length() - 1); //Delete last comma
+
+ sb.append("]}");
+
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampPropertyConstraint.java b/src/main/java/org/openecomp/aai/champ/model/ChampPropertyConstraint.java
new file mode 100644
index 0000000..d244f5a
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampPropertyConstraint.java
@@ -0,0 +1,110 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampPropertyConstraint.Builder.class)
+public final class ChampPropertyConstraint implements Comparable<ChampPropertyConstraint> {
+
+ private final ChampField field;
+ private final boolean required;
+ private final ChampCardinality cardinality;
+
+ private ChampPropertyConstraint() {
+ throw new RuntimeException("Cannot call ChampPropertyConstraint() constructor");
+ }
+
+ private ChampPropertyConstraint(Builder builder) {
+ this.field = builder.field;
+ this.required = builder.required;
+ this.cardinality = builder.cardinality;
+ }
+
+ public ChampField getField() { return field; }
+ public boolean isRequired() { return required; }
+ public ChampCardinality getCardinality() { return cardinality; }
+
+ @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+ public static class Builder {
+ private final ChampField field;
+
+ private boolean required = false;
+ private ChampCardinality cardinality = ChampCardinality.SINGLE;
+
+ public Builder(@JsonProperty("field") ChampField field) {
+ this.field = field;
+ }
+
+ public Builder required(boolean required) {
+ this.required = required;
+ return this;
+ }
+
+ public Builder cardinality(ChampCardinality cardinality) {
+ this.cardinality = cardinality;
+ return this;
+ }
+
+ public ChampPropertyConstraint build() {
+ return new ChampPropertyConstraint(this);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * (getField().hashCode());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ChampPropertyConstraint) {
+ final ChampPropertyConstraint propertyConstraint = (ChampPropertyConstraint) o;
+
+ if (propertyConstraint.getField().equals(getField()))
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "{field: " + getField() +
+ ", required: " + isRequired() +
+ ", cardinality: " + getCardinality() +
+ "}";
+ }
+
+ @Override
+ public int compareTo(ChampPropertyConstraint o) {
+ final int fieldComparison = o.getField().compareTo(getField());
+
+ if (fieldComparison == 0) {
+ return o.getCardinality().compareTo(getCardinality());
+ }
+
+ return fieldComparison;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampRelationship.java b/src/main/java/org/openecomp/aai/champ/model/ChampRelationship.java
new file mode 100644
index 0000000..3e5e366
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampRelationship.java
@@ -0,0 +1,217 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.openecomp.aai.champ.model.fluent.relationship.CreateChampRelationshipable;
+import org.openecomp.aai.champ.model.fluent.relationship.impl.CreateChampRelationshipableImpl;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public final class ChampRelationship implements ChampElement {
+ private final String type; //AKA edge label
+ private final Optional<Object> key;
+ private final Map<String, Object> properties;
+ private final ChampObject source;
+ private final ChampObject target;
+
+ public static CreateChampRelationshipable create() {
+ return new CreateChampRelationshipableImpl();
+ }
+
+ private ChampRelationship() { //Not instantiable
+ throw new RuntimeException("Cannot call ChampRelationship() constructor");
+ }
+
+ private ChampRelationship(Builder builder) {
+ this.properties = builder.properties;
+ this.source = builder.source;
+ this.target = builder.target;
+ this.type = builder.type;
+ this.key = builder.key;
+ }
+
+ @JsonIgnore
+ public Optional<Object> getKey() {
+ return key;
+ }
+
+ @JsonProperty("key")
+ public Object getKeyValue() {
+ return key.orElse("");
+ }
+
+ public ChampObject getSource() {
+ return source;
+ }
+
+ public ChampObject getTarget() {
+ return target;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> Optional<T> getProperty(String key) {
+ if (!properties.containsKey(key)) return Optional.empty();
+
+ return Optional.of((T) properties.get(key));
+ }
+
+ public Map<String, Object> getProperties() {
+ return properties;
+ }
+
+ public static class Builder {
+ private final Map<String, Object> properties = new HashMap<String, Object> ();
+ private final ChampObject source;
+ private final ChampObject target;
+ private final String type;
+
+ private Optional<Object> key = Optional.empty();
+
+ public Builder(ChampObject source, ChampObject target, String type) {
+ this.source = source;
+ this.target = target;
+ this.type = type;
+ }
+
+ public Builder(ChampRelationship relationship) {
+ this.source = relationship.source;
+ this.target = relationship.target;
+ this.type = relationship.type;
+
+ properties.putAll(relationship.getProperties());
+ }
+
+ public Builder key(Object key) {
+ this.key = Optional.of(key);
+ return this;
+ }
+
+ public Builder properties(Map<String, Object> properties) {
+ for (Entry<String, Object> property : properties.entrySet()) {
+ property(property.getKey(), property.getValue());
+ }
+
+ return this;
+ }
+
+ public Builder property(String key, Object value) {
+ if (ChampRelationship.ReservedPropertyKeys.contains(key)) throw new IllegalArgumentException("Cannot make use of reserved property key " + key);
+
+ properties.put(key, value);
+ return this;
+ }
+
+ public ChampRelationship build() {
+ return new ChampRelationship(this);
+ }
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) return true;
+ if (object instanceof ChampRelationship) {
+ final ChampRelationship champRelationship = (ChampRelationship) object;
+
+ if (getKey().isPresent() && champRelationship.getKey().isPresent()) {
+ if (getKey().get().equals(champRelationship.getKey().get())) return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "{key: " + (getKey().isPresent() ? getKey().get() : "")
+ + ", type: " + getType()
+ + ", source: " + getSource()
+ + ", target: " + getTarget()
+ + ", properties: " + getProperties() + "}";
+ }
+
+ public enum ReservedPropertyKeys {
+ CHAMP_RELATIONSHIP_TYPE ("relationshipType"),
+ CHAMP_RELATIONSHIP_KEY ("key");
+
+ private final String text;
+
+ private ReservedPropertyKeys(final String text) {
+ this.text = text;
+ }
+
+ public String toString() {
+ return text;
+ }
+
+ public static boolean contains(String key) {
+ for (ReservedPropertyKeys choice : ReservedPropertyKeys.values()) {
+ if (choice.toString().equals(key)) return true;
+ }
+
+ return false;
+ }
+ }
+
+ public enum ReservedTypes {
+ ANY ("ANY");
+
+ private final String text;
+
+ private ReservedTypes(final String text) {
+ this.text = text;
+ }
+
+ public String toString() {
+ return text;
+ }
+ }
+
+ @Override
+ public boolean isObject() {
+ return false;
+ }
+
+ @Override
+ public ChampObject asObject() {
+ throw new UnsupportedOperationException("Cannot call asObject() on ChampRelationship");
+ }
+
+ @Override
+ public boolean isRelationship() {
+ return true;
+ }
+
+ @Override
+ public ChampRelationship asRelationship() {
+ return this;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipConstraint.java b/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipConstraint.java
new file mode 100644
index 0000000..238637d
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipConstraint.java
@@ -0,0 +1,139 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampRelationshipConstraint.Builder.class)
+public final class ChampRelationshipConstraint {
+
+ private final String type;
+ private final Map<String, ChampPropertyConstraint> propertyConstraints;
+ private final Set<ChampConnectionConstraint> connectionConstraints;
+
+ private ChampRelationshipConstraint() {
+ throw new RuntimeException("Cannot call ChampObjectConstraint() constructor");
+ }
+
+ private ChampRelationshipConstraint(Builder builder) {
+ this.type = builder.type;
+ this.propertyConstraints = builder.propertyConstraints;
+ this.connectionConstraints = builder.connectionConstraints;
+ }
+
+ public String getType() { return type; }
+ public Set<ChampPropertyConstraint> getPropertyConstraints() { return new HashSet<ChampPropertyConstraint> (propertyConstraints.values()); }
+
+ public Optional<ChampPropertyConstraint> getPropertyConstraint(String fieldName) {
+ if (!propertyConstraints.containsKey(fieldName)) return Optional.empty();
+
+ return Optional.of(propertyConstraints.get(fieldName));
+ }
+
+ public Set<ChampConnectionConstraint> getConnectionConstraints() { return connectionConstraints; }
+
+ public Optional<ChampConnectionConstraint> getConnectionConstraint(ChampRelationship incidentRelationship) {
+ if (!incidentRelationship.getType().equals(getType())) return Optional.empty();
+
+ final String sourceType = incidentRelationship.getSource().getType();
+ final String targetType = incidentRelationship.getTarget().getType();
+
+ for (ChampConnectionConstraint connConstraint : getConnectionConstraints()) {
+ if (connConstraint.getSourceType().equals(sourceType) &&
+ connConstraint.getTargetType().equals(targetType)) return Optional.of(connConstraint);
+ }
+
+ return Optional.empty();
+ }
+
+ @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+ public static class Builder {
+ private final String type;
+ private final Map<String, ChampPropertyConstraint> propertyConstraints;
+ private final Set<ChampConnectionConstraint> connectionConstraints;
+
+ public Builder(@JsonProperty("type") String type) {
+ this.type = type;
+ this.propertyConstraints = new HashMap<String, ChampPropertyConstraint> ();
+ this.connectionConstraints = new HashSet<ChampConnectionConstraint> ();
+ }
+
+ public Builder propertyConstraints(Set<ChampPropertyConstraint> propertyConstraints) {
+
+ for (ChampPropertyConstraint propConstraint : propertyConstraints) {
+ constraint(propConstraint);
+ }
+
+ return this;
+ }
+
+ public Builder connectionConstraints(Set<ChampConnectionConstraint> connectionConstraints) {
+ this.connectionConstraints.addAll(connectionConstraints);
+ return this;
+ }
+
+ @JsonIgnore
+ public Builder constraint(ChampPropertyConstraint propConstraint) {
+ propertyConstraints.put(propConstraint.getField().getName(), propConstraint);
+ return this;
+ }
+
+ @JsonIgnore
+ public Builder constraint(ChampConnectionConstraint connConstraint) {
+ connectionConstraints.add(connConstraint);
+ return this;
+ }
+ public ChampRelationshipConstraint build() {
+ return new ChampRelationshipConstraint(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{type: " + getType() +
+ ", connectionConstraints: " + getConnectionConstraints() +
+ ", propertyConstraints: " + getPropertyConstraints() +
+ "}";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof ChampRelationshipConstraint) {
+ final ChampRelationshipConstraint relConstraint = (ChampRelationshipConstraint) o;
+
+ if (relConstraint.getType().equals(getType()) &&
+ relConstraint.getConnectionConstraints().equals(getConnectionConstraints()) &&
+ relConstraint.getPropertyConstraints().equals(getPropertyConstraints())) return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipIndex.java b/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipIndex.java
new file mode 100644
index 0000000..7f8f36f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampRelationshipIndex.java
@@ -0,0 +1,87 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import org.openecomp.aai.champ.model.fluent.index.CreateRelationshipIndexable;
+import org.openecomp.aai.champ.model.fluent.index.impl.CreateRelationshipIndexableImpl;
+
+public final class ChampRelationshipIndex {
+
+ private final String name;
+ private final String type;
+ private final ChampField field;
+
+ public static CreateRelationshipIndexable create() {
+ return new CreateRelationshipIndexableImpl();
+ }
+
+ private ChampRelationshipIndex() {
+ throw new RuntimeException("Cannot call ChampRelationshipIndex() constructor");
+ }
+
+ private ChampRelationshipIndex(Builder builder) {
+ this.name = builder.name;
+ this.type = builder.type;
+ this.field = builder.field;
+ }
+
+ public String getName() { return name; }
+ public String getType() { return type; }
+ public ChampField getField() { return field; }
+
+ public static class Builder {
+ private final String name;
+ private final String type;
+ private final ChampField field;
+
+ public Builder(String name, String type, ChampField field) {
+ this.name = name;
+ this.type = type;
+ this.field = field;
+ }
+
+ public ChampRelationshipIndex build() {
+ return new ChampRelationshipIndex(this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "{name: " + getName()
+ + ", type: " + getType()
+ + ", field: " + getField() + "}";
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) return true;
+
+ if (object instanceof ChampRelationshipIndex) {
+ final ChampRelationshipIndex relationshipIndex = (ChampRelationshipIndex) object;
+
+ if (getName().equals(relationshipIndex.getName()) &&
+ getField().getName().equals(relationshipIndex.getField().getName())) return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/ChampSchema.java b/src/main/java/org/openecomp/aai/champ/model/ChampSchema.java
new file mode 100644
index 0000000..90c42b3
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/ChampSchema.java
@@ -0,0 +1,148 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import org.openecomp.aai.champ.model.fluent.schema.CreateChampSchemable;
+import org.openecomp.aai.champ.model.fluent.schema.impl.CreateChampSchemableImpl;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+
+@JsonDeserialize(builder = ChampSchema.Builder.class)
+public final class ChampSchema {
+
+ private final Map<String, ChampObjectConstraint> objectConstraints;
+ private final Map<String, ChampRelationshipConstraint> relationshipConstraints;
+
+ private ChampSchema() {
+ throw new RuntimeException("Cannot call ChampSchema() constructor");
+ }
+
+ private ChampSchema(Builder builder) {
+ this.objectConstraints = builder.objectConstraints;
+ this.relationshipConstraints = builder.relationshipConstraints;
+ }
+
+ public static CreateChampSchemable create() {
+ return new CreateChampSchemableImpl();
+ }
+
+ public Optional<ChampObjectConstraint> getObjectConstraint(String type) {
+ if (!getObjectConstraints().containsKey(type)) return Optional.empty();
+
+ return Optional.of(getObjectConstraints().get(type));
+ }
+
+ public Optional<ChampRelationshipConstraint> getRelationshipConstraint(String type) {
+ if (!getRelationshipConstraints().containsKey(type)) return Optional.empty();
+
+ return Optional.of(getRelationshipConstraints().get(type));
+ }
+
+ public Map<String, ChampObjectConstraint> getObjectConstraints() {
+ return objectConstraints;
+ }
+
+ public Map<String, ChampRelationshipConstraint> getRelationshipConstraints() {
+ return relationshipConstraints;
+ }
+
+ @JsonPOJOBuilder(buildMethodName = "build", withPrefix = "")
+ public static class Builder {
+ private Map<String, ChampObjectConstraint> objectConstraints = new HashMap<String, ChampObjectConstraint> ();
+ private Map<String, ChampRelationshipConstraint> relationshipConstraints = new HashMap<String, ChampRelationshipConstraint> ();
+
+ public Builder() {}
+
+ public Builder(ChampSchema schema) {
+ objectConstraints.putAll(schema.getObjectConstraints());
+ relationshipConstraints.putAll(schema.getRelationshipConstraints());
+ }
+
+ public Builder objectConstraints(Map<String, ChampObjectConstraint> objectConstraints) {
+ this.objectConstraints.putAll(objectConstraints);
+ return this;
+ }
+
+ public Builder relationshipConstraints(Map<String, ChampRelationshipConstraint> relationshipConstraints) {
+ this.relationshipConstraints.putAll(relationshipConstraints);
+ return this;
+ }
+
+ @JsonIgnore
+ public Builder constraint(ChampObjectConstraint objConstraint) {
+ objectConstraints.put(objConstraint.getType(), objConstraint);
+ return this;
+ }
+
+ @JsonIgnore
+ public Builder constraint(ChampRelationshipConstraint relConstraint) {
+ relationshipConstraints.put(relConstraint.getType(), relConstraint);
+ return this;
+ }
+
+ public ChampSchema build() {
+ return new ChampSchema(this);
+ }
+ }
+
+ @Override
+ public boolean equals(Object schema) {
+ if (schema instanceof ChampSchema) {
+
+ for (Entry<String, ChampObjectConstraint> objConstraint : getObjectConstraints().entrySet()) {
+ final Optional<ChampObjectConstraint> champObjConstraint = ((ChampSchema) schema).getObjectConstraint(objConstraint.getKey());
+
+ if (!champObjConstraint.isPresent() ||
+ !champObjConstraint.get().equals(objConstraint.getValue())) return false;
+ }
+
+ for (Entry<String, ChampRelationshipConstraint> relConstraint : getRelationshipConstraints().entrySet()) {
+ final Optional<ChampRelationshipConstraint> champRelConstraint = ((ChampSchema) schema).getRelationshipConstraint(relConstraint.getKey());
+
+ if (!champRelConstraint.isPresent() ||
+ !champRelConstraint.get().equals(relConstraint.getValue())) return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "{objectConstraints: " + getObjectConstraints() +
+ ", relationshipConstraints: " + getRelationshipConstraints() +
+ "}";
+ }
+
+ public static ChampSchema emptySchema() {
+ return new ChampSchema.Builder().build();
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/BuildStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/BuildStep.java
new file mode 100644
index 0000000..4da29c3
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/BuildStep.java
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent;
+
+public interface BuildStep<T> {
+ public T build();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/KeyStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/KeyStep.java
new file mode 100644
index 0000000..291f087
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/KeyStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent;
+
+public interface KeyStep<T> {
+ public T withKey(Object key);
+ public T withoutKey();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/PropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/PropertiesStep.java
new file mode 100644
index 0000000..3f67650
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/PropertiesStep.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent;
+
+import java.util.Map;
+
+public interface PropertiesStep<T> {
+
+ public T withProperty(String key, Object value);
+ public T withProperties(Map<String, Object> properties);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateObjectIndexable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateObjectIndexable.java
new file mode 100644
index 0000000..f2281ed
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateObjectIndexable.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.index;
+
+public interface CreateObjectIndexable {
+
+ public ObjectIndexTypeStep ofName(String name);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateRelationshipIndexable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateRelationshipIndexable.java
new file mode 100644
index 0000000..e4f42ae
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/index/CreateRelationshipIndexable.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.index;
+
+public interface CreateRelationshipIndexable {
+
+ public RelationshipIndexTypeStep ofName(String name);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexFieldStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexFieldStep.java
new file mode 100644
index 0000000..be1d60b
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexFieldStep.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.index;
+
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface ObjectIndexFieldStep {
+
+ public BuildStep<ChampObjectIndex> forField(String fieldName);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexTypeStep.java
new file mode 100644
index 0000000..99977e6
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/index/ObjectIndexTypeStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.index;
+
+public interface ObjectIndexTypeStep {
+
+ public ObjectIndexFieldStep onType(String objectType);
+ public ObjectIndexFieldStep onAnyType();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexFieldStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexFieldStep.java
new file mode 100644
index 0000000..fb784e5
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexFieldStep.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.index;
+
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface RelationshipIndexFieldStep {
+
+ public BuildStep<ChampRelationshipIndex> forField(String fieldName);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexTypeStep.java
new file mode 100644
index 0000000..988dc05
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/index/RelationshipIndexTypeStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.index;
+
+public interface RelationshipIndexTypeStep {
+
+ public RelationshipIndexFieldStep onType(String objectType);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateObjectIndexableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateObjectIndexableImpl.java
new file mode 100644
index 0000000..675dbb2
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateObjectIndexableImpl.java
@@ -0,0 +1,77 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.index.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.index.CreateObjectIndexable;
+import org.openecomp.aai.champ.model.fluent.index.ObjectIndexFieldStep;
+import org.openecomp.aai.champ.model.fluent.index.ObjectIndexTypeStep;
+
+public final class CreateObjectIndexableImpl implements CreateObjectIndexable {
+
+ @Override
+ public ObjectIndexTypeStep ofName(String name) {
+ return new ObjectIndexTypeStep() {
+
+ @Override
+ public ObjectIndexFieldStep onType(String objectType) {
+ return new ObjectIndexFieldStep() {
+
+ @Override
+ public BuildStep<ChampObjectIndex> forField(String fieldName) {
+ return new BuildStep<ChampObjectIndex> () {
+
+ @Override
+ public ChampObjectIndex build() {
+ return new ChampObjectIndex.Builder(
+ name, objectType, new ChampField.Builder(fieldName).build()
+ ).build();
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public ObjectIndexFieldStep onAnyType() {
+ return new ObjectIndexFieldStep() {
+
+ @Override
+ public BuildStep<ChampObjectIndex> forField(String fieldName) {
+ return new BuildStep<ChampObjectIndex> () {
+
+ @Override
+ public ChampObjectIndex build() {
+ return new ChampObjectIndex.Builder(
+ name, ChampObject.ReservedTypes.ANY.toString(), new ChampField.Builder(fieldName).build()
+ ).build();
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateRelationshipIndexableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateRelationshipIndexableImpl.java
new file mode 100644
index 0000000..d41839e
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/index/impl/CreateRelationshipIndexableImpl.java
@@ -0,0 +1,58 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.index.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.index.CreateRelationshipIndexable;
+import org.openecomp.aai.champ.model.fluent.index.RelationshipIndexFieldStep;
+import org.openecomp.aai.champ.model.fluent.index.RelationshipIndexTypeStep;
+
+public final class CreateRelationshipIndexableImpl implements CreateRelationshipIndexable {
+
+ @Override
+ public RelationshipIndexTypeStep ofName(String name) {
+ return new RelationshipIndexTypeStep() {
+
+ @Override
+ public RelationshipIndexFieldStep onType(String relationshipType) {
+ return new RelationshipIndexFieldStep() {
+
+ @Override
+ public BuildStep<ChampRelationshipIndex> forField(String fieldName) {
+ return new BuildStep<ChampRelationshipIndex> () {
+
+ @Override
+ public ChampRelationshipIndex build() {
+ return new ChampRelationshipIndex.Builder(
+ name, relationshipType, new ChampField.Builder(fieldName).build()
+ ).build();
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/CreateChampObjectable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/CreateChampObjectable.java
new file mode 100644
index 0000000..79c2026
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/object/CreateChampObjectable.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.object;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+
+public interface CreateChampObjectable {
+ public KeyStep<ObjectBuildOrPropertiesStep> ofType(String type);
+ public KeyStep<ObjectBuildOrPropertiesStep> from(ChampObject object);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStep.java
new file mode 100644
index 0000000..73cdb78
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStep.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.object;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.PropertiesStep;
+
+public interface ObjectBuildOrPropertiesStep extends BuildStep<ChampObject>, PropertiesStep<ObjectBuildOrPropertiesStep> {
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStepImpl.java
new file mode 100644
index 0000000..5feabcf
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectBuildOrPropertiesStepImpl.java
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.object;
+
+import java.util.Map;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObject.Builder;
+
+public final class ObjectBuildOrPropertiesStepImpl implements ObjectBuildOrPropertiesStep {
+
+ private final ChampObject.Builder builder;
+
+ public ObjectBuildOrPropertiesStepImpl(Builder builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public ChampObject build() {
+ return builder.build();
+ }
+
+ @Override
+ public ObjectBuildOrPropertiesStep withProperty(String key, Object value) {
+ builder.property(key, value);
+ return this;
+ }
+
+ @Override
+ public ObjectBuildOrPropertiesStep withProperties(Map<String, Object> properties) {
+ builder.properties(properties);
+ return this;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectKeyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectKeyStepImpl.java
new file mode 100644
index 0000000..a21aecc
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/object/ObjectKeyStepImpl.java
@@ -0,0 +1,47 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.object;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObject.Builder;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+
+public final class ObjectKeyStepImpl implements KeyStep<ObjectBuildOrPropertiesStep> {
+
+ private final ChampObject.Builder builder;
+
+ public ObjectKeyStepImpl(Builder builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public ObjectBuildOrPropertiesStep withKey(Object key) {
+ builder.key(key);
+ return new ObjectBuildOrPropertiesStepImpl(builder);
+ }
+
+ @Override
+ public ObjectBuildOrPropertiesStep withoutKey() {
+ return new ObjectBuildOrPropertiesStepImpl(builder);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/object/impl/CreateChampObjectableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/object/impl/CreateChampObjectableImpl.java
new file mode 100644
index 0000000..4645b44
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/object/impl/CreateChampObjectableImpl.java
@@ -0,0 +1,42 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.object.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+import org.openecomp.aai.champ.model.fluent.object.CreateChampObjectable;
+import org.openecomp.aai.champ.model.fluent.object.ObjectBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.object.ObjectKeyStepImpl;
+
+public final class CreateChampObjectableImpl implements CreateChampObjectable {
+
+ @Override
+ public KeyStep<ObjectBuildOrPropertiesStep> ofType(String type) {
+ return new ObjectKeyStepImpl(new ChampObject.Builder(type));
+ }
+
+ @Override
+ public KeyStep<ObjectBuildOrPropertiesStep> from(ChampObject object) {
+ return new ObjectKeyStepImpl(new ChampObject.Builder(object));
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/partition/CreateChampPartitionable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/partition/CreateChampPartitionable.java
new file mode 100644
index 0000000..e2a36fb
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/partition/CreateChampPartitionable.java
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.partition;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface CreateChampPartitionable extends BuildStep<ChampPartition> {
+
+ public CreateChampPartitionable withObject(ChampObject object);
+ public CreateChampPartitionable withRelationship(ChampRelationship relationship);
+ public ChampPartition build();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/partition/impl/CreateChampPartionableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/partition/impl/CreateChampPartionableImpl.java
new file mode 100644
index 0000000..ddf9663
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/partition/impl/CreateChampPartionableImpl.java
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.partition.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.partition.CreateChampPartitionable;
+
+public final class CreateChampPartionableImpl implements CreateChampPartitionable {
+
+ private final ChampPartition.Builder builder;
+
+ public CreateChampPartionableImpl() {
+ this.builder = new ChampPartition.Builder();
+ }
+
+ @Override
+ public CreateChampPartitionable withObject(ChampObject object) {
+ builder.object(object);
+ return this;
+ }
+
+ @Override
+ public CreateChampPartitionable withRelationship(ChampRelationship relationship) {
+ builder.relationship(relationship);
+ return this;
+ }
+
+ @Override
+ public ChampPartition build() {
+ return builder.build();
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/CreateChampRelationshipable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/CreateChampRelationshipable.java
new file mode 100644
index 0000000..15b96f1
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/CreateChampRelationshipable.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+
+public interface CreateChampRelationshipable {
+ public KeyStep<SourceStep> ofType(String type);
+ public KeyStep<RelationshipBuildOrPropertiesStep> from(ChampRelationship relationship);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/RelationshipBuildOrPropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/RelationshipBuildOrPropertiesStep.java
new file mode 100644
index 0000000..09c18b8
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/RelationshipBuildOrPropertiesStep.java
@@ -0,0 +1,29 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.PropertiesStep;
+
+public interface RelationshipBuildOrPropertiesStep extends BuildStep<ChampRelationship>, PropertiesStep<RelationshipBuildOrPropertiesStep> {
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceBuildOrPropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceBuildOrPropertiesStep.java
new file mode 100644
index 0000000..51d4dda
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceBuildOrPropertiesStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceBuildOrPropertiesStep {
+
+ public SourceBuildOrPropertiesStep withProperty(String key, Object value);
+ public TargetStep build();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceFromStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceFromStep.java
new file mode 100644
index 0000000..4dbc845
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceFromStep.java
@@ -0,0 +1,29 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+import org.openecomp.aai.champ.model.ChampObject;
+
+public interface SourceFromStep {
+
+ public SourceBuildOrPropertiesStep from(ChampObject object);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceKeyStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceKeyStep.java
new file mode 100644
index 0000000..c054364
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceKeyStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceKeyStep {
+
+ public SourceBuildOrPropertiesStep withKey(Object key);
+ public SourceBuildOrPropertiesStep withoutKey();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceStep.java
new file mode 100644
index 0000000..6e467bc
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceStep.java
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceStep {
+ public SourceTypeOrFromStep withSource();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeOrFromStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeOrFromStep.java
new file mode 100644
index 0000000..5597496
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeOrFromStep.java
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceTypeOrFromStep extends SourceTypeStep, SourceFromStep {
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeStep.java
new file mode 100644
index 0000000..6c1f976
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/SourceTypeStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface SourceTypeStep {
+
+ public SourceKeyStep ofType(String type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetBuildOrPropertiesStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetBuildOrPropertiesStep.java
new file mode 100644
index 0000000..b02b7af
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetBuildOrPropertiesStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetBuildOrPropertiesStep {
+
+ public TargetBuildOrPropertiesStep withProperty(String key, Object value);
+ public RelationshipBuildOrPropertiesStep build();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetFromStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetFromStep.java
new file mode 100644
index 0000000..afcdf4a
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetFromStep.java
@@ -0,0 +1,29 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+import org.openecomp.aai.champ.model.ChampObject;
+
+public interface TargetFromStep {
+
+ public TargetBuildOrPropertiesStep from(ChampObject object);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetKeyStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetKeyStep.java
new file mode 100644
index 0000000..e236be0
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetKeyStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetKeyStep {
+
+ public TargetBuildOrPropertiesStep withKey(Object key);
+ public TargetBuildOrPropertiesStep withoutKey();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetStep.java
new file mode 100644
index 0000000..4a9f047
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetStep.java
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetStep {
+ public TargetTypeOrFromStep withTarget();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeOrFromStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeOrFromStep.java
new file mode 100644
index 0000000..31a5a63
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeOrFromStep.java
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetTypeOrFromStep extends TargetTypeStep, TargetFromStep {
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeStep.java
new file mode 100644
index 0000000..308c06f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/TargetTypeStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship;
+
+public interface TargetTypeStep {
+
+ public TargetKeyStep ofType(String type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/ChampRelationshipKeyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/ChampRelationshipKeyStepImpl.java
new file mode 100644
index 0000000..2875d8a
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/ChampRelationshipKeyStepImpl.java
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceStep;
+
+public final class ChampRelationshipKeyStepImpl implements KeyStep<SourceStep> {
+
+ private final String type;
+ private final ChampRelationship relationship;
+
+ public ChampRelationshipKeyStepImpl(String type) {
+ this.type = type;
+ this.relationship = null;
+ }
+
+ public ChampRelationshipKeyStepImpl(ChampRelationship relationship) {
+ this.type = null;
+ this.relationship = relationship;
+ }
+
+ @Override
+ public SourceStep withKey(Object key) {
+ return new SourceStepImpl(type, relationship, key);
+ }
+
+ @Override
+ public SourceStep withoutKey() {
+ return new SourceStepImpl(type, relationship, null);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/CreateChampRelationshipableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/CreateChampRelationshipableImpl.java
new file mode 100644
index 0000000..3182416
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/CreateChampRelationshipableImpl.java
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.KeyStep;
+import org.openecomp.aai.champ.model.fluent.relationship.CreateChampRelationshipable;
+import org.openecomp.aai.champ.model.fluent.relationship.RelationshipBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceStep;
+
+public final class CreateChampRelationshipableImpl implements CreateChampRelationshipable {
+
+ @Override
+ public KeyStep<SourceStep> ofType(String type) {
+ return new ChampRelationshipKeyStepImpl(type);
+ }
+
+ @Override
+ public KeyStep<RelationshipBuildOrPropertiesStep> from(ChampRelationship relationship) {
+ return new KeyStep<RelationshipBuildOrPropertiesStep> () {
+
+ @Override
+ public RelationshipBuildOrPropertiesStep withKey(Object key) {
+ return new RelationshipBuildOrPropertiesStepImpl(new ChampRelationship.Builder(relationship).key(key));
+ }
+
+ @Override
+ public RelationshipBuildOrPropertiesStep withoutKey() {
+ return new RelationshipBuildOrPropertiesStepImpl(new ChampRelationship.Builder(relationship));
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/RelationshipBuildOrPropertiesStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/RelationshipBuildOrPropertiesStepImpl.java
new file mode 100644
index 0000000..6233b8f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/RelationshipBuildOrPropertiesStepImpl.java
@@ -0,0 +1,54 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import java.util.Map;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.RelationshipBuildOrPropertiesStep;
+
+public final class RelationshipBuildOrPropertiesStepImpl implements RelationshipBuildOrPropertiesStep {
+
+ private final ChampRelationship.Builder relationshipBuilder;
+
+ public RelationshipBuildOrPropertiesStepImpl(ChampRelationship.Builder relationshipBuilder) {
+ this.relationshipBuilder = relationshipBuilder;
+ }
+
+ @Override
+ public ChampRelationship build() {
+ return relationshipBuilder.build();
+ }
+
+ @Override
+ public RelationshipBuildOrPropertiesStep withProperty(String key, Object value) {
+ relationshipBuilder.property(key, value);
+ return this;
+ }
+
+ @Override
+ public RelationshipBuildOrPropertiesStep withProperties(Map<String, Object> properties) {
+ relationshipBuilder.properties(properties);
+ return this;
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceBuildOrPropertiesStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceBuildOrPropertiesStepImpl.java
new file mode 100644
index 0000000..e7324a3
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceBuildOrPropertiesStepImpl.java
@@ -0,0 +1,55 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetStep;
+
+public final class SourceBuildOrPropertiesStepImpl implements SourceBuildOrPropertiesStep {
+
+ private ChampObject.Builder sourceBuilder;
+ private Object relationshipKey;
+ private ChampRelationship relationship;
+ private String relationshipType;
+
+ public SourceBuildOrPropertiesStepImpl(String relationshipType, ChampRelationship relationship,
+ Object relationshipKey, ChampObject.Builder sourceBuilder) {
+ this.relationshipType = relationshipType;
+ this.relationship = relationship;
+ this.relationshipKey = relationshipKey;
+ this.sourceBuilder = sourceBuilder;
+ }
+
+ @Override
+ public SourceBuildOrPropertiesStep withProperty(String key, Object value) {
+ sourceBuilder.property(key, value);
+ return this;
+ }
+
+ @Override
+ public TargetStep build() {
+ return new TargetStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder.build());
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceKeyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceKeyStepImpl.java
new file mode 100644
index 0000000..5b99d90
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceKeyStepImpl.java
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObject.Builder;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceKeyStep;
+
+public final class SourceKeyStepImpl implements SourceKeyStep {
+
+ private final String relationshipType;
+ private final ChampRelationship relationship;
+ private final Object relationshipKey;
+ private final ChampObject.Builder sourceBuilder;
+
+ public SourceKeyStepImpl(String relationshipType, ChampRelationship relationship, Object relationshipKey,
+ Builder sourceBuilder) {
+ this.relationshipType = relationshipType;
+ this.relationship = relationship;
+ this.relationshipKey = relationshipKey;
+ this.sourceBuilder = sourceBuilder;
+ }
+
+ @Override
+ public SourceBuildOrPropertiesStep withKey(Object key) {
+ sourceBuilder.key(key);
+ return new SourceBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder);
+ }
+
+ @Override
+ public SourceBuildOrPropertiesStep withoutKey() {
+ return new SourceBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceStepImpl.java
new file mode 100644
index 0000000..9203072
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceStepImpl.java
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceTypeOrFromStep;
+
+public final class SourceStepImpl implements SourceStep {
+
+ private final String relationshipType;
+ private final Object key;
+ private final ChampRelationship relationship;
+
+ public SourceStepImpl(String relationshipType, ChampRelationship relationship, Object key) {
+ this.relationshipType = relationshipType;
+ this.key = key;
+ this.relationship = relationship;
+ }
+
+ @Override
+ public SourceTypeOrFromStep withSource() {
+ return new SourceTypeOrFromStepImpl(relationshipType, relationship, key);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceTypeOrFromStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceTypeOrFromStepImpl.java
new file mode 100644
index 0000000..be7fa3d
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/SourceTypeOrFromStepImpl.java
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceKeyStep;
+import org.openecomp.aai.champ.model.fluent.relationship.SourceTypeOrFromStep;
+
+public final class SourceTypeOrFromStepImpl implements SourceTypeOrFromStep {
+
+ private final String relationshipType;
+ private final Object relationshipKey;
+ private final ChampRelationship relationship;
+
+ public SourceTypeOrFromStepImpl(String relationshipType, ChampRelationship relationship, Object key) {
+ this.relationshipType = relationshipType;
+ this.relationship = relationship;
+ this.relationshipKey = key;
+ }
+
+ @Override
+ public SourceKeyStep ofType(String type) {
+ final ChampObject.Builder sourceBuilder = new ChampObject.Builder(type);
+ return new SourceKeyStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder);
+ }
+
+ @Override
+ public SourceBuildOrPropertiesStep from(ChampObject object) {
+ final ChampObject.Builder sourceBuilder = new ChampObject.Builder(object);
+ return new SourceBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, sourceBuilder);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetBuildOrPropertiesStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetBuildOrPropertiesStepImpl.java
new file mode 100644
index 0000000..50ad6ab
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetBuildOrPropertiesStepImpl.java
@@ -0,0 +1,65 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.RelationshipBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetBuildOrPropertiesStep;
+
+public final class TargetBuildOrPropertiesStepImpl implements TargetBuildOrPropertiesStep {
+
+ private final String relationshipType;
+ private final ChampRelationship relationship;
+ private final Object relationshipKey;
+ private final ChampObject source;
+ private final ChampObject.Builder targetBuilder;
+
+ public TargetBuildOrPropertiesStepImpl(String relationshipType, ChampRelationship relationship, Object relationshipKey,
+ ChampObject source, ChampObject.Builder targetBuilder) {
+ this.relationshipType = relationshipType;
+ this.relationship = relationship;
+ this.relationshipKey = relationshipKey;
+ this.source = source;
+ this.targetBuilder = targetBuilder;
+ }
+
+ @Override
+ public TargetBuildOrPropertiesStep withProperty(String key, Object value) {
+ targetBuilder.property(key, value);
+ return this;
+ }
+
+ @Override
+ public RelationshipBuildOrPropertiesStep build() {
+
+ final ChampRelationship.Builder relationshipBuilder;
+
+ if (relationship != null) relationshipBuilder = new ChampRelationship.Builder(relationship);
+ else relationshipBuilder = new ChampRelationship.Builder(source, targetBuilder.build(), relationshipType);
+
+ if (relationshipKey != null) relationshipBuilder.key(relationshipKey);
+
+ return new RelationshipBuildOrPropertiesStepImpl(relationshipBuilder);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetKeyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetKeyStepImpl.java
new file mode 100644
index 0000000..0d4e45c
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetKeyStepImpl.java
@@ -0,0 +1,56 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetKeyStep;
+
+public final class TargetKeyStepImpl implements TargetKeyStep {
+
+ private final String relationshipType;
+ private final Object relationshipKey;
+ private final ChampRelationship relationship;
+ private final ChampObject source;
+ private final ChampObject.Builder targetBuilder;
+
+ public TargetKeyStepImpl(String relationshipType, ChampRelationship relationship, Object key, ChampObject source, ChampObject.Builder targetBuilder) {
+ this.relationshipType = relationshipType;
+ this.relationship = relationship;
+ this.relationshipKey = key;
+ this.source = source;
+ this.targetBuilder = targetBuilder;
+ }
+
+ @Override
+ public TargetBuildOrPropertiesStep withKey(Object key) {
+ targetBuilder.key(key);
+ return new TargetBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, source, targetBuilder);
+ }
+
+ @Override
+ public TargetBuildOrPropertiesStep withoutKey() {
+ return new TargetBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, source, targetBuilder);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetStepImpl.java
new file mode 100644
index 0000000..a0df564
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetStepImpl.java
@@ -0,0 +1,49 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetTypeOrFromStep;
+
+public final class TargetStepImpl implements TargetStep {
+
+ private String relationshipType;
+ private ChampRelationship relationship;
+ private Object relationshipKey;
+ private ChampObject source;
+
+ public TargetStepImpl(String relationshipType, ChampRelationship relationship, Object relationshipKey,
+ ChampObject source) {
+ this.relationshipType = relationshipType;
+ this.relationship = relationship;
+ this.relationshipKey = relationshipKey;
+ this.source = source;
+ }
+
+ @Override
+ public TargetTypeOrFromStep withTarget() {
+ return new TargetTypeOrFromStepImpl(relationshipType, relationship, relationshipKey, source);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetTypeOrFromStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetTypeOrFromStepImpl.java
new file mode 100644
index 0000000..e2f4153
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/relationship/impl/TargetTypeOrFromStepImpl.java
@@ -0,0 +1,55 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.relationship.impl;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetKeyStep;
+import org.openecomp.aai.champ.model.fluent.relationship.TargetTypeOrFromStep;
+
+public final class TargetTypeOrFromStepImpl implements TargetTypeOrFromStep {
+
+ private final String relationshipType;
+ private final Object relationshipKey;
+ private final ChampRelationship relationship;
+ private final ChampObject source;
+
+ public TargetTypeOrFromStepImpl(String relationshipType, ChampRelationship relationship, Object key, ChampObject source) {
+ this.relationshipType = relationshipType;
+ this.relationship = relationship;
+ this.relationshipKey = key;
+ this.source = source;
+ }
+
+ @Override
+ public TargetKeyStep ofType(String type) {
+ final ChampObject.Builder targetBuilder = new ChampObject.Builder(type);
+ return new TargetKeyStepImpl(relationshipType, relationship, relationshipKey, source, targetBuilder);
+ }
+
+ @Override
+ public TargetBuildOrPropertiesStep from(ChampObject object) {
+ final ChampObject.Builder targetBuilder = new ChampObject.Builder(object);
+ return new TargetBuildOrPropertiesStepImpl(relationshipType, relationship, relationshipKey, source, targetBuilder);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/CreateChampSchemable.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/CreateChampSchemable.java
new file mode 100644
index 0000000..804f8c0
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/CreateChampSchemable.java
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface CreateChampSchemable extends BuildStep<ChampSchema> {
+
+ public ObjectConstraintTypeStep withObjectConstraint();
+ public RelationshipConstraintTypeStep withRelationshipConstraint();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintBuildStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintBuildStep.java
new file mode 100644
index 0000000..00ea332
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintBuildStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface ObjectConstraintBuildStep extends BuildStep<ObjectConstraintPropertyStep> {
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldStep.java
new file mode 100644
index 0000000..ea8d00c
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface ObjectConstraintFieldStep {
+
+ public ObjectConstraintSubStep onField(String name);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldTypeStep.java
new file mode 100644
index 0000000..fe88b34
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintFieldTypeStep.java
@@ -0,0 +1,29 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampField;
+
+public interface ObjectConstraintFieldTypeStep {
+
+ public ObjectConstraintRequiredOptionalStep ofType(ChampField.Type type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintPropertyStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintPropertyStep.java
new file mode 100644
index 0000000..921af34
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintPropertyStep.java
@@ -0,0 +1,29 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface ObjectConstraintPropertyStep extends BuildStep<CreateChampSchemable> {
+
+ public ObjectConstraintFieldStep withPropertyConstraint();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintRequiredOptionalStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintRequiredOptionalStep.java
new file mode 100644
index 0000000..5d40c24
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintRequiredOptionalStep.java
@@ -0,0 +1,31 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampCardinality;
+
+public interface ObjectConstraintRequiredOptionalStep {
+
+ public ObjectConstraintBuildStep required();
+ public ObjectConstraintBuildStep optional();
+ public ObjectConstraintRequiredOptionalStep cardinality(ChampCardinality cardinality);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintSubStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintSubStep.java
new file mode 100644
index 0000000..709cea6
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintSubStep.java
@@ -0,0 +1,26 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface ObjectConstraintSubStep extends ObjectConstraintFieldTypeStep, ObjectConstraintRequiredOptionalStep{
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintTypeStep.java
new file mode 100644
index 0000000..a81c30f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/ObjectConstraintTypeStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface ObjectConstraintTypeStep {
+
+ public ObjectConstraintPropertyStep onType(String type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintBuildStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintBuildStep.java
new file mode 100644
index 0000000..9fb3e30
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintBuildStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface RelationshipConstraintBuildStep extends BuildStep<RelationshipConstraintSubStep> {
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldStep.java
new file mode 100644
index 0000000..2ef66b4
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintFieldStep {
+
+ public RelationshipConstraintPropertyOptionalsStep onField(String name);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldTypeStep.java
new file mode 100644
index 0000000..67d612c
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintFieldTypeStep.java
@@ -0,0 +1,29 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampField;
+
+public interface RelationshipConstraintFieldTypeStep {
+
+ public RelationshipConstraintRequiredOptionalStep ofType(ChampField.Type type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintMultiplicityStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintMultiplicityStep.java
new file mode 100644
index 0000000..2c712de
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintMultiplicityStep.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.ChampConnectionMultiplicity;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface RelationshipConstraintMultiplicityStep extends BuildStep<RelationshipConstraintSubStep> {
+
+ public BuildStep<RelationshipConstraintSubStep> withMultiplicity(ChampConnectionMultiplicity multiplicity);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintPropertyOptionalsStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintPropertyOptionalsStep.java
new file mode 100644
index 0000000..f8ea388
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintPropertyOptionalsStep.java
@@ -0,0 +1,25 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintPropertyOptionalsStep extends RelationshipConstraintFieldTypeStep, RelationshipConstraintRequiredOptionalStep {
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintRequiredOptionalStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintRequiredOptionalStep.java
new file mode 100644
index 0000000..b318d73
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintRequiredOptionalStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintRequiredOptionalStep {
+
+ public RelationshipConstraintBuildStep required();
+ public RelationshipConstraintBuildStep optional();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSourceStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSourceStep.java
new file mode 100644
index 0000000..a497f56
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSourceStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintSourceStep {
+
+ public RelationshipConstraintTargetStep sourcedFrom(String sourceType);
+ public RelationshipConstraintTargetStep sourcedFromAny();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSubStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSubStep.java
new file mode 100644
index 0000000..67fda0e
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintSubStep.java
@@ -0,0 +1,30 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+
+public interface RelationshipConstraintSubStep extends BuildStep<CreateChampSchemable> {
+
+ public RelationshipConstraintFieldStep withPropertyConstraint();
+ public RelationshipConstraintSourceStep withConnectionConstraint();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTargetStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTargetStep.java
new file mode 100644
index 0000000..0b31918
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTargetStep.java
@@ -0,0 +1,28 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintTargetStep {
+
+ public RelationshipConstraintMultiplicityStep targetedTo(String targetType);
+ public RelationshipConstraintMultiplicityStep targetedToAny();
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTypeStep.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTypeStep.java
new file mode 100644
index 0000000..e0f7d7b
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/RelationshipConstraintTypeStep.java
@@ -0,0 +1,27 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema;
+
+public interface RelationshipConstraintTypeStep {
+
+ public RelationshipConstraintSubStep onType(String type);
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/CreateChampSchemableImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/CreateChampSchemableImpl.java
new file mode 100644
index 0000000..2d1c2ed
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/CreateChampSchemableImpl.java
@@ -0,0 +1,74 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.CreateChampSchemable;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintPropertyStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintTypeStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintSubStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintTypeStep;
+
+public class CreateChampSchemableImpl implements CreateChampSchemable {
+
+ private final ChampSchema.Builder schemaBuilder;
+
+ public CreateChampSchemableImpl() {
+ this.schemaBuilder = new ChampSchema.Builder();
+ }
+
+ public CreateChampSchemableImpl(ChampSchema.Builder schemaBuilder) {
+ this.schemaBuilder = schemaBuilder;
+ }
+
+ @Override
+ public ChampSchema build() {
+ return schemaBuilder.build();
+ }
+
+ @Override
+ public ObjectConstraintTypeStep withObjectConstraint() {
+ return new ObjectConstraintTypeStep() {
+
+ @Override
+ public ObjectConstraintPropertyStep onType(String type) {
+ return new ObjectConstraintPropertyStepImpl(schemaBuilder,
+ new ChampObjectConstraint.Builder(type));
+ }
+ };
+ }
+
+ @Override
+ public RelationshipConstraintTypeStep withRelationshipConstraint() {
+ return new RelationshipConstraintTypeStep() {
+
+ @Override
+ public RelationshipConstraintSubStep onType(String type) {
+ return new RelationshipConstraintSubStepImpl(schemaBuilder,
+ new ChampRelationshipConstraint.Builder(type));
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintBuildStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintBuildStepImpl.java
new file mode 100644
index 0000000..d344350
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintBuildStepImpl.java
@@ -0,0 +1,43 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintPropertyStep;
+
+public class ObjectConstraintBuildStepImpl implements ObjectConstraintBuildStep {
+
+ private final ChampSchema.Builder schemaBuilder;
+ private final ChampObjectConstraint.Builder constraintBuilder;
+
+ public ObjectConstraintBuildStepImpl(ChampSchema.Builder schemaBuilder, ChampObjectConstraint.Builder constraintBuilder) {
+ this.schemaBuilder = schemaBuilder;
+ this.constraintBuilder = constraintBuilder;
+ }
+
+ @Override
+ public ObjectConstraintPropertyStep build() {
+ return new ObjectConstraintPropertyStepImpl(schemaBuilder, constraintBuilder);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintFieldStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintFieldStepImpl.java
new file mode 100644
index 0000000..9ce1d03
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintFieldStepImpl.java
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintFieldStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintSubStep;
+
+public class ObjectConstraintFieldStepImpl implements ObjectConstraintFieldStep {
+
+ private final ChampSchema.Builder schemaBuilder;
+ private final ChampObjectConstraint.Builder constraintBuilder;
+
+ public ObjectConstraintFieldStepImpl(ChampSchema.Builder schemaBuilder, ChampObjectConstraint.Builder constraintBuilder) {
+ this.schemaBuilder = schemaBuilder;
+ this.constraintBuilder = constraintBuilder;
+ }
+
+ @Override
+ public ObjectConstraintSubStep onField(String name) {
+ return new ObjectConstraintSubStepImpl(schemaBuilder, constraintBuilder, new ChampField.Builder(name));
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintPropertyStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintPropertyStepImpl.java
new file mode 100644
index 0000000..de2406e
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintPropertyStepImpl.java
@@ -0,0 +1,50 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.CreateChampSchemable;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintFieldStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintPropertyStep;
+
+public class ObjectConstraintPropertyStepImpl implements ObjectConstraintPropertyStep {
+
+ private final ChampObjectConstraint.Builder constraintBuilder;
+ private final ChampSchema.Builder schemaBuilder;
+
+ public ObjectConstraintPropertyStepImpl(ChampSchema.Builder schemaBuilder, ChampObjectConstraint.Builder constraintBuilder) {
+ this.constraintBuilder = constraintBuilder;
+ this.schemaBuilder = schemaBuilder;
+ }
+
+ @Override
+ public CreateChampSchemable build() {
+ return new CreateChampSchemableImpl(schemaBuilder.constraint(constraintBuilder.build()));
+ }
+
+ @Override
+ public ObjectConstraintFieldStep withPropertyConstraint() {
+ return new ObjectConstraintFieldStepImpl(schemaBuilder, constraintBuilder);
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintSubStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintSubStepImpl.java
new file mode 100644
index 0000000..01eba74
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/ObjectConstraintSubStepImpl.java
@@ -0,0 +1,111 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampCardinality;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintPropertyStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintRequiredOptionalStep;
+import org.openecomp.aai.champ.model.fluent.schema.ObjectConstraintSubStep;
+
+public class ObjectConstraintSubStepImpl implements ObjectConstraintSubStep {
+
+ private final ChampSchema.Builder schemaBuilder;
+ private final ChampObjectConstraint.Builder constraintBuilder;
+ private final ChampField.Builder fieldBuilder;
+ private final ChampPropertyConstraint.Builder propConstBuilder;
+
+ public ObjectConstraintSubStepImpl(ChampSchema.Builder schemaBuilder,
+ ChampObjectConstraint.Builder constraintBuilder,
+ ChampField.Builder fieldBuilder) {
+ this.schemaBuilder = schemaBuilder;
+ this.constraintBuilder = constraintBuilder;
+ this.fieldBuilder = fieldBuilder;
+ this.propConstBuilder = new ChampPropertyConstraint.Builder(fieldBuilder.build());
+
+ }
+
+ @Override
+ public ObjectConstraintRequiredOptionalStep ofType(ChampField.Type type) {
+ return new ObjectConstraintRequiredOptionalStep() {
+ final ChampPropertyConstraint.Builder propConstBuilder = new ChampPropertyConstraint.Builder(fieldBuilder.type(type).build());
+
+ @Override
+ public ObjectConstraintBuildStep required() {
+ constraintBuilder.constraint(propConstBuilder.required(true).build());
+
+ return new ObjectConstraintBuildStep() {
+
+ @Override
+ public ObjectConstraintPropertyStep build() {
+ return new ObjectConstraintPropertyStepImpl(schemaBuilder, constraintBuilder);
+ }
+ };
+ }
+
+ @Override
+ public ObjectConstraintBuildStep optional() {
+ constraintBuilder.constraint(propConstBuilder.required(false).build());
+
+ return new ObjectConstraintBuildStep() {
+
+ @Override
+ public ObjectConstraintPropertyStep build() {
+ return new ObjectConstraintPropertyStepImpl(schemaBuilder, constraintBuilder);
+ }
+ };
+ }
+
+ @Override
+ public ObjectConstraintRequiredOptionalStep cardinality(ChampCardinality cardinality) {
+ propConstBuilder.cardinality(cardinality);
+ return this;
+ }
+ };
+ }
+
+ @Override
+ public ObjectConstraintBuildStep required() {
+ constraintBuilder.constraint(propConstBuilder.required(true).build());
+
+ return new ObjectConstraintBuildStepImpl(schemaBuilder, constraintBuilder);
+ }
+
+ @Override
+ public ObjectConstraintBuildStep optional() {
+ final ChampPropertyConstraint.Builder propConstBuilder = new ChampPropertyConstraint.Builder(fieldBuilder.build());
+
+ constraintBuilder.constraint(propConstBuilder.required(false).build());
+
+ return new ObjectConstraintBuildStepImpl(schemaBuilder, constraintBuilder);
+ }
+
+ @Override
+ public ObjectConstraintRequiredOptionalStep cardinality(ChampCardinality cardinality) {
+ propConstBuilder.cardinality(cardinality);
+ return this;
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintBuildStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintBuildStepImpl.java
new file mode 100644
index 0000000..018c73f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintBuildStepImpl.java
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.ChampSchema.Builder;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintSubStep;
+
+public class RelationshipConstraintBuildStepImpl implements RelationshipConstraintBuildStep {
+
+ private final ChampSchema.Builder schemaBuilder;
+ private final ChampRelationshipConstraint.Builder relConstraintBuilder;
+
+ public RelationshipConstraintBuildStepImpl(Builder schemaBuilder,
+ ChampRelationshipConstraint.Builder relConstraintBuilder) {
+ this.schemaBuilder = schemaBuilder;
+ this.relConstraintBuilder = relConstraintBuilder;
+ }
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ return new RelationshipConstraintSubStepImpl(schemaBuilder, relConstraintBuilder);
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintPropertyOptionalsStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintPropertyOptionalsStepImpl.java
new file mode 100644
index 0000000..62176b7
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintPropertyOptionalsStepImpl.java
@@ -0,0 +1,68 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintPropertyOptionalsStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintRequiredOptionalStep;
+
+public class RelationshipConstraintPropertyOptionalsStepImpl implements RelationshipConstraintPropertyOptionalsStep {
+
+ private final ChampSchema.Builder schemaBuilder;
+ private final ChampRelationshipConstraint.Builder relConstraintBuilder;
+ private final ChampField.Builder fieldBuilder;
+
+ public RelationshipConstraintPropertyOptionalsStepImpl(ChampSchema.Builder schemaBuilder,
+ ChampRelationshipConstraint.Builder relConstraintBuilder,
+ ChampField.Builder fieldBuilder) {
+ this.schemaBuilder = schemaBuilder;
+ this.relConstraintBuilder = relConstraintBuilder;
+ this.fieldBuilder = fieldBuilder;
+ }
+
+ @Override
+ public RelationshipConstraintRequiredOptionalStep ofType(ChampField.Type type) {
+ return new RelationshipConstraintRequiredOptionalStepImpl(schemaBuilder, relConstraintBuilder, fieldBuilder.type(type));
+ }
+
+ @Override
+ public RelationshipConstraintBuildStep required() {
+ final ChampPropertyConstraint propConstraint = new ChampPropertyConstraint.Builder(fieldBuilder.build())
+ .required(true)
+ .build();
+
+ return new RelationshipConstraintBuildStepImpl(schemaBuilder, relConstraintBuilder.constraint(propConstraint));
+ }
+
+ @Override
+ public RelationshipConstraintBuildStep optional() {
+ final ChampPropertyConstraint propConstraint = new ChampPropertyConstraint.Builder(fieldBuilder.build())
+ .required(false)
+ .build();
+
+ return new RelationshipConstraintBuildStepImpl(schemaBuilder, relConstraintBuilder.constraint(propConstraint));
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintRequiredOptionalStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintRequiredOptionalStepImpl.java
new file mode 100644
index 0000000..63720a4
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintRequiredOptionalStepImpl.java
@@ -0,0 +1,64 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.ChampSchema.Builder;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintBuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintRequiredOptionalStep;
+
+public class RelationshipConstraintRequiredOptionalStepImpl implements RelationshipConstraintRequiredOptionalStep {
+
+ private final ChampSchema.Builder schemaBuilder;
+ private final ChampRelationshipConstraint.Builder relConstraintBuilder;
+ private final ChampField.Builder fieldBuilder;
+
+ public RelationshipConstraintRequiredOptionalStepImpl(Builder schemaBuilder,
+ ChampRelationshipConstraint.Builder relConstraintBuilder,
+ ChampField.Builder fieldBuilder) {
+ this.schemaBuilder = schemaBuilder;
+ this.relConstraintBuilder = relConstraintBuilder;
+ this.fieldBuilder = fieldBuilder;
+ }
+
+ @Override
+ public RelationshipConstraintBuildStep required() {
+ final ChampPropertyConstraint propConstraint = new ChampPropertyConstraint.Builder(fieldBuilder.build())
+ .required(true)
+ .build();
+
+ return new RelationshipConstraintBuildStepImpl(schemaBuilder, relConstraintBuilder.constraint(propConstraint));
+ }
+
+ @Override
+ public RelationshipConstraintBuildStep optional() {
+ final ChampPropertyConstraint propConstraint = new ChampPropertyConstraint.Builder(fieldBuilder.build())
+ .required(false)
+ .build();
+
+ return new RelationshipConstraintBuildStepImpl(schemaBuilder, relConstraintBuilder.constraint(propConstraint));
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintSubStepImpl.java b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintSubStepImpl.java
new file mode 100644
index 0000000..4d88690
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/model/fluent/schema/impl/RelationshipConstraintSubStepImpl.java
@@ -0,0 +1,205 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.model.fluent.schema.impl;
+
+import org.openecomp.aai.champ.model.ChampConnectionConstraint;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampConnectionMultiplicity;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.model.fluent.BuildStep;
+import org.openecomp.aai.champ.model.fluent.schema.CreateChampSchemable;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintFieldStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintMultiplicityStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintPropertyOptionalsStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintSourceStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintSubStep;
+import org.openecomp.aai.champ.model.fluent.schema.RelationshipConstraintTargetStep;
+
+public class RelationshipConstraintSubStepImpl implements RelationshipConstraintSubStep {
+
+ private final ChampSchema.Builder schemaBuilder;
+ private final ChampRelationshipConstraint.Builder relConstraintBuilder;
+
+ public RelationshipConstraintSubStepImpl(ChampSchema.Builder schemaBuilder,
+ ChampRelationshipConstraint.Builder relConstraintBuilder) {
+ this.schemaBuilder = schemaBuilder;
+ this.relConstraintBuilder = relConstraintBuilder;
+ }
+
+ @Override
+ public CreateChampSchemable build() {
+ return new CreateChampSchemableImpl(schemaBuilder.constraint(relConstraintBuilder.build()));
+ }
+
+ @Override
+ public RelationshipConstraintFieldStep withPropertyConstraint() {
+ return new RelationshipConstraintFieldStep() {
+
+ @Override
+ public RelationshipConstraintPropertyOptionalsStep onField(String name) {
+ return new RelationshipConstraintPropertyOptionalsStepImpl(schemaBuilder, relConstraintBuilder, new ChampField.Builder(name));
+ }
+ };
+ }
+
+ @Override
+ public RelationshipConstraintSourceStep withConnectionConstraint() {
+ return new RelationshipConstraintSourceStep() {
+
+ @Override
+ public RelationshipConstraintTargetStep sourcedFrom(String sourceType) {
+
+ return new RelationshipConstraintTargetStep() {
+
+ @Override
+ public RelationshipConstraintMultiplicityStep targetedTo(String targetType) {
+ final ChampConnectionConstraint.Builder connectionConstraint = new ChampConnectionConstraint.Builder(sourceType, targetType);
+
+ return new RelationshipConstraintMultiplicityStep() {
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ relConstraintBuilder.constraint(connectionConstraint.build());
+
+ return RelationshipConstraintSubStepImpl.this;
+ }
+
+ @Override
+ public BuildStep<RelationshipConstraintSubStep> withMultiplicity(
+ ChampConnectionMultiplicity multiplicity) {
+ connectionConstraint.multiplicity(multiplicity);
+ return new BuildStep<RelationshipConstraintSubStep> () {
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ relConstraintBuilder.constraint(connectionConstraint.build());
+
+ return RelationshipConstraintSubStepImpl.this;
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public RelationshipConstraintMultiplicityStep targetedToAny() {
+ final ChampConnectionConstraint.Builder connectionConstraint = new ChampConnectionConstraint.Builder(sourceType, ChampObject.ReservedTypes.ANY.toString());
+
+ return new RelationshipConstraintMultiplicityStep() {
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ relConstraintBuilder.constraint(connectionConstraint.build());
+
+ return RelationshipConstraintSubStepImpl.this;
+ }
+
+ @Override
+ public BuildStep<RelationshipConstraintSubStep> withMultiplicity(
+ ChampConnectionMultiplicity multiplicity) {
+ connectionConstraint.multiplicity(multiplicity);
+ return new BuildStep<RelationshipConstraintSubStep> () {
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ relConstraintBuilder.constraint(connectionConstraint.build());
+
+ return RelationshipConstraintSubStepImpl.this;
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public RelationshipConstraintTargetStep sourcedFromAny() {
+ return new RelationshipConstraintTargetStep() {
+
+ @Override
+ public RelationshipConstraintMultiplicityStep targetedTo(String targetType) {
+ final ChampConnectionConstraint.Builder connectionConstraint = new ChampConnectionConstraint.Builder(ChampObject.ReservedTypes.ANY.toString(), targetType);
+
+ return new RelationshipConstraintMultiplicityStep() {
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ relConstraintBuilder.constraint(connectionConstraint.build());
+
+ return RelationshipConstraintSubStepImpl.this;
+ }
+
+ @Override
+ public BuildStep<RelationshipConstraintSubStep> withMultiplicity(
+ ChampConnectionMultiplicity multiplicity) {
+ connectionConstraint.multiplicity(multiplicity);
+ return new BuildStep<RelationshipConstraintSubStep> () {
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ relConstraintBuilder.constraint(connectionConstraint.build());
+
+ return RelationshipConstraintSubStepImpl.this;
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public RelationshipConstraintMultiplicityStep targetedToAny() {
+ final ChampConnectionConstraint.Builder connectionConstraint = new ChampConnectionConstraint.Builder(ChampObject.ReservedTypes.ANY.toString(), ChampObject.ReservedTypes.ANY.toString());
+
+ return new RelationshipConstraintMultiplicityStep() {
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ relConstraintBuilder.constraint(connectionConstraint.build());
+
+ return RelationshipConstraintSubStepImpl.this;
+ }
+
+ @Override
+ public BuildStep<RelationshipConstraintSubStep> withMultiplicity(
+ ChampConnectionMultiplicity multiplicity) {
+ connectionConstraint.multiplicity(multiplicity);
+ return new BuildStep<RelationshipConstraintSubStep> () {
+
+ @Override
+ public RelationshipConstraintSubStep build() {
+ relConstraintBuilder.constraint(connectionConstraint.build());
+
+ return RelationshipConstraintSubStepImpl.this;
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+
+}
diff --git a/src/main/java/org/openecomp/aai/champ/perf/ChampAPIPerformanceTest.java b/src/main/java/org/openecomp/aai/champ/perf/ChampAPIPerformanceTest.java
new file mode 100644
index 0000000..563ae67
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/perf/ChampAPIPerformanceTest.java
@@ -0,0 +1,430 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.perf;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.graph.impl.InMemoryChampGraphImpl;
+import org.openecomp.aai.champ.graph.impl.TitanChampGraphImpl;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.thinkaurelius.titan.core.TitanFactory;
+import com.thinkaurelius.titan.core.TitanFactory.Builder;
+import com.thinkaurelius.titan.core.TitanGraph;
+import com.thinkaurelius.titan.core.util.TitanCleanup;
+
+public class ChampAPIPerformanceTest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ChampAPIPerformanceTest.class);
+
+ private static final int NUM_OBJECTS = 1000;
+ private static final int NUM_RELATIONSHIPS = 1000;
+ private static final String GRAPH_NAME = ChampAPIPerformanceTest.class.getSimpleName();
+
+ private static final String getGraphName() {
+ return GRAPH_NAME;
+ }
+
+ private static void cleanUp(String graphName, Map<String, String> settings) {
+ LOGGER.debug("Cleaning up graph {}", graphName);
+
+ try {
+ final Builder graphBuilder = TitanFactory.build();
+
+ for (Entry<String, String> setting : settings.entrySet()) {
+ graphBuilder.set(setting.getKey(), setting.getValue());
+ }
+
+ final String storageBackend = settings.getOrDefault("storage.backend", "inmemory");
+
+ if (storageBackend.equals("cassandra") ||
+ storageBackend.equals("cassandrathrift") ||
+ storageBackend.equals("astyanax") ||
+ storageBackend.equals("embeddedcassandra")) {
+ graphBuilder.set("storage.cassandra.keyspace", graphName);
+ } else if (storageBackend.equals("hbase")) {
+ graphBuilder.set("storage.hbase.table", graphName);
+ }
+
+ final TitanGraph graph = graphBuilder.open();
+
+ graph.close();
+ TitanCleanup.clear(graph);
+ } catch (IllegalArgumentException e) {
+ LOGGER.warn("Could not clean up graph - unable to instantiate");
+ }
+ }
+
+ public static void main(String[] args) {
+
+ if (args.length < 1 || !args[0].startsWith("--champ.graph.type=")) {
+ throw new RuntimeException("Must provide --champ.graph.type=" + ChampGraph.Type.values() + " as first parameter");
+ }
+
+ final ChampGraph.Type graphType = ChampGraph.Type.valueOf(args[0].split("=")[1]);
+
+ final Map<String, String> settings = new HashMap<String, String> ();
+
+ for (int i = 1; i < args.length; i++) {
+ if (!args[i].startsWith("--")) throw new RuntimeException("Bad command line argument: " + args[i]);
+
+ final String[] keyValue = args[i].replaceFirst("--", "").split("=");
+
+ if (keyValue.length != 2) throw new RuntimeException("Bad command line argument: " + args[i]);
+
+ settings.put(keyValue[0], keyValue[1]);
+ }
+
+ LOGGER.info("Provided graph settings: " + settings);
+
+ if (graphType == ChampGraph.Type.TITAN) cleanUp(getGraphName(), settings);
+
+ LOGGER.info("Graph cleaned, instantiating ChampGraph");
+
+ final ChampGraph graph;
+
+ switch (graphType) {
+ case IN_MEMORY:
+ final InMemoryChampGraphImpl.Builder inMemGraphBuilder = new InMemoryChampGraphImpl.Builder();
+
+ if (settings.containsKey("champ.schema.enforcer")) {
+ final String schemaEnforcerClassStr = settings.get("champ.schema.enforcer");
+
+ try {
+ final Class<?> schemaEnforcer = Class.forName(schemaEnforcerClassStr);
+
+ if (!schemaEnforcer.isAssignableFrom(ChampSchemaEnforcer.class)) throw new RuntimeException("Unknown ChampSchemaEnforcer " + schemaEnforcer);
+
+ inMemGraphBuilder.schemaEnforcer((ChampSchemaEnforcer) schemaEnforcer.newInstance());
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ graph = inMemGraphBuilder.build();
+ break;
+ case TITAN:
+ final TitanChampGraphImpl.Builder graphBuilder = new TitanChampGraphImpl.Builder(getGraphName());
+
+ for (Entry<String, String> setting : settings.entrySet()) {
+ graphBuilder.property(setting.getKey(), setting.getValue());
+ }
+
+ graph = graphBuilder.build();
+ break;
+ default:
+ throw new RuntimeException("Unknown ChampGraph.Type " + graphType);
+ }
+
+ if (graph.queryObjects(Collections.emptyMap()).limit(1).count() > 0) {
+ graph.shutdown();
+ throw new RuntimeException("Expected empty graph");
+ }
+
+ LOGGER.info("Graph instantiated, warming up JVM");
+ warmUp(graph);
+
+ LOGGER.info("Warm up complete, starting to record performance measurements");
+
+ LOGGER.info("Performance without indexing/schema");
+
+ storeObjects(graph, false);
+ storeRelationships(graph, false);
+ retrieveIndividualObjects(graph, false);
+ retrieveBulkRelationships(graph, false);
+ retrieveIndividualRelationships(graph, false);
+
+ LOGGER.info("Storing indices + schema");
+
+ storeIndices(graph, false);
+ storeSchema(graph, false);
+
+ LOGGER.info("Stored indices + schema");
+
+ LOGGER.info("Performance with indexing + schema");
+
+ storeObjects(graph, false);
+ storeRelationships(graph, false);
+ retrieveIndividualObjects(graph, false);
+ retrieveBulkRelationships(graph, false);
+ retrieveIndividualRelationships(graph, false);
+
+ LOGGER.info("Performance test complete, shutting down graph");
+
+ graph.shutdown();
+
+ LOGGER.info("Graph shutdown, JVM exiting");
+ }
+
+ private static void storeSchema(ChampGraph graph, boolean warmUp) {
+ try {
+ graph.storeSchema(
+ ChampSchema.create()
+ .withObjectConstraint()
+ .onType("foo")
+ .withPropertyConstraint()
+ .onField("fooObjectNumber")
+ .optional()
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("bar")
+ .withPropertyConstraint()
+ .onField("barObjectNumber")
+ .ofType(ChampField.Type.INTEGER)
+ .optional()
+ .build()
+ .build()
+ .build()
+ );
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static void storeIndices(ChampGraph graph, boolean warmUp) {
+ graph.storeObjectIndex(
+ ChampObjectIndex.create()
+ .ofName("objectNumberIndex")
+ .onType("foo")
+ .forField("objectNumber")
+ .build()
+ );
+
+ graph.storeRelationshipIndex(ChampRelationshipIndex.create()
+ .ofName("relationshipNumberIndex")
+ .onType("bazz")
+ .forField("relationshipNumber")
+ .build()
+ );
+ }
+
+ private static void warmUp(ChampGraph graph) {
+ storeObjects(graph, false);
+ storeRelationships(graph, false);
+ retrieveIndividualObjects(graph, false);
+ retrieveBulkRelationships(graph, false);
+ retrieveIndividualRelationships(graph, false);
+ }
+
+ private static void retrieveIndividualRelationships(ChampGraph graph, boolean warmUp) {
+ final double[] latencies = new double[NUM_RELATIONSHIPS];
+ final long totalStartTime = System.nanoTime();
+
+ for (int i = 0; i < NUM_RELATIONSHIPS; i++) {
+ final long startTime = System.nanoTime();
+
+ final Stream<ChampRelationship> objects = graph.queryRelationships(Collections.singletonMap("relationshipNumber", i));
+ objects.findFirst().get();
+ final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+ latencies[i] = elapsedMs;
+ }
+
+ final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
+ LOGGER.info("Individually read " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)");
+
+ Arrays.sort(latencies);
+
+ if (!warmUp) {
+ LOGGER.info("Retrieve individual relationship latencies");
+ LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]);
+ LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]);
+ LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]);
+ LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]);
+ }
+ }
+
+ private static void retrieveIndividualObjects(ChampGraph graph, boolean warmUp) {
+
+ final double[] latencies = new double[NUM_OBJECTS];
+ final long totalStartTime = System.nanoTime();
+
+ for (int i = 0; i < NUM_OBJECTS; i++) {
+ final long startTime = System.nanoTime();
+ final Stream<ChampObject> objects = graph.queryObjects(Collections.singletonMap("objectNumber", i));
+
+ objects.findFirst().get();
+
+ final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+
+ latencies[i] = elapsedMs;
+ }
+
+ final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
+
+ LOGGER.info("Individually read " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)");
+ Arrays.sort(latencies);
+
+ if (!warmUp) {
+ LOGGER.info("Retrieve individual object latencies");
+ LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]);
+ LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]);
+ LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]);
+ LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]);
+ }
+ }
+
+ private static List<ChampObject> retrieveBulkObjects(ChampGraph graph, boolean warmUp) {
+
+ final long startTime = System.nanoTime();
+ final Stream<ChampObject> objects = graph.queryObjects(
+ Collections.singletonMap(
+ ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo"
+ )
+ );
+
+ final List<ChampObject> objectsAsList = objects.collect(Collectors.toList());
+ final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0;
+
+ if (!warmUp) LOGGER.info("Bulk read " + objectsAsList.size() + " objects in " + elapsedSecs + "s (" + objectsAsList.size() / elapsedSecs + " objects/s)");
+
+ return objectsAsList;
+ }
+
+ private static List<ChampRelationship> retrieveBulkRelationships(ChampGraph graph, boolean warmUp) {
+ final long startTime = System.nanoTime();
+ final Stream<ChampRelationship> relationships = graph.queryRelationships(
+ Collections.singletonMap(
+ ChampRelationship.ReservedPropertyKeys.CHAMP_RELATIONSHIP_TYPE.toString(), "bazz"
+ )
+ );
+
+ final List<ChampRelationship> relationshipsAsList = relationships.collect(Collectors.toList());
+ final double elapsedSecs = (System.nanoTime() - startTime) / 1000.0 / 1000.0 / 1000.0;
+
+ if (!warmUp) LOGGER.info("Bulk read " + relationshipsAsList.size() + " relationships in " + elapsedSecs + "s (" + relationshipsAsList.size() / elapsedSecs + " relationships/s)");
+
+ return relationshipsAsList;
+ }
+
+ private static void storeObjects(ChampGraph graph, boolean warmUp) {
+ final double[] latencies = new double[NUM_OBJECTS];
+ final long totalStartTime = System.nanoTime();
+
+ for (int i = 0; i < NUM_OBJECTS; i++) {
+ try {
+ final long startTime = System.nanoTime();
+
+ graph.storeObject(
+ ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("objectNumber", i)
+ .build()
+ );
+
+ final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+ latencies[i] = elapsedMs;
+ } catch (ChampMarshallingException e) {
+ throw new RuntimeException(e);
+ } catch (ChampSchemaViolationException e) {
+ //Ignore, no schema set
+ } catch (ChampObjectNotExistsException e) {
+ //Ignore, not an update
+ }
+ }
+
+ final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
+ LOGGER.info("Wrote " + NUM_OBJECTS + " objects in " + totalElapsedTimeSecs + "s (" + NUM_OBJECTS / totalElapsedTimeSecs + " objects/s)");
+
+ Arrays.sort(latencies);
+
+ if (!warmUp) {
+ LOGGER.info("Store object latencies");
+ LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_OBJECTS * 0.50)]);
+ LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_OBJECTS * 0.75)]);
+ LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_OBJECTS * 0.90)]);
+ LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_OBJECTS * 0.99)]);
+ }
+ }
+
+ private static void storeRelationships(ChampGraph graph, boolean warmUp) {
+ final List<ChampObject> objects = retrieveBulkObjects(graph, warmUp);
+ final double[] latencies = new double[NUM_RELATIONSHIPS];
+ final long totalStartTime = System.nanoTime();
+
+ for (int i = 0; i < NUM_RELATIONSHIPS; i++) {
+ try {
+ final long startTime = System.nanoTime();
+
+ graph.storeRelationship(
+ new ChampRelationship.Builder(
+ objects.get(i % objects.size()), objects.get((i + 1) % objects.size()), "bazz"
+ ).property("relationshipNumber", i)
+ .build()
+ );
+
+ final double elapsedMs = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+
+ latencies[i] = elapsedMs;
+ } catch (ChampMarshallingException e) {
+ throw new RuntimeException(e);
+ } catch (ChampObjectNotExistsException e) {
+ throw new RuntimeException(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new RuntimeException(e);
+ } catch (ChampRelationshipNotExistsException e) {
+ throw new RuntimeException(e);
+ } catch (ChampUnmarshallingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ final double totalElapsedTimeSecs = (System.nanoTime() - totalStartTime) / 1000.0 / 1000.0 / 1000.0;
+ LOGGER.info("Wrote " + NUM_RELATIONSHIPS + " relationships in " + totalElapsedTimeSecs + "s (" + NUM_RELATIONSHIPS / totalElapsedTimeSecs + " relationships/s)");
+
+ Arrays.sort(latencies);
+
+ if (!warmUp) {
+ LOGGER.info("Store relationship latencies");
+ LOGGER.info("\t50th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.50)]);
+ LOGGER.info("\t75th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.75)]);
+ LOGGER.info("\t90th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.90)]);
+ LOGGER.info("\t99th percentile: " + latencies[(int) (NUM_RELATIONSHIPS * 0.99)]);
+ }
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/schema/AlwaysValidChampSchemaEnforcer.java b/src/main/java/org/openecomp/aai/champ/schema/AlwaysValidChampSchemaEnforcer.java
new file mode 100644
index 0000000..25083b1
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/schema/AlwaysValidChampSchemaEnforcer.java
@@ -0,0 +1,45 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.schema;
+
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public final class AlwaysValidChampSchemaEnforcer implements ChampSchemaEnforcer {
+
+ @Override
+ public void validate(ChampObject champObject, ChampObjectConstraint champObjectConstraint) {
+ }
+
+ @Override
+ public void validate(ChampRelationship champRelationship,
+ ChampRelationshipConstraint champRelationshipConstraint) {
+ }
+
+ @Override
+ public void validate(ChampPartition champPartition, ChampSchema schema) {
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/schema/ChampSchemaEnforcer.java b/src/main/java/org/openecomp/aai/champ/schema/ChampSchemaEnforcer.java
new file mode 100644
index 0000000..7918e14
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/schema/ChampSchemaEnforcer.java
@@ -0,0 +1,37 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.schema;
+
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public interface ChampSchemaEnforcer {
+
+ public void validate(ChampObject champObject, ChampObjectConstraint champObjectConstraint) throws ChampSchemaViolationException;
+ public void validate(ChampRelationship champRelationship, ChampRelationshipConstraint champRelationshipConstraint) throws ChampSchemaViolationException;
+ public void validate(ChampPartition champPartition, ChampSchema schema) throws ChampSchemaViolationException;
+}
diff --git a/src/main/java/org/openecomp/aai/champ/schema/DefaultChampSchemaEnforcer.java b/src/main/java/org/openecomp/aai/champ/schema/DefaultChampSchemaEnforcer.java
new file mode 100644
index 0000000..19ae46f
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/schema/DefaultChampSchemaEnforcer.java
@@ -0,0 +1,144 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.schema;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.model.ChampConnectionConstraint;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public final class DefaultChampSchemaEnforcer implements ChampSchemaEnforcer {
+
+ @Override
+ public void validate(ChampObject champObject, ChampObjectConstraint champObjectConstraint) throws ChampSchemaViolationException {
+ for (ChampPropertyConstraint pc : champObjectConstraint.getPropertyConstraints()) {
+ final ChampField field = pc.getField();
+ final Optional<Object> property = champObject.getProperty(field.getName());
+
+ if (pc.isRequired() && !property.isPresent()) {
+ throw new ChampSchemaViolationException("Required property " + pc.getField().getName() + " is not present");
+ }
+
+ if (property.isPresent()) {
+ switch (pc.getCardinality()) {
+ case SINGLE:
+ if (!pc.getField().getJavaType().isInstance(property.get())) {
+ throw new ChampSchemaViolationException("Expected type " + pc.getField().getType() + " for type " + pc.getField().getName());
+ }
+ break;
+ case LIST:
+ if (!(property.get() instanceof List)) throw new ChampSchemaViolationException("Expected List type for ChampCardinality." + pc.getCardinality());
+ break;
+ case SET:
+ if (!(property.get() instanceof Set)) throw new ChampSchemaViolationException("Expected Set type for ChampCardinality." + pc.getCardinality());
+ break;
+ default:
+ throw new RuntimeException("Unknown property constraint cardinality " + pc.getCardinality());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void validate(ChampRelationship champRelationship,
+ ChampRelationshipConstraint champRelationshipConstraint) throws ChampSchemaViolationException {
+
+ for (ChampPropertyConstraint pc : champRelationshipConstraint.getPropertyConstraints()) {
+ final ChampField field = pc.getField();
+ final Optional<Object> property = champRelationship.getProperty(field.getName());
+
+ if (pc.isRequired() && !property.isPresent()) {
+ throw new ChampSchemaViolationException("Required property " + pc.getField().getName() + " is not present");
+ }
+
+ if (property.isPresent() && !pc.getField().getJavaType().isInstance(property.get())) {
+ throw new ChampSchemaViolationException("Expected type " + pc.getField().getType() + " for type " + pc.getField().getName());
+ }
+ }
+ }
+
+ @Override
+ public void validate(ChampPartition champPartition, ChampSchema schema) throws ChampSchemaViolationException {
+
+ for (ChampObject object : champPartition.getChampObjects()) {
+ final Optional<ChampObjectConstraint> objConstraint = schema.getObjectConstraint(object.getType());
+
+ if (!objConstraint.isPresent()) continue;
+
+ validate(object, objConstraint.get());
+
+ final Map<String, Set<ChampRelationship>> incidentRelationshipsByType = champPartition.getIncidentRelationshipsByType(object);
+
+ for (Map.Entry<String, Set<ChampRelationship>> incidentRelationshipsOfType : incidentRelationshipsByType.entrySet()) {
+ final Optional<ChampRelationshipConstraint> relConstraint = schema.getRelationshipConstraint(incidentRelationshipsOfType.getKey());
+
+ if (relConstraint.isPresent()) {
+ final ChampRelationshipConstraint relationshipConstraint = relConstraint.get();
+ final Map<ChampConnectionConstraint, AtomicInteger> connectionCounts = new HashMap<ChampConnectionConstraint, AtomicInteger> ();
+
+ for (ChampRelationship incidentRelationship : incidentRelationshipsOfType.getValue()) {
+ final Optional<ChampConnectionConstraint> connectionConstraint = relationshipConstraint.getConnectionConstraint(incidentRelationship);
+
+ validate(incidentRelationship, relationshipConstraint);
+
+ if (connectionConstraint.isPresent()) {
+
+ if (!connectionCounts.containsKey(connectionConstraint.get())) {
+ connectionCounts.put(connectionConstraint.get(), new AtomicInteger(0));
+ }
+
+ final int connectionCount = connectionCounts.get(connectionConstraint.get()).incrementAndGet();
+
+ switch (connectionConstraint.get().getMultiplicity()) {
+ case MANY:
+ //Always valid
+ break;
+ case NONE:
+ if (connectionCount > 0) throw new ChampSchemaViolationException("Violated connection constraint " + connectionConstraint.get());
+ break;
+ case ONE:
+ if (connectionCount > 1) throw new ChampSchemaViolationException("Violated connection constraint " + connectionConstraint.get());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/openecomp/aai/champ/transform/Champformer.java b/src/main/java/org/openecomp/aai/champ/transform/Champformer.java
new file mode 100644
index 0000000..dc79cd8
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/transform/Champformer.java
@@ -0,0 +1,34 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.transform;
+
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+
+public interface Champformer<V, E> {
+ public V marshallObject(ChampObject object) throws ChampUnmarshallingException;
+ public E marshallRelationship(ChampRelationship relationship) throws ChampUnmarshallingException;
+
+ public ChampObject unmarshallObject(V data) throws ChampUnmarshallingException;
+ public ChampRelationship unmarshallRelationship(E data) throws ChampUnmarshallingException;
+}
diff --git a/src/main/java/org/openecomp/aai/champ/transform/TinkerpopChampformer.java b/src/main/java/org/openecomp/aai/champ/transform/TinkerpopChampformer.java
new file mode 100644
index 0000000..412c8c3
--- /dev/null
+++ b/src/main/java/org/openecomp/aai/champ/transform/TinkerpopChampformer.java
@@ -0,0 +1,94 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.transform;
+
+import java.util.Iterator;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.fluent.object.ObjectBuildOrPropertiesStep;
+import org.openecomp.aai.champ.model.fluent.relationship.RelationshipBuildOrPropertiesStep;
+
+public final class TinkerpopChampformer implements Champformer<Vertex, Edge> {
+
+ @Override
+ public Vertex marshallObject(ChampObject object) throws ChampUnmarshallingException {
+ throw new UnsupportedOperationException("Cannot marshall object to Tinkerpop Vertex without adding it to a graph");
+ }
+
+ @Override
+ public Edge marshallRelationship(ChampRelationship relationship) throws ChampUnmarshallingException {
+ throw new UnsupportedOperationException("Cannot marshall relationships to Tinkerpop Edge without adding it to a graph");
+ }
+
+ @Override
+ public ChampObject unmarshallObject(Vertex vertex) throws ChampUnmarshallingException {
+ final String type = vertex.label();
+ final ObjectBuildOrPropertiesStep aaiObjBuilder = ChampObject.create()
+ .ofType(type)
+ .withKey(vertex.id());
+ final Iterator<VertexProperty<Object>> properties = vertex.properties();
+
+ while (properties.hasNext()) {
+ final VertexProperty<Object> property = properties.next();
+
+ if (ChampObject.ReservedPropertyKeys.contains(property.key()) ||
+ ChampObject.IgnoreOnReadPropertyKeys.contains(property.key())) continue;
+
+ aaiObjBuilder.withProperty(property.key(), property.value());
+ }
+
+ return aaiObjBuilder.build();
+ }
+
+ @Override
+ public ChampRelationship unmarshallRelationship(Edge edge) throws ChampUnmarshallingException {
+ final ChampObject source = unmarshallObject(edge.outVertex());
+ final ChampObject target = unmarshallObject(edge.inVertex());
+ final String type = edge.label();
+ final RelationshipBuildOrPropertiesStep aaiRelBuilder = ChampRelationship.create()
+ .ofType(type)
+ .withKey(edge.id())
+ .withSource()
+ .from(source)
+ .build()
+ .withTarget()
+ .from(target)
+ .build();
+ final Iterator<Property<Object>> properties = edge.properties();
+
+ while (properties.hasNext()) {
+ final Property<Object> property = properties.next();
+
+ if (ChampRelationship.ReservedPropertyKeys.contains(property.key())) continue;
+
+ aaiRelBuilder.withProperty(property.key(), property.value());
+ }
+
+ return aaiRelBuilder.build();
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/concurrency/ConcurrencyTest.java b/src/test/java/org/openecomp/aai/champ/concurrency/ConcurrencyTest.java
new file mode 100644
index 0000000..a9a6b75
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/concurrency/ConcurrencyTest.java
@@ -0,0 +1,72 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.concurrency;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.core.ChampObjectTest;
+import org.openecomp.aai.champ.core.ChampRelationshipTest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConcurrencyTest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ConcurrencyTest.class);
+
+ @Test
+ public void runConcurrentTest() {
+ for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+ final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+ runConcurrencyTest(api);
+ api.shutdown();
+ }
+ }
+
+ private void runConcurrencyTest(ChampAPI api) {
+ final int numThreads = 10;
+ final ExecutorService es = Executors.newFixedThreadPool(numThreads);
+
+ for (int i = 0; i < numThreads * 2; i++) {
+ es.submit(new Runnable() {
+ @Override
+ public void run() {
+ final ChampGraph graph = api.getGraph(ConcurrencyTest.class.getSimpleName());
+ ChampObjectTest.testChampObjectCrud(graph);
+ ChampRelationshipTest.testChampRelationshipCrud(graph);
+ }
+ });
+ }
+
+ try {
+ es.shutdown();
+ es.awaitTermination(60, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ LOGGER.warn("Interrupted while waiting for concurrency test to finish", e);
+ return;
+ }
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/BaseChampAPITest.java b/src/test/java/org/openecomp/aai/champ/core/BaseChampAPITest.java
new file mode 100644
index 0000000..7d374c3
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/BaseChampAPITest.java
@@ -0,0 +1,50 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BaseChampAPITest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BaseChampAPITest.class);
+
+ protected static void cleanUp(String graphName) {
+ LOGGER.warn("cleanUp is disabled for unit tests - you can enable it by commenting out the code below this log message, please don't commit it though");
+
+ /*
+ LOGGER.debug("Cleaning up graph {}", graphName);
+
+ try {
+ final TitanGraph graph = TitanFactory.build()
+ .set("storage.backend", "cassandra")
+ .set("storage.hostname", "localhost")
+ .set("storage.cassandra.keyspace", graphName)
+ .open();
+ graph.close();
+ TitanCleanup.clear(graph);
+ } catch (IllegalArgumentException e) {
+ LOGGER.warn("Could not clean up graph - unable to instantiate");
+ }
+ */
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampAPITest.java b/src/test/java/org/openecomp/aai/champ/core/ChampAPITest.java
new file mode 100644
index 0000000..7cd259e
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampAPITest.java
@@ -0,0 +1,239 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+
+public class ChampAPITest {
+
+ @Test
+ public void testChampAPIInstantiation() {
+
+ for (ChampGraph.Type type : ChampGraph.Type.values()) {
+
+ final ChampAPI api = ChampAPI.Factory.newInstance(type);
+
+ assertTrue(type == ChampGraph.Type.valueOf(type.name()));
+ assertTrue(api.getType() == type);
+
+ api.getGraph("foo");
+ api.shutdown();
+
+ try {
+ api.getGraph("foo");
+ throw new AssertionError("Able to call getGraph(String name) after shutdown()");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+ }
+ }
+
+ @Test
+ public void testChampGraphInstantiation() throws Exception {
+ for (ChampGraph.Type type : ChampGraph.Type.values()) {
+ final ChampGraph graph = ChampGraph.Factory.newInstance(type, "foo");
+
+ graph.shutdown();
+
+ try {
+ graph.deleteObject(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.deleteObjectIndex(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.deletePartition(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.deleteRelationship(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.deleteRelationshipIndex(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.deleteSchema();
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.queryObjects(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.queryRelationships(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.retrieveObject(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.retrieveObjectIndex(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.retrieveObjectIndices();
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.retrieveRelationship(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.retrieveRelationshipIndex(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.retrieveRelationshipIndices();
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.retrieveRelationships(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.retrieveSchema();
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.storeObject(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.storeObjectIndex(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.storePartition(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.storeRelationship(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.storeRelationshipIndex(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.storeSchema(null);
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.updateSchema(new ChampObjectConstraint.Builder("").build());
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.updateSchema(new ChampRelationshipConstraint.Builder("").build());
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+
+ try {
+ graph.shutdown();
+ throw new AssertionError("Able to call API method after shutdown was initiated");
+ } catch (IllegalStateException e) {
+ //Expected
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampElementTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampElementTest.java
new file mode 100644
index 0000000..4d34f8a
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampElementTest.java
@@ -0,0 +1,62 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.model.ChampElement;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+
+public class ChampElementTest {
+
+ @Test
+ public void testChampElement() {
+ final ChampElement fooElement = new ChampObject.Builder("foo").build();
+
+ assertTrue(fooElement.isObject());
+ assertTrue(!fooElement.isRelationship());
+ assertTrue(fooElement.asObject() instanceof ChampObject);
+
+ try {
+ fooElement.asRelationship();
+ throw new AssertionError("Failed to throw exception when calling asRelationship() on a ChampObject");
+ } catch (UnsupportedOperationException e) {
+ //Expected
+ }
+
+ final ChampElement barElement = new ChampObject.Builder("bar").build();
+ final ChampElement usesElement = new ChampRelationship.Builder(fooElement.asObject(), barElement.asObject(), "uses").build();
+
+ assertTrue(usesElement.isRelationship());
+ assertTrue(!usesElement.isObject());
+ assertTrue(usesElement.asRelationship() instanceof ChampRelationship);
+
+ try {
+ usesElement.asObject();
+ throw new AssertionError("Failed to throw exception when calling asObject() on a ChampRelationship");
+ } catch (UnsupportedOperationException e) {
+ //Expected
+ }
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampFieldTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampFieldTest.java
new file mode 100644
index 0000000..f852d9d
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampFieldTest.java
@@ -0,0 +1,53 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.model.ChampField;
+
+public class ChampFieldTest {
+
+ @Test
+ public void testChampField() {
+ final ChampField a = new ChampField.Builder("a")
+ .type(ChampField.Type.STRING)
+ .build();
+
+ final ChampField aEquivalent = new ChampField.Builder("a")
+ .build();
+
+ final ChampField b = new ChampField.Builder("b")
+ .build();
+
+ assertTrue(a.equals(aEquivalent));
+ assertTrue(!a.equals(new Object()));
+ assertTrue(!a.equals(b));
+
+ assertTrue(a.compareTo(aEquivalent) == 0);
+ assertTrue(a.compareTo(b) != 0);
+
+ assertTrue(a.toString().equals(aEquivalent.toString()));
+ assertTrue(!a.toString().equals(b.toString()));
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampObjectIndexTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampObjectIndexTest.java
new file mode 100644
index 0000000..6f71664
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampObjectIndexTest.java
@@ -0,0 +1,142 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+
+public class ChampObjectIndexTest extends BaseChampAPITest {
+ @Test
+ public void runTest() {
+ for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+ final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+ final String graphName = api.getClass().getSimpleName();
+
+ switch (apiType) {
+ case IN_MEMORY:
+ break;
+ case TITAN:
+ cleanUp(graphName);
+ break;
+ default:
+ break;
+ }
+
+ ChampObjectIndexTest.testChampObjectIndexCrud(api.getGraph(graphName));
+
+ api.shutdown();
+ }
+ }
+
+ private static void testChampObjectIndexCrud(ChampGraph graph) {
+
+ final ChampObjectIndex objectIndex = ChampObjectIndex.create()
+ .ofName("fooObjectIndex")
+ .onType("foo")
+ .forField("propertyName")
+ .build();
+
+ testChampObjectIndexStorage(graph, objectIndex);
+ testChampObjectIndexDelete(graph, objectIndex);
+ }
+
+ private static void testChampObjectIndexDelete(ChampGraph graph, ChampObjectIndex objectIndex) {
+
+ if (!graph.capabilities().canDeleteObjectIndices()) {
+ try {
+ graph.deleteObjectIndex("someindex");
+ throw new AssertionError("Graph claims it does not support object index delete, but failed to throw UnsupportedOperationException");
+ } catch (UnsupportedOperationException e) {
+ } catch (ChampIndexNotExistsException e) {
+ throw new AssertionError("Graph claims it does not support object index delete, but failed to throw UnsupportedOperationException");
+ }
+ } else {
+ try {
+ graph.deleteObjectIndex(objectIndex.getName());
+
+ final Optional<ChampObjectIndex> retrievedObjectIndex = graph.retrieveObjectIndex(objectIndex.getName());
+
+ if (retrievedObjectIndex.isPresent()) throw new AssertionError("Retrieved object index after deleting it");
+
+ final Stream<ChampObjectIndex> retrievedObjectIndices = graph.retrieveObjectIndices();
+ final Collection<ChampObjectIndex> allObjectIndices = retrievedObjectIndices.collect(Collectors.toList());
+
+ if (allObjectIndices.contains(objectIndex)) throw new AssertionError("Retrieve all indices contained index previously deleted");
+ if (allObjectIndices.size() != 0) throw new AssertionError("Wrong number of indices returned by retrieve all indices");
+
+ } catch (ChampIndexNotExistsException e) {
+ throw new AssertionError(e);
+ }
+
+ try {
+ graph.deleteObjectIndex(objectIndex.getName());
+ throw new AssertionError("Failed to throw exception on non-existent object index");
+ } catch (ChampIndexNotExistsException e) {
+ //Expected
+ }
+ }
+ }
+
+ private static void testChampObjectIndexStorage(ChampGraph graph, ChampObjectIndex objectIndex) {
+
+ graph.storeObjectIndex(objectIndex);
+ graph.storeObjectIndex(objectIndex); //Test storing an already existing object index
+
+ assertTrue(!graph.retrieveRelationshipIndex(objectIndex.getName()).isPresent()); //Make sure this wasn't stored as an object index
+
+ final Optional<ChampObjectIndex> retrieveObjectIndex = graph.retrieveObjectIndex(objectIndex.getName());
+
+ if (!retrieveObjectIndex.isPresent()) throw new AssertionError("Failed to retrieve object index after storing it");
+ if (!objectIndex.equals(retrieveObjectIndex.get())) throw new AssertionError("Non-equal object index returned from API after storing it");
+
+ final Stream<ChampObjectIndex> retrievedObjectIndices = graph.retrieveObjectIndices();
+ final Collection<ChampObjectIndex> allObjectIndices = retrievedObjectIndices.collect(Collectors.toList());
+
+ if (!allObjectIndices.contains(objectIndex)) throw new AssertionError("Retrieve all indices did not contained index previously stored");
+ if (allObjectIndices.size() != 1) throw new AssertionError("Wrong number of indices returned by retrieve all indices");
+
+ assertTrue(!graph.retrieveObjectIndex("nonExistentIndexName").isPresent());
+ }
+
+ @Test
+ public void testFluentRelationshipCreation() {
+ final ChampObjectIndex objectIndex = ChampObjectIndex.create()
+ .ofName("fooNameIndex")
+ .onType("foo")
+ .forField("name")
+ .build();
+
+ assertTrue(objectIndex.getName().equals("fooNameIndex"));
+ assertTrue(objectIndex.getType().equals("foo"));
+ assertTrue(objectIndex.getField().getName().equals("name"));
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampObjectTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampObjectTest.java
new file mode 100644
index 0000000..f149243
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampObjectTest.java
@@ -0,0 +1,331 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampCardinality;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampSchema;
+
+public class ChampObjectTest extends BaseChampAPITest {
+
+ @Test
+ public void testHashCode() {
+ final ChampObject foo1 = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property", "value")
+ .withProperty("prop", 1)
+ .build();
+
+ final ChampObject foo2 = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property", "value")
+ .withProperty("prop", 1)
+ .build();
+
+ final ChampObject foo1Copy = ChampObject.create()
+ .from(foo1)
+ .withoutKey()
+ .build();
+
+ final ChampObject foo2Copy = ChampObject.create()
+ .from(foo2)
+ .withoutKey()
+ .build();
+
+ assertTrue(foo1.hashCode() == foo2.hashCode());
+ assertTrue(foo1.hashCode() == foo1.hashCode());
+ assertTrue(foo2.hashCode() == foo2.hashCode());
+ assertTrue(foo1.hashCode() == foo1Copy.hashCode());
+ assertTrue(foo2.hashCode() == foo2Copy.hashCode());
+
+ assertTrue(Collections.singleton(foo1).contains(foo1));
+ assertTrue(Collections.singleton(foo1).contains(foo1Copy));
+ }
+
+ @Test
+ public void runTest() {
+ for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+ final String graphName = ChampObjectTest.class.getSimpleName();
+ switch (apiType) {
+ case TITAN:
+ cleanUp(graphName);
+ break;
+ default:
+ break;
+ }
+
+ final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+ ChampObjectTest.testChampObjectCrud(api.getGraph(graphName));
+ testChampObjectReservedProperties(api.getGraph(graphName));
+ api.shutdown();
+ }
+ }
+
+ public static void testChampObjectCrud(ChampGraph graph) {
+ final ChampObject bookooObject = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property1", "value1")
+ .withProperty("integer", 1)
+ .withProperty("long", 1L)
+ .withProperty("double", 1.2)
+ .withProperty("float", 2.3F)
+ .withProperty("string", "foo")
+ .withProperty("boolean", true)
+ .withProperty("list", Collections.singletonList("list"))
+ .withProperty("set", Collections.singleton("set"))
+ .build();
+
+ final ChampObject storedBookooObject;
+
+ try {
+
+ graph.storeSchema(ChampSchema.create()
+ .withObjectConstraint()
+ .onType("foo")
+ .withPropertyConstraint()
+ .onField("list")
+ .ofType(ChampField.Type.STRING)
+ .cardinality(ChampCardinality.LIST)
+ .optional()
+ .build()
+ .withPropertyConstraint()
+ .onField("set")
+ .ofType(ChampField.Type.STRING)
+ .cardinality(ChampCardinality.SET)
+ .optional()
+ .build()
+ .build()
+ .build());
+
+ storedBookooObject = graph.storeObject(bookooObject);
+
+ assertTrue(storedBookooObject.getProperty("property1").get().equals("value1"));
+ assertTrue(storedBookooObject.getProperty("integer").get().equals(1));
+ assertTrue(storedBookooObject.getProperty("long").get().equals(1L));
+ assertTrue(storedBookooObject.getProperty("double").get().equals(1.2));
+ assertTrue(storedBookooObject.getProperty("float").get().equals(2.3F));
+ assertTrue(storedBookooObject.getProperty("string").get().equals("foo"));
+ assertTrue(storedBookooObject.getProperty("boolean").get().equals(true));
+ assertTrue(storedBookooObject.getProperty("list").get().equals(Collections.singletonList("list")));
+ assertTrue(storedBookooObject.getProperty("set").get().equals(Collections.singleton("set")));
+
+ final Optional<ChampObject> retrievedBookooObject = graph.retrieveObject(storedBookooObject.getKey().get());
+ final Stream<ChampObject> emptyStream = graph.queryObjects(new HashMap<String, Object> () {{
+ put(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo");
+ put("long", 2L);
+ }});
+
+ assertTrue(emptyStream.limit(1).count() == 0);
+
+ final Stream<ChampObject> oneStream = graph.queryObjects(new HashMap<String, Object> () {{
+ put(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_TYPE.toString(), "foo");
+ put("long", 1L);
+ }});
+ final List<ChampObject> oneObject = oneStream.limit(2).collect(Collectors.toList());
+ assertTrue(oneObject.size() == 1);
+ assertTrue(oneObject.get(0).equals(storedBookooObject));
+
+ final List<ChampObject> queryByKey = graph.queryObjects(Collections.singletonMap(ChampObject.ReservedPropertyKeys.CHAMP_OBJECT_KEY.toString(), storedBookooObject.getKey().get()))
+ .limit(2)
+ .collect(Collectors.toList());
+
+ assertTrue(queryByKey.size() == 1);
+ assertTrue(queryByKey.get(0).equals(storedBookooObject));
+
+ if (!retrievedBookooObject.isPresent()) throw new AssertionError("Failed to retrieve stored object " + bookooObject);
+ if (!storedBookooObject.equals(retrievedBookooObject.get())) throw new AssertionError("Retrieved object does not equal stored object");
+
+ final ChampObject updatedBookoo = graph.storeObject(ChampObject.create()
+ .from(storedBookooObject)
+ .withKey(storedBookooObject.getKey().get())
+ .withProperty("long", 2L)
+ .build());
+
+ final Optional<ChampObject> retrievedUpdBookooObject = graph.retrieveObject(updatedBookoo.getKey().get());
+
+ assertTrue(updatedBookoo.getProperty("property1").get().equals("value1"));
+ assertTrue(updatedBookoo.getProperty("integer").get().equals(1));
+ assertTrue(updatedBookoo.getProperty("long").get().equals(2L));
+ assertTrue(updatedBookoo.getProperty("double").get().equals(1.2));
+ assertTrue(updatedBookoo.getProperty("float").get().equals(2.3F));
+ assertTrue(updatedBookoo.getProperty("string").get().equals("foo"));
+ assertTrue(updatedBookoo.getProperty("boolean").get().equals(true));
+ assertTrue(updatedBookoo.getProperty("list").get().equals(Collections.singletonList("list")));
+ assertTrue(updatedBookoo.getProperty("set").get().equals(Collections.singleton("set")));
+
+ if (!retrievedUpdBookooObject.isPresent()) throw new AssertionError("Failed to retrieve stored object " + bookooObject);
+ if (!updatedBookoo.equals(retrievedUpdBookooObject.get())) throw new AssertionError("Retrieved object does not equal stored object");
+
+ //validate the replaceObject method
+ final ChampObject replacedBookoo = graph.replaceObject(ChampObject.create()
+ .ofType("foo")
+ .withKey(storedBookooObject.getKey().get())
+ .withProperty("property1", "value2")
+ .withProperty("list", Collections.singletonList("list"))
+ .withProperty("set", Collections.singleton("set"))
+ .build());
+
+ final Optional<ChampObject> retrievedReplacedBookooObject = graph.retrieveObject(replacedBookoo.getKey().get());
+
+ assertTrue(replacedBookoo.getProperties().size()==3);
+ assertTrue(replacedBookoo.getProperty("property1").get().equals("value2"));
+ assertTrue(replacedBookoo.getProperty("list").get().equals(Collections.singletonList("list")));
+ assertTrue(replacedBookoo.getProperty("set").get().equals(Collections.singleton("set")));
+
+
+ if (!retrievedReplacedBookooObject.isPresent()) throw new AssertionError("Failed to retrieve stored object " + replacedBookoo);
+ if (!replacedBookoo.equals(retrievedReplacedBookooObject.get())) throw new AssertionError("Retrieved object does not equal stored object");
+
+
+
+
+
+ graph.deleteObject(storedBookooObject.getKey().get());
+ if (graph.retrieveObject(storedBookooObject.getKey().get()).isPresent()) throw new AssertionError("Object not successfully deleted");
+
+ assertTrue(graph.queryObjects(Collections.emptyMap()).count() == 0);
+ assertTrue(graph.queryRelationships(Collections.emptyMap()).count() == 0);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError("Schema mismatch while storing object", e);
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError("Marshalling exception while storing object", e);
+ } catch (ChampUnmarshallingException e) {
+ throw new AssertionError("Unmarshalling exception while retrieving object", e);
+ }catch (ChampObjectNotExistsException e) {
+ throw new AssertionError("Missing object on delete/update", e);
+ }
+
+ try {
+ graph.deleteObject(storedBookooObject.getKey().get());
+ throw new AssertionError("Delete succeeded when it should have failed");
+ } catch (ChampObjectNotExistsException e) {
+ //Expected
+ }
+
+ try {
+ graph.storeObject(ChampObject.create()
+ .ofType("foo")
+ .withKey("non-existent object key")
+ .build());
+ throw new AssertionError("Expected ChampObjectNotExistsException but object was successfully stored");
+ } catch (ChampObjectNotExistsException e) {
+ //Expected
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+
+ try {
+ // validate the replaceObject method when Object key is not passed
+ graph.replaceObject(
+ ChampObject.create().ofType("foo").withoutKey().withProperty("property1", "value2").build());
+ } catch (ChampObjectNotExistsException e) {
+ // Expected
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+
+ }
+
+ public void testChampObjectReservedProperties(ChampGraph graph) {
+
+ for (ChampObject.ReservedPropertyKeys key : ChampObject.ReservedPropertyKeys.values()) {
+ try {
+ ChampObject.create()
+ .ofType(ChampObject.ReservedTypes.ANY.toString())
+ .withoutKey()
+ .withProperty(key.toString(), "")
+ .build();
+ throw new AssertionError("Allowed reserved property key to be used during object creation");
+ } catch (IllegalArgumentException e) {
+ //Expected
+ }
+ }
+ }
+
+ @Test
+ public void testFluentObjectCreation() {
+ final Object value1 = new Object();
+ final String value2 = "value2";
+ final float value3 = 0.0f;
+
+ final ChampObject champObject1 = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("key1", value1)
+ .withProperty("key2", value2)
+ .withProperty("key3", value3)
+ .build();
+
+ assertTrue(champObject1.getKey().equals(Optional.empty()));
+ assertTrue(champObject1.getKey().isPresent() == false);
+ assertTrue(champObject1.getType().equals("foo"));
+ assertTrue(champObject1.getProperty("key1").get() instanceof Object);
+ assertTrue(champObject1.getProperty("key1").get().equals(value1));
+ assertTrue(champObject1.getProperty("key2").get() instanceof String);
+ assertTrue(champObject1.getProperty("key2").get().equals(value2));
+ assertTrue(champObject1.getProperty("key3").get() instanceof Float);
+ assertTrue(champObject1.getProperty("key3").get().equals(value3));
+
+ final ChampObject champObject2 = ChampObject.create()
+ .ofType("foo")
+ .withKey(1)
+ .withProperty("key1", value1)
+ .withProperty("key2", value2)
+ .withProperty("key3", value3)
+ .build();
+
+ assertTrue(champObject2.getType().equals("foo"));
+ assertTrue(champObject2.getKey().isPresent() == true);
+ assertTrue(champObject2.getKey().get() instanceof Integer);
+ assertTrue(champObject2.getKey().get().equals(1));
+ assertTrue(champObject2.getProperty("key1").get() instanceof Object);
+ assertTrue(champObject2.getProperty("key1").get().equals(value1));
+ assertTrue(champObject2.getProperty("key2").get() instanceof String);
+ assertTrue(champObject2.getProperty("key2").get().equals(value2));
+ assertTrue(champObject2.getProperty("key3").get() instanceof Float);
+ assertTrue(champObject2.getProperty("key3").get().equals(value3));
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampPartitionTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampPartitionTest.java
new file mode 100644
index 0000000..c8671bb
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampPartitionTest.java
@@ -0,0 +1,195 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Optional;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+
+public class ChampPartitionTest extends BaseChampAPITest {
+
+ @Test
+ public void runTests() {
+ for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+ final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+ final String graphName = ChampPartitionTest.class.getSimpleName();
+
+ switch (apiType) {
+ case IN_MEMORY:
+ break;
+ case TITAN:
+ cleanUp(graphName);
+ break;
+ default:
+ break;
+ }
+
+ ChampPartitionTest.testChampPartitionCrud(api.getGraph(graphName));
+ api.shutdown();
+ }
+ }
+
+ @Test
+ public void testHashCode() {
+
+ final ChampObject foo = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .build();
+ final ChampObject bar = ChampObject.create()
+ .ofType("bar")
+ .withoutKey()
+ .build();
+ final ChampRelationship baz = ChampRelationship.create()
+ .ofType("baz")
+ .withoutKey()
+ .withSource()
+ .from(foo)
+ .build()
+ .withTarget()
+ .from(bar)
+ .build()
+ .build();
+
+ final ChampPartition partition = ChampPartition.create()
+ .withObject(foo)
+ .withObject(bar)
+ .withRelationship(baz)
+ .build();
+
+ assertTrue(partition.getChampObjects().contains(foo));
+ assertTrue(partition.getChampObjects().contains(bar));
+ assertTrue(partition.getChampRelationships().contains(baz));
+ }
+
+ @Test
+ public void testBuilder() {
+ final ChampObject foo = new ChampObject.Builder("foo").build();
+ final ChampObject bar = new ChampObject.Builder("bar").build();
+ final ChampRelationship uses = new ChampRelationship.Builder(foo, bar, "uses")
+ .build();
+ final ChampPartition a = new ChampPartition.Builder()
+ .object(foo)
+ .objects(Collections.singleton(bar))
+ .relationship(uses)
+ .relationships(Collections.singleton(uses))
+ .build();
+ assertTrue(a.getChampObjects().size() == 2);
+ assertTrue(a.getChampObjects().contains(foo));
+ assertTrue(a.getChampObjects().contains(bar));
+
+ assertTrue(a.getChampRelationships().size() == 1);
+ assertTrue(a.getChampRelationships().contains(uses));
+ }
+
+ public static void testChampPartitionCrud(ChampGraph graph) {
+
+ final ChampObject foo = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("prop1", "value1")
+ .build();
+ final ChampObject bar = ChampObject.create()
+ .ofType("bar")
+ .withoutKey()
+ .withProperty("prop2", "value2")
+ .build();
+
+ final ChampRelationship baz = ChampRelationship.create()
+ .ofType("baz")
+ .withoutKey()
+ .withSource()
+ .from(foo)
+ .build()
+ .withTarget()
+ .from(bar)
+ .build()
+ .withProperty("prop3", "value3")
+ .build();
+
+ final ChampPartition partition = ChampPartition.create()
+ .withObject(foo)
+ .withObject(bar)
+ .withRelationship(baz)
+ .build();
+
+ assertTrue(partition.getIncidentRelationships(foo).contains(baz));
+ assertTrue(partition.getIncidentRelationships(bar).contains(baz));
+ assertTrue(partition.getIncidentRelationshipsByType(foo).get("baz").contains(baz));
+
+ try {
+ final ChampPartition storedPartition = graph.storePartition(partition);
+
+ ChampPartitionTest.retrievePartitionElements(graph, storedPartition, true);
+
+ graph.deletePartition(storedPartition);
+
+ ChampPartitionTest.retrievePartitionElements(graph, storedPartition, false);
+
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampObjectNotExistsException e) {
+ throw new AssertionError(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ } catch (ChampRelationshipNotExistsException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static void retrievePartitionElements(ChampGraph graph, ChampPartition partition, boolean expectFound) {
+ for (ChampObject object : partition.getChampObjects()) {
+ try {
+ final Optional<ChampObject> retrievedObject = graph.retrieveObject(object.getKey().get());
+
+ if (!expectFound && retrievedObject.isPresent()) throw new AssertionError("Expected object to not be found, but it was found");
+ if (expectFound && !retrievedObject.isPresent()) throw new AssertionError("Expected object to be found, but it was not found");
+ } catch (ChampUnmarshallingException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ for (ChampRelationship relationship : partition.getChampRelationships()) {
+ try {
+ final Optional<ChampRelationship> retrievedRelationship = graph.retrieveRelationship(relationship.getKey().get());
+
+ if (!expectFound && retrievedRelationship.isPresent()) throw new AssertionError("Expected relationship to not be found, but it was found");
+ if (expectFound && !retrievedRelationship.isPresent()) throw new AssertionError("Expected relationship to be found, but it was not found");
+ } catch (ChampUnmarshallingException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampPropertyConstraintTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampPropertyConstraintTest.java
new file mode 100644
index 0000000..8ee45f4
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampPropertyConstraintTest.java
@@ -0,0 +1,57 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.model.ChampCardinality;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+
+public class ChampPropertyConstraintTest {
+
+ @Test
+ public void testChampPropertyConstraint() {
+ final ChampField z = new ChampField.Builder("z").build();
+ final ChampField y = new ChampField.Builder("y").build();
+
+ final ChampPropertyConstraint a = new ChampPropertyConstraint.Builder(z)
+ .cardinality(ChampCardinality.SINGLE)
+ .required(false)
+ .build();
+ final ChampPropertyConstraint aEquivalent = new ChampPropertyConstraint.Builder(z)
+ .build();
+
+ final ChampPropertyConstraint b = new ChampPropertyConstraint.Builder(y)
+ .build();
+ assertTrue(a.equals(aEquivalent));
+ assertTrue(!a.equals(b));
+ assertTrue(!a.equals(new Object()));
+
+ assertTrue(a.toString().equals(aEquivalent.toString()));
+ assertTrue(!a.toString().equals(b.toString()));
+
+ assertTrue(a.compareTo(aEquivalent) == 0);
+ assertTrue(a.compareTo(b) != 0);
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipIndexTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipIndexTest.java
new file mode 100644
index 0000000..a611df7
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipIndexTest.java
@@ -0,0 +1,175 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+
+public class ChampRelationshipIndexTest extends BaseChampAPITest {
+
+ @Test
+ public void runTest() {
+ for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+ final String graphName = ChampRelationshipIndexTest.class.getSimpleName();
+
+ switch (apiType) {
+ case IN_MEMORY:
+ break;
+ case TITAN:
+ cleanUp(graphName);
+ break;
+ default:
+ break;
+ }
+
+ final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+ testChampRelationshipIndexCrud(api.getGraph(graphName));
+ api.shutdown();
+ }
+ }
+
+ private void testChampRelationshipIndexCrud(ChampGraph graph) {
+
+ final ChampField relationshipField = new ChampField.Builder("propertyName").build();
+ final ChampRelationshipIndex relationshipIndex = new ChampRelationshipIndex.Builder("fooEdgeIndex", "foo", relationshipField).build();
+
+ //Test on an empty graph
+ testChampRelationshipIndexStorage(graph, relationshipIndex);
+ testChampRelationshipIndexDelete(graph, relationshipIndex);
+
+ //Test with existing data in graph
+ try {
+ graph.storeRelationship(ChampRelationship.create()
+ .ofType("uses")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .build()
+ .withTarget()
+ .ofType("bar")
+ .withoutKey()
+ .build()
+ .build());
+ testChampRelationshipIndexStorage(graph, relationshipIndex);
+ testChampRelationshipIndexDelete(graph, relationshipIndex);
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ } catch (ChampObjectNotExistsException e) {
+ throw new AssertionError(e);
+ } catch (ChampRelationshipNotExistsException e) {
+ throw new AssertionError(e);
+ } catch (ChampUnmarshallingException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private void testChampRelationshipIndexDelete(ChampGraph graph, ChampRelationshipIndex relationshipIndex) {
+
+ if (!graph.capabilities().canDeleteRelationshipIndices()) {
+ try {
+ graph.deleteRelationshipIndex("someindex");
+ throw new AssertionError("Graph claims it doesn't support relationship index delete, but it failed to throw UnsupportedOperationException");
+ } catch (UnsupportedOperationException e) {
+ //Expected
+ } catch (ChampIndexNotExistsException e) {
+ throw new AssertionError("Graph claims it doesn't support relationship index delete, but it failed to throw UnsupportedOperationException");
+ }
+ } else {
+ try {
+ graph.deleteRelationshipIndex(relationshipIndex.getName());
+
+ final Optional<ChampRelationshipIndex> retrieveRelationshipIndex = graph.retrieveRelationshipIndex(relationshipIndex.getName());
+
+ if (retrieveRelationshipIndex.isPresent()) throw new AssertionError("Retrieve relationship index after deleting it");
+
+ final Stream<ChampRelationshipIndex> relationshipIndices = graph.retrieveRelationshipIndices();
+ final Collection<ChampRelationshipIndex> allRelationshipIndices = relationshipIndices.collect(Collectors.toList());
+
+ if (allRelationshipIndices.contains(relationshipIndex)) throw new AssertionError("Retrieve all relationship indices contains previously deleted index");
+ if (allRelationshipIndices.size() != 0) throw new AssertionError("Wrong number of relationship indices returned by retrieve all indices");
+ } catch (ChampIndexNotExistsException e) {
+ throw new AssertionError(e);
+ }
+
+ try {
+ graph.deleteRelationshipIndex(relationshipIndex.getName());
+ throw new AssertionError("Failed to throw exception on non-existent object index");
+ } catch (ChampIndexNotExistsException e) {
+ //Expected
+ }
+ }
+ }
+
+ private void testChampRelationshipIndexStorage(ChampGraph graph, ChampRelationshipIndex relationshipIndex) {
+
+ graph.storeRelationshipIndex(relationshipIndex);
+ graph.storeRelationshipIndex(relationshipIndex); //Test storing duplicate relationship index
+
+ assertTrue(!graph.retrieveObjectIndex(relationshipIndex.getName()).isPresent()); //Make sure this wasn't stored as an object index
+
+ final Optional<ChampRelationshipIndex> retrieveRelationshipIndex = graph.retrieveRelationshipIndex(relationshipIndex.getName());
+
+ if (!retrieveRelationshipIndex.isPresent()) throw new AssertionError("Failed to retrieve relationship index after storing it");
+ if (!relationshipIndex.equals(retrieveRelationshipIndex.get())) throw new AssertionError("Non-equal relationship index returned from API after storing it");
+
+ final Stream<ChampRelationshipIndex> relationshipIndices = graph.retrieveRelationshipIndices();
+ final Collection<ChampRelationshipIndex> allRelationshipIndices = relationshipIndices.collect(Collectors.toList());
+
+ if (!allRelationshipIndices.contains(relationshipIndex)) throw new AssertionError("Retrieve all relationship indices did not return previously stored relationship index");
+ if (allRelationshipIndices.size() != 1) throw new AssertionError("Wrong number of relationship indices returned by retrieve all indices");
+
+ assertTrue(!graph.retrieveRelationshipIndex("nonExistentIndexName").isPresent());
+ }
+
+ @Test
+ public void testFluentRelationshipIndexCreation() {
+ final ChampRelationshipIndex relationshipIndex = ChampRelationshipIndex.create()
+ .ofName("fooNameIndex")
+ .onType("foo")
+ .forField("name")
+ .build();
+
+ assertTrue(relationshipIndex.getName().equals("fooNameIndex"));
+ assertTrue(relationshipIndex.getType().equals("foo"));
+ assertTrue(relationshipIndex.getField().getName().equals("name"));
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipTest.java
new file mode 100644
index 0000000..cf025d6
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampRelationshipTest.java
@@ -0,0 +1,261 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationship.ReservedPropertyKeys;
+import org.openecomp.aai.champ.model.ChampRelationship.ReservedTypes;
+
+public class ChampRelationshipTest extends BaseChampAPITest {
+
+ @Test
+ public void runTest() {
+ for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+ final String graphName = ChampRelationshipTest.class.getSimpleName();
+
+ switch (apiType) {
+ case IN_MEMORY:
+ break;
+ case TITAN:
+ cleanUp(graphName);
+ break;
+ default:
+ break;
+ }
+
+ final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+ ChampRelationshipTest.testChampRelationshipCrud(api.getGraph(graphName));
+ api.shutdown();
+ }
+ }
+
+ public static void testChampRelationshipCrud(ChampGraph graph) {
+ final ChampObject source = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("property1", "value1")
+ .build();
+
+ final ChampObject target = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .build();
+
+ try {
+ final ChampObject storedSource = graph.storeObject(source);
+ final ChampObject storedTarget = graph.storeObject(target);
+
+ final ChampRelationship relationship = new ChampRelationship.Builder(storedSource, storedTarget, "relationship")
+ .property("property-1", "value-1")
+ .property("property-2", 3)
+ .build();
+
+ final ChampRelationship storedRelationship = graph.storeRelationship(relationship);
+ final Optional<ChampRelationship> retrievedRelationship = graph.retrieveRelationship(storedRelationship.getKey().get());
+
+ if (!retrievedRelationship.isPresent()) throw new AssertionError("Failed to retrieve stored relationship " + storedRelationship);
+ if (!storedRelationship.equals(retrievedRelationship.get())) throw new AssertionError("Retrieved relationship does not equal stored object");
+
+ assertTrue(retrievedRelationship.get().getProperty("property-1").get().equals("value-1"));
+ assertTrue(retrievedRelationship.get().getProperty("property-2").get().equals(3));
+
+ if (!graph.retrieveRelationships(storedRelationship.getSource()).collect(Collectors.toList()).contains(storedRelationship))
+ throw new AssertionError("Failed to retrieve relationships for source object");
+
+ final ChampRelationship updatedRelationship = ChampRelationship.create()
+ .from(retrievedRelationship.get())
+ .withKey(retrievedRelationship.get().getKey().get())
+ .withProperty("property-2", 4)
+ .build();
+
+ final ChampRelationship storedUpdRel = graph.storeRelationship(updatedRelationship);
+ final Optional<ChampRelationship> retrievedUpdRel = graph.retrieveRelationship(storedUpdRel.getKey().get());
+
+ assertTrue(retrievedUpdRel.isPresent());
+ assertTrue(retrievedUpdRel.get().equals(storedUpdRel));
+ assertTrue(retrievedUpdRel.get().getProperty("property-1").get().equals("value-1"));
+ assertTrue(retrievedUpdRel.get().getProperty("property-2").get().equals(4));
+
+
+ // validate the replaceRelationship method
+ final ChampRelationship replacedRelationship = new ChampRelationship.Builder(storedSource, storedTarget, "relationship")
+ .key(retrievedRelationship.get().getKey().get())
+ .property("property-2", 4)
+ .build();
+
+ final ChampRelationship replacedRel = graph.replaceRelationship(replacedRelationship);
+ final Optional<ChampRelationship> retrievedReplacedRel = graph
+ .retrieveRelationship(replacedRel.getKey().get());
+
+ assertTrue(replacedRel.getProperties().size()==1);
+ assertTrue(replacedRel.getProperty("property-2").get().equals(4));
+
+ assertTrue(retrievedReplacedRel.get().getProperties().size()==1);
+ assertTrue(retrievedReplacedRel.get().getProperty("property-2").get().equals(4));
+
+ if (!retrievedReplacedRel.isPresent()) throw new AssertionError("Failed to retrieve stored relationship " + replacedRel);
+ if (!replacedRel.equals(retrievedReplacedRel.get())) throw new AssertionError("Retrieved relationship does not equal stored object");
+
+
+ graph.deleteRelationship(retrievedRelationship.get());
+
+ if (graph.retrieveRelationship(relationship.getKey()).isPresent()) throw new AssertionError("Relationship not successfully deleted");
+
+ try {
+ graph.deleteRelationship(retrievedRelationship.get());
+ throw new AssertionError("Failed to throw exception for missing relationship");
+ } catch (ChampRelationshipNotExistsException e) {
+ //Expected
+ }
+
+ assertTrue(graph.queryRelationships(Collections.emptyMap()).count() == 0);
+ assertTrue(graph.queryObjects(Collections.emptyMap()).count() == 2);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError("Schema mismatch while storing object", e);
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError("Marshalling exception while storing object", e);
+ } catch (ChampUnmarshallingException e) {
+ throw new AssertionError("Unmarshalling exception while retrieving relationship", e);
+ } catch (ChampRelationshipNotExistsException e) {
+ throw new AssertionError("Attempted to delete non-existent relationship", e);
+ } catch (ChampObjectNotExistsException e) {
+ throw new AssertionError("Object does not exist after storing it", e);
+ }
+
+ try {
+ graph.retrieveRelationships(ChampObject.create().ofType("").withoutKey().build());
+ throw new AssertionError("Failed to handle missing object while retrieving relationships");
+ } catch (ChampUnmarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampObjectNotExistsException e) {
+ //Expected
+ }
+ //Negative test cases for replace relationship
+
+ try{
+ graph.replaceRelationship(new ChampRelationship.Builder(ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .build(), ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .build(), "relationship")
+ .key("1234")
+ .property("property-2", 4)
+ .build());
+ }
+ catch (ChampUnmarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ } catch (ChampRelationshipNotExistsException e) {
+ throw new AssertionError(e);
+ } catch(IllegalArgumentException e){
+ //expected
+ }
+
+ try{
+ graph.replaceRelationship(new ChampRelationship.Builder(ChampObject.create()
+ .ofType("foo")
+ .withKey("123")
+ .build(), ChampObject.create()
+ .ofType("foo")
+ .withKey("456")
+ .build(), "relationship")
+ .property("property-2", 4)
+ .build());
+ }
+ catch (ChampUnmarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ } catch (ChampRelationshipNotExistsException e) {
+ //expected
+ } catch(IllegalArgumentException e){
+ throw new AssertionError(e);
+ }
+
+
+ }
+
+ @Test
+ public void testFluentRelationshipCreation() {
+ final Object value1 = new Object();
+ final String value2 = "value2";
+ final float value3 = 0.0f;
+
+ final ChampRelationship champRelationship = ChampRelationship.create()
+ .ofType("foo")
+ .withoutKey()
+ .withSource()
+ .ofType("bar")
+ .withoutKey()
+ .build()
+ .withTarget()
+ .ofType("baz")
+ .withKey(1)
+ .build()
+ .withProperty("key1", value1)
+ .withProperty("key2", value2)
+ .withProperty("key3", value3)
+ .build();
+
+ assertTrue(champRelationship.getKey().equals(Optional.empty()));
+ assertTrue(champRelationship.getType().equals("foo"));
+ assertTrue(champRelationship.getProperty("key1").get() instanceof Object);
+ assertTrue(champRelationship.getProperty("key1").get().equals(value1));
+ assertTrue(champRelationship.getProperty("key2").get() instanceof String);
+ assertTrue(champRelationship.getProperty("key2").get().equals(value2));
+ assertTrue(champRelationship.getProperty("key3").get() instanceof Float);
+ assertTrue(champRelationship.getProperty("key3").get().equals(value3));
+ }
+
+ @Test
+ public void testChampRelationshipEnums() {
+ for (ReservedPropertyKeys key : ChampRelationship.ReservedPropertyKeys.values()) {
+ assertTrue(ChampRelationship.ReservedPropertyKeys.valueOf(key.name()) == key);
+ }
+
+ for (ReservedTypes type : ChampRelationship.ReservedTypes.values()) {
+ assertTrue(ChampRelationship.ReservedTypes.valueOf(type.name()) == type);
+ }
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/core/ChampSchemaTest.java b/src/test/java/org/openecomp/aai/champ/core/ChampSchemaTest.java
new file mode 100644
index 0000000..e3f099e
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/core/ChampSchemaTest.java
@@ -0,0 +1,761 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.core;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.model.ChampConnectionConstraint;
+import org.openecomp.aai.champ.model.ChampConnectionMultiplicity;
+import org.openecomp.aai.champ.model.ChampField;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObject.ReservedTypes;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampPropertyConstraint;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.openecomp.aai.champ.schema.AlwaysValidChampSchemaEnforcer;
+import org.openecomp.aai.champ.schema.ChampSchemaEnforcer;
+import org.openecomp.aai.champ.schema.DefaultChampSchemaEnforcer;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class ChampSchemaTest extends BaseChampAPITest {
+
+ @Test
+ public void runTest() {
+ for (ChampGraph.Type apiType : ChampGraph.Type.values()) {
+ final String graphName = ChampSchemaTest.class.getSimpleName();
+
+ switch (apiType) {
+ case IN_MEMORY:
+ break;
+ case TITAN:
+ cleanUp(graphName);
+ break;
+ default:
+ break;
+ }
+
+ final ChampAPI api = ChampAPI.Factory.newInstance(apiType);
+
+ try {
+ ChampSchemaTest.testChampSchemaCrud(api.getGraph(graphName));
+ } catch (Throwable t) {
+ throw new AssertionError(apiType + " unit test failed", t);
+ }
+
+ api.shutdown();
+ }
+ }
+
+ public static void testChampSchemaCrud(ChampGraph graph) {
+
+ final ChampSchema schema = ChampSchema.create()
+ .withObjectConstraint()
+ .onType("foo")
+ .withPropertyConstraint()
+ .onField("property1")
+ .required()
+ .build()
+ .withPropertyConstraint()
+ .onField("property2")
+ .optional()
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("bar")
+ .withPropertyConstraint()
+ .onField("at")
+ .ofType(ChampField.Type.STRING)
+ .optional()
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("foo")
+ .targetedToAny()
+ .build()
+ .build()
+ .build();
+
+ try {
+ graph.storeSchema(schema);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+
+ final ChampObject emptyFoo = ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .build();
+
+ try {
+ graph.storeObject(emptyFoo);
+ } catch (ChampMarshallingException e1) {
+ throw new AssertionError(e1);
+ } catch (ChampSchemaViolationException e1) {
+ //Expected, since it does not have the required property "property1"
+ } catch (ChampObjectNotExistsException e) {
+ throw new AssertionError(e);
+ }
+
+ final ChampSchema retrievedSchema = graph.retrieveSchema();
+
+ if (!schema.equals(retrievedSchema)) throw new AssertionError("Retrieved schema is not the same as the schema that was previously stored");
+
+ try {
+ graph.updateSchema(new ChampRelationshipConstraint.Builder("bard").build());
+ assertTrue(graph.retrieveSchema().getRelationshipConstraint("bard").isPresent());
+
+ graph.updateSchema(new ChampObjectConstraint.Builder("baz").build());
+ assertTrue(graph.retrieveSchema().getObjectConstraint("baz").isPresent());
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+
+ final ChampSchema updatedSchema = graph.retrieveSchema();
+
+ if (!updatedSchema.getObjectConstraint("baz").isPresent()) throw new AssertionError("Updated schema and retrieved, but retrieved schema did not contain updates");
+ if (!updatedSchema.getRelationshipConstraint("bard").isPresent()) throw new AssertionError("Updated schema and retrieved, but retrieved schema did not contain updates");
+
+ try {
+ graph.updateSchema(new ChampObjectConstraint.Builder("foo")
+ .constraint(
+ new ChampPropertyConstraint.Builder(
+ new ChampField.Builder("property2")
+ .build()
+ )
+ .required(false)
+ .build()
+ )
+ .build());
+
+ final ChampObject storedEmptyFoo = graph.storeObject(emptyFoo);
+
+ graph.deleteObject(storedEmptyFoo.getKey().get());
+ } catch (ChampMarshallingException e) {
+ throw new AssertionError(e);
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ } catch (ChampObjectNotExistsException e) {
+ throw new AssertionError(e);
+ }
+
+ graph.deleteSchema();
+ assertTrue(graph.retrieveSchema().equals(ChampSchema.emptySchema()));
+ }
+
+ @Test
+ public void testChampSchemaFluentApi() {
+ final ChampSchema schema = ChampSchema.create()
+ .withObjectConstraint()
+ .onType("foo")
+ .withPropertyConstraint()
+ .onField("bar")
+ .ofType(ChampField.Type.STRING)
+ .required()
+ .build()
+ .withPropertyConstraint()
+ .onField("baz")
+ .ofType(ChampField.Type.BOOLEAN)
+ .optional()
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("eats")
+ .withPropertyConstraint()
+ .onField("at")
+ .ofType(ChampField.Type.STRING)
+ .required()
+ .build()
+ .withPropertyConstraint()
+ .onField("for")
+ .optional()
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("foo")
+ .targetedTo("foo")
+ .withMultiplicity(ChampConnectionMultiplicity.ONE)
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("bar")
+ .targetedTo("bar")
+ .build()
+ .build()
+ .build();
+
+ assertTrue(schema.getObjectConstraint("foo").get().getType().equals("foo"));
+
+ for (ChampPropertyConstraint propConst : schema.getObjectConstraint("foo").get().getPropertyConstraints()) {
+ if (propConst.getField().getName().equals("bar")) {
+ assertTrue(propConst.getField().getJavaType().equals(String.class));
+ assertTrue(propConst.isRequired());
+ } else if (propConst.getField().getName().equals("baz")) {
+ assertTrue(propConst.getField().getJavaType().equals(Boolean.class));
+ assertTrue(!propConst.isRequired());
+ } else {
+ throw new AssertionError("Unknown property constraint found: " + propConst);
+ }
+ }
+
+ assertTrue(schema.getRelationshipConstraint("eats").get().getType().equals("eats"));
+
+ for (ChampPropertyConstraint propConst : schema.getRelationshipConstraint("eats").get().getPropertyConstraints()) {
+ if (propConst.getField().getName().equals("at")) {
+ assertTrue(propConst.getField().getJavaType().equals(String.class));
+ assertTrue(propConst.isRequired());
+ } else if (propConst.getField().getName().equals("for")) {
+ assertTrue(propConst.getField().getJavaType().equals(String.class));
+ assertTrue(!propConst.isRequired());
+ } else {
+ throw new AssertionError("Unknown property constraint found: " + propConst);
+ }
+ }
+
+ for (ChampConnectionConstraint connConst : schema.getRelationshipConstraint("eats").get().getConnectionConstraints()) {
+ if (connConst.getSourceType().equals("foo")) {
+ assertTrue(connConst.getTargetType().equals("foo"));
+ assertTrue(connConst.getMultiplicity() == ChampConnectionMultiplicity.ONE);
+ } else if (connConst.getSourceType().equals("bar")) {
+ assertTrue(connConst.getTargetType().equals("bar"));
+ assertTrue(connConst.getMultiplicity() == ChampConnectionMultiplicity.MANY);
+ } else {
+ throw new AssertionError("Unknown connection constraint found: " + connConst);
+ }
+ }
+ }
+
+ @Test
+ public void testDefaultChampSchemaEnforcer() {
+
+ final ChampSchemaEnforcer schemaEnforcer = new DefaultChampSchemaEnforcer();
+ final ChampSchema champSchema = ChampSchema.create()
+ .withObjectConstraint()
+ .onType("foo")
+ .withPropertyConstraint()
+ .onField("bar")
+ .ofType(ChampField.Type.STRING)
+ .required()
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("makes")
+ .withPropertyConstraint()
+ .onField("bar")
+ .required()
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("foo")
+ .targetedTo("fiz")
+ .withMultiplicity(ChampConnectionMultiplicity.ONE)
+ .build()
+ .build()
+ .build();
+
+ try {
+ schemaEnforcer.validate(ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", "true")
+ .build(),
+ champSchema.getObjectConstraint("foo").get());
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+
+ try {
+ schemaEnforcer.validate(ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .build(),
+ champSchema.getObjectConstraint("foo").get());
+ throw new AssertionError("Failed to enforce required property constraint on object");
+ } catch (ChampSchemaViolationException e) {
+ //Expected
+ }
+
+ try {
+ schemaEnforcer.validate(ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", true)
+ .build(),
+ champSchema.getObjectConstraint("foo").get());
+ throw new AssertionError("Failed to enforce property type constraint on object");
+ } catch (ChampSchemaViolationException e) {
+ //Expected
+ }
+
+ try {
+ schemaEnforcer.validate(ChampRelationship.create()
+ .ofType("makes")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .build()
+ .withTarget()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .withProperty("bar", "true")
+ .build(),
+ champSchema.getRelationshipConstraint("makes").get()
+ );
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+
+ try {
+ schemaEnforcer.validate(ChampRelationship.create()
+ .ofType("makes")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .build()
+ .withTarget()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .build(),
+ champSchema.getRelationshipConstraint("makes").get()
+ );
+ throw new AssertionError("Failed to enforce required property constraint on relationship");
+ } catch (ChampSchemaViolationException e) {
+ //Expected
+ }
+
+ try {
+ schemaEnforcer.validate(ChampPartition.create()
+ .withObject(
+ ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", "true")
+ .build()
+ )
+ .withObject(
+ ChampObject.create()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ )
+ .withRelationship(
+ ChampRelationship.create()
+ .ofType("makes")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", "true")
+ .build()
+ .withTarget()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .withProperty("bar", "true")
+ .build()
+ )
+ .withRelationship(
+ ChampRelationship.create()
+ .ofType("makes")
+ .withoutKey()
+ .withSource()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .withTarget()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", "true")
+ .build()
+ .withProperty("bar", "true")
+ .build()
+ )
+ .build(),
+ champSchema
+ );
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+
+ try {
+ schemaEnforcer.validate(ChampPartition.create()
+ .withObject(
+ ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", "true")
+ .build()
+ )
+ .withObject(
+ ChampObject.create()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ )
+ .withRelationship(
+ ChampRelationship.create()
+ .ofType("makes")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", "true")
+ .build()
+ .withTarget()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .withProperty("bar", "true")
+ .build()
+ )
+ .withRelationship(
+ ChampRelationship.create()
+ .ofType("makes")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", "true")
+ .build()
+ .withTarget()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .withProperty("bar", "true")
+ .build()
+ )
+ .build(),
+ champSchema
+ );
+ throw new AssertionError("Failed to enforce connection constraint on relationship type 'makes'");
+ } catch (ChampSchemaViolationException e) {
+ //Expected
+ }
+ }
+
+ @Test
+ public void testAlwaysValidChampSchemaEnforcer() {
+
+ final ChampSchemaEnforcer schemaEnforcer = new AlwaysValidChampSchemaEnforcer();
+
+ try {
+ schemaEnforcer.validate(ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", true)
+ .build(),
+ new ChampObjectConstraint.Builder("foo")
+ .constraint(
+ new ChampPropertyConstraint.Builder(
+ new ChampField.Builder("bar")
+ .type(ChampField.Type.STRING)
+ .build()
+ )
+ .required(true)
+ .build()
+ )
+ .build()
+ );
+
+ schemaEnforcer.validate(ChampRelationship.create()
+ .ofType("foo")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .build()
+ .withTarget()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .withProperty("bar", true)
+ .build(),
+ new ChampRelationshipConstraint.Builder("bar")
+ .constraint(
+ new ChampPropertyConstraint.Builder(
+ new ChampField.Builder("bar")
+ .type(ChampField.Type.STRING)
+ .build()
+ )
+ .required(true)
+ .build()
+ )
+ .build()
+ );
+
+ schemaEnforcer.validate(ChampPartition.create()
+ .withObject(
+ ChampObject.create()
+ .ofType("foo")
+ .withoutKey()
+ .withProperty("bar", true)
+ .build()
+ )
+ .withObject(
+ ChampObject.create()
+ .ofType("fiz")
+ .withoutKey()
+ .withProperty("bar", true)
+ .build()
+ )
+ .withRelationship(
+ ChampRelationship.create()
+ .ofType("makes")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .build()
+ .withTarget()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .build()
+ )
+ .withRelationship(
+ ChampRelationship.create()
+ .ofType("makes")
+ .withoutKey()
+ .withSource()
+ .ofType("foo")
+ .withoutKey()
+ .build()
+ .withTarget()
+ .ofType("fiz")
+ .withoutKey()
+ .build()
+ .withProperty("bar", true)
+ .build()
+ )
+ .build(),
+ ChampSchema.create()
+ .withObjectConstraint()
+ .onType("foo")
+ .withPropertyConstraint()
+ .onField("bar")
+ .required()
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("makes")
+ .withPropertyConstraint()
+ .onField("bar")
+ .required()
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("foo")
+ .targetedTo("fiz")
+ .withMultiplicity(ChampConnectionMultiplicity.ONE)
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("uses")
+ .withConnectionConstraint()
+ .sourcedFromAny()
+ .targetedTo("computer")
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("destroys")
+ .withConnectionConstraint()
+ .sourcedFrom("computer")
+ .targetedToAny()
+ .build()
+ .build()
+ .build()
+
+ );
+ } catch (ChampSchemaViolationException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Test
+ public void testFluentSchemaApi() {
+ final ChampSchema schema = ChampSchema.create()
+ .withObjectConstraint()
+ .onType("a")
+ .withPropertyConstraint()
+ .onField("z")
+ .ofType(ChampField.Type.STRING)
+ .optional()
+ .build()
+ .build()
+ .withObjectConstraint()
+ .onType("b")
+ .withPropertyConstraint()
+ .onField("y")
+ .ofType(ChampField.Type.LONG)
+ .required()
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("one")
+ .withPropertyConstraint()
+ .onField("nine")
+ .ofType(ChampField.Type.INTEGER)
+ .optional()
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("a")
+ .targetedTo("b")
+ .withMultiplicity(ChampConnectionMultiplicity.NONE)
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("a")
+ .targetedToAny()
+ .withMultiplicity(ChampConnectionMultiplicity.ONE)
+ .build()
+ .withConnectionConstraint()
+ .sourcedFromAny()
+ .targetedTo("b")
+ .withMultiplicity(ChampConnectionMultiplicity.MANY)
+ .build()
+ .withConnectionConstraint()
+ .sourcedFromAny()
+ .targetedToAny()
+ .withMultiplicity(ChampConnectionMultiplicity.MANY)
+ .build()
+ .build()
+ .build();
+
+ final ChampObjectConstraint aObjConstraint = schema.getObjectConstraint("a").get();
+
+ assertTrue(aObjConstraint.getType().equals("a"));
+
+ final ChampPropertyConstraint zPropertyConstraint = aObjConstraint.getPropertyConstraint("z").get();
+
+ assertTrue(zPropertyConstraint.getField().getName().equals("z"));
+ assertTrue(zPropertyConstraint.getField().getJavaType().equals(String.class));
+ assertTrue(!zPropertyConstraint.isRequired());
+
+ final ChampObjectConstraint bObjConstraint = schema.getObjectConstraint("b").get();
+
+ assertTrue(bObjConstraint.getType().equals("b"));
+
+ final ChampPropertyConstraint yPropertyConstraint = bObjConstraint.getPropertyConstraint("y").get();
+
+ assertTrue(yPropertyConstraint.getField().getName().equals("y"));
+ assertTrue(yPropertyConstraint.getField().getJavaType().equals(Long.class));
+ assertTrue(yPropertyConstraint.isRequired());
+
+ final ChampRelationshipConstraint oneRelConstraint = schema.getRelationshipConstraint("one").get();
+
+ assertTrue(oneRelConstraint.getType().equals("one"));
+
+ final ChampPropertyConstraint ninePropertyConstraint = oneRelConstraint.getPropertyConstraint("nine").get();
+
+ assertTrue(ninePropertyConstraint.getField().getName().equals("nine"));
+ assertTrue(ninePropertyConstraint.getField().getJavaType().equals(Integer.class));
+ assertTrue(!ninePropertyConstraint.isRequired());
+
+ final Set<ChampConnectionConstraint> connectionConstraints = oneRelConstraint.getConnectionConstraints();
+
+ for (ChampConnectionConstraint cc : connectionConstraints) {
+ if (cc.getSourceType().equals("a") && cc.getTargetType().equals("b")) {
+ assertTrue(cc.getMultiplicity() == ChampConnectionMultiplicity.NONE);
+ } else if (cc.getSourceType().equals(ReservedTypes.ANY.toString()) && cc.getTargetType().equals("b")) {
+ assertTrue(cc.getMultiplicity() == ChampConnectionMultiplicity.MANY);
+ } else if (cc.getSourceType().equals(ReservedTypes.ANY.toString()) && cc.getTargetType().equals(ReservedTypes.ANY.toString())) {
+ assertTrue(cc.getMultiplicity() == ChampConnectionMultiplicity.MANY);
+ } else if (cc.getSourceType().equals("a") && cc.getTargetType().equals(ReservedTypes.ANY.toString())) {
+ assertTrue(cc.getMultiplicity() == ChampConnectionMultiplicity.ONE);
+ } else {
+ throw new AssertionError("Found unspecified connection constraint " + cc);
+ }
+ }
+ }
+
+ @Test
+ public void testJacksonObjectMapping() {
+ final ChampSchema schema = ChampSchema.create()
+ .withObjectConstraint()
+ .onType("a")
+ .withPropertyConstraint()
+ .onField("z")
+ .ofType(ChampField.Type.STRING)
+ .optional()
+ .build()
+ .build()
+ .withObjectConstraint()
+ .onType("b")
+ .withPropertyConstraint()
+ .onField("y")
+ .ofType(ChampField.Type.LONG)
+ .required()
+ .build()
+ .build()
+ .withRelationshipConstraint()
+ .onType("one")
+ .withPropertyConstraint()
+ .onField("nine")
+ .ofType(ChampField.Type.INTEGER)
+ .optional()
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("a")
+ .targetedTo("b")
+ .withMultiplicity(ChampConnectionMultiplicity.NONE)
+ .build()
+ .withConnectionConstraint()
+ .sourcedFrom("a")
+ .targetedToAny()
+ .withMultiplicity(ChampConnectionMultiplicity.ONE)
+ .build()
+ .withConnectionConstraint()
+ .sourcedFromAny()
+ .targetedTo("b")
+ .withMultiplicity(ChampConnectionMultiplicity.MANY)
+ .build()
+ .withConnectionConstraint()
+ .sourcedFromAny()
+ .targetedToAny()
+ .withMultiplicity(ChampConnectionMultiplicity.MANY)
+ .build()
+ .build()
+ .build();
+
+ final ObjectMapper om = new ObjectMapper();
+
+ try {
+ final byte[] serialized = om.writeValueAsBytes(schema);
+ System.out.println(new String(serialized, "UTF-8"));
+ final ChampSchema deserialized = om.readValue(serialized, ChampSchema.class);
+ assert schema.equals(deserialized);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraphTest.java b/src/test/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraphTest.java
new file mode 100644
index 0000000..5383d28
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/event/AbstractLoggingChampGraphTest.java
@@ -0,0 +1,1029 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.event;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampCapabilities;
+import org.openecomp.aai.champ.exceptions.ChampIndexNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampMarshallingException;
+import org.openecomp.aai.champ.exceptions.ChampObjectNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampRelationshipNotExistsException;
+import org.openecomp.aai.champ.exceptions.ChampSchemaViolationException;
+import org.openecomp.aai.champ.exceptions.ChampUnmarshallingException;
+import org.openecomp.aai.champ.model.ChampObject;
+import org.openecomp.aai.champ.model.ChampObjectConstraint;
+import org.openecomp.aai.champ.model.ChampObjectIndex;
+import org.openecomp.aai.champ.model.ChampPartition;
+import org.openecomp.aai.champ.model.ChampRelationship;
+import org.openecomp.aai.champ.model.ChampRelationshipConstraint;
+import org.openecomp.aai.champ.model.ChampRelationshipIndex;
+import org.openecomp.aai.champ.model.ChampSchema;
+import org.slf4j.Logger;
+
+import com.att.nsa.cambria.client.CambriaPublisher;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+
+public class AbstractLoggingChampGraphTest {
+
+ /** Event stream producer stub. */
+ private InMemoryPublisher producer;
+
+ /** In memory graph for testing purposes. */
+ private TestGraph testGraph;
+
+
+ /**
+ * Perform any setup tasks that need to be done prior to each test.
+ */
+ @Before
+ public void setup() {
+
+ // Instantiate an event stream producer stub to use in our tests.
+ producer = new InMemoryPublisher();
+
+ // Instantiate an 'in-memory' graph for test purposes.
+ Map<String, Object> graphProperties = new HashMap<String, Object>();
+ graphProperties.put("champ.event.stream.hosts", "myeventstreamhost");
+ graphProperties.put("champ.event.stream.batch-size", 1);
+ testGraph = new TestGraph(graphProperties, producer);
+ }
+
+
+ /**
+ * Perform any cleanup that needs to be done after each test.
+ */
+ @After
+ public void tearDown() {
+
+ // Close our stubbed producer and graph.
+ producer.close();
+ testGraph.shutdown();
+ }
+
+
+ /**
+ * Validates that store/replace/delete operation against vertices result in the expected events
+ * being published to the event stream.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ */
+ @Test
+ public void vertexOperationsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException {
+
+ // Create a vertex and store it in the graph data store.
+ ChampObject obj1 = ChampObject.create()
+ .ofType("foo")
+ .withKey("123")
+ .withProperty("p1", "v1")
+ .withProperty("p2", "v2")
+ .build();
+ testGraph.storeObject(obj1);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+ assertTrue("Entity type for store event was not a vertex.", loggedEventStr.contains("vertex"));
+
+ // Create a new vertex based on the one that we already created.
+ ChampObject obj2 = ChampObject.create()
+ .from(obj1)
+ .withKey("123")
+ .withProperty("p3", "v3")
+ .build();
+
+ // Now, try doing a replace operation.
+ testGraph.replaceObject(obj2);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected REPLACE event.", loggedEventStr.contains("REPLACE"));
+ assertTrue("Entity type for store event was not a vertex.", loggedEventStr.contains("vertex"));
+
+ // Finally, delete the vertex.
+ testGraph.deleteObject("123");
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+ assertTrue("Entity type for store event was not a vertex.", loggedEventStr.contains("vertex"));
+ }
+
+
+ /**
+ * This test validates that performing vertex operations in the case where the data to be
+ * forwarded to the event stream is unavailable results in no event being generated, but
+ * does not otherwise create issues.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ */
+ @Test
+ public void vertexOperationsWithNullsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException {
+
+ // Setup our test graph to simulate failures to retrieve data from the graph data store.
+ testGraph.returnNulls();
+
+ // Create a vertex and store it in the graph data store.
+ ChampObject obj1 = ChampObject.create()
+ .ofType("foo")
+ .withKey("123")
+ .withProperty("p1", "v1")
+ .withProperty("p2", "v2")
+ .build();
+ testGraph.storeObject(obj1);
+
+ // Check our simulated event stream to verify that an event log was produced.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Validate that we did not get an event from the stream.
+ assertNull("Store vertex event should not have been logged to the event stream", loggedEventStr);
+
+ // Create a new vertex based on the one that we already created.
+ ChampObject obj2 = ChampObject.create()
+ .from(obj1)
+ .withKey("123")
+ .withProperty("p3", "v3")
+ .build();
+
+ // Now, try doing a replace operation.
+ testGraph.replaceObject(obj2);
+
+ // Check our simulated event stream to see if an event log was not produced.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Validate that we did not get an event from the stream.
+ assertNull("Store vertex event should not have been logged to the event stream", loggedEventStr);
+
+ // Finally, delete the vertex.
+ testGraph.deleteObject("123");
+
+ // Check our simulated event stream to see if an event log was not produced.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Validate that we did not get an event from the stream.
+ assertNull("Store vertex event should not have been logged to the event stream", loggedEventStr);
+ }
+
+
+ /**
+ * Validates that store/replace/delete operation against edges result in the expected events
+ * being published to the event stream.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ */
+ @Test
+ public void edgeOperationsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException,
+ ChampUnmarshallingException,
+ ChampRelationshipNotExistsException {
+
+ // Create two vertices to act as the end points of our edge.
+ ChampObject obj1 = ChampObject.create()
+ .ofType("foo")
+ .withKey("123")
+ .withProperty("p1", "v1")
+ .withProperty("p2", "v2")
+ .build();
+
+ ChampObject obj2 = ChampObject.create()
+ .ofType("bar")
+ .withKey("123")
+ .withProperty("p3", "v3")
+ .build();
+
+ // Now, create an edge object and write it to the graph data store.
+ ChampRelationship rel = new ChampRelationship.Builder(obj1, obj2, "relationship")
+ .property("property-1", "value-1")
+ .property("property-2", "value-2")
+ .build();
+ testGraph.storeRelationship(rel);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+ assertTrue("Entity type for store event was not an edge.", loggedEventStr.contains("relationship"));
+
+ // Now, create another edge object based on the one we just wrote, and use it to perform
+ // a replace operation.
+ ChampRelationship rel2 = ChampRelationship.create()
+ .from(rel)
+ .withKey("123")
+ .withProperty("property-3", "value-3")
+ .build();
+ testGraph.replaceRelationship(rel2);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected REPLACE event.", loggedEventStr.contains("REPLACE"));
+ assertTrue("Entity type for store event was not an edge.", loggedEventStr.contains("relationship"));
+
+ // Finally, delete our edge.
+ testGraph.deleteRelationship(rel2);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+ assertTrue("Entity type for store event was not an edge.", loggedEventStr.contains("relationship"));
+ }
+
+
+ /**
+ * This test validates that performing edge operations in the case where the data to be
+ * forwarded to the event stream is unavailable results in no event being generated, but
+ * does not otherwise create issues.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ */
+ @Test
+ public void edgeOperationsWithNullsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException,
+ ChampUnmarshallingException,
+ ChampRelationshipNotExistsException {
+
+ // Set up our graph to simulate a failure to retrieve some of the data we need to generate
+ // events.
+ testGraph.returnNulls();
+
+ // Create two vertices to act as the endpoints of our edge.
+ ChampObject obj1 = ChampObject.create()
+ .ofType("foo")
+ .withKey("123")
+ .withProperty("p1", "v1")
+ .withProperty("p2", "v2")
+ .build();
+
+ ChampObject obj2 = ChampObject.create()
+ .ofType("bar")
+ .withKey("123")
+ .withProperty("p3", "v3")
+ .build();
+
+ // Now, create an edge object and write it to the graph data store.
+ ChampRelationship rel = new ChampRelationship.Builder(obj1, obj2, "relationship")
+ .property("property-1", "value-1")
+ .property("property-2", "value-2")
+ .build();
+ testGraph.storeRelationship(rel);
+
+ // Check our simulated event stream to see if an event log was produced.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Validate that we did not get an event from the stream.
+ assertNull("Store edge event should not have been logged to the event stream", loggedEventStr);
+
+ // Now, create another edge object based on the one we just wrote, and use it to perform
+ // a replace operation.
+ ChampRelationship rel2 = ChampRelationship.create()
+ .from(rel)
+ .withKey("123")
+ .withProperty("property-3", "value-3")
+ .build();
+ testGraph.replaceRelationship(rel2);
+
+ // Check our simulated event stream to see if an event log was produced.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Validate that we did not get an event from the stream.
+ assertNull("Store edge event should not have been logged to the event stream", loggedEventStr);
+ }
+
+
+ /**
+ * Validates that store/replace/delete operation against partitions result in the expected events
+ * being published to the event stream.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ */
+ @Test
+ public void partitionOperationsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException,
+ ChampUnmarshallingException,
+ ChampRelationshipNotExistsException {
+
+ // Create the vertices and edge objects that we need to create a partition.
+ ChampObject obj1 = ChampObject.create()
+ .ofType("foo")
+ .withKey("123")
+ .withProperty("p1", "v1")
+ .withProperty("p2", "v2")
+ .build();
+
+ ChampObject obj2 = ChampObject.create()
+ .ofType("bar")
+ .withKey("123")
+ .withProperty("p3", "v3")
+ .build();
+
+ // Now, create an edge object and write it to the graph data store.
+ ChampRelationship rel = new ChampRelationship.Builder(obj1, obj2, "relationship")
+ .property("property-1", "value-1")
+ .property("property-2", "value-2")
+ .build();
+
+ // Now, create our partition object and store it in the graph.
+ ChampPartition partition = ChampPartition.create()
+ .withObject(obj1)
+ .withObject(obj2)
+ .withRelationship(rel)
+ .build();
+ testGraph.storePartition(partition);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+ assertTrue("Entity type for store event was not a partition.", loggedEventStr.contains("partition"));
+
+ // Now, delete our partition.
+ testGraph.deletePartition(partition);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+ assertTrue("Entity type for store event was not a partition.", loggedEventStr.contains("partition"));
+ }
+
+
+ /**
+ * This test validates that performing partition operations in the case where the data to be
+ * forwarded to the event stream is unavailable results in no event being generated, but
+ * does not otherwise create issues.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ */
+ @Test
+ public void partitionOperationsWithNullsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException,
+ ChampUnmarshallingException,
+ ChampRelationshipNotExistsException {
+
+ // Set up our graph to simulate a failure to retrieve some of the data we need to generate
+ // events.
+ testGraph.returnNulls();
+
+ // Create all of the objects we need to create a partition, and store the partition
+ // in the graph.
+ ChampObject obj1 = ChampObject.create()
+ .ofType("foo")
+ .withKey("123")
+ .withProperty("p1", "v1")
+ .withProperty("p2", "v2")
+ .build();
+
+ ChampObject obj2 = ChampObject.create()
+ .ofType("bar")
+ .withKey("123")
+ .withProperty("p3", "v3")
+ .build();
+
+ ChampRelationship rel = new ChampRelationship.Builder(obj1, obj2, "relationship")
+ .property("property-1", "value-1")
+ .property("property-2", "value-2")
+ .build();
+
+ ChampPartition partition = ChampPartition.create()
+ .withObject(obj1)
+ .withObject(obj2)
+ .withRelationship(rel)
+ .build();
+ testGraph.storePartition(partition);
+
+ // Check our simulated event stream to see if an an event log was produced.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Validate that we did not get an event from the stream.
+ assertNull("Store partition event should not have been logged to the event stream", loggedEventStr);
+ }
+
+
+ /**
+ * Validates that store/replace/delete operation against vertex indexes result in the expected
+ * events being published to the event stream.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ * @throws ChampIndexNotExistsException
+ */
+ @Test
+ public void indexOperationsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException,
+ ChampUnmarshallingException,
+ ChampRelationshipNotExistsException,
+ ChampIndexNotExistsException {
+
+ // Create an index object and store it in the graph.
+ ChampObjectIndex objIndex = ChampObjectIndex.create()
+ .ofName("myIndex")
+ .onType("type")
+ .forField("myField")
+ .build();
+ testGraph.storeObjectIndex(objIndex);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+ assertTrue("Entity type for store event was not a vertex index.", loggedEventStr.contains("objectIndex"));
+
+ // Now, delete our partition.
+ testGraph.deleteObjectIndex("myIndex");
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+ assertTrue("Entity type for store event was not a vertex index.", loggedEventStr.contains("objectIndex"));
+ }
+
+ /**
+ * This test validates that performing index operations in the case where the data to be
+ * forwarded to the event stream is unavailable results in no event being generated, but
+ * does not otherwise create issues.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ * @throws ChampIndexNotExistsException
+ */
+ @Test
+ public void indexOperationsWithNullsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException,
+ ChampUnmarshallingException,
+ ChampRelationshipNotExistsException,
+ ChampIndexNotExistsException {
+
+ // Set up our graph to simulate a failure to retrieve some of the data we need to generate
+ // events.
+ testGraph.returnNulls();
+
+ // Create an index object and store it in the graph.
+ ChampObjectIndex objIndex = ChampObjectIndex.create()
+ .ofName("myIndex")
+ .onType("type")
+ .forField("myField")
+ .build();
+ testGraph.storeObjectIndex(objIndex);
+
+ // Check our simulated event stream to see if an an event log was produced.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Now, delete our index.
+ testGraph.deleteObjectIndex("myIndex");
+
+ // Check our simulated event stream to see if an an event log was produced.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Validate that we did not get an event from the stream.
+ assertNull("Delete partition event should not have been logged to the event stream", loggedEventStr);
+ }
+
+
+ /**
+ * This test validates that performing relationship index operations in the case where
+ * the data to be forwarded to the event stream is unavailable results in no event being
+ * generated, but does not otherwise create issues.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ * @throws ChampIndexNotExistsException
+ */
+ @Test
+ public void relationshipIndexOperationsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException,
+ ChampUnmarshallingException,
+ ChampRelationshipNotExistsException,
+ ChampIndexNotExistsException {
+
+ // Create a relationship index object and store it in the graph.
+ ChampRelationshipIndex relIndex = ChampRelationshipIndex.create()
+ .ofName("myIndex")
+ .onType("type")
+ .forField("myField")
+ .build();
+ testGraph.storeRelationshipIndex(relIndex);
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected STORE event.", loggedEventStr.contains("STORE"));
+ assertTrue("Entity type for store event was not a relationship index.", loggedEventStr.contains("relationshipIndex"));
+
+ // Now, delete our partition.
+ testGraph.deleteRelationshipIndex("myIndex");
+
+ // Retrieve the next event from the event stream and validate that it is what we expect.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+ assertTrue("Expected DELETE event.", loggedEventStr.contains("DELETE"));
+ assertTrue("Entity type for store event was not a relationship index.", loggedEventStr.contains("relationshipIndex"));
+ }
+
+
+ /**
+ * This test validates that performing index operations in the case where the data to be
+ * forwarded to the event stream is unavailable results in no event being generated, but
+ * does not otherwise create issues.
+ *
+ * @throws ChampMarshallingException
+ * @throws ChampSchemaViolationException
+ * @throws ChampObjectNotExistsException
+ * @throws InterruptedException
+ * @throws JsonParseException
+ * @throws JsonMappingException
+ * @throws IOException
+ * @throws ChampUnmarshallingException
+ * @throws ChampRelationshipNotExistsException
+ * @throws ChampIndexNotExistsException
+ */
+ @Test
+ public void relationshipIndexOperationsWithNullsTest() throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException,
+ InterruptedException,
+ JsonParseException,
+ JsonMappingException,
+ IOException,
+ ChampUnmarshallingException,
+ ChampRelationshipNotExistsException,
+ ChampIndexNotExistsException {
+
+ // Set up our graph to simulate a failure to retrieve some of the data we need to generate
+ // events.
+ testGraph.returnNulls();
+
+ // Create a relationship index object and store it in the graph.
+ ChampRelationshipIndex relIndex = ChampRelationshipIndex.create()
+ .ofName("myIndex")
+ .onType("type")
+ .forField("myField")
+ .build();
+
+ testGraph.storeRelationshipIndex(relIndex);
+
+ // Check our simulated event stream to see if an an event log was produced.
+ String loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Now, delete our index.
+ testGraph.deleteRelationshipIndex("myIndex");
+
+ // Check our simulated event stream to see if an event log was produced.
+ loggedEventStr = producer.eventStream.poll(5000, TimeUnit.MILLISECONDS);
+
+ // Validate that we did not get an event from the stream.
+ assertNull("Delete partition event should not have been logged to the event stream", loggedEventStr);
+ }
+
+
+ /**
+ * This is a simple graph stub that extends our {@link AbstractLoggingChampGraph} class which
+ * we can use to validate that log events get generated without worrying about having a real
+ * underlying graph.
+ */
+ private class TestGraph extends AbstractLoggingChampGraph {
+
+ /** If set, this causes simulated retrieve operations to fail. */
+ private boolean returnNulls = false;
+
+
+ protected TestGraph(Map<String, Object> properties, CambriaPublisher producer) {
+ super(properties);
+
+ setProducer(producer);
+ }
+
+ public void returnNulls() {
+ returnNulls = true;
+ }
+
+ @Override
+ public void shutdown() {
+ if(returnNulls) {
+ publisherPool = null;
+ }
+ super.shutdown();
+ }
+
+ @Override
+ public ChampObject executeStoreObject(ChampObject object) throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException {
+ if(!returnNulls) {
+ return object;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public ChampObject executeReplaceObject(ChampObject object) throws ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampObjectNotExistsException {
+ if(!returnNulls) {
+ return object;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Optional<ChampObject> retrieveObject(Object key) throws ChampUnmarshallingException {
+
+ if(!returnNulls) {
+ return(Optional.of(ChampObject.create()
+ .ofType("foo")
+ .withKey(key)
+ .build()));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public void executeDeleteObject(Object key) throws ChampObjectNotExistsException {
+
+ }
+
+ @Override
+ public Stream<ChampObject> queryObjects(Map<String, Object> queryParams) {
+ // Not used by any tests.
+ return null;
+ }
+
+ @Override
+ public ChampRelationship executeStoreRelationship(ChampRelationship relationship)
+ throws ChampUnmarshallingException,
+ ChampMarshallingException,
+ ChampObjectNotExistsException,
+ ChampSchemaViolationException,
+ ChampRelationshipNotExistsException {
+
+ if(!returnNulls) {
+ return relationship;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public ChampRelationship executeReplaceRelationship(ChampRelationship relationship)
+ throws ChampUnmarshallingException,
+ ChampMarshallingException,
+ ChampSchemaViolationException,
+ ChampRelationshipNotExistsException {
+
+ if(!returnNulls) {
+ return relationship;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Optional<ChampRelationship> retrieveRelationship(Object key) throws ChampUnmarshallingException {
+ // Not used by any tests.
+ return null;
+ }
+
+ @Override
+ public void executeDeleteRelationship(ChampRelationship relationship) throws ChampRelationshipNotExistsException {
+ // Not used by any tests.
+ }
+
+ @Override
+ public Stream<ChampRelationship> retrieveRelationships(ChampObject object)
+ throws ChampUnmarshallingException, ChampObjectNotExistsException {
+
+ // Not used by any tests.
+ return null;
+ }
+
+ @Override
+ public Stream<ChampRelationship> queryRelationships(Map<String, Object> queryParams) {
+
+ // Not used by any tests.
+ return null;
+ }
+
+ @Override
+ public ChampPartition executeStorePartition(ChampPartition partition)
+ throws ChampSchemaViolationException,
+ ChampRelationshipNotExistsException,
+ ChampMarshallingException,
+ ChampObjectNotExistsException {
+
+ if(!returnNulls) {
+ return partition;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void executeDeletePartition(ChampPartition graph) {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void executeStoreObjectIndex(ChampObjectIndex index) {
+ // Not used by any tests.
+ }
+
+ @Override
+ public Optional<ChampObjectIndex> retrieveObjectIndex(String indexName) {
+
+ if(!returnNulls) {
+ return Optional.of(ChampObjectIndex.create()
+ .ofName(indexName)
+ .onType("doesnt matter")
+ .forField("doesnt matter")
+ .build());
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Stream<ChampObjectIndex> retrieveObjectIndices() {
+ // Not used by any tests.
+ return null;
+ }
+
+ @Override
+ public void executeDeleteObjectIndex(String indexName) throws ChampIndexNotExistsException {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void executeStoreRelationshipIndex(ChampRelationshipIndex index) {
+ // Not used by any tests.
+ }
+
+ @Override
+ public Optional<ChampRelationshipIndex> retrieveRelationshipIndex(String indexName) {
+ if(!returnNulls) {
+ return Optional.of(ChampRelationshipIndex.create()
+ .ofName(indexName)
+ .onType("doesnt matter")
+ .forField("doesnt matter")
+ .build());
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public Stream<ChampRelationshipIndex> retrieveRelationshipIndices() {
+ // Not used by any tests.
+ return null;
+ }
+
+ @Override
+ public void executeDeleteRelationshipIndex(String indexName)
+ throws ChampIndexNotExistsException {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void storeSchema(ChampSchema schema) throws ChampSchemaViolationException {
+ // Not used by any tests.
+ }
+
+ @Override
+ public ChampSchema retrieveSchema() {
+ // Not used by any tests.
+ return null;
+ }
+
+ @Override
+ public void updateSchema(ChampObjectConstraint objectConstraint)
+ throws ChampSchemaViolationException {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void updateSchema(ChampRelationshipConstraint schema)
+ throws ChampSchemaViolationException {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void deleteSchema() {
+ // Not used by any tests.
+ }
+
+ @Override
+ public ChampCapabilities capabilities() {
+ // Not used by any tests.
+ return null;
+ }
+ }
+
+ private class InMemoryPublisher implements CambriaPublisher {
+
+ public BlockingQueue<String> eventStream = new ArrayBlockingQueue<String>(50);
+ public BlockingQueue<String> failedMsgs = new ArrayBlockingQueue<String>(10);
+
+ private boolean failMode=false;
+
+ public void enterFailMode() {
+ failMode=true;
+ }
+
+ @Override
+ public void logTo(Logger log) {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void setApiCredentials(String apiKey, String apiSecret) {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void clearApiCredentials() {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void setHttpBasicCredentials(String username, String password) {
+ // Not used by any tests.
+ }
+
+ @Override
+ public void clearHttpBasicCredentials() {
+ // Not used by any tests.
+ }
+
+ @Override
+ public int send(String partition, String msg) throws IOException {
+
+ if(!failMode) {
+ eventStream.add(msg);
+ return 0;
+ } else {
+ failedMsgs.add(msg);
+ throw new IOException("nope");
+ }
+ }
+
+ @Override
+ public int send(message msg) throws IOException {
+ eventStream.add(msg.toString());
+ return 0;
+ }
+
+ @Override
+ public int send(Collection<message> msgs) throws IOException {
+ for(message msg : msgs) {
+ eventStream.add(msg.toString());
+ }
+ return 0;
+ }
+
+ @Override
+ public void close() {
+ // Not used by any tests.
+ }
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/exceptions/ChampExceptionTest.java b/src/test/java/org/openecomp/aai/champ/exceptions/ChampExceptionTest.java
new file mode 100644
index 0000000..3f813f5
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/exceptions/ChampExceptionTest.java
@@ -0,0 +1,150 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.exceptions;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ChampExceptionTest {
+
+ @Test
+ public void testChampIndexNotExistsException() {
+ final ChampIndexNotExistsException e1 = new ChampIndexNotExistsException();
+
+ assertTrue(e1.getMessage() == null);
+
+ final ChampIndexNotExistsException e2 = new ChampIndexNotExistsException("foo");
+
+ assertTrue(e2.getMessage().equals("foo"));
+
+ final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+ assertTrue(e3.getCause().equals(e2));
+
+ final ChampIndexNotExistsException e4 = new ChampIndexNotExistsException("foo", e3);
+
+ assertTrue(e4.getMessage().equals("foo"));
+ assertTrue(e4.getCause().equals(e3));
+ }
+
+ @Test
+ public void testChampMarshallingException() {
+ final ChampMarshallingException e1 = new ChampMarshallingException();
+
+ assertTrue(e1.getMessage() == null);
+
+ final ChampMarshallingException e2 = new ChampMarshallingException("foo");
+
+ assertTrue(e2.getMessage().equals("foo"));
+
+ final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+ assertTrue(e3.getCause().equals(e2));
+
+ final ChampMarshallingException e4 = new ChampMarshallingException("foo", e3);
+
+ assertTrue(e4.getMessage().equals("foo"));
+ assertTrue(e4.getCause().equals(e3));
+ }
+
+ @Test
+ public void testChampObjectNotExistsException() {
+ final ChampObjectNotExistsException e1 = new ChampObjectNotExistsException();
+
+ assertTrue(e1.getMessage() == null);
+
+ final ChampObjectNotExistsException e2 = new ChampObjectNotExistsException("foo");
+
+ assertTrue(e2.getMessage().equals("foo"));
+
+ final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+ assertTrue(e3.getCause().equals(e2));
+
+ final ChampObjectNotExistsException e4 = new ChampObjectNotExistsException("foo", e3);
+
+ assertTrue(e4.getMessage().equals("foo"));
+ assertTrue(e4.getCause().equals(e3));
+ }
+
+ @Test
+ public void testChampRelationshipNotExistsException() {
+ final ChampRelationshipNotExistsException e1 = new ChampRelationshipNotExistsException();
+
+ assertTrue(e1.getMessage() == null);
+
+ final ChampRelationshipNotExistsException e2 = new ChampRelationshipNotExistsException("foo");
+
+ assertTrue(e2.getMessage().equals("foo"));
+
+ final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+ assertTrue(e3.getCause().equals(e2));
+
+ final ChampRelationshipNotExistsException e4 = new ChampRelationshipNotExistsException("foo", e3);
+
+ assertTrue(e4.getMessage().equals("foo"));
+ assertTrue(e4.getCause().equals(e3));
+ }
+
+ @Test
+ public void testChampSchemaViolationException() {
+ final ChampSchemaViolationException e1 = new ChampSchemaViolationException();
+
+ assertTrue(e1.getMessage() == null);
+
+ final ChampSchemaViolationException e2 = new ChampSchemaViolationException("foo");
+
+ assertTrue(e2.getMessage().equals("foo"));
+
+ final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+ assertTrue(e3.getCause().equals(e2));
+
+ final ChampSchemaViolationException e4 = new ChampSchemaViolationException("foo", e3);
+
+ assertTrue(e4.getMessage().equals("foo"));
+ assertTrue(e4.getCause().equals(e3));
+ }
+
+ @Test
+ public void testChampUnmarshallingException() {
+ final ChampUnmarshallingException e1 = new ChampUnmarshallingException();
+
+ assertTrue(e1.getMessage() == null);
+
+ final ChampUnmarshallingException e2 = new ChampUnmarshallingException("foo");
+
+ assertTrue(e2.getMessage().equals("foo"));
+
+ final ChampIndexNotExistsException e3 = new ChampIndexNotExistsException(e2);
+
+ assertTrue(e3.getCause().equals(e2));
+
+ final ChampUnmarshallingException e4 = new ChampUnmarshallingException("foo", e3);
+
+ assertTrue(e4.getMessage().equals("foo"));
+ assertTrue(e4.getCause().equals(e3));
+ }
+
+}
diff --git a/src/test/java/org/openecomp/aai/champ/ie/ExportTest.java b/src/test/java/org/openecomp/aai/champ/ie/ExportTest.java
new file mode 100644
index 0000000..7ecb3de
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/ie/ExportTest.java
@@ -0,0 +1,42 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.ie;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+
+public class ExportTest {
+
+ @Test
+ public void testGraphMLExport() {
+
+ final GraphMLImporterExporter ie = new GraphMLImporterExporter();
+ final ChampAPI api = ChampAPI.Factory.newInstance(ChampGraph.Type.IN_MEMORY);
+
+ ie.importData(api, getClass().getClassLoader().getResourceAsStream("import-test.graphml"));
+
+ ie.exportData(api.getGraph("unit-test"), System.out);
+
+ api.shutdown();
+ }
+}
diff --git a/src/test/java/org/openecomp/aai/champ/ie/ImportTest.java b/src/test/java/org/openecomp/aai/champ/ie/ImportTest.java
new file mode 100644
index 0000000..ba2cbb1
--- /dev/null
+++ b/src/test/java/org/openecomp/aai/champ/ie/ImportTest.java
@@ -0,0 +1,93 @@
+/**
+ * ============LICENSE_START==========================================
+ * org.onap.aai
+ * ===================================================================
+ * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright © 2017 Amdocs
+ * ===================================================================
+ * 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============================================
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ */
+package org.openecomp.aai.champ.ie;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Optional;
+
+import org.junit.Test;
+import org.openecomp.aai.champ.ChampAPI;
+import org.openecomp.aai.champ.ChampGraph;
+
+public class ImportTest {
+
+ private final String GRAPH_NAME = "unit-test";
+
+ @Test
+ public void testGraphMLImport() {
+
+ final GraphMLImporterExporter importer = new GraphMLImporterExporter();
+ final ChampAPI api = ChampAPI.Factory.newInstance(ChampGraph.Type.IN_MEMORY);
+
+ importer.importData(api, getClass().getClassLoader().getResourceAsStream("import-test.graphml"));
+
+ final ChampGraph graph = api.getGraph(GRAPH_NAME);
+
+ graph.queryObjects(Collections.emptyMap()).forEach(object -> {
+ final Optional<String> nameOpt = object.getProperty("name");
+ final Optional<Boolean> studentOpt = object.getProperty("student");
+ final Optional<Long> worthOpt = object.getProperty("worth");
+ final Optional<Integer> ageOpt = object.getProperty("age");
+ final Optional<Float> heightOpt = object.getProperty("height");
+ final Optional<Double> weightOpt = object.getProperty("weight");
+ final Optional<String> favoriteColorOpt = object.getProperty("favoriteColor");
+
+ final String name = nameOpt.get();
+
+ if (name.equals("Champ")) {
+ assertTrue(!studentOpt.isPresent());
+ assertTrue(!ageOpt.isPresent());
+ assertTrue(!worthOpt.isPresent());
+ assertTrue(!heightOpt.isPresent());
+ assertTrue(!weightOpt.isPresent());
+ assertTrue(favoriteColorOpt.get().equals("green"));
+ } else if (name.equals("Max")) {
+ assertTrue(!studentOpt.isPresent());
+ assertTrue(!ageOpt.isPresent());
+ assertTrue(!worthOpt.isPresent());
+ assertTrue(!heightOpt.isPresent());
+ assertTrue(!weightOpt.isPresent());
+ assertTrue(favoriteColorOpt.get().equals("red"));
+ } else if (name.equals("Ace")) {
+ assertTrue(studentOpt.get());
+ assertTrue(worthOpt.get().equals(50000L));
+ assertTrue(ageOpt.get().equals(21));
+ assertTrue(heightOpt.get().equals(72.5f));
+ assertTrue(weightOpt.get().equals(180.5d));
+ assertTrue(favoriteColorOpt.get().equals("yellow"));
+ } else if (name.equals("Fido")) {
+ assertTrue(!studentOpt.isPresent());
+ assertTrue(!ageOpt.isPresent());
+ assertTrue(!worthOpt.isPresent());
+ assertTrue(!heightOpt.isPresent());
+ assertTrue(!weightOpt.isPresent());
+ assertTrue(favoriteColorOpt.get().equals("blue"));
+ } else {
+ throw new AssertionError("Unknown object " + name + " - update unit test");
+ }
+ });
+
+ api.shutdown();
+ }
+}
diff --git a/src/test/resources/import-test.graphml b/src/test/resources/import-test.graphml
new file mode 100755
index 0000000..fd13b6b
--- /dev/null
+++ b/src/test/resources/import-test.graphml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
+ <key id="d0" for="node" attr.name="student" attr.type="boolean"/>
+ <key id="d1" for="node" attr.name="worth" attr.type="long"/>
+ <key id="d2" for="node" attr.name="age" attr.type="int"/>
+ <key id="d3" for="node" attr.name="height" attr.type="float"/>
+ <key id="d4" for="node" attr.name="weight" attr.type="double"/>
+ <key id="d5" for="node" attr.name="favoriteColor" attr.type="string">
+ <default>green</default>
+ </key>
+ <key id="d6" for="node" attr.name="name" attr.type="string"/>
+ <key id="d7" for="edge" attr.name="at" attr.type="long"/>
+ <key id="d8" for="node" attr.name="type" attr.type="string"/>
+ <key id="d9" for="edge" attr.name="type" attr.type="string"/>
+ <graph id="unit-test" edgedefault="directed">
+ <node id="n0">
+ <data key="d0">true</data>
+ <data key="d1">50000</data>
+ <data key="d2">21</data>
+ <data key="d3">72.5</data>
+ <data key="d4">180.5</data>
+ <data key="d5">yellow</data>
+ <data key="d6">Ace</data>
+ <data key="d8">Dog</data>
+ </node>
+ <node id="n1">
+ <data key="d6">Champ</data>
+ <data key="d8">Dog</data>
+ </node>
+ <node id="n2">
+ <data key="d5">blue</data>
+ <data key="d6">Fido</data>
+ <data key="d8">Dog</data>
+ </node>
+ <node id="n3">
+ <data key="d5">red</data>
+ <data key="d6">Max</data>
+ <data key="d8">Dog</data>
+ </node>
+ <edge id="e0" source="n0" target="n2">
+ <data key="d7">12348234</data>
+ <data key="d9">Dog</data>
+ </edge>
+ <edge id="e3" source="n3" target="n2">
+ <data key="d9">Dog</data>
+ </edge>
+ </graph>
+</graphml> \ No newline at end of file
diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml
new file mode 100644
index 0000000..72cd644
--- /dev/null
+++ b/src/test/resources/logback.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ ============LICENSE_START==========================================
+ org.onap.aai
+ ===================================================================
+ Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ Copyright © 2017 Amdocs
+ ===================================================================
+ 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============================================
+ ECOMP is a trademark and service mark of AT&T Intellectual Property.
+
+-->
+<configuration>
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d %-5level [%thread] %logger{255}: %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <logger name="com.thinkaurelius.titan" level="OFF"/>
+ <logger name="com.netflix.astyanax" level="OFF"/>
+ <logger name="com.datastax" level="ERROR"/>
+ <logger name="org.apache.hadoop" level="ERROR"/>
+ <logger name="org.apache.zookeeper" level="ERROR"/>
+
+ <root level="INFO">
+ <appender-ref ref="STDOUT" />
+ </root>
+
+</configuration>