TEIV: Adding error handling and caching to FOCOM 32/15332/1
authorKrupaNagabhushan <krupa.nagabhushan@est.tech>
Thu, 4 Dec 2025 13:02:43 +0000 (13:02 +0000)
committerKrupaNagabhushan <krupa.nagabhushan@est.tech>
Thu, 4 Dec 2025 13:02:43 +0000 (13:02 +0000)
Issue-ID: SMO-208
Change-Id: Iac66177326d4407bd8c8903bd6f3507c94c84f67
Signed-off-by: KrupaNagabhushan <krupa.nagabhushan@est.tech>
adapters/focom-to-teiv-adapter/pom.xml
adapters/focom-to-teiv-adapter/src/main/java/org/oran/smo/teiv/adapters/focom_to_teiv_adapter/FocomToTeivIngestion.java
adapters/focom-to-teiv-adapter/src/main/java/org/oran/smo/teiv/adapters/focom_to_teiv_adapter/custom_resource_json/EntityItem.java
adapters/focom-to-teiv-adapter/src/main/java/org/oran/smo/teiv/adapters/focom_to_teiv_adapter/service/FocomToTeivModelBuilder.java

index ed2243c..21d4ab1 100644 (file)
             <version>${sundr.builder.version}</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
                     <source>src/main/resources/crds</source>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 
index 0a92a10..174f0fe 100644 (file)
@@ -27,8 +27,10 @@ import lombok.extern.slf4j.Slf4j;
 import org.oran.smo.teiv.adapters.focom_to_teiv_adapter.service.FocomToTeivModelBuilder;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
+import org.testcontainers.shaded.com.google.common.hash.Hashing;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.Map;
 
 @Slf4j
@@ -38,14 +40,30 @@ public class FocomToTeivIngestion {
 
     private static final ObjectMapper objectMapper = new ObjectMapper();
     private final KafkaEventProducer kafkaEventProducer;
-    private final FocomToTeivModelBuilder jsonBuilder;
+    private final FocomToTeivModelBuilder focomToTeivModelBuilder;
+
+    private String lastPayloadHash = null;
 
     @Scheduled(fixedRateString = "${polling.interval}")
     public void pollExternalApi() throws IOException {
-        Map<String, Object> json = jsonBuilder.getFocomtoTeivJson();
-        log.debug("Retrieved JSON for FOCOM_PROVISION_REQUEST_NAME: {}", json);
+        Map<String, Object> modelJson = focomToTeivModelBuilder.getFocomtoTeivJson();
+        if (modelJson == null || modelJson.isEmpty()) {
+            log.info("FOCOM JSON is empty. CloudEvent will NOT be sent.");
+            return;
+        }
+        log.debug("Retrieved JSON for FOCOM_PROVISION_REQUEST_NAME: {}", modelJson);
+
+        String newPayload = objectMapper.writeValueAsString(modelJson);
+        String newPayloadHash = Hashing.sha256().hashString(newPayload, StandardCharsets.UTF_8).toString();
+
+        if (newPayloadHash.equals(lastPayloadHash)) {
+            log.info("No change detected in CR. Skipping CloudEvent.");
+            return;
+        }
+
         try {
-            sendCloudEvent(json, "merge");
+            sendCloudEvent(modelJson, "merge");
+            lastPayloadHash = newPayloadHash;
         } catch (IOException e) {
             log.error("Failed to poll external API or send CloudEvent", e);
         }
index c1facce..00e88d9 100644 (file)
@@ -32,4 +32,9 @@ public class EntityItem {
 
     @JsonProperty("attributes")
     private Map<String, Object> attributes;
+
+    public boolean isEmpty() {
+        return (id == null || id.isBlank()) &&
+                (attributes == null || attributes.isEmpty());
+    }
 }
index 1271fd1..721c6e9 100644 (file)
@@ -22,6 +22,7 @@ package org.oran.smo.teiv.adapters.focom_to_teiv_adapter.service;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
+import io.fabric8.kubernetes.client.KubernetesClientException;
 import lombok.extern.slf4j.Slf4j;
 import org.nephio.focom.v1alpha1.FocomProvisioningRequest;
 import org.oran.provisioning.o2ims.v1alpha1.ProvisioningRequest;
@@ -32,10 +33,11 @@ import org.oran.smo.teiv.adapters.focom_to_teiv_adapter.custom_resource_json.Rel
 import org.springframework.stereotype.Component;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.List;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 
 import static org.oran.smo.teiv.adapters.common.utils.Constants.ENTITY_OCLOUD_NAMESPACE;
@@ -63,10 +65,23 @@ public class FocomToTeivModelBuilder {
         List<EntityItem> oCloudNamespaces = new ArrayList<>();
         List<EntityItem> nodeClusters = new ArrayList<>();
         List<RelationshipItem> deployOnRelationships = new ArrayList<>();
+        List<FocomProvisioningRequest> focomRequests;
 
         int index = 1;
 
-        List<FocomProvisioningRequest> focomRequests = service.getAllFocomProvisioningRequests();
+        try {
+            focomRequests = service.getAllFocomProvisioningRequests();
+        } catch (KubernetesClientException e) {
+            log.error("Failed to retrieve FOCOM provisioning requests from Kubernetes: {}", e.getMessage(), e);
+            return Collections.emptyMap();
+        } catch (Exception e) {
+            log.error("Unexpected error while retrieving FOCOM provisioning requests", e);
+            return Collections.emptyMap();
+        }
+
+        if (focomRequests == null || focomRequests.isEmpty()) {
+            return Collections.emptyMap();
+        }
 
         for (FocomProvisioningRequest focomProvisioningRequest : focomRequests) {
 
@@ -76,6 +91,11 @@ public class FocomToTeivModelBuilder {
             oCloudNamespaces.add(oCloudNamespace);
 
             EntityItem nodeCluster = buildNodeCluster(focomProvisioningRequest, index);
+            if (nodeCluster == null || nodeCluster.isEmpty()) {
+                log.error("Failed to build Nodecluster, attributes missing");
+                index++;
+                continue;
+            }
             nodeClusters.add(nodeCluster);
 
             RelationshipItem rel = modelService.getTeivRelationshipDeployOn(oCloudNamespace, nodeCluster, index);
@@ -89,20 +109,25 @@ public class FocomToTeivModelBuilder {
                 buildEntityTypeName(SMO_TEIV_CLOUD_PREFIX, ENTITY_OCLOUD_NAMESPACE),
                 oCloudNamespaces
         );
-        entities.put(
-                buildEntityTypeName(SMO_TEIV_CLOUD_PREFIX, ENTITY_NODE_CLUSTER),
-                nodeClusters
-        );
+
+        if (!nodeClusters.isEmpty()) {
+            entities.put(
+                    buildEntityTypeName(SMO_TEIV_CLOUD_PREFIX, ENTITY_NODE_CLUSTER),
+                    nodeClusters
+            );
+        }
 
         Map<String, List<RelationshipItem>> relationships = new HashMap<>();
-        relationships.put(
-                buildTeivFocomRelationshipTypeName(
-                        REL_DEPLOYED_ON,
-                        ENTITY_OCLOUD_NAMESPACE.toUpperCase(),
-                        ENTITY_NODE_CLUSTER.toUpperCase()
-                ),
-                deployOnRelationships
-        );
+        if (!deployOnRelationships.isEmpty()) {
+            relationships.put(
+                    buildTeivFocomRelationshipTypeName(
+                            REL_DEPLOYED_ON,
+                            ENTITY_OCLOUD_NAMESPACE.toUpperCase(),
+                            ENTITY_NODE_CLUSTER.toUpperCase()
+                    ),
+                    deployOnRelationships
+            );
+        }
 
         EntityAndRelationshipModel entityAndRelationshipModel = new EntityAndRelationshipModel();
         entityAndRelationshipModel.setEntities(List.of(entities));
@@ -128,14 +153,23 @@ public class FocomToTeivModelBuilder {
     }
 
     private EntityItem buildNodeCluster(FocomProvisioningRequest focomProvisioningRequest, int index) {
-        ProvisioningRequest o2imsReq = service.getO2imsProvisioningRequest(focomProvisioningRequest.getMetadata().getName());
+        ProvisioningRequest o2imsRequest;
+        try {
+            o2imsRequest = service.getO2imsProvisioningRequest(focomProvisioningRequest.getMetadata().getName());
+        } catch (KubernetesClientException e) {
+            log.error("Failed to retrieve O2ims provisioning requests from Kubernetes: {}", e.getMessage(), e);
+            return new EntityItem();
+        } catch (Exception e) {
+            log.error("Unexpected error while retrieving O2ims provisioning requests", e);
+            return new EntityItem();
+        }
 
         String clusterName = focomProvisioningRequest.getSpec().getTemplateParameters()
                 .getAdditionalProperties()
                 .get("clusterName")
                 .toString();
 
-        String nodeClusterId = String.valueOf(o2imsReq.getStatus()
+        String nodeClusterId = String.valueOf(o2imsRequest.getStatus()
                 .getProvisionedResourceSet()
                 .getOCloudNodeClusterId());