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 2c02d90ba884dfa516b5ed4f06227e92e7058951 Author: yasithdev <[email protected]> AuthorDate: Thu Mar 26 10:57:40 2026 -0500 feat: add remaining experiment methods to ExperimentService Adds getExperimentStatistics, getExperimentsInProject, getUserExperiments, getDetailedExperimentTree, updateExperiment, updateExperimentConfiguration, updateResourceScheduleing, validateExperiment, getJobStatuses, getJobDetails. Rewires corresponding AiravataServerHandler methods to ThriftAdapter one-liners. --- .../service/experiment/ExperimentService.java | 132 +++++++++++++++++++++ .../service/experiment/ExperimentServiceTest.java | 46 +++++++ 2 files changed, 178 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 e03b725a00..b1d61e3125 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 @@ -2,11 +2,17 @@ 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.ExperimentStatistics; 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.job.JobModel; +import org.apache.airavata.model.scheduling.ComputationalResourceSchedulingModel; import org.apache.airavata.model.status.ExperimentState; import org.apache.airavata.model.status.ExperimentStatus; +import org.apache.airavata.model.status.JobStatus; +import org.apache.airavata.model.workspace.Project; +import org.apache.airavata.model.experiment.UserConfigurationDataModel; import org.apache.airavata.registry.api.service.handler.RegistryServerHandler; import org.apache.airavata.service.context.RequestContext; import org.apache.airavata.service.exception.ServiceAuthorizationException; @@ -336,6 +342,132 @@ public class ExperimentService { } } + public ExperimentStatistics getExperimentStatistics( + RequestContext ctx, String gatewayId, long fromTime, long toTime, + String userName, String applicationName, String resourceHostName, + int limit, int offset) throws ServiceException { + try { + return registryHandler.getExperimentStatistics( + gatewayId, fromTime, toTime, userName, applicationName, + resourceHostName, null, limit, offset); + } catch (Exception e) { + throw new ServiceException("Error while retrieving experiment statistics: " + e.getMessage(), e); + } + } + + public List<ExperimentModel> getExperimentsInProject( + RequestContext ctx, String projectId, int limit, int offset) throws ServiceException { + try { + Project project = registryHandler.getProject(projectId); + if (isSharingEnabled() + && (!ctx.getUserId().equals(project.getOwner()) + || !ctx.getGatewayId().equals(project.getGatewayId()))) { + String qualifiedUserId = ctx.getUserId() + "@" + ctx.getGatewayId(); + if (!sharingHandler.userHasAccess( + ctx.getGatewayId(), qualifiedUserId, projectId, ctx.getGatewayId() + ":READ")) { + throw new ServiceAuthorizationException( + "User does not have permission to access this resource"); + } + } + return registryHandler.getExperimentsInProject(ctx.getGatewayId(), projectId, limit, offset); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while retrieving experiments in project: " + e.getMessage(), e); + } + } + + public List<ExperimentModel> getUserExperiments( + RequestContext ctx, String gatewayId, String userName, int limit, int offset) throws ServiceException { + try { + return registryHandler.getUserExperiments(gatewayId, userName, limit, offset); + } catch (Exception e) { + throw new ServiceException("Error while retrieving user experiments: " + e.getMessage(), e); + } + } + + public ExperimentModel getDetailedExperimentTree(RequestContext ctx, String experimentId) + throws ServiceException { + try { + return registryHandler.getDetailedExperimentTree(experimentId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving experiment tree: " + e.getMessage(), e); + } + } + + public void updateExperiment(RequestContext ctx, String experimentId, ExperimentModel experiment) + throws ServiceException { + try { + ExperimentModel existing = registryHandler.getExperiment(experimentId); + if (isSharingEnabled() + && (!ctx.getUserId().equals(existing.getUserName()) + || !ctx.getGatewayId().equals(existing.getGatewayId()))) { + 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 update this resource"); + } + } + if (isSharingEnabled()) { + try { + Entity entity = sharingHandler.getEntity(ctx.getGatewayId(), experimentId); + entity.setName(experiment.getExperimentName()); + entity.setDescription(experiment.getDescription()); + entity.setParentEntityId(experiment.getProjectId()); + sharingHandler.updateEntity(entity); + } catch (Exception e) { + throw new ServiceException("Failed to update entity in sharing registry", e); + } + } + registryHandler.updateExperiment(experimentId, experiment); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + throw new ServiceException("Error while updating experiment: " + e.getMessage(), e); + } + } + + public void updateExperimentConfiguration(RequestContext ctx, String experimentId, + UserConfigurationDataModel userConfiguration) throws ServiceException { + try { + registryHandler.updateExperimentConfiguration(experimentId, userConfiguration); + } catch (Exception e) { + throw new ServiceException("Error while updating experiment configuration: " + e.getMessage(), e); + } + } + + public void updateResourceScheduleing(RequestContext ctx, String experimentId, + ComputationalResourceSchedulingModel resourceScheduling) throws ServiceException { + try { + registryHandler.updateResourceScheduleing(experimentId, resourceScheduling); + } catch (Exception e) { + throw new ServiceException("Error while updating resource scheduling: " + e.getMessage(), e); + } + } + + public boolean validateExperiment(RequestContext ctx, String experimentId) throws ServiceException { + // TODO: call validation module and validate experiment + return true; + } + + public Map<String, JobStatus> getJobStatuses(RequestContext ctx, String experimentId) throws ServiceException { + try { + return registryHandler.getJobStatuses(experimentId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving job statuses: " + e.getMessage(), e); + } + } + + public List<JobModel> getJobDetails(RequestContext ctx, String experimentId) throws ServiceException { + try { + return registryHandler.getJobDetails(experimentId); + } catch (Exception e) { + throw new ServiceException("Error while retrieving job details: " + 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 cd549165b9..a62c77e095 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,10 @@ package org.apache.airavata.service.experiment; import org.apache.airavata.model.experiment.ExperimentModel; +import org.apache.airavata.model.experiment.ExperimentStatistics; import org.apache.airavata.model.status.ExperimentState; import org.apache.airavata.model.status.ExperimentStatus; +import org.apache.airavata.model.status.JobStatus; import org.apache.airavata.model.application.io.OutputDataObjectType; import org.apache.airavata.registry.api.service.handler.RegistryServerHandler; import org.apache.airavata.service.context.RequestContext; @@ -21,6 +23,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import static org.mockito.Mockito.doNothing; @ExtendWith(MockitoExtension.class) class ExperimentServiceTest { @@ -131,4 +134,47 @@ class ExperimentServiceTest { List<OutputDataObjectType> result = experimentService.getExperimentOutputs(ctx, "exp-123"); assertEquals(1, result.size()); } + + @Test + void getExperimentStatistics_delegatesToRegistry() throws Exception { + ExperimentStatistics stats = new ExperimentStatistics(); + stats.setAllExperimentCount(5); + when(registryHandler.getExperimentStatistics( + "testGateway", 1000L, 2000L, null, null, null, null, 10, 0)) + .thenReturn(stats); + ExperimentStatistics result = experimentService.getExperimentStatistics( + ctx, "testGateway", 1000L, 2000L, null, null, null, 10, 0); + assertEquals(5, result.getAllExperimentCount()); + } + + @Test + void updateExperiment_ownerCanUpdate() throws Exception { + ExperimentModel existing = new ExperimentModel(); + existing.setUserName("testUser"); + existing.setGatewayId("testGateway"); + ExperimentModel updated = new ExperimentModel(); + updated.setExperimentName("new-name"); + updated.setProjectId("proj-1"); + + when(registryHandler.getExperiment("exp-123")).thenReturn(existing); + doNothing().when(registryHandler).updateExperiment("exp-123", updated); + + // Should not throw — owner has implicit WRITE + assertDoesNotThrow(() -> experimentService.updateExperiment(ctx, "exp-123", updated)); + verify(registryHandler).updateExperiment("exp-123", updated); + } + + @Test + void getJobStatuses_delegatesToRegistry() throws Exception { + Map<String, JobStatus> statuses = Map.of("job-1", new JobStatus()); + when(registryHandler.getJobStatuses("exp-123")).thenReturn(statuses); + Map<String, JobStatus> result = experimentService.getJobStatuses(ctx, "exp-123"); + assertEquals(1, result.size()); + } + + @Test + void validateExperiment_returnsTrue() throws Exception { + boolean result = experimentService.validateExperiment(ctx, "exp-123"); + assertTrue(result); + } }
