From ce0f33be6c400f19b24e3e464ec4720d236afb9d Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Tue, 28 Nov 2017 08:12:34 +0000
Subject: [PATCH] SSDM-5721: Person with role assignments implemented in Java
 and JS with system test.

SVN: 38961
---
 .../IPersonRoleAssignmentTranslator.java      | 31 +++++++++
 .../v3/translator/person/PersonQuery.java     |  4 ++
 .../PersonRoleAssignmentTranslator.java       | 69 +++++++++++++++++++
 .../translator/person/PersonTranslator.java   | 18 ++++-
 .../resources/api/v3/as/dto/person/Person.js  | 17 ++++-
 .../person/fetchoptions/PersonFetchOptions.js | 18 ++++-
 .../systemtest/asapi/v3/SearchPersonTest.java | 26 +++++--
 .../generic/asapi/v3/dto/person/Person.java   | 23 +++++++
 .../fetchoptions/PersonFetchOptions.java      | 23 +++++++
 9 files changed, 222 insertions(+), 7 deletions(-)
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/IPersonRoleAssignmentTranslator.java
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonRoleAssignmentTranslator.java

diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/IPersonRoleAssignmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/IPersonRoleAssignmentTranslator.java
new file mode 100644
index 00000000000..72fdd201d7a
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/IPersonRoleAssignmentTranslator.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 ETH Zuerich, SIS
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.person;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleAssignment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.fetchoptions.RoleAssignmentFetchOptions;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.IObjectToManyRelationTranslator;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+public interface IPersonRoleAssignmentTranslator extends IObjectToManyRelationTranslator<RoleAssignment, RoleAssignmentFetchOptions>
+{
+
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonQuery.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonQuery.java
index 8b04ac98981..a0ed80d1d35 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonQuery.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonQuery.java
@@ -42,4 +42,8 @@ public interface PersonQuery extends ObjectQuery
     @Select(sql = "select id as objectId, pers_id_registerer as relatedId from persons where id = any(?{1})", parameterBindings = { LongSetMapper.class }, fetchSize = FETCH_SIZE)
     public List<ObjectRelationRecord> getRegistratorIds(LongSet personIds);
 
+    @Select(sql = "select pers_id_grantee as objectId, id as relatedId from role_assignments where pers_id_grantee = any(?{1})",
+            parameterBindings = { LongSetMapper.class }, fetchSize = FETCH_SIZE)
+    public List<ObjectRelationRecord> getRoleAssignmentIds(LongSet personIds);
+
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonRoleAssignmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonRoleAssignmentTranslator.java
new file mode 100644
index 00000000000..7c9e32449c9
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonRoleAssignmentTranslator.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 ETH Zuerich, SIS
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.person;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleAssignment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.fetchoptions.RoleAssignmentFetchOptions;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.TranslationContext;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.ObjectRelationRecord;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.common.ObjectToManyRelationTranslator;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.roleassignment.IRoleAssignmentTranslator;
+
+import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
+import net.lemnik.eodsql.QueryTool;
+
+/**
+ * 
+ *
+ * @author Franz-Josef Elmer
+ */
+@Component
+public class PersonRoleAssignmentTranslator
+        extends ObjectToManyRelationTranslator<RoleAssignment, RoleAssignmentFetchOptions>
+        implements IPersonRoleAssignmentTranslator
+{
+   @Autowired
+   private IRoleAssignmentTranslator roleAssignmentTranslator;
+   
+   @Override
+   protected List<ObjectRelationRecord> loadRecords(LongOpenHashSet objectIds)
+   {
+       PersonQuery query = QueryTool.getManagedQuery(PersonQuery.class);
+       return query.getRoleAssignmentIds(objectIds);
+   }
+
+   @Override
+   protected Map<Long, RoleAssignment> translateRelated(TranslationContext context, Collection<Long> relatedIds,
+           RoleAssignmentFetchOptions relatedFetchOptions)
+   {
+       return roleAssignmentTranslator.translate(context, relatedIds, relatedFetchOptions);
+   }
+
+   @Override
+   protected Collection<RoleAssignment> createCollection()
+   {
+       return new ArrayList<>();
+   }
+}
\ No newline at end of file
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonTranslator.java
index 05d4aba8eba..ce04766431d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/person/PersonTranslator.java
@@ -17,12 +17,14 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.translator.person;
 
 import java.util.Collection;
+import java.util.List;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleAssignment;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.AbstractCachingTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.TranslationResults;
@@ -43,6 +45,9 @@ public class PersonTranslator extends AbstractCachingTranslator<Long, Person, Pe
     @Autowired
     private IPersonRegistratorTranslator registratorTranslator;
 
+    @Autowired
+    private IPersonRoleAssignmentTranslator roleAssignmentTranslator;
+
     @Override
     protected Person createObject(TranslationContext context, Long personId, PersonFetchOptions fetchOptions)
     {
@@ -68,6 +73,11 @@ public class PersonTranslator extends AbstractCachingTranslator<Long, Person, Pe
             relations.put(IPersonRegistratorTranslator.class, registratorTranslator.translate(context, personIds, fetchOptions.withRegistrator()));
         }
 
+        if (fetchOptions.hasRoleAssignments())
+        {
+            relations.put(IPersonRoleAssignmentTranslator.class, roleAssignmentTranslator.translate(context, personIds, fetchOptions.withRoleAssignments()));
+        }
+
         return relations;
     }
 
@@ -96,5 +106,11 @@ public class PersonTranslator extends AbstractCachingTranslator<Long, Person, Pe
             result.setRegistrator(relations.get(IPersonRegistratorTranslator.class, personId));
             result.getFetchOptions().withRegistratorUsing(fetchOptions.withRegistrator());
         }
-    }
+
+        if (fetchOptions.hasRoleAssignments())
+        {
+            result.setRoleAssignments((List<RoleAssignment>) relations.get(IPersonRoleAssignmentTranslator.class, personId));
+            result.getFetchOptions().withRoleAssignmentsUsing(fetchOptions.withRoleAssignments());
+        }
+}
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/Person.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/Person.js
index 6ac8de9b00d..15fe4418204 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/Person.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/Person.js
@@ -18,6 +18,7 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.active = null;
 		prototype.space = null;
 		prototype.registrator = null;
+		prototype.roleAssignments = null;
 		prototype.getFetchOptions = function() {
 			return this.fetchOptions;
 		};
@@ -86,12 +87,26 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.setRegistrator = function(registrator) {
 			this.registrator = registrator;
 		};
+		prototype.getRoleAssignments = function() {
+			if (this.getFetchOptions() && this.getFetchOptions().hasRoleAssignments()) {
+				return this.roleAssignments;
+			} else {
+				throw new exceptions.NotFetchedException("RoleAssignments have not been fetched.");
+			}
+		};
+		prototype.setRoleAssignments = function(roleAssignments) {
+			this.roleAssignments = roleAssignments;
+		};
 	}, {
 		fetchOptions : "PersonFetchOptions",
 		permId : "PersonPermId",
 		registrationDate : "Date",
 		space : "Space",
-		registrator : "Person"
+		registrator : "Person",
+		roleAssignments : {
+			name : "List",
+			arguments : [ "RoleAssignment" ]
+		}
 	});
 	return Person;
 })
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/fetchoptions/PersonFetchOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/fetchoptions/PersonFetchOptions.js
index e976267203f..e5c6e8737ce 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/fetchoptions/PersonFetchOptions.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/person/fetchoptions/PersonFetchOptions.js
@@ -2,7 +2,8 @@
  * Class automatically generated with
  * {@link ch.ethz.sis.openbis.generic.shared.api.v3.dto.generators.DtoGenerator}
  */
-define([ "require", "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/space/fetchoptions/SpaceFetchOptions", "as/dto/person/fetchoptions/PersonSortOptions" ], function(require, stjs, FetchOptions) {
+define([ "require", "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/space/fetchoptions/SpaceFetchOptions", 
+         "as/dto/person/fetchoptions/PersonSortOptions", "as/dto/roleassignment/fetchoptions/RoleAssignmentFetchOptions" ], function(require, stjs, FetchOptions) {
 	var PersonFetchOptions = function() {
 	};
 	stjs.extend(PersonFetchOptions, FetchOptions, [ FetchOptions ], function(constructor, prototype) {
@@ -10,6 +11,7 @@ define([ "require", "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/s
 		constructor.serialVersionUID = 1;
 		prototype.space = null;
 		prototype.registrator = null;
+		prototype.roleAssignments = null;
 		prototype.sort = null;
 		prototype.withSpace = function() {
 			if (this.space == null) {
@@ -36,6 +38,19 @@ define([ "require", "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/s
 		prototype.hasRegistrator = function() {
 			return this.registrator != null;
 		};
+		prototype.withRoleAssignments = function() {
+			if (this.roleAssignments == null) {
+				var RoleAssignmentsFetchOptions = require("as/dto/roleassignment/fetchoptions/RoleAssignmentFetchOptions");
+				this.roleAssignments = new RoleAssignmentsFetchOptions();
+			}
+			return this.roleAssignments;
+		};
+		prototype.withRoleAssignmentsUsing = function(fetchOptions) {
+			return this.roleAssignments = fetchOptions;
+		};
+		prototype.hasRoleAssignments = function() {
+			return this.roleAssignments != null;
+		};
 		prototype.sortBy = function() {
 			if (this.sort == null) {
 				var PersonSortOptions = require("as/dto/person/fetchoptions/PersonSortOptions");
@@ -49,6 +64,7 @@ define([ "require", "stjs", "as/dto/common/fetchoptions/FetchOptions", "as/dto/s
 	}, {
 		space : "SpaceFetchOptions",
 		registrator : "PersonFetchOptions",
+		roleAssignments : "RoleAssignmentFetchOptions",
 		sort : "PersonSortOptions"
 	});
 	return PersonFetchOptions;
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchPersonTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchPersonTest.java
index 51f9a9f8388..2b7cdf7f0ee 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchPersonTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchPersonTest.java
@@ -27,6 +27,7 @@ import org.testng.annotations.Test;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.search.PersonSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space;
 
 /**
@@ -40,18 +41,21 @@ public class SearchPersonTest extends AbstractTest
     public void testSearchPersonByUserId()
     {
         // Given
-        String sessionToken = v3api.login(TEST_SPACE_USER, PASSWORD);
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
         PersonSearchCriteria searchCriteria = new PersonSearchCriteria();
         searchCriteria.withUserId().thatStartsWith("observer");
         PersonFetchOptions fetchOptions = new PersonFetchOptions();
         fetchOptions.withSpace();
+        fetchOptions.withRoleAssignments().withSpace();
         
         // Then
         List<Person> persons = v3api.searchPersons(sessionToken, searchCriteria, fetchOptions).getObjects();
         
         // When
-        assertEquals(render(persons), "observer: John Observer observer@o.o\n"
-                + "observer_cisd: John ObserverCISD observer_cisd@o.o\n");
+        assertEquals(render(persons), "observer: John Observer observer@o.o, home space:CISD, "
+                + "[SPACE_OBSERVER Space TESTGROUP]\n"
+                + "observer_cisd: John ObserverCISD observer_cisd@o.o, home space:CISD, "
+                + "[SPACE_ADMIN Space TESTGROUP, SPACE_OBSERVER Space CISD]\n");
     }
     
     private String render(List<Person> persons)
@@ -80,10 +84,24 @@ public class SearchPersonTest extends AbstractTest
         Space space = person.getSpace();
         if (space != null)
         {
-            builder.append(" home space:").append(space.getCode());
+            builder.append(", home space:").append(space.getCode());
         }
+        List<RoleAssignment> roleAssignments = person.getRoleAssignments();
+        String string = renderAssignments(roleAssignments);
+        builder.append(", ").append(string);
         return builder.toString();
     }
+
+    private String renderAssignments(List<RoleAssignment> roleAssignments)
+    {
+        List<String> renderedAssignments = new ArrayList<>();
+        for (RoleAssignment roleAssignment : roleAssignments)
+        {
+            renderedAssignments.add(roleAssignment.getRoleLevel() + "_" + roleAssignment.getRole() + " " + roleAssignment.getSpace());
+        }
+        Collections.sort(renderedAssignments);
+        return renderedAssignments.toString();
+    }
     
     private void appendTo(StringBuilder builder, String stringOrNull)
     {
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/Person.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/Person.java
index e4d87c116f8..5aeaf73e119 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/Person.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/Person.java
@@ -22,6 +22,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ISpaceHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.id.PersonPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.RoleAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space;
 import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.NotFetchedException;
 import ch.systemsx.cisd.base.annotation.JsonObject;
@@ -29,6 +30,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.io.Serializable;
 import java.util.Date;
+import java.util.List;
 
 /*
  * Class automatically generated with DtoGenerator
@@ -68,6 +70,9 @@ public class Person implements Serializable, IPermIdHolder, IRegistrationDateHol
     @JsonProperty
     private Person registrator;
 
+    @JsonProperty
+    private List<RoleAssignment> roleAssignments;
+
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     public PersonFetchOptions getFetchOptions()
@@ -216,6 +221,24 @@ public class Person implements Serializable, IPermIdHolder, IRegistrationDateHol
         this.registrator = registrator;
     }
 
+    
+    @JsonIgnore
+    public List<RoleAssignment> getRoleAssignments()
+    {
+        if (getFetchOptions() != null && getFetchOptions().hasRoleAssignments())
+        {
+            return roleAssignments;
+        }
+        else
+        {
+            throw new NotFetchedException("Role assignments have not been fetched.");
+        }
+    }
+    
+    public void setRoleAssignments(List<RoleAssignment> roleAssignments)
+    {
+        this.roleAssignments = roleAssignments;
+    }
     // Method automatically generated with DtoGenerator
     @Override
     public String toString()
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/fetchoptions/PersonFetchOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/fetchoptions/PersonFetchOptions.java
index 3fab3a8da3f..976b0ad3807 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/fetchoptions/PersonFetchOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/person/fetchoptions/PersonFetchOptions.java
@@ -19,6 +19,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptions
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptionsToStringBuilder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.roleassignment.fetchoptions.RoleAssignmentFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.fetchoptions.SpaceFetchOptions;
 import ch.systemsx.cisd.base.annotation.JsonObject;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -38,6 +39,9 @@ public class PersonFetchOptions extends FetchOptions<Person> implements Serializ
     @JsonProperty
     private PersonFetchOptions registrator;
 
+    @JsonProperty
+    private RoleAssignmentFetchOptions roleAssignments;
+    
     @JsonProperty
     private PersonSortOptions sort;
 
@@ -85,6 +89,25 @@ public class PersonFetchOptions extends FetchOptions<Person> implements Serializ
         return registrator != null;
     }
 
+    public RoleAssignmentFetchOptions withRoleAssignments()
+    {
+        if (roleAssignments == null)
+        {
+            roleAssignments = new RoleAssignmentFetchOptions();
+        }
+        return roleAssignments;
+    }
+    
+    public RoleAssignmentFetchOptions withRoleAssignmentsUsing(RoleAssignmentFetchOptions fetchOptions)
+    {
+        return roleAssignments = fetchOptions;
+    }
+    
+    public boolean hasRoleAssignments()
+    {
+        return roleAssignments != null;
+    }
+    
     // Method automatically generated with DtoGenerator
     @Override
     public PersonSortOptions sortBy()
-- 
GitLab