Implementing Copyright API in Open Event Frontend

This article illustrates how the copyright details have been displayed in the Open Event Frontend project using the Open Event Orga API. The API endpoints which will be mainly focussing on for fetching the copyright details are:

GET /v1/event-copyright/{event_copyright_id}

The events have copyrights which give the creator of the event exclusive rights for its use and distribution. In the Open Event application, the copyright details can be seen on the public event page. The public event page contains the events details like description, venue, tickets, speakers, sponsors along with the copyright details and these details are present on the public/index route in the application. Apart from index route, we have multiple subroutes to display the detailed information of speakers, sessions and schedule. The one thing which remains common to all the public event pages is the copyright information. Most of the time the copyright details are event specific so they are nested within the event model so if we want to display them we need to fetch the event model first.

The code to fetch the event model looks like this:

model(params) {
return this.store.findRecord('event', params.event_id, { include: 'social-links, event-copyright' });
}

If we try to comprehend the code then we can see that ‘event-copyright’ details are included inside the model. The reason behind this is the fact that the copyright information is not specific to a particular route and is displayed on the all the public event pages. After fetching the copyright details the next step we need to perform is to display them on the event’s index page.

The code to display the copyright details looks like this:

{{#if model.event.copyright}}
  <div class="copyright">
    {{public/copyright-item copyright=model.event.copyright}}
  </div>
{{/if}}

In the first line, we have an if conditional statement which verifies whether the copyright data exists or not. If the data does not exist then the copyright class will not be visible on the page and if the model is not empty then it will be displayed with the help of model.event.copyright which is responsible for displaying the fetched data on the page.

If we see in the third line, we have called an another template ‘copyright-item’ which is responsible for how the data will look or in simpler words the UI of the copyright data.

The code which determines UI of the copyright details looks like this:

<img src="{{copyright.logoUrl}}" class="copyright-image" alt="{{copyright.licence}}">
<br>
<div class='copyright text'>
  <p>
    {{t 'This event is licenced under'}} <a href="{{copyright.licenceUrl}}"> {{copyright.licence}} </a>.
  </p>
</div>

In the first line of code, we are providing the src to the image which is stored in ‘logoUrl’ variable of the copyright object. If we hover the image we can see the copyright license which is stored in the ‘license’ variable. Then finally we have copyright license’s URL which is stored under ‘licenceUrl’ variable of copyright object. The resulting UI from the above source code looks like this :

Fig. 1: The user interface of the copyright details

Now we need to test whether the copyright details are completely displayed or not. To test it we created an integration test in which we created a sample ember object to check the correctness of the code. The sample ember object for copyright details looks like this:

To view the complete code regarding the copyright API integration check this.

const copyright = EmberObject.create({
  holder     : 'Creative Commons',
  holderUrl  : 'https://creativecommons.org',
  licence    : 'Public Domain Dedication (CC0)',
  licenceUrl : 'https://creativecommons.org/publicdomain/zero/1.0/',
  year       : 2007,
  logoUrl    : 'http://image.ibb.co/gt7q7v/pdd.png'
});

To conclude, this is how we integrated copyright information inside the Open Event Frontend project using the Open Event Orga API efficiently.

Resources:

Image Source : https://libsource.com/understanding-creative-commons-licensing/

Ember Mixins used in Open Event Frontend

This post will illustrate how ember mixins are used in the Open Event Frontend to avoid code duplication and to keep it clean to reduce code complexity.

The Open Event application needs forms at several places like at the time of login, for the creation of the event, taking the details of the user, creating discount codes for tickets etc. Every form performs similar actions which taking input and finally storing it in the database. In this process, a set of things keep executing in the background such as continuous form validation which means checking inputted values to ensure they are correctly matched with their type as provided in the validation rules, popping out an error message in case of wrong inputted values, etc. which is common to all the forms. The only thing which changes is the set of validation rules which are unique for every form. So let’s see how we solved this issue using Ember Mixins.

While writing the code, we often run into the situation where we need to use the similar behaviour in different parts of the project. We always try not to duplicate the code and keep finding the ways to DRY ( Don’t Repeat Yourself ) up the code. In Ember, we can share the code in the different parts of the project using Ember.Mixins.

While creating the forms, we mostly have differing templates but the component logic remains same. So we created a mixin form.js which contains all the properties which are common to all the forms. The code mixin contains can be reused throughout different parts of the application and is not a concern of any one route, controller, component, etc. The mixins don’t get instantiated until we pass them into some object, and they get created in the order of which they’re passed in. The code for form.js mixin looks like this.

export default Mixin.create({
  autoScrollToErrors : true,
  autoScrollSpeed    : 200,

  getForm() {
    return this.get('$form');
  },

  onValid(callback) {
    this.getForm().form('validate form');
    if (this.getForm().form('is valid')) {
      callback();
    }
  },
  
  didRender() {
      const $popUps = this.$('.has.popup');
      if ($popUps) {
        $popUps.popup({
          hoverable: true
        });
      }
      let $form = this.$('.ui.form');
      if ($form) {
        $form = $form.first();
        if (this.get('getValidationRules') && $form) {
          $form.form(merge(defaultFormRules, this.getValidationRules()));
        }
      },
  didInsertElement() {
    $.fn.form.settings.rules.date = (value, format = FORM_DATE_FORMAT) => {
      if (value && value.length > 0 && format) {
        return moment(value, format).isValid();
      }
      return true;
    };
  }

The complete code can be seen here.

Let’s start understanding the above code. In the first line, we created a mixin via Ember.Mixin.create() method. We have then specified the property ‘autoScrollToErrors’ to true so in case if there is some error, the form will automatically scroll to the error. The property ‘autoScrollSpeed’ specifies the speed with which the form will auto scroll to show the error. ‘getForm()’ method helps in getting the object which will be passed to the mixin. In ‘onValid()’ method we’re validating the form and passing the callbacks if it is correctly validated. We then have ‘didRender()’ method which renders the popups, checkboxes and form rules. The popups help in showing the errors on the form. In this method, we’re fetching the validation rules which are written in child/subclasses which are using this mixin to create the form.  The validation rules help in validating the form and tells if the value inputted is correct or not. In most of the forms, we have a field which asks for some specific date. The piece of code under ‘didInsertElement()’ helps in validating the date and returns true if it is correct. We have ‘willDestroyElement()’ method which destroys the popup if the window is changed/refreshed.

Let see the use case of the above form mixin. At the time of login, we see a form which asks for the user’s credentials to validate if the user is already registered or not. To create that login form we use form mixin. The code for login-form.js looks like this.

export default Component.extend(FormMixin, {
getValidationRules() {
  
fields : {
        identification: {
          identifier : 'email',
          rules      : [
            {
              type   : 'empty',
              prompt : this.l10n.t('Please enter your email ID')
            },
            {
              type   : 'email',
              prompt : this.l10n.t('Please enter a valid email ID')
            }
          ]
        },
        password: {
          identifier : 'password',
          rules      : [
            {
              type   : 'empty',
              prompt : this.l10n.t('Please enter your password')
            }
          ]
        }
      }
   }
});

The complete code can be found here.

We can see that in above code we are creating the form by extending our FormMixin which means that the form will have all the properties which are part of mixin along with the properties which remain unique to this class. Since the validation rules remain unique per form so we’re also providing the rules (easy to comprehend) which will help in validating the fields.

This is how our forms look like after and before applying validation rules.

Fig. 1: Login form before applying validation rules

          Fig. 2: Login form after applying validation rules

To sum it up, we can say that mixins are of great use when we want to keep our code DRY or reduce the code duplication. It helps in removing the unnecessary inheritance keeping it short. The code written using mixin is lesser complex and easily understandable.

References:

Adding Build Type option in the Apk Generator of the Open Event Android App

The apk-generator provided ability to the event organiser to build an apk from a single click by providing the necessary json/binary files. However it gave only one type of apk where on the other hand the Open Event Android was available with apk of different build versions.

Recently the functionality of the apk generator of the Open Event Android App was enhanced where the user is asked to select an option for build type either Google Play or FDroid which generates the apk according to that selected type.

The main difference in the googleplay apk and fdroid apk is the inclusion of googleplay libraries which aren’t included in the app/build.gradle file in case of fdroid build.

To include support for build type the following files for the apk-generator had to be changed:

  1. app/views/__init__.py
  2. app/tasks/__init__.py
  3. app/static/js/main.js
  4. app/generator/generator.py
  5. scripts/build.sh
  6. app/templates/index.html

Changes to the files

  • app/templates/index.html

This file was where the changes to the UI of the apk-generator showing build type option to the user were made. With this the user was presented with an option to choose build type among googleplay and fdroid apart from the rest of the essential information.

  • scripts/build.sh

The android app supported two different flavours one for google play and the other for fdroid. This required the build script to be modified according to the build type selected by the user during the filling of form.

If user selected “Google Play” or “Fdroid”, the script would look something like this:

#!/bin/bash
./gradlew assemblegoogleplayRelease --info
echo "signing"
jarsigner -keystore ${KEYSTORE_PATH} -storepass ${KEYSTORE_PASSWORD} app/build/outputs/apk/app-googleplay-release-unsigned.apk ${KEY_ALIAS}
echo "zipaligning"
${1}/zipalign -v 4 app/build/outputs/apk/app-$2-release-unsigned.apk release.apk
echo "done"

Where $2 is googleplay or fdroid depending on what build type the user has selected while building the apk from the apk generator.

  • app/views/__init__.py and app/tasks/__init__.py

These files were modified by adding another parameter for supporting the two build type options in the desired functions.

  • app/static/js/main.js

This is where the option selected by the user was taken and accordingly the apk corresponding to the build type option selected was made available to the user. The code for it was shown as follows:

$buildTypeRadio.change(
   function () {
       if (this.checked) {
           enableGenerateButton(true);
           buildType = $(this).val();
       }
  }
);

This was how the option to display build type option to the user was incorporated. This gave the user the ability to install different build versions of an apk, thus making it more useful from the user point of view.

Related Links:

Stubbed Routing Inbuilt Service used in Open Event Frontend

In Open Event Frontend, we have used services like ‘auth-manager’, ‘l10n’, ‘loader’, ‘sanitizer’, etc to ease our work with the help of predefined-functions in those services. However, while dealing with an issue in the project, there was a need to use ‘Routing’ as a service.

In the issue, we wanted to generate an access link dynamically from the access code entered by the user. The format of the access link was as follows:

“base_url + event_id + access_code”

So, for the above URL, we needed to have ‘event_id’ and ‘access_code’.

The ‘access_code’ can be readily accessed from the user’s input itself, whereas to get the event_id, we used the ‘Routing’ service in Ember.

Generally to use a service in Ember, it has to be written first,then registered, injected and then used.

‘Routing’ service in Ember is an inbuilt service unlike the ones listed at the beginning.

There is no need to write it. It can be simply registered, injected and used.

this.register('service:routing', routingStub);
this.inject.service('routing', { as: 'routing' });

where ‘register’ and ‘inject’ are the methods on Ember objects.

The integration tests in Open Event Frontend are written such that the services can be used without injecting, but the tests will fail. To pass those tests, we had to register and inject the service in the required component.

The Routing service could thus be registered and injected into the specific component( injection in the component’s integration test ) only but for future needs, this service might be needed in any other component too. For this purpose, this service was registered and injected in ‘component-helper.js’.

const routingStub = Service.extend({
  router: {
    router: {
      state: {
        params: {
          'events.view': {
            event_id: 1
          }
        }
      }
    },
    generate() {
      return 'http://dummy-url.com';
    }
  }
});


export default function(path, name, testCase = null) {
  moduleForComponent(path, name, {
    integration: true,

    beforeEach() {
      this.register('service:routing', routingStub);
      this.inject.service('routing', { as: 'routing' });
      this.register('service:l10n', L10n);
      this.inject.service('l10n', { as: 'l10n' });
      this.application = startApp();
      l10nTestHelper(this);
      run(() => fragmentTransformInitializer.initialize(getOwner(this)));
    }
  }
}

Stubbing a Service: This is a process of faking an app of importing a service when no path is available to import. Stubbing of a service is mainly done when one needs to deal with the testing of the app. In our case, the same is done. We have stubbed the ‘Routing’ service in order to deal with the testing part. It can be seen from the above code that we have generated a ‘routingStub’ which fakes the app while registering the service in the ‘beforeModel’. The next line of code shows the ‘injection’ of service into the app.

Now we are just left with one task i.e to pass ‘routing’ from our integration tests to the component.

test('it renders', function(assert) {
  this.render(hbs`{{forms/events/view/create-access-code routing=routing}}`);
  assert.ok(this.$().html().trim().includes('Save'));
});

Above code shows the same.

Thus we can stub the services in Ember when any component depends on them.

Resources:

Official Ember guide: https://guides.emberjs.com/v2.1.0/testing/testing-components

Blog by Todd Jordan: http://presentationtier.com/stubbing-services-in-emberjs-integration-tests/

Source codehttps://github.com/sumedh123/open-event-frontend/blob/0b193ca679ce3b51f65e19ee0d03ac6a679258de/tests/helpers/component-helper.js

Implementing ETags Support In flask-rest-jsonapi For Open Event Server

In the Open Event Server Project, the client apps required to implement ETags support so that they could efficiently consume the API.

What is an ETag ?

An entity tag (ETag) is an HTTP header used for Web cache validation and conditional requests from browsers for resources.

What is the need for an ETag ?

  • Clients can make use of this and request complete data if and only if the data has changed else use their local cache.
  • This can be used to ensure concurrency in the case of multiple clients trying to modify the same data at the same time.

How to implement ETags in the API framework ?

To implement ETags in the API framework, changes need to be done in the dispatch_request function of Resource class located at resource.py at the root of the framework.

A config variable will also be added in order to turn ETags on and off. You can name anything you want, but we went ahead with just ETAG. Now the first thing we should do is calculate the ETag hash from the original response. The response variable can be grabbed in dispatch_request and hashing can be performed on it as follows:

etag = hashlib.sha1(resp.get_data()).hexdigest()
resp.headers['ETag'] = etag # return ETag in response headers

Why did we use SHA-1 ?

In the above mentioned lines of code, you will notice that we are using SHA-1 for hashing purposes. SHA-1 is known to have collisions, so why use it ? In ETags we are not storing the hashes anywhere but are returning the ETag in the response header directly. So there is a very less probability of collision even if we used MD5, so using SHA-1 won’t hurt much 😉

Till now, the above code enables to return an ETag but that is of no use if we do not support request headers If-Match and If-None-Match. Both of these headers can be obtained from the request as follows:

if_match = request.headers.get('If-Match')
if_none_match = request.headers.get('If-None-Match')

 

For both If-Match and If-None-Match request headers, the system will accept a comma separated list of Etags. This can be accomplished as follows:

etag_list = [tag.strip() for tag in if_match.split(',')]

 

For If-Match, the response is returned only if the ETag of the current response matches any of the comma-separated ETags in the If-Match header. If none of the given ETags match, a 412 Precondition Failed status code will be returned. This can be implemented with a check as follows:

if if_match:
   etag_list = [tag.strip() for tag in if_match.split(',')]
   if etag not in etag_list and '*' not in etag_list:
       exc = PreconditionFailed({'pointer': ''}, 'Precondition failed')
       return make_response(json.dumps(jsonapi_errors([exc.to_dict()])),
                            exc.status,
                            headers)

 

For If-None-Match, the response is returned only if the ETag of the current response does not match any of the comma-separated ETags in the If-Match header. If none of the given ETags match, a 304 Not Modified status code will be returned as follows:

elif if_none_match:
   etag_list = [tag.strip() for tag in if_none_match.split(',')]
   if etag in etag_list or '*' in etag_list:
       exc = NotModified({'pointer': ''}, 'Resource not modified')
       return make_response(json.dumps(jsonapi_errors([exc.to_dict()])),
                            exc.status,
                            headers)

 

Related Links:

Supporting Dasherized Attributes and Query Params in flask-rest jsonapi for Open Event Server

In the Open Event API Server project attributes of the API are dasherized.

What was the need for dasherizing the attributes in the API ?

All the attributes in our database models are separated by underscores i.e first name would be stored as first_name. But most of the API client implementations support dasherized attributes by default. In order to attract third party client implementations in the future and making the API easy to set up for them was the primary reason behind this decision.Also to quote the official json-api spec recommendation for the same:

Member names SHOULD contain only the characters “a-z” (U+0061 to U+007A), “0-9” (U+0030 to U+0039), and the hyphen minus (U+002D HYPHEN-MINUS, “-“) as separator between multiple words.

Note: The dasherized version for first_name will be first-name.

flask-rest-jsonapi is the API framework used by the project. We were able to dasherize the API responses and requests by adding inflect=dasherize to each API schema, where dasherize is the following function:

def dasherize(text):
   return text.replace('_', '-')

 

flask-rest-jsonapi also provides powerful features like the following through query params:

But we observed that the query params were not being dasherized which rendered the above awesome features useless 🙁 . The reason for this was that flask-rest-jsonapi took the query params as-is and search for them in the API schema. As Python variable names cannot contain a dash, naming the attributes with a dash in the internal API schema was out of the question.

For adding dasherizing support to the query params, change in the QueryStringManager located at querystring.py of the framework root are required. A config variable named DASHERIZE_APIwas added to turn this feature on and off.

Following are the changes required for dasherizing query params:

For Sparse Fieldsets in the fields function, replace the following line:

result[key] = [value]
with
if current_app.config['DASHERIZE_API'] is True:
    result[key] = [value.replace('-', '_')]
else:
    result[key] = [value]

 

For sorting, in the sorting function, replace the following line:

field = sort_field.replace('-', '')

with

if current_app.config['DASHERIZE_API'] is True:
   field = sort_field[0].replace('-', '') + sort_field[1:].replace('-', '_')
else:
   field = sort_field[0].replace('-', '') + sort_field[1:]

 

For Include related objects, in include function, replace the following line:

return include_param.split(',') if include_param else []

with

if include_param:
   param_results = []
   for param in include_param.split(','):
       if current_app.config['DASHERIZE_API'] is True:
           param = param.replace('-', '_')
       param_results.append(param)
   return param_results
return []

Related links:

Modifying flask-rest-jsonapi Exception Handling in Open Event Server to Enable Support for Sentry

In Open Event Server Project, Sentry support was enabled for the project. So first of all,

What is Sentry ?

Sentry provides open source error tracking that shows you every crash in your stack as it happens, with the details needed to prioritize, identify, reproduce, and fix each issue.

The basic error tracking can be enabled with the following two simple lines,

from raven.contrib.flask import Sentry
sentry = Sentry(app, dsn='https://<key>:<secret>@sentry.io/<project>')

 

But after sometime, it was noticed that app-related errors were not being caught in Sentry, while migration related errors were being caught. This meant that sentry was functioning properly in the app, but it was having some trouble in identifying uncaught exceptions.

After a lot of digging, it came to knowledge that the api framework, flask-rest-jsonapi caught all unknown exceptions while dispatching the request. After catching the exceptions, it gave a jsonapi error with status 500 in return. Following is the code responsible for that:

except Exception as e:
           if current_app.config['DEBUG'] is True:
               raise e
           exc = JsonApiException('', 'Unknown error')
           return make_response(json.dumps(jsonapi_errors([exc.to_dict()])),
                                exc.status,
                                headers)

 

We now had to let these exceptions go uncaught and that required us to modify the api framework. Modifications were done in the api-framework’s exception handling as shown below

if 'API_PROPOGATE_UNCAUGHT_EXCEPTIONS' in current_app.config:
               if current_app.config['API_PROPOGATE_UNCAUGHT_EXCEPTIONS'] is True:
                   raise
           if current_app.config['DEBUG'] is True:
               raise e
           exc = JsonApiException({'pointer': ''}, 'Unknown error')
           return make_response(json.dumps(jsonapi_errors([exc.to_dict()])),
                                exc.status,
                                headers)

A config parameter named API_PROPOGATE_UNCAUGHT_EXCEPTIONS was added to the config to turn this feature on or off.

But with all this done, Sentry was able to catch and report uncaught exceptions, but the json-api spec compliant error messages on an unknown error were not being returned due to this new change. So it was decided to handle these uncaught exceptions in the app itself. Flask’s default error handlers were used to tackle this situation:

@app.errorhandler(500)
def internal_server_error(error):
   exc = JsonApiException({'pointer': ''}, 'Unknown error')
   return make_response(json.dumps(jsonapi_errors([exc.to_dict()])), exc.status,
                        {'Content-Type': 'application/vnd.api+json'})

Thus, all uncaught exceptions were now returning a proper json-api spec compliant error response.

Related links:

Running Dredd Hooks as a Flask App in the Open Event Server

The Open Event Server is based on the micro-framework Flask from its initial phases. After implementing API documentation, we decided to implement the Dredd testing in the Open Event API.

After isolating each request in Dredd testing, the real challenge is now to bind the database engine to the Dredd Hooks. And as we have been using Flask-SQLAlchemy db.Model Baseclass for building all the models and Flask, being a micro framework itself, came to our rescue as we could easily bind the database engine to the Flask app. Conventionally dredd hooks are written in pure Python, but we will running them as a self contained Flask app itself.

How to initialise this flask app in our dredd hooks. The Flask app can be initialised in the before_all hook easily as shown below:

def before_all(transaction):
    app = Flask(__name__)
    app.config.from_object('config.TestingConfig')

 

The database can be binded to the app as follows:

def before_all(transaction):
app = Flask(__name__)
app.config.from_object('config.TestingConfig')
db.init_app(app)
Migrate(app, db)

 

The challenge now is how to bind the application context when applying the database fixtures. In a normal Flask application this can be done as following:

with app.app_context():
#perform your operation

 

While for unit tests in python:

with app.test_request_context():
#perform tests

 

But as all the hooks are separate from each other, Dredd-hooks-python supports idea of a single stash list where you can store all the desired variables(a list or the name stash is not necessary).

The app and db can be added to stash as shown below:

@hooks.before_all
def before_all(transaction):
app = Flask(__name__)
app.config.from_object('config.TestingConfig')
db.init_app(app)
Migrate(app, db)
stash['app'] = app
stash['db'] = db

 

These variables stored in the stash can be used efficiently as below:

@hooks.before_each
def before_each(transaction):
with stash['app'].app_context():
db.engine.execute("drop schema if exists public cascade")
db.engine.execute("create schema public")
db.create_all()

 

and many other such examples.

Related Links:
1. Testing Your API Documentation With Dredd: https://matthewdaly.co.uk/blog/2016/08/08/testing-your-api-documentation-with-dredd/
2. Dredd Tutorial: https://help.apiary.io/api_101/dredd-tutorial/
3. Dredd Docs: http://dredd.readthedocs.io/

Creating System Images UI in Open Event Frontend

In Open Event Frontend, under the ‘admin/content’ route, ‘system-images’ route is present in which a user can update the image of the event topic he has uploaded at the time of creating an event. We achieved this as follows:

First, we create a route called ‘system/images’.

ember g route admin/content/system-images

This will generate three files:
1) routes/admin/content/system-images.js (route)
2) templates/admin/content/system-images.hbs (template)
3) test/unit/routes/admin/content/system-images-test.js (test file)
We also create a subroute of system-images route so as to render the subtopics queried through API.

ember g route admin/content/system-images/list

This will generate three files:
1) routes/admin/content/system-images/list.js(subroute)
2) templates/admin/content/system-images/list.hbs(template)
3) test/unit/routes/admin/content/system-imageslist-test.js(test file)

From our ‘system-images’ route, we render the ‘system-images’ template. We have a subroute of system-images route called as ‘list’ in which we render the subtopics available to us via API. The left side menu is the content of ‘system-images.hbs’ and the content on the right is it’s subroute i.e ‘list.hbs’. The ‘list’ subroute provides a facility to upload the system image. The API returns an array of objects containing subtopics as follows(single object is shown here, there will be multiple in the array)

{
            id          : 4545,
            name        : 'avatar',
            placeholder : {
              originalImageUrl : 'https://placeimg.com/360/360/any',
              copyright        : 'All rights reserved',
              origin           : 'Google Images'
            }
          },

Following is the content of our uploader i.e ‘list.hbs’ which is a subroute of the system-images.hbs.

<div class="ui segment">
  {{#each model as |subTopic|}}
    <h4>{{subTopic.name}}</h4>
    <img src="{{subTopic.placeholder.originalImageUrl}}" class="ui fluid image" alt={{subTopic.name}}>
    <div class="ui hidden divider"></div>
    <button class="ui button primary" {{action 'openModal' subTopic}} id="changebutton">{{t 'Change'}}</button>
  {{/each}}
</div>
{{modals/change-image-modal isOpen=isModalOpen subTopic=selectedSubTopic}}

We can see from the above template that we are iterating the response(subtopics) from the API. For now, we are just using the mock server response since we don’t have API ready for it. There is one ‘upload’ button which opens up the ‘change-image-modal’ to upload the image which looks as follows:

The ‘change-image-modal.hbs’ has a content as follows:

<div class="sixteen wide column">
        {{widgets/forms/image-upload
          needsCropper=true
          label=(t 'Update Image')
          id='user_image'
          aspectRatio=(if (eq subTopic.name 'avatar') (array 1 1))
          icon='photo'
          hint=(t 'Select Image')
          maxSizeInKb=10000
          helpText=(t 'For Cover Photos : 300x150px (2:1 ratio) image.
                    For Avatar Photos : 150x150px (1:1 ratio) image.')}}

        <form class="ui form">
          <div class="field">
            <label class="ui label">{{t 'Copyright information'}}</label>
            <div class="ui input">
              {{input type="text"}}
            </div>
          </div>
          <div class="field">
            <label class="ui label">{{t 'Origin information'}}</label>
            <div class="ui input">
              {{input type="text"}}
            </div>
          </div>
        </form>

      </div>

The above uploader has a custom ‘image-upload’ widget which we are using throughout the Open Event Frontend. Also, there are two input fields i.e ‘copyright’ and ‘origin’ information of the image. On clicking the ‘Select Image’ button and after selecting our image from the file input, we get a cropper for the image to be uploaded. The image can be cropped there according to the aspect ration maintained for it. The cropper looks like:

Thus, a user can update the image of the Event Topic that he created.

Resources:

Ember JS Official guide.

Mastering modals in Ember JS by Ember Guru.

Source codehttps://github.com/fossasia/open-event-frontend

 

Testing Errors and Exceptions Using Unittest in Open Event Server

Like all other helper functions in FOSSASIA‘s Open Event Server, we also need to test the exception and error helper functions and classes. The error helper classes are mainly used to create error handler responses for known errors. For example we know error 403 is Access Forbidden, but we want to send a proper source message along with a proper error message to help identify and handle the error, hence we use the error classes. To ensure that future commits do not mismatch the error, we implemented the unit tests for errors.

There are mainly two kind of error classes, one are HTTP status errors and the other are the exceptions. Depending on the type of error we get in the try-except block for a particular API, we raise that particular exception or error.

Unit Test for Exception

Exceptions are written in this form:

@validates_schema
    def validate_quantity(self, data):
        if 'max_order' in data and 'min_order' in data:
            if data['max_order'] < data['min_order']:
                raise UnprocessableEntity({'pointer': '/data/attributes/max-order'},
                                          "max-order should be greater than min-order")

 

This error is raised wherever the data that is sent as POST or PATCH is unprocessable. For example, this is how we raise this error:

raise UnprocessableEntity({'pointer': '/data/attributes/min-quantity'},

           "min-quantity should be less than max-quantity")

This exception is raised due to error in validation of data where maximum quantity should be more than minimum quantity.

To test that the above line indeed raises an exception of UnprocessableEntity with status 422, we use the assertRaises() function. Following is the code:

 def test_exceptions(self):
        # Unprocessable Entity Exception
        with self.assertRaises(UnprocessableEntity):
            raise UnprocessableEntity({'pointer': '/data/attributes/min-quantity'},
                                      "min-quantity should be less than max-quantity")


In the above code,
with self.assertRaises() creates a context of exception type, so that when the next line raises an exception, it asserts that the exception that it was expecting is same as the exception raised and hence ensures that the correct exception is being raised

Unit Test for Error

In error helper classes, what we do is, for known HTTP status codes we return a response that is user readable and understandable. So this is how we raise an error:

ForbiddenError({'source': ''}, 'Super admin access is required')

This is basically the 403: Access Denied error. But with the “Super admin access is required” message it becomes far more clear. However we need to ensure that status code returned when this error message is shown still stays 403 and isn’t modified in future unwantedly.

Here, errors and exceptions work a little different. When we declare a custom error class, we don’t really raise that error. Instead we show that error as a response. So we can’t use the assertRaises() function. However what we can do is we can compare the status code and ensure that the error raised is the same as the expected one. So we do this:

def test_errors(self):
        with app.test_request_context():
            # Forbidden Error
            forbidden_error = ForbiddenError({'source': ''}, 'Super admin access is required')
            self.assertEqual(forbidden_error.status, 403)

            # Not Found Error
            not_found_error = NotFoundError({'source': ''}, 'Object not found.')
            self.assertEqual(not_found_error.status, 404)


Here we firstly create an object of the error class
ForbiddenError with a sample source and message. We then assert that the status attribute of this object is 403 which ensures that this error is of the Access Denied type using the assertEqual() function, which is what was expected.
The above helps us maintain that no one in future unknowingly or by mistake changes the error messages and status code so as to maintain the HTTP status codes in the response.


Resources: