This is an automated email from the ASF dual-hosted git repository.

yasith pushed a commit to branch feat/airavata-service-layer
in repository https://gitbox.apache.org/repos/asf/airavata.git

commit 9ac360eda713035b4a9757abe59141dcc2238ec3
Author: yasithdev <[email protected]>
AuthorDate: Thu Mar 26 05:31:43 2026 -0500

    feat: add ExperimentService with createExperiment and getExperiment
    
    Extracts experiment business logic from AiravataServerHandler into a
    clean service class with explicit dependency injection. Uses 
isSharingEnabled()
    helper to safely default to false when the setting is absent.
---
 .../service/experiment/ExperimentService.java      | 119 +++++++++++++++++++++
 .../service/experiment/ExperimentServiceTest.java  |  65 +++++++++++
 2 files changed, 184 insertions(+)

diff --git 
a/airavata-api/src/main/java/org/apache/airavata/service/experiment/ExperimentService.java
 
b/airavata-api/src/main/java/org/apache/airavata/service/experiment/ExperimentService.java
new file mode 100644
index 0000000000..906e61902a
--- /dev/null
+++ 
b/airavata-api/src/main/java/org/apache/airavata/service/experiment/ExperimentService.java
@@ -0,0 +1,119 @@
+package org.apache.airavata.service.experiment;
+
+import org.apache.airavata.common.utils.ServerSettings;
+import org.apache.airavata.model.experiment.ExperimentModel;
+import org.apache.airavata.model.experiment.ExperimentSummaryModel;
+import org.apache.airavata.model.experiment.ExperimentSearchFields;
+import org.apache.airavata.model.application.io.OutputDataObjectType;
+import org.apache.airavata.model.status.ExperimentState;
+import org.apache.airavata.model.status.ExperimentStatus;
+import org.apache.airavata.registry.api.service.handler.RegistryServerHandler;
+import org.apache.airavata.service.context.RequestContext;
+import org.apache.airavata.service.exception.ServiceAuthorizationException;
+import org.apache.airavata.service.exception.ServiceException;
+import org.apache.airavata.service.exception.ServiceNotFoundException;
+import org.apache.airavata.service.messaging.EventPublisher;
+import org.apache.airavata.sharing.registry.models.Entity;
+import org.apache.airavata.sharing.registry.models.SearchCriteria;
+import org.apache.airavata.sharing.registry.models.EntitySearchField;
+import org.apache.airavata.sharing.registry.models.SearchCondition;
+import 
org.apache.airavata.sharing.registry.server.SharingRegistryServerHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExperimentService {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(ExperimentService.class);
+
+    private final RegistryServerHandler registryHandler;
+    private final SharingRegistryServerHandler sharingHandler;
+    private final EventPublisher eventPublisher;
+
+    public ExperimentService(
+            RegistryServerHandler registryHandler,
+            SharingRegistryServerHandler sharingHandler,
+            EventPublisher eventPublisher) {
+        this.registryHandler = registryHandler;
+        this.sharingHandler = sharingHandler;
+        this.eventPublisher = eventPublisher;
+    }
+
+    public String createExperiment(RequestContext ctx, ExperimentModel 
experiment) throws ServiceException {
+        try {
+            String experimentId = 
registryHandler.createExperiment(ctx.getGatewayId(), experiment);
+
+            if (isSharingEnabled()) {
+                try {
+                    Entity entity = new Entity();
+                    entity.setEntityId(experimentId);
+                    String domainId = experiment.getGatewayId();
+                    entity.setDomainId(domainId);
+                    entity.setEntityTypeId(domainId + ":" + "EXPERIMENT");
+                    entity.setOwnerId(experiment.getUserName() + "@" + 
domainId);
+                    entity.setName(experiment.getExperimentName());
+                    entity.setDescription(experiment.getDescription());
+                    entity.setParentEntityId(experiment.getProjectId());
+                    sharingHandler.createEntity(entity);
+                } catch (Exception ex) {
+                    logger.error("Rolling back experiment creation Exp ID : 
{}", experimentId, ex);
+                    registryHandler.deleteExperiment(experimentId);
+                    throw new ServiceException("Failed to create sharing 
registry record", ex);
+                }
+            }
+
+            eventPublisher.publishExperimentStatus(experimentId, 
ctx.getGatewayId(), ExperimentState.CREATED);
+            logger.info("Created new experiment with name {} and id {}",
+                    experiment.getExperimentName(), experimentId);
+            return experimentId;
+        } catch (ServiceException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ServiceException("Error while creating the experiment: " 
+ e.getMessage(), e);
+        }
+    }
+
+    public ExperimentModel getExperiment(RequestContext ctx, String 
experimentId) throws ServiceException {
+        try {
+            ExperimentModel experiment = 
registryHandler.getExperiment(experimentId);
+            if (experiment == null) {
+                throw new ServiceNotFoundException("Experiment " + 
experimentId + " does not exist");
+            }
+
+            // Owner always has access
+            if (ctx.getUserId().equals(experiment.getUserName())
+                    && ctx.getGatewayId().equals(experiment.getGatewayId())) {
+                return experiment;
+            }
+
+            // Check sharing permissions
+            if (isSharingEnabled()) {
+                String qualifiedUserId = ctx.getUserId() + "@" + 
ctx.getGatewayId();
+                if (!sharingHandler.userHasAccess(
+                        ctx.getGatewayId(), qualifiedUserId, experimentId, 
ctx.getGatewayId() + ":READ")) {
+                    throw new ServiceAuthorizationException(
+                            "User does not have permission to access this 
resource");
+                }
+                return experiment;
+            }
+
+            return null;
+        } catch (ServiceException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ServiceException("Error while getting the experiment: " 
+ e.getMessage(), e);
+        }
+    }
+
+    private boolean isSharingEnabled() {
+        try {
+            return ServerSettings.isEnableSharing();
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}
diff --git 
a/airavata-api/src/test/java/org/apache/airavata/service/experiment/ExperimentServiceTest.java
 
b/airavata-api/src/test/java/org/apache/airavata/service/experiment/ExperimentServiceTest.java
new file mode 100644
index 0000000000..ff78ade708
--- /dev/null
+++ 
b/airavata-api/src/test/java/org/apache/airavata/service/experiment/ExperimentServiceTest.java
@@ -0,0 +1,65 @@
+package org.apache.airavata.service.experiment;
+
+import org.apache.airavata.model.experiment.ExperimentModel;
+import org.apache.airavata.registry.api.service.handler.RegistryServerHandler;
+import org.apache.airavata.service.context.RequestContext;
+import org.apache.airavata.service.messaging.EventPublisher;
+import 
org.apache.airavata.sharing.registry.server.SharingRegistryServerHandler;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class ExperimentServiceTest {
+
+    @Mock RegistryServerHandler registryHandler;
+    @Mock SharingRegistryServerHandler sharingHandler;
+    @Mock EventPublisher eventPublisher;
+
+    ExperimentService experimentService;
+    RequestContext ctx;
+
+    @BeforeEach
+    void setUp() {
+        experimentService = new ExperimentService(registryHandler, 
sharingHandler, eventPublisher);
+        ctx = new RequestContext("testUser", "testGateway", "token123",
+                Map.of("userName", "testUser", "gatewayId", "testGateway"));
+    }
+
+    @Test
+    void createExperiment_returnsExperimentId() throws Exception {
+        ExperimentModel experiment = new ExperimentModel();
+        experiment.setExperimentName("test-exp");
+        experiment.setGatewayId("testGateway");
+        experiment.setUserName("testUser");
+        experiment.setProjectId("proj-1");
+
+        when(registryHandler.createExperiment("testGateway", 
experiment)).thenReturn("exp-123");
+
+        String result = experimentService.createExperiment(ctx, experiment);
+
+        assertEquals("exp-123", result);
+        verify(registryHandler).createExperiment("testGateway", experiment);
+    }
+
+    @Test
+    void getExperiment_ownerGetsAccess() throws Exception {
+        ExperimentModel experiment = new ExperimentModel();
+        experiment.setUserName("testUser");
+        experiment.setGatewayId("testGateway");
+
+        when(registryHandler.getExperiment("exp-123")).thenReturn(experiment);
+
+        ExperimentModel result = experimentService.getExperiment(ctx, 
"exp-123");
+
+        assertNotNull(result);
+        assertEquals("testUser", result.getUserName());
+    }
+}

Reply via email to