Skip to content

Commit

Permalink
A bunch of improvements
Browse files Browse the repository at this point in the history
* Documentation improved ( see route4me#17 )
* One step closer to browser version, by removing `os` dependency (fix #4) (see route4me#16)
* Validation of input and output for/from API will be provided by external module, and could be configured additionally
* Log calls unified (passing only objects)
  • Loading branch information
maxkoryukov committed Jan 24, 2017
1 parent 9998067 commit affb023
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .jsdoc.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"docdash": {
"static": true, // Display the static members inside the navbar
"sort": true // Sort the methods in the navbar
"sort": false // Sort the methods in the navbar
}

}
10 changes: 10 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@ module.exports.Route4MeApiError = errors.Route4MeApiError
* @param {Error} err Error (if ocurred)
* @param {Object} [res] Value returned by API (on success)
*/

/**
* Input and output validation callback
*
* @callback ValidationCallback
* @private
* @param {*} obj Object to validate
* @param {string} schemaName Name of the schema to validate against
* @return {null|Error} **Falsey** value or {@link Error} object
*/
35 changes: 29 additions & 6 deletions lib/resources/optimizations.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,35 @@ class Optimizations {
}

/**
* GET a single optimization by [optimization_problem_id]{@linkcode Optimizations#get~id} parameter.
* Create a new optimization
*
* @see {@link https://route4me.io/docs/#create-an-optimization Route4Me API}
* @category Optimizations
*
* @param {[type]} params [description]
* @param {Function} callback [description]
* @return {[type]} [description]
*/
create(params, callback) {
const verror = this.r._validate(params, "Optimization_create")
if (verror) { return callback(verror) }

return this.r._makeRequest({
method: "POST",
path: "api.v4/optimization_problem.php",
body: params,
schemaName: "Optimization_response",
}, callback)
}

/**
* GET a single optimization by
* [optimization_problem_id]{@linkcode Optimizations#get~id} parameter.
*
* @see {@link https://route4me.io/docs/#get-an-optimization Route4Me API}
* @category Optimizations
*
* @param {string} id Optimization Problem ID
* @param {string|number} id Optimization Problem ID
* @param {module:route4me-node~RequestCallback} [callback]
*/
get(id, callback) {
Expand All @@ -52,10 +75,10 @@ class Optimizations {
* @see {@link https://route4me.io/docs/#get-optimizations Route4Me API}
* @category Optimizations
*
* @param {(integer|string|Array.<string>|Array.<integer>)} states List of states [1..6]
* @param {integer} [limit] Search limitation
* @param {integer} [offset] Search starting position
* @param {module:route4me-node~RequestCallback} [callback]
* @param {(integer|string|Array.<string>|Array.<integer>)} states List of states [1..6]
* @param {integer} [limit] Search limitation
* @param {integer} [offset] Search starting position
* @param {module:route4me-node~RequestCallback} [callback]
*/
list(states, limit, offset, callback) {
const _states = utils.parseStates(states)
Expand Down
59 changes: 38 additions & 21 deletions lib/route4me.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"use strict"

const os = require("os")
const request = require("superagent")
const debug = require("debug")("route4me")
const _ = require("lodash")
const request = require("superagent")
const debug = require("debug")("route4me")
const _ = require("lodash")
const platform = require("platform")

const Optimizations = require("./resources/optimizations")
// const Track = require("./resources/track")
// const Route = require("./resources/route")
// const OptimizationProblem = require("./resources/optimization_problem")
// const Order = require("./resources/order")

const packageJson = require("./../package.json")
Expand All @@ -24,37 +24,52 @@ class Route4Me {
* @param {object} [options] Additional options for new instance
* @param {string} [options.baseUrl="https://route4me.com"] Base URL for sending requests
* @param {ILogger} [options.logger] Logger facility
* @param {module:route4me-node~ValidationCallback} [options.validate=false]
* Validator for input and output parameters of the API methods. Set **falsey**
* value to skip autovalidation (in favor of manual check).
* @return {Route4Me} New API client
*/
constructor(apiKey, options) {
const opt = {}
_.defaults(opt, options, {
"baseUrl": "https://route4me.com",
"logger": utils.noopLogger,
"validate": false,
})

// TODO: make a decision, whether this param could be configured
opt.userAgent = `superagent/3.3.2 (${os.type()}; Route4Me Node/${Route4Me.version})`
opt.userAgent = `superagent/3.3.2 (${platform.name}; Route4Me Node/${Route4Me.version})`

debug("init", opt)
debug("version", Route4Me.version)

if (!apiKey) {
throw new errors.Route4MeError("ApiKey is not set")
throw new errors.Route4MeError("'apiKey' is not set")
}

this._apiKey = apiKey
this._baseUrl = opt.baseUrl
this._logger = opt.logger
this._userAgent = opt.userAgent

this._logger = opt.logger
/**
* Provides an validation interface of input-output parameters for
* certain internal classes.
*
* @since 0.3.0
* @internal
* @method
* @type {module:route4me-node~ValidationCallback}
*/
this._validate = typeof opt.validate === "function" ? opt.validate : _.noop

/**
* [Optimizations description]
* **Optimizations** related API calls
* @type {Optimizations}
*/
this.Optimizations = null
this.Optimizations = new Optimizations(this)

this._logger.debug("initialized")
this._logger.debug({ msg: "initialized" })
}

/**
Expand All @@ -71,16 +86,17 @@ class Route4Me {
}

/**
* Just a shortcut for access to {@link superagent} interface with all
* options applied.
* Wrapper around {@link external:superagent} with all options applied.
*
* @private
* @internal
*
* @param {object} options NO DESCRIPTION
* @param {string} options.method HTTP method
* @param {string} options.path Server path
* @param {Object} [options.qs] Query string
* @param {Object} [options.body] Body
* @param {object} options Request options
* @param {string} options.method HTTP method
* @param {string} options.path Server path
* @param {object} [options.qs] Query string
* @param {object} [options.body] Body
* @param {null|string} [options.schemaName=null] The name of JSON Schema which should be used for
* validation of the response
*
* @param {module:route4me-node~RequestCallback} [callback]
*/
Expand All @@ -89,15 +105,16 @@ class Route4Me {
const apiUrl = `${this._baseUrl}/${options.path}`
const qs = options.qs || null // {} /* query string */
const body = options.body || null // {} /* body */
const schemaName = options.schemaName || null
const timeouts = {
response: 5000, // Wait 5 seconds for the server to start sending,
deadline: 10000, // but allow 10 seconds to finish loading.
}

const resHandler = new utils.ResponseHandler(this._logger, callback)
const resHandler = new utils.ResponseHandler(this._logger, this._validate, schemaName, callback)

debug("sending request", apiUrl, qs)
this._logger.info("sending request", apiUrl, qs)
this._logger.info({ msg: "sending request", "url": apiUrl, "queryString": qs })

request[method](apiUrl)
.set("User-Agent", this._userAgent)
Expand Down
27 changes: 18 additions & 9 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,35 @@ class ILogger {
}

class ResponseHandler {
constructor(logger, callback) {
constructor(logger, validate, schemaName, callback) {
const cb = typeof callback !== "function" ? _.noop : callback

this._logger = logger
this._cb = cb

this._validate = validate
this._schemaName = schemaName
}

callback(err, res) {
if (err) {
return this._handleError(err, res)
}
return this._handleResult(res)
return this._handleOk(res)
}

_handleResult(res) {
const data = res
_handleOk(res) {
debug("response ok")

if (this._schemaName) {
const verror = this._validate(res, this._schemaName)
if (verror) {
this._logger.error({ "msg": "response validation error", "err": verror })
return this._cb(verror)
}
}
this._logger.info({ "msg": "response ok" })
return this._cb(null, data)
return this._cb(null, res)
}

_handleError(err, res) {
Expand All @@ -77,11 +86,11 @@ class ResponseHandler {
}
}

function _isInStateRange(state) {
return _.inRange(state, 1, 6)
}

function parseStates(states) {
function _isInStateRange(state) {
return _.inRange(state, 1, 6)
}

const t = typeof states
if (t === "number") {
if (_isInStateRange(states)) {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"dependencies": {
"debug": "^2.6.0",
"lodash": "^4.17.4",
"platform": "^1.3.3",
"superagent": "^3.3.2"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion test/route4me.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe("route4me.spec", () => {
[null, undefined, false, ""].forEach((apiKey) => {
it(`should raise when apiKey=${apiKey}`, () => {
const fn = () => new Route4Me(apiKey)
expect(fn).to.throw(/apikey is not set/i)
expect(fn).to.throw(/.?apikey.? is not set/i)
})
})
})
Expand Down

0 comments on commit affb023

Please sign in to comment.