22 changed files with 541 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||
|
facebook.* |
||||
|
node_modules |
||||
@ -0,0 +1,2 @@ |
|||||
|
node_modules |
||||
|
logs |
||||
@ -0,0 +1 @@ |
|||||
|
# appc.facebook |
||||
@ -0,0 +1,17 @@ |
|||||
|
/** |
||||
|
* NOTE: This file is simply for testing this connector and will not |
||||
|
* be used or packaged with the actual connector when published. |
||||
|
*/ |
||||
|
var Arrow = require('arrow'), |
||||
|
server = new Arrow(); |
||||
|
|
||||
|
// TODO: Define a model that you can use when you run the connector locally for testing.
|
||||
|
server.addModel(Arrow.Model.extend('tbray', { |
||||
|
fields: { |
||||
|
// TODO: Add fields to it.
|
||||
|
title: {type: String} |
||||
|
}, |
||||
|
connector: 'appc.facebook' |
||||
|
})); |
||||
|
|
||||
|
server.start(); |
||||
@ -0,0 +1,6 @@ |
|||||
|
{ |
||||
|
"type":"connector", |
||||
|
"group":"arrow", |
||||
|
"dependencies": { |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
local.js |
||||
|
*.local.js |
||||
|
deploy.json |
||||
@ -0,0 +1,3 @@ |
|||||
|
local.js |
||||
|
*.local.js |
||||
|
deploy.json |
||||
@ -0,0 +1,90 @@ |
|||||
|
/** |
||||
|
* this is your configuration file defaults. |
||||
|
* |
||||
|
* You can create additional configuration files to that the server will load based on your |
||||
|
* environment. For example, if you want to have specific settings for production which are different |
||||
|
* than your local development environment, you can create a production.js and a local.js. Any changes |
||||
|
* in those files will overwrite changes to this file (a object merge is performed). By default, your |
||||
|
* local.js file will not be commited to git or the registry. |
||||
|
* |
||||
|
* This is a JavaScript file (instead of JSON) so you can also perform logic in this file if needed. |
||||
|
*/ |
||||
|
module.exports = { |
||||
|
// these are your generated API keys. They were generated uniquely when you created this project.
|
||||
|
// DO NOT SHARE these keys with other projects and be careful with these keys since they control
|
||||
|
// access to your API using the default configuration. if you don't want two different keys for
|
||||
|
// production and test (not recommended), use the key 'apikey'. To simulate running in production,
|
||||
|
// set the environment variable NODE_ENV to production before running such as:
|
||||
|
//
|
||||
|
// NODE_ENV=production appc run
|
||||
|
//
|
||||
|
// production key, this is the key that will be required when you are running in production
|
||||
|
apikey_production: 'xhA+NICf8qJy9r1+zKylI3ZiEegjl25J', |
||||
|
// development key, this is the key that will be required when you are testing non-production (such as locally)
|
||||
|
apikey_development: 'a5EJlozJdCVIrFkgB87dOyzAW2huRN+G', |
||||
|
// preproduction key, this is the key that will be required when you are testing non-production (such as locally)
|
||||
|
apikey_preproduction: 'FG+50Zn5qdNGyx76nUC3oHO0BCn8YtXo', |
||||
|
|
||||
|
// by default the authentication strategy is 'basic' which will use HTTP Basic Authorization where the
|
||||
|
// usename is the key and the password is blank. the other option is 'apikey' where the value of the
|
||||
|
// APIKey header is the value of the key. you can also set this to 'plugin' and define the key 'APIKeyAuthPlugin'
|
||||
|
// which points to a file or a module that implements the authentication strategy
|
||||
|
APIKeyAuthType: 'basic', |
||||
|
|
||||
|
// The number of milliseconds before timing out a request to the server.
|
||||
|
timeout: 120000, |
||||
|
|
||||
|
// logging configuration
|
||||
|
logLevel: 'debug', // Log level of the main logger.
|
||||
|
logging: { |
||||
|
// location of the logs if enabled
|
||||
|
logdir: './logs', |
||||
|
// turn on transaction logs
|
||||
|
transactionLogEnabled: true |
||||
|
}, |
||||
|
|
||||
|
// prefix to use for apis
|
||||
|
apiPrefix: '/api', |
||||
|
|
||||
|
// control the settings for the admin website
|
||||
|
admin: { |
||||
|
// control whether the admin website is available
|
||||
|
enabled: true, |
||||
|
// the prefix to the admin website
|
||||
|
prefix: '/arrow', |
||||
|
// the prefix for the public apidocs website
|
||||
|
apiDocPrefix: '/apidoc', |
||||
|
// if you set disableAuth, in production only your API docs will show up
|
||||
|
disableAuth: false, |
||||
|
// if you set disableAPIDoc, your API docs will not show up (regardless of disableAuth)
|
||||
|
disableAPIDoc: false, |
||||
|
// if you set disableDefault404, Arrow will not register a default 404 handler
|
||||
|
disableDefault404: false, |
||||
|
// set to true to allow the admin website to be accessed in production. however, you will still need a
|
||||
|
// login unless disableAuth is false. if you set this to false, the admin website will not be enabled
|
||||
|
// when in production (still respects enabled above)
|
||||
|
enableAdminInProduction: true, |
||||
|
// set the email addresses you want to be able to log in to the admin website
|
||||
|
validEmails: ["nmasse@axway.com"], |
||||
|
// set the organization ids you want to be able to log in to the admin website
|
||||
|
validOrgs: [100094705] |
||||
|
}, |
||||
|
|
||||
|
// you can generally leave this as-is since it is generated for each new project you created.
|
||||
|
session: { |
||||
|
encryptionAlgorithm: 'aes256', |
||||
|
encryptionKey: '+aeBAIMvknm4yVebHQDZh9k2e6szb0s9DNIcWAetoEk=', |
||||
|
signatureAlgorithm: 'sha512-drop256', |
||||
|
signatureKey: 'OXTlgTMf+FT7TiByTtHtz9VQ/7CgPIu3Cxw4Ld/vWDCLWhwwxAZbPWH0Ytuwjs/cY6Ot8kEkQjPKMtnufKaQVg==', |
||||
|
secret: 'kjOmippGFVDL38Vyifc9Vow7Oq/wymcq', // should be a large unguessable string
|
||||
|
duration: 86400000, // how long the session will stay valid in ms
|
||||
|
activeDuration: 300000 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
|
||||
|
}, |
||||
|
|
||||
|
// if you want signed cookies, you can set this value. if you don't want signed cookies, remove or make null
|
||||
|
cookieSecret: 'zVciXXTRsEixUGUuVLteOOlhWlH2ppBx', |
||||
|
|
||||
|
// your connector configuration goes here
|
||||
|
connectors: { |
||||
|
} |
||||
|
}; |
||||
@ -0,0 +1,7 @@ |
|||||
|
module.exports = { |
||||
|
connectors: { |
||||
|
'appc.facebook': { |
||||
|
'accessToken': '<your facebook access_token here>' |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
@ -0,0 +1,23 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
redirect_uri="http://localhost:8888/" |
||||
|
|
||||
|
if [ $# -ne 2 ]; then |
||||
|
echo "Usage: $0 <facebook appId> <facebook appSecret> " |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
open "https://www.facebook.com/v2.8/dialog/oauth?client_id=$1&redirect_uri=$redirect_uri&scope=user_likes,manage_pages,user_posts" |
||||
|
|
||||
|
nc -l 8888 >facebook.authcode <<EOF |
||||
|
HTTP 1.0 200 OK |
||||
|
Content-Type: text/html |
||||
|
|
||||
|
<html><body><h1>OK</h1></body></html> |
||||
|
EOF |
||||
|
|
||||
|
code=$(sed -E -e 's|GET /[?]code=([^ ]+) HTTP/1[.]1|\1|' -e t -e d facebook.authcode |tr -d "\r\n") |
||||
|
|
||||
|
curl "https://graph.facebook.com/v2.8/oauth/access_token?client_id=$1&redirect_uri=$redirect_uri&client_secret=$2&code=$code" -o facebook.access_token |
||||
|
|
||||
|
jq -r ".access_token" facebook.access_token |
||||
@ -0,0 +1,17 @@ |
|||||
|
exports = module.exports = require('./lib'); |
||||
|
|
||||
|
// if this is being run directly, then attempt to load it as
|
||||
|
// server instead of module
|
||||
|
if (module.id === '.') { |
||||
|
var fs = require('fs'), |
||||
|
path = require('path'), |
||||
|
appjs = path.join(__dirname, 'app.js'); |
||||
|
if (fs.existsSync(appjs)) { |
||||
|
try { |
||||
|
require(appjs); |
||||
|
} |
||||
|
catch (E) { |
||||
|
console.error(E); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
/* |
||||
|
Welcome to your new connector! |
||||
|
TODO: First things first, look at the "capabilities" array TODOs down below. |
||||
|
*/ |
||||
|
var _ = require('lodash'), |
||||
|
semver = require('semver'); |
||||
|
|
||||
|
/** |
||||
|
* Creates your connector for Arrow. |
||||
|
*/ |
||||
|
exports.create = function (Arrow) { |
||||
|
var min = '1.7.0'; |
||||
|
if (semver.lt(Arrow.Version || '0.0.1', min)) { |
||||
|
throw new Error('This connector requires at least version ' + min + ' of Arrow; please run `appc use latest`.'); |
||||
|
} |
||||
|
var Connector = Arrow.Connector, |
||||
|
Capabilities = Connector.Capabilities; |
||||
|
|
||||
|
return Connector.extend({ |
||||
|
filename: module.filename, |
||||
|
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
|
||||
|
] |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,28 @@ |
|||||
|
// TODO: Reference the module to connect to your data store.
|
||||
|
var FB = require('fb'); |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Connects to your data store; this connection can later be used by your connector's methods. |
||||
|
* @param next |
||||
|
*/ |
||||
|
exports.connect = function (next) { |
||||
|
// Note: Our current context, aka "this", is a reference to your connector.
|
||||
|
var self = this; |
||||
|
|
||||
|
connection = FB.extend({ "appId": this.config.appId, "appSecret": this.config.appSecret }); |
||||
|
self.logger.debug("Trying to validate our Facebook access_token by calling the /me endpoint..."); |
||||
|
FB.api('/me', { |
||||
|
fields: 'name,id', |
||||
|
access_token: this.config.accessToken |
||||
|
}, function (result) { |
||||
|
if(!result || result.error) { |
||||
|
self.logger.error("Facebook error: " + (result != null ? result.error : "")); |
||||
|
next('Got an error while validating the access_token on /me: ' + (result != null ? result.error : "")); |
||||
|
} else { |
||||
|
self.logger.info("Successfully validated our access_token ! Facebook response = " + JSON.stringify(result)); |
||||
|
next(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
}; |
||||
@ -0,0 +1,7 @@ |
|||||
|
/** |
||||
|
* Disconnects from your data store. |
||||
|
* @param next |
||||
|
*/ |
||||
|
exports.disconnect = function (next) { |
||||
|
next(); // allways succeed
|
||||
|
}; |
||||
@ -0,0 +1,17 @@ |
|||||
|
var Arrow = require('arrow'); |
||||
|
|
||||
|
/** |
||||
|
* Fetches metadata describing your connector's proper configuration. |
||||
|
* @param next |
||||
|
*/ |
||||
|
exports.fetchMetadata = function fetchMetadata(next) { |
||||
|
next(null, { |
||||
|
fields: [ |
||||
|
Arrow.Metadata.Text({ |
||||
|
name: 'accessToken', |
||||
|
description: 'A Facebook access_token, that you can get with the "get-oauth-token.sh".', |
||||
|
required: true |
||||
|
}) |
||||
|
] |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,21 @@ |
|||||
|
// TODO: Reference the module to connect to your data store.
|
||||
|
var yourDataStore = /*require('your-data-store')*/{}; |
||||
|
|
||||
|
/** |
||||
|
* Performs a query and returns a distinct result set based on the field(s). |
||||
|
* @param {Arrow.Model} Model Model class to check. |
||||
|
* @param {String} field Comma-separated list of fields. |
||||
|
* @param {ArrowQueryOptions} [options] Query options. |
||||
|
* @param {Function} callback Callback passed an Error object (or null if successful) and the distinct values array. |
||||
|
*/ |
||||
|
exports.distinct = function distinct(Model, field, options, callback) { |
||||
|
// TODO: Find the distinct results for this Model from your data store.
|
||||
|
yourDataStore.distinct(field, options.where, function (err, results) { |
||||
|
if (err) { |
||||
|
return callback(err); |
||||
|
} |
||||
|
|
||||
|
// TODO: Return just the distinct values array.
|
||||
|
callback(null, results); |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,11 @@ |
|||||
|
var FB = require('fb'), |
||||
|
Arrow = require('arrow'); |
||||
|
|
||||
|
/** |
||||
|
* Finds all model instances. A maximum of 1000 models are returned. |
||||
|
* @param {Arrow.Model} Model The model class being updated. |
||||
|
* @param {Function} callback Callback passed an Error object (or null if successful) and the models. |
||||
|
*/ |
||||
|
exports.findAll = function findAll(Model, callback) { |
||||
|
Model.query({}, callback); |
||||
|
}; |
||||
@ -0,0 +1,29 @@ |
|||||
|
var FB = require('fb'); |
||||
|
|
||||
|
/** |
||||
|
* Finds a model instance using the primary key. |
||||
|
* @param {Arrow.Model} Model The model class being updated. |
||||
|
* @param {String} id ID of the model to find. |
||||
|
* @param {Function} callback Callback passed an Error object (or null if successful) and the found model. |
||||
|
*/ |
||||
|
exports.findByID = function (Model, id, callback) { |
||||
|
self.logger.debug("--> findByID(" + Model.name + ", " + id + ")"); |
||||
|
|
||||
|
|
||||
|
// TODO: Find the instance with the provided id.
|
||||
|
yourDataStore.findByID(id, function (err, result) { |
||||
|
if (err) { |
||||
|
return callback(err); |
||||
|
} |
||||
|
|
||||
|
// TODO: If nothing was found by this request:
|
||||
|
if (!result) { |
||||
|
return callback(); |
||||
|
} |
||||
|
|
||||
|
// TODO: Otherwise, if all went well:
|
||||
|
var instance = Model.instance(result, true); |
||||
|
instance.setPrimaryKey(String(result.id)); // Note: the primary key can be a number, too.
|
||||
|
callback(null, instance); |
||||
|
}); |
||||
|
}; |
||||
@ -0,0 +1,74 @@ |
|||||
|
var FB = require('fb'), |
||||
|
Arrow = require('arrow'), |
||||
|
_ = require('lodash'); |
||||
|
|
||||
|
/** |
||||
|
* Queries for particular model records. |
||||
|
* @param {Arrow.Model} Model The model class being updated. |
||||
|
* @param {ArrowQueryOptions} options Query options. |
||||
|
* @param {Function} callback Callback passed an Error object (or null if successful) and the model records. |
||||
|
* @throws {Error} Failed to parse query options. |
||||
|
*/ |
||||
|
exports.query = function (Model, options, callback) { |
||||
|
var self = this; |
||||
|
var query = { |
||||
|
/** |
||||
|
* A dictionary of the fields to include, such as { first_name: 1 } |
||||
|
*/ |
||||
|
//sel: Model.translateKeysForPayload(options.sel),
|
||||
|
/** |
||||
|
* A dictionary of the fields to exclude, such as { last_name: 0 } |
||||
|
*/ |
||||
|
//unsel: Model.translateKeysForPayload(options.unsel),
|
||||
|
/** |
||||
|
* A dictionary of fields to search by, ignoring keys that aren't specified in our model, and including "id", |
||||
|
* such as { first_name: 'Daws%', last_name: 'Toth' } |
||||
|
*/ |
||||
|
//where: _.pick(Model.translateKeysForPayload(options.where), Model.payloadKeys().concat(['id'])),
|
||||
|
/** |
||||
|
* A dictionary of fields to order by, with a direction, such as { first_name: 1, last_name: -1 } where 1 is |
||||
|
* ascending and -1 is descending. |
||||
|
*/ |
||||
|
//order: Model.translateKeysForPayload(options.order),
|
||||
|
/** |
||||
|
* A number indicating how far to skip through the results before returning them, such as 0 or 100, as well |
||||
|
* as a limit on how many to return, such as 10 or 20. Alternatively, use options.page and options.per_page. |
||||
|
* Arrow translates these for you. |
||||
|
* |
||||
|
* For example, a skip of 50 and a limit of 10 is equivalent to a page of 5 and a per_page of 10. |
||||
|
*/ |
||||
|
//skip: options.skip,
|
||||
|
//limit: options.limit,
|
||||
|
access_token: this.config.accessToken, |
||||
|
}; |
||||
|
|
||||
|
self.logger.debug("--> query(" + Model.name + ", " + JSON.stringify(options) + ")"); |
||||
|
self.logger.info("Fetching Facebook posts and likes for page '" + Model.private.name + "'..."); |
||||
|
FB.api("/" + Model.private.pageId + "/posts?fields=likes{id,name},id,message&limit=" + options.limit + "&offset=" + options.skip, query, function (result) { |
||||
|
if(!result || result.error) { |
||||
|
self.logger.error("Facebook error: " + (result != null ? result.error : "")); |
||||
|
callback('Got an error while Trying to find all likes of ' + Model.name + ": " + (result != null ? result.error : "")); |
||||
|
} else { |
||||
|
self.logger.info("Successfully found all likes for page '" + Model.private.name + "': " + JSON.stringify(result)); |
||||
|
var results = result.data; |
||||
|
var array = []; |
||||
|
for (var p = 0; p < results.length; p++) { |
||||
|
var post = results[p]; |
||||
|
var postId = post.id; |
||||
|
var postName = post.message; |
||||
|
|
||||
|
var likes = post.likes.data; |
||||
|
for (var l = 0; l < likes.length; l++) { |
||||
|
var like = likes[l]; |
||||
|
var instance = Model.instance({postId: postId, postName: postName, userId: like.id, userName: like.name}, true); |
||||
|
//instance.setPrimaryKey(String(results[c].id));
|
||||
|
array.push(instance); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Turn the array of instances in to a collection, and return it.
|
||||
|
callback(null, new Arrow.Collection(Model, array)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
}; |
||||
@ -0,0 +1,40 @@ |
|||||
|
var Arrow = require('arrow'), |
||||
|
_ = require('lodash'); |
||||
|
|
||||
|
/** |
||||
|
* Creates models from your schema (see "fetchSchema" for more information on the schema). |
||||
|
*/ |
||||
|
exports.createModelsFromSchema = function () { |
||||
|
var self = this, |
||||
|
models = {}; |
||||
|
|
||||
|
// TODO: Iterate through the models in your schema.
|
||||
|
Object.keys(self.metadata.schema.objects).forEach(function (modelName) { |
||||
|
var object = self.metadata.schema.objects[modelName], |
||||
|
fields = {}, private = null; |
||||
|
Object.keys(object).forEach(function (fieldName) { |
||||
|
|
||||
|
var field = object[fieldName]; |
||||
|
if (fieldName !== 'id') { |
||||
|
// TODO: Define the Arrow field definitions based on the schema.
|
||||
|
fields[fieldName] = { |
||||
|
type: field.type || String, |
||||
|
required: field.required |
||||
|
}; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
models[self.name + '/' + modelName] = Arrow.Model.extend(self.name + '/' + modelName, { |
||||
|
name: self.name + '/' + modelName, |
||||
|
autogen: !!self.config.modelAutogen, // Controls if APIs are automatically created for this model.
|
||||
|
fields: fields, |
||||
|
connector: self, |
||||
|
logger: self.logger, |
||||
|
generated: true, |
||||
|
// cache Facebook attrs in the model to save a roundtrip
|
||||
|
"private": self.metadata.schema.private[modelName] |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
self.models = _.defaults(self.models || {}, models); |
||||
|
}; |
||||
@ -0,0 +1,71 @@ |
|||||
|
var FB = require('fb'); |
||||
|
|
||||
|
/** |
||||
|
* Fetches the schema for your connector. |
||||
|
* |
||||
|
* For example, your schema could look something like this: |
||||
|
* { |
||||
|
* objects: { |
||||
|
* person: { |
||||
|
* first_name: { |
||||
|
* type: 'string', |
||||
|
* required: true |
||||
|
* }, |
||||
|
* last_name: { |
||||
|
* type: 'string', |
||||
|
* required: false |
||||
|
* }, |
||||
|
* age: { |
||||
|
* type: 'number', |
||||
|
* required: false |
||||
|
* } |
||||
|
* } |
||||
|
* } |
||||
|
* } |
||||
|
* |
||||
|
* @param next |
||||
|
* @returns {*} |
||||
|
*/ |
||||
|
exports.fetchSchema = function (next) { |
||||
|
var self = this; |
||||
|
// If we already have the schema, just return it.
|
||||
|
if (this.metadata.schema) { |
||||
|
return next(null, this.metadata.schema); |
||||
|
} |
||||
|
|
||||
|
var action = "get the list of all Facebook pages managed by the current user"; |
||||
|
self.logger.debug("Trying to " + action + "..."); |
||||
|
FB.api('/me/accounts', { |
||||
|
access_token: this.config.accessToken |
||||
|
}, function (result) { |
||||
|
if(!result || result.error) { |
||||
|
self.logger.error("Could not " + action + ": " + (result != null ? result.error : "")); |
||||
|
next('Got an error while trying to ' + action + ': ' + (result != null ? result.error : "")); |
||||
|
} else { |
||||
|
var n = result.data.length; |
||||
|
self.logger.info("Successfully retrieved the list of all Facebook pages managed by the current user (" + n + " pages) !"); |
||||
|
self.logger.debug("Computing the schema from the facebook response..."); |
||||
|
var objects = {}; |
||||
|
var schemaCache = {}; |
||||
|
for (var i = 0; i < n; i++) { |
||||
|
var facebookPage = result.data[i]; |
||||
|
var pageName = "LikesOf" + camelize(facebookPage.name); |
||||
|
objects[pageName] = { "postId": { "type": "string", "required": "true" }, |
||||
|
"postName": { "type": "string", "required": "false" }, |
||||
|
"userId": { "type": "string", "required": "true" }, |
||||
|
"userName": { "type": "string", "required": "false" } }; |
||||
|
schemaCache[pageName] = { accessToken: facebookPage.access_token, pageId: facebookPage.id, name: facebookPage.name }; |
||||
|
self.logger.debug("New Schema: " + pageName); |
||||
|
} |
||||
|
var schema = { 'objects': objects, 'private': schemaCache }; |
||||
|
next(null, schema); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
function camelize(str) { |
||||
|
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) { |
||||
|
return letter.toUpperCase(); |
||||
|
}).replace(/\s+/g, ''); |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
{ |
||||
|
"name": "appc.facebook", |
||||
|
"description": "", |
||||
|
"version": "1.0.0", |
||||
|
"author": "Nicolas MASSE", |
||||
|
"license": "", |
||||
|
"framework": "none", |
||||
|
"keywords": [ |
||||
|
"appcelerator", |
||||
|
"arrow" |
||||
|
], |
||||
|
"repository": {}, |
||||
|
"private": true, |
||||
|
"dependencies": { |
||||
|
"async": "^1.5.0", |
||||
|
"lodash": "^3.10.1", |
||||
|
"pkginfo": "^0.3.1", |
||||
|
"semver": "^5.0.3", |
||||
|
"fb": "^*" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"grunt": "^0.4.5", |
||||
|
"grunt-appc-js": "^1.0.19", |
||||
|
"grunt-contrib-clean": "^0.7.0", |
||||
|
"grunt-mocha-istanbul": "^3.0.1", |
||||
|
"istanbul": "^0.4.1", |
||||
|
"mocha": "^2.3.4", |
||||
|
"should": "^8.0.2", |
||||
|
"arrow": "^*" |
||||
|
}, |
||||
|
"scripts": { |
||||
|
"start": "appc run", |
||||
|
"test": "grunt" |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue