Add synchronization DTO and implement multicloud client 23/127223/26
authorGrzegorz Wielgosinski <g.wielgosins@samsung.com>
Mon, 21 Feb 2022 13:18:54 +0000 (14:18 +0100)
committerLukasz Rajewski <lukasz.rajewski@orange.com>
Wed, 9 Mar 2022 09:21:44 +0000 (10:21 +0100)
Issue-ID: SO-3845
Signed-off-by: Grzegorz Wielgosinski <g.wielgosins@samsung.com>
Signed-off-by: Lukasz Rajewski <lukasz.rajewski@orange.com>
Change-Id: I397c3592fe96af98405acf8fe43d87ea325f1745

14 files changed:
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/client/MulticloudApiUrl.java
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/client/MulticloudClient.java
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/NotificationRequest.java [new file with mode: 0644]
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/SubscriptionRequest.java [new file with mode: 0644]
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/SubscriptionResponse.java [new file with mode: 0644]
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/rest/CnfAdapterRest.java
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/rest/SubscriptionNotifyController.java [new file with mode: 0644]
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/CnfAdapterService.java
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/synchrornization/SubscriptionsRecoveryProvider.java [new file with mode: 0644]
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/synchrornization/SynchronizationService.java [new file with mode: 0644]
so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/client/MulticloudClientTest.java
so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/service/CnfAdapterServiceTest.java
so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/service/synchrornization/SubscriptionsRecoveryProvider.java [new file with mode: 0644]
so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/service/synchrornization/SynchronizationServiceTest.java [new file with mode: 0644]

index d21b97a..617eef7 100644 (file)
@@ -13,7 +13,11 @@ class MulticloudApiUrl {
     }
 
     String apiUrl(String instanceId) {
-        return multicloudConfiguration.getMulticloudUrl() + "/v1/instance/" + instanceId;
+        String url = multicloudConfiguration.getMulticloudUrl() + "/v1/instance";
+        if (!instanceId.isEmpty()) {
+            url += "/" + instanceId;
+        }
+        return url;
     }
 
 }
\ No newline at end of file
index 5edb34c..0f5df15 100644 (file)
@@ -23,11 +23,13 @@ package org.onap.so.adapters.cnf.client;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import org.onap.so.adapters.cnf.MulticloudConfiguration;
+import org.onap.so.adapters.cnf.model.InstanceResponse;
 import org.onap.so.adapters.cnf.model.MulticloudInstanceRequest;
 import org.onap.so.adapters.cnf.model.healthcheck.K8sRbInstanceHealthCheck;
 import org.onap.so.adapters.cnf.model.healthcheck.K8sRbInstanceHealthCheckSimple;
 import org.onap.so.adapters.cnf.model.statuscheck.K8sRbInstanceStatus;
+import org.onap.so.adapters.cnf.model.synchronization.SubscriptionRequest;
+import org.onap.so.adapters.cnf.model.synchronization.SubscriptionResponse;
 import org.onap.so.client.exception.BadResponseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,9 +39,11 @@ import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Component;
+import org.springframework.web.client.HttpServerErrorException;
 import org.springframework.web.client.RestTemplate;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static org.springframework.http.HttpMethod.DELETE;
@@ -61,6 +65,78 @@ public class MulticloudClient {
         this.objectMapper = new ObjectMapper();
     }
 
+    public List<InstanceResponse> getAllInstances() throws BadResponseException {
+        String endpoint = multicloudApiUrl.apiUrl("");
+        ResponseEntity<String> result = restTemplate.exchange(endpoint, GET, getHttpEntity(), String.class);
+        checkResponseStatusCode(result);
+        log.info("getAllInstances response status: {}", result.getStatusCode());
+        String body = result.getBody();
+        log.debug("getAllInstances response body: {}", body);
+
+        try {
+            return Arrays.asList(objectMapper.readValue(body, InstanceResponse[].class));
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public SubscriptionResponse registerSubscription(String instanceId, SubscriptionRequest subscriptionRequest) throws BadResponseException {
+        String endpoint = multicloudApiUrl.apiUrl(instanceId) + "/status/subscription";
+        ResponseEntity<String> result = restTemplate.exchange(endpoint, POST, getHttpEntity(subscriptionRequest), String.class);
+        checkResponseStatusCode(result);
+        log.info("registerSubscription response status: {}", result.getStatusCode());
+        String body = result.getBody();
+        log.debug("registerSubscription response body: {}", body);
+
+        try {
+            return objectMapper.readValue(body, SubscriptionResponse.class);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Boolean hasSubscription(String instanceId, String subscriptionId) throws BadResponseException {
+        String endpoint = multicloudApiUrl.apiUrl(instanceId) + "/status/subscription/" + subscriptionId;
+        try {
+            ResponseEntity<String> result = restTemplate.exchange(endpoint, GET, getHttpEntity(), String.class);
+            checkResponseStatusCode(result);
+            log.info("hasSubscription response status: {}", result.getStatusCode());
+            return true;
+        } catch (HttpServerErrorException e) {
+            if (e.getMessage().contains("no documents")) {
+                log.info("hasSubscription response status: {}", 500);
+                return false;
+            } else
+                throw e;
+        }
+    }
+
+    public SubscriptionResponse getSubscription(String instanceId, String subscriptionId) throws BadResponseException {
+        String endpoint = multicloudApiUrl.apiUrl(instanceId) + "/status/subscription/" + subscriptionId;
+        try {
+            ResponseEntity<String> result = restTemplate.exchange(endpoint, GET, getHttpEntity(), String.class);
+            checkResponseStatusCode(result);
+            log.info("getSubscription response status: {}", result.getStatusCode());
+            String body = result.getBody();
+            log.debug("getSubscription response body: {}", body);
+            return objectMapper.readValue(body, SubscriptionResponse.class);
+        } catch (HttpServerErrorException e) {
+            if (e.getMessage().contains("no documents")) {
+                throw new BadResponseException("Multicloud response status error 404");
+            } else
+                throw e;
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void deleteSubscription(String instanceId, String subscriptionId) throws BadResponseException {
+        String endpoint = multicloudApiUrl.apiUrl(instanceId) + "/status/subscription/" + subscriptionId;
+        ResponseEntity<String> result = restTemplate.exchange(endpoint, DELETE, getHttpEntity(), String.class);
+        checkResponseStatusCode(result);
+        log.info("deleteSubscription response status: {}", result.getStatusCode());
+    }
+
     public String upgradeInstance(String instanceId, MulticloudInstanceRequest upgradeRequest) throws BadResponseException {
         String endpoint = multicloudApiUrl.apiUrl(instanceId) + "/upgrade";
         ResponseEntity<String> result = restTemplate.exchange(endpoint, POST, getHttpEntity(upgradeRequest), String.class);
diff --git a/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/NotificationRequest.java b/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/NotificationRequest.java
new file mode 100644 (file)
index 0000000..e0b021f
--- /dev/null
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved.
+ * Modifications Copyright (C) 2021 Samsung Technologies Co.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.cnf.model.synchronization;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.onap.so.adapters.cnf.model.aai.AaiRequest;
+
+import java.util.Map;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(value = "true")
+public class NotificationRequest {
+
+    @JsonProperty("instance-id")
+    private String instanceId;
+    @JsonProperty("subscription-name")
+    private String subscriptionName;
+    @JsonProperty("metadata")
+    private AaiRequest metadata;
+
+    public NotificationRequest() { }
+
+    public String getInstanceId() {
+        return instanceId;
+    }
+
+    public void setInstanceId(String instanceId) {
+        this.instanceId = instanceId;
+    }
+
+    public String getSubscriptionName() {
+        return subscriptionName;
+    }
+
+    public void setSubscriptionName(String subscriptionName) {
+        this.subscriptionName = subscriptionName;
+    }
+
+    public AaiRequest getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(AaiRequest metadata) {
+        this.metadata = metadata;
+    }
+
+    @Override
+    public String toString() {
+        return "NotificationRequest{" +
+                "instanceId='" + instanceId + '\'' +
+                ", subscriptionName='" + subscriptionName + '\'' +
+                ", metadata=" + metadata +
+                '}';
+    }
+}
diff --git a/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/SubscriptionRequest.java b/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/SubscriptionRequest.java
new file mode 100644 (file)
index 0000000..238eef5
--- /dev/null
@@ -0,0 +1,85 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved.
+ * Modifications Copyright (C) 2021 Samsung Technologies Co.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.cnf.model.synchronization;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.onap.so.adapters.cnf.model.aai.AaiRequest;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(value = "true")
+public class SubscriptionRequest {
+
+    @JsonProperty(value = "name")
+    private String name;
+
+    @JsonProperty(value = "callback-url")
+    private String callbackUrl;
+
+    @JsonProperty(value = "min-notify-interval")
+    private int minNotifyInterval;
+
+    @JsonProperty(value = "metadata")
+    private AaiRequest metadata;
+
+    public String getCallbackUrl() {
+        return callbackUrl;
+    }
+
+    public void setCallbackUrl(String callbackUrl) {
+        this.callbackUrl = callbackUrl;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getMinNotifyInterval() {
+        return minNotifyInterval;
+    }
+
+    public void setMinNotifyInterval(int minNotifyInterval) {
+        this.minNotifyInterval = minNotifyInterval;
+    }
+
+    public AaiRequest getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(AaiRequest metadata) {
+        this.metadata = metadata;
+    }
+
+    @Override
+    public String toString() {
+        return "SubscriptionRequest{" +
+                "callbackUrl='" + callbackUrl + '\'' +
+                ", name='" + name + '\'' +
+                ", minNotifyInterval=" + minNotifyInterval +
+                ", metadata='" + metadata + '\'' +
+                '}';
+    }
+}
diff --git a/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/SubscriptionResponse.java b/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/synchronization/SubscriptionResponse.java
new file mode 100644 (file)
index 0000000..5f05126
--- /dev/null
@@ -0,0 +1,118 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved.
+ * Modifications Copyright (C) 2021 Samsung Technologies Co.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.cnf.model.synchronization;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Date;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class SubscriptionResponse {
+
+    @JsonProperty("callback-url")
+    private String callbackUrl;
+    @JsonProperty("name")
+    private String name;
+    @JsonProperty("last-update-time")
+    private Date lastUpdateTime;
+    @JsonProperty("last-notify-time")
+    private Date lastNotifyTime;
+    @JsonProperty("last-notify-status")
+    private int lastNotifyStatus;
+    @JsonProperty("min-notify-interval")
+    private int minNotifyInterval;
+    @JsonProperty("metadata ")
+    private String metadata;
+
+    public SubscriptionResponse() { }
+
+    public String getCallbackUrl() {
+        return callbackUrl;
+    }
+
+    public void setCallbackUrl(String callbackUrl) {
+        this.callbackUrl = callbackUrl;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Date getLastUpdateTime() {
+        return lastUpdateTime;
+    }
+
+    public void setLastUpdateTime(Date lastUpdateTime) {
+        this.lastUpdateTime = lastUpdateTime;
+    }
+
+    public Date getLastNotifyTime() {
+        return lastNotifyTime;
+    }
+
+    public void setLastNotifyTime(Date lastNotifyTime) {
+        this.lastNotifyTime = lastNotifyTime;
+    }
+
+    public int getLastNotifyStatus() {
+        return lastNotifyStatus;
+    }
+
+    public void setLastNotifyStatus(int lastNotifyStatus) {
+        this.lastNotifyStatus = lastNotifyStatus;
+    }
+
+    public int getMinNotifyInterval() {
+        return minNotifyInterval;
+    }
+
+    public void setMinNotifyInterval(int minNotifyInterval) {
+        this.minNotifyInterval = minNotifyInterval;
+    }
+
+    public String getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(String metadata) {
+        this.metadata = metadata;
+    }
+
+    @Override
+    public String toString() {
+        return "SubscriptionResponse{" +
+                "callbackUrl='" + callbackUrl + '\'' +
+                ", name='" + name + '\'' +
+                ", lastUpdateTime=" + lastUpdateTime +
+                ", lastNotifyTime=" + lastNotifyTime +
+                ", lastNotifyStatus=" + lastNotifyStatus +
+                ", minNotifyInterval=" + minNotifyInterval +
+                ", metadata='" + metadata + '\'' +
+                '}';
+    }
+}
index 3272ad9..df674f8 100644 (file)
@@ -59,6 +59,7 @@ import org.onap.so.adapters.cnf.service.CnfAdapterService;
 import org.onap.so.adapters.cnf.service.aai.AaiService;
 import org.onap.so.adapters.cnf.service.healthcheck.HealthCheckService;
 import org.onap.so.adapters.cnf.service.statuscheck.SimpleStatusCheckService;
+import org.onap.so.adapters.cnf.service.synchrornization.SynchronizationService;
 import org.onap.so.adapters.cnf.service.upgrade.InstanceUpgradeService;
 import org.onap.so.client.exception.BadResponseException;
 import org.slf4j.Logger;
@@ -89,6 +90,7 @@ public class CnfAdapterRest {
     private final CnfAdapterService cnfAdapterService;
     private final SoCallbackClient callbackClient;
     private final AaiService aaiService;
+    private final SynchronizationService synchronizationService;
     private final String uri;
 
     @Autowired
@@ -98,6 +100,7 @@ public class CnfAdapterRest {
                           CnfAdapterService cnfAdapterService,
                           SoCallbackClient callbackClient,
                           AaiService aaiService,
+                          SynchronizationService synchronizationService,
                           MulticloudConfiguration multicloudConfiguration) {
         this.simpleStatusCheckService = simpleStatusCheckService;
         this.healthCheckService = healthCheckService;
@@ -105,6 +108,7 @@ public class CnfAdapterRest {
         this.cnfAdapterService = cnfAdapterService;
         this.aaiService = aaiService;
         this.callbackClient = callbackClient;
+        this.synchronizationService = synchronizationService;
         this.uri = multicloudConfiguration.getMulticloudUrl();
     }
 
@@ -154,6 +158,7 @@ public class CnfAdapterRest {
             AaiCallbackResponse callbackResponse = new AaiCallbackResponse();
             try {
                 aaiService.aaiUpdate(aaiRequest);
+                synchronizationService.createSubscriptionIfNotExists(aaiRequest);
                 callbackResponse.setCompletionStatus(AaiCallbackResponse.CompletionStatus.COMPLETED);
             } catch (Exception e) {
                 logger.warn("Failed to create resource in AAI", e);
@@ -178,6 +183,7 @@ public class CnfAdapterRest {
             logger.info("Processing aai delete");
             AaiCallbackResponse callbackResponse = new AaiCallbackResponse();
             try {
+                synchronizationService.deleteSubscriptionIfExists(aaiRequest);
                 aaiService.aaiDelete(aaiRequest);
                 callbackResponse.setCompletionStatus(AaiCallbackResponse.CompletionStatus.COMPLETED);
             } catch (Exception e) {
@@ -218,9 +224,7 @@ public class CnfAdapterRest {
     @ResponseBody
     @RequestMapping(value = {"/api/cnf-adapter/v1/instance"}, method = RequestMethod.POST,
             produces = "application/json", consumes = "application/json")
-    public String createInstance(@RequestBody BpmnInstanceRequest bpmnInstanceRequest)
-            throws JsonParseException, JsonMappingException, IOException {
-
+    public String createInstance(@RequestBody BpmnInstanceRequest bpmnInstanceRequest) throws BadResponseException {
         logger.info("createInstance called.");
         return cnfAdapterService.createInstance(bpmnInstanceRequest);
     }
@@ -294,8 +298,7 @@ public class CnfAdapterRest {
     @ResponseBody
     @RequestMapping(value = {"/api/cnf-adapter/v1/instance/{instID}"}, method = RequestMethod.DELETE,
             produces = "application/json")
-    public String deleteInstanceByInstanceId(@PathVariable("instID") String instanceID)
-            throws JsonParseException, JsonMappingException, IOException {
+    public String deleteInstanceByInstanceId(@PathVariable("instID") String instanceID) throws BadResponseException {
 
         logger.info("deleteInstanceByInstanceId called.");
         if (instanceID == null || instanceID.isEmpty() || instanceID.equals("null")) {
diff --git a/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/rest/SubscriptionNotifyController.java b/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/rest/SubscriptionNotifyController.java
new file mode 100644 (file)
index 0000000..88df42b
--- /dev/null
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved.
+ * Modifications Copyright (C) 2021 Samsung Technologies Co.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.cnf.rest;
+
+
+import com.google.gson.Gson;
+import org.onap.so.adapters.cnf.model.aai.AaiRequest;
+import org.onap.so.adapters.cnf.model.synchronization.NotificationRequest;
+import org.onap.so.adapters.cnf.service.aai.AaiService;
+import org.onap.so.adapters.cnf.service.synchrornization.SynchronizationService;
+import org.onap.so.client.exception.BadResponseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+@RestController
+public class SubscriptionNotifyController {
+
+    private static final Logger logger = LoggerFactory.getLogger(SubscriptionNotifyController.class);
+    private final static Gson gson = new Gson();
+
+    private final AaiService aaiService;
+    private final SynchronizationService synchronizationService;
+
+    public SubscriptionNotifyController(AaiService aaiService, SynchronizationService synchronizationService) {
+        this.aaiService = aaiService;
+        this.synchronizationService = synchronizationService;
+    }
+
+    @PostMapping(value = "/api/cnf-adapter/v1/instance/{instanceId}/status/notify")
+    public ResponseEntity subscriptionNotifyEndpoint(@PathVariable String instanceId,
+                                                     @RequestBody NotificationRequest body) throws BadResponseException {
+        String subscriptionName = synchronizationService.getSubscriptionName(instanceId);
+        boolean isSubscriptionActive = synchronizationService.isSubscriptionActive(subscriptionName);
+        if (isSubscriptionActive) {
+            logger.info("AAI update- START");
+            aaiService.aaiUpdate(body.getMetadata());
+            return ResponseEntity
+                    .accepted()
+                    .build();
+        } else {
+            return ResponseEntity
+                    .badRequest()
+                    .body(String.format("Cannot handle notification. Subscription %s not exists", subscriptionName));
+        }
+    }
+
+    private AaiRequest convertMetadataToAaiRequest(Map<String, Object> metadata) {
+        String json = gson.toJsonTree(metadata)
+                .getAsJsonObject()
+                .get("metadata")
+                .toString();
+
+        return gson.fromJson(json, AaiRequest.class);
+    }
+
+}
index 7e667a0..6d84911 100644 (file)
@@ -24,14 +24,15 @@ package org.onap.so.adapters.cnf.service;
 
 import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonMappingException;
+import com.google.gson.Gson;
 import org.apache.http.HttpStatus;
+import org.json.JSONObject;
 import org.onap.so.adapters.cnf.MulticloudConfiguration;
 import org.onap.so.adapters.cnf.model.BpmnInstanceRequest;
-import org.onap.so.adapters.cnf.model.CheckInstanceRequest;
 import org.onap.so.adapters.cnf.model.MulticloudInstanceRequest;
-import org.onap.so.adapters.cnf.model.healthcheck.HealthCheckResponse;
-import org.onap.so.adapters.cnf.service.healthcheck.HealthCheckService;
-import org.onap.so.adapters.cnf.service.statuscheck.SimpleStatusCheckService;
+import org.onap.so.adapters.cnf.model.aai.AaiRequest;
+import org.onap.so.adapters.cnf.service.synchrornization.SynchronizationService;
+import org.onap.so.client.exception.BadResponseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -66,8 +67,7 @@ public class CnfAdapterService {
         this.uri = multicloudConfiguration.getMulticloudUrl();
     }
 
-    public String createInstance(BpmnInstanceRequest bpmnInstanceRequest)
-            throws JsonParseException, JsonMappingException, IOException {
+    public String createInstance(BpmnInstanceRequest bpmnInstanceRequest) {
         try {
             logger.info("CnfAdapterService createInstance called");
             MulticloudInstanceRequest multicloudInstanceRequest = new MulticloudInstanceRequest();
@@ -262,8 +262,7 @@ public class CnfAdapterService {
         }
     }
 
-    public String deleteInstanceByInstanceId(String instanceId)
-            throws JsonParseException, JsonMappingException, IOException {
+    public String deleteInstanceByInstanceId(String instanceId) {
 
         logger.info("CnfAdapterService deleteInstanceByInstanceId called");
         ResponseEntity<String> result = null;
@@ -278,6 +277,7 @@ public class CnfAdapterService {
             logger.info("request: " + requestEntity);
             result = restTemplate.exchange(endpoint, HttpMethod.DELETE, requestEntity, String.class);
             logger.info("response: " + result);
+
             return result.getBody();
         } catch (HttpClientErrorException e) {
             logger.error("Error Calling Multicloud, e");
diff --git a/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/synchrornization/SubscriptionsRecoveryProvider.java b/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/synchrornization/SubscriptionsRecoveryProvider.java
new file mode 100644 (file)
index 0000000..68b49a8
--- /dev/null
@@ -0,0 +1,29 @@
+package org.onap.so.adapters.cnf.service.synchrornization;
+
+import org.onap.so.adapters.cnf.client.MulticloudClient;
+import org.onap.so.adapters.cnf.model.InstanceResponse;
+import org.onap.so.client.exception.BadResponseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Component
+public class SubscriptionsRecoveryProvider {
+
+    private final MulticloudClient multicloudClient;
+
+    public SubscriptionsRecoveryProvider(MulticloudClient multicloudClient) {
+        this.multicloudClient = multicloudClient;
+    }
+
+    public Set<String> getInstanceList() throws BadResponseException {
+        Set<String> instanceIds;
+        instanceIds = multicloudClient.getAllInstances().stream()
+                .map(InstanceResponse::getId)
+                .collect(Collectors.toSet());
+        return instanceIds;
+    }
+}
diff --git a/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/synchrornization/SynchronizationService.java b/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/synchrornization/SynchronizationService.java
new file mode 100644 (file)
index 0000000..643977e
--- /dev/null
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved.
+ * Modifications Copyright (C) 2021 Samsung Technologies Co.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.cnf.service.synchrornization;
+
+import org.apache.http.client.utils.URIBuilder;
+import org.onap.so.adapters.cnf.client.MulticloudClient;
+import org.onap.so.adapters.cnf.model.InstanceResponse;
+import org.onap.so.adapters.cnf.model.aai.AaiRequest;
+import org.onap.so.adapters.cnf.model.synchronization.SubscriptionRequest;
+import org.onap.so.client.exception.BadResponseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+public class SynchronizationService {
+
+    private static final Logger logger = LoggerFactory.getLogger(SynchronizationService.class);
+
+    private static final String PROTOCOL = "http";
+    private static final String HOST = "so-cnf-adapter";
+    private static final int PORT = 8090;
+
+    private final Set<String> subscriptions = new HashSet<>();
+    private final MulticloudClient multicloudClient;
+    private final SubscriptionsRecoveryProvider recoveryProvider;
+
+    public SynchronizationService(MulticloudClient multicloudClient, SubscriptionsRecoveryProvider recoveryProvider) {
+        this.multicloudClient = multicloudClient;
+        this.recoveryProvider = recoveryProvider;
+    }
+
+    @PostConstruct
+    private void postConstruct() {
+        recoverSubscriptions();
+    }
+
+    private void recoverSubscriptions() {
+        if (subscriptions.isEmpty()) {
+            Set<String> instanceIds;
+            try {
+
+                instanceIds = recoveryProvider.getInstanceList();
+
+                instanceIds.forEach(this::addSubscriptionIfSubscriptionFound);
+            } catch (BadResponseException e) {
+                logger.error("Instances not found", e);
+            }
+        }
+    }
+
+    private void addSubscriptionIfSubscriptionFound(String instanceId) {
+        String subscriptionName = getSubscriptionName(instanceId);
+        try {
+            if (multicloudClient.hasSubscription(instanceId, subscriptionName))
+                subscriptions.add(subscriptionName);
+        } catch (BadResponseException e) {
+            logger.warn("Subscriptions not found instanceId={} subscriptionName={}", instanceId, subscriptionName);
+        }
+    }
+
+    public void createSubscriptionIfNotExists(AaiRequest aaiRequest) throws BadResponseException {
+        logger.debug("createSubscriptionIfNotExists- START");
+        String instanceId = aaiRequest.getInstanceId();
+        String subscriptionName = getSubscriptionName(instanceId);
+        String callbackUrl = generateCallbackUrl(instanceId);
+
+        if (isSubscriptionActive(subscriptionName)) {
+            logger.info("Subscription: {} already exits", subscriptionName);
+        } else {
+            multicloudClient.registerSubscription(instanceId, getSubscriptionRequest(subscriptionName, callbackUrl, aaiRequest));
+            subscriptions.add(subscriptionName);
+            logger.info("Subscription: {} registered", subscriptionName);
+        }
+        logger.debug("createSubscriptionIfNotExists- END");
+    }
+
+    public void deleteSubscriptionIfExists(AaiRequest aaiRequest) throws BadResponseException {
+        logger.debug("deleteSubscriptionIfExists - START");
+        String instanceId = aaiRequest.getInstanceId();
+        String subscriptionName = getSubscriptionName(instanceId);
+
+        if (isSubscriptionActive(subscriptionName)) {
+            multicloudClient.deleteSubscription(instanceId, subscriptionName);
+            subscriptions.remove(subscriptionName);
+            logger.info("Subscription: {} removed", subscriptionName);
+        } else {
+            logger.info("Subscription: {} already removed or was not present", subscriptionName);
+        }
+        logger.debug("deleteSubscriptionIfExists - END");
+    }
+
+    public boolean isSubscriptionActive(String subscriptionName) {
+        return subscriptions.contains(subscriptionName);
+    }
+
+    public String getSubscriptionName(String instanceId) {
+        return instanceId + "-cnf-adapter";
+    }
+
+    private SubscriptionRequest getSubscriptionRequest(String name, String endpoint, AaiRequest aaiRequest) {
+        SubscriptionRequest subscriptionRequest = new SubscriptionRequest();
+
+        subscriptionRequest.setName(name);
+        subscriptionRequest.setCallbackUrl(endpoint);
+        subscriptionRequest.setMinNotifyInterval(30);
+
+        return subscriptionRequest;
+    }
+
+    private String generateCallbackUrl(String instanceId) {
+        String path = String.format("/api/cnf-adapter/v1/instance/%s/status/notify", instanceId);
+
+        URIBuilder uriBuilder = new URIBuilder();
+        try {
+            return uriBuilder
+                    .setScheme(PROTOCOL)
+                    .setHost(HOST)
+                    .setPort(PORT)
+                    .setPath(path)
+                    .build()
+                    .toString();
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
index 738c184..87b0853 100644 (file)
@@ -27,17 +27,21 @@ import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnitRunner;
+import org.onap.so.adapters.cnf.model.InstanceResponse;
 import org.onap.so.adapters.cnf.model.MulticloudInstanceRequest;
 import org.onap.so.adapters.cnf.model.healthcheck.K8sRbInstanceHealthCheck;
 import org.onap.so.adapters.cnf.model.healthcheck.K8sRbInstanceHealthCheckSimple;
 import org.onap.so.adapters.cnf.model.statuscheck.K8sRbInstanceStatus;
+import org.onap.so.adapters.cnf.model.synchronization.SubscriptionRequest;
+import org.onap.so.adapters.cnf.model.synchronization.SubscriptionResponse;
 import org.onap.so.client.exception.BadResponseException;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.web.client.RestTemplate;
 
+import java.util.List;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.eq;
@@ -65,10 +69,72 @@ public class MulticloudClientTest {
     private ArgumentCaptor<String> instanceIdCaptor;
 
 
+    @Test
+    public void shouldRegisterSubscription() throws BadResponseException {
+        // given
+        SubscriptionRequest request = mock(SubscriptionRequest.class);
+        ResponseEntity result = mock(ResponseEntity.class);
+        String body = "{\"name\":\"name\"}";
+
+        // when
+        when(multicloudApiUrl.apiUrl(instanceId)).thenReturn(endpoint);
+        when(restTemplate.exchange(eq(getUrl("/status/subscription")), eq(POST), any(), eq(String.class))).thenReturn(result);
+        when(result.getStatusCode()).thenReturn(HttpStatus.OK);
+        when(result.getBody()).thenReturn(body);
+
+        // then
+        SubscriptionResponse actual = tested.registerSubscription(instanceId, request);
+
+        assertEquals("name", actual.getName());
+        verify(multicloudApiUrl).apiUrl(instanceIdCaptor.capture());
+        assertInstanceIdCaperedValue(instanceIdCaptor.getValue());
+    }
+
+    @Test
+    public void shouldGetSubscription() throws BadResponseException {
+        // given
+        String request = "subscriptionId";
+        ResponseEntity result = mock(ResponseEntity.class);
+        String body = "{\"name\":\"name\"}";
+
+        // when
+        when(multicloudApiUrl.apiUrl(instanceId)).thenReturn(endpoint);
+        when(restTemplate.exchange(eq(getUrl("/status/subscription/" + request)), eq(GET), any(), eq(String.class))).thenReturn(result);
+        when(result.getStatusCode()).thenReturn(HttpStatus.OK);
+        when(result.getBody()).thenReturn(body);
+
+        // then
+        SubscriptionResponse actual = tested.getSubscription(instanceId, request);
+
+        assertEquals("name", actual.getName());
+        verify(multicloudApiUrl).apiUrl(instanceIdCaptor.capture());
+        assertInstanceIdCaperedValue(instanceIdCaptor.getValue());
+    }
+
+    @Test
+    public void shouldDeleteSubscription() throws BadResponseException {
+        // given
+        String request = "subscriptionId";
+        ResponseEntity result = mock(ResponseEntity.class);
+        String body = "{\"name\":\"name\"}";
+
+        // when
+        when(multicloudApiUrl.apiUrl(instanceId)).thenReturn(endpoint);
+        when(restTemplate.exchange(eq(getUrl("/status/subscription/" + request)), eq(DELETE), any(), eq(String.class))).thenReturn(result);
+        when(result.getStatusCode()).thenReturn(HttpStatus.OK);
+        when(result.getBody()).thenReturn(body);
+
+        // then
+        tested.deleteSubscription(instanceId, request);
+
+        verify(multicloudApiUrl).apiUrl(instanceIdCaptor.capture());
+        assertInstanceIdCaperedValue(instanceIdCaptor.getValue());
+    }
+
     @Test
     public void shouldUpgradeInstance() throws BadResponseException {
         // given
-        MulticloudInstanceRequest upgradeRequest = mock(MulticloudInstanceRequest.class);
+        MulticloudInstanceRequest request = mock(MulticloudInstanceRequest.class);
         ResponseEntity result = mock(ResponseEntity.class);
         String body = "body";
 
@@ -79,7 +145,7 @@ public class MulticloudClientTest {
         when(result.getBody()).thenReturn(body);
 
         // then
-        String actual = tested.upgradeInstance(instanceId, upgradeRequest);
+        String actual = tested.upgradeInstance(instanceId, request);
 
         assertEquals(body, actual);
         verify(multicloudApiUrl).apiUrl(instanceIdCaptor.capture());
@@ -135,7 +201,7 @@ public class MulticloudClientTest {
 
         // when
         when(multicloudApiUrl.apiUrl(instanceId)).thenReturn(endpoint);
-        when(restTemplate.exchange(eq(getUrl("/healthcheck/"+healthCheckInstance)), eq(GET), any(), eq(String.class))).thenReturn(result);
+        when(restTemplate.exchange(eq(getUrl("/healthcheck/" + healthCheckInstance)), eq(GET), any(), eq(String.class))).thenReturn(result);
         when(result.getStatusCode()).thenReturn(HttpStatus.OK);
         when(result.getBody()).thenReturn(body);
 
@@ -156,7 +222,7 @@ public class MulticloudClientTest {
 
         // when
         when(multicloudApiUrl.apiUrl(instanceId)).thenReturn(endpoint);
-        when(restTemplate.exchange(eq(getUrl("/healthcheck/"+healthCheckInstance)), eq(DELETE), any(), eq(String.class))).thenReturn(result);
+        when(restTemplate.exchange(eq(getUrl("/healthcheck/" + healthCheckInstance)), eq(DELETE), any(), eq(String.class))).thenReturn(result);
         when(result.getStatusCode()).thenReturn(HttpStatus.OK);
         when(result.getBody()).thenReturn(body);
 
@@ -167,6 +233,29 @@ public class MulticloudClientTest {
         assertInstanceIdCaperedValue(instanceIdCaptor.getValue());
     }
 
+    @Test
+    public void shouldGetAllInstances() throws BadResponseException {
+        // given
+        ResponseEntity result = mock(ResponseEntity.class);
+        String body = "[{\"id\":\"clever_proskuriakova\",\"release-name\":\"rel-1-apache\",\"namespace\":\"test-cnf\"}]";
+
+        // when
+        when(multicloudApiUrl.apiUrl("")).thenReturn(endpoint);
+        when(restTemplate.exchange(eq(getUrl("")), eq(GET), any(), eq(String.class))).thenReturn(result);
+        when(result.getStatusCode()).thenReturn(HttpStatus.OK);
+        when(result.getBody()).thenReturn(body);
+
+        // then
+        List<InstanceResponse> actual = tested.getAllInstances();
+
+        verify(multicloudApiUrl).apiUrl(instanceIdCaptor.capture());
+        assertEquals("", instanceIdCaptor.getValue());
+        assertEquals(1, actual.size());
+        assertEquals("clever_proskuriakova", actual.get(0).getId());
+        assertEquals("rel-1-apache", actual.get(0).getReleaseName());
+        assertEquals("test-cnf", actual.get(0).getNamespace());
+    }
+
     private void assertInstanceIdCaperedValue(String instanceIdCapturedValue) {
         assertEquals(instanceId, instanceIdCapturedValue);
     }
index 06e93e1..d50f633 100644 (file)
@@ -17,7 +17,6 @@
  * limitations under the License.
  * ============LICENSE_END=========================================================
  */
-
 package org.onap.so.adapters.cnf.service;
 
 import lombok.Data;
@@ -30,7 +29,8 @@ import org.onap.so.adapters.cnf.MulticloudConfiguration;
 import org.onap.so.adapters.cnf.model.BpmnInstanceRequest;
 import org.onap.so.adapters.cnf.service.healthcheck.HealthCheckService;
 import org.onap.so.adapters.cnf.service.statuscheck.SimpleStatusCheckService;
-import org.springframework.boot.test.context.SpringBootTest;
+import org.onap.so.adapters.cnf.service.synchrornization.SynchronizationService;
+import org.onap.so.client.exception.BadResponseException;
 import org.springframework.http.HttpMethod;
 import org.springframework.http.HttpStatus;
 import org.springframework.test.context.junit4.SpringRunner;
@@ -49,7 +49,6 @@ import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
-@SpringBootTest
 @RunWith(SpringRunner.class)
 public class CnfAdapterServiceTest {
 
@@ -66,6 +65,9 @@ public class CnfAdapterServiceTest {
     @Mock
     SimpleStatusCheckService simpleStatusCheckService;
 
+    @Mock
+    SynchronizationService synchronizationService;
+
 
     @Before
     public void setUp() {
@@ -85,25 +87,17 @@ public class CnfAdapterServiceTest {
     }
 
     @Test(expected = EntityNotFoundException.class)
-    public void testcreateInstanceHttpException() {
+    public void testcreateInstanceHttpException() throws BadResponseException {
         doThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)).when(restTemplate).exchange(ArgumentMatchers.anyString(),
                 ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(), ArgumentMatchers.<Class<String>>any());
-        try {
-            cnfAdapterService.createInstance(getBpmnInstanceRequest());
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+        cnfAdapterService.createInstance(getBpmnInstanceRequest());
     }
 
     @Test(expected = HttpStatusCodeException.class)
-    public void testcreateInstanceHttpStatusCodeException() {
+    public void testCreateInstanceHttpStatusCodeException() throws BadResponseException {
         doThrow(new HttpServerErrorException(HttpStatus.CONFLICT)).when(restTemplate).exchange(ArgumentMatchers.anyString(),
                 ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(), ArgumentMatchers.<Class<String>>any());
-        try {
-            cnfAdapterService.createInstance(getBpmnInstanceRequest());
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+        cnfAdapterService.createInstance(getBpmnInstanceRequest());
     }
 
     @Test
@@ -215,25 +209,17 @@ public class CnfAdapterServiceTest {
     }
 
     @Test(expected = EntityNotFoundException.class)
-    public void testdeleteInstanceByInstanceIdHttpException() {
+    public void testdeleteInstanceByInstanceIdHttpException() throws BadResponseException {
         doThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)).when(restTemplate).exchange(ArgumentMatchers.anyString(),
                 ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(), ArgumentMatchers.<Class<String>>any());
-        try {
-            cnfAdapterService.deleteInstanceByInstanceId(INSTANCE_ID);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+        cnfAdapterService.deleteInstanceByInstanceId(INSTANCE_ID);
     }
 
     @Test(expected = HttpStatusCodeException.class)
-    public void testdeleteInstanceByInstanceIdException() {
+    public void testDeleteInstanceByInstanceIdException() throws BadResponseException {
         doThrow(new HttpServerErrorException(HttpStatus.CONFLICT)).when(restTemplate).exchange(ArgumentMatchers.anyString(),
                 ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(), ArgumentMatchers.<Class<String>>any());
-        try {
-            cnfAdapterService.deleteInstanceByInstanceId(INSTANCE_ID);
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
+        cnfAdapterService.deleteInstanceByInstanceId(INSTANCE_ID);
     }
 
     @Test
@@ -269,9 +255,9 @@ public class CnfAdapterServiceTest {
     }
 
     private BpmnInstanceRequest getBpmnInstanceRequest() {
-        Map<String, String> labels = new HashMap<String, String>();
+        Map<String, String> labels = new HashMap<>();
         labels.put("custom-label-1", "label1");
-        Map<String, String> overrideValues = new HashMap<String, String>();
+        Map<String, String> overrideValues = new HashMap<>();
         overrideValues.put("a", "b");
         labels.put("image.tag", "latest");
         labels.put("dcae_collector_ip", "1.2.3.4");
diff --git a/so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/service/synchrornization/SubscriptionsRecoveryProvider.java b/so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/service/synchrornization/SubscriptionsRecoveryProvider.java
new file mode 100644 (file)
index 0000000..32feb78
--- /dev/null
@@ -0,0 +1,17 @@
+package org.onap.so.adapters.cnf.service.synchrornization;
+
+import org.onap.so.client.exception.BadResponseException;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.Set;
+
+@Component
+@Primary
+public class SubscriptionsRecoveryProvider {
+
+    public Set<String> getInstanceList() throws BadResponseException {
+        return Collections.emptySet();
+    }
+}
\ No newline at end of file
diff --git a/so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/service/synchrornization/SynchronizationServiceTest.java b/so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/service/synchrornization/SynchronizationServiceTest.java
new file mode 100644 (file)
index 0000000..4a159bc
--- /dev/null
@@ -0,0 +1,96 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP - SO
+ * ================================================================================
+ * Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved.
+ * Modifications Copyright (C) 2021 Samsung Technologies Co.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.so.adapters.cnf.service.synchrornization;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.onap.so.adapters.cnf.client.MulticloudClient;
+import org.onap.so.adapters.cnf.model.aai.AaiRequest;
+import org.onap.so.adapters.cnf.model.synchronization.SubscriptionRequest;
+import org.onap.so.client.exception.BadResponseException;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SpringRunner.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SynchronizationServiceTest {
+
+    private static final String INSTANCE_ID = "INSTANCE_ID";
+    private static final String SUFFIX = "-cnf-adapter";
+    private static final String SUBSCRIPTION_NAME = INSTANCE_ID + SUFFIX;
+
+    @InjectMocks
+    private SynchronizationService tested;
+
+    @Mock
+    private MulticloudClient multicloudClient;
+    @Captor
+    private ArgumentCaptor<SubscriptionRequest> subscriptionRequestCaptor;
+
+    @Test
+    public void shouldCreateSubscription() throws BadResponseException {
+        // given
+        String callbackUrl = "http://so-cnf-adapter:8090/api/cnf-adapter/v1/instance/INSTANCE_ID/status/notify";
+        AaiRequest aaiRequest = mock(AaiRequest.class);
+
+        // when
+        when(aaiRequest.getInstanceId()).thenReturn(INSTANCE_ID);
+
+        // then
+        tested.createSubscriptionIfNotExists(aaiRequest);
+
+        verify(multicloudClient).registerSubscription(eq(INSTANCE_ID), subscriptionRequestCaptor.capture());
+        SubscriptionRequest subscriptionRequest = subscriptionRequestCaptor.getValue();
+        assertEquals(SUBSCRIPTION_NAME, subscriptionRequest.getName());
+        assertEquals(callbackUrl, subscriptionRequest.getCallbackUrl());
+        assertEquals(30, subscriptionRequest.getMinNotifyInterval());
+    }
+
+    @Test
+    public void shouldGetSubscriptionName() {
+        // given
+        // when
+        // then
+        String actual = tested.getSubscriptionName(INSTANCE_ID);
+        assertEquals(SUBSCRIPTION_NAME, actual);
+    }
+
+    @Test
+    public void shouldCheckIfSubscriptionActive() {
+        // given
+        // when
+        // then
+        boolean subscriptionActive = tested.isSubscriptionActive(INSTANCE_ID);
+        assertFalse(subscriptionActive);
+    }
+}
\ No newline at end of file