aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig11
-rw-r--r--.npmignore3
-rw-r--r--Gruntfile.js20
-rw-r--r--LICENSE.md25
-rw-r--r--README.md164
-rw-r--r--examples/oauth.js16
-rw-r--r--examples/sample_app.js14
-rw-r--r--lib/Agency.js45
-rw-r--r--lib/Device.js12
-rw-r--r--lib/active911.js63
-rw-r--r--lib/clients/refresh.js118
-rw-r--r--lib/util.js3
-rw-r--r--package.json67
-rw-r--r--tests/active911.js128
-rw-r--r--tests/replies/devices.json0
15 files changed, 275 insertions, 414 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..5ed6bbc
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+max_line_length = 80
+
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..a7abe3b
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,3 @@
+tests
+examples
+
diff --git a/Gruntfile.js b/Gruntfile.js
deleted file mode 100644
index 34f8dc8..0000000
--- a/Gruntfile.js
+++ /dev/null
@@ -1,20 +0,0 @@
-module.exports = function (grunt) {
-
- grunt.initConfig({
- jshint: {
- all: ['lib/*.js', 'examples/*.js']
- },
- mochaTest: {
- test: {
- options: {
- reporter: 'spec'
- },
- src: ['tests/*.js']
- }
- }
- });
-
- grunt.loadNpmTasks('grunt-contrib-jshint');
- grunt.loadNpmTasks('grunt-mocha-test');
- grunt.registerTask('default', ['jshint', 'mochaTest']);
-};
diff --git a/LICENSE.md b/LICENSE.md
index a77e2bf..29a4317 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,13 +1,18 @@
-Copyright 2013 Ben Burwell
+The MIT License (MIT) Copyright (c) 2013-2015 Ben Burwell
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
+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:
- http://www.apache.org/licenses/LICENSE-2.0
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
- 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.
+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.
diff --git a/README.md b/README.md
index 9f3048e..6ec5caa 100644
--- a/README.md
+++ b/README.md
@@ -1,137 +1,57 @@
Active911 for Node.js
=====================
-*Note:* This package was built for an older version of the Active911 API. It will be updated for the new version in the coming weeks.
-
-by Ben Burwell <hi@benburwell.com>
+by Ben Burwell <ben@benburwell.com>
Installation
------------
-Installation is simple: `npm install active911`.
-
-You can use it in your project by adding `active911` to your dependencies and running `npm install`. Sample ping:
-
- var active911 = require('active911')('YOUR_APP_KEY', 'YOUR_API_KEY');
-
- active911.ping(function (err) {
- if (err) {
- return console.log('Could not connect to Active911: ' + err);
- }
-
- console.log('Connection to Active911 established.');
- });
-
-Usage
------
-
-The `active911` package provides support for the current Active911 API functions. We follow the standard Node.js pattern of `function(options, callback(error, data))`. If an error occurs, the first parameter of the callback will contain the error message. Otherwise, the first parameter will be false and the second parameter will contain the API response.
-
-### `ping(callback)`
-
-The `ping` command checks that your API and app keys are valid on the server. It takes only a callback function with a parameter for errors.
-
-### `getDevice(device_id, callback)`
-
-Returns some data about the device associated with the `device_id`.
-
-Example response:
-
- {
- "response_action" : "watch",
- "response_alert_id" : 4681099,
- "lat" : 44.541404,
- "name" : "Joseph Sullivan",
- "position_accuracy" : 30,
- "response_stamp" : 1371315741,
- "device_id" : 2435,
- "position_stamp" : 1371463606,
- "device_type" : "smartphone",
- "lon" : -123.364192
- }
-
-### `getAlert(alert_id, callback)`
-
-Returns data about the alert associated with the `alert_id`.
-
-Example response:
-
- {
- "source" : "",
- "priority" : "F3",
- "cad_code" : "",
- "lat" : 44.5379997,
- "place" : "",
- "agency_id" : 3,
- "state" : "OR",
- "map_code" : "",
- "city" : "Philomath",
- "timestamp" : 1369781684,
- "address" : "100 S. 16th ST",
- "description" : "Cat in tree",
- "details" : "",
- "unit" : "",
- "lon" : -123.3634479,
- "cross_street" : "",
- "alert_id" : 4264162,
- "units" : ""
- }
-
-### `getLocations(north, south, east, west, callback)`
-
-Returns an array of locations that exist within the area bounded by `north`, `south`, `east`, and `west`. The parameters should be floating point numbers representing the latitude or longitude, whichever is appropriate for the compass direction.
-
-For example,
-
- active911.getLocations(46.12345, 44.54321, -122.00021, -124.132132, function (err, locations) {
- if (err) {
- return console.log('Unable to retrieve locations: ' + err);
- }
-
- console.log('Returned ' + locations.length + ' locations.');
- });
-
-Example response:
-
- [
- {
- "resources" : [],
- "lat" : 44.540121,
- "name" : "Pinehurst Memorial",
- "agency_id" : 3,
- "icon_filename" : "icon-flag.png",
- "description" : "Main Station Hosp.\nNear the depot\n5 entrances",
- "id" : 175,
- "lon" : -123.367601,
- "icon_color" : "blue"
- },
- {
- "resources" : [
- {
- "extension" : "jpg",
- "title" : "Photo",
- "id" : 5,
- "details" : "",
- "size" : 963979
- }
- ],
- "lat" : 44.541252,
- "name" : "Hydrant",
- "agency_id" : 3,
- "icon_filename" : "icon-hydrant.png",
- "description" : "15th & College",
- "id" : 700,
- "lon" : -123.364689,
- "icon_color" : "red"
- }
- ]
+Installation is simple: `npm install --save active911`.
+
+Basic Usage
+-----------
+
+```javascript
+var Active911 = require('active911');
+var client = new Active911.RefreshClient('YOUR REFRESH TOKEN');
+
+client.getAgency().then(function(agency) {
+ console.log(agency.name);
+}).catch(function(err) {
+ console.log('Problem getting Agency details:', err);
+});
+```
+
+API Methods
+-----------
+
+The following public API methods are available:
+
+* `getAgency()`
+* `getDevice(device_id)`
+* `getAlerts()`
+* `getDeviceAlerts(device_id)`
+* `getAlert(alert_id)`
+* `getLocations()`
+* `getLocation(location_id)`
+* `getResource(resource_id)`
+
+Each method returns a promise for a result, which will resolve as either an
+object or an array, depending on the cardinality (e.g. `getAlerts` resolves as
+an array, while `getAlert` resolves as an object).
+
+For details on the format of the result, please see the [Active911 API
+wiki](http://wiki.active911.com/wiki/index.php/Accessing_the_API).
Contributing
------------
-Contributions are encouraged. For a list of open issues, see <https://github.com/benburwell/active911/issues>.
+Contributions are encouraged. For a list of open issues, see
+<https://github.com/benburwell/active911/issues>.
More Information
----------------
-More information about the API is available on [the Active911 Github wiki](https://github.com/active911/open-api/wiki).
+More information about the API is available on [the Active911
+wiki](http://wiki.active911.com/wiki/index.php/Active911_Developer_API).
+
diff --git a/examples/oauth.js b/examples/oauth.js
new file mode 100644
index 0000000..575d0d0
--- /dev/null
+++ b/examples/oauth.js
@@ -0,0 +1,16 @@
+var Active911 = require('../');
+var client = new Active911.RefreshClient('YOUR REFRESH TOKEN');
+
+client.getAgency().then(function(agency) {
+ console.log(agency.name);
+ agency.devices.forEach(function(deviceInfo) {
+ client.getDevice(deviceInfo.id).then(function(device) {
+ console.log(device.name);
+ }).catch(function(err) {
+ console.log('Error retrieving device info for #' + deviceInfo.id);
+ });
+ });
+}).catch(function(err) {
+ console.log('Error retrieving agency info:', err);
+});
+
diff --git a/examples/sample_app.js b/examples/sample_app.js
deleted file mode 100644
index a08b039..0000000
--- a/examples/sample_app.js
+++ /dev/null
@@ -1,14 +0,0 @@
-var Active911 = require('../lib/active911').Active911;
-
-var oauth_id = '12345';
-var oauth_secret = 'asdf';
-var oauth_scope = 'read_agency read_alert read_device';
-
-var api = new Active911(oauth_id, oauth_secret, oauth_scope);
-var uri = api.getAuthorizationUri();
-console.log('Please go to ' + uri + ' to authenticate.');
-
-api.ready(function() {
- console.log('OAuth workflow complete');
-
-});
diff --git a/lib/Agency.js b/lib/Agency.js
deleted file mode 100644
index c22a141..0000000
--- a/lib/Agency.js
+++ /dev/null
@@ -1,45 +0,0 @@
-var _util = require('./util');
-
-module.exports.Agency = function(options) {
- this._id = options.id;
- this._name = options.name;
- this._address = options.address;
- this._city = options.city;
- this._state = options.state;
- this._latitude = options.latitude;
- this._longitude = options.longitude;
- this._devices = options.devices;
- this._uri = options.uri || _util.API_BASE + '/agency/' + options.id;
-};
-
-module.exports.Agency.prototype.getId = function() {
- return this._id;
-};
-
-module.exports.Agency.prototype.getName = function() {
- return this._name;
-};
-
-module.exports.Agency.prototype.getAddress = function() {
- return this._address;
-};
-
-module.exports.Agency.prototype.getCity = function() {
- return this._city;
-};
-
-module.exports.Agency.prototype.getState = function() {
- return this._state;
-};
-
-module.exports.Agency.prototype.getLatitude = function() {
- return this._latitude;
-};
-
-module.exports.Agency.prototype.getLongitude = function() {
- return this._longitude;
-};
-
-module.exports.Agency.prototype.getDevices = function() {
- return this._devices;
-};
diff --git a/lib/Device.js b/lib/Device.js
deleted file mode 100644
index bfc4d34..0000000
--- a/lib/Device.js
+++ /dev/null
@@ -1,12 +0,0 @@
-var _util = require('./util');
-
-module.exports.Device = function(options) {
- this._id = options.id;
- this._name = options.name;
- this._latitude = options.latitude;
- this._longitude = options.longitude;
- this._position_accuracy = options.position_accuracy;
- this._position_timestamp = options.position_timestamp;
- this._agencies = options.agencies;
- this._uri = options.uri || _util.API_BASE + '/devices/' + options.id;
-};
diff --git a/lib/active911.js b/lib/active911.js
index e75eaca..42e51d0 100644
--- a/lib/active911.js
+++ b/lib/active911.js
@@ -1,63 +1,6 @@
-var OAuth2 = require('oauth').OAuth2;
-var http = require('http');
-var events = require('events');
-var querystring = require('querystring');
-var url = require('url');
-var Agency = require('./Agency.js').Agency;
-var Device = require('./Device.js').Device;
-var _util = require('./util');
-
-module.exports.Agency = Agency;
-module.exports.Device = Device;
-
-// Constructor
-module.exports.Active911 = function(client_id, client_secret, scope) {
- this._client_id = client_id;
- this._client_secret = client_secret;
- this._scope = scope;
- this._oauth_complete = false;
- this._emitter = new events.EventEmitter();
-
- // Create the HTTP server that will receive the OAuth code after the user
- // has authenticated to Active911.
- this._http_server = new http.Server();
-
- // The request handler
- this._http_server.on('request', function(req, res) {
-
- // The only thing we care about is the /?code=xxx
- var qs = url.parse(req.url).query;
-
- // If a code has been sent, and we don't already have one, we are good
- // to go.
- if (qs.split('=')[0] === 'code' && !this._oauth_complete) {
- this._oauth_code = qs.split('=')[1];
- this._oauth_complete = true;
- emitter.emit('oauth_complete');
- }
-
- res.writeHead(200, {'Content-Type': 'text/plain'});
- res.end();
- });
-
- // Once the server is listening, store its port number for use in auth uri
- this._http_server.listen(0, function() {
-
- });
+var Active911 = {
+ RefreshClient: require('./clients/refresh.js')
};
-module.exports.Active911.prototype.getAuthorizationUri = function() {
-
- var qs = querystring.stringify({
- client_id: this._client_id,
- response_type: 'code',
- redirection_uri: 'http://localhost:' + this._oauth_server_port,
- scope: this._scope
- });
+module.exports = Active911;
- return _util.API_BASE_NAME + '/open_api/authorize_agency.php' + qs;
-};
-
-module.exports.Active911.prototype.ready = function(func) {
- emitter.on('oauth_complete', func);
-};
diff --git a/lib/clients/refresh.js b/lib/clients/refresh.js
new file mode 100644
index 0000000..b4a3930
--- /dev/null
+++ b/lib/clients/refresh.js
@@ -0,0 +1,118 @@
+var request = require('request');
+
+var RefreshClient = function(refreshString) {
+ if (typeof refreshString !== 'string') {
+ throw new Error('Refresh token must be supplied');
+ }
+ this._refreshToken = refreshString;
+ this._accessToken = '';
+ this._accessTokenExpiration = Math.floor( new Date()/1000 );
+};
+
+RefreshClient.prototype._doRequest = function(url) {
+ var self = this;
+ var base = 'https://access.active911.com/interface/open_api/api';
+ return new Promise(function(fulfill, reject) {
+ self._getAccessToken().then(function(token) {
+ request({
+ method: 'GET',
+ url: base + url,
+ headers: {
+ 'Authorization': 'Bearer ' + token
+ }
+ }, function(err, res, body) {
+ if (err) {
+ reject(err);
+ } else {
+ var result = JSON.parse(body);
+ if (result.result == 'success') {
+ var properties = Object.getOwnPropertyNames(result.message);
+ if (properties.length == 0) {
+ fulfill({});
+ } else {
+ var propertyToReturn = properties[0];
+ fulfill(result.message[propertyToReturn]);
+ }
+ } else {
+ reject(result.message);
+ }
+ }
+ });
+ }).catch(function(err) {
+ reject(err);
+ });
+ });
+};
+
+RefreshClient.prototype._getAccessToken = function() {
+ var self = this;
+ return new Promise(function(fulfill, reject) {
+ var currentTime = Math.floor( new Date()/1000 );
+ if (self._accessTokenExpiration - currentTime <= 10) {
+ fulfill(self._refreshAccessToken());
+ } else {
+ fulfill(self._accessToken);
+ }
+ });
+};
+
+RefreshClient.prototype._refreshAccessToken = function() {
+ var self = this;
+ return new Promise(function(fulfill, reject) {
+ request({
+ url: 'https://www.active911.com/interface/dev/api_access.php',
+ method: 'POST',
+ form: {
+ 'refresh_token': self._refreshToken
+ }
+ }, function(err, res, body) {
+ if (err) {
+ reject(err);
+ } else {
+ var response = JSON.parse(body);
+ if (response.access_token && response.expiration) {
+ self._accessToken = response.access_token;
+ self._accessTokenExpiration = response.expiration;
+ fulfill(response.access_token);
+ } else {
+ reject(new Error('Malformed access token response'));
+ }
+ }
+ });
+ });
+};
+
+RefreshClient.prototype.getAgency = function() {
+ return this._doRequest('/');
+};
+
+RefreshClient.prototype.getDevice = function(id) {
+ return this._doRequest('/devices/' + id);
+};
+
+RefreshClient.prototype.getAlerts = function() {
+ return this._doRequest('/alerts');
+};
+
+RefreshClient.prototype.getAlert = function(id) {
+ return this._doRequest('/alerts/' + id);
+};
+
+RefreshClient.prototype.getDeviceAlerts = function(id) {
+ return this._doRequest('/devices/' + id + '/alerts');
+};
+
+RefreshClient.prototype.getLocations = function() {
+ return this._doRequest('/locations');
+};
+
+RefreshClient.prototype.getLocation = function(id) {
+ return this._doRequest('/locations/' + id);
+};
+
+RefreshClient.prototype.getResource = function(id) {
+ return this._doRequest('/resources/' + id);
+};
+
+module.exports = RefreshClient;
+
diff --git a/lib/util.js b/lib/util.js
deleted file mode 100644
index 08efbfb..0000000
--- a/lib/util.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports.API_BASE_NAME = 'https://access.active911.com';
-module.exports.API_BASE_PATH = '/interface/open_api/api';
-module.exports.API_BASE = exports.API_BASE_NAME + exports.API_BASE_PATH;
diff --git a/package.json b/package.json
index 2bf3062..6473beb 100644
--- a/package.json
+++ b/package.json
@@ -1,33 +1,38 @@
{
- "name": "active911",
- "version": "0.1.1",
- "author": "Ben Burwell <hi@benburwell.com>",
- "description": "A simple interface to Active911",
- "tags": [ "active911", "cad", "fire", "ems", "dispatching" ],
- "main": "./lib/active911.js",
- "dependencies": {
- "oauth": ">=0.9.11"
- },
- "devDependencies": {
- "mocha": "latest",
- "nock": "latest",
- "should": "latest",
- "grunt-mocha-test": "latest",
- "grunt": "latest",
- "grunt-cli": "latest",
- "grunt-contrib-jshint": "latest"
- },
- "scripts": {
- "test": "grunt --verbose"
- },
- "repository": {
- "type": "git",
- "url": "https://github.com/benburwell/active911.git"
- },
- "bugs": {
- "url": "https://github.com/benburwell/active911/issues"
- },
- "engines": {
- "node": ">=0.10"
- }
+ "name": "active911",
+ "version": "0.2.0",
+ "description": "A simple client for the Active911 API",
+ "main": "./lib/active911.js",
+ "scripts": {
+ "test": "mocha tests"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/benburwell/active911.git"
+ },
+ "keywords": [
+ "active911",
+ "client",
+ "ems",
+ "fire",
+ "response",
+ "api"
+ ],
+ "author": "Ben Burwell",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/benburwell/active911/issues"
+ },
+ "homepage": "https://github.com/benburwell/active911#readme",
+ "dependencies": {
+ "request": "^2.67.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ },
+ "devDependencies": {
+ "mocha": "^2.3.4",
+ "nock": "^3.4.0",
+ "should": "^8.0.1"
+ }
}
diff --git a/tests/active911.js b/tests/active911.js
index 309aafb..c0a9e1b 100644
--- a/tests/active911.js
+++ b/tests/active911.js
@@ -1,105 +1,39 @@
-require('should');
-require('nock');
-Active911 = require('../lib/active911.js');
+var should = require('should');
+var nock = require('nock');
+var Active911 = require('../lib/active911.js');
var a911;
-
-var testDevicesResponse = require('replies/devices.json');
-var testAgencyResponse = require('replies/agency.json');
-var testDeviceResponse = require('replies/device.json');
-var testErrorResponse = require('replies/error.json');
+var testDeviceResponse = require('./replies/device.json');
+var testAgencyResponse = require('./replies/agency.json');
+var testErrorResponse = require('./replies/error.json');
describe('Active911 API', function() {
-
- beforeEach(function(done) {
- a911 = new Active911();
- });
-
- describe('#getAgency', function() {
-
- it('Should return correct agency data', function() {
-
- nock('https://access.active911.com')
- .get('/interface/open_api/api/')
- .replyWithFile(200, __dirname + 'replies/agency.json'});
-
- a911.getAgency(function(err, agency) {
- should.not.exist(err);
- agency.should.equal(testAgencyResponse.message.agency);
- });
- });
-
- it('Should give an error if the API gives an error', function() {
- nock('https://access.active911.com')
- .get('/interface/open_api/api/')
- .replyWithFile(400, __dirname + 'replies/error.json');
-
- a911.getAgency(function(err, agency) {
- err.should.equal(testErrorResponse.message);
- should.not.exist(agency);
- });
- });
-
- });
-
- describe('#getDevices', function() {
- it('Should return correct device data', function() {
-
- nock('https://access.active911.com')
- .get('/interface/open_api/api/')
- .replyWithFile(400, __dirname + 'replies/agency.json');
-
- a911.getDevices(function(err, devices) {
- should.not.exist(err);
- devices.should.equal(testDevicesResponse.message.devices);
- });
- });
-
- it('Should give an error if the API gives an error', function() {
- nock('https://access.active911.com')
- .get('/interface/open_api/api/')
- .replyWithFile(400, __dirname + 'replies/error.json');
-
- a911.getDevices(function(err, devices) {
- err.should.equal(testErrorResponse.message);
- should.not.exist(devices);
- });
- });
+ beforeEach(function(done) {
+ a911 = new Active911.RefreshClient('CLIENT');
+ done();
+ });
+
+ describe('#getAgency', function() {
+ it('Should return correct agency data', function() {
+ nock('https://access.active911.com')
+ .get('/interface/open_api/api/')
+ .replyWithFile(200, __dirname + 'replies/agency.json');
+ a911.getAgency().then(function(agency) {
+ agency.should.equal(testAgencyResponse.message.agency);
+ }).catch(function(err) {
+ should.fail();
+ });
});
- describe('#getDevice', function() {
- it('Should return correct device data (String id)', function() {
- nock('https://access.active911.com')
- .get('/interface/open_api/api/devices/1')
- .replyWithFile(200, __dirname + 'replies/device.json');
-
- a911.getDevice('1', function(err, device) {
- should.not.exist(err);
- device.should.equal(testDeviceResponse.message.device);
- });
- });
-
- it('Should return correct device data (Number id)', function() {
- nock('https://access.active911.com')
- .get('/interface/open_api/api/devices/1')
- .replyWithFile(200, __dirname + 'replies/device.json');
-
- a911.getDevice(1, function(err, device) {
- should.not.exist(err);
- device.should.equal(testDeviceResponse.message.device);
- });
- });
-
- it('Should give an error if the API gives an error', function() {
- nock('https://access.active911.com')
- .get('/interface/open_api/api/devices/1')
- .replyWithFile(400, __dirname + 'replies/error.json');
-
- a911.getDevice(1, function(err, device) {
- err.should.equal(testErrorResponse.message);
- should.not.exist(device);
- });
- });
+ it('Should give an error if the API gives an error', function() {
+ nock('https://access.active911.com')
+ .get('/interface/open_api/api/')
+ .replyWithFile(400, __dirname + 'replies/error.json');
+ a911.getAgency().then(function(err, agency) {
+ should.fail();
+ }).catch(function(err) {
+ err.should.equal(testErrorResponse.message);
+ });
});
-
+ });
});
diff --git a/tests/replies/devices.json b/tests/replies/devices.json
deleted file mode 100644
index e69de29..0000000
--- a/tests/replies/devices.json
+++ /dev/null