Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

To create a new connector, from your workspace directory, execute the appc generate command. When prompted, select Component for the type of component, Connector for the component to generate, and Empty Connector Project for the connector to generate. Enter com.connector.twitter as the name and directory name for your project.

Code Block
languagebash
$ appc generate
Appcelerator Command-Line Interface, version 0.2.230
Copyright (c) 2014-2015, Appcelerator, Inc. All Rights Reserved.

? What type of component would you like to generate? Arrow Component
? What Arrow component would you like to generate? Arrow Connector
? Which Connector would you like to generate? Empty Connector Project
? What is the connector name? com.connector.twitter
? Which directory to generate into? com.connector.twitter

...

Code Block
titlepackage.json
languagejs
{ ...
  "dependencies" : {
    ...
    "twitter": "~1.2.5"
  }
 ... 
}

...

Code Block
title./conf/example.config.js
languagejs
module.exports = {
  connectors: {
    'com.connector.twitter': {
      account: 'TWITTER_ACCOUNT',
      consumer_key: 'TWITTER_API_KEY',
      consumer_secret: 'TWITTER_API_SECRET',
      access_token_key: 'TWITTER_ACCESS_TOKEN',
      access_token_secret: 'TWITTER_ACCESS_TOKEN_SECRET'    
    }
  }
}

...

The app.js file contains code that initializes the connector when it is used as a server for testing. You can hook into the lifecycle events of the server. The boilerplate file contains logic that is used to create a model. Update the code to let the application create a connector and access the tweet's text response parameter as the status parameter. Later on, you will add logic to automatically create a model when a connector is installed on an application.

Code Block
titleapp.js
languagejs
var Arrow = require('arrow'),
    server = new Arrow();

server.addModel(Arrow.Model.extend('tweet', {
  fields: {
    status: { type: String, name: 'text' }
  },
  connector: 'com.connector.twitter'
}));

server.start();

...

Let's get started. First, run the project and look at the log output:

Code Block
languagebash
appc run
...
This connector does not do much of anything at the moment.
Why don't you take a look at the "capabilities" array in:
/Users/jdoe/workspace/com.connector.twitternew/lib/index.js

...

  1. Add the defaultConfig field to the parameter object passed to the extend() method. The defaultConfig field tells API Builder to copy the example configuration file to the project's conf directory when the user adds the connector to the project.
  2. Uncomment the line containing Capabilities.ConnectsToADataSource.
  3. Add the postCreate() function to attach a helper function to the connector instance that can be used by other connector methods.

    Code Block
    title./lib/index.js
    languagejs
    collapsetrue
    ...
    exports.create = function (Arrow) {
      ...
      return Connector.extend({
        defaultConfig: require('fs').readFileSync(__dirname + '/../conf/example.config.js', 'utf8'),
        ...
        capabilities: [
          Capabilities.ConnectsToADataSource,
    
          // TODO: Each of these capabilities is optional; add the ones you want, and delete the rest.
          // (Hint: I've found it to be easiest to add these one at a time, running `appc run` for guidance.)
          //Capabilities.ValidatesConfiguration,
          //Capabilities.ContainsModels,
          //Capabilities.GeneratesModels,
          //Capabilities.CanCreate,
          //Capabilities.CanRetrieve,
          //Capabilities.CanUpdate,
          //Capabilities.CanDelete,
          //Capabilities.AuthenticatesThroughConnector
        ],
        postCreate: function() {
          this.createInstance = function (Model, data) {
            var model = Model.instance(data, true);
            model.setPrimaryKey(data.id_str);
            return model;
          };
        }
      });
    };
  4. Save the file. You will see the following log output:

    Code Block
    languagejs
    The "ConnectsToADataSource" capability has been enabled, so we need to make a couple of changes:
     - Created `lib/lifecycle/connect.js` (contains 2 TODOs)
     - Created `lib/lifecycle/disconnect.js` (contains 2 TODOs)
    
    Please go take a look at the TODOs in these new files, then do an `appc run` or `npm test` to try out the new capabilities.

...

  1. API Builder generates some new files in the lib folder. You will need to implement some logic to connect to and disconnect from the datasource.

Add connect logic

Open ./lib/lifecycle/connect.js and replace the contents of the file with the following. The connect.js file contains logic to implement the connector's connect() method, which authorizes the connector to talk to the Twitter APIs.

...

  1. Open the ./lib/index.js file, uncomment the line containing Capabilities.CanRetrieve, and save the file. The log output tells us to update four new files that API Builder just generated.

    Code Block
    languagejs
    The "CanRetrieve" capability has been enabled, so we need to make a couple of changes:
     - Created `lib/methods/distinct.js` (contains 3 TODOs)
     - Created `lib/methods/findAll.js` (contains 3 TODOs)
     - Created `lib/methods/findById.js` (contains 4 TODOs)
     - Created `lib/methods/query.js` (contains 5 TODOs)
    (Hint: If you only want to support some of these methods, feel free to delete the others.)
    Please go take a look at the TODOs in these new files, then do an `appc run` or `npm test` to try out the new capabilities.
  2. Remove the distinct.js and query.js files. We will not be implementing these for the example.

    Code Block
    title./lib/lifecycle/connect.js
    languagejs
    var Twitter = require('twitter');
    
    exports.connect = function (next) {
      // Initialize the client
      // Use this.config to get values from the configuration file
      this.client = new Twitter({
        consumer_key: this.config.consumer_key,
        consumer_secret: this.config.consumer_secret,
        access_token_key: this.config.access_token_key,
        access_token_secret: this.config.access_token_secret
      });    
      next();
    };
  3. Open ./lib/methods/findAll.js and replace the contents of the file with the following. The findAll.js file implements the connector's findAll() method, which retrieves all tweets from the user's Twitter account, or at least, as many tweets as Twitter will allow us to fetch.

    Code Block
    title./lib/methods/findAll.js
    languagejs
    var Arrow = require('arrow'),
      Collection = Arrow.Collection,
      ORMError = Arrow.ORMError;
    
    exports.findAll = function findAll(Model, callback) {
        var params = {screen_name: this.config.account};
        // this Twitter API only returns the last twenty tweets
        this.client.get('statuses/user_timeline', params, function(error, tweets, response) {
        if (!error) {
          var results = [];
          for (var i = 0; i < tweets.length; i++) {
            results.push(Model.connector.createInstance(Model, tweets[i]));
          }
          callback(null, new Collection(Model, results));
        } else {
          this.logger.error(error);
          callback(new ORMError('ERROR: Could not fetch tweets!'));
        }
      });
    };
  4. Open ./lib/methods/findById.js and replace the contents of the file with the following. The findById.js file implements the connector's findById() method, which retrieves one specific tweet, identified by its ID, from the datasource.

    Code Block
    title./lib/methods/findById.js
    languagejs
    var Arrow = require('arrow'),
      ORMError = Arrow.ORMError;
    
    exports.findById = function (Model, id, callback) {
      this.client.get('statuses/show/' + id, {}, function(error, tweet, response) {
        if (!error) {
          callback(null, Model.connector.createInstance(Model, tweet));
        } else {
          this.logger.error(error);
          callback(new ORMError('ERROR: Could not fetch tweet!'));
        }
      });
    };

...

  1. Open the ./lib/index.js file, uncomment the line containing Capabilities.CanCreate and save the file. You will see the following console output:

    Code Block
    languagejs
    The "CanCreate" capability has been enabled, so we need to make a couple of changes:
     - Created `lib/methods/create.js` (contains 3 TODOs)
    Please go take a look at the TODOs in these new files, then do an `appc run` or `npm test` to try out the new capabilities.
  2. Open ./lib/methods/create.js and replace the contents of the file with the following. The create.js file implements the connector's create() method, which allows the connector to post a tweet on the user's Twitter feed.

    Code Block
    title./lib/methods/create.js
    languagejs
    var Arrow = require('arrow'),
      ORMError = Arrow.ORMError;
    
    exports.create = function (Model, values, callback) {
      var params = {'status': values.text};
      this.client.post('statuses/update', params, function(error, tweet, response) {
        if (!error) {
          callback(null, Model.connector.createInstance(Model, tweet));
        } else {
          callback(new ORMError('ERROR: Unable to create tweet!'));
        }
      });
    };

...

  1. Open the ./lib/index.js file, uncomment the line containing Capabilities.CanDelete and save the file. You will see the following console output: 

    Code Block
    languagejs
    The "CanDelete" capability has been enabled, so we need to make a couple of changes:
     - Created `lib/methods/delete.js` (contains 4 TODOs)
     - Created `lib/methods/deleteAll.js` (contains 3 TODOs)
    (Hint: If you only want to support some of these methods, feel free to delete the others.)
    Please go take a look at the TODOs in these new files, then do an `appc run` or `npm test` to try out the new capabilities.
  2. Remove the lib/methods/deleteAll.js.

  3. Open ./lib/methods/delete.js and replace the contents of the file with the following. The delete.js file implements the connector's delete() method, which allows the connector to delete a specific tweet, specified by its ID, from the user's Twitter feed.

    Code Block
    title./lib/methods/delete.js
    languagejs
    var Arrow = require('arrow'),
      ORMError = Arrow.ORMError;
    
    exports['delete'] = function (Model, instance, callback) {
      this.client.post('statuses/destroy/' + instance.id + '.json', {}, function(error, tweet, response) {
        if (!error) {
          callback(null, Model.connector.createInstance(Model, tweet));
        } else {
          callback(new ORMError('ERROR: Could not delete tweet!'));
        }
      });
    };

...

  1. Remove the server.addModel method from the app.js file.
  2. Open the ./lib/index.js file, uncomment the line containing Capabilities.ContainModel and save the file. You will see the following console output: 

    Code Block
    languagejs
    The "ContainsModels" capability has been enabled, so we need to make a couple of changes:
     - Created `models/yourModel.js` (contains 2 TODOs)
  3. Rename the yourModel.js file to tweet.js.
  4. Open ./models/tweet.js and replace the contents of the file with the following: 

    Code Block
    title./models/tweet.js
    languagejs
    var Arrow = require('arrow');
     
    var Tweet = Arrow.Model.extend('tweet', {
      fields: {
        status: { type: String, name: 'text' }
      },
      connector: 'com.connector.twitter'
    });
     
    module.exports = Tweet;
  5. Reload the Admin console and retest adding and removing a model.

Publish the connector

To publish the connector, execute the following command from the project directory:

Code Block
languagebash
appc publish

By default, the access level for the connector is set to private, so only the creator can access the connector. To share the connector with other people or publicly, specify a different access level with the appc access command and add people or organizations to your component using the appc user and appc org commands.

...