This repository has been archived by the owner on Feb 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Adam Alexander
committed
Jan 26, 2013
0 parents
commit 8756735
Showing
9 changed files
with
612 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
language: node_js | ||
node_js: | ||
- 0.8 | ||
- 0.6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
Copyright ExactTarget, Inc. All rights reserved. Permission is hereby | ||
granted, free of charge, to any person obtaining a copy of this software and | ||
associated documentation files (the "Software"), to deal in the Software | ||
without restriction, including without limitation the rights to use, copy, | ||
modify, merge, publish, distribute, sublicense, and/or sell copies of the | ||
Software, and to permit persons to whom the Software is furnished to do so, | ||
subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
# fuel | ||
|
||
Client for ExactTarget's Fuel REST APIs | ||
|
||
## Getting Started | ||
Install the module with: `npm install fuel` | ||
|
||
### Call a Fuel REST API | ||
|
||
```javascript | ||
// Load and configure the module | ||
|
||
var fuel = require('fuel').configure({ | ||
authUrl: 'https://auth.exacttargetapis.com/v1/requestToken', | ||
clientId: 'xxxxxxxxxxxxxxxxxxxxxxxx', | ||
clientSecret: 'yyyyyyyyyyyyyyyyyyyyyyyy' | ||
}); | ||
|
||
// OR, if you have a refresh token | ||
|
||
var fuel = require('fuel').configure({ | ||
authUrl: 'https://auth.exacttargetapis.com/v1/requestToken', | ||
clientId: 'xxxxxxxxxxxxxxxxxxxxxxxx', | ||
clientSecret: 'yyyyyyyyyyyyyyyyyyyyyyyy', | ||
refreshToken: 'zzzzzzzzzzzzzzzzzzzzzzzz', | ||
accessType: 'offline' | ||
}); | ||
|
||
// The fuel module will manage your access token behind the | ||
// scenes, renewing it when necessary, maintaining state | ||
// using the refresh token if present | ||
|
||
// Call the API (this example displays your user context) | ||
|
||
fuel({ | ||
url: 'https://www.exacttargetapis.com/platform/v1/tokenContext' | ||
}, function (error, request, body) { | ||
console.log(body); | ||
}); | ||
``` | ||
#### Syntax | ||
|
||
The general format is as follows: | ||
|
||
`fuel(options, callback);` | ||
|
||
The `options` and `callback` parameters are compatible with the `request` module. For details, see the documentation: | ||
|
||
https://github.com/mikeal/request#requestoptions-callback | ||
|
||
### Just manage a Fuel OAuth token | ||
|
||
```javascript | ||
// Load and configure the module | ||
|
||
var token = require('fuel').token.configure({ | ||
authUrl: 'https://auth.exacttargetapis.com/v1/requestToken', | ||
clientId: 'xxxxxxxxxxxxxxxxxxxxxxxx', | ||
clientSecret: 'yyyyyyyyyyyyyyyyyyyyyyyy' | ||
}); | ||
|
||
// OR, if you have a refresh token | ||
|
||
var token = require('fuel').token.configure({ | ||
authUrl: 'https://auth.exacttargetapis.com/v1/requestToken', | ||
clientId: 'xxxxxxxxxxxxxxxxxxxxxxxx', | ||
clientSecret: 'yyyyyyyyyyyyyyyyyyyyyyyy', | ||
refreshToken: 'zzzzzzzzzzzzzzzzzzzzzzzz', | ||
accessType: 'offline' | ||
}); | ||
|
||
// Get a token (this example displays the token data) | ||
|
||
token(function (error, response, tokenData) { | ||
console.log(tokenData); | ||
}); | ||
|
||
// Repeated calls to the token function will return a cached | ||
// token. This module will manage your access token behind the | ||
// scenes, renewing it when necessary, maintaining state | ||
// using the refresh token if present | ||
``` | ||
|
||
#### Syntax | ||
|
||
The general format is as follows: | ||
|
||
`token(callback);` | ||
|
||
The `callback` parameter is compatible with the `request` module. For details, see the documentation: | ||
|
||
https://github.com/mikeal/request#requestoptions-callback | ||
|
||
## Release History | ||
|
||
_This module is semantically versioned: <http://semver.org>_ | ||
|
||
### Version 0.1.0 `2013-01-26` | ||
|
||
* Initial release | ||
|
||
## Contributing | ||
Before writing code, we suggest you [search for issues](https://github.com/ExactTarget/node-mashery/issues?state=open) | ||
or [create a new one](https://github.com/ExactTarget/node-mashery/issues/new) to confirm where your contribution fits into | ||
our roadmap. | ||
|
||
In lieu of a formal style guide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. | ||
Lint and test your code using [grunt](https://github.com/cowboy/grunt). | ||
|
||
## Acknowledgements | ||
|
||
We are grateful to the following maintainers, contributors, and sponsors of the technologies used by this module. | ||
|
||
* [Node.js](http://nodejs.org) | ||
|
||
* [Request](https://github.com/mikeal/request) (De facto HTTP request module for Node) | ||
|
||
* [grunt](https://github.com/cowboy/grunt) (Build tool for JavaScript projects) | ||
|
||
##Authors | ||
|
||
**Adam Alexander** | ||
|
||
+ http://twitter.com/adamalex | ||
+ http://github.com/adamalex | ||
|
||
## Copyright and license | ||
|
||
Copyright (c) 2013 ExactTarget | ||
|
||
Licensed under the MIT License (the "License"); | ||
you may not use this work except in compliance with the License. | ||
You may obtain a copy of the License in the COPYING file. | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
module.exports = function (grunt) { | ||
|
||
// Project configuration. | ||
grunt.initConfig({ | ||
pkg: '<json:package.json>', | ||
test: { | ||
files: ['test/**/*.js'] | ||
}, | ||
lint: { | ||
files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js'] | ||
}, | ||
watch: { | ||
files: '<config:lint.files>', | ||
tasks: 'default' | ||
}, | ||
jshint: { | ||
options: { | ||
curly: false, | ||
strict: false, | ||
eqeqeq: true, | ||
immed: true, | ||
latedef: false, | ||
newcap: true, | ||
noarg: true, | ||
sub: true, | ||
undef: true, | ||
boss: true, | ||
eqnull: true, | ||
node: true | ||
}, | ||
globals: { | ||
exports: true | ||
} | ||
} | ||
}); | ||
|
||
// Default task. | ||
grunt.registerTask('default', 'lint test'); | ||
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/* | ||
* fuel | ||
* https://github.com/ExactTarget/node-fuel | ||
* | ||
* Copyright (c) 2013 ExactTarget | ||
* Licensed under the MIT license. | ||
*/ | ||
|
||
var request = require('request'); | ||
|
||
function fuel(options, callback) { | ||
|
||
fuel.token(options, function (error, response, body) { | ||
if (error) { | ||
callback(error); | ||
} else { | ||
options.json = true; | ||
options.headers = options.headers || {}; | ||
options.headers.authorization = 'Bearer ' + body.accessToken; | ||
|
||
fuel._performRequest(options, callback); | ||
} | ||
}); | ||
} | ||
|
||
fuel.token = function (options, callback) { | ||
|
||
if (!fuel._validateOptions(options, callback)) return; | ||
|
||
var authOptions = { | ||
url: options.authUrl, | ||
method: 'POST', | ||
json: true, | ||
body: { | ||
clientId: options.clientId, | ||
clientSecret: options.clientSecret | ||
} | ||
}; | ||
|
||
if (options.accessType) authOptions.body.accessType = options.accessType; | ||
if (options.refreshToken) authOptions.body.refreshToken = options.refreshToken; | ||
if (options.scope) authOptions.body.scope = options.scope; | ||
|
||
var authOptionsHash = fuel._authOptionsHash(authOptions); | ||
|
||
if (fuel._refreshTokenCache[authOptionsHash]) { | ||
authOptions.body.refreshToken = fuel._refreshTokenCache[authOptionsHash]; | ||
} | ||
|
||
fuel._performTokenRequest(authOptions, function (error, response, body) { | ||
if (error) { | ||
callback(error); | ||
} else { | ||
if (body.refreshToken) { | ||
fuel._refreshTokenCache[authOptionsHash] = body.refreshToken; | ||
} | ||
callback(error, response, body); | ||
} | ||
}); | ||
}; | ||
|
||
fuel.configure = createConfigurationWrapper(fuel); | ||
fuel.token.configure = createConfigurationWrapper(fuel.token); | ||
|
||
fuel._validateOptions = function (options, callback) { | ||
var message = 'not configured - missing authUrl, clientId or clientSecret'; | ||
|
||
if (options.authUrl && options.clientId && options.clientSecret) { | ||
return true; | ||
} else { | ||
callback(new Error(message)); | ||
return false; | ||
} | ||
}; | ||
|
||
fuel._removeAuthOptions = function (options) { | ||
options = deepExtend({}, options); | ||
delete options.clientId; | ||
delete options.clientSecret; | ||
delete options.refreshToken; | ||
delete options.accessType; | ||
delete options.authUrl; | ||
delete options.scope; | ||
return options; | ||
}; | ||
|
||
fuel._performRequest = function (options, callback) { | ||
return request(fuel._removeAuthOptions(options), callback); | ||
}; | ||
|
||
fuel._performTokenRequest = memoize(fuel._performRequest, function (options) { | ||
return fuel._authOptionsHash(options); | ||
}, function (error, response, body) { | ||
if (!error) return (body.expiresIn - 30) * 1000; | ||
}); | ||
|
||
fuel._authOptionsHash = function (options) { | ||
return options.url + options.body.clientId + options.body.scope; | ||
}; | ||
|
||
fuel._refreshTokenCache = {}; | ||
|
||
module.exports = fuel; | ||
|
||
|
||
// Utility | ||
|
||
function deepExtend(dest, source) { | ||
for (var prop in source) { | ||
if (typeof source[prop] === 'object' && source[prop] !== null ) { | ||
dest[prop] = dest[prop] || {}; | ||
deepExtend(dest[prop], source[prop]); | ||
} else { | ||
dest[prop] = source[prop]; | ||
} | ||
} | ||
return dest; | ||
} | ||
|
||
function createConfigurationWrapper(func) { | ||
return function (storedOptions) { | ||
return function (options, callback) { | ||
if (!callback) { | ||
callback = options; | ||
options = {}; | ||
} | ||
return func(deepExtend(storedOptions, options), callback); | ||
}; | ||
}; | ||
} | ||
|
||
function memoize(fn, hasher, invalidator) { | ||
var memo = {}; | ||
var queues = {}; | ||
hasher = hasher || function (x) { | ||
return x; | ||
}; | ||
invalidator = invalidator || function () {}; | ||
var memoized = function () { | ||
var args = Array.prototype.slice.call(arguments); | ||
var callback = args.pop(); | ||
var key = hasher.apply(null, args); | ||
if (key in memo) { | ||
callback.apply(null, memo[key]); | ||
} | ||
else if (key in queues) { | ||
queues[key].push(callback); | ||
} | ||
else { | ||
queues[key] = [callback]; | ||
fn.apply(null, args.concat([function () { | ||
var timeout = invalidator.apply(null, arguments); | ||
if (timeout) { | ||
setTimeout(function () { | ||
delete memo[key]; | ||
}, timeout); | ||
} | ||
memo[key] = arguments; | ||
var q = queues[key]; | ||
delete queues[key]; | ||
for (var i = 0, l = q.length; i < l; i++) { | ||
q[i].apply(null, arguments); | ||
} | ||
}])); | ||
} | ||
}; | ||
memoized.unmemoized = fn; | ||
return memoized; | ||
} |
Oops, something went wrong.