diff options
Diffstat (limited to 'zusammen-lib')
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 — Cassandra hostname or IP address. + +- CS_PORT — Cassandra Thrift client port. If not specified, the default of 9160 will be used. + +- CS_AUTHENTICATE — whether password authentication must be used to connect to Cassandra. A *false* will be +assumed if this variable is not specified. + +- CS_USER — Cassandra username if CS_AUTHENTICATE is *true*. + +- CS_PASSWORD — 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 — 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 — SSL key password if SERVER_SSL_ENABLED is *true*. + +- SERVER_SSL_KEYSTORE_PATH — SSL Keystore path if SERVER_SSL_ENABLED is *true*. + +- SERVER_SSL_KEYSTORE_TYPE — SSL Keystore type if SERVER_SSL_ENABLED is *true*. + +- CS_HOSTS — comma-separated list of Cassandra hostnames or IP addresses. + +- CS_PORT — CQL native client port. If not specified, the default of 9042 will be used. + +- CS_AUTHENTICATE — whether password authentication must be used to connect to Cassandra. A *false* will be +assumed if this variable is not specified. + +- CS_USER — Cassandra username if CS_AUTHENTICATE is *true*. + +- CS_PASSWORD — Cassandra password if CS_AUTHENTICATE is *true*. + +- CS_SSL_ENABLED — 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 — Cassandra Truststore path if CS_SSL_ENABLED is *true*. + +- CS_TRUST_STORE_PASSWORD — Cassandra Truststore password if CS_SSL_ENABLED is *true*. + +- TRANSLATION_PROTOCOL — protocol to be used for calling Translation APIs (http or https). + +- TRANSLATION_HOST — a Translation server. + +- TRANSLATION_PORT — a Translation server port, usually 8080. + +- SDC_PROTOCOL — protocol to be used for calling SDC APIs (http or https). + +- SDC_HOST — a SDC backend server. + +- SDC_PORT — a SDC backend server port, usually 8080. + +- SDC_USER — Onboarding consumer username + +- SDC_PASSWORD — Onboarding consumer password + +- JAVA_OPTIONS — 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 — root endpoint of the RESTful APIs exposed by a backend server. + +- JAVA_OPTIONS — 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 |