From c5ace10d84876abe50dc6918f9002a15433a1c35 Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Thu, 28 Oct 2010 22:35:09 +0000
Subject: [PATCH] LMS-1844 Added search for experiments to the general
 information service.

SVN: 18481
---
 .../api/v1/GeneralInformationService.java     |  42 +++
 .../v1/GeneralInformationServiceLogger.java   |   9 +
 .../generic/server/api/v1/Translator.java     |  20 ++
 .../api/v1/IGeneralInformationService.java    |  16 ++
 .../generic/shared/api/v1/dto/Experiment.java | 255 ++++++++++++++++++
 .../api/v1/GeneralInformationServiceTest.java |  72 +++++
 .../api/v1/GeneralInformationServiceTest.java |  23 +-
 7 files changed, 435 insertions(+), 2 deletions(-)
 create mode 100644 openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/Experiment.java

diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
index bf016bcfa7c..224e899d18b 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
@@ -31,12 +31,14 @@ import org.springframework.stereotype.Component;
 
 import ch.systemsx.cisd.authentication.ISessionManager;
 import ch.systemsx.cisd.common.spring.IInvocationLoggerContext;
+import ch.systemsx.cisd.openbis.generic.client.web.client.exception.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.AbstractServer;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDatabaseInstanceDAO;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Role;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
@@ -44,6 +46,7 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SpaceWithProjectsAndRoleAssignments;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetRelatedEntities;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy;
 import ch.systemsx.cisd.openbis.generic.shared.dto.AuthorizationGroupPE;
@@ -54,6 +57,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.RoleAssignmentPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.Session;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier;
 
 /**
  * @author Franz-Josef Elmer
@@ -244,4 +248,42 @@ public class GeneralInformationService extends AbstractServer<IGeneralInformatio
         }
         return dataSets;
     }
+
+    public List<Experiment> listExperiments(String sessionToken, List<Project> projects,
+            String experimentTypeString)
+    {
+        checkSession(sessionToken);
+
+        // Convert the string to an experiment type
+        List<ExperimentType> experimentTypes = commonServer.listExperimentTypes(sessionToken);
+        ExperimentType experimentType = null;
+        for (ExperimentType anExperimentType : experimentTypes)
+        {
+            if (anExperimentType.getCode().equals(experimentTypeString))
+            {
+                experimentType = anExperimentType;
+            }
+        }
+        if (null == experimentType)
+        {
+            throw new UserFailureException("Unknown experiment type : " + experimentTypeString);
+        }
+
+        // Retrieve the matches for each project
+        ArrayList<Experiment> experiments = new ArrayList<Experiment>();
+
+        for (Project project : projects)
+        {
+            ProjectIdentifier projectIdentifier =
+                    new ProjectIdentifier(project.getSpaceCode(), project.getCode());
+
+            List<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment> basicExperiments =
+                    commonServer.listExperiments(sessionToken, experimentType, projectIdentifier);
+            for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment basicExperiment : basicExperiments)
+            {
+                experiments.add(Translator.translate(basicExperiment));
+            }
+        }
+        return experiments;
+    }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceLogger.java
index 2582501e1ac..371ee977c42 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceLogger.java
@@ -25,6 +25,8 @@ import ch.systemsx.cisd.common.spring.IInvocationLoggerContext;
 import ch.systemsx.cisd.openbis.generic.server.AbstractServerLogger;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Role;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
@@ -83,4 +85,11 @@ class GeneralInformationServiceLogger extends AbstractServerLogger implements
         return null;
     }
 
+    public List<Experiment> listExperiments(String sessionToken, List<Project> projects,
+            String experimentType)
+    {
+        logAccess(sessionToken, "list-experiments", "EXP_TYPE(%s)", experimentType);
+        return null;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/Translator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/Translator.java
index defaee999b8..8c014d50988 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/Translator.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/Translator.java
@@ -22,6 +22,8 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet.DataSetInitializer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Role;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment.ExperimentInitializer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample.SampleInitializer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
@@ -61,6 +63,24 @@ class Translator
         return new Sample(initializer);
     }
 
+    static Experiment translate(
+            ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment privateExperiment)
+    {
+        ExperimentInitializer initializer = new ExperimentInitializer();
+        initializer.setId(privateExperiment.getId());
+        initializer.setPermId(privateExperiment.getPermId());
+        initializer.setCode(privateExperiment.getCode());
+        initializer.setIdentifier(privateExperiment.getIdentifier());
+        initializer.setExperimentTypeCode(privateExperiment.getExperimentType().getCode());
+        List<IEntityProperty> properties = privateExperiment.getProperties();
+        for (IEntityProperty prop : properties)
+        {
+            initializer.putProperty(prop.getPropertyType().getCode(), prop.getValue());
+        }
+
+        return new Experiment(initializer);
+    }
+
     private Translator()
     {
     }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationService.java
index 1ab55ae9f39..8e47a081865 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/IGeneralInformationService.java
@@ -24,6 +24,8 @@ import org.springframework.transaction.annotation.Transactional;
 
 import ch.systemsx.cisd.common.api.IRpcService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Role;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
@@ -86,6 +88,7 @@ public interface IGeneralInformationService extends IRpcService
      * Return all samples that match the search criteria. Available since minor version 1.
      * 
      * @param searchCriteria The sample metadata values to be matched against.
+     * @since 1.1
      */
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.INSTANCE_OBSERVER)
@@ -100,4 +103,17 @@ public interface IGeneralInformationService extends IRpcService
     @RolesAllowed(RoleWithHierarchy.INSTANCE_OBSERVER)
     public List<DataSet> listDataSets(String sessionToken, List<Sample> samples);
 
+    /**
+     * Return all experiments of the given type that belong to the supplied projects. Available
+     * since minor version 2.
+     * 
+     * @param projects The projects for which we return attached experiments.
+     * @param experimentType The experiment type of the experiments we want to list.
+     * @since 1.2
+     */
+    @Transactional(readOnly = true)
+    @RolesAllowed(RoleWithHierarchy.INSTANCE_OBSERVER)
+    public List<Experiment> listExperiments(String sessionToken, List<Project> projects,
+            String experimentType);
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/Experiment.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/Experiment.java
new file mode 100644
index 00000000000..ac09770b102
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/Experiment.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2010 ETH Zuerich, CISD
+ *
+ * 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.
+ */
+
+package ch.systemsx.cisd.openbis.generic.shared.api.v1.dto;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+/**
+ * Immutable value object representing an experiment.
+ * 
+ * @author Chandrasekhar Ramakrishnan
+ */
+public final class Experiment implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Class used to initialize a new experiment instance. Necessary since all the fields of a
+     * sample are final.
+     * <p>
+     * All of the properties must be filled (non-null) before being used to initialize an
+     * Experiment, otherwise the Experiment constructor will throw an exception.
+     * 
+     * @author Chandrasekhar Ramakrishnan
+     */
+    public static final class ExperimentInitializer
+    {
+        private Long id;
+
+        private String permId;
+
+        private String code;
+
+        private String identifier;
+
+        private String experimentTypeCode;
+
+        private HashMap<String, String> properties = new HashMap<String, String>();
+
+        public void setId(Long id)
+        {
+            this.id = id;
+        }
+
+        public Long getId()
+        {
+            return id;
+        }
+
+        public void setPermId(String permId)
+        {
+            this.permId = permId;
+        }
+
+        public String getPermId()
+        {
+            return permId;
+        }
+
+        public void setCode(String code)
+        {
+            this.code = code;
+        }
+
+        public String getCode()
+        {
+            return code;
+        }
+
+        public String getIdentifier()
+        {
+            return identifier;
+        }
+
+        public void setIdentifier(String identifier)
+        {
+            this.identifier = identifier;
+        }
+
+        public void setExperimentTypeCode(String experimentTypeCode)
+        {
+            this.experimentTypeCode = experimentTypeCode;
+        }
+
+        public String getExperimentTypeCode()
+        {
+            return experimentTypeCode;
+        }
+
+        public HashMap<String, String> getProperties()
+        {
+            return properties;
+        }
+
+        public void putProperty(String propCode, String value)
+        {
+            properties.put(propCode, value);
+        }
+    }
+
+    private final Long id;
+
+    private final String permId;
+
+    private final String code;
+
+    private final String identifier;
+
+    private final String experimentTypeCode;
+
+    private final HashMap<String, String> properties;
+
+    /**
+     * Creates a new instance with the provided initializer
+     * 
+     * @throws IllegalArgumentException if some of the required information is not provided.
+     */
+    public Experiment(ExperimentInitializer initializer)
+    {
+        checkValidLong(initializer.getId(), "Unspecified id.");
+        this.id = initializer.getId();
+
+        checkValidString(initializer.getPermId(), "Unspecified permanent id.");
+        this.permId = initializer.getPermId();
+
+        checkValidString(initializer.getCode(), "Unspecified code.");
+        this.code = initializer.getCode();
+
+        checkValidString(initializer.getIdentifier(), "Unspecified identifier.");
+        this.identifier = initializer.getIdentifier();
+
+        checkValidString(initializer.getExperimentTypeCode(), "Unspecified eperiment type code.");
+        this.experimentTypeCode = initializer.getExperimentTypeCode();
+
+        this.properties = initializer.getProperties();
+    }
+
+    private void checkValidString(String string, String message) throws IllegalArgumentException
+    {
+        if (string == null || string.length() == 0)
+        {
+            throw new IllegalArgumentException(message);
+        }
+    }
+
+    private void checkValidLong(Long longValue, String message) throws IllegalArgumentException
+    {
+        if (longValue == null || longValue == 0)
+        {
+            throw new IllegalArgumentException(message);
+        }
+    }
+
+    /**
+     * Returns the experiment id.
+     */
+    public Long getId()
+    {
+        return id;
+    }
+
+    /**
+     * Returns the experiment permanent id.
+     */
+    public String getPermId()
+    {
+        return permId;
+    }
+
+    /**
+     * Returns the experiment code.
+     */
+    public String getCode()
+    {
+        return code;
+    }
+
+    /**
+     * Returns the experiment identifier.
+     */
+    public String getIdentifier()
+    {
+        return identifier;
+    }
+
+    /**
+     * Returns the experiment type code.
+     */
+    public String getExperimentTypeCode()
+    {
+        return experimentTypeCode;
+    }
+
+    public Map<String, String> getProperties()
+    {
+        return Collections.unmodifiableMap(properties);
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+        if (obj instanceof Experiment == false)
+        {
+            return false;
+        }
+
+        EqualsBuilder builder = new EqualsBuilder();
+        Experiment other = (Experiment) obj;
+        builder.append(getId(), other.getId());
+        return builder.isEquals();
+    }
+
+    @Override
+    public int hashCode()
+    {
+        HashCodeBuilder builder = new HashCodeBuilder();
+        builder.append(getId());
+        return builder.toHashCode();
+    }
+
+    @Override
+    public String toString()
+    {
+        ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
+        builder.append(getIdentifier());
+        builder.append(getExperimentTypeCode());
+        builder.append(getProperties());
+        return builder.toString();
+    }
+}
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceTest.java
index d8d8d18c30e..1e4e5c7750b 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceTest.java
@@ -33,6 +33,7 @@ import ch.rinn.restrictions.Friend;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Role;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
@@ -44,6 +45,7 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SpaceWithProjectsAndRo
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetRelatedEntities;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
@@ -51,6 +53,7 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.GroupPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.PersonPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.ProjectPE;
 import ch.systemsx.cisd.openbis.generic.shared.dto.RoleAssignmentPE;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ProjectIdentifier;
 
 /**
  * @author Franz-Josef Elmer
@@ -283,6 +286,75 @@ public class GeneralInformationServiceTest extends AbstractServerTestCase
         context.assertIsSatisfied();
     }
 
+    private void prepareSearchForExperiments()
+    {
+        context.checking(new Expectations()
+            {
+                {
+                    one(roleAssignmentDAO).listRoleAssignments();
+                    RoleAssignmentPE assignment0 =
+                            createUserAssignment("user0", null, RoleCode.ADMIN);
+                    RoleAssignmentPE assignment1 =
+                            createUserAssignment("user1", "SPACE-1", RoleCode.USER);
+                    RoleAssignmentPE assignment2 =
+                            createUserAssignment("user1", "SPACE-2", RoleCode.ADMIN);
+                    will(returnValue(Arrays.asList(assignment0, assignment1, assignment2)));
+
+                    one(groupDAO).listGroups(daoFactory.getHomeDatabaseInstance());
+                    List<GroupPE> spaces = createSpaces("SPACE-1", "SPACE-2");
+                    will(returnValue(spaces));
+
+                    one(projectDAO).listProjects(spaces.get(0));
+                    ProjectPE project1 = new ProjectPE();
+                    project1.setCode("PROJECT-1");
+                    project1.setGroup(spaces.get(0));
+                    will(returnValue(Collections.singletonList(project1)));
+
+                    one(projectDAO).listProjects(spaces.get(1));
+                    will(returnValue(Collections.emptyList()));
+
+                    ExperimentType returnExperimentType = new ExperimentType();
+                    returnExperimentType.setCode("EXP-TYPE-CODE");
+                    one(commonServer).listExperimentTypes(SESSION_TOKEN);
+                    will(returnValue(Collections.singletonList(returnExperimentType)));
+
+                    ProjectIdentifier projectIdentifier =
+                            new ProjectIdentifier("SPACE-1", "PROJECT-1");
+                    one(commonServer).listExperiments(SESSION_TOKEN, returnExperimentType,
+                            projectIdentifier);
+
+                    ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment returnExperiment =
+                            new ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment();
+                    returnExperiment.setId(new Long(1));
+                    returnExperiment.setPermId("EXP-PERMID");
+                    returnExperiment.setCode("EXP-CODE");
+                    returnExperiment.setIdentifier("/SPACE-1/PROJECT-1/EXP-CODE");
+                    returnExperiment.setExperimentType(returnExperimentType);
+                    returnExperiment.setProperties(new ArrayList<IEntityProperty>());
+                    will(returnValue(Collections.singletonList(returnExperiment)));
+                }
+            });
+    }
+
+    @Test
+    public void testListExperiments()
+    {
+        prepareGetSession();
+        prepareSearchForExperiments();
+        List<SpaceWithProjectsAndRoleAssignments> enrichedSpaces =
+                service.listSpacesWithProjectsAndRoleAssignments(SESSION_TOKEN, null);
+        ArrayList<Project> projects = new ArrayList<Project>();
+        for (SpaceWithProjectsAndRoleAssignments space : enrichedSpaces)
+        {
+            projects.addAll(space.getProjects());
+        }
+        List<Experiment> result = service.listExperiments(SESSION_TOKEN, projects, "EXP-TYPE-CODE");
+        assertEquals(1, result.size());
+        Experiment resultExperiment = result.get(0);
+        assertEquals("/SPACE-1/PROJECT-1/EXP-CODE", resultExperiment.getIdentifier());
+        context.assertIsSatisfied();
+    }
+
     private SearchCriteria createSearchCriteria()
     {
         SearchCriteria sc = new SearchCriteria();
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
index 84a1d6f0e5d..1fdd4310a9d 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
@@ -32,6 +32,7 @@ import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Role;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
@@ -89,8 +90,8 @@ public class GeneralInformationServiceTest extends SystemTestCase
                 }
             });
         checkSpace("CISD", "[/CISD/DEFAULT, /CISD/NEMO, /CISD/NOE]",
-                "[ADMIN(instance), ADMIN(space), ETL_SERVER(instance), ETL_SERVER(space)]", spaces
-                        .get(0));
+                "[ADMIN(instance), ADMIN(space), ETL_SERVER(instance), ETL_SERVER(space)]",
+                spaces.get(0));
         checkSpace("TESTGROUP", "[/TESTGROUP/TESTPROJ]",
                 "[ADMIN(instance), ADMIN(space), ETL_SERVER(instance)]", spaces.get(1));
         assertEquals(2, spaces.size());
@@ -174,4 +175,22 @@ public class GeneralInformationServiceTest extends SystemTestCase
             });
         assertEquals(expectedRoles, roles.toString());
     }
+
+    @Test
+    public void testListExperiments()
+    {
+        List<SpaceWithProjectsAndRoleAssignments> spaces =
+                generalInformationService.listSpacesWithProjectsAndRoleAssignments(sessionToken,
+                        null);
+        ArrayList<Project> projects = new ArrayList<Project>();
+        for (SpaceWithProjectsAndRoleAssignments space : spaces)
+        {
+            projects.addAll(space.getProjects());
+        }
+        List<Experiment> result =
+                generalInformationService.listExperiments(sessionToken, projects, "SIRNA_HCS");
+        assertEquals(true, result.size() > 0);
+        Experiment resultExperiment = result.get(0);
+        assertEquals("/CISD/DEFAULT/EXP-REUSE", resultExperiment.getIdentifier());
+    }
 }
-- 
GitLab