diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/entity/AbstractEntityPropertyHolder.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/entity/AbstractEntityPropertyHolder.java index e1ada597066e1ffa829e91cf36efd700a7d0e3e2..ecd61bf70d3b8567551875b18f694c1a3d2a61b4 100644 --- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/entity/AbstractEntityPropertyHolder.java +++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/entity/AbstractEntityPropertyHolder.java @@ -30,6 +30,8 @@ import java.io.Serializable; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; @JsonObject("as.dto.common.entity.AbstractEntityPropertyHolder") public abstract class AbstractEntityPropertyHolder implements Serializable, IPropertiesHolder @@ -45,13 +47,19 @@ public abstract class AbstractEntityPropertyHolder implements Serializable, IPro public abstract void setProperties(Map<String, Serializable> properties); @Override - public Serializable getProperty(String propertyName) // String!!! + public String getPropertyAsString(String propertyName) { return getProperties() != null ? PropertiesDeserializer.getPropertyAsString(getProperties().get(propertyName)) : null; } + @Override + public Serializable getProperty(String propertyName) + { + return getProperties() != null ? getProperties().get(propertyName) : null; + } + @Override public void setProperty(String propertyName, Serializable propertyValue) { @@ -65,7 +73,7 @@ public abstract class AbstractEntityPropertyHolder implements Serializable, IPro @Override public Long getIntegerProperty(String propertyName) { - String propertyValue = (String) getProperty(propertyName); + String propertyValue = getPropertyAsString(propertyName); return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Long.parseLong(propertyValue); @@ -121,7 +129,10 @@ public abstract class AbstractEntityPropertyHolder implements Serializable, IPro public ZonedDateTime getTimestampProperty(String propertyName) { String propertyValue = (String) getProperty(propertyName); - return propertyValue == null ? null : ZonedDateTime.parse(propertyValue); + return propertyValue == null ? + null : + ZonedDateTime.parse(propertyValue, + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X")); } @Override @@ -129,7 +140,7 @@ public abstract class AbstractEntityPropertyHolder implements Serializable, IPro { String value = (propertyValue == null) ? null : - propertyValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX")); + propertyValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z")); setProperty(propertyName, value); } @@ -150,56 +161,29 @@ public abstract class AbstractEntityPropertyHolder implements Serializable, IPro } @Override - public String[] getControlledVocabularyProperty(String propertyName) + public String getControlledVocabularyProperty(String propertyName) { - if (getProperties() == null || getProperties().get(propertyName) == null) - { - return null; - } - Serializable value = getProperties().get(propertyName); - if (value.getClass().isArray()) - { - Serializable[] values = (Serializable[]) value; - return Arrays.stream(values).map(x -> (String) x).toArray(String[]::new); - } else - { - String propertyValue = (String) value; - return new String[] { propertyValue }; - } + return (String) getProperty(propertyName); } @Override - public void setControlledVocabularyProperty(String propertyName, String[] propertyValue) + public void setControlledVocabularyProperty(String propertyName, String propertyValue) { setProperty(propertyName, propertyValue); } @Override - public SamplePermId[] getSampleProperty(String propertyName) + public SamplePermId getSampleProperty(String propertyName) { - if (getProperties() == null || getProperties().get(propertyName) == null) - { - return null; - } - Serializable value = getProperties().get(propertyName); - if (value.getClass().isArray()) - { - Serializable[] values = (Serializable[]) value; - return Arrays.stream(values).map(x -> new SamplePermId((String) x)) - .toArray(SamplePermId[]::new); - } else - { - String propertyValue = (String) value; - return new SamplePermId[] { new SamplePermId(propertyValue) }; - } + + String propertyValue = (String) getProperty(propertyName); + return new SamplePermId(propertyValue); } @Override - public void setSampleProperty(String propertyName, SamplePermId[] propertyValue) + public void setSampleProperty(String propertyName, SamplePermId propertyValue) { - setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue) - .map(ObjectPermId::getPermId) - .toArray(String[]::new)); + setProperty(propertyName, propertyValue == null ? null : propertyValue.getPermId()); } @Override @@ -301,8 +285,9 @@ public abstract class AbstractEntityPropertyHolder implements Serializable, IPro public void setTimestampArrayProperty(String propertyName, ZonedDateTime[] propertyValue) { String[] value = (propertyValue == null) ? null : Arrays.stream(propertyValue) - .map(dateTime -> dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"))) - .toArray(String[]::new); + .map(dateTime -> dateTime.format( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z"))) + .toArray(String[]::new); setProperty(propertyName, value); } @@ -317,4 +302,359 @@ public abstract class AbstractEntityPropertyHolder implements Serializable, IPro { setProperty(propertyName, propertyValue); } + + private <T> List<T> getListOfValues(Serializable generalValue, Function<Serializable, T> fun) + { + if (generalValue != null) + { + if (generalValue.getClass().isArray()) + { + List<T> result = new ArrayList<>(); + for (Serializable singleValue : (Serializable[]) generalValue) + { + result.add(fun.apply(singleValue)); + } + return result; + } else + { + return List.of(fun.apply(generalValue)); + } + } + return null; + } + + @Override + public List<Long> getMultiValueIntegerProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> Long.parseLong((String) x)); + } + + @Override + public void setMultiValueIntegerProperty(String propertyName, List<Long> propertyValues) + { + if (propertyValues != null) + { + setProperty(propertyName, propertyValues.toArray(Long[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<String> getMultiValueVarcharProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> (String) x); + } + + @Override + public void setMultiValueVarcharProperty(String propertyName, List<String> propertyValues) + { + if (propertyValues != null) + { + setProperty(propertyName, propertyValues.toArray(String[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + public List<String> getMultiValueControlledVocabularyProperty(String propertyName) + { + if (getProperties() == null || getProperties().get(propertyName) == null) + { + return null; + } + Serializable value = getProperties().get(propertyName); + if (value.getClass().isArray()) + { + Serializable[] values = (Serializable[]) value; + return Arrays.stream(values).map(x -> (String) x).collect(Collectors.toList()); + } else + { + return List.of((String) value); + } + } + + @Override + public void setMultiValueControlledVocabularyProperty(String propertyName, + List<String> propertyValues) + { + if (propertyValues != null) + { + setProperty(propertyName, propertyValues.toArray(String[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<SamplePermId> getMultiValueSampleProperty(String propertyName) + { + if (getProperties() == null || getProperties().get(propertyName) == null) + { + return null; + } + Serializable value = getProperties().get(propertyName); + if (value.getClass().isArray()) + { + Serializable[] values = (Serializable[]) value; + return Arrays.stream(values).map(x -> new SamplePermId((String) x)) + .collect(Collectors.toList()); + } else + { + String propertyValue = (String) value; + return List.of(new SamplePermId(propertyValue)); + } + } + + @Override + public void setMultiValueSampleProperty(String propertyName, List<SamplePermId> propertyValue) + { + setProperty(propertyName, propertyValue == null ? null : propertyValue.stream() + .map(ObjectPermId::getPermId) + .toArray(String[]::new)); + } + + @Override + public List<String> getMultiValueMultilineVarcharProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> (String) x); + } + + @Override + public void setMultiValueMultilineVarcharProperty(String propertyName, + List<String> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(String[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<Double> getMultiValueRealProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> Double.parseDouble((String) x)); + } + + @Override + public void setMultiValueRealProperty(String propertyName, List<Double> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(Double[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<ZonedDateTime> getMultiValueTimestampProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> ZonedDateTime.parse((String)x, + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X"))); + } + + @Override + public void setMultiValueTimestampProperty(String propertyName, + List<ZonedDateTime> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.stream() + .map(x -> x.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z"))) + .toArray(String[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<Boolean> getMultiValueBooleanProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> Boolean.parseBoolean((String) x)); + } + + @Override + public void setMultiValueBooleanProperty(String propertyName, List<Boolean> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(Boolean[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<String> getMultiValueHyperlinkProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> (String) x); + } + + @Override + public void setMultiValueHyperlinkProperty(String propertyName, List<String> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(String[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<String> getMultiValueXmlProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> (String) x); + } + + @Override + public void setMultiValueXmlProperty(String propertyName, List<String> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(String[]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<Long[]> getMultiValueIntegerArrayProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, + (x) -> Arrays.stream((Serializable[]) x) + .map(Serializable::toString) + .map(Long::parseLong) + .toArray(Long[]::new)); + } + + @Override + public void setMultiValueIntegerArrayProperty(String propertyName, List<Long[]> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(Long[][]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<Double[]> getMultiValueRealArrayProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, + (x) -> Arrays.stream((Serializable[]) x) + .map(Serializable::toString) + .map(Double::parseDouble) + .toArray(Double[]::new)); + } + + @Override + public void setMultiValueRealArrayProperty(String propertyName, List<Double[]> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(Double[][]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<String[]> getMultiValueStringArrayProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, + (x) -> Arrays.stream((Serializable[]) x) + .map(Serializable::toString) + .toArray(String[]::new)); + } + + @Override + public void setMultiValueStringArrayProperty(String propertyName, List<String[]> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(String[][]::new)); + } else + { + setProperty(propertyName, null); + } + } + + @Override + public List<ZonedDateTime[]> getMultiValueTimestampArrayProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, + (x) -> Arrays.stream((Serializable[]) x) + .map(Serializable::toString) + .map(dateTime -> ZonedDateTime.parse(dateTime, + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X"))) + .toArray(ZonedDateTime[]::new)); + } + + @Override + public void setMultiValueTimestampArrayProperty(String propertyName, + List<ZonedDateTime[]> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.stream() + .map(dateTimeArray -> Arrays.stream(dateTimeArray) + .map(x -> x.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss Z"))) + .toArray(String[]::new)) + .toArray(String[][]::new)); + } else + { + setProperty(propertyName, null); + } + // String[] value = (propertyValue == null) ? null : Arrays.stream(propertyValue) + // .map(dateTime -> dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"))) + // .toArray(String[]::new); + // setProperty(propertyName, value); + } + + @Override + public List<String> getMultiValueJsonProperty(String propertyName) + { + Serializable propertyValue = getProperty(propertyName); + return getListOfValues(propertyValue, (x) -> (String) x); + } + + @Override + public void setMultiValueJsonProperty(String propertyName, List<String> propertyValue) + { + if (propertyValue != null) + { + setProperty(propertyName, propertyValue.toArray(String[]::new)); + } else + { + setProperty(propertyName, null); + } + } } diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/interfaces/IPropertiesHolder.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/interfaces/IPropertiesHolder.java index 7a5346813db382a1c786d6d085486400d355ca0f..a2517eef5b126aabcc4bb7b0eb3a5541709160e4 100644 --- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/interfaces/IPropertiesHolder.java +++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/interfaces/IPropertiesHolder.java @@ -17,6 +17,7 @@ package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces; import java.io.Serializable; import java.time.ZonedDateTime; +import java.util.List; import java.util.Map; import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId; @@ -33,6 +34,8 @@ public interface IPropertiesHolder void setProperties(Map<String, Serializable> properties); + String getPropertyAsString(String propertyName); + Serializable getProperty(String propertyName); void setProperty(String propertyName, Serializable propertyValue); @@ -61,13 +64,13 @@ public interface IPropertiesHolder void setBooleanProperty(String propertyName, Boolean propertyValue); - String[] getControlledVocabularyProperty(String propertyName); + String getControlledVocabularyProperty(String propertyName); - void setControlledVocabularyProperty(String propertyName, String[] propertyValue); + void setControlledVocabularyProperty(String propertyName, String propertyValue); - SamplePermId[] getSampleProperty(String propertyName); + SamplePermId getSampleProperty(String propertyName); - void setSampleProperty(String propertyName, SamplePermId[] propertyValue); + void setSampleProperty(String propertyName, SamplePermId propertyValue); String getHyperlinkProperty(String propertyName); @@ -97,4 +100,66 @@ public interface IPropertiesHolder void setJsonProperty(String propertyName, String propertyValue); + // Multi-value properties + + List<Long> getMultiValueIntegerProperty(String propertyName); + + void setMultiValueIntegerProperty(String propertyName, List<Long> propertyValues); + + List<String> getMultiValueVarcharProperty(String propertyName); + + void setMultiValueVarcharProperty(String propertyName, List<String> propertyValues); + + List<String> getMultiValueMultilineVarcharProperty(String propertyName); + + void setMultiValueMultilineVarcharProperty(String propertyName, List<String> propertyValue); + + List<Double> getMultiValueRealProperty(String propertyName); + + void setMultiValueRealProperty(String propertyName, List<Double> propertyValue); + + List<ZonedDateTime> getMultiValueTimestampProperty(String propertyName); + + void setMultiValueTimestampProperty(String propertyName, List<ZonedDateTime> propertyValue); + + List<Boolean> getMultiValueBooleanProperty(String propertyName); + + void setMultiValueBooleanProperty(String propertyName, List<Boolean> propertyValue); + + List<String> getMultiValueHyperlinkProperty(String propertyName); + + void setMultiValueHyperlinkProperty(String propertyName, List<String> propertyValue); + + List<String> getMultiValueXmlProperty(String propertyName); + + void setMultiValueXmlProperty(String propertyName, List<String> propertyValue); + + List<String> getMultiValueControlledVocabularyProperty(String propertyName); + + void setMultiValueControlledVocabularyProperty(String propertyName, List<String> propertyValue); + + List<SamplePermId> getMultiValueSampleProperty(String propertyName); + + void setMultiValueSampleProperty(String propertyName, List<SamplePermId> propertyValue); + + List<Long[]> getMultiValueIntegerArrayProperty(String propertyName); + + void setMultiValueIntegerArrayProperty(String propertyName, List<Long[]> propertyValue); + + List<Double[]> getMultiValueRealArrayProperty(String propertyName); + + void setMultiValueRealArrayProperty(String propertyName, List<Double[]> propertyValue); + + List<String[]> getMultiValueStringArrayProperty(String propertyName); + + void setMultiValueStringArrayProperty(String propertyName, List<String[]> propertyValue); + + List<ZonedDateTime[]> getMultiValueTimestampArrayProperty(String propertyName); + + void setMultiValueTimestampArrayProperty(String propertyName, List<ZonedDateTime[]> propertyValue); + + List<String> getMultiValueJsonProperty(String propertyName); + + void setMultiValueJsonProperty(String propertyName, List<String> propertyValue); + } diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdateEntityPropertyExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdateEntityPropertyExecutor.java index 8f0ca454644e5e3f8f5eba830a456a42a56a4cfc..13084d9623d9bb637597c3e97a30e570547449b3 100644 --- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdateEntityPropertyExecutor.java +++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdateEntityPropertyExecutor.java @@ -463,7 +463,17 @@ public class UpdateEntityPropertyExecutor implements IUpdateEntityPropertyExecut { entityProperties.add(EntityHelper.createNewProperty(entry.getKey(), entry.getValue())); } - Set<? extends EntityPropertyPE> existingProperties = propertiesHolder.getProperties(); + List<? extends EntityPropertyPE> propertiesList = new ArrayList<>(propertiesHolder.getProperties()); + // Add existing properties in the order of creation + Set<EntityPropertyPE> existingProperties = propertiesList.stream() + .filter((EntityPropertyPE a) -> a.getId() != null) + .sorted( + Comparator.comparing((EntityPropertyPE a) -> a.getId()) + ).collect(Collectors.toCollection(LinkedHashSet::new)); + // Newly created sample properties doesn't have id so add then at the end + propertiesList.stream() + .filter((EntityPropertyPE a) -> a.getId() == null) + .forEach(existingProperties::add); Map<String, List<Object>> existingPropertyValuesByCode = new HashMap<String, List<Object>>(); for (EntityPropertyPE existingProperty : existingProperties) diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/dataset/DataSetQuery.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/dataset/DataSetQuery.java index fe9a8838d1f67918100f4f3e32c0922d38412c70..698726f000a47a08160123d7ea4dba6633d17427 100644 --- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/dataset/DataSetQuery.java +++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/dataset/DataSetQuery.java @@ -117,7 +117,7 @@ public interface DataSetQuery extends ObjectQuery + "left join material_types mt on m.maty_id = mt.id " + "join data_set_type_property_types etpt on p.dstpt_id = etpt.id " + "join property_types pt on etpt.prty_id = pt.id " - + "where p.ds_id = any(?{1}) order by id", parameterBindings = { LongSetMapper.class }, + + "where p.ds_id = any(?{1}) order by id asc", parameterBindings = { LongSetMapper.class }, resultSetBinding = PropertyRecordDataObjectBinding.class, fetchSize = FETCH_SIZE) public List<PropertyRecord> getProperties(LongSet dataSetIds); diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentQuery.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentQuery.java index f38a1602e1a941ca3836894e569161356895fa6a..1426d01095239a253c97332cce7ca267ed3f5594 100644 --- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentQuery.java +++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/experiment/ExperimentQuery.java @@ -85,7 +85,7 @@ public interface ExperimentQuery extends ObjectQuery + "left join material_types mt on m.maty_id = mt.id " + "join experiment_type_property_types etpt on p.etpt_id = etpt.id " + "join property_types pt on etpt.prty_id = pt.id " - + "where p.expe_id = any(?{1}) order by id", parameterBindings = { LongSetMapper.class }, + + "where p.expe_id = any(?{1}) order by p.id asc", parameterBindings = { LongSetMapper.class }, resultSetBinding = PropertyRecordDataObjectBinding.class, fetchSize = FETCH_SIZE) public List<PropertyRecord> getProperties(LongSet experimentIds); diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleQuery.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleQuery.java index 0c77078bfc997d1b5a010c3eb9f0d119f29e4aa1..b81ce66e8c4bcfcd0e222be4436f9cf9b6ffe2dd 100644 --- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleQuery.java +++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleQuery.java @@ -82,7 +82,7 @@ public interface SampleQuery extends ObjectQuery + "left join material_types mt on m.maty_id = mt.id " + "join sample_type_property_types etpt on p.stpt_id = etpt.id " + "join property_types pt on etpt.prty_id = pt.id " - + "where p.samp_id = any(?{1}) order by id", parameterBindings = { LongSetMapper.class }, + + "where p.samp_id = any(?{1}) order by id asc", parameterBindings = { LongSetMapper.class }, resultSetBinding = PropertyRecordDataObjectBinding.class, fetchSize = FETCH_SIZE) public List<PropertyRecord> getProperties(LongSet sampleIds); @@ -101,7 +101,8 @@ public interface SampleQuery extends ObjectQuery + "from sample_properties p " + "join sample_type_property_types etpt on p.stpt_id = etpt.id " + "join property_types pt on etpt.prty_id = pt.id " - + "where p.samp_prop_id is not null and p.samp_id = any(?{1})", parameterBindings = { + + "where p.samp_prop_id is not null and p.samp_id = any(?{1}) " + + "order by p.id asc", parameterBindings = { LongSetMapper.class }, fetchSize = FETCH_SIZE) public List<SamplePropertyRecord> getSampleProperties(LongSet sampleIds); diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/EntityPropertiesConverter.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/EntityPropertiesConverter.java index 9eef7a021908b618402bd7c41ac53a7929bed908..9870031fab2b1d8e6bf26da063a063cfdf1e55ef 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/EntityPropertiesConverter.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/EntityPropertiesConverter.java @@ -19,9 +19,12 @@ import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Pattern; +import java.util.stream.Collectors; import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.openbis.generic.shared.basic.IIdHolder; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.*; import org.apache.log4j.Logger; import org.hibernate.Session; @@ -35,10 +38,6 @@ import ch.systemsx.cisd.common.collection.TableMap.UniqueKeyViolationStrategy; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.openbis.generic.server.dataaccess.util.KeyExtractorFactory; import ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant; -import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode; -import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; -import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ManagedProperty; -import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialIdentifier; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.api.IManagedInputWidgetDescription; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.api.IManagedProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.api.IPerson; @@ -82,6 +81,11 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert } }; + private static final String SEPARATOR = ","; + + private static final List<DataTypeCode> ARRAY_TYPES = List.of(DataTypeCode.ARRAY_STRING, + DataTypeCode.ARRAY_INTEGER, DataTypeCode.ARRAY_REAL, DataTypeCode.ARRAY_TIMESTAMP); + private static final String NO_ENTITY_PROPERTY_VALUE_FOR_S = "Value of mandatory property '%s' not specified."; @@ -281,22 +285,24 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert if (isNullOrBlank(valueOrNull) == false) { List<T> results = new ArrayList<>(); - if(propertyType.isMultiValue() && valueOrNull.getClass().isArray()) { - for (Serializable value : (Serializable[]) valueOrNull) + Serializable parsedValue = translateProperty(propertyType, valueOrNull); + if(propertyType.isMultiValue() && parsedValue.getClass().isArray()) { + for (Serializable value : (Serializable[]) parsedValue) { - String translatedValue = - extendedETPT.translate(registrator, (String) value); - final String validatedValue = + Serializable translatedValue = + extendedETPT.translate(registrator, value); + final Serializable validatedValue = propertyValueValidator.validatePropertyValue(propertyType, translatedValue); results.addAll(createEntityProperty(registrator, propertyType, entityTypePropertyTypePE, validatedValue)); + } } else { - String translatedValue = extendedETPT.translate(registrator, property.tryGetAsString()); + Serializable translatedValue = extendedETPT.translate(registrator, parsedValue); - final String validatedValue = + final Serializable validatedValue = propertyValueValidator.validatePropertyValue(propertyType, translatedValue); results.addAll(createEntityProperty(registrator, propertyType, entityTypePropertyTypePE, validatedValue)); @@ -306,6 +312,56 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert return null; } + private Serializable translateProperty(PropertyTypePE propertyType, Serializable value) { + if(value == null || !value.getClass().equals(String.class)) { + //Nothing to translate + return value; + } + + String regex = "(?<!\\\\)" + Pattern.quote(SEPARATOR); + String propertyValue = value.toString().trim(); + if(propertyValue.isEmpty()) { + return null; + } + + if(propertyType.isMultiValue()) { + if(propertyValue.startsWith("[")) { + propertyValue = propertyValue.substring(1, propertyValue.length()-1).trim(); + } + if(propertyValue.isEmpty()) { + return null; + } + if(ARRAY_TYPES.contains(propertyType.getType().getCode())) { + // Multi-value array properties + String multiArrayRegex = "\\],\\s*\\["; + if(propertyValue.startsWith("[")) { + propertyValue = propertyValue.substring(1, propertyValue.length()-1).trim(); + } + return Arrays.stream(propertyValue.split(multiArrayRegex)) + .map(String::trim) + .map(x -> Arrays.stream(x.split(regex)) + .map(String::trim) + .toArray(String[]::new)) + .toArray(String[][]::new); + } else { + return Arrays.stream(propertyValue.split(regex)) + .map(String::trim) + .toArray(String[]::new); + } + } else { + if(ARRAY_TYPES.contains(propertyType.getType().getCode())) { + if(propertyValue.startsWith("[")) { + propertyValue = propertyValue.substring(1, propertyValue.length()-1); + } + return Arrays.stream(propertyValue.split(regex)) + .map(String::trim) + .toArray(String[]::new); + } else { + return propertyValue; + } + } + } + private Serializable getPropertyValue(final IEntityProperty property) { Serializable result = property.getValue(); if(result != null) { @@ -329,31 +385,12 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert private final <T extends EntityPropertyPE> List<T> createEntityProperty( final PersonPE registrator, final PropertyTypePE propertyType, - final EntityTypePropertyTypePE entityTypePropertyType, final String value) + final EntityTypePropertyTypePE entityTypePropertyType, final Serializable value) { List<T> entityProperties = new ArrayList<>(); - String val = value; - List<DataTypeCode> arrayTypes = List.of(DataTypeCode.ARRAY_STRING, - DataTypeCode.ARRAY_INTEGER, DataTypeCode.ARRAY_REAL, DataTypeCode.ARRAY_TIMESTAMP); - if (propertyType.isMultiValue() && !arrayTypes.contains(propertyType.getType().getCode())) - { - if (val.startsWith("[")) - { - val = val.substring(1, val.length() - 1); - } - for (String v : val.split(",")) - { - String singleValue = v.trim(); - final T entityProperty = getEntityPropertyBase(registrator, entityTypePropertyType); - setPropertyValue(entityProperty, propertyType, singleValue); - entityProperties.add(entityProperty); - } - } else - { - final T entityProperty = getEntityPropertyBase(registrator, entityTypePropertyType); - setPropertyValue(entityProperty, propertyType, val); - entityProperties.add(entityProperty); - } + final T entityProperty = getEntityPropertyBase(registrator, entityTypePropertyType); + setPropertyValue(entityProperty, propertyType, value); + entityProperties.add(entityProperty); return entityProperties; } @@ -500,9 +537,9 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert } if (isNullOrBlank(value) == false) { - final String validated = + final Serializable validated = propertyValueValidator.validatePropertyValue(propertyType, value); - return validated; + return validated.toString(); } return null; } @@ -519,15 +556,21 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert @Override public final <T extends EntityPropertyPE> void setPropertyValue(final T entityProperty, - final PropertyTypePE propertyType, final String validatedValue) + final PropertyTypePE propertyType, final Serializable validatedValue) { assert validatedValue != null; - if (validatedValue.startsWith(BasicConstant.ERROR_PROPERTY_PREFIX)) - { - // save errors as strings - entityProperty.setUntypedValue(validatedValue, null, null, null, null, null, null, null, - null); - } else + + if(validatedValue.getClass().equals(String.class)) { + String value = (String) validatedValue; + if (value.startsWith(BasicConstant.ERROR_PROPERTY_PREFIX)) + { + // save errors as strings + entityProperty.setUntypedValue(value, null, null, null, null, null, null, null, + null); + } + } + + { final VocabularyTermPE vocabularyTerm = complexPropertyValueHelper.tryGetVocabularyTerm(validatedValue, propertyType); @@ -544,7 +587,7 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert complexPropertyValueHelper.tryGetTimestampArray(validatedValue, propertyType); final String jsonValue = complexPropertyValueHelper.tryGetJsonValue(validatedValue, propertyType); - entityProperty.setUntypedValue(validatedValue, vocabularyTerm, material, sample, + entityProperty.setUntypedValue(validatedValue.toString(), vocabularyTerm, material, sample, integerArray, realArray, stringArray, timestampArray, jsonValue); } } @@ -565,20 +608,27 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert { // TODO: create null property history entry } - + Set<T> oldPropertiesTemp = new HashSet<>(oldProperties); final List<T> convertedProperties = convertPropertiesForUpdate(newProperties, entityType.getCode(), author); - final Set<T> set = new HashSet<T>(); - for (T newProperty : convertedProperties) - { + final Set<T> set = new LinkedHashSet<>(); + + for (int i=0; i< convertedProperties.size(); i++) { + T newProperty = convertedProperties.get(i); PropertyTypePE propertyType = newProperty.getEntityTypePropertyType().getPropertyType(); - T existingProperty; - if (propertyType.isMultiValue()) - { - existingProperty = tryFindMulti(oldProperties, propertyType, newProperty); - } else + T existingProperty = null; + if(!propertyType.isMultiValue()) { - existingProperty = tryFind(oldProperties, propertyType); + existingProperty = tryFind(oldPropertiesTemp, propertyType); + } else { + List<T> oldMulti = oldPropertiesTemp.stream() + .filter(oldProp -> oldProp.getEntityTypePropertyType().getPropertyType().equals(propertyType)) + .sorted(Comparator.comparing(IIdHolder::getId)) + .collect(Collectors.toList()); + if(!oldMulti.isEmpty()) { + existingProperty = oldMulti.get(0); + oldPropertiesTemp.remove(existingProperty); + } } if (existingProperty != null) @@ -601,9 +651,13 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert set.add(existingProperty); } else { + if (propertyType.isMultiValue()) { + newProperty.setIndex(i); + } // TODO: create new property history entry set.add(newProperty); } + } return set; } @@ -619,6 +673,7 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert String oldValue = oldProperty.tryGetUntypedValue(); if (oldValue != null && oldValue.equals(propertyValue)) { + oldProperties.remove(oldProperty); return oldProperty; } } @@ -715,6 +770,7 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert { if (oldProperty.getEntityTypePropertyType().getPropertyType().equals(propertyType)) { + oldProperties.remove(oldProperty); return oldProperty; } } @@ -782,42 +838,47 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert } @SuppressWarnings("unchecked") - String translate(PersonPE personPE, String propertyValue) + Serializable translate(PersonPE personPE, Serializable value) { - if (inputWidgetDescriptions.isEmpty() - || propertyValue == null - || propertyValue.startsWith(BasicConstant.ERROR_PROPERTY_PREFIX) - || propertyValue.startsWith( - BasicConstant.MANAGED_PROPERTY_JSON_PREFIX) == false) - { - return propertyValue; + if(value == null || !value.getClass().equals(String.class)) { + return value; } - try + else { - List<?> readValue = - new ObjectMapper().readValue(propertyValue - .substring(BasicConstant.MANAGED_PROPERTY_JSON_PREFIX.length()), - List.class); - ManagedProperty managedProperty = new ManagedProperty(); - IPerson person = PersonTranslator.translateToIPerson(personPE); + String propertyValue = (String) value; + if (inputWidgetDescriptions.isEmpty() + || propertyValue.startsWith(BasicConstant.ERROR_PROPERTY_PREFIX) + || propertyValue.startsWith(BasicConstant.MANAGED_PROPERTY_JSON_PREFIX) == false) + { + return propertyValue; + } + try + { + List<?> readValue = + new ObjectMapper().readValue(propertyValue + .substring(BasicConstant.MANAGED_PROPERTY_JSON_PREFIX.length()), + List.class); + ManagedProperty managedProperty = new ManagedProperty(); + IPerson person = PersonTranslator.translateToIPerson(personPE); - List<Map<String, String>> bindingsList = new ArrayList<Map<String, String>>(); + List<Map<String, String>> bindingsList = new ArrayList<Map<String, String>>(); - for (Object row : readValue) - { - if (row instanceof Map == false) + for (Object row : readValue) { - continue; + if (row instanceof Map == false) + { + continue; + } + + bindingsList.add((Map<String, String>) row); } - bindingsList.add((Map<String, String>) row); + evaluator.updateFromRegistrationForm(managedProperty, person, bindingsList); + return managedProperty.getValue(); + } catch (Exception ex) + { + throw CheckedExceptionTunnel.wrapIfNecessary(ex); } - - evaluator.updateFromRegistrationForm(managedProperty, person, bindingsList); - return (String) managedProperty.getValue(); - } catch (Exception ex) - { - throw CheckedExceptionTunnel.wrapIfNecessary(ex); } } @@ -855,12 +916,19 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert this.entityInfoProvider = entityInfoProvider; } - public SamplePE tryGetSample(String value, PropertyTypePE propertyType) + public SamplePE tryGetSample(Serializable val, PropertyTypePE propertyType) { if (propertyType.getType().getCode() != DataTypeCode.SAMPLE) { return null; // this is not a property of SAMPLE type } + String value; + if(val.getClass().equals(Sample.class)) { + value = ((Sample)val).getPermId(); + } else { + value = val.toString(); + } + ISampleDAO sampleDAO = daoFactory.getSampleDAO(); String samplePermId = value; if (samplePermId.startsWith("[")) @@ -885,14 +953,14 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert return samples.get(0); } - public MaterialPE tryGetMaterial(String value, PropertyTypePE propertyType) + public MaterialPE tryGetMaterial(Serializable value, PropertyTypePE propertyType) { if (propertyType.getType().getCode() != DataTypeCode.MATERIAL) { return null; // this is not a property of MATERIAL type } MaterialIdentifier materialIdentifier = - MaterialIdentifier.tryCreate(value, propertyType.getMaterialType()); + MaterialIdentifier.tryCreate((String)value, propertyType.getMaterialType()); if (materialIdentifier == null) { return null; @@ -917,7 +985,7 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert return material; } - public VocabularyTermPE tryGetVocabularyTerm(final String value, + public VocabularyTermPE tryGetVocabularyTerm(final Serializable value, final PropertyTypePE propertyType) { if (propertyType.getType().getCode() != DataTypeCode.CONTROLLEDVOCABULARY) @@ -930,7 +998,7 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert { return null; } - final VocabularyTermPE term = vocabulary.tryGetVocabularyTerm(value); + final VocabularyTermPE term = vocabulary.tryGetVocabularyTerm((String)value); if (term != null) { return term; @@ -940,69 +1008,71 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert vocabulary.getCode()); } - private static final String SEPARATOR = ","; - public Long[] tryGetIntegerArray(final String value, final PropertyTypePE propertyType) + + public Long[] tryGetIntegerArray(final Serializable value, final PropertyTypePE propertyType) { DataTypeCode code = propertyType.getType().getCode(); if (code != DataTypeCode.ARRAY_INTEGER) { return null; } - if (value == null || value.trim().isEmpty()) + if (value == null || !value.getClass().isArray() || ((Serializable[])value).length == 0) { return null; } - return Arrays.stream(value.split(SEPARATOR)) - .map(x -> Long.parseLong(x.trim())) + return Arrays.stream((Serializable[])value) + .map(x -> Long.parseLong(x.toString().trim())) .toArray(Long[]::new); } - public Double[] tryGetRealArray(final String value, final PropertyTypePE propertyType) + public Double[] tryGetRealArray(final Serializable value, final PropertyTypePE propertyType) { DataTypeCode code = propertyType.getType().getCode(); if (code != DataTypeCode.ARRAY_REAL) { return null; } - if (value == null || value.trim().isEmpty()) + if (value == null || !value.getClass().isArray() || ((Serializable[])value).length == 0) { return null; } - return Arrays.stream(value.split(SEPARATOR)) - .map(x -> Double.parseDouble(x.trim())) + return Arrays.stream((Serializable[])value) + .map(x -> Double.parseDouble(x.toString().trim())) .toArray(Double[]::new); } - public String[] tryGetStringArray(final String value, final PropertyTypePE propertyType) + public String[] tryGetStringArray(final Serializable value, final PropertyTypePE propertyType) { DataTypeCode code = propertyType.getType().getCode(); if (code != DataTypeCode.ARRAY_STRING) { return null; } - if (value == null || value.trim().isEmpty()) + if (value == null || !value.getClass().isArray() || ((Serializable[])value).length == 0) { return null; } - // Special regex to allow strings with ',' character - String regex = "(?<!\\\\)" + Pattern.quote(SEPARATOR); - return Arrays.stream(value.split(regex)) - .map(String::trim) + return Arrays.stream((Serializable[])value) + .map(Serializable::toString) .toArray(String[]::new); } - public Date[] tryGetTimestampArray(final String value, final PropertyTypePE propertyType) + public Date[] tryGetTimestampArray(final Serializable value, final PropertyTypePE propertyType) { DataTypeCode code = propertyType.getType().getCode(); if (code != DataTypeCode.ARRAY_TIMESTAMP) { return null; } + if (value == null || !value.getClass().isArray() || ((Serializable[])value).length == 0) + { + return null; + } SimpleDateFormat format = new SimpleDateFormat(BasicConstant.DATE_HOURS_MINUTES_SECONDS_PATTERN); - return Arrays.stream(value.split(SEPARATOR)) - .map(x -> parseDateFromString(x, format)) + return Arrays.stream((Serializable[])value) + .map(x -> parseDateFromString((String)x, format)) .toArray(Date[]::new); } @@ -1017,14 +1087,14 @@ public final class EntityPropertiesConverter implements IEntityPropertiesConvert } } - public String tryGetJsonValue(final String value, final PropertyTypePE propertyType) + public String tryGetJsonValue(final Serializable value, final PropertyTypePE propertyType) { DataTypeCode code = propertyType.getType().getCode(); - if (code != DataTypeCode.JSON) + if (code != DataTypeCode.JSON || value == null) { return null; } - return value; + return value.toString(); } } diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IEntityPropertiesConverter.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IEntityPropertiesConverter.java index edce2f19e146b6fd4d263fe0fc7ed748fe5ea141..3088939737bd2d67ed4e4c77abe1cab1ed933176 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IEntityPropertiesConverter.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IEntityPropertiesConverter.java @@ -15,6 +15,7 @@ */ package ch.systemsx.cisd.openbis.generic.server.dataaccess; +import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; @@ -62,7 +63,7 @@ public interface IEntityPropertiesConverter * Modifies value of given {@link EntityPropertyPE}. Value should be already validated. */ public <T extends EntityPropertyPE> void setPropertyValue(final T entityProperty, - final PropertyTypePE propertyType, final String validatedValue); + final PropertyTypePE propertyType, final Serializable validatedValue); /** Updates Set<T> of properties. */ public <T extends EntityPropertyPE> Set<T> updateProperties(Collection<T> oldProperties, diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IPropertyValueValidator.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IPropertyValueValidator.java index 5cef2a3d4dac33a39efb70c038321707ff902320..f2de1690fae53c2db229ab87f288fbf372e0b0a5 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IPropertyValueValidator.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IPropertyValueValidator.java @@ -18,6 +18,8 @@ package ch.systemsx.cisd.openbis.generic.server.dataaccess; import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.openbis.generic.shared.dto.PropertyTypePE; +import java.io.Serializable; + /** * A property value validator. * @@ -30,7 +32,7 @@ public interface IPropertyValueValidator * * @return the validated value. It does not implicitly equal given <var>value</var> */ - public String validatePropertyValue(final PropertyTypePE propertyType, final String value) + public Serializable validatePropertyValue(final PropertyTypePE propertyType, final Serializable value) throws UserFailureException; } diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/PropertyValidator.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/PropertyValidator.java index adc7c71407627c3cadd1d9241f083f5d8c1987df..10ea6923b3c2642f247097b3ae9618fe7191d567 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/PropertyValidator.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/PropertyValidator.java @@ -15,9 +15,13 @@ */ package ch.systemsx.cisd.openbis.generic.server.dataaccess; +import java.io.Serializable; +import java.util.Arrays; import java.util.EnumMap; import java.util.Map; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm; import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Document; @@ -61,14 +65,14 @@ public final class PropertyValidator implements IPropertyValueValidator } @Override - public final String validatePropertyValue(final PropertyTypePE propertyType, final String value) + public final Serializable validatePropertyValue(final PropertyTypePE propertyType, final Serializable value) throws UserFailureException { assert propertyType != null : "Unspecified property type."; assert value != null : "Unspecified value."; // don't validate error messages and placeholders - if (value.startsWith(BasicConstant.ERROR_PROPERTY_PREFIX)) + if (value.getClass().equals(String.class) && ((String)value).startsWith(BasicConstant.ERROR_PROPERTY_PREFIX)) { return value; } @@ -118,26 +122,37 @@ public final class PropertyValidator implements IPropertyValueValidator * @return the validated value. Note that it can differ from the given one. * @throws UserFailureException if given <var>value</var> is not valid. */ - public String validate(final String value) throws UserFailureException; + public Serializable validate(final Serializable value) throws UserFailureException; } private final static class SampleValidator implements IDataTypeValidator { @Override - public String validate(String value) throws UserFailureException { + public Serializable validate(Serializable value) throws UserFailureException { assert value != null : "Unspecified value."; - if (StringUtils.isBlank(value)) - { - return null; - } - - if (value.startsWith("/")) { - // Is well formed identifier? + if(value.getClass().isArray()) { + Serializable[] arrayValues = (Serializable[]) value; + if(arrayValues.length == 0) { + return null; + } } else { - // Is well formed permId? + String stringValue; + if(value.getClass().equals(Sample.class)) { + stringValue = ((Sample) value).getPermId(); + } else { + stringValue = value.toString(); + } + if (StringUtils.isBlank(stringValue)) + { + return null; + } + if (stringValue.startsWith("/")) { + // Is well formed identifier? + } else { + // Is well formed permId? + } } - return value; } } @@ -145,14 +160,21 @@ public final class PropertyValidator implements IPropertyValueValidator private final static class JsonValidator implements IDataTypeValidator { @Override - public String validate(String value) throws UserFailureException { + public Serializable validate(Serializable value) throws UserFailureException { assert value != null : "Unspecified value."; - if (StringUtils.isBlank(value)) - { - return null; + if(value.getClass().isArray()) { + Serializable[] arrayValues = (Serializable[]) value; + if(arrayValues.length == 0) { + return null; + } + } else { + String val = (String) value; + if (StringUtils.isBlank(val)) + { + return null; + } } - //TODO: implement validation for json return value; } } @@ -167,14 +189,13 @@ public final class PropertyValidator implements IPropertyValueValidator } @Override - public String validate(String value) throws UserFailureException { + public Serializable validate(Serializable value) throws UserFailureException { assert value != null : "Unspecified value."; - if (StringUtils.isBlank(value)) - { - return null; + if(!value.getClass().isArray()) { + throw UserFailureException.fromTemplate("Array value '%s' is not valid. " + + "Provided value is not an array", value); } - //TODO: implement validation for array types return value; } } @@ -196,29 +217,40 @@ public final class PropertyValidator implements IPropertyValueValidator // @Override - public final String validate(final String value) throws UserFailureException + public Serializable validate(final Serializable value) throws UserFailureException { assert value != null : "Unspecified value."; assert vocabulary != null : "Unspecified vocabulary."; - String upperCaseValue = value.toUpperCase(); - boolean guard = true; - if(propertyTypePE.isMultiValue()) { - if(upperCaseValue.startsWith("[") && upperCaseValue.endsWith("]")) { - upperCaseValue = upperCaseValue.substring(1, upperCaseValue.length()-1); - } - final String[] split = upperCaseValue.split(","); - for(String singleValue : split) { - guard = guard && hasTerm(singleValue.trim()); + if(value.getClass().isArray()) { + Serializable[] arrayValues = (Serializable[]) value; + if(arrayValues.length == 0) { + return null; } + return Arrays.stream(arrayValues) + .map(x -> validateSingleValue((String)x)) + .toArray(Serializable[]::new); } else { - guard = hasTerm(upperCaseValue); + String val = value.toString(); + if(value.getClass().equals(VocabularyTerm.class)) { + val = ((VocabularyTerm)value).getCode(); + } + + if (StringUtils.isBlank(val)) + { + return null; + } + return validateSingleValue(val); } - if(guard) { + } + + private Serializable validateSingleValue(final String value) { + String upperCaseValue = value.toUpperCase(); + if(hasTerm(upperCaseValue)) { return upperCaseValue; } throw UserFailureException.fromTemplate("Vocabulary value '%s' of property '%s' is not valid. " - + "It must exist in '%s' controlled vocabulary %s", upperCaseValue, propertyTypePE.getCode(), + + "It must exist in '%s' controlled vocabulary %s", upperCaseValue, propertyTypePE.getCode(), vocabulary.getCode(), getVocabularyDetails()); } @@ -277,9 +309,10 @@ public final class PropertyValidator implements IPropertyValueValidator // @Override - public final String validate(final String value) throws UserFailureException + public final Serializable validate(final Serializable val) throws UserFailureException { - assert value != null : "Unspecified value."; + assert val != null : "Unspecified value."; + String value = (String) val; if (StringUtils.isBlank(value)) { @@ -335,10 +368,23 @@ public final class PropertyValidator implements IPropertyValueValidator // @Override - public final String validate(final String value) throws UserFailureException + public final Serializable validate(final Serializable value) throws UserFailureException { assert value != null : "Unspecified value."; + if(value.getClass().isArray()) { + for(Serializable singleValue : (Serializable[]) value) { + validateSingleValue(singleValue); + } + } else { + validateSingleValue(value); + } + // validated value is valid + return value; + } + + private Serializable validateSingleValue(Serializable val) { + String value = (String) val; // parsing checks if the value is a well-formed XML document Document document = XmlUtils.parseXmlDocument(value); if (xmlSchema != null) @@ -356,7 +402,6 @@ public final class PropertyValidator implements IPropertyValueValidator e.getMessage()); } } - // validated value is valid return value; } diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/PropertyBuilder.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/PropertyBuilder.java index 2365ca8265b9825638945571f4b272a81c7c1df5..c0cc08e92cf2ec026d6ddf38290391771dd7c7e7 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/PropertyBuilder.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/builders/PropertyBuilder.java @@ -17,6 +17,7 @@ package ch.systemsx.cisd.openbis.generic.shared.basic.dto.builders; import static ch.systemsx.cisd.openbis.generic.shared.basic.BasicConstant.CANONICAL_DATE_FORMAT_PATTERN; +import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date; @@ -109,6 +110,12 @@ public class PropertyBuilder return this; } + public PropertyBuilder value(Serializable value) + { + property.setValue(value); + return this; + } + public PropertyBuilder value(int value) { type(DataTypeCode.INTEGER); diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java index 33d7fa71720a848731003c4e9fa280beb400fb59..09b2c94428cbb73786a5e7d1488e10745e0713da 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java @@ -760,9 +760,10 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements this.experimentFrozen = experimentFrozen; } - private Set<DataSetPropertyPE> properties = new HashSet<DataSetPropertyPE>(); + private Set<DataSetPropertyPE> properties = new LinkedHashSet<>(); @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "entity", orphanRemoval = true) + @OrderBy(clause = "id ASC") @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @BatchSize(size = 100) private Set<DataSetPropertyPE> getDataSetProperties() diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/EntityPropertyPE.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/EntityPropertyPE.java index 88088a965a04cac746d3b95eecb0140f397bdcc0..da0ae21f8a2312f664257c15d3eb3dcca9b51c8a 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/EntityPropertyPE.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/EntityPropertyPE.java @@ -108,6 +108,11 @@ public abstract class EntityPropertyPE extends HibernateAbstractRegistrationHold protected boolean entityFrozen; + /** + * Special field for multi-value properties hashcode computing + */ + protected transient Long index; + public <T extends EntityTypePropertyTypePE> void setEntityTypePropertyType( final T entityTypePropertyType) { @@ -119,6 +124,11 @@ public abstract class EntityPropertyPE extends HibernateAbstractRegistrationHold this.id = id; } + public void setIndex(final long index) + { + this.index = index; + } + public void setEntityFrozen(boolean frozen) { this.entityFrozen = frozen; @@ -415,6 +425,8 @@ public abstract class EntityPropertyPE extends HibernateAbstractRegistrationHold builder.append(getEntity()); builder.append(getEntityTypePropertyType()); builder.append(tryGetUntypedValue()); + builder.append(index); + builder.append(id); return builder.toHashCode(); } diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java index e3b98e5452b0a233cb3624779936319d2b7bbd3c..3453188e291a41c21b10afc93ff055b308b40d41 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/ExperimentPE.java @@ -98,7 +98,7 @@ public class ExperimentPE extends AttachmentHolderPE implements private DeletionPE deletion; - private Set<ExperimentPropertyPE> properties = new HashSet<ExperimentPropertyPE>(); + private Set<ExperimentPropertyPE> properties = new LinkedHashSet<>(); private List<DataPE> dataSets = new ArrayList<DataPE>(); @@ -326,6 +326,7 @@ public class ExperimentPE extends AttachmentHolderPE implements } @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "entity", orphanRemoval = true) + @OrderBy(clause = "id ASC") @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @BatchSize(size = 100) private Set<ExperimentPropertyPE> getExperimentProperties() diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java index 3a1449798799f2c911cb376da9e247ff73a224a9..2c0f577783a64b7c3bdbc0b777e9fcfa7d37d84b 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java @@ -241,7 +241,7 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co */ private Integer originalDeletion; - private Set<SamplePropertyPE> properties = new HashSet<SamplePropertyPE>(); + private Set<SamplePropertyPE> properties = new LinkedHashSet<>(); /** * Person who registered this entity. @@ -464,6 +464,7 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co } @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "entity", orphanRemoval = true) + @OrderBy(clause = "id ASC") @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) @BatchSize(size = 100) private Set<SamplePropertyPE> getSampleProperties() diff --git a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/util/SimplePropertyValidator.java b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/util/SimplePropertyValidator.java index 2c8f111115c0431ccb7e6e925ef9cbd360c5cd9c..21aaa99cc4486ed2dd971c2dc6a6708f269ff054 100644 --- a/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/util/SimplePropertyValidator.java +++ b/server-application-server/source/java/ch/systemsx/cisd/openbis/generic/shared/util/SimplePropertyValidator.java @@ -140,7 +140,7 @@ public class SimplePropertyValidator return builder.toString(); } else { - String propertyValue = (String) value; + String propertyValue = value.toString(); // don't validate error messages and placeholders if (propertyValue.startsWith(BasicConstant.ERROR_PROPERTY_PREFIX)) { diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdateEntityPropertyExecutorTest.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdateEntityPropertyExecutorTest.java index 77a6ce3585cf0606b9860f97e35c2cc82bbbc03c..4f3e21974e671cc5dc604e00ed53b70175b0f605 100644 --- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdateEntityPropertyExecutorTest.java +++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/property/UpdateEntityPropertyExecutorTest.java @@ -145,6 +145,12 @@ public class UpdateEntityPropertyExecutorTest extends AbstractEntityPropertyExec throw new UnsupportedOperationException(); } + @Override + public String getPropertyAsString(String propertyName) + { + throw new UnsupportedOperationException(); + } + @Override public String getProperty(String propertyName) { @@ -170,26 +176,52 @@ public class UpdateEntityPropertyExecutorTest extends AbstractEntityPropertyExec } @Override - public String[] getControlledVocabularyProperty(String propertyName) + public String getControlledVocabularyProperty(String propertyName) { throw new UnsupportedOperationException(); } @Override public void setControlledVocabularyProperty(String propertyName, - String[] propertyValue) + String propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getMultiValueControlledVocabularyProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueControlledVocabularyProperty(String propertyName, + List<String> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<SamplePermId> getMultiValueSampleProperty(String propertyName) { throw new UnsupportedOperationException(); } @Override - public SamplePermId[] getSampleProperty(String propertyName) + public void setMultiValueSampleProperty(String propertyName, + List<SamplePermId> propertyValue) { throw new UnsupportedOperationException(); } @Override - public void setSampleProperty(String propertyName, SamplePermId[] propertyValue) + public SamplePermId getSampleProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setSampleProperty(String propertyName, SamplePermId propertyValue) { throw new UnsupportedOperationException(); } @@ -339,6 +371,176 @@ public class UpdateEntityPropertyExecutorTest extends AbstractEntityPropertyExec throw new UnsupportedOperationException(); } + @Override + public List<Long> getMultiValueIntegerProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueIntegerProperty(String propertyName, + List<Long> propertyValues) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getMultiValueVarcharProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueVarcharProperty(String propertyName, + List<String> propertyValues) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getMultiValueMultilineVarcharProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueMultilineVarcharProperty(String propertyName, + List<String> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<Double> getMultiValueRealProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueRealProperty(String propertyName, + List<Double> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<ZonedDateTime> getMultiValueTimestampProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueTimestampProperty(String propertyName, + List<ZonedDateTime> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<Boolean> getMultiValueBooleanProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueBooleanProperty(String propertyName, + List<Boolean> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getMultiValueHyperlinkProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueHyperlinkProperty(String propertyName, + List<String> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getMultiValueXmlProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueXmlProperty(String propertyName, + List<String> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<Long[]> getMultiValueIntegerArrayProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueIntegerArrayProperty(String propertyName, + List<Long[]> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<Double[]> getMultiValueRealArrayProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueRealArrayProperty(String propertyName, + List<Double[]> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<String[]> getMultiValueStringArrayProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueStringArrayProperty(String propertyName, + List<String[]> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<ZonedDateTime[]> getMultiValueTimestampArrayProperty( + String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueTimestampArrayProperty(String propertyName, + List<ZonedDateTime[]> propertyValue) + { + throw new UnsupportedOperationException(); + } + + @Override + public List<String> getMultiValueJsonProperty(String propertyName) + { + throw new UnsupportedOperationException(); + } + + @Override + public void setMultiValueJsonProperty(String propertyName, + List<String> propertyValue) + { + throw new UnsupportedOperationException(); + } + }; MapBatch<IPropertiesHolder, IEntityInformationWithPropertiesHolder> batch = new MapBatch<>(0, 0, 1, Collections.singletonMap(holder, entity), 1); diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractSearchPropertyTest.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractSearchPropertyTest.java index 30400c294e6d5be9f130e31d5c563d642acc55b8..0d2a3777ec79cd00cf4d973c521f58c23a9b74fa 100644 --- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractSearchPropertyTest.java +++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractSearchPropertyTest.java @@ -839,7 +839,7 @@ public abstract class AbstractSearchPropertyTest extends AbstractTest final VocabularyPermId vocabularyPermId = createControlledVocabulary(sessionToken, "SEASONS"); final PropertyTypePermId propertyTypeId = createAPropertyType(sessionToken, DataType.CONTROLLEDVOCABULARY, - vocabularyPermId); + vocabularyPermId, false); final ObjectPermId entityPermId = createEntity(sessionToken, propertyTypeId, value); final AbstractEntitySearchCriteria<?> searchCriteria = createSearchCriteria(); new StringQueryInjector(searchCriteria, propertyTypeId).buildCriteria(queryString); @@ -875,7 +875,7 @@ public abstract class AbstractSearchPropertyTest extends AbstractTest final VocabularyPermId vocabularyPermId = createControlledVocabulary(sessionToken, vocabularyCode); final PropertyTypePermId propertyTypeId = createAPropertyType(sessionToken, DataType.CONTROLLEDVOCABULARY, - vocabularyPermId, propertyTypeCode); + vocabularyPermId, propertyTypeCode, false); final ObjectPermId entityPermId = createEntity(sessionToken, propertyTypeId, value); final AbstractEntitySearchCriteria<?> searchCriteria = createSearchCriteria(); new VocabularyQueryInjector(searchCriteria, propertyTypeCode).buildCriteria(queryString); diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java index 084f44a657a152f71bf0de45b086ca27b08db991..972d9eced25f0e2c51965d2301e8ff0eb246e290 100644 --- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java +++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractTest.java @@ -1487,17 +1487,22 @@ public class AbstractTest extends SystemTestCase protected PropertyTypePermId createAPropertyType(final String sessionToken, final DataType dataType) { - return createAPropertyType(sessionToken, dataType, new VocabularyPermId("ORGANISM")); + return createAPropertyType(sessionToken, dataType, new VocabularyPermId("ORGANISM"), false); + } + + protected PropertyTypePermId createAPropertyType(final String sessionToken, final DataType dataType, final boolean multiValue) + { + return createAPropertyType(sessionToken, dataType, new VocabularyPermId("ORGANISM"), multiValue); } protected PropertyTypePermId createAPropertyType(final String sessionToken, final DataType dataType, - final VocabularyPermId vocabularyPermId) + final VocabularyPermId vocabularyPermId, final boolean multiValue) { - return createAPropertyType(sessionToken, dataType, vocabularyPermId, "TYPE-" + System.currentTimeMillis()); + return createAPropertyType(sessionToken, dataType, vocabularyPermId, "TYPE-" + System.currentTimeMillis(), multiValue); } protected PropertyTypePermId createAPropertyType(final String sessionToken, final DataType dataType, - final VocabularyPermId vocabularyPermId, final String code) + final VocabularyPermId vocabularyPermId, final String code, final boolean multiValue) { final PropertyTypeCreation creation = new PropertyTypeCreation(); creation.setCode(code); @@ -1508,7 +1513,7 @@ public class AbstractTest extends SystemTestCase { creation.setVocabularyId(vocabularyPermId); } - creation.setMultiValue(false); + creation.setMultiValue(multiValue); return v3api.createPropertyTypes(sessionToken, Collections.singletonList(creation)).get(0); } diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateDataSetTest.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateDataSetTest.java index 59018087e35b98a7fb7d88228b2e9a617e9c6ad6..4d888fd737044d20b0422b09a68edfb6764d485b 100644 --- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateDataSetTest.java +++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateDataSetTest.java @@ -16,6 +16,7 @@ package ch.ethz.sis.openbis.systemtest.asapi.v3; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertEqualsNoOrder; import static org.testng.Assert.assertNull; import java.io.Serializable; @@ -2107,6 +2108,63 @@ public class CreateDataSetTest extends AbstractDataSetTest assertEquals(dataSet.getProperties().get(propertyType.getPermId()), "2008-12-24 03:04:00 +0100"); assertEquals(dataSet.getProperties().size(), 2); } + + @Test + public void testCreateWithPropertyOfTypeTimestampDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.TIMESTAMP); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + creation.setTimestampProperty(propertyType.getPermId(), time1); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEquals(dataSet.getTimestampProperty(propertyType.getPermId()), ZonedDateTime.parse("2023-05-16T11:22:33+02:00")); + assertEquals(dataSet.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeTimestamp() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.TIMESTAMP, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + ZonedDateTime time2 = ZonedDateTime.parse("2023-05-18T11:17:03+02"); + creation.setMultiValueTimestampProperty(propertyType.getPermId(), List.of(time1, time2)); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEqualsNoOrder(dataSet.getMultiValueTimestampProperty(propertyType.getPermId()).toArray(ZonedDateTime[]::new), new ZonedDateTime[] {time1, time2}); + assertEquals(dataSet.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeJson() @@ -2134,6 +2192,94 @@ public class CreateDataSetTest extends AbstractDataSetTest assertEquals(dataSet.getJsonProperty(propertyType.getPermId()), "{\"key\": \"value\", \"array\": [1, 2, 3]}"); assertEquals(dataSet.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeJson() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.JSON, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueJsonProperty(propertyType.getPermId(), List.of("{\"key\": \"value\", \"array\":[1,2,3]}", "{\"key\": \"value2\", \"array\":[]}")); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<String> properties = dataSet.getMultiValueJsonProperty(propertyType.getPermId()); + assertEquals(properties.size(), 2); + assertEqualsNoOrder(properties.toArray(String[]::new), new String[] {"{\"key\": \"value\", \"array\": [1, 2, 3]}", "{\"key\": \"value2\", \"array\": []}"}); + assertEquals(dataSet.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeInteger() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.INTEGER, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setProperty(propertyType.getPermId(), new Long[] {1L, 1L, 3L}); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + assertEqualsNoOrder((Serializable[]) dataSet.getProperties().get(propertyType.getPermId()), new Serializable[] { "1", "1", "3" }); + assertEqualsNoOrder(dataSet.getMultiValueIntegerProperty(propertyType.getPermId()).toArray(Long[]::new), new Long[] { 1L, 1L, 3L }); + assertEquals(dataSet.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeIntegerDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.INTEGER, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueIntegerProperty(propertyType.getPermId(), List.of(1L, 2L, 3L)); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + assertEqualsNoOrder((Serializable[]) dataSet.getProperties().get(propertyType.getPermId()), new Serializable[] { "1", "2", "3" }); + assertEqualsNoOrder(dataSet.getMultiValueIntegerProperty(propertyType.getPermId()).toArray(Long[]::new), new Long[] { 1L, 2L, 3L }); + assertEquals(dataSet.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeArrayInteger() @@ -2161,6 +2307,80 @@ public class CreateDataSetTest extends AbstractDataSetTest assertEquals(dataSet.getIntegerArrayProperty(propertyType.getPermId()), new Long[]{1L, 2L, 3L}); assertEquals(dataSet.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayInteger() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setProperty(propertyType.getPermId(), new Long[][]{ new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} }); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = dataSet.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(dataSet.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayIntegerDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueIntegerArrayProperty(propertyType.getPermId(), List.of( new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} )); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = dataSet.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(dataSet.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeArrayReal() @@ -2188,6 +2408,80 @@ public class CreateDataSetTest extends AbstractDataSetTest assertEquals(dataSet.getRealArrayProperty(propertyType.getPermId()), new Double[]{1.0, 2.0, 3.0}); assertEquals(dataSet.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayReal() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_REAL, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setProperty(propertyType.getPermId(), new Double[][]{ new Double[]{1.0, 2.0, 3.0}, new Double[]{4.0, 5.0, 6.0} }); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = dataSet.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(dataSet.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayRealDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueIntegerArrayProperty(propertyType.getPermId(), List.of( new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} )); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = dataSet.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(dataSet.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeArrayString() @@ -2215,6 +2509,41 @@ public class CreateDataSetTest extends AbstractDataSetTest assertEquals(dataSet.getStringArrayProperty(propertyType.getPermId()), new String[]{"a", "b", "c"}); assertEquals(dataSet.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayString() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_STRING, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueStringArrayProperty(propertyType.getPermId(), List.of(new String[]{"a,a", "b", "c"}, new String[]{"a", "b", "c", "d"})); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + List<String[]> result = dataSet.getMultiValueStringArrayProperty(propertyType.getPermId()); + assertEquals(result.size(), 2); + for(String[] prop : result) { + if(prop.length > 3) { + assertEqualsNoOrder(prop, new String[]{"a", "b", "c", "d"}); + } else { + assertEqualsNoOrder(prop, new String[]{"a,a", "b", "c"}); + } + } + assertEquals(dataSet.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeArrayTimestamp() @@ -2244,6 +2573,45 @@ public class CreateDataSetTest extends AbstractDataSetTest assertEquals(dataSet.getTimestampArrayProperty(propertyType.getPermId()), new ZonedDateTime[]{time1, time2}); assertEquals(dataSet.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayTimestamp() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_TIMESTAMP, true); + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + ZonedDateTime time2 = ZonedDateTime.parse("2023-05-18T11:17:03+02"); + ZonedDateTime time3 = ZonedDateTime.parse("2023-05-20T11:10:03+02"); + creation.setMultiValueTimestampArrayProperty(propertyType.getPermId(), List.of(new ZonedDateTime[]{time1, time2}, new ZonedDateTime[]{time3, time2, time1})); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<ZonedDateTime[]> properties = dataSet.getMultiValueTimestampArrayProperty(propertyType.getPermId()); + assertEquals(properties.size(), 2); + for(ZonedDateTime[] prop : properties) { + if(prop.length == 2) { + assertEqualsNoOrder(prop, new ZonedDateTime[]{time1, time2}); + } else { + assertEqualsNoOrder(prop, new ZonedDateTime[]{time1, time2, time3}); + } + } + assertEquals(dataSet.getProperties().size(), 2); + } @Test @@ -2265,7 +2633,45 @@ public class CreateDataSetTest extends AbstractDataSetTest DataSetCreation creation = physicalDataSetCreation(); creation.setTypeId(dataSetType); creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); - creation.setControlledVocabularyProperty(propertyType.getPermId(), new String[] {"DOG", "HUMAN"}); + creation.setMultiValueControlledVocabularyProperty(propertyType.getPermId(), List.of("DOG", "HUMAN")); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + List<String> vocabProperties = dataSet.getMultiValueControlledVocabularyProperty(propertyType.getPermId()); + Collections.sort(vocabProperties); + assertEquals(vocabProperties, List.of("DOG", "HUMAN")); + assertEquals(dataSet.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyVocabularyDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + final PropertyTypeCreation propertyTypeCreation = new PropertyTypeCreation(); + propertyTypeCreation.setCode("TYPE-" + System.currentTimeMillis()); + propertyTypeCreation.setDataType(DataType.CONTROLLEDVOCABULARY); + propertyTypeCreation.setLabel("label"); + propertyTypeCreation.setDescription("description"); + propertyTypeCreation.setMultiValue(true); + propertyTypeCreation.setVocabularyId(new VocabularyPermId("ORGANISM")); + PropertyTypePermId propertyType = v3api.createPropertyTypes(sessionToken, Collections.singletonList(propertyTypeCreation)).get(0); + + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueControlledVocabularyProperty(propertyType.getPermId(), List.of("DOG", "HUMAN")); // When List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); @@ -2277,9 +2683,9 @@ public class CreateDataSetTest extends AbstractDataSetTest fetchOptions.withSampleProperties(); DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); - String[] vocabProperties = dataSet.getControlledVocabularyProperty(propertyType.getPermId()); - Arrays.sort(vocabProperties); - assertEquals(vocabProperties, new String[] {"DOG", "HUMAN"}); + List<String> vocabProperties = dataSet.getMultiValueControlledVocabularyProperty(propertyType.getPermId()); + Collections.sort(vocabProperties); + assertEquals(vocabProperties, List.of("DOG", "HUMAN")); assertEquals(dataSet.getProperties().size(), 2); } @@ -2348,6 +2754,73 @@ public class CreateDataSetTest extends AbstractDataSetTest assertEquals(sampleProps, new Serializable[]{"200811050919915-8", sample2.getPermId().getPermId()}); assertEquals(dataSet.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertySampleDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + //Create sample + PropertyTypePermId propertyType1 = createASamplePropertyType(sessionToken, null); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType1, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY-" + System.currentTimeMillis()); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setProperty(propertyType1.getPermId(), "200811050919915-8"); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + + SampleFetchOptions sampleFetchOptions = new SampleFetchOptions(); + sampleFetchOptions.withProperties(); + sampleFetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, sampleFetchOptions).get(sampleIds.get(0)); + + final PropertyTypeCreation propertyTypeCreation = new PropertyTypeCreation(); + propertyTypeCreation.setCode("TYPE-" + System.currentTimeMillis()); + propertyTypeCreation.setDataType(DataType.SAMPLE); + propertyTypeCreation.setLabel("label"); + propertyTypeCreation.setDescription("description"); + propertyTypeCreation.setMultiValue(true); + PropertyTypePermId propertyType = v3api.createPropertyTypes(sessionToken, Collections.singletonList(propertyTypeCreation)).get(0); + + EntityTypePermId dataSetType = createADataSetType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + + DataSetCreation creation = physicalDataSetCreation(); + creation.setTypeId(dataSetType); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueSampleProperty(propertyType.getPermId(), List.of(new SamplePermId("200811050919915-8"), sampleIds.get(0))); + + // When + List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(dataSetIds.size(), 1); + DataSetFetchOptions fetchOptions = new DataSetFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + DataSet dataSet = v3api.getDataSets(sessionToken, dataSetIds, fetchOptions).get(dataSetIds.get(0)); + assertEquals(dataSet.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + Map<String, Sample[]> sampleProperties = dataSet.getSampleProperties(); + + Sample[] samples = sampleProperties.get(propertyType.getPermId()); + Serializable[] sampleProps = Arrays.stream(samples).map(x -> x.getPermId().getPermId()).sorted().toArray(String[]::new); + assertEquals(sampleProps, new Serializable[]{"200811050919915-8", sample2.getPermId().getPermId()}); + + sampleProps = (Serializable[]) dataSet.getProperties().get(propertyType.getPermId()); + Arrays.sort(sampleProps); + assertEquals(sampleProps, new Serializable[]{"200811050919915-8", sample2.getPermId().getPermId()}); + assertEquals(dataSet.getProperties().size(), 2); + } @Test public void testCreateWithMultiValuePropertySample2() { @@ -2390,7 +2863,7 @@ public class CreateDataSetTest extends AbstractDataSetTest DataSetCreation creation = physicalDataSetCreation(); creation.setTypeId(dataSetType); creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); - creation.setSampleProperty(propertyType.getPermId(), new SamplePermId[]{ new SamplePermId("/CISD/CL1"), sampleIds.get(0)}); + creation.setMultiValueSampleProperty(propertyType.getPermId(), List.of(new SamplePermId("/CISD/CL1"), sampleIds.get(0))); // When List<DataSetPermId> dataSetIds = v3api.createDataSets(sessionToken, Arrays.asList(creation)); diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateExperimentTest.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateExperimentTest.java index 78555ce9665dd53b9bc0bf084430aa3075e573c3..aed6a6ed9198c887e02ec93ca66eb861bab3ca7c 100644 --- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateExperimentTest.java +++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateExperimentTest.java @@ -16,19 +16,13 @@ package ch.ethz.sis.openbis.systemtest.asapi.v3; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertEqualsNoOrder; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import java.io.Serializable; import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.create.PropertyTypeCreation; import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions; @@ -870,6 +864,67 @@ public class CreateExperimentTest extends AbstractExperimentTest assertEquals(experiment2.getProperties().size(), 2); } + @Test + public void testCreateWithPropertyOfTypeTimestampDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.TIMESTAMP); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_SAMPLE_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + creation.setTimestampProperty(propertyType.getPermId(), time1); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEquals(experiment2.getTimestampProperty(propertyType.getPermId()), time1); + assertEquals(experiment2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeTimestamp() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.TIMESTAMP, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_SAMPLE_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + ZonedDateTime time2 = ZonedDateTime.parse("2023-05-18T11:17:03+02"); + creation.setMultiValueTimestampProperty(propertyType.getPermId(), List.of(time1, time2)); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEqualsNoOrder(experiment2.getMultiValueTimestampProperty(propertyType.getPermId()).toArray(ZonedDateTime[]::new), new ZonedDateTime[] {time1, time2}); + assertEquals(experiment2.getProperties().size(), 2); + } + @Test public void testCreateWithPropertyOfTypeJson() { @@ -899,6 +954,99 @@ public class CreateExperimentTest extends AbstractExperimentTest assertEquals(experiment2.getProperties().size(), 2); } + @Test + public void testCreateWithMultiValuePropertyOfTypeJson() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.JSON, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_SAMPLE_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueJsonProperty(propertyType.getPermId(), List.of("{\"key\": \"value\", \"array\":[1,2,3]}", "{\"key\": \"value2\", \"array\":[]}")); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + List<String> properties = experiment2.getMultiValueJsonProperty(propertyType.getPermId()); + assertEquals(properties.size(), 2); + assertEqualsNoOrder(properties.toArray(String[]::new), new String[] {"{\"key\": \"value\", \"array\": [1, 2, 3]}", "{\"key\": \"value2\", \"array\": []}"}); + assertEquals(experiment2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeInteger() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.INTEGER, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_MULTI_INTEGER_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setProperty(propertyType.getPermId(), new Long[] {1L, 1L, 3L}); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEqualsNoOrder((Serializable[]) experiment2.getProperties().get(propertyType.getPermId()), new Serializable[] { "1", "1", "3" }); + assertEqualsNoOrder(experiment2.getMultiValueIntegerProperty(propertyType.getPermId()).toArray(Long[]::new), new Long[] { 1L, 1L, 3L }); + assertEquals(experiment2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeIntegerDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.INTEGER, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_MULTI_INTEGER_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueIntegerProperty(propertyType.getPermId(), List.of(1L, 2L, 3L)); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEqualsNoOrder((Serializable[]) experiment2.getProperties().get(propertyType.getPermId()), new Serializable[] { "1", "2", "3" }); + assertEqualsNoOrder(experiment2.getMultiValueIntegerProperty(propertyType.getPermId()).toArray(Long[]::new), new Long[] { 1L, 2L, 3L }); + assertEquals(experiment2.getProperties().size(), 2); + } + @Test public void testCreateWithPropertyOfTypeArrayInteger() { @@ -908,7 +1056,7 @@ public class CreateExperimentTest extends AbstractExperimentTest EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); ExperimentCreation creation = new ExperimentCreation(); - creation.setCode("EXPERIMENT_WITH_SAMPLE_PROPERTY"); + creation.setCode("EXPERIMENT_WITH_ARRAY_INTEGER_PROPERTY"); creation.setTypeId(experimentType); creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); @@ -928,6 +1076,85 @@ public class CreateExperimentTest extends AbstractExperimentTest assertEquals(experiment2.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayInteger() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_MULTI_VALUE_INTEGER_ARRAY_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setProperty(propertyType.getPermId(), new Long[][]{ new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} }); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = experiment2.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(experiment2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayIntegerDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_MULTI_VALUE_INTEGER_ARRAY_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueIntegerArrayProperty(propertyType.getPermId(), List.of( new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} )); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = experiment2.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(experiment2.getProperties().size(), 2); + } + @Test public void testCreateWithPropertyOfTypeArrayReal() { @@ -937,7 +1164,7 @@ public class CreateExperimentTest extends AbstractExperimentTest EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); ExperimentCreation creation = new ExperimentCreation(); - creation.setCode("EXPERIMENT_WITH_SAMPLE_PROPERTY"); + creation.setCode("EXPERIMENT_WITH_REAL_PROPERTY"); creation.setTypeId(experimentType); creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); @@ -957,6 +1184,84 @@ public class CreateExperimentTest extends AbstractExperimentTest assertEquals(experiment2.getProperties().size(), 2); } + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayReal() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_REAL, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_MULTI_VALUE_REAL_ARRAY_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setProperty(propertyType.getPermId(), new Double[][]{ new Double[]{1.0, 2.0, 3.0}, new Double[]{4.0, 5.0, 6.0} }); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = experiment2.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(experiment2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayRealDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_MULTI_VALUE_INTEGER_ARRAY_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueIntegerArrayProperty(propertyType.getPermId(), List.of( new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} )); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = experiment2.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(experiment2.getProperties().size(), 2); + } + @Test public void testCreateWithPropertyOfTypeArrayString() { @@ -966,11 +1271,11 @@ public class CreateExperimentTest extends AbstractExperimentTest EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); ExperimentCreation creation = new ExperimentCreation(); - creation.setCode("EXPERIMENT_WITH_SAMPLE_PROPERTY"); + creation.setCode("EXPERIMENT_WITH_ARRAY_STRING_PROPERTY"); creation.setTypeId(experimentType); creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); - creation.setStringArrayProperty(propertyType.getPermId(), new String[]{"a", "b", "c"}); + creation.setStringArrayProperty(propertyType.getPermId(), new String[]{"a,a", "b", "c"}); // When List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); @@ -982,7 +1287,44 @@ public class CreateExperimentTest extends AbstractExperimentTest fetchOptions.withSampleProperties(); Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); - assertEquals(experiment2.getStringArrayProperty(propertyType.getPermId()), new String[]{"a", "b", "c"}); + assertEquals(experiment2.getStringArrayProperty(propertyType.getPermId()), new String[]{"a,a", "b", "c"}); + assertEquals(experiment2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayString() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_STRING, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_ARRAY_STRING_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueStringArrayProperty(propertyType.getPermId(), List.of(new String[]{"a,a", "b", "c"}, new String[]{"a", "b", "c", "d"})); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + List<String[]> result = experiment2.getMultiValueStringArrayProperty(propertyType.getPermId()); + assertEquals(result.size(), 2); + for(String[] prop : result) { + if(prop.length > 3) { + assertEqualsNoOrder(prop, new String[]{"a", "b", "c", "d"}); + } else { + assertEqualsNoOrder(prop, new String[]{"a,a", "b", "c"}); + } + } assertEquals(experiment2.getProperties().size(), 2); } @@ -995,7 +1337,7 @@ public class CreateExperimentTest extends AbstractExperimentTest EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); ExperimentCreation creation = new ExperimentCreation(); - creation.setCode("EXPERIMENT_WITH_SAMPLE_PROPERTY"); + creation.setCode("EXPERIMENT_WITH_ARRAY_TIMESTAMP_PROPERTY"); creation.setTypeId(experimentType); creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); @@ -1017,6 +1359,47 @@ public class CreateExperimentTest extends AbstractExperimentTest assertEquals(experiment2.getProperties().size(), 2); } + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayTimestamp() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_TIMESTAMP, true); + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_ARRAY_TIMESTAMP_PROPERTY"); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + ZonedDateTime time2 = ZonedDateTime.parse("2023-05-18T11:17:03+02"); + ZonedDateTime time3 = ZonedDateTime.parse("2023-05-20T11:10:03+02"); + creation.setMultiValueTimestampArrayProperty(propertyType.getPermId(), List.of(new ZonedDateTime[]{time1, time2}, new ZonedDateTime[]{time3, time2, time1})); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<ZonedDateTime[]> properties = experiment2.getMultiValueTimestampArrayProperty(propertyType.getPermId()); + assertEquals(properties.size(), 2); + for(ZonedDateTime[] prop : properties) { + if(prop.length == 2) { + assertEqualsNoOrder(prop, new ZonedDateTime[]{time1, time2}); + } else { + assertEqualsNoOrder(prop, new ZonedDateTime[]{time1, time2, time3}); + } + } + assertEquals(experiment2.getProperties().size(), 2); + } + @Test public void testCreateWithMultiValuePropertyVocabulary() { @@ -1039,7 +1422,7 @@ public class CreateExperimentTest extends AbstractExperimentTest creation.setTypeId(experimentType); creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); - creation.setControlledVocabularyProperty(propertyType.getPermId(), new String[] {"DOG", "HUMAN"}); + creation.setProperty(propertyType.getPermId(), new String[] {"DOG", "HUMAN"}); // When List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); @@ -1051,12 +1434,54 @@ public class CreateExperimentTest extends AbstractExperimentTest fetchOptions.withSampleProperties(); Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); - String[] vocabProperties = experiment2.getControlledVocabularyProperty(propertyType.getPermId()); + String[] vocabProperties = Arrays.stream((Serializable[])experiment2.getProperty(propertyType.getPermId())) + .map(Serializable::toString) + .toArray(String[]::new); Arrays.sort(vocabProperties); assertEquals(vocabProperties, new String[] {"DOG", "HUMAN"}); assertEquals(experiment2.getProperties().size(), 2); } + @Test + public void testCreateWithMultiValuePropertyVocabularyDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + final PropertyTypeCreation propertyTypeCreation = new PropertyTypeCreation(); + propertyTypeCreation.setCode("TYPE-" + System.currentTimeMillis()); + propertyTypeCreation.setDataType(DataType.CONTROLLEDVOCABULARY); + propertyTypeCreation.setLabel("label"); + propertyTypeCreation.setDescription("description"); + propertyTypeCreation.setMultiValue(true); + propertyTypeCreation.setVocabularyId(new VocabularyPermId("ORGANISM")); + PropertyTypePermId propertyType = v3api.createPropertyTypes(sessionToken, Collections.singletonList(propertyTypeCreation)).get(0); + + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_MULTI_VOCAB_PROPERTY-" + System.currentTimeMillis()); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueControlledVocabularyProperty(propertyType.getPermId(), List.of("DOG", "HUMAN")); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + List<String> vocabProperties = experiment2.getMultiValueControlledVocabularyProperty(propertyType.getPermId()); + Collections.sort(vocabProperties); + assertEquals(vocabProperties, List.of("DOG", "HUMAN")); + assertEquals(experiment2.getProperties().size(), 2); + } + @Test public void testCreateWithMultiValuePropertySample() { @@ -1124,6 +1549,78 @@ public class CreateExperimentTest extends AbstractExperimentTest assertEquals(experiment2.getProperties().size(), 2); } + @Test + public void testCreateWithMultiValuePropertySampleDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + //Create sample + PropertyTypePermId propertyType1 = createASamplePropertyType(sessionToken, null); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType1, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY-" + System.currentTimeMillis()); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setProperty(propertyType1.getPermId(), "200811050919915-8"); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + + SampleFetchOptions sampleFetchOptions = new SampleFetchOptions(); + sampleFetchOptions.withProperties(); + sampleFetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, sampleFetchOptions).get(sampleIds.get(0)); + + final PropertyTypeCreation propertyTypeCreation = new PropertyTypeCreation(); + propertyTypeCreation.setCode("TYPE-" + System.currentTimeMillis()); + propertyTypeCreation.setDataType(DataType.SAMPLE); + propertyTypeCreation.setLabel("label"); + propertyTypeCreation.setDescription("description"); + propertyTypeCreation.setMultiValue(true); + PropertyTypePermId propertyType = v3api.createPropertyTypes(sessionToken, Collections.singletonList(propertyTypeCreation)).get(0); + + EntityTypePermId experimentType = createAnExperimentType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + ExperimentCreation creation = new ExperimentCreation(); + creation.setCode("EXPERIMENT_WITH_SAMPLE_PROPERTY-" + System.currentTimeMillis()); + creation.setTypeId(experimentType); + creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); + creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + creation.setMultiValueSampleProperty(propertyType.getPermId(), List.of(new SamplePermId("200811050919915-8"), sampleIds.get(0))); + + // When + List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); + + // Then + assertEquals(experimentIds.size(), 1); + ExperimentFetchOptions fetchOptions = new ExperimentFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Experiment experiment2 = v3api.getExperiments(sessionToken, experimentIds, fetchOptions).get(experimentIds.get(0)); + assertEquals(experiment2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<SamplePermId> properties = experiment2.getMultiValueSampleProperty(propertyType.getPermId()); + assertEquals(properties.size(), 2); + assertEqualsNoOrder(properties.stream().map(SamplePermId::getPermId).toArray(String[]::new), new String[] {"200811050919915-8", sample2.getPermId().getPermId()}); + + Map<String, Sample[]> sampleProperties = experiment2.getSampleProperties(); + + Sample[] samples = sampleProperties.get(propertyType.getPermId()); + Serializable[] sampleProps = Arrays.stream(samples).map(x -> x.getPermId().getPermId()).sorted().toArray(String[]::new); + assertEquals(sampleProps, new Serializable[]{"200811050919915-8", sample2.getPermId().getPermId()}); + + sampleProps = (Serializable[]) experiment2.getProperties().get(propertyType.getPermId()); + Arrays.sort(sampleProps); + assertEquals(sampleProps, new Serializable[]{"200811050919915-8", sample2.getPermId().getPermId()}); + assertEquals(experiment2.getProperties().size(), 2); + } + @Test public void testCreateWithMultiValuePropertySample2() { @@ -1167,7 +1664,7 @@ public class CreateExperimentTest extends AbstractExperimentTest creation.setTypeId(experimentType); creation.setProjectId(new ProjectIdentifier("/CISD/NEMO")); creation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); - creation.setSampleProperty(propertyType.getPermId(), new SamplePermId[]{ new SamplePermId("/CISD/CL1"), sampleIds.get(0)}); + creation.setMultiValueSampleProperty(propertyType.getPermId(), List.of(new SamplePermId("/CISD/CL1"), sampleIds.get(0))); // When List<ExperimentPermId> experimentIds = v3api.createExperiments(sessionToken, Arrays.asList(creation)); diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateSampleTest.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateSampleTest.java index e5632a606feab89a3156178b8ed441b5986f9d94..a271155d6e110850df89afeb6705380cfb9eb1f9 100644 --- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateSampleTest.java +++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/CreateSampleTest.java @@ -16,6 +16,7 @@ package ch.ethz.sis.openbis.systemtest.asapi.v3; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertEqualsNoOrder; import static org.testng.Assert.assertNull; import java.io.Serializable; @@ -1523,6 +1524,67 @@ public class CreateSampleTest extends AbstractSampleTest assertEquals(sample2.getProperties().get(propertyType.getPermId()), "2020-11-05 19:23:00 +0100"); assertEquals(sample2.getProperties().size(), 2); } + + @Test + public void testCreateWithPropertyOfTypeTimestampDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.TIMESTAMP); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + sample.setTimestampProperty(propertyType.getPermId(), time1); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEquals(sample2.getTimestampProperty(propertyType.getPermId()), ZonedDateTime.parse("2023-05-16T11:22:33+02:00")); + assertEquals(sample2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeTimestamp() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.TIMESTAMP, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + ZonedDateTime time2 = ZonedDateTime.parse("2023-05-18T11:17:03+02"); + sample.setMultiValueTimestampProperty(propertyType.getPermId(), List.of(time1, time2)); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEqualsNoOrder(sample2.getMultiValueTimestampProperty(propertyType.getPermId()).toArray(ZonedDateTime[]::new), new ZonedDateTime[] {time1, time2}); + assertEquals(sample2.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeJson() @@ -1552,6 +1614,100 @@ public class CreateSampleTest extends AbstractSampleTest assertEquals(sample2.getJsonProperty(propertyType.getPermId()), "{\"key\": \"value\", \"array\": [1, 2, 3]}"); assertEquals(sample2.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeJson() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.JSON, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setMultiValueJsonProperty(propertyType.getPermId(), List.of("{\"key\": \"value\", \"array\":[1,2,3]}", "{\"key\": \"value2\", \"array\":[]}")); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + List<String> properties = sample2.getMultiValueJsonProperty(propertyType.getPermId()); + assertEquals(properties.size(), 2); + assertEqualsNoOrder(properties.toArray(String[]::new), new String[] {"{\"key\": \"value\", \"array\": [1, 2, 3]}", "{\"key\": \"value2\", \"array\": []}"}); + assertEquals(sample2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeInteger() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.INTEGER, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setProperty(propertyType.getPermId(), new Long[] {1L, 1L, 3L}); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEqualsNoOrder((Serializable[]) sample2.getProperties().get(propertyType.getPermId()), new Serializable[] { "1", "1", "3" }); + assertEqualsNoOrder(sample2.getMultiValueIntegerProperty(propertyType.getPermId()).toArray(Long[]::new), new Long[] { 1L, 1L, 3L }); + assertEquals(sample2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeIntegerDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.INTEGER, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setMultiValueIntegerProperty(propertyType.getPermId(), List.of(1L, 2L, 3L)); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + assertEqualsNoOrder((Serializable[]) sample2.getProperties().get(propertyType.getPermId()), new Serializable[] { "1", "2", "3" }); + assertEqualsNoOrder(sample2.getMultiValueIntegerProperty(propertyType.getPermId()).toArray(Long[]::new), new Long[] { 1L, 2L, 3L }); + assertEquals(sample2.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeArrayInteger() @@ -1581,6 +1737,84 @@ public class CreateSampleTest extends AbstractSampleTest assertEquals(sample2.getIntegerArrayProperty(propertyType.getPermId()), new Long[]{1L, 2L, 3L}); assertEquals(sample2.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayInteger() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setProperty(propertyType.getPermId(), new Long[][]{ new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} }); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = sample2.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(sample2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayIntegerDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setMultiValueIntegerArrayProperty(propertyType.getPermId(), List.of( new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} )); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = sample2.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(sample2.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeArrayReal() @@ -1610,6 +1844,84 @@ public class CreateSampleTest extends AbstractSampleTest assertEquals(sample2.getRealArrayProperty(propertyType.getPermId()), new Double[]{1.0, 2.0, 3.0}); assertEquals(sample2.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayReal() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_REAL, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setProperty(propertyType.getPermId(), new Double[][]{ new Double[]{1.0, 2.0, 3.0}, new Double[]{4.0, 5.0, 6.0} }); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = sample2.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(sample2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayRealDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_INTEGER, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setMultiValueIntegerArrayProperty(propertyType.getPermId(), List.of( new Long[]{1L, 2L, 3L}, new Long[]{4L, 5L, 6L} )); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<Long[]> props = sample2.getMultiValueIntegerArrayProperty(propertyType.getPermId()); + assertEquals(props.size(), 2); + for(Long[] prop : props) { + if(prop[0] > 3L) { + assertEqualsNoOrder(prop, new Long[] {4L, 5L, 6L}); + } else { + assertEqualsNoOrder(prop, new Long[] {1L, 2L, 3L}); + } + } + + assertEquals(sample2.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeArrayString() @@ -1639,6 +1951,43 @@ public class CreateSampleTest extends AbstractSampleTest assertEquals(sample2.getStringArrayProperty(propertyType.getPermId()), new String[]{"a", "b", "c"}); assertEquals(sample2.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayString() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_STRING, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setMultiValueStringArrayProperty(propertyType.getPermId(), List.of(new String[]{"a,a", "b", "c"}, new String[]{"a", "b", "c", "d"})); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + List<String[]> result = sample2.getMultiValueStringArrayProperty(propertyType.getPermId()); + assertEquals(result.size(), 2); + for(String[] prop : result) { + if(prop.length > 3) { + assertEqualsNoOrder(prop, new String[]{"a", "b", "c", "d"}); + } else { + assertEqualsNoOrder(prop, new String[]{"a,a", "b", "c"}); + } + } + assertEquals(sample2.getProperties().size(), 2); + } @Test public void testCreateWithPropertyOfTypeArrayTimestamp() @@ -1670,6 +2019,47 @@ public class CreateSampleTest extends AbstractSampleTest assertEquals(sample2.getTimestampArrayProperty(propertyType.getPermId()), new ZonedDateTime[]{time1, time2}); assertEquals(sample2.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertyOfTypeArrayTimestamp() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + PropertyTypePermId propertyType = createAPropertyType(sessionToken, DataType.ARRAY_TIMESTAMP, true); + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SAMPLE_PROPERTY"); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + ZonedDateTime time1 = ZonedDateTime.parse("2023-05-16T11:22:33+02"); + ZonedDateTime time2 = ZonedDateTime.parse("2023-05-18T11:17:03+02"); + ZonedDateTime time3 = ZonedDateTime.parse("2023-05-20T11:10:03+02"); + sample.setMultiValueTimestampArrayProperty(propertyType.getPermId(), List.of(new ZonedDateTime[]{time1, time2}, new ZonedDateTime[]{time3, time2, time1})); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + List<ZonedDateTime[]> properties = sample2.getMultiValueTimestampArrayProperty(propertyType.getPermId()); + assertEquals(properties.size(), 2); + for(ZonedDateTime[] prop : properties) { + if(prop.length == 2) { + assertEqualsNoOrder(prop, new ZonedDateTime[]{time1, time2}); + } else { + assertEqualsNoOrder(prop, new ZonedDateTime[]{time1, time2, time3}); + } + } + assertEquals(sample2.getProperties().size(), 2); + } @Test public void testCreateWithMultiValuePropertyVocabulary() @@ -1693,7 +2083,7 @@ public class CreateSampleTest extends AbstractSampleTest sample.setTypeId(sampleType); sample.setSpaceId(new SpacePermId("CISD")); sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); - sample.setControlledVocabularyProperty(propertyType.getPermId(), new String[] {"DOG", "HUMAN"}); + sample.setMultiValueControlledVocabularyProperty(propertyType.getPermId(), List.of("DOG", "HUMAN")); // When List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); @@ -1705,9 +2095,49 @@ public class CreateSampleTest extends AbstractSampleTest fetchOptions.withSampleProperties(); Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); - String[] vocabProperties = sample2.getControlledVocabularyProperty(propertyType.getPermId()); - Arrays.sort(vocabProperties); - assertEquals(vocabProperties, new String[] {"DOG", "HUMAN"}); + List<String> vocabProperties = sample2.getMultiValueControlledVocabularyProperty(propertyType.getPermId()); + Collections.sort(vocabProperties); + assertEquals(vocabProperties, List.of("DOG", "HUMAN")); + assertEquals(sample2.getProperties().size(), 2); + } + + @Test + public void testCreateWithMultiValuePropertyVocabularyDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + final PropertyTypeCreation propertyTypeCreation = new PropertyTypeCreation(); + propertyTypeCreation.setCode("TYPE-" + System.currentTimeMillis()); + propertyTypeCreation.setDataType(DataType.CONTROLLEDVOCABULARY); + propertyTypeCreation.setLabel("label"); + propertyTypeCreation.setDescription("description"); + propertyTypeCreation.setMultiValue(true); + propertyTypeCreation.setVocabularyId(new VocabularyPermId("ORGANISM")); + PropertyTypePermId propertyType = v3api.createPropertyTypes(sessionToken, Collections.singletonList(propertyTypeCreation)).get(0); + + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_MULTI_VOCAB_PROPERTY-" + System.currentTimeMillis()); + sample.setTypeId(sampleType); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setMultiValueControlledVocabularyProperty(propertyType.getPermId(), List.of("DOG", "HUMAN")); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + List<String> vocabProperties = sample2.getMultiValueControlledVocabularyProperty(propertyType.getPermId()); + Collections.sort(vocabProperties); + assertEquals(vocabProperties, List.of("DOG", "HUMAN")); assertEquals(sample2.getProperties().size(), 2); } @@ -1772,6 +2202,69 @@ public class CreateSampleTest extends AbstractSampleTest assertEquals(sampleProps, new Serializable[]{"200811050919915-8", testSampleIds.get(0).getPermId()}); assertEquals(sample2.getProperties().size(), 2); } + + @Test + public void testCreateWithMultiValuePropertySampleDedicatedMethod() + { + // Given + String sessionToken = v3api.login(TEST_USER, PASSWORD); + + //Create new sample that will be used as property + PropertyTypePermId propertyType1 = createASamplePropertyType(sessionToken, null); + EntityTypePermId sampleType1 = createASampleType(sessionToken, true, propertyType1, PLATE_GEOMETRY); + + SampleCreation sample = new SampleCreation(); + sample.setCode("SAMPLE_WITH_SOME_SAMPLE_PROPERTY-" + System.currentTimeMillis()); + sample.setTypeId(sampleType1); + sample.setSpaceId(new SpacePermId("CISD")); + sample.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sample.setProperty(propertyType1.getPermId(), "200811050919915-8"); + + List<SamplePermId> testSampleIds = v3api.createSamples(sessionToken, Arrays.asList(sample)); + + assertEquals(testSampleIds.size(), 1); + + // Create multi-value sample property + final PropertyTypeCreation propertyTypeCreation = new PropertyTypeCreation(); + propertyTypeCreation.setCode("TYPE-" + System.currentTimeMillis()); + propertyTypeCreation.setDataType(DataType.SAMPLE); + propertyTypeCreation.setLabel("label"); + propertyTypeCreation.setDescription("description"); + propertyTypeCreation.setMultiValue(true); + PropertyTypePermId propertyType = v3api.createPropertyTypes(sessionToken, Collections.singletonList(propertyTypeCreation)).get(0); + + EntityTypePermId sampleType = createASampleType(sessionToken, true, propertyType, PLATE_GEOMETRY); + + // Create a sample with multi-value sample property + SampleCreation sampleCreation = new SampleCreation(); + sampleCreation.setCode("SAMPLE_WITH_MULTI_SAMPLE_PROPERTY-" + System.currentTimeMillis()); + sampleCreation.setTypeId(sampleType); + sampleCreation.setSpaceId(new SpacePermId("CISD")); + sampleCreation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); + sampleCreation.setMultiValueSampleProperty(propertyType.getPermId(), List.of(new SamplePermId("200811050919915-8"), testSampleIds.get(0))); + + // When + List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sampleCreation)); + + // Then + assertEquals(sampleIds.size(), 1); + SampleFetchOptions fetchOptions = new SampleFetchOptions(); + fetchOptions.withProperties(); + fetchOptions.withSampleProperties(); + Sample sample2 = v3api.getSamples(sessionToken, sampleIds, fetchOptions).get(sampleIds.get(0)); + assertEquals(sample2.getProperties().get(PLATE_GEOMETRY.getPermId()), "384_WELLS_16X24"); + + Map<String, Sample[]> sampleProperties = sample2.getSampleProperties(); + + Sample[] samples = sampleProperties.get(propertyType.getPermId()); + Serializable[] sampleProps = Arrays.stream(samples).map(x -> x.getPermId().getPermId()).sorted().toArray(String[]::new); + assertEquals(sampleProps, new Serializable[]{"200811050919915-8", testSampleIds.get(0).getPermId()}); + + sampleProps = (Serializable[]) sample2.getProperties().get(propertyType.getPermId()); + Arrays.sort(sampleProps); + assertEquals(sampleProps, new Serializable[]{"200811050919915-8", testSampleIds.get(0).getPermId()}); + assertEquals(sample2.getProperties().size(), 2); + } @Test public void testCreateWithMultiValuePropertySample2() { @@ -1809,7 +2302,7 @@ public class CreateSampleTest extends AbstractSampleTest sampleCreation.setTypeId(sampleType); sampleCreation.setSpaceId(new SpacePermId("CISD")); sampleCreation.setProperty(PLATE_GEOMETRY.getPermId(), "384_WELLS_16X24"); - sampleCreation.setSampleProperty(propertyType.getPermId(), new SamplePermId[]{ new SamplePermId("/CISD/CL1"), testSampleIds.get(0)}); + sampleCreation.setMultiValueSampleProperty(propertyType.getPermId(), List.of(new SamplePermId("/CISD/CL1"), testSampleIds.get(0))); // When List<SamplePermId> sampleIds = v3api.createSamples(sessionToken, Arrays.asList(sampleCreation)); diff --git a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchMaterialTest.java b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchMaterialTest.java index 3e923a3f85135480e25548b4fb52061dd81497b2..0fd28c38d5a436b0a0d6e3fb5465b044ef8090e5 100644 --- a/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchMaterialTest.java +++ b/server-application-server/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchMaterialTest.java @@ -1214,7 +1214,7 @@ public class SearchMaterialTest extends AbstractTest v3api.createVocabularies(sessionToken, Collections.singletonList(vocabularyCreation)).get(0); final PropertyTypePermId propertyTypeId = createAPropertyType(sessionToken, DataType.CONTROLLEDVOCABULARY, - vocabularyPermId); + vocabularyPermId, false); final MaterialPermId entityPermId = createMaterial(sessionToken, propertyTypeId, value); final MaterialSearchCriteria searchCriteria = new MaterialSearchCriteria(); new AbstractSearchPropertyTest.StringQueryInjector(searchCriteria, propertyTypeId).buildCriteria(queryString);