summaryrefslogtreecommitdiffstats
path: root/zusammen-lib
diff options
context:
space:
mode:
Diffstat (limited to 'zusammen-lib')
-rw-r--r--zusammen-lib/README.md272
-rw-r--r--zusammen-lib/pom.xml73
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/config/ZusammenConfig.java88
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/config/ZusammenConfigProvider.java20
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/ZusammenConnector.java98
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/impl/ZusammenAdaptorsConfig.java32
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/impl/ZusammenConnectorImpl.java281
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ElementConvertor.java15
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ZusammenAdaptor.java110
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ZusammenElementUtil.java27
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/exceptions/ZusammenException.java8
-rw-r--r--zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/impl/ZusammenAdaptorImpl.java277
-rw-r--r--zusammen-lib/src/test/java/org/onap/sdc/common/zusammen/services/impl/ZusammenAdaptorImplTest.java315
13 files changed, 1616 insertions, 0 deletions
diff --git a/zusammen-lib/README.md b/zusammen-lib/README.md
new file mode 100644
index 0000000..e5c2040
--- /dev/null
+++ b/zusammen-lib/README.md
@@ -0,0 +1,272 @@
+Introduction
+============
+
+This zusammen library is a library which encapsulate access to Zusammen collaborative database based on cassandra.
+
+Components
+==========
+
+The onboarding is comprised of the following deployment units:
+
+- Designer backend is the core component. It exposes RESTful APIs for managing vsp. The backend
+currently supports VNFD packages of ETSI SOL001 standard only.
+
+- Designer frontend serves static content of a Web application for creating and managing vsps, and forwards API
+requests to the backend. The static content includes JavaScript, images, CSS, etc.
+
+- Translator from Tosca SOL001 standard to Onboarding internal model is used by the designer backend.
+
+- Cassandra database is used by the designer backend as the main storage for onboarding data. A dedicated instance of
+Cassandra can be deployed, or an existing cluster may be used.
+
+- Database initialization scripts run once per deployment to create the necessary Cassandra keyspaces and tables,
+pre-populate data, etc.
+
+Execute Backend from IntelliJ
+=============================
+Create a copy of `application.properties` (located in `vnf-onboarding-backend\src\main\resources`) and name it `application-dev.properties`.
+
+In this file, populate the required properties with your Cassandra, Translation and SDC Catalog info.
+
+Run `org.onap.sdc.onboarding.SpringBootWebApplication` with the VM options: `-Dspring.profiles.active=dev`.
+
+Deployment on Docker
+====================
+
+The procedure below describes manual deployment on plain Docker for development or a demo.
+
+## 1. Database
+
+Create a dedicated instance of Cassandra. This step is optional if you already have a Cassandra cluster.
+The designer is not expected to have problems working with Cassandra 3.x, but has been tested with 2.1.x because this
+is the version used by SDC.
+
+An easy way to spin up a Cassandra instance is using a Cassandra Docker image as described in the
+[official documentation](https://hub.docker.com/_/cassandra/).
+
+### Example
+
+`docker run -d --name onboard-cassandra cassandra:2.1`
+
+## 2. Database Initialization
+
+**WARNING**: *This step must be executed only once.*
+
+the designer requires two Cassandra namespaces:
+
+- ONBOARDING
+- ZUSAMMEN_ONBOARDING
+
+By default, these keyspaces are configured to use a simple replication strategy (`'class' : 'SimpleStrategy'`)
+and the replication factor of one (`'replication_factor' : 1`). In order to override this configuration, override
+the *create_keyspaces.cql* file at the root of the initialization container using
+[Docker volume mapping](https://docs.docker.com/storage/volumes/). Include `IF NOT EXISTS` clause in the keyspace
+creation statements to prevent accidental data loss.
+
+`docker run -ti -e CS_HOST=<cassandra-host> -e CS_PORT=<cassandra-port> -e CS_AUTHENTICATE=true/false
+-e CS_USER=<cassandra-user> -e CS_PASSWORD=<cassandra-password> nexus3.onap.org:10001/NPO/vnf-onboard-init:latest`
+
+### Environment Variables
+
+- CS_HOST &mdash; Cassandra hostname or IP address.
+
+- CS_PORT &mdash; Cassandra Thrift client port. If not specified, the default of 9160 will be used.
+
+- CS_AUTHENTICATE &mdash; whether password authentication must be used to connect to Cassandra. A *false* will be
+assumed if this variable is not specified.
+
+- CS_USER &mdash; Cassandra username if CS_AUTHENTICATE is *true*.
+
+- CS_PASSWORD &mdash; Cassandra password if CS_AUTHENTICATE is *true*.
+
+### Example
+
+Assuming you have created a dedicated Cassandra container as described in Database section, and the access to it is not
+protected with a password, the following command will initialize the database:
+
+`docker run -d --name vnf-onboard-init
+-e CS_HOST=$(docker inspect vnf-onboard-cassandra --format={{.NetworkSettings.IPAddress}})
+nexus3.onap.org:10001/onap/vnf-onboard-init:latest`
+
+### Troubleshooting
+
+In order to see if the the designer was successfully initialized, make sure the console does not contain error
+messages. You can also see the logs of the initialization container using `docker logs vnf-onboard-init` command.
+## 3. Translation
+
+`docker run -d --name vnfd-sol001-translation -p 8080:8080 npo/vnfd-sol001-translation:latest`
+
+## 4. Backend
+
+`docker run -d --name vnf-onboard-backend
+-e SERVER_SSL_ENABLED=true/false
+-e SERVER_SSL_KEY_PASSWORD=<ssl_key_password>
+-e SERVER_SSL_KEYSTORE_PATH=<ssl_keystore_path>
+-e SERVER_SSL_KEYSTORE_TYPE=<ssl_keystore_type>
+-e SDC_PROTOCL=http/https
+-e CS_HOSTS=<cassandra-hosts>
+-e CS_PORT=<cassandra-port>
+-e CS_AUTHENTICATE=true/false
+-e CS_USER=<cassandra user>
+-e CS_PASSWORD=<cassandra password>
+-e CS_SSL_ENABLED=true/false
+--volume <cassandra-truststore-path_container>:<cassandra-truststore-path_local>
+-e CS_TRUST_STORE_PATH=<cassandra-truststore-path_container>
+-e CS_TRUST_STORE_PASSWORD=<cassandra-truststore-password>
+-e TRANSLATION_HOST=<translation ip>
+-e TRANSLATION_PORT=<translation port>
+-e SDC_HOST=<sdc catalog ip>
+-e SDC_PORT=<sdc catalog port>
+-e SDC_USER=<sdc consumer user>
+-e SDC_PASSWORD=<secret>
+-e JAVA_OPTIONS="-Xmx1536m -Xms1536m"
+-p 8443:8443
+npo/vnf-onboard-backend:latest`
+
+### Environment Variables
+
+- SERVER_SSL_ENABLED &mdash; whether ssl authentication must be used to connect to application. A *false* will be
+assumed if this variable is not specified.
+
+- SERVER_SSL_KEY_PASSWORD &mdash; SSL key password if SERVER_SSL_ENABLED is *true*.
+
+- SERVER_SSL_KEYSTORE_PATH &mdash; SSL Keystore path if SERVER_SSL_ENABLED is *true*.
+
+- SERVER_SSL_KEYSTORE_TYPE &mdash; SSL Keystore type if SERVER_SSL_ENABLED is *true*.
+
+- CS_HOSTS &mdash; comma-separated list of Cassandra hostnames or IP addresses.
+
+- CS_PORT &mdash; CQL native client port. If not specified, the default of 9042 will be used.
+
+- CS_AUTHENTICATE &mdash; whether password authentication must be used to connect to Cassandra. A *false* will be
+assumed if this variable is not specified.
+
+- CS_USER &mdash; Cassandra username if CS_AUTHENTICATE is *true*.
+
+- CS_PASSWORD &mdash; Cassandra password if CS_AUTHENTICATE is *true*.
+
+- CS_SSL_ENABLED &mdash; whether ssl authentication must be used to connect to Cassandra. A *false* will be
+assumed if this variable is not specified.
+
+- CS_TRUST_STORE_PATH &mdash; Cassandra Truststore path if CS_SSL_ENABLED is *true*.
+
+- CS_TRUST_STORE_PASSWORD &mdash; Cassandra Truststore password if CS_SSL_ENABLED is *true*.
+
+- TRANSLATION_PROTOCOL &mdash; protocol to be used for calling Translation APIs (http or https).
+
+- TRANSLATION_HOST &mdash; a Translation server.
+
+- TRANSLATION_PORT &mdash; a Translation server port, usually 8080.
+
+- SDC_PROTOCOL &mdash; protocol to be used for calling SDC APIs (http or https).
+
+- SDC_HOST &mdash; a SDC backend server.
+
+- SDC_PORT &mdash; a SDC backend server port, usually 8080.
+
+- SDC_USER &mdash; Onboarding consumer username
+
+- SDC_PASSWORD &mdash; Onboarding consumer password
+
+- JAVA_OPTIONS &mdash; optionally, JVM (Java Virtual Machine) arguments.
+
+### Example
+
+Assuming you have a dedicated Cassandra container as described in Database section, and the access to it is not
+protected with a password. The following command will start a backend container without SSL support:
+
+`docker run -d --name vnf-onboard-backend
+-e CS_HOSTS=$(docker inspect vnf-onboard-cassandra --format={{.NetworkSettings.IPAddress}})
+-e TRANSLATION_HOST=<translation ip>
+-e TRANSLATION_PORT=<translation port>
+-e SDC_HOST=<sdc catalog ip>
+-e SDC_PORT=<sdc catalog port>
+-e SDC_USER=<sdc consumer user>
+-e SDC_PASSWORD=<secret>
+-e JAVA_OPTIONS="-Xmx1536m -Xms1536m"
+-p 8443:8443
+npo/vnf-onboard-backend:latest`
+
+### Troubleshooting
+
+In order to verify that the backend has started successfully, check the logs of the
+backend container. For example, by running `docker logs vnf-onboard-backend`. The logs must not contain any
+error messages.
+
+Application logs are located in the */var/log/... directory of a backend
+container. For example, you can view the audit log by running
+`docker exec -ti vnf-onboard-backend less /var/log/npo/vnf-onboard-backend/backend/audit.log`.
+
+## 5. Frontend
+
+`docker run -d -e BACKEND=http://<backend-host>:<backend-port> -e JAVA_OPTIONS=<jvm-options>
+nexus3.onap.org:10001/npo/vnf-onboard-frontend:latest`
+
+- BACKEND &mdash; root endpoint of the RESTful APIs exposed by a backend server.
+
+- JAVA_OPTIONS &mdash; optionally, JVM (Java Virtual Machine) arguments.
+
+### Example
+
+`docker run -d --name vnf-onboard-frontend
+-e BACKEND=http://$(docker inspect vnf-onboard-backend --format={{.NetworkSettings.IPAddress}}):8080
+-e JAVA_OPTIONS="-Xmx64m -Xms64m -Xss1m" -p 9088:8080 nexus3.onap.org:10001/npo/vnf-onboard-frontend:latest`
+
+Notice that port 8080 of the frontend container has been
+[mapped]( https://docs.docker.com/config/containers/container-networking/#published-ports) to port 9088 of the host
+machine. This makes the Designer Web application accessible from the outside world via the host machine's
+IP address/hostname.
+
+### Troubleshooting
+
+In order to check if the Designer frontend has successfully started, look at the logs of the
+frontend container. For example, by running `docker logs vnf-onboard-frontend`. The logs should not contain
+error messages.
+
+Frontend does not have backend logic, therefore there are no application logs.
+
+
+SDC Plugin Configuration
+========================
+
+In order to run as an SDC pluggable designer, the designer must be added to SDC configuration as described in
+[Generic plugin support](https://wiki.onap.org/display/DW/Generic+Designer+Support).
+
+If you are deploying SDC using a standard procedure (OOM or the
+[SDC shell script](https://wiki.onap.org/display/DW/Deploying+SDC+on+a+Linux+VM+for+Development)),
+the easiest way to configure the Onboarding plugin is to edit the *plugins-configuration.yaml*.
+
+### Plugin Source
+
+The main endpoint to load the designer Web application is defined by `"pluginSourceUrl": "http://<host>:<port>"`.
+
+Keep in mind that the URL **must be accessible from a user's browser**. In most cases, `<host>` will be the hostname or
+IP address of the machine that runs Docker engine, and `<port>` will be a host port to which you have published port
+8080 of the Onboarding frontend container.
+
+### Plugin Discovery
+
+In order to check the availability of a plugin, SDC uses `"pluginDiscoveryUrl"`. For Onboarding the value is
+`http://<host>:<port>/ping`.
+
+### Example
+
+Let's assume that hostname of the machine that runs Docker containers with the Onboarding application is
+*onboard.example.com*, and port 8080 of the Onboarding frontend is mapped to 9088 on the host. In this case the
+corresponding section of *plugins-configuration.yaml* will look like below:
+
+```
+
+- pluginId: ONBOARD
+ pluginDiscoveryUrl: "http://onboard.example.com:9088/ping"
+ pluginSourceUrl: "http://onboard.example.com:9088"
+ pluginStateUrl: "onboarding"
+ pluginDisplayOptions:
+ tab:
+ displayName: "ONBOARD"
+ displayRoles: ["DESIGNER", "TESTER"]
+
+```
+
+In a development or demo environment, the designer will run on the same host as SDC, so that its IP address will
+be the one of the Docker host. \ No newline at end of file
diff --git a/zusammen-lib/pom.xml b/zusammen-lib/pom.xml
new file mode 100644
index 0000000..f76b377
--- /dev/null
+++ b/zusammen-lib/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright © 2019 European Support Limited
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>zusammen-lib</artifactId>
+ <parent>
+ <groupId>org.onap.sdc.common</groupId>
+ <artifactId>sdc-be-common</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <properties>
+ <zusammen.version>1.0.1</zusammen.version>
+ <zusammen-state-store.version>1.0.1</zusammen-state-store.version>
+ <zusammen-collaboration-store.version>1.0.1</zusammen-collaboration-store.version>
+ <zusammen-index-store.version>1.0.0</zusammen-index-store.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-data-cassandra</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.amdocs.zusammen</groupId>
+ <artifactId>zusammen-datatypes</artifactId>
+ <version>${zusammen.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.amdocs.zusammen</groupId>
+ <artifactId>zusammen-adaptor-inbound-api</artifactId>
+ <version>${zusammen.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.amdocs.zusammen</groupId>
+ <artifactId>zusammen-adaptor-inbound-impl</artifactId>
+ <version>${zusammen.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.amdocs.zusammen.plugin</groupId>
+ <artifactId>zusammen-collaboration-cassandra-plugin</artifactId>
+ <version>${zusammen-collaboration-store.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.amdocs.zusammen.plugin</groupId>
+ <artifactId>zusammen-search-index-empty-plugin</artifactId>
+ <version>${zusammen-index-store.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+</project>
+
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/config/ZusammenConfig.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/config/ZusammenConfig.java
new file mode 100644
index 0000000..920b07a
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/config/ZusammenConfig.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2018 European Support Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.sdc.common.zusammen.config;
+
+import com.datastax.driver.core.RemoteEndpointAwareJdkSSLOptions;
+import com.datastax.driver.core.SSLOptions;
+import java.io.FileInputStream;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import javax.annotation.PostConstruct;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.cassandra.ClusterBuilderCustomizer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ZusammenConfig {
+
+ private static final String[] CIPHER_SUITES = {"TLS_RSA_WITH_AES_128_CBC_SHA"};
+ private static final String KEYSTORE_TYPE = "JKS";
+ private static final String SECURE_SOCKET_PROTOCOL = "SSL";
+ private static final String KEYSPACE = "zusammen";
+
+ private final ZusammenConfigProvider provider;
+
+
+ @Autowired
+ public ZusammenConfig(ZusammenConfigProvider provider) {
+ this.provider = provider;
+ }
+
+ @PostConstruct
+ public void init() {
+ System.setProperty("cassandra.nodes", provider.getCassandraAddresses());
+ System.setProperty("cassandra.ssl.port", provider.getCassandraPort());
+ System.setProperty("cassandra.keyspace", KEYSPACE);
+
+ System.setProperty("cassandra.authenticate", Boolean.toString(Boolean.valueOf(provider.getCassandraAuth())));
+ System.setProperty("cassandra.user", provider.getCassandraUser());
+ System.setProperty("cassandra.password", provider.getCassandraPassword());
+
+ System.setProperty("cassandra.ssl", Boolean.toString(Boolean.valueOf(provider.getCassandraSSL())));
+ System.setProperty("cassandra.truststore", provider.getCassandraTrustStorePath());
+ System.setProperty("cassandra.truststore.password", provider.getCassandraTrustStorePassword());
+ }
+
+ @Bean
+ @ConditionalOnProperty("cassandra.ssl")
+ ClusterBuilderCustomizer clusterBuilderCustomizer() {
+ SSLOptions sslOptions = RemoteEndpointAwareJdkSSLOptions
+ .builder()
+ .withSSLContext(getSslContext())
+ .withCipherSuites(CIPHER_SUITES).build();
+ return builder -> builder.withSSL(sslOptions);
+ }
+
+ private SSLContext getSslContext() {
+ try (FileInputStream tsf = new FileInputStream(provider.getCassandraTrustStorePath())) {
+ SSLContext ctx = SSLContext.getInstance(SECURE_SOCKET_PROTOCOL);
+ KeyStore ts = KeyStore.getInstance(KEYSTORE_TYPE);
+ ts.load(tsf, provider.getCassandraTrustStorePassword().toCharArray());
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(ts);
+ ctx.init(null, tmf.getTrustManagers(), new SecureRandom());
+ return ctx;
+ } catch (Exception ex) {
+ throw new BeanCreationException(ex.getMessage(), ex);
+ }
+ }
+} \ No newline at end of file
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/config/ZusammenConfigProvider.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/config/ZusammenConfigProvider.java
new file mode 100644
index 0000000..4cbe1f2
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/config/ZusammenConfigProvider.java
@@ -0,0 +1,20 @@
+package org.onap.sdc.common.zusammen.config;
+
+public interface ZusammenConfigProvider {
+
+ String getCassandraAddresses();
+
+ String getCassandraPort();
+
+ String getCassandraAuth();
+
+ String getCassandraUser();
+
+ String getCassandraPassword();
+
+ String getCassandraSSL();
+
+ String getCassandraTrustStorePath();
+
+ String getCassandraTrustStorePassword();
+}
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/ZusammenConnector.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/ZusammenConnector.java
new file mode 100644
index 0000000..98640e2
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/ZusammenConnector.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2016-2018 European Support Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.sdc.common.zusammen.persistence;
+
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.Element;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementConflict;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementInfo;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ItemVersionConflict;
+import com.amdocs.zusammen.commons.health.data.HealthInfo;
+import com.amdocs.zusammen.datatypes.Id;
+import com.amdocs.zusammen.datatypes.SessionContext;
+import com.amdocs.zusammen.datatypes.item.ElementContext;
+import com.amdocs.zusammen.datatypes.item.Info;
+import com.amdocs.zusammen.datatypes.item.Item;
+import com.amdocs.zusammen.datatypes.item.ItemVersion;
+import com.amdocs.zusammen.datatypes.item.ItemVersionData;
+import com.amdocs.zusammen.datatypes.item.ItemVersionStatus;
+import com.amdocs.zusammen.datatypes.item.Resolution;
+import com.amdocs.zusammen.datatypes.itemversion.ItemVersionRevisions;
+import com.amdocs.zusammen.datatypes.itemversion.Tag;
+import java.util.Collection;
+
+public interface ZusammenConnector {
+
+ Collection<HealthInfo> checkHealth(SessionContext sessionContext);
+
+ String getReleaseVersion(SessionContext sessionContext);
+
+ Collection<Item> listItems(SessionContext context);
+
+ Item getItem(SessionContext context, Id itemId);
+
+ Id createItem(SessionContext context, Info info);
+
+ void deleteItem(SessionContext context, Id itemId);
+
+ void updateItem(SessionContext context, Id itemId, Info info);
+
+
+ Collection<ItemVersion> listPublicVersions(SessionContext context, Id itemId);
+
+ ItemVersion getPublicVersion(SessionContext context, Id itemId, Id versionId);
+
+ Id createVersion(SessionContext context, Id itemId, Id baseVersionId, ItemVersionData itemVersionData);
+
+ void updateVersion(SessionContext context, Id itemId, Id versionId, ItemVersionData itemVersionData);
+
+ ItemVersion getVersion(SessionContext context, Id itemId, Id versionId);
+
+ ItemVersionStatus getVersionStatus(SessionContext context, Id itemId, Id versionId);
+
+ void tagVersion(SessionContext context, Id itemId, Id versionId, Tag tag);
+
+ void resetVersionRevision(SessionContext context, Id itemId, Id versionId, Id revisionId);
+
+ void revertVersionRevision(SessionContext context, Id itemId, Id versionId, Id revisionId);
+
+ ItemVersionRevisions listVersionRevisions(SessionContext context, Id itemId, Id versionId);
+
+ void publishVersion(SessionContext context, Id itemId, Id versionId, String message);
+
+ void syncVersion(SessionContext context, Id itemId, Id versionId);
+
+ void forceSyncVersion(SessionContext context, Id itemId, Id versionId);
+
+ void cleanVersion(SessionContext context, Id itemId, Id versionId);
+
+ ItemVersionConflict getVersionConflict(SessionContext context, Id itemId, Id versionId);
+
+
+ Collection<ElementInfo> listElements(SessionContext context, ElementContext elementContext, Id parentElementId);
+
+ ElementInfo getElementInfo(SessionContext context, ElementContext elementContext, Id elementId);
+
+ Element getElement(SessionContext context, ElementContext elementContext, Id elementId);
+
+ ElementConflict getElementConflict(SessionContext context, ElementContext elementContext, Id elementId);
+
+ Element saveElement(SessionContext context, ElementContext elementContext, Element element, String message);
+
+ void resolveElementConflict(SessionContext context, ElementContext elementContext, Element element,
+ Resolution resolution);
+
+}
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/impl/ZusammenAdaptorsConfig.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/impl/ZusammenAdaptorsConfig.java
new file mode 100644
index 0000000..18d34bc
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/impl/ZusammenAdaptorsConfig.java
@@ -0,0 +1,32 @@
+package org.onap.sdc.common.zusammen.persistence.impl;
+
+import com.amdocs.zusammen.adaptor.inbound.api.health.HealthAdaptorFactory;
+import com.amdocs.zusammen.adaptor.inbound.api.item.ElementAdaptorFactory;
+import com.amdocs.zusammen.adaptor.inbound.api.item.ItemAdaptorFactory;
+import com.amdocs.zusammen.adaptor.inbound.api.item.ItemVersionAdaptorFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ZusammenAdaptorsConfig {
+
+ @Bean
+ public ItemAdaptorFactory itemAdaptorFactory() {
+ return ItemAdaptorFactory.getInstance();
+ }
+
+ @Bean
+ public ItemVersionAdaptorFactory itemVersionAdaptorFactory() {
+ return ItemVersionAdaptorFactory.getInstance();
+ }
+
+ @Bean
+ public ElementAdaptorFactory elementAdaptorFactory() {
+ return ElementAdaptorFactory.getInstance();
+ }
+
+ @Bean
+ public HealthAdaptorFactory healthAdaptorFactory() {
+ return HealthAdaptorFactory.getInstance();
+ }
+}
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/impl/ZusammenConnectorImpl.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/impl/ZusammenConnectorImpl.java
new file mode 100644
index 0000000..5d9c749
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/persistence/impl/ZusammenConnectorImpl.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.sdc.common.zusammen.persistence.impl;
+
+import com.amdocs.zusammen.adaptor.inbound.api.health.HealthAdaptorFactory;
+import com.amdocs.zusammen.adaptor.inbound.api.item.ElementAdaptorFactory;
+import com.amdocs.zusammen.adaptor.inbound.api.item.ItemAdaptorFactory;
+import com.amdocs.zusammen.adaptor.inbound.api.item.ItemVersionAdaptorFactory;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.Element;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementConflict;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementInfo;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ItemVersionConflict;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.MergeResult;
+import com.amdocs.zusammen.commons.health.data.HealthInfo;
+import com.amdocs.zusammen.datatypes.Id;
+import com.amdocs.zusammen.datatypes.SessionContext;
+import com.amdocs.zusammen.datatypes.Space;
+import com.amdocs.zusammen.datatypes.item.ElementContext;
+import com.amdocs.zusammen.datatypes.item.Info;
+import com.amdocs.zusammen.datatypes.item.Item;
+import com.amdocs.zusammen.datatypes.item.ItemVersion;
+import com.amdocs.zusammen.datatypes.item.ItemVersionData;
+import com.amdocs.zusammen.datatypes.item.ItemVersionStatus;
+import com.amdocs.zusammen.datatypes.item.Resolution;
+import com.amdocs.zusammen.datatypes.itemversion.ItemVersionRevisions;
+import com.amdocs.zusammen.datatypes.itemversion.Tag;
+import com.amdocs.zusammen.datatypes.response.Response;
+import java.util.Collection;
+import org.onap.sdc.common.zusammen.persistence.ZusammenConnector;
+import org.onap.sdc.common.zusammen.services.exceptions.ZusammenException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public class ZusammenConnectorImpl implements ZusammenConnector {
+
+ private static final String GET_ELEMENT_ERR_MSG =
+ "Failed to get element. Item Id: %s, version Id: %s, element Id: %s message: %s";
+ private static final String GET_ELEMENT_IN_REV_ERR_MSG =
+ "Failed to get element. Item Id: %s, version Id: %s, revision Id: %s, element Id: %s message: %s";
+ private final ItemAdaptorFactory itemAdaptorFactory;
+ private final ItemVersionAdaptorFactory versionAdaptorFactory;
+ private final ElementAdaptorFactory elementAdaptorFactory;
+ private final HealthAdaptorFactory healthAdaptorFactory;
+
+ @Autowired
+ public ZusammenConnectorImpl(ItemAdaptorFactory itemAdaptorFactory, ItemVersionAdaptorFactory versionAdaptorFactory,
+ ElementAdaptorFactory elementAdaptorFactory, HealthAdaptorFactory healthAdaptorFactory) {
+ this.itemAdaptorFactory = itemAdaptorFactory;
+ this.versionAdaptorFactory = versionAdaptorFactory;
+ this.elementAdaptorFactory = elementAdaptorFactory;
+ this.healthAdaptorFactory = healthAdaptorFactory;
+ }
+
+ @Override
+ public Collection<HealthInfo> checkHealth(SessionContext sessionContext) {
+ return healthAdaptorFactory.createInterface(sessionContext).getHealthStatus(sessionContext);
+ }
+
+ @Override
+ public String getReleaseVersion(SessionContext sessionContext) {
+ return healthAdaptorFactory.createInterface(sessionContext).getVersion();
+ }
+
+ @Override
+ public Collection<Item> listItems(SessionContext context) {
+ Response<Collection<Item>> response = itemAdaptorFactory.createInterface(context).list(context);
+ return getResponseValue(response, "list items");
+ }
+
+ @Override
+ public Item getItem(SessionContext context, Id itemId) {
+ Response<Item> response = itemAdaptorFactory.createInterface(context).get(context, itemId);
+ return getResponseValue(response, String.format("get item %s", itemId));
+ }
+
+
+ @Override
+ public Id createItem(SessionContext context, Info info) {
+ Response<Id> response = itemAdaptorFactory.createInterface(context).create(context, info);
+ return getResponseValue(response, "create item");
+ }
+
+ @Override
+ public void deleteItem(SessionContext context, Id itemId) {
+ Response<Void> response = itemAdaptorFactory.createInterface(context).delete(context, itemId);
+ getResponseValue(response, String.format("get item %s", itemId));
+ }
+
+ @Override
+ public void updateItem(SessionContext context, Id itemId, Info info) {
+ Response<Void> response = itemAdaptorFactory.createInterface(context).update(context, itemId, info);
+ getResponseValue(response, String.format("update item %s", itemId));
+ }
+
+ @Override
+ public Collection<ItemVersion> listPublicVersions(SessionContext context, Id itemId) {
+ Response<Collection<ItemVersion>> response =
+ versionAdaptorFactory.createInterface(context).list(context, Space.PUBLIC, itemId);
+ return getResponseValue(response, String.format("list public versions of item %s", itemId));
+ }
+
+ @Override
+ public ItemVersion getPublicVersion(SessionContext context, Id itemId, Id versionId) {
+ Response<ItemVersion> response =
+ versionAdaptorFactory.createInterface(context).get(context, Space.PUBLIC, itemId, versionId);
+ return getResponseValue(response, String.format("get public version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public Id createVersion(SessionContext context, Id itemId, Id baseVersionId, ItemVersionData itemVersionData) {
+ Response<Id> response =
+ versionAdaptorFactory.createInterface(context).create(context, itemId, baseVersionId, itemVersionData);
+ return getResponseValue(response,
+ String.format("create version for item %s based on version %s", itemId, baseVersionId));
+ }
+
+ @Override
+ public void updateVersion(SessionContext context, Id itemId, Id versionId, ItemVersionData itemVersionData) {
+ Response<Void> response =
+ versionAdaptorFactory.createInterface(context).update(context, itemId, versionId, itemVersionData);
+ getResponseValue(response, String.format("update version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public ItemVersion getVersion(SessionContext context, Id itemId, Id versionId) {
+ Response<ItemVersion> response =
+ versionAdaptorFactory.createInterface(context).get(context, Space.PRIVATE, itemId, versionId);
+ return getResponseValue(response, String.format("get version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public ItemVersionStatus getVersionStatus(SessionContext context, Id itemId, Id versionId) {
+ Response<ItemVersionStatus> response =
+ versionAdaptorFactory.createInterface(context).getStatus(context, itemId, versionId);
+ return getResponseValue(response, String.format("get status of version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public void tagVersion(SessionContext context, Id itemId, Id versionId, Tag tag) {
+ Response<Void> response =
+ versionAdaptorFactory.createInterface(context).tag(context, itemId, versionId, null, tag);
+ getResponseValue(response,
+ String.format("tag version %s of item %s with tag %s", versionId, itemId, tag.getName()));
+ }
+
+ @Override
+ public void resetVersionRevision(SessionContext context, Id itemId, Id versionId, Id revisionId) {
+ Response<Void> response =
+ versionAdaptorFactory.createInterface(context).resetRevision(context, itemId, versionId, revisionId);
+ getResponseValue(response,
+ String.format("reset version %s of item %s to revision %s", versionId, itemId, revisionId));
+ }
+
+ @Override
+ public void revertVersionRevision(SessionContext context, Id itemId, Id versionId, Id revisionId) {
+ Response<Void> response =
+ versionAdaptorFactory.createInterface(context).revertRevision(context, itemId, versionId, revisionId);
+ getResponseValue(response,
+ String.format("revert version %s of item %s to revision %s", versionId, itemId, revisionId));
+ }
+
+ @Override
+ public ItemVersionRevisions listVersionRevisions(SessionContext context, Id itemId, Id versionId) {
+ Response<ItemVersionRevisions> response =
+ versionAdaptorFactory.createInterface(context).listRevisions(context, itemId, versionId);
+ return getResponseValue(response, String.format("list revisions of version %s of item %s", versionId, itemId));
+ }
+
+
+ @Override
+ public void publishVersion(SessionContext context, Id itemId, Id versionId, String message) {
+ Response<Void> response =
+ versionAdaptorFactory.createInterface(context).publish(context, itemId, versionId, message);
+ getResponseValue(response, String.format("publish version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public void syncVersion(SessionContext context, Id itemId, Id versionId) {
+ Response<MergeResult> response =
+ versionAdaptorFactory.createInterface(context).sync(context, itemId, versionId);
+ getResponseValue(response, String.format("sync version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public void forceSyncVersion(SessionContext context, Id itemId, Id versionId) {
+ Response<MergeResult> response =
+ versionAdaptorFactory.createInterface(context).forceSync(context, itemId, versionId);
+ getResponseValue(response, String.format("force sync version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public void cleanVersion(SessionContext context, Id itemId, Id versionId) {
+ Response<Void> response = versionAdaptorFactory.createInterface(context).delete(context, itemId, versionId);
+ getResponseValue(response, String.format("clean version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public ItemVersionConflict getVersionConflict(SessionContext context, Id itemId, Id versionId) {
+ Response<ItemVersionConflict> response =
+ versionAdaptorFactory.createInterface(context).getConflict(context, itemId, versionId);
+ return getResponseValue(response, String.format("get conflict of version %s of item %s", versionId, itemId));
+ }
+
+ @Override
+ public Collection<ElementInfo> listElements(SessionContext context, ElementContext elementContext,
+ Id parentElementId) {
+ Response<Collection<ElementInfo>> response =
+ elementAdaptorFactory.createInterface(context).list(context, elementContext, parentElementId);
+ return getResponseValue(response,
+ String.format("list elements of version %s of item %s", elementContext.getVersionId(),
+ elementContext.getItemId()));
+ }
+
+
+ @Override
+ public ElementInfo getElementInfo(SessionContext context, ElementContext elementContext, Id elementId) {
+ Response<ElementInfo> response =
+ elementAdaptorFactory.createInterface(context).getInfo(context, elementContext, elementId);
+ return getResponseValue(response, String.format("get info of element %s of version %s of item %s", elementId,
+ elementContext.getVersionId(), elementContext.getItemId()));
+ }
+
+ @Override
+ public Element getElement(SessionContext context, ElementContext elementContext, Id elementId) {
+ Response<Element> response =
+ elementAdaptorFactory.createInterface(context).get(context, elementContext, elementId);
+ return getResponseValue(response,
+ String.format("get element %s of version %s of item %s", elementId, elementContext.getVersionId(),
+ elementContext.getItemId()));
+ }
+
+ @Override
+ public ElementConflict getElementConflict(SessionContext context, ElementContext elementContext, Id elementId) {
+ Response<ElementConflict> response =
+ elementAdaptorFactory.createInterface(context).getConflict(context, elementContext, elementId);
+ return getResponseValue(response,
+ String.format("get conflict of element %s of version %s of item %s", elementId,
+ elementContext.getVersionId(), elementContext.getItemId()));
+ }
+
+ @Override
+ public Element saveElement(SessionContext context, ElementContext elementContext, Element element, String message) {
+ Response<Element> response =
+ elementAdaptorFactory.createInterface(context).save(context, elementContext, element, message);
+ return getResponseValue(response,
+ String.format("save element %s of version %s of item %s", element.getElementId(),
+ elementContext.getVersionId(), elementContext.getItemId()));
+ }
+
+ @Override
+ public void resolveElementConflict(SessionContext context, ElementContext elementContext, Element element,
+ Resolution resolution) {
+ Response<Void> response = elementAdaptorFactory.createInterface(context)
+ .resolveConflict(context, elementContext, element, resolution);
+ getResponseValue(response,
+ String.format("resolve conflict of element %s of version %s of item %s", element.getElementId(),
+ elementContext.getVersionId(), elementContext.getItemId()));
+ }
+
+ private <T> T getResponseValue(Response<T> response, String action) {
+ if (!response.isSuccessful()) {
+ throw new ZusammenException(String.format("Failed to %s: %s", action, response.getReturnCode().toString()));
+ }
+ return response.getValue();
+ }
+}
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ElementConvertor.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ElementConvertor.java
new file mode 100644
index 0000000..5a678ce
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ElementConvertor.java
@@ -0,0 +1,15 @@
+package org.onap.sdc.common.zusammen.services;
+
+
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.Element;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementInfo;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ZusammenElement;
+
+public interface ElementConvertor<T> {
+
+ void toElement(T source, ZusammenElement target);
+
+ T fromElement(Element element);
+
+ T fromElementInfo(ElementInfo element);
+}
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ZusammenAdaptor.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ZusammenAdaptor.java
new file mode 100644
index 0000000..6397726
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ZusammenAdaptor.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2016-2018 European Support Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.sdc.common.zusammen.services;
+
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.Element;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementConflict;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementInfo;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ItemVersionConflict;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ZusammenElement;
+import com.amdocs.zusammen.commons.health.data.HealthInfo;
+import com.amdocs.zusammen.datatypes.Id;
+import com.amdocs.zusammen.datatypes.SessionContext;
+import com.amdocs.zusammen.datatypes.item.ElementContext;
+import com.amdocs.zusammen.datatypes.item.Info;
+import com.amdocs.zusammen.datatypes.item.Item;
+import com.amdocs.zusammen.datatypes.item.ItemVersion;
+import com.amdocs.zusammen.datatypes.item.ItemVersionData;
+import com.amdocs.zusammen.datatypes.item.ItemVersionStatus;
+import com.amdocs.zusammen.datatypes.item.Resolution;
+import com.amdocs.zusammen.datatypes.itemversion.ItemVersionRevisions;
+import com.amdocs.zusammen.datatypes.itemversion.Tag;
+import java.util.Collection;
+import java.util.Optional;
+
+public interface ZusammenAdaptor {
+
+ Collection<Item> listItems(SessionContext context);
+
+ Item getItem(SessionContext context, Id itemId);
+
+ void deleteItem(SessionContext context, Id itemId);
+
+ Id createItem(SessionContext context, Info info);
+
+ void updateItem(SessionContext context, Id itemId, Info info);
+
+ Collection<ItemVersion> listPublicVersions(SessionContext context, Id itemId);
+
+ ItemVersion getPublicVersion(SessionContext context, Id itemId, Id versionId);
+
+ Id createVersion(SessionContext context, Id itemId, Id baseVersionId, ItemVersionData itemVersionData);
+
+ void updateVersion(SessionContext context, Id itemId, Id versionId, ItemVersionData itemVersionData);
+
+ ItemVersion getVersion(SessionContext context, Id itemId, Id versionId);
+
+ ItemVersionStatus getVersionStatus(SessionContext context, Id itemId, Id versionId);
+
+ ItemVersionConflict getVersionConflict(SessionContext context, Id itemId, Id versionId);
+
+ void tagVersion(SessionContext context, Id itemId, Id versionId, Tag tag);
+
+ void publishVersion(SessionContext context, Id itemId, Id versionId, String message);
+
+ void syncVersion(SessionContext context, Id itemId, Id versionId);
+
+ void forceSyncVersion(SessionContext context, Id itemId, Id versionId);
+
+ void cleanVersion(SessionContext context, Id itemId, Id versionId);
+
+ Optional<ElementInfo> getElementInfo(SessionContext context, ElementContext elementContext, Id elementId);
+
+ Optional<Element> getElement(SessionContext context, ElementContext elementContext, Id elementId);
+
+ Optional<Element> getElementByName(SessionContext context, ElementContext elementContext, Id parentElementId,
+ String elementName);
+
+ Collection<ElementInfo> listElements(SessionContext context, ElementContext elementContext, Id parentElementId);
+
+ Collection<Element> listElementData(SessionContext context, ElementContext elementContext, Id parentElementId);
+
+ /**
+ * Lists the sub elements of the element named elementName which is a sub element of
+ * parentElementId
+ */
+ Collection<ElementInfo> listElementsByName(SessionContext context, ElementContext elementContext,
+ Id parentElementId, String elementName);
+
+ Optional<ElementInfo> getElementInfoByName(SessionContext context, ElementContext elementContext,
+ Id parentElementId, String elementName);
+
+ Optional<ElementConflict> getElementConflict(SessionContext context, ElementContext elementContext, Id elementId);
+
+ Element saveElement(SessionContext context, ElementContext elementContext, ZusammenElement element, String message);
+
+ void resolveElementConflict(SessionContext context, ElementContext elementContext, ZusammenElement element,
+ Resolution resolution);
+
+ void revert(SessionContext context, Id itemId, Id versionId, Id revisionId);
+
+ ItemVersionRevisions listRevisions(SessionContext context, Id itemId, Id versionId);
+
+ Collection<HealthInfo> checkHealth(SessionContext context);
+
+ String getReleaseVersion(SessionContext context);
+}
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ZusammenElementUtil.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ZusammenElementUtil.java
new file mode 100644
index 0000000..c083ea4
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/ZusammenElementUtil.java
@@ -0,0 +1,27 @@
+package org.onap.sdc.common.zusammen.services;
+
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ZusammenElement;
+import com.amdocs.zusammen.datatypes.Id;
+import com.amdocs.zusammen.datatypes.item.Action;
+import com.amdocs.zusammen.datatypes.item.Info;
+
+public class ZusammenElementUtil {
+
+ public static final String ELEMENT_TYPE_PROPERTY = "elementType";
+
+ public static ZusammenElement buildStructuralElement(String elementType, Action action) {
+ ZusammenElement element = buildElement(null, action);
+ Info info = new Info();
+ info.setName(elementType);
+ info.addProperty(ELEMENT_TYPE_PROPERTY, elementType);
+ element.setInfo(info);
+ return element;
+ }
+
+ public static ZusammenElement buildElement(Id elementId, Action action) {
+ ZusammenElement element = new ZusammenElement();
+ element.setElementId(elementId);
+ element.setAction(action);
+ return element;
+ }
+}
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/exceptions/ZusammenException.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/exceptions/ZusammenException.java
new file mode 100644
index 0000000..4e7f06f
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/exceptions/ZusammenException.java
@@ -0,0 +1,8 @@
+package org.onap.sdc.common.zusammen.services.exceptions;
+
+public class ZusammenException extends RuntimeException {
+
+ public ZusammenException(String message) {
+ super(message);
+ }
+}
diff --git a/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/impl/ZusammenAdaptorImpl.java b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/impl/ZusammenAdaptorImpl.java
new file mode 100644
index 0000000..1624e49
--- /dev/null
+++ b/zusammen-lib/src/main/java/org/onap/sdc/common/zusammen/services/impl/ZusammenAdaptorImpl.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright © 2016-2017 European Support Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.sdc.common.zusammen.services.impl;
+
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.Element;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementConflict;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementInfo;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ItemVersionConflict;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ZusammenElement;
+import com.amdocs.zusammen.commons.health.data.HealthInfo;
+import com.amdocs.zusammen.datatypes.Id;
+import com.amdocs.zusammen.datatypes.SessionContext;
+import com.amdocs.zusammen.datatypes.item.Action;
+import com.amdocs.zusammen.datatypes.item.ElementContext;
+import com.amdocs.zusammen.datatypes.item.Info;
+import com.amdocs.zusammen.datatypes.item.Item;
+import com.amdocs.zusammen.datatypes.item.ItemVersion;
+import com.amdocs.zusammen.datatypes.item.ItemVersionData;
+import com.amdocs.zusammen.datatypes.item.ItemVersionStatus;
+import com.amdocs.zusammen.datatypes.item.Resolution;
+import com.amdocs.zusammen.datatypes.itemversion.ItemVersionRevisions;
+import com.amdocs.zusammen.datatypes.itemversion.Tag;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import org.onap.sdc.common.zusammen.persistence.ZusammenConnector;
+import org.onap.sdc.common.zusammen.services.ZusammenAdaptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class ZusammenAdaptorImpl implements ZusammenAdaptor {
+
+ private final ZusammenConnector connector;
+
+ @Autowired
+ public ZusammenAdaptorImpl(ZusammenConnector connector) {
+ this.connector = connector;
+ }
+
+ @Override
+ public Optional<ElementInfo> getElementInfo(SessionContext context, ElementContext elementContext, Id elementId) {
+ return Optional.ofNullable(connector.getElementInfo(context, elementContext, elementId));
+ }
+
+ @Override
+ public Optional<Element> getElement(SessionContext context, ElementContext elementContext, Id elementId) {
+ return Optional.ofNullable(connector.getElement(context, elementContext, elementId));
+ }
+
+ @Override
+ public Optional<Element> getElementByName(SessionContext context, ElementContext elementContext, Id parentElementId,
+ String elementName) {
+ Collection<ElementInfo> elementInfos = connector.listElements(context, elementContext, parentElementId);
+ Predicate<ElementInfo> elementInfoPredicate =
+ elementInfo -> elementInfo.getInfo() != null && elementName.equals(elementInfo.getInfo().getName());
+ return getFirstElementInfo(elementInfos, elementInfoPredicate)
+ .flatMap(elementInfo -> getElement(context, elementContext, elementInfo.getId()));
+ }
+
+ @Override
+ public Collection<ElementInfo> listElements(SessionContext context, ElementContext elementContext,
+ Id parentElementId) {
+ return connector.listElements(context, elementContext, parentElementId);
+ }
+
+ @Override
+ public Collection<Element> listElementData(SessionContext context, ElementContext elementContext,
+ Id parentElementId) {
+ Collection<ElementInfo> elementInfoList = connector.listElements(context, elementContext, parentElementId);
+
+ return elementInfoList == null ? new ArrayList<>() : elementInfoList.stream().map(elementInfo -> connector
+ .getElement(
+ context,
+ elementContext,
+ elementInfo
+ .getId()))
+ .collect(Collectors.toList());
+ }
+
+
+ @Override
+ public Collection<ElementInfo> listElementsByName(SessionContext context, ElementContext elementContext,
+ Id parentElementId, String elementName) {
+ Optional<ElementInfo> elementInfoByName =
+ getElementInfoByName(context, elementContext, parentElementId, elementName);
+
+ return elementInfoByName.isPresent() ?
+ connector.listElements(context, elementContext, elementInfoByName.get().getId()) :
+ new ArrayList<>();
+ }
+
+ @Override
+ public Optional<ElementInfo> getElementInfoByName(SessionContext context, ElementContext elementContext,
+ Id parentElementId, String elementName) {
+ Collection<ElementInfo> elementInfos = connector.listElements(context, elementContext, parentElementId);
+ return getFirstElementInfo(elementInfos,
+ elementInfo -> elementInfo.getInfo() != null && elementName.equals(elementInfo.getInfo().getName()));
+ }
+
+ @Override
+ public Optional<ElementConflict> getElementConflict(SessionContext context, ElementContext elementContext,
+ Id elementId) {
+ return Optional.ofNullable(connector.getElementConflict(context, elementContext, elementId));
+ }
+
+ @Override
+ public Element saveElement(SessionContext context, ElementContext elementContext, ZusammenElement element,
+ String message) {
+ enrichElementHierarchyRec(context, elementContext, null, element);
+ return connector.saveElement(context, elementContext, element, message);
+ }
+
+ @Override
+ public void resolveElementConflict(SessionContext context, ElementContext elementContext, ZusammenElement element,
+ Resolution resolution) {
+ connector.resolveElementConflict(context, elementContext, element, resolution);
+ }
+
+ private void enrichElementHierarchyRec(SessionContext context, ElementContext elementContext, Id parentElementId,
+ ZusammenElement element) {
+ if (element.getAction() == Action.CREATE) {
+ return;
+ }
+ locateElementAndUpdateAction(context, elementContext, parentElementId, element);
+ element.getSubElements().forEach(
+ subElement -> enrichElementHierarchyRec(context, elementContext, element.getElementId(),
+ (ZusammenElement) subElement));
+ }
+
+ // should be applied only for structural elements
+ private void locateElementAndUpdateAction(SessionContext context, ElementContext elementContext, Id parentElementId,
+ ZusammenElement element) {
+ if (element.getElementId() != null) {
+ return;
+ }
+ if (element.getInfo() == null || element.getInfo().getName() == null) {
+ throw new IllegalArgumentException("When saving element to zusammen - its Id or name must be supplied");
+ }
+ Optional<ElementInfo> elementInfo =
+ getElementInfoByName(context, elementContext, parentElementId, element.getInfo().getName());
+ if (elementInfo.isPresent()) {
+ element.setElementId(elementInfo.get().getId());
+ if (element.getAction() == null) {
+ element.setAction(Action.IGNORE);
+ }
+ } else {
+ element.setAction(Action.CREATE);
+ }
+ }
+
+ private Optional<ElementInfo> getFirstElementInfo(Collection<ElementInfo> elementInfos,
+ Predicate<ElementInfo> elementInfoPredicate) {
+ return elementInfos.stream().filter(elementInfoPredicate).findFirst();
+ }
+
+ @Override
+ public Collection<Item> listItems(SessionContext context) {
+ return connector.listItems(context);
+ }
+
+ @Override
+ public Item getItem(SessionContext context, Id itemId) {
+ return connector.getItem(context, itemId);
+ }
+
+ @Override
+ public Id createItem(SessionContext context, Info info) {
+ return connector.createItem(context, info);
+ }
+
+ @Override
+ public void deleteItem(SessionContext context, Id itemId) {
+ connector.deleteItem(context, itemId);
+ }
+
+ @Override
+ public void updateItem(SessionContext context, Id itemId, Info info) {
+ connector.updateItem(context, itemId, info);
+ }
+
+ @Override
+ public Collection<ItemVersion> listPublicVersions(SessionContext context, Id itemId) {
+ return connector.listPublicVersions(context, itemId);
+ }
+
+ @Override
+ public ItemVersion getPublicVersion(SessionContext context, Id itemId, Id versionId) {
+ return connector.getPublicVersion(context, itemId, versionId);
+ }
+
+ @Override
+ public ItemVersion getVersion(SessionContext context, Id itemId, Id versionId) {
+ return connector.getVersion(context, itemId, versionId);
+ }
+
+ @Override
+ public ItemVersionStatus getVersionStatus(SessionContext context, Id itemId, Id versionId) {
+ return connector.getVersionStatus(context, itemId, versionId);
+ }
+
+ @Override
+ public ItemVersionConflict getVersionConflict(SessionContext context, Id itemId, Id versionId) {
+ return connector.getVersionConflict(context, itemId, versionId);
+ }
+
+ @Override
+ public Id createVersion(SessionContext context, Id itemId, Id baseVersionId, ItemVersionData itemVersionData) {
+ return connector.createVersion(context, itemId, baseVersionId, itemVersionData);
+ }
+
+ @Override
+ public void updateVersion(SessionContext context, Id itemId, Id versionId, ItemVersionData itemVersionData) {
+ connector.updateVersion(context, itemId, versionId, itemVersionData);
+ }
+
+ @Override
+ public void tagVersion(SessionContext context, Id itemId, Id versionId, Tag tag) {
+ connector.tagVersion(context, itemId, versionId, tag);
+ }
+
+ @Override
+ public void publishVersion(SessionContext context, Id itemId, Id versionId, String message) {
+ connector.publishVersion(context, itemId, versionId, message);
+ }
+
+ @Override
+ public void syncVersion(SessionContext context, Id itemId, Id versionId) {
+ connector.syncVersion(context, itemId, versionId);
+ }
+
+ @Override
+ public void forceSyncVersion(SessionContext context, Id itemId, Id versionId) {
+ connector.forceSyncVersion(context, itemId, versionId);
+ }
+
+ @Override
+ public void cleanVersion(SessionContext context, Id itemId, Id versionId) {
+ connector.cleanVersion(context, itemId, versionId);
+ }
+
+ @Override
+ public void revert(SessionContext context, Id itemId, Id versionId, Id revisionId) {
+ connector.revertVersionRevision(context, itemId, versionId, revisionId);
+ }
+
+ @Override
+ public ItemVersionRevisions listRevisions(SessionContext context, Id itemId, Id versionId) {
+ return connector.listVersionRevisions(context, itemId, versionId);
+ }
+
+ @Override
+ public Collection<HealthInfo> checkHealth(SessionContext context) {
+ return connector.checkHealth(context);
+ }
+
+ @Override
+ public String getReleaseVersion(SessionContext context) {
+ return connector.getReleaseVersion(context);
+ }
+}
diff --git a/zusammen-lib/src/test/java/org/onap/sdc/common/zusammen/services/impl/ZusammenAdaptorImplTest.java b/zusammen-lib/src/test/java/org/onap/sdc/common/zusammen/services/impl/ZusammenAdaptorImplTest.java
new file mode 100644
index 0000000..a6ddbc5
--- /dev/null
+++ b/zusammen-lib/src/test/java/org/onap/sdc/common/zusammen/services/impl/ZusammenAdaptorImplTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright © 2016-2018 European Support Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onap.sdc.common.zusammen.services.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.Element;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ElementInfo;
+import com.amdocs.zusammen.adaptor.inbound.api.types.item.ZusammenElement;
+import com.amdocs.zusammen.datatypes.Id;
+import com.amdocs.zusammen.datatypes.SessionContext;
+import com.amdocs.zusammen.datatypes.item.Action;
+import com.amdocs.zusammen.datatypes.item.ElementContext;
+import com.amdocs.zusammen.datatypes.item.Info;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.sdc.common.zusammen.persistence.ZusammenConnector;
+
+public class ZusammenAdaptorImplTest {
+
+ private static final SessionContext CONTEXT = new SessionContext();
+ private static final ElementContext ELEMENT_CONTEXT = new ElementContext();
+ private static final Id ELEMENT_ID = new Id("elementId 0");
+ private static final List<ElementInfo> ELEMENTS =
+ Arrays.asList(createElementInfo("elementId1", "element1"), createElementInfo("elementId2", "element2"),
+ createElementInfo("elementId3", "element3"));
+
+ @Mock
+ private ZusammenConnector connector;
+ @InjectMocks
+ private ZusammenAdaptorImpl zusammenAdaptor;
+ @Captor
+ private ArgumentCaptor<Element> savedElementArg;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void getEmptyWhenElementNameNotExist() {
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ Optional<Element> element =
+ zusammenAdaptor.getElementByName(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID, "nonExistingName");
+
+ assertFalse(element.isPresent());
+ }
+
+ @Test
+ public void getEmptyInfoWhenElementNameNotExist() {
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ Optional<ElementInfo> elementInfo =
+ zusammenAdaptor.getElementInfoByName(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID, "nonExistingName");
+
+ assertFalse(elementInfo.isPresent());
+ }
+
+ @Test
+ public void getElementWhenItsNameExist() {
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+ ZusammenElement returnedElement = new ZusammenElement();
+ doReturn(returnedElement).when(connector).getElement(CONTEXT, ELEMENT_CONTEXT, ELEMENTS.get(1).getId());
+
+ Optional<Element> element = zusammenAdaptor.getElementByName(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID, "element2");
+
+ assertTrue(element.isPresent());
+ assertEquals(returnedElement, element.get());
+ }
+
+ @Test
+ public void getElementInfoWhenItsNameExist() {
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ Optional<ElementInfo> elementInfo =
+ zusammenAdaptor.getElementInfoByName(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID, "element2");
+
+ assertTrue(elementInfo.isPresent());
+ assertEquals(ELEMENTS.get(1), elementInfo.get());
+
+ }
+
+ @Test
+ public void listElementsWhenTheirParentIdExist() {
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ List<ZusammenElement> returnedElements =
+ Arrays.asList(new ZusammenElement(), new ZusammenElement(), new ZusammenElement());
+ doReturn(returnedElements.get(0)).when(connector).getElement(CONTEXT, ELEMENT_CONTEXT, ELEMENTS.get(0).getId());
+ doReturn(returnedElements.get(1)).when(connector).getElement(CONTEXT, ELEMENT_CONTEXT, ELEMENTS.get(1).getId());
+ doReturn(returnedElements.get(2)).when(connector).getElement(CONTEXT, ELEMENT_CONTEXT, ELEMENTS.get(2).getId());
+
+ Collection<Element> elements = zusammenAdaptor.listElementData(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ assertEquals(returnedElements, elements);
+ }
+
+ @Test
+ public void getEmptyListWhenParentElementNameNotExist() {
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ Collection<ElementInfo> elements =
+ zusammenAdaptor.listElementsByName(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID, "nonExistingName");
+
+ assertTrue(elements.isEmpty());
+ }
+
+ @Test
+ public void listElementsInfoWhenTheirParentElementNameExist() {
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ List<ElementInfo> returnedElements = Arrays.asList(new ElementInfo(), new ElementInfo());
+ doReturn(returnedElements).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENTS.get(1).getId());
+
+ Collection<ElementInfo> elements =
+ zusammenAdaptor.listElementsByName(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID, "element2");
+
+ assertEquals(returnedElements, elements);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void failWhenSavingElementWithoutIdNameOrAction() {
+ zusammenAdaptor.saveElement(CONTEXT, ELEMENT_CONTEXT, new ZusammenElement(), "Illegal element save");
+ }
+
+ @Test
+ public void saveElementAsRootWhenParentIdNotSupplied() {
+ String message = "Create new element tree";
+ ZusammenElement element = new ZusammenElement();
+ element.setAction(Action.CREATE);
+
+ ZusammenElement subElement = new ZusammenElement();
+ subElement.setAction(Action.CREATE);
+ element.addSubElement(subElement);
+
+ testSaveElement(message, element);
+
+ verify(connector).saveElement(CONTEXT, ELEMENT_CONTEXT, element, message);
+ }
+
+ @Test
+ public void saveElementAsSubWhenParentIdSupplied() {
+ String message = "Create sub element";
+ ZusammenElement element = new ZusammenElement();
+ element.setAction(Action.IGNORE);
+ element.setElementId(ELEMENT_ID);
+
+ ZusammenElement subElement = new ZusammenElement();
+ subElement.setAction(Action.CREATE);
+ element.addSubElement(subElement);
+
+ testSaveElement(message, element);
+
+ verify(connector).saveElement(CONTEXT, ELEMENT_CONTEXT, element, message);
+ }
+
+ @Test
+ public void saveElementWhenItsIdSupplied() {
+ String message = "Update element";
+ ZusammenElement element = new ZusammenElement();
+ element.setAction(Action.UPDATE);
+ element.setElementId(new Id("id"));
+
+ testSaveElement(message, element);
+
+ verify(connector).saveElement(CONTEXT, ELEMENT_CONTEXT, element, message);
+ }
+
+ @Test
+ public void saveRootElementWhenItsNameSupplied() {
+ String message = "Update element";
+ ZusammenElement element = new ZusammenElement();
+ element.setAction(Action.UPDATE);
+ element.setInfo(ELEMENTS.get(1).getInfo());
+
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, null);
+
+ testSaveElement(message, element);
+
+ verify(connector).saveElement(eq(CONTEXT), eq(ELEMENT_CONTEXT), savedElementArg.capture(), eq(message));
+
+ Element savedElement = savedElementArg.getValue();
+ assertEquals(element, savedElement);
+ assertNotNull(savedElement.getElementId());
+ }
+
+ @Test
+ public void saveElementWhenItsNameAndParentIdSupplied() {
+ String message = "Update existing element";
+ ZusammenElement element = new ZusammenElement();
+ element.setAction(Action.IGNORE);
+ element.setElementId(ELEMENT_ID);
+
+ ZusammenElement existingSub = new ZusammenElement();
+ existingSub.setAction(Action.UPDATE);
+ existingSub.setInfo(ELEMENTS.get(2).getInfo());
+ element.addSubElement(existingSub);
+
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ testSaveElement(message, element);
+
+ verify(connector).saveElement(eq(CONTEXT), eq(ELEMENT_CONTEXT), savedElementArg.capture(), eq(message));
+
+ Element savedElement = savedElementArg.getValue();
+ assertEquals(element, savedElement);
+
+ Element updated = savedElement.getSubElements().iterator().next();
+ assertNotNull(updated.getElementId());
+ assertEquals(Action.UPDATE, updated.getAction());
+ }
+
+ @Test
+ public void saveElementWithCreateActionInsteadOfUpdateWhenItDoesNotExist() {
+ String message = "Create new element";
+ ZusammenElement element = new ZusammenElement();
+ element.setAction(Action.IGNORE);
+ element.setElementId(ELEMENT_ID);
+
+ ZusammenElement nonExistingSub = new ZusammenElement();
+ nonExistingSub.setAction(Action.UPDATE);
+ Info info = new Info();
+ info.setName("nonExistingName");
+ nonExistingSub.setInfo(info);
+
+ element.addSubElement(nonExistingSub);
+
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ testSaveElement(message, element);
+
+ verify(connector).saveElement(eq(CONTEXT), eq(ELEMENT_CONTEXT), savedElementArg.capture(), eq(message));
+
+ Element savedElement = savedElementArg.getValue();
+ assertEquals(element, savedElement);
+
+ Element created = savedElement.getSubElements().iterator().next();
+ assertNull(created.getElementId());
+ assertEquals(Action.CREATE, created.getAction());
+ }
+
+ @Test
+ public void saveElementWithIgnoreActionWhenItExistAndActionNotSupplied() {
+ String message = "save existing element";
+ ZusammenElement element = new ZusammenElement();
+ element.setAction(Action.IGNORE);
+ element.setElementId(ELEMENT_ID);
+
+ ZusammenElement existingSub = new ZusammenElement();
+ existingSub.setInfo(ELEMENTS.get(2).getInfo());
+ element.addSubElement(existingSub);
+
+ doReturn(ELEMENTS).when(connector).listElements(CONTEXT, ELEMENT_CONTEXT, ELEMENT_ID);
+
+ testSaveElement(message, element);
+
+ verify(connector).saveElement(eq(CONTEXT), eq(ELEMENT_CONTEXT), savedElementArg.capture(), eq(message));
+
+ Element savedElement = savedElementArg.getValue();
+ assertEquals(element, savedElement);
+
+ Element ignored = savedElement.getSubElements().iterator().next();
+ assertNotNull(ignored.getElementId());
+ assertEquals(Action.IGNORE, ignored.getAction());
+ }
+
+ private void testSaveElement(String message, ZusammenElement element) {
+ ZusammenElement returnedElement = new ZusammenElement();
+ doReturn(returnedElement).when(connector).saveElement(CONTEXT, ELEMENT_CONTEXT, element, message);
+
+ Element saved = zusammenAdaptor.saveElement(CONTEXT, ELEMENT_CONTEXT, element, message);
+
+ assertEquals(returnedElement, saved);
+ }
+
+ private static ElementInfo createElementInfo(String id, String name) {
+ ElementInfo elementInfo = new ElementInfo();
+ elementInfo.setId(new Id(id));
+ Info info = new Info();
+ info.setName(name);
+ elementInfo.setInfo(info);
+ return elementInfo;
+ }
+} \ No newline at end of file