Grails: GORM: Traversing Many To Many Relationships - java

I have 2 domain objects, User and SystemRights (It's a many to many, so 1 user can have many rights and 1 right can be owned by many users). I'm looking for a simple way to check if a user has the required rights.
User Domain
class User {
static hasMany = [systemRights: SystemRight, enterpriseUsers: EnterpriseUser]
String email;
String passwordHash;
}
SystemRight Domain
class SystemRight {
public static final String LOGIN = "LOGIN"
public static final String MODIFY_ALL_ENTERPRISES = "MODIFY_ALL_ENTERPRISES"
public static final String ADMINISTER_SYSTEM = "ADMINISTER_SYSTEM"
public static final String VALIDATE_SUPPLIER_SCORECARDS = "VALIDATE_SUPPLIER_SCORECARDS"
static hasMany = [users:User]
static belongsTo = User
String name
}
The following did not work for me:
In User.class
public boolean hasRights(List<String> requiredRights) {
def userHasRight = SystemRight.findByUserAndSystemRight (this, SystemRight.findByName(requiredRight));
// Nor this
def userHasRight = this.systemRights.contains(SystemRight.findByName(requiredRight));
}
Current Horrible Solution
public boolean hasRights(List<String> requiredRights) {
for (String requiredRight : requiredRights) {
def has = false
for (SystemRight userRight : user.systemRights) {
if (userRight.name == requiredRight) {
has = true
break;
}
}
if (has == false) {
return false;
}
}
return true
}

If you're able/willing to change things up a bit, I'd highly recommend doing the following. It will make you're life so much easier.
First, remove the hasMany for SystemRight and User from both Domains and remove the belongsTo User from SystemRight.
Next, create the Domain to represent the join table.
class UserSystemRight {
User user
SystemRight systemRight
boolean equals(other) {
if (!(other instanceof UserSystemRight)) {
return false
}
other.user?.id == user?.id && other.systemRight?.id == systemRight?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (user) builder.append(user.id)
if (systemRight) builder.append(systemRight.id)
builder.toHashCode()
}
// add some more convenience methods here if you want like...
static UserSystemRight get(long userId, long systemRightId, String systemRightName) {
find 'from UserSystemRight where user.id=:userId and systemRight.id=:systemRightId and systemRight.name=:systemRightName',
[userId: userId, systemRightId: systemRightId, systemRightName: systemRightName]
}
}
Then, in your User class you can add this method:
Set<SystemRight> getSystemRights() {
UserSystemRight.findAllByUser(this).collect { it.systemRight } as Set
}
Then, add this to your SystemRight Domain:
Set<User> getUsers() {
UserSystemRight.findAllBySystemRight(this).collect { it.user } as Set
}
For a more detailed explenation of why this approach is full of win, aside from actually solving your problem, take a gander at this.

I would definitely try to solve this in the database.
def relevantUserRights = SystemRight.withCriteria {
eq("user", this)
"in"("name", requiredRights);
}
return relevantUserRights.size() == requiredRights.size()

How about the following?
public boolean hasRights(List<String> requiredRights) {
return null != (this.systemRights.find { requiredRights.contains(it) });
}
(Not tested: Groovy newbie here)

Related

Restrict acces in DTO class for fields and return different value

I'm implementing authorization in my Spring Boot application. And I want to apply authorization to dto's.
See the class below
public class ExampleDTO{
private String phoneNumber; // want to restrict acces on this field.
}
E.g.
User A with permission A can see phoneNumber 123456789
User B with permission B can see phoneNumber 123456***
A solutions can be something like this:
public void setPhoneNumber(String iban) {
if (Service.checkPermission("A")) {
this.phoneNumber= 123456789;
} else if (Service.checkPermission("B")) {
this.phoneNumber= "123456***";
} else {
this.phoneNumber= "*********";
}
}
But this is against the OOP principle.
Also I can do this in a service:
public ExampleDTO getExampleDto(String iban) {
if (checkPermission("A")) {
return ExampleDtoA;
} else if (checkPermission("B")) {
return ExampleDtoB;
} else {
return ExampleDtoC;
}
}
But this will allow code duplication;
So does one of you have a better solution?
One way to go is to use a builder to build ExampleDto. Something like this:
public class ExampleDTOBuilder {
public enum PhonePermission { A, B, NONE };
private String phoneNumber;
private PhonePermission phonePermission = PhonePermission.NONE;
public ExampleDTOBuilder setPhoneNumber( String phoneNumber ) {
this.phoneNumber = phoneNumber;
return this;
}
public ExampleDTOBuilder setPermission(PhonePermission phonePermission) {
this.phonePermission = phonePermission;
return this;
}
public ExampleDTO build() {
ExampleDTO dto = new ExampleDTO();
switch (this.phonePermission) {
case A:
dto.setPhoneNumber(this.phoneNumber);
break;
case B:
dto.setPhoneNumber(getMaskedPhoneNumber(this.phoneNumber));
default:
dto.setPhoneNumber("********");
break;
}
return dto;
}
private String getMaskedPhoneNumber(String phoneNumber) {
return "123***"; // do your masking
}
}
Use:
ExampleDTO dto = new ExampleDTOBuilder()
.setPermission(PhonePermission.A)
.setPhoneNumber("123456")
.build();
More details about the Builder pattern can be found here: https://en.wikipedia.org/wiki/Builder_pattern
I am assuming that you are saving these phone numbers in a database and that you want to persist the real phone numbers, but return different values based on permissions.
If this is the case, then you should not alter the setPhoneNumber method of the DTO as this could put the following phone number values into your DB: 123456***, *********.
You can encapsulate the logic in a converter between your entity and dto layer
public class ExampleService {
ExampleDto getExample(String iban) {
// Get entity from your DB
ExampleEntity entity = entityRepository.getEntity(iban);
return converter.convert(entity);
}
}
public class Converter {
public ExampleDto convert(ExampleEntity entity) {
ExampleDto dto = new ExampleDto();
if (checkPermission("A"))
dto.setPhoneNumber(entity.getPhoneNumber());
else if (checkPermission("B"))
dto.setPhoneNumber(entity.getPhoneNumberPartialMasked();
else
dto.setPhoneNumber(entity.getPhoneNumberMasked());
return dto;
}
}
If you are using the spring framework, then you can see the following classes: org.springframework.core.convert.converter.Converter and org.springframework.core.convert.ConversionService. There are lots of examples out there on how to use these.

Best way to iterate two lists and extract few things?

I have two classes as shown below. I need to use these two classes to extract few things.
public final class ProcessMetadata {
private final String clientId;
private final String deviceId;
// .. lot of other fields here
// getters here
}
public final class ProcMetadata {
private final String deviceId;
private final Schema schema;
// .. lot of other fields here
}
Now I have below code where I am iterating above two classes and extracting schema given a clientId.
public Optional<Schema> getSchema(final String clientId) {
for (ProcessMetadata metadata1 : processMetadataList) {
if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
String deviceId = metadata1.getDeviceId();
for (ProcMetadata metadata2 : procMetadataList) {
if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
return Optional.of(metadata2.getSchema());
}
}
}
}
return Optional.absent();
}
Is there any better way of getting what I need by iterating those two above classes in couple of lines instead of what I have? I am using Java 7.
You're doing a quadratic* search operation, which is inneficient. You can do this operation in constant time by first creating (in linear time) a mapping from id->object for each list. This would look something like this:
// do this once, in the constructor or wherever you create these lists
// even better discard the lists and use the mappings everywhere
Map<String, ProcessMetadata> processMetadataByClientId = new HashMap<>();
for (ProcessMetadata process : processMetadataList) {
processMetadataByClientId.put(process.getClientId(), process);
}
Map<String, ProcMetadata> procMetadataByDeviceId = new HashMap<>();
for (ProcMetadata metadata2 : procMetadataList) {
procMetadataByDeviceId.put(proc.getDeviceId(), proc);
}
Then your lookup simply becomes:
public Optional<Schema> getSchema(String clientId) {
ProcessMetadata process = processMetadataByClientId.get(clientId);
if (process != null) {
ProcMetadata proc = procMetadataByDeviceId.get(process.getDeviceId());
if (proc != null) {
return Optional.of(proc.getSchema());
}
}
return Optional.absent();
}
In Java 8 you could write it like this:
public Optional<Schema> getSchema(String clientId) {
return Optional.fromNullable(processMetadataByClientId.get(clientId))
.map(p -> procMetadataByDeviceId.get(p.getDeviceId()))
.map(p -> p.getSchema());
}
* In practice your algorithm is linear assuming client IDs are unique, but it's still technically O(n^2) because you potentially touch every element of the proc list for every element of the process list. A slight tweak to your algorithm can guarentee linear time (again assuming unique IDs):
public Optional<Schema> getSchema(final String clientId) {
for (ProcessMetadata metadata1 : processMetadataList) {
if (metadata1.getClientId().equalsIgnoreCase(clientId)) {
String deviceId = metadata1.getDeviceId();
for (ProcMetadata metadata2 : procMetadataList) {
if (metadata2.getDeviceId().equalsIgnoreCase(deviceId)) {
return Optional.of(metadata2.getSchema());
}
}
// adding a break here ensures the search doesn't become quadratic
break;
}
}
return Optional.absent();
}
Though of course using maps ensures constant-time, which is far better.
I wondered what could be done with Guava, and accidentally wrote this hot mess.
import static com.google.common.collect.Iterables.tryFind
public Optional<Schema> getSchema(final String clientId) {
Optional<String> deviceId = findDeviceIdByClientId(clientId);
return deviceId.isPresent() ? findSchemaByDeviceId(deviceId.get()) : Optional.absent();
}
public Optional<String> findDeviceIdByClientId(String clientId) {
return tryFind(processMetadataList, new ClientIdPredicate(clientId))
.transform(new Function<ProcessMetadata, String>() {
String apply(ProcessMetadata processMetadata) {
return processMetadata.getDeviceId();
}
});
}
public Optional<Schema> findSchemaByDeviceId(String deviceId) {
return tryFind(procMetadataList, new DeviceIdPredicate(deviceId.get())
.transform(new Function<ProcMetadata, Schema>() {
Schema apply(ProcMetadata procMetadata) {
return processMetadata.getSchema();
}
});
}
class DeviceIdPredicate implements Predicate<ProcMetadata> {
private String deviceId;
public DeviceIdPredicate(String deviceId) {
this.deviceId = deviceId;
}
#Override
public boolean apply(ProcMetadata metadata2) {
return metadata2.getDeviceId().equalsIgnoreCase(deviceId)
}
}
class ClientIdPredicate implements Predicate<ProcessMetadata> {
private String clientId;
public ClientIdPredicate(String clientId) {
this.clientId = clientId;
}
#Override
public boolean apply(ProcessMetadata metadata1) {
return metadata1.getClientId().equalsIgnoreCase(clientId);
}
}
Sorry.

How to create object based on boolean condition?

I have an Item object having 4 String fields and 3 boolean fields.
I have to construct this object based on the 3 boolean variables.
The target is whenever any one of the boolean variable is true we have to create the object having that/those boolean variable set.
If for any situation none of the boolean variables are true, we wont create the object.
I am using a COR to check whether any of the boolean fields will be set or not based on some business logic.
I was trying this with builder, but then I have to construct so many objects and later discard them when none of the boolean variables found true.
Can anyone have any better idea, to solve this kind of problem ?
Well thanks for the 2 delete flag for this question. Thank for the thoughts on this question as well.
I did something to achieve what I want. Which is quite flexible I believe. Only part if there is a dependency on If loop, but that is acceptable since Report class can have extra boolean so when that class is changed, it's builder should be touched to cater that change. Rest this is flexible which I wanted.
public class Report {
private String acftNo;
private Date plannedDate;
private String plannedStn;
private Integer mntncId;
private Set<String> capableStations;
private String routedStn;
private boolean isRoutedNEQPlannedStn; //Inconsistency type 1
private boolean isCapableAtPlannedStn; //Inconsistency type 2
private boolean isPlannedOrRoutedStationExists; //Inconsistency type 3/5
public Report(String acftNo, Integer mntncId) {
super();
this.acftNo = acftNo;
this.mntncId = mntncId;
}
public Report(String acftNo, Date plannedDate, String plannedStn,
Integer mntncId) {
super();
this.acftNo = acftNo;
this.plannedDate = plannedDate;
this.plannedStn = plannedStn;
this.mntncId = mntncId;
}
//setters and getters. Removed for space.
public static Report buildReport(Maintenance<?> task, Set<InconsistencyReport> enumSet) {
Report temp = new Report(task.getAssignment().getAircraftNumber(),task.getAssignment().getMntncScheduleDate(),
task.getAssignment().getStationCode(),task.getAssignment().getMntncId());
temp.setCapableStations(InconsistencyReport.getCapableStations(task));
for(InconsistencyReport ir : enumSet)
{
if(ir.compareTo(InconsistencyReport.ROUTED_STN_NEQ_PLANNED_STN)==0)
temp.setRoutedNEQPlannedStn(true);
if(ir.compareTo(InconsistencyReport.ITEM_NT_CAPABLE_AT_PLANNED_STN)==0)
temp.setCapableAtPlannedStn(true);
if(ir.compareTo(InconsistencyReport.NO_ROUTD_STN_ON_A_DATE)==0)
temp.setPlannedOrRoutedStationExists(true);
}
return temp;
}
}
calculateInconsitencyReport() method which will decide whether to create object or not.
public class InconsistencyReportChain {
public enum InconsistencyReport implements InconsistencyReportIface {
ROUTED_STN_NEQ_PLANNED_STN {
#Override
public boolean findInconsistency(Maintenance<?> task ) {
if(!validate(task))
return false;
//some logic
return true;
return false;
}
},
ITEM_NT_CAPABLE_AT_PLANNED_STN {
#Override
public boolean findInconsistency(Maintenance<?> task) {
if(!validate(task))
return false;
//some logic
return true;
return false;
}
},
NO_ROUTD_STN_ON_A_DATE {
#Override
public boolean findInconsistency(Maintenance<?> task) {
if(!validate(task))
return false;
//some logic
return true
return false;
}
};
#Override
public boolean validate(Maintenance<?> task) {
return !(null == task.getAssignment());
}
static Set<String> getCapableStations(Maintenance<?> task)
{
Set<String> capableStations = newHashSet();
if(task.getCapStationList() != null)
{
capableStations.addAll(Arrays.asList(task.getCapStationList().split(StringConstants.COMMA_SPLIT_REGEX)));
}
if(task.getCapStationClassList() != null)
{
Map<String, List<String>> stationClassMap = CacheManager.get(STN_CLASS.name());
List<String> stationClass = Arrays.asList(task.getCapStationClassList().split(StringConstants.COMMA_SPLIT_REGEX));
for(String stnClass : stationClass)
{
capableStations.addAll(stationClassMap.get(stnClass));
}
}
return capableStations;
}
}
public static Report calculateInconsitencyReport(Maintenance<?> task) {
Set<InconsistencyReport> enumSet = null;
for(InconsistencyReport iReport : InconsistencyReport.values())
{
if(iReport.findInconsistency(task))
{
if(null==enumSet)
enumSet = EnumSet.of(iReport);
else
enumSet.add(iReport);
}
}
if(null!= enumSet && enumSet.size() > 0)
return Report.buildReport(task,enumSet);
return null;
}
}
Helper Interface:
public interface InconsistencyReportIface {
public boolean findInconsistency(Maintenance<?> task );
public boolean validate(Maintenance<?> task );
}
Details of class logic is teared off because of security.
What is the problem? Just create your object when one of your booleans is true.
if(bool1 || bool2 || bool3) {
item = new Item(str1, str2, str3, str4, bool1, bool2, bool3);
}
From what I understand of your description:
a) you will have some bools that will determine wether you create a certain object or not.
b) you may have to include some more bools into the "check protocol"
c) you have to do this checking in a loop where
i/ you check for the bool variable
ii/ you check if the object had been created previously
I still don't quite get it yet, but.. that looks pretty straight forward to me. Let's say your bools are stored in a boolean array boolean[] bools and your strings in a string array String[] strings (which, btw, I don't know what they are used for). You are saying to check if every bool is true and then create an object based on that result.
boolean[] bools = new boolean[] { ... };
String[] strings = new String[] { ... };
boolean checks = false;
for(int i = 0; i<bools.length && !checks; i++)
checks = bools[i];
//so far we will have processed if any of the bools was false, which was your condition
if(checks)
Object object = new Object(); //create your desired object
I don't understand why you would need to check if the object has been constructed previously, though, so I didn't include it in my suggestion :P

recursive call in looping a list

I m trying to loop a list of users to find a person. Every person has a friends list. So i m using recursive call to check if the person is in someones friends list.
My Junit test is look like
#Test
public void IsInFriendsCicle() throws UserAlreadyInFriendListException, NoFriendFoundException, UsersNotConnectedException {
User one = new UserImpl("John","Snow");
User two = new UserImpl("Richard","Gerns");
User three = new UserImpl("Natalie","Portman");
User four = new UserImpl("Brad","Pitt");
User five = new UserImpl("Angelina","Jolie");
one.addFriend(two);
two.addFriend(three);
three.addFriend(four);
four.addFriend(five);
assertTrue(one.isInFriendsCycle(five, one.getFriends(), new Stack()));
}
So as it can be seen here, i want to know if Angelina is in the friends list of john. So it supposed to give back true.
The responsible method is for that :
public boolean isInFriendsCycle(User userToFind, ArrayList<User> list, Stack stack){
Stack s = stack;
ArrayList<User> groupList = list;
if(groupList.contains(userToFind)){
return true;
}else{
for (User user : groupList) {
if(!s.contains(user)){
s.push(user);
if(user.getFriends().contains(userToFind)){
return true;
}else{
return isInFriendsCycle(userToFind, user.getFriends(), s);
}
}
}
}
return false;
}
So the class is :
public class UserImpl implements User{
private String name;
private String surname;
private static int count = 0;
private int id;
private ArrayList<User> friends;
private ArrayList<Message> messagebox;
final static Logger logger = Logger.getLogger(UserImpl.class);
public UserImpl(String name, String surname) {
this.name = name;
this.surname = surname;
this.id = ++count;
this.friends = new ArrayList<User>();
this.messagebox = new ArrayList<Message>();
}
#Override
public User addFriend(User person) throws UserAlreadyInFriendListException,IllegalArgumentException{
if(this.getFriends().contains(person)){
throw new UserAlreadyInFriendListException("user is already in the friendlist");
}else if(person == null || this.equals(person) ){
throw new IllegalArgumentException("parameter is null or user trying to add himself as friend");
}else{
this.getFriends().add(person);
person.getFriends().add(this);
logger.debug(this.name + " added the user "+person.getName());
return person;
}
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final UserImpl other = (UserImpl) obj;
if (this.id != other.id) {
return false;
}
return true;
}
}
There is a problem with stack somehow. I m using it to mark the persons so i dont get in the infinitive loop. There is a reason for passing user.getFriends() so it should stay in that way.
Any help will be appreciated !
replace
return isInFriendsCycle(userToFind, user.getFriends(), s);
with
if (isInFriendsCycle(userToFind, user.getFriends(), s)) return true;
As you have it, you prematurely exit if you didn't find it in the first branch of the recursive call. You don't want that - you want to continue until you find it, or there is nothing left to search through.
As an aside - your unit test wasn't helping you as you made the nesting too deep. If you had a few unit tests, first testing a friend-friend, then a friend-friend-friend, etc., you would have started to see where it was going wrong.
Also, as another aside: I would not use Stack (it is a legacy class) use Deque instead, which is the Java 6+ replacement. However in this case I would use a Set<User> in the form HashSet<User> as it fits your use case and probably performance requirements. I would also make the list variable types a List<User> not an ArrayList<User> inside your UserImpl class and isInFriendsCycle method, just to concentrate on the contracts and not the implementation.
I'd implement it as follows:
private Set<User> friends = new HashSet<User>();
public void addFriend(User friend) {
friends.add(friend);
}
public boolean isImmediateFriend(User user) {
return friends.contains(user);
}
public boolean isInFriendNetwork(User user) {
Set<User> visited = new HashSet<>();
List<User> stack = new LinkedList<>();
stack.push(this);
while (!stack.isEmpty()) {
User current = stack.removeFirst();
if (current.isImmediateFriend(user)) {
return true;
}
visited.add(current);
for (User friend : current.getFriends()) {
// never visit the same user twice
if (!visited.contains(friend)) {
stack.addLast(friend);
}
}
}
return false;
}
Pseudocode for the recursive algorithm with marking:
IsFriendOf(A, B):
if A == B:
return True
B.Marked= True
for all C in B.Friends:
if not C.Marked and IsFriendOf(A, C):
B.Marked= False
return True
B.Marked= False
return False

How to compose streams\Observables correctly

I have conceptual problem understanding how to compose between streams\Observables which have different return type.
Here is a draft method I'm trying to code:
public void findSeat() {
rx.Observable<GameObject> userObs = context.getUser();
rx.Observable<ActiveGame> gameObs = context.findGame();
rx.Observable.zip(userObs, gameObs, (userObj, game) -> {
User user = ...;
final List<Object> results = new ArrayList<Object>(3);
if(userObj.getStatus() != ErrorCodes.STATUS_OK) {
results.add(-1);
return results;
}
...
...
//***********************************
// THE PROBLEM IS HERE:
// "context.getActiveGameManager().updateGame(game)" returns Observable<GameOBject> and not List<Object> like .zip() expects.
// because of that I cannot do:
// "return context.getActiveGameManager().updateGame(game);"
// How can I do this convertion from Observable<GameObject> to List<Object>
//************************************
context.getActiveGameManager().updateGame(game)
.map((gameObj) -> {
if(gameObj.getStatus() != ErrorCodes.STATUS_OK) {
results.add(-2);
return (Observable<? extends Object>) results;
}
results.add(ErrorCodes.STATUS_OK);
results.add(user);
results.add(gameObj);
return gameObs;
});
return Observable.empty();
}).subscribe((results) -> {
int status = (int) results.get(0);
User user = (User) results.get(1);
ActiveGame game = (ActiveGame) results.get(2);
replyObj.reply(new JsonObject()
.putString("action", CommandActions.FIND_SEAT)
.putNumber("status", status);
.putNumber("game_id", game.getGameId())
);
});
}
The flow is as follow:
1. emit 2 Observable using .zip method.
2. do some logic on the return value of streams and if it results in error-code --> put it in list and return it so "subscribe" can return the error to user.
3. if no error, emit another "update" method using flatMap() - and this is where I have my problem.
4. eventually, all the results should be processed in "subscribe" because this is the point I acknowledge the user about his request.
Hope it's clear enough...
by the way, I'm trying to learn rxJava, but it's very hard I find there are enough\good sources - can someone recommend to me the best way to learn it?? I trying looking at tutorials at Youtube, Wikipedia, Github...most of them teaches using Scala and other scripting languages - couldn't find anything in Java.
Thank you for everyone that put the effort trying understand it!!
I think you were almost there, but try breaking down the code inside your .zip lambda into smaller Rx operations. For example:
rx.Observable
.zip(userObs, gameObs, (userObj, game) -> {
// Combine the user & game objects and pass them to the
// next Rx operation.
return new UserAndActiveGame(userObj, game);
})
.filter(userAndActiveGame -> {
// Remove this filter if you want errors to make it to the subscriber.
return userAndActiveGame.getUserObj().getStatus() == ErrorCodes.STATUS_OK;
})
.flatMap(userAndActiveGame -> {
// Remove this check if you filter errors above.
if (userAndActiveGame.getUserObj().getStatus() != ErrorCodes.STATUS_OK) {
return Observable.just(new FindSeatResult(-1));
}
return context.getActiveGameManager().updateGame(userAndActiveGame.getGame())
.map(gameObj -> {
if (gameObj.getStatus() != ErrorCodes.STATUS_OK) {
return new FindSeatResult(-2);
}
User user =...; // Whatever you are doing to get this in your example code.
return new FindSeatResult(ErrorCodes.STATUS_OK, user, gameObj);
});
})
The following classes are used for passing intermediate and final results:
private class UserAndActiveGame {
private final GameObject userObj;
private final ActiveGame game;
public UserAndActiveGame(GameObject userObj, ActiveGame game) {
this.userObj = userObj;
this.game = game;
}
public GameObject getUserObj() {
return userObj;
}
public ActiveGame getGame() {
return game;
}
}
private class FindSeatResult {
private final int status;
private final User user;
private final ActiveGame game;
public FindSeatResult(int status) {
this(status, null, null);
}
public FindSeatResult(int status, User user, ActiveGame game) {
this.status = status;
this.user = user;
this.game = game;
}
public User getUser() {
return user;
}
public int getStatus() {
return status;
}
public ActiveGame getGame() {
return game;
}
}
Your subscriber then uses the packaged result similar to what you are already doing.
.subscribe((results) -> {
// You don't need this if you filter errors above.
if (findSeatResult.getStatus() == -1) {
return;
}
int status = findSeatResult.getStatus();
User user = findSeatResult.getUser();
ActiveGame game = findSeatResult.getGame();
replyObj.reply(new JsonObject()
.putString("action", CommandActions.FIND_SEAT)
.putNumber("status", status);
.putNumber("game_id", game.getGameId())
);
});
By using the intermediate and final results classes instead of passing around your results in a List<Object> your code is much more forgiving to changes and the compiler will type check everything for you.

Categories

Resources