Skip to content

How to Add an Interface

Brian Redd edited this page Jan 20, 2018 · 2 revisions

The objective for Augeo is to incorporate all sorts of interfaces that can portray a person's skills. For example, when interfaced with Instagram, people can gain photography experience. Or a person can increase their Software rank by contributing to open source projects on GitHub. This guide gives a detailed overview on what needs to be done in order to add a new interface with Augeo. Make sure to review Augeo's coding/ commit standards and application design before starting development. Also, this commit which added the Fitbit interface may act as a good reference. This wiki page is a living document and should not be considered complete.

There are 8 major steps:

  1. Create Tab in Profile Popup
  2. Setup Interface Connection
  3. Update Profile Tab
  4. Display Activity
  5. Poll Interface
  6. Update Interface History Screen
  7. Write Test Cases
  8. Update Documentation

1. Create Tab in Profile Popup

The first step is to add a tab in the profile pop up. Initially this tab will provide a link for the user to authenticate with the interface.

src/public/image/{{interface}}

Create a directory to house all of the interface images and logos (e.g., src/public/image/fitbit). Best practice is to place 2 standard size logos, one with normal logo color and one with a gray color (#9E9E9E) to represent an unfocused tab. This directory should also house smaller versions (24x24) of the logos for the actual tab icons.

src/public/javascript/controller/profile-controller.js

In the buildUserInterfaces function add an interface object to the interfaces array using the logo directory information you just created. This will be used by Angular to populate the interface icons on the bottom of the profile popup.

src/public/html/directive/profile-tab/{{interface}}-profile-tab.html

Declare the structure of the profile tab here. Every tab should be similar, so you can use this as a reference.

src/public/javascript/directive/profile-tab/{{interface}}-profile-tab.js

This file declares the profile tab directive for the interface. You will need to specify the html file that you created before this as the templateUrl. Make sure to set the $scope.interfaceIndex to the value that corresponds to the index you have in profile-controller. You can see an example here: fitbit-profile-tab.js. You don't need to implement the submitAuthentication function yet, this will be done in the next step.

src/public/javascript/directive/profile-tab/index.js

In order to see the profile tab added above, add an entry for your interface here.

2. Setup Interface Connection

For this step you will need to research exactly how your interface handles authentication since every interface is different. Most interfaces will require you to register your app, and after doing so will give you codes that will allow you to communicate with their servers. Usually they have a separate site for their API documentation (e.g., https://dev.fitbit.com).

/environment.env

Update this file with any configurations that you received from registering your application. Usually, the interface authentication requires some sort of combination of client ID, client secret, or access tokens.

Note: only blank configurations should be checked into source control. The configurations will be specific to you.

src/public/javascript/directive/profile-tab/{{interface}}-profile-tab.js

This file was created in the first step, however, now we need to implement the submitAuthentication function. This function call should be referenced in the profile tab directive html when the user presses the authenticate button. The implementation of the submitAuthentication function should first gather the interface's authentication data that is defined in the configuration file. There is an Angular Service, InterfaceClientService, configured to dynamically create a request to hit Augeo's API, all you need to do is pass the name of the interface into the getAuthenticationData function. Check out an example here.

src/api/augeo-api.js

Now we need to create an endpoint on the Augeo server to capture the getAuthenticationData request for the interface. In order to do so, we will first need to add an entry to augeo-api.js to route your interface requests to it's own file.

// Route all {{interface}}-api requests to {{interface}}-api.js
app.use('/{{interface}}-api', require('./{{interface}}-api'));

src/api/{{interface}}-api.js

Make sure the name of this file matches the name that you passed into the InterfaceClientService.getAuthenticationData() function in the interface directive. Here, you can create the endpoint for the getAuthenticationData request. In the new endpoint, create a response object that includes the necessary authentication data along with the data defined in the configuration file. Take a look at how the fitbit-api handles it.

src/public/javascript/directive/profile-tab/{{interface}}-profile-tab.js

Now back in the interface directive, perform the request to authorize the user. At a high level, when the user presses the authenticate button they will be routed to the interface's authorization screen. Here is where the user grants Augeo access to their data. After the user agrees, the interface will send a request back to Augeo with user specific authentication tokens/codes. The request back to Augeo is usually referred to as the callback, and is configured during the interface app registration and/or the user authorization request. It is important to use this guideline when specifying the callback url: /{{interface}}-api/callback so we can capture the request in the {{interface}}-api.js file. Again, take a look at fitbit-profile-tab.js for clarification.

src/api/{{interface}}-api.js

At this point, a request is being made by the interface after a user grants Augeo access to their data. In order to capture this request, we need to add another endpoint to this file. Typically, the following steps need to be performed when receiving the interface request.

  • Exchange codes/tokens in request for access tokens
  • Verify access tokens are unique
  • Get user's data from the interface (e.g., name, screen name)
  • Add the user interface information to a new database collection
  • Initiate the process to retrieve interface activity

Use fitbit-api's, github-api's, or twitter-api's callback endpoints as guidelines.

src/interface/{{interface}}-interface.js

To make requests to the interface, we encapsulate the logic into this singleton object. This object is not responsible for any business logic and is used only to make requests to the interface. From the use case above, we would use this object to "Exchange codes/tokens [...] for access tokens" and "Get user's data [...]". Take a look here for an example.

src/interface-service/{{interface}}-interface-service.js

Since most of the time we need to perform business logic on the data retrieved from the interface (e.g., calculate experience, extract data into Augeo objects) we use this object to do so. This singleton object uses a reference of the interface singleton we just previously created to grab the necessary data, and then executes the desired logic. Again, fitbit-interface-service.js is a nice example.

src/model/schema/{{interface}}/user.js

After retrieving data from the interface and extracting the relevant features, we need to store this information. It was become a standard to create a user schema for every interface. This file will house the functions to interact with the interface's user collection. The functions needed for each interface user schema should be similar, therefore the Fitbit's user schema can be used as a template.

Note: make sure attribute names are consistent with other schemas. There is logic that uses the same attribute names across multiple interfaces for abstraction purposes. (e.g., accessToken, augeoUser, name, profileImageUrl)

src/service/{{interface}}-service.js

Use this singleton to perform validations and execute calls to the interface user collection (e.g., checkExistingFitbitId(), addUser()). Implementation examples can be found here.

src/model/schema/augeo/user.js

It has become a standard to maintain a reference of the interface user document in the Augeo user schema. Therefore, we need to add this field to the Augeo user schema, and update some method projections so desired information is retrieved when accessing an Augeo user. Take a look at how Fitbit is referenced in the Augeo user schema.

src/server.js

For collections to be referenced during app execution the schemas need to be required into the application. We have decided to do that in the server.js file. Add the require statements for the above schemas with all the other schemas.

3. Update Profile Tab

At this point, Augeo is authorized on behalf of the user, the user's interface information has been retrieved, and the data is saved into the necessary collections. Now, the user's interface information needs to be displayed in the profile interface tab.

src/public/javascript/service/user-client-service.js

This Angular service is used to grab Augeo user related information from the server. Here, we need to determine if the logged in user has been authenticated with any of the available interfaces. Specifically, we need to add a check for the new interface in the setAuthentications function. If the user has any authentications, Augeo will not display notifications for the user to authenticate with an interface.

src/public/html/directive/profile-tab/{{interface}}-profile-tab.html

Make sure the html is structured to have at least the user's interface profile image (this should be saved after retrieving the user's interface information), check box to use interface's profile image as Augeo's profile image, and the user's interface screen name. See fitbit-profile-tab as an example.

src/api/{{interface}}-api.js

Make sure there is logic in the callback end point to set the user's profile image to the interface profile image if this is the user's first authenticated interface. The fitbit-api callback endpoint has an example of this.

src/service/user-service.js

Add logic to account for new interface's profile image being set as Augeo's profile image. A new case for the switch statement in the setProfileImage function needs to be added. This function is called when the check box in the interface profile tab is checked.

4. Display Activity

Activities are displayed in 2 locations of the app. A rotating list of the most recent activities are shown in the user's dashboard, and then the user has the option to view all the activity in the View Activity screen.

At this point, there is a structure in place to capture the interfaces activity. An initial grab of the interface activity is usually done during the interface api callback end point. Functions will need to be created in the {{interface}}-interface-service.js and {{interface}}-interface.js files to do the actual retrieval.

src/public/javascript/common/{{interface-activity}}.js

This is a javascript object that will contain all the necessary information for the interface activity (e.g., steps, tweet, commit). This is a common object that is used on both the server and client side. Take a look at the day-steps object as an example.

src/model/schema/{{interface}}/{{interface-activity}}.js

Once activities are extracted to the common activity object they can be used to insert the activities into the interface activity schema. There are 2 schemas that depict one activity, the ACTIVITY schema, and the specific interface schema. The ACTIVITY schema contains all the information that is common between all activities (e.g., classification, experience, timestamp). The interface activity schema contains data that is specific to that activity. For example, the FITBIT_DAY_STEP activity has attributes such as screenName and steps. Mongoose has a populate function that allows us to grab ACTIVITIES with their specific activity data from the interface. This works only if the ACTIVITY has a reference to the interface activity.

src/service/{{interface}}-service.js

Since activities are technically stored in 2 separate collections, we use this object to make sure both schemas are in sync when inserting them into the collections. The addDailySteps() function is a good example in the fitbit-service object.

src/public/html/directive/activity/card/activity-card.js

To display the activities on the View Activity screen, we use a common card directive called activity-card. Here, either a custom html object is defined if the card needs customization, or the standard-activity html object. As an example, the Tweet object from Twitter requires a customized card, however the Github Commit and Fitbit DaySteps objects do not.

src/public/html/directive/activity/transition/activity-transition-item.js

The same logic applies here as with the card above. If the interface requires a customized html object to display the activity's information, refer to the html template here. If not, the standard-activity.html file can be used.

src/public/javascript/service/activity-service.js

In order for the activity retrieval mechanism to work, an entry for the new interface activity needs to be added to the Angular Service, activity-service.

5. Poll Interface

At this point, all the user's past interface data should be retrieved. However, there is no mechanism to gather current data. To introduce this behavior, you must create a queue to gather new data from the interface. There is existing common logic, however most of the logic will depend on the interface's API.

src/queue/{{interface}}-event-queue.js

For the queue to work properly with the base-queue there are some required fields that need to be set in the constructor, such as: QUEUE (queue type), isRevolvingQueue (should tasks be added to end of queue once they are complete), and maxExecutionTime (to determine queue wait times). There are also some functions that should be implemented to work well with the base-queue. These functions include addAllUsers (used to initially populate the queue), addTask, finishTask, and prepareTask. Take a look at the Twitter, Github, and Fitbit queues. They are all implemented differently and should provide some guidance.

src/queue-task/{{interface}}/{{interface}}-queue-task.js

Now that we have a queue, we want to create task objects that will actually perform the logic of retrieving new interface data. This object inherits from the abstract-task and should implement the execute function. Again, take a look at Twitter's, Github's, and Fitbit's queue-tasks for examples.

src/service/queue-service.js

In order for the queue to be initialized on application startup, configure this service to addAllUsers to the new interface queue.

6. Update Interface History Screen

The Interface History screen is used to notify Augeo user's of how long the wait times will be for Augeo to gather their different interface data.

src/public/html/interface-history.html

First, update the html for the Interface History screen. The screen should include a description of the interface API with a description and image of the interface activity. There should also be a section for the queue wait times. The interface-history.html file should contain examples for Fitbit, Github, and Twitter.

src/public/javascript/controller/interface-history-controller.js

This Angular Controller makes a call to Augeo's API to gather the queue wait time data and binds it to an element on the UI. Add logic for the new interface here. Implementations for Fitbit, Github, and Twitter should be similar.

src/api/{{interface}}-api.js

Add an endpoint for getQueueWaitTimes that retrieves the wait times from your interface queue object. See the fitbit-api for an example.

7. Write Test Cases

It's important to note that all server side changes will require mocha test cases before any pull requests will be approved and accepted.

test/helper/db-helper.js

This file is used to prepare the test database before test execution. Make sure to add all necessary entries for your new interface.

test/data/{{interface}}-data.js

Add all common test data to this file. (e.g., interface user, hardcoded activities)

test/test-interface/{{interface}}-test-interface.js

Since several tests will be run very quickly, we create this test-interface object to be used in lieu of the regular interface object so we don't surpass interface rate limits. These objects should be configured in the src/interface-service/{{interface}}-interface-service objects to be used when the app config variable for TEST is true. See the fitbit-interface-service as an example.

test/test-case/**

The /test-case directory has the same structure as the /src directory. If any files were created/changed in the /src directory, they should also be created/changed in the /test-case directory and contain the proper test cases.

8. Update Documentation

Make sure the following files/ pages are up to date:

Clone this wiki locally