From ad51bce2fd6b181b4da9c527a33b042e163c5797 Mon Sep 17 00:00:00 2001 From: kaloyane <kaloyane> Date: Tue, 13 Sep 2011 13:38:09 +0000 Subject: [PATCH] [LMS-2500] experimentally expose an API method to check if MATLAB users can digest POJOs SVN: 22918 --- screening/source/java/OpenBISScreeningML.java | 20 +++++++++++ .../api/v1/ScreeningOpenbisServiceFacade.java | 8 ++--- .../screening/server/ScreeningServer.java | 6 ++-- .../server/ScreeningServerLogger.java | 4 +-- .../server/logic/PlateContentLoader.java | 2 +- .../server/logic/ScreeningApiImpl.java | 35 ++++++++++--------- .../shared/api/v1/IScreeningApiServer.java | 4 +-- ...PlateWithWells.java => PlateMetadata.java} | 20 +++++------ .../v1/dto/{Well.java => WellMetadata.java} | 10 +++--- 9 files changed, 65 insertions(+), 44 deletions(-) rename screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/{PlateWithWells.java => PlateMetadata.java} (77%) rename screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/{Well.java => WellMetadata.java} (89%) diff --git a/screening/source/java/OpenBISScreeningML.java b/screening/source/java/OpenBISScreeningML.java index 7b4d9e716ae..441a7251f69 100644 --- a/screening/source/java/OpenBISScreeningML.java +++ b/screening/source/java/OpenBISScreeningML.java @@ -43,6 +43,7 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.filter.PropertiesBasedData import ch.systemsx.cisd.openbis.generic.shared.api.v1.filter.TypeBasedDataSetFilter; import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.IScreeningOpenbisServiceFacade; import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.IScreeningOpenbisServiceFacadeFactory; +import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.ScreeningOpenbisServiceFacade; import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.ScreeningOpenbisServiceFacade.IImageOutputStreamProvider; import ch.systemsx.cisd.openbis.plugin.screening.client.api.v1.ScreeningOpenbisServiceFacadeFactory; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.ExperimentIdentifier; @@ -60,6 +61,7 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialTypeI import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateImageReference; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateMetadata; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition; @@ -2040,6 +2042,24 @@ public class OpenBISScreeningML return result; } + /** + * Experimental method that returns an array of {@link PlateMetadata} Java objects for a given + * list of plate codes. + * <p> + * The method can be removed from the API in the future if the MATLAB users are unable to cope + * with return values. + */ + public static PlateMetadata[] getPlateMetadataList(String[] platesCodes) + { + checkLoggedIn(); + + List<PlateMetadata> metadataList = + ((ScreeningOpenbisServiceFacade) openbis) + .getPlateMetadataList(toPlates(platesCodes)); + + return metadataList.toArray(new PlateMetadata[0]); + } + // // Helper methods // diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java index 6538d7a8f88..bee6b2d8254 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/client/api/v1/ScreeningOpenbisServiceFacade.java @@ -69,9 +69,9 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialTypeI import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateImageReference; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateMetadata; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets; -import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition; import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants; @@ -277,10 +277,10 @@ public class ScreeningOpenbisServiceFacade implements IScreeningOpenbisServiceFa return openbisScreeningServer.listPlates(sessionToken); } - public List<PlateWithWells> getPlates(List<? extends PlateIdentifier> plateIdentifiers) + public List<PlateMetadata> getPlateMetadataList(List<? extends PlateIdentifier> plateIdentifiers) { - checkASMinimalMinorVersion("getPlates", List.class); - return openbisScreeningServer.getPlates(sessionToken, plateIdentifiers); + checkASMinimalMinorVersion("getPlateMetadataList", List.class); + return openbisScreeningServer.getPlateMetadataList(sessionToken, plateIdentifiers); } /** diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java index 0c77afe61dd..ced338ac579 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServer.java @@ -84,9 +84,9 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialIdent import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialTypeIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateMetadata; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets; -import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.AnalysisProcedures; import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference; @@ -517,10 +517,10 @@ public final class ScreeningServer extends AbstractServer<IScreeningServer> impl return MINOR_VERSION; } - public List<PlateWithWells> getPlates(String sessionToken, + public List<PlateMetadata> getPlateMetadataList(String sessionToken, List<? extends PlateIdentifier> plateIdentifiers) throws IllegalArgumentException { - return createScreeningApiImpl(sessionToken).getPlates(plateIdentifiers); + return createScreeningApiImpl(sessionToken).getPlateMetadata(plateIdentifiers); } } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java index 489ef8d08fc..69d81b539d1 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/ScreeningServerLogger.java @@ -47,9 +47,9 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialIdent import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialTypeIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateMetadata; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets; -import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.AnalysisProcedures; import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.DatasetReference; @@ -390,7 +390,7 @@ final class ScreeningServerLogger extends AbstractServerLogger implements IScree return null; } - public List<PlateWithWells> getPlates( + public List<PlateMetadata> getPlateMetadataList( String sessionToken, List<? extends PlateIdentifier> plates) throws IllegalArgumentException { diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java index e0794eeb092..8df31d7244b 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/PlateContentLoader.java @@ -115,7 +115,7 @@ public class PlateContentLoader .getImageDatasetInfosForSample(sampleId, wellLocationOrNull); } - public static List<PlateMetadata> loadPlateMetadatas(Session session, + public static List<PlateMetadata> loadPlateMetadata(Session session, IScreeningBusinessObjectFactory businessObjectFactory, List<TechId> plateIds) { return new PlateContentLoader(session, businessObjectFactory).getPlateMetadatas(plateIds); diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImpl.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImpl.java index 1421dd59c7c..e512050f5d8 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImpl.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/ScreeningApiImpl.java @@ -81,17 +81,15 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialIdent import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialTypeIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateMetadata; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets; -import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells; -import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Well; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellMetadata; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellPosition; -import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMetadata; import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.ScreeningConstants; import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellContent; import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellLocation; -import ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellMetadata; /** * Contains implementations of the screening public API calls. @@ -868,7 +866,7 @@ public class ScreeningApiImpl return plateWellReferences; } - public List<PlateWithWells> getPlates(List<? extends PlateIdentifier> plateIdentifiers) + public List<PlateMetadata> getPlateMetadata(List<? extends PlateIdentifier> plateIdentifiers) { List<TechId> techIds = new ArrayList<TechId>(); for (PlateIdentifier identifier : plateIdentifiers) @@ -880,31 +878,33 @@ public class ScreeningApiImpl } } - List<PlateMetadata> plateMetadatas = - PlateContentLoader.loadPlateMetadatas(session, businessObjectFactory, techIds); + List<ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMetadata> plateMetadatas = + PlateContentLoader.loadPlateMetadata(session, businessObjectFactory, techIds); - List<PlateWithWells> result = new ArrayList<PlateWithWells>(); + List<PlateMetadata> result = new ArrayList<PlateMetadata>(); Map<Long, Material> materialsCache = new HashMap<Long, Material>(); - for (PlateMetadata plateMetaData : plateMetadatas) + for (ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMetadata plateMetaData : plateMetadatas) { - result.add(asPlateWithWells(plateMetaData, materialsCache)); + result.add(asApiPlateMetadata(plateMetaData, materialsCache)); } return result; } - private PlateWithWells asPlateWithWells(PlateMetadata plateMetadata, + private PlateMetadata asApiPlateMetadata( + ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.PlateMetadata plateMetadata, Map<Long, Material> materialsCache) { Sample plate = plateMetadata.getPlate(); String spaceCodeOrNull = plate.getSpace() == null ? null : plate.getSpace().getCode(); PlateIdentifier plateIdentifier = new PlateIdentifier(plate.getCode(), spaceCodeOrNull, plate.getPermId()); - List<Well> wells = new ArrayList<Well>(); + List<WellMetadata> wells = new ArrayList<WellMetadata>(); if (plateMetadata.getWells() != null) { - for (WellMetadata wellMetadata : plateMetadata.getWells()) + for (ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellMetadata wellMetadata : plateMetadata + .getWells()) { - Well well = asWell(plateIdentifier, wellMetadata, materialsCache); + WellMetadata well = asApiWell(plateIdentifier, wellMetadata, materialsCache); wells.add(well); } } @@ -913,10 +913,11 @@ public class ScreeningApiImpl plateMetadata.getColsNum()); Map<String, String> properties = EntityHelper.convertToStringMap(plate.getProperties()); - return new PlateWithWells(plateIdentifier, geometry, properties, wells); + return new PlateMetadata(plateIdentifier, geometry, properties, wells); } - private Well asWell(PlateIdentifier plateIdentifier, WellMetadata wellMetadata, + private WellMetadata asApiWell(PlateIdentifier plateIdentifier, + ch.systemsx.cisd.openbis.plugin.screening.shared.basic.dto.WellMetadata wellMetadata, Map<Long, Material> materialsCache) { Sample well = wellMetadata.getWellSample(); @@ -925,7 +926,7 @@ public class ScreeningApiImpl Map<String, String> properties = EntityHelper.convertToStringMap(well.getProperties()); Map<String, Material> materialProperties = convertMaterialProperties(well.getProperties(), materialsCache); - return new Well(plateIdentifier, well.getCode(), well.getPermId(), wellPosition, + return new WellMetadata(plateIdentifier, well.getCode(), well.getPermId(), wellPosition, properties, materialProperties); } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/IScreeningApiServer.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/IScreeningApiServer.java index 2b67aba777f..9830b658b53 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/IScreeningApiServer.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/IScreeningApiServer.java @@ -43,9 +43,9 @@ import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialIdent import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.MaterialTypeIdentifier; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.Plate; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateIdentifier; +import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateMetadata; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellMaterialMapping; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWellReferenceWithDatasets; -import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.PlateWithWells; import ch.systemsx.cisd.openbis.plugin.screening.shared.api.v1.dto.WellIdentifier; /** @@ -118,7 +118,7 @@ public interface IScreeningApiServer extends IRpcService @Transactional(readOnly = true) @RolesAllowed(RoleWithHierarchy.SPACE_OBSERVER) @MinimalMinorVersion(8) - List<PlateWithWells> getPlates( + List<PlateMetadata> getPlateMetadataList( String sessionToken, @AuthorizationGuard(guardClass = ScreeningPlateListReadOnlyPredicate.class) List<? extends PlateIdentifier> plates) throws IllegalArgumentException; diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateWithWells.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateMetadata.java similarity index 77% rename from screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateWithWells.java rename to screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateMetadata.java index 265a1ac521f..f03503dc196 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateWithWells.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/PlateMetadata.java @@ -28,7 +28,7 @@ import java.util.Map; * @since 1.8 * @author Kaloyan Enimanev */ -public class PlateWithWells extends PlateIdentifier +public class PlateMetadata extends PlateIdentifier { private static final long serialVersionUID = 1L; @@ -37,10 +37,10 @@ public class PlateWithWells extends PlateIdentifier private final Map<String, String> properties; - private final List<Well> wells; + private final List<WellMetadata> wells; - public PlateWithWells(PlateIdentifier identifier, Geometry plateGeometry, Map<String, String> properties, - List<Well> unsortedWells) + public PlateMetadata(PlateIdentifier identifier, Geometry plateGeometry, Map<String, String> properties, + List<WellMetadata> unsortedWells) { super(identifier.getPlateCode(), identifier.tryGetSpaceCode(), identifier.getPermId()); this.plateGeometry = plateGeometry; @@ -58,12 +58,12 @@ public class PlateWithWells extends PlateIdentifier return properties; } - public List<Well> getWells() + public List<WellMetadata> getWells() { return Collections.unmodifiableList(wells); } - public Well getWell(int row, int col) + public WellMetadata getWell(int row, int col) { int idx = getWellIndexForRowAndCol(row, col); return wells.get(idx); @@ -74,11 +74,11 @@ public class PlateWithWells extends PlateIdentifier return (row - 1) * plateGeometry.getNumberOfColumns() + (col - 1); } - private List<Well> sortWells(List<Well> unsortedWells) + private List<WellMetadata> sortWells(List<WellMetadata> unsortedWells) { - Well[] wellsArray = - new Well[plateGeometry.getNumberOfRows() * plateGeometry.getNumberOfColumns()]; - for (Well well : unsortedWells) + WellMetadata[] wellsArray = + new WellMetadata[plateGeometry.getNumberOfRows() * plateGeometry.getNumberOfColumns()]; + for (WellMetadata well : unsortedWells) { int row = well.getWellPosition().getWellRow(); int col = well.getWellPosition().getWellColumn(); diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Well.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/WellMetadata.java similarity index 89% rename from screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Well.java rename to screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/WellMetadata.java index 2e3a43fc0b0..d25923affbe 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/Well.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/api/v1/dto/WellMetadata.java @@ -21,12 +21,12 @@ import java.util.HashMap; import java.util.Map; /** - * {@link Well} holds a complete set of metadata for an openBIS well. Material properties of wells + * {@link WellMetadata} holds a complete set of metadata for an openBIS well. Material properties of wells * are given a special treatment - API users can retrieve {@link Material} property values via the method {@link #getMaterialProperties()}. All other property values are available via {@link #getProperties()}. * @since 1.8 * @author Kaloyan Enimanev */ -public class Well extends WellIdentifier +public class WellMetadata extends WellIdentifier { private static final long serialVersionUID = 1L; @@ -36,7 +36,7 @@ public class Well extends WellIdentifier private final Map<String, Material> materialProperties; - public Well(PlateIdentifier plateIdentifier, String code, String permId, + public WellMetadata(PlateIdentifier plateIdentifier, String code, String permId, WellPosition wellPosition, Map<String, String> properties, Map<String, Material> materialProperties) { @@ -88,11 +88,11 @@ public class Well extends WellIdentifier { return false; } - if (!(obj instanceof Well)) + if (!(obj instanceof WellMetadata)) { return false; } - Well other = (Well) obj; + WellMetadata other = (WellMetadata) obj; if (code == null) { if (other.code != null) -- GitLab