diff options
Diffstat (limited to 'src')
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> |