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 4fe3ef49e395251694ba5a60f7a2e4dae421421e
Author: yasithdev <[email protected]>
AuthorDate: Thu Mar 26 05:34:42 2026 -0500

    feat: add remaining ExperimentService methods
---
 .../service/experiment/ExperimentService.java      | 227 +++++++++++++++++++++
 .../service/experiment/ExperimentServiceTest.java  |  69 +++++++
 2 files changed, 296 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
index 906e61902a..e03b725a00 100644
--- 
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
@@ -109,6 +109,233 @@ public class ExperimentService {
         }
     }
 
+    public boolean deleteExperiment(RequestContext ctx, String experimentId) 
throws ServiceException {
+        try {
+            ExperimentModel experiment = 
registryHandler.getExperiment(experimentId);
+
+            if (!ctx.getUserId().equals(experiment.getUserName())
+                    || !ctx.getGatewayId().equals(experiment.getGatewayId())) {
+                if (isSharingEnabled()) {
+                    String qualifiedUserId = ctx.getUserId() + "@" + 
ctx.getGatewayId();
+                    if (!sharingHandler.userHasAccess(
+                            ctx.getGatewayId(), qualifiedUserId, experimentId,
+                            ctx.getGatewayId() + ":WRITE")) {
+                        throw new ServiceAuthorizationException(
+                                "User does not have permission to delete this 
resource");
+                    }
+                }
+            }
+
+            if (experiment.getExperimentStatus().get(0).getState() != 
ExperimentState.CREATED) {
+                throw new ServiceException(
+                        "Experiment is not in CREATED state. Cannot be 
deleted. ID: " + experimentId);
+            }
+
+            return registryHandler.deleteExperiment(experimentId);
+        } catch (ServiceException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ServiceException("Error while deleting the experiment: " 
+ e.getMessage(), e);
+        }
+    }
+
+    public ExperimentModel getExperimentByAdmin(RequestContext ctx, String 
experimentId)
+            throws ServiceException {
+        try {
+            ExperimentModel experiment = 
registryHandler.getExperiment(experimentId);
+            if (ctx.getGatewayId().equals(experiment.getGatewayId())) {
+                return experiment;
+            }
+            throw new ServiceAuthorizationException(
+                    "User does not have permission to access this resource");
+        } catch (ServiceException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ServiceException("Error while getting the experiment: " 
+ e.getMessage(), e);
+        }
+    }
+
+    public List<ExperimentSummaryModel> searchExperiments(
+            RequestContext ctx, String gatewayId, String userName,
+            Map<ExperimentSearchFields, String> filters, int limit, int offset)
+            throws ServiceException {
+        try {
+            List<String> accessibleExpIds = new ArrayList<>();
+            Map<ExperimentSearchFields, String> filtersCopy = new 
HashMap<>(filters);
+            List<SearchCriteria> sharingFilters = new ArrayList<>();
+
+            SearchCriteria entityTypeCriteria = new SearchCriteria();
+            
entityTypeCriteria.setSearchField(EntitySearchField.ENTITY_TYPE_ID);
+            entityTypeCriteria.setSearchCondition(SearchCondition.EQUAL);
+            entityTypeCriteria.setValue(gatewayId + ":EXPERIMENT");
+            sharingFilters.add(entityTypeCriteria);
+
+            if (filtersCopy.containsKey(ExperimentSearchFields.FROM_DATE)) {
+                String fromTime = 
filtersCopy.remove(ExperimentSearchFields.FROM_DATE);
+                SearchCriteria c = new SearchCriteria();
+                c.setSearchField(EntitySearchField.CREATED_TIME);
+                c.setSearchCondition(SearchCondition.GTE);
+                c.setValue(fromTime);
+                sharingFilters.add(c);
+            }
+            if (filtersCopy.containsKey(ExperimentSearchFields.TO_DATE)) {
+                String toTime = 
filtersCopy.remove(ExperimentSearchFields.TO_DATE);
+                SearchCriteria c = new SearchCriteria();
+                c.setSearchField(EntitySearchField.CREATED_TIME);
+                c.setSearchCondition(SearchCondition.LTE);
+                c.setValue(toTime);
+                sharingFilters.add(c);
+            }
+            if (filtersCopy.containsKey(ExperimentSearchFields.PROJECT_ID)) {
+                String projectId = 
filtersCopy.remove(ExperimentSearchFields.PROJECT_ID);
+                SearchCriteria c = new SearchCriteria();
+                c.setSearchField(EntitySearchField.PARRENT_ENTITY_ID);
+                c.setSearchCondition(SearchCondition.EQUAL);
+                c.setValue(projectId);
+                sharingFilters.add(c);
+            }
+            if (filtersCopy.containsKey(ExperimentSearchFields.USER_NAME)) {
+                String username = 
filtersCopy.remove(ExperimentSearchFields.USER_NAME);
+                SearchCriteria c = new SearchCriteria();
+                c.setSearchField(EntitySearchField.OWNER_ID);
+                c.setSearchCondition(SearchCondition.EQUAL);
+                c.setValue(username + "@" + gatewayId);
+                sharingFilters.add(c);
+            }
+            if 
(filtersCopy.containsKey(ExperimentSearchFields.EXPERIMENT_NAME)) {
+                String name = 
filtersCopy.remove(ExperimentSearchFields.EXPERIMENT_NAME);
+                SearchCriteria c = new SearchCriteria();
+                c.setSearchField(EntitySearchField.NAME);
+                c.setSearchCondition(SearchCondition.LIKE);
+                c.setValue(name);
+                sharingFilters.add(c);
+            }
+            if 
(filtersCopy.containsKey(ExperimentSearchFields.EXPERIMENT_DESC)) {
+                String desc = 
filtersCopy.remove(ExperimentSearchFields.EXPERIMENT_DESC);
+                SearchCriteria c = new SearchCriteria();
+                c.setSearchField(EntitySearchField.DESCRIPTION);
+                c.setSearchCondition(SearchCondition.LIKE);
+                c.setValue(desc);
+                sharingFilters.add(c);
+            }
+
+            int searchOffset = 0;
+            int searchLimit = Integer.MAX_VALUE;
+            boolean filteredInSharing = filtersCopy.isEmpty();
+            if (filteredInSharing) {
+                searchOffset = offset;
+                searchLimit = limit;
+            }
+
+            sharingHandler.searchEntities(
+                    gatewayId, userName + "@" + gatewayId,
+                    sharingFilters, searchOffset, searchLimit)
+                    .forEach(e -> accessibleExpIds.add(e.getEntityId()));
+
+            int finalOffset = filteredInSharing ? 0 : offset;
+            return registryHandler.searchExperiments(
+                    gatewayId, userName, accessibleExpIds, filtersCopy, limit, 
finalOffset);
+        } catch (Exception e) {
+            throw new ServiceException("Error while searching experiments: " + 
e.getMessage(), e);
+        }
+    }
+
+    public ExperimentStatus getExperimentStatus(RequestContext ctx, String 
experimentId)
+            throws ServiceException {
+        try {
+            return registryHandler.getExperimentStatus(experimentId);
+        } catch (Exception e) {
+            throw new ServiceException("Error while getting experiment status: 
" + e.getMessage(), e);
+        }
+    }
+
+    public List<OutputDataObjectType> getExperimentOutputs(RequestContext ctx, 
String experimentId)
+            throws ServiceException {
+        try {
+            return registryHandler.getExperimentOutputs(experimentId);
+        } catch (Exception e) {
+            throw new ServiceException("Error while retrieving experiment 
outputs: " + e.getMessage(), e);
+        }
+    }
+
+    public void terminateExperiment(RequestContext ctx, String experimentId)
+            throws ServiceException {
+        try {
+            ExperimentModel experiment = 
registryHandler.getExperiment(experimentId);
+            if (experiment == null) {
+                throw new ServiceNotFoundException("Experiment " + 
experimentId + " does not exist");
+            }
+            ExperimentStatus status = 
registryHandler.getExperimentStatus(experimentId);
+            switch (status.getState()) {
+                case COMPLETED:
+                case CANCELED:
+                case FAILED:
+                case CANCELING:
+                    logger.warn("Can't terminate already {} experiment", 
status.getState().name());
+                    return;
+                case CREATED:
+                    logger.warn("Experiment termination is only allowed for 
launched experiments.");
+                    return;
+                default:
+                    eventPublisher.publishExperimentCancel(experimentId, 
ctx.getGatewayId());
+                    logger.debug("Cancelled experiment {}", experimentId);
+            }
+        } catch (ServiceException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ServiceException("Error while cancelling the experiment: 
" + e.getMessage(), e);
+        }
+    }
+
+    public String cloneExperiment(RequestContext ctx, String 
existingExperimentId,
+                                   String newExperimentName, String 
newExperimentProjectId,
+                                   boolean adminMode) throws ServiceException {
+        try {
+            ExperimentModel existingExperiment;
+            if (adminMode) {
+                existingExperiment = getExperimentByAdmin(ctx, 
existingExperimentId);
+            } else {
+                existingExperiment = getExperiment(ctx, existingExperimentId);
+            }
+
+            if (existingExperiment == null) {
+                throw new ServiceNotFoundException("Experiment " + 
existingExperimentId + " does not exist");
+            }
+
+            if (newExperimentProjectId != null) {
+                existingExperiment.setProjectId(newExperimentProjectId);
+            }
+
+            // Verify write access to target project
+            String qualifiedUserId = ctx.getUserId() + "@" + 
ctx.getGatewayId();
+            if (!sharingHandler.userHasAccess(
+                    ctx.getGatewayId(), qualifiedUserId,
+                    existingExperiment.getProjectId(), ctx.getGatewayId() + 
":WRITE")) {
+                throw new ServiceAuthorizationException(
+                        "User does not have permission to clone an experiment 
in this project");
+            }
+
+            existingExperiment.setCreationTime(System.currentTimeMillis());
+            if (existingExperiment.getExecutionId() != null) {
+                List<OutputDataObjectType> appOutputs =
+                        
registryHandler.getApplicationOutputs(existingExperiment.getExecutionId());
+                existingExperiment.setExperimentOutputs(appOutputs);
+            }
+            if (newExperimentName != null && !newExperimentName.isEmpty()) {
+                existingExperiment.setExperimentName(newExperimentName);
+            }
+            existingExperiment.unsetErrors();
+            existingExperiment.unsetProcesses();
+            existingExperiment.unsetExperimentStatus();
+
+            return createExperiment(ctx, existingExperiment);
+        } catch (ServiceException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ServiceException("Error while cloning experiment: " + 
e.getMessage(), e);
+        }
+    }
+
     private boolean isSharingEnabled() {
         try {
             return ServerSettings.isEnableSharing();
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
index ff78ade708..cd549165b9 100644
--- 
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
@@ -1,8 +1,13 @@
 package org.apache.airavata.service.experiment;
 
 import org.apache.airavata.model.experiment.ExperimentModel;
+import org.apache.airavata.model.status.ExperimentState;
+import org.apache.airavata.model.status.ExperimentStatus;
+import org.apache.airavata.model.application.io.OutputDataObjectType;
 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.messaging.EventPublisher;
 import 
org.apache.airavata.sharing.registry.server.SharingRegistryServerHandler;
 import org.junit.jupiter.api.BeforeEach;
@@ -11,6 +16,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.util.List;
 import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -62,4 +68,67 @@ class ExperimentServiceTest {
         assertNotNull(result);
         assertEquals("testUser", result.getUserName());
     }
+
+    @Test
+    void deleteExperiment_onlyDeletesCreatedExperiments() throws Exception {
+        ExperimentModel experiment = new ExperimentModel();
+        experiment.setUserName("testUser");
+        experiment.setGatewayId("testGateway");
+        ExperimentStatus status = new ExperimentStatus();
+        status.setState(ExperimentState.CREATED);
+        experiment.addToExperimentStatus(status);
+        when(registryHandler.getExperiment("exp-123")).thenReturn(experiment);
+        when(registryHandler.deleteExperiment("exp-123")).thenReturn(true);
+        boolean result = experimentService.deleteExperiment(ctx, "exp-123");
+        assertTrue(result);
+        verify(registryHandler).deleteExperiment("exp-123");
+    }
+
+    @Test
+    void deleteExperiment_rejectsNonCreatedExperiment() throws Exception {
+        ExperimentModel experiment = new ExperimentModel();
+        experiment.setUserName("testUser");
+        experiment.setGatewayId("testGateway");
+        ExperimentStatus status = new ExperimentStatus();
+        status.setState(ExperimentState.EXECUTING);
+        experiment.addToExperimentStatus(status);
+        when(registryHandler.getExperiment("exp-123")).thenReturn(experiment);
+        assertThrows(ServiceException.class, () -> 
experimentService.deleteExperiment(ctx, "exp-123"));
+    }
+
+    @Test
+    void getExperimentByAdmin_allowsSameGateway() throws Exception {
+        ExperimentModel experiment = new ExperimentModel();
+        experiment.setUserName("otherUser");
+        experiment.setGatewayId("testGateway");
+        when(registryHandler.getExperiment("exp-123")).thenReturn(experiment);
+        ExperimentModel result = experimentService.getExperimentByAdmin(ctx, 
"exp-123");
+        assertNotNull(result);
+    }
+
+    @Test
+    void getExperimentByAdmin_rejectsDifferentGateway() throws Exception {
+        ExperimentModel experiment = new ExperimentModel();
+        experiment.setUserName("otherUser");
+        experiment.setGatewayId("otherGateway");
+        when(registryHandler.getExperiment("exp-123")).thenReturn(experiment);
+        assertThrows(ServiceAuthorizationException.class, () -> 
experimentService.getExperimentByAdmin(ctx, "exp-123"));
+    }
+
+    @Test
+    void getExperimentStatus_delegatesToRegistry() throws Exception {
+        ExperimentStatus status = new ExperimentStatus();
+        status.setState(ExperimentState.COMPLETED);
+        
when(registryHandler.getExperimentStatus("exp-123")).thenReturn(status);
+        ExperimentStatus result = experimentService.getExperimentStatus(ctx, 
"exp-123");
+        assertEquals(ExperimentState.COMPLETED, result.getState());
+    }
+
+    @Test
+    void getExperimentOutputs_delegatesToRegistry() throws Exception {
+        List<OutputDataObjectType> outputs = List.of(new 
OutputDataObjectType());
+        
when(registryHandler.getExperimentOutputs("exp-123")).thenReturn(outputs);
+        List<OutputDataObjectType> result = 
experimentService.getExperimentOutputs(ctx, "exp-123");
+        assertEquals(1, result.size());
+    }
 }

Reply via email to