diff --git a/.gitignore b/.gitignore index c2658d7..2bcf324 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/ +.idea/* \ No newline at end of file diff --git a/README.md b/README.md index dd5b110..7bc9ff3 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,13 @@ In your NodeJS program, add: ``` javascript var tp = require('tp-api')({ - domain: // your domain here; eg: 'fullscreen.tpondemand.com' - username: // your username; eg: 'paul@fullscreen.net' - password: // your password - version: // Optional API version - defaults to 1 - protocol: // Optional protocol - defaults to https - }) + domain: // your domain here; eg: 'fullscreen.tpondemand.com' + username: // your username; eg: 'paul@fullscreen.net' + password: // your password + version: // Optional API version - defaults to 1 + protocol: // Optional protocol - defaults to https, + convertJsonDates: true // Optional convert all dates ((string) `/Date(1467210530000+0200)/`) returned by API to JS Date-Objects +}) tp('Tasks') .take(5) @@ -144,35 +145,41 @@ tp('Tasks'). }) ``` -#### Nested JSON objects -It may happen that the following code: -```javascript -tp('Tasks') - .take(1) - .pluck('CustomFields') - .then(function(err, tasks) { - console.log('my tasks', tasks) - } -) +## Hint +Some of these methods will return deeply nested objects. If you want to debug them it's recommended to use node's [`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options) ``` -will return a lot of nested JSON objects: -```bash -my tasks [ { ResourceType: 'Task', - Id: 3631, - Project: { ResourceType: 'Project', Id: 4026, Process: [Object] }, - CustomFields: [ [Object], [Object], [Object], [Object], [Object] ] } ] -``` -The returned object `tasks` here is a JavaScript object. In order to convert it into JSON string, use [`JSON.stringify()`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify): -```javascript +var util = require('util'); tp('Tasks') - .take(1) - .pluck('CustomFields') + .take(10) .then(function(err, tasks) { - console.log('my tasks', JSON.stringify(tasks)) // changed here + console.log(util.inspect(tasks, { showHidden: true, depth: null })); } ) ``` -and now it returns: -```bash -my tasks [{"ResourceType":"Task","Id":3631,"Project":{"ResourceType":"Project","Id":4026,"Process":{"Id":5,"Name":"AIScrum"}},"CustomFields":[{"Name":"Component","Type":"DropDown","Value":null},{"Name":"trac","Type":"Number","Value":null},{"Name":"Job","Type":"DropDown","Value":null},{"Name":"Resolution","Type":"DropDown","Value":null},{"Name":"Domain","Type":"DropDown","Value":null}]}] + +## Changelog + +### Version 1.3.0 +Starting from this version you can return real [Date-Objects](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Date) if `convertJsonDates` is set to `true` +``` javascript +var tp = require('tp-api')({ + domain: 'domain.tld', + token: 'abc' + convertJsonDates: true +}); +tp('Tasks') + .take(3) + .pluck('Name', 'StartDate') + .then((err, tasks) => { + if (err) return console.log('err', err); + tasks.forEach(function(t) { + console.log( t.Id + ' :: ' + (t.StartDate.getMonth() + 1) + '-' + t.StartDate.getDate() + '-' + t.StartDate.getFullYear() + ' :: ' + t.Name ); + /* + Outputs: + 85299 :: 12-8-2015 :: Task 1 + 100853 :: 6-14-2016 :: Task 2 + 85708 :: 1-4-2016 :: Task 3 + */ + }) + }); ``` diff --git a/index.js b/index.js index 4fe2428..2323f16 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,3 @@ -var request = require('request') var _ = require('lodash') var TPQuery = require('./lib/tpquery') var TPEntity= require('./lib/tpentity') @@ -17,14 +16,8 @@ function configure(opts) { throw new Error('A TargetProcess domain is required') } - var version = opts.version || 1 - var domain = opts.domain - var token = opts.token - var protocol = opts.protocol || 'https' - var urlRoot = protocol + '://'+domain+'/api/v'+version - return function(entity, id) { - var collection = new TPQuery(urlRoot, token) + var collection = new TPQuery(opts) , model if (entity) { collection.get(entity) } diff --git a/lib/tpentity.js b/lib/tpentity.js index 5122e3b..55c9f1f 100644 --- a/lib/tpentity.js +++ b/lib/tpentity.js @@ -13,6 +13,13 @@ function TPEntity(data, options) { } } +TPEntity.prototype.fetch = function(cb) { + this.then({ + method: 'GET', + uri: this.opts.uri + '/' + this.Id + }, cb) +} + TPEntity.prototype.create = function(data, cb) { this.then({ json: data, @@ -44,7 +51,7 @@ TPEntity.prototype.setState = function(stateName, cb) { uri: this.baseUrl + '/EntityStates', method: 'GET', // force build a query for our current entity state - qs: { + qs: { where: 'Id eq ' + this.EntityState.Id, include: '[NextStates]' }, @@ -78,6 +85,7 @@ TPEntity.prototype.sync = function(data){ * @return {Object} TPEntity */ TPEntity.prototype.then = function(opts, cb) { + if(!cb) { cb = noop } opts.json = _.extend({}, opts.json || {}) if( this.Id ) { // mixing in id so TP will be able to get the right object @@ -87,12 +95,10 @@ TPEntity.prototype.then = function(opts, cb) { var that = this TPSync(options, function(err, data){ if( err ) { - return err; + return cb(err); } that.sync(data); - if( cb ) { - cb(err, data); - } + cb(err, data); }); } diff --git a/lib/tpquery.js b/lib/tpquery.js index fc5ad83..2a451a7 100644 --- a/lib/tpquery.js +++ b/lib/tpquery.js @@ -4,12 +4,18 @@ var _ = require('lodash') // TP Entity Query // ---------------------- -function TPQuery(baseUrl, token) { - this.baseUrl = baseUrl +function TPQuery(opts) { + this.version = opts.version || 1 + this.domain = opts.domain + this.token = opts.token + this.protocol = opts.protocol || 'https' + this.baseUrl = this.protocol + '://' + this.domain + '/api/v' + this.version this.opts = { - json: true, - qs: { token: token }, - headers: { Authorization: 'Basic '+ token } + convertJsonDates: opts.convertJsonDates, + qs: { token: this.token }, + headers: { + Authorization: 'Basic '+ this.token + } } } @@ -23,7 +29,6 @@ TPQuery.prototype.entities = [ TPQuery.prototype.get = function(entity) { this.opts = _.extend({}, this.opts, { - json: true, uri: this.baseUrl+'/'+entity }) return this; @@ -89,7 +94,6 @@ TPQuery.prototype.append = function() { */ TPQuery.prototype.then = function(cb) { var opts = this.opts - var that = this TPSync(opts, function(err, data){ cb(err, data); }) @@ -132,4 +136,17 @@ TPQuery.prototype.comment = function(entityId, comment, cb) { }, cb) } +TPQuery.prototype.tag = function(entityId, tags, cb) { + var that = this + var commentEntity = new TPEntity({}, that.opts) + commentEntity.then({ + json: { + Id: entityId, + Tags: tags + }, + uri: that.baseUrl + '/UserStories', + method: 'POST' + }, cb) +} + module.exports = TPQuery diff --git a/lib/tpsync.js b/lib/tpsync.js index 8cfac53..d2eae70 100644 --- a/lib/tpsync.js +++ b/lib/tpsync.js @@ -1,9 +1,33 @@ -request = require('request') +var request = require('request'); +var dotNetDate = require('json-dotnet-date')({ + useInputTimeZone: true +}); + +var jsonReviver = function(key, value) { + if (dotNetDate.testStr(value)) { + return dotNetDate.parse(value); + } + return value; +} function TPSync(opts, cb) { - return request(opts, function(err, res, json) { - if (typeof json === 'string') { - err = new Error("Couldn't find resource at "+ opts.uri) + // (Re)set options for request + opts.json = false; + opts.headers['content-type'] = 'application/json'; + + return request(opts, function(err, res, body) { + // TP returns XML in case of errors. Yay. ¯\_(ツ)_/¯ + if (body.indexOf('') === 0) { + err = new Error('Couldn\'t find resource at ' + opts.uri + ' ' + JSON.stringify(opts.qs)); + cb(err, null); + return; + } + + var json = null; + try { + json = JSON.parse(body, opts.convertJsonDates ? jsonReviver : null); + } catch (e) { + err = new Error("Can't parse JSON: \n" + body); } if (json && json.Error) { @@ -13,12 +37,11 @@ function TPSync(opts, cb) { if (typeof err === 'string') { err = new Error(err) } - + // normalize reponse data if (json && json.Items) { json = json.Items } - cb(err, (err) ? null : json) }) } -module.exports = TPSync \ No newline at end of file +module.exports = TPSync diff --git a/package.json b/package.json index 8946d3e..e0f2738 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tp-api", - "version": "1.2.1", + "version": "1.3.0", "description": "An API to access entities from TargetProcess", "main": "index.js", "scripts": { @@ -10,15 +10,17 @@ "contributors": [ "Ryan Barry (https://github.com/ryancbarry)", "Jeff Uyeno (https://github.com/jeffuyeno)", - "Julian Kleinhans (http://blog.kj187.de)" + "Julian Kleinhans (http://blog.kj187.de)", + "Mathias Schopmans (http://aoe.com)" ], "license": "BSD", "dependencies": { - "request": "~2.27.0", - "lodash": "~2.4.1" + "json-dotnet-date": "^0.1.7", + "lodash": "~4.16.4", + "request": "~2.76.0" }, "devDependencies": { - "mocha": "~1.17.0" + "mocha": "~3.1.2" }, "repository": { "type": "git",