Implementing User Permissions API on Open Event Frontend to View and Update User Permission Settings

This blog article will illustrate how the user permissions  are displayed and updated on the admin permissions page in Open Event Frontend, using the user permissions API. Our discussion primarily will involve the admin/permissions/index route to illustrate the process.

The primary end point of Open Event API with which we are concerned with for fetching the permissions  for a user is

GET /v1/user-permissions

First we need to create a model for the user-permissions, which will have the fields corresponding to the api, so we proceed with the ember CLI command:

ember g model user-permission

Next we define the model according to the requirements. The model needs to extend the base model class, and other than the name and description all the fields will be boolean since the user permissions frontend primarily consists of checkboxes to grant and revoke permissions. Hence the model will be of the following format.

import attr from 'ember-data/attr';
import ModelBase from 'open-event-frontend/models/base';

export default ModelBase.extend({
 name           : attr('string'),
 description    : attr('string'),
 unverifiedUser : attr('boolean'),
 anonymousUser  : attr('boolean')
});

Now we need to load the data from the api using this model, so will send a get request to the api to fetch the current permissions. This can be easily achieved via a store query in the model hook of the admin/permissions/system-roles route. It is important to note here, that findAll is preferred over an empty query. To quote the source of this information,

The reason findAll is preferred over query when no filtering is done is, query will always make a server request. findAll on the other hand, will not make a server request if findAll has already been used once somewhere before. It’ll re-use the data already available whenever possible.

model() {
   return this.get('store').findAll('user-permission');
 }

The user permissions form is not a separate component and is directly embedded in the route template hence, there is no need to explicitly pass the model, it will be available in the route template by default. And can be used as following:

{{#each model as |userPermission|}}
<tr>
  <td>
    {{userPermission.name}}
    <div class="muted text">
      {{userPermission.description}}
    </div>
  </td>
  <td>
     {{ui-checkbox label=(t 'Unverified User') checked=userPermission.unverifiedUser onChange=(action (mut userPermission.unverifiedUser))}}
  </td>
  <td>
    {{ui-checkbox label=(t 'Anonymous User') checked=userPermission.anonymousUser onChange=(action (mut userPermission.anonymousUser))}}
  </td>
</tr>
{{/each}}

In the template after mutating the model’s values according to whether the checkboxes are checked or not, the only thing left is triggering the update action in the controller which will be triggered with the default submit action of the form.

updatePermissions() {
     this.set('isLoading', true);
     this.get('model').save()
       .then(() => {
         this.set('isLoading', false);
         this.notify.success(this.l10n.t('User permissions have been saved successfully.'));
       })
       .catch(()=> {
         this.set('isLoading', false);
         this.notify.error(this.l10n.t('An unexpected error has occurred. User permissions not saved.'));
       });
   }

The controller action first sets the isLoading property to true. This adds the semantic UI class loading to the the form,  and so the form goes in the loading state, to let the user know the request is being processed. Then the save()  call occurs and this makes a PATCH request to the API to update the values stored inside the database. And if the PATCH request is successful, the .then() clause executes, which in addition to setting the isLoading as false, notifies the user that the settings have been saved  successfully using the notify service.

However, in case there is an unexpected error and the PATCH request fails, the .catch() executes. After setting isLoading to false, it notifies the user of the error via an error notification.

Resources

 

 

Published by

CosmicCoder96

GSOC 17 @ FOSSASIA | Full Stack Developer | Swimmer