Implementation of Role Invites in Open Event Organizer Android App

Open Event Organizer Android App consists of various features which can be used by event organizers to manage their events. Also, they can invite other people for various roles. After acceptance of the role invite, the particular user would have access to features like the event settings and functionalities like scanning of tickets and editing of event details, depending on the access level of the role. There can be various roles which can be assigned to a user: Organizer, Co-Organizer, Track Organizer, Moderator, Attendee, Registrar. Here we will go through the process of implementing the feature to invite a person for a particular role for an event using that person’s email address. The ‘Add Role’ screen has an email field to enter the invitee’s email address and select the desired role for the person. Upon clicking the ‘Send Invite’ button, the person would be sent a mail containing a link to accept the role invite. The Role class is used for the different types of available roles. @Data @Builder @Type("role") @AllArgsConstructor @NoArgsConstructor @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) public class Role { @Id(LongIdHandler.class) public Long id; public String name; public String titleName; } The RoleInvite class: @Data @Builder @Type("role-invite") @AllArgsConstructor @NoArgsConstructor @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) public class RoleInvite { @Id(LongIdHandler.class) public Long id; @Relationship("event") public Event event; @Relationship("role") public Role role; public String email; public String createdAt; public String status; public String roleName; } A POST request is required for sending the role invite using the email address of the recipient as well as the role name. @POST("role-invites") Observable<RoleInvite> postRoleInvite(@Body RoleInvite roleInvite); On clicking the ‘Send Invite’ button, the email address would be validated and if it is valid, the invite would be sent. binding.btnSubmit.setOnClickListener(v -> { if (!validateEmail(binding.email.getText().toString())){ showError(getString(R.string.email_validation_error)); return; } roleId = binding.selectRole.getSelectedItemPosition() + 1; roleInviteViewModel.createRoleInvite(roleId); }); createRoleInvite() method in RoleInviteViewModel: public void createRoleInvite(long roleId) { long eventId = ContextManager.getSelectedEvent().getId(); Event event = new Event(); event.setId(eventId); roleInvite.setEvent(event); role.setId(roleId); roleInvite.setRole(role); compositeDisposable.add( roleRepository .sendRoleInvite(roleInvite) .doOnSubscribe(disposable -> progress.setValue(true)) .doFinally(() -> progress.setValue(false)) .subscribe(sentRoleInvite -> { success.setValue("Role Invite Sent"); }, throwable -> error.setValue(ErrorUtils.getMessage(throwable).toString()))); } It takes roleId as an argument which is used to set the desired role before sending the POST request. We can notice the use of sendRoleInvite() method of RoleRepository. Let’s have a look at that: @Override public Observable<RoleInvite> sendRoleInvite(RoleInvite roleInvite) { if (!repository.isConnected()) { return Observable.error(new Throwable(Constants.NO_NETWORK)); } return roleApi .postRoleInvite(roleInvite) .doOnNext(inviteSent -> Timber.d(String.valueOf(inviteSent))) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } Resources: API Documentation: Roles, Role Invites Pull Request: feat: Implement system of role invites Open Event Organizer App: Project repo, Play Store, F-Droid

Continue ReadingImplementation of Role Invites in Open Event Organizer Android App

How User Event Roles relationship is handled in Open Event Server

Users and Events are the most important part of FOSSASIA's Open Event Server. Through the advent and upgradation of the project, the way of implementing user event roles has gone through a lot many changes. When the open event organizer server was first decoupled to serve as an API server, the user event roles like all other models was decided to be served as a separate API to provide a data layer above the database for making changes in the entries. Whenever a new role invite was accepted, a POST request was made to the User Events Roles table to insert the new entry. Whenever there was a change in the role of an user for a particular event, a PATCH request was made. Permissions were made such that a user could insert only his/her user id and not someone else’s entry. def before_create_object(self, data, view_kwargs): """ method to create object before post :param data: :param view_kwargs: :return: """ if view_kwargs.get('event_id'): event = safe_query(self, Event, 'id', view_kwargs['event_id'], 'event_id') data['event_id'] = event.id elif view_kwargs.get('event_identifier'): event = safe_query(self, Event, 'identifier', view_kwargs['event_identifier'], 'event_identifier') data['event_id'] = event.id email = safe_query(self, User, 'id', data['user'], 'user_id').email invite = self.session.query(RoleInvite).filter_by(email=email).filter_by(role_id=data['role'])\ .filter_by(event_id=data['event_id']).one_or_none() if not invite: raise ObjectNotFound({'parameter': 'invite'}, "Object: not found") def after_create_object(self, obj, data, view_kwargs): """ method to create object after post :param data: :param view_kwargs: :return: """ email = safe_query(self, User, 'id', data['user'], 'user_id').email invite = self.session.query(RoleInvite).filter_by(email=email).filter_by(role_id=data['role'])\ .filter_by(event_id=data['event_id']).one_or_none() if invite: invite.status = "accepted" save_to_db(invite) else: raise ObjectNotFound({'parameter': 'invite'}, "Object: not found") Initially what we did was when a POST request was sent to the User Event Roles API endpoint, we would first check whether a role invite from the organizer exists for that particular combination of user, event and role. If it existed, only then we would make an entry to the database. Else we would raise an “Object: not found” error. After the entry was made in the database, we would update the role_invites table to change the status for the role_invite. Later it was decided that we need not make a separate API endpoint. Since API endpoints are all user accessible and may cause some problem with permissions, it was decided that the user event roles would be handled entirely through the model instead of a separate API. Also, the workflow wasn’t very clear for an user. So we decided on a workflow where the role_invites table is first updated with the particular status and after the update has been made, we make an entry to the user_event_roles table with the data that we get from the role_invites table. When a role invite is accepted, sqlalchemy add() and commit() is used to insert a new entry into the table. When a role is changed for a particular user, we make a query, update the values and save it back into the table. So the entire process is handled in the data layer level rather than the API level. The code implementation is as follows: def before_update_object(self, role_invite, data, view_kwargs): """ Method to edit object…

Continue ReadingHow User Event Roles relationship is handled in Open Event Server