From cbd1f15e678ae420fcefec12829c552a559a6857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Bra=C5=A1na?= Date: Thu, 30 Jul 2015 01:32:46 +0200 Subject: [PATCH 01/11] Fix incorrect handling of callbacks --- lib/tpentity.js | 9 ++++----- lib/tpquery.js | 2 +- lib/tpsync.js | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/tpentity.js b/lib/tpentity.js index 5122e3b..0b99761 100644 --- a/lib/tpentity.js +++ b/lib/tpentity.js @@ -44,7 +44,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 +78,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 +88,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 c0f0b02..f4912ee 100644 --- a/lib/tpquery.js +++ b/lib/tpquery.js @@ -67,7 +67,7 @@ TPQuery.prototype.sortBy = function(property) { // ---------------------------------------------------------------------------- /** * Simple callback structure for fetching queries - * @param {Function} cb + * @param {Function} cb */ TPQuery.prototype.then = function(cb) { var opts = this.opts diff --git a/lib/tpsync.js b/lib/tpsync.js index 8cfac53..1ddbfd9 100644 --- a/lib/tpsync.js +++ b/lib/tpsync.js @@ -13,10 +13,10 @@ 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) }) } From d33cd13e6e9881db136d19233382ea62463bcd08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Bra=C5=A1na?= Date: Thu, 30 Jul 2015 01:38:00 +0200 Subject: [PATCH 02/11] Add fetch method to TPEntity --- lib/tpentity.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/tpentity.js b/lib/tpentity.js index 0b99761..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, From f40535d0bc224cb82e97e7eda26d362be237c237 Mon Sep 17 00:00:00 2001 From: MH Holdings Date: Tue, 27 Sep 2016 18:00:14 -0400 Subject: [PATCH 03/11] Add .tag method to modify UserStories.Tags --- .gitignore | 1 + lib/tpquery.js | 13 +++++++++++++ 2 files changed, 14 insertions(+) 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/lib/tpquery.js b/lib/tpquery.js index fc5ad83..ec426b5 100644 --- a/lib/tpquery.js +++ b/lib/tpquery.js @@ -132,4 +132,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 From 040f3a7013ae9cd7f6edebe4019716d1c55042ea Mon Sep 17 00:00:00 2001 From: Mathias Schopmans Date: Wed, 26 Oct 2016 19:47:50 +0200 Subject: [PATCH 04/11] Use `json-dotnet-date` to return Date-Objects instead of Date-Strings --- lib/tpsync.js | 40 +++++++++++++++++++++++++++++++++------- package.json | 5 +++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/tpsync.js b/lib/tpsync.js index 8cfac53..2a36a92 100644 --- a/lib/tpsync.js +++ b/lib/tpsync.js @@ -1,9 +1,36 @@ -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) + + // Remap options, because we want to use our own JSON.parse + if (opts.json) { + opts.headers['content-type'] = 'application/json'; + opts.json = false; + } + + 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, jsonReviver); + } catch (e) { + err = new Error("Can't parse JSON: \n" + body); } if (json && json.Error) { @@ -13,12 +40,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..1ea8196 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,9 @@ ], "license": "BSD", "dependencies": { - "request": "~2.27.0", - "lodash": "~2.4.1" + "json-dotnet-date": "^0.1.7", + "lodash": "~2.4.1", + "request": "~2.27.0" }, "devDependencies": { "mocha": "~1.17.0" From 91e9a00626de80c506803251ab32c3fa83a5846f Mon Sep 17 00:00:00 2001 From: Mathias Schopmans Date: Wed, 26 Oct 2016 19:51:51 +0200 Subject: [PATCH 05/11] Add Mathias as author --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ea8196..22efba8 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "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": { From d0d18dd917e502cefd41bd08d8d36344b1643924 Mon Sep 17 00:00:00 2001 From: Mathias Schopmans Date: Wed, 26 Oct 2016 19:59:50 +0200 Subject: [PATCH 06/11] Update dependencies --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 22efba8..8ad5a8b 100644 --- a/package.json +++ b/package.json @@ -16,11 +16,11 @@ "license": "BSD", "dependencies": { "json-dotnet-date": "^0.1.7", - "lodash": "~2.4.1", - "request": "~2.27.0" + "lodash": "~4.16.4", + "request": "~2.76.0" }, "devDependencies": { - "mocha": "~1.17.0" + "mocha": "~3.1.2" }, "repository": { "type": "git", From af1b1316fa0a009bdef39e280071ec0dd2f96685 Mon Sep 17 00:00:00 2001 From: Mathias Schopmans Date: Wed, 26 Oct 2016 20:30:00 +0200 Subject: [PATCH 07/11] Bump version to 1.3 and add CHANGELOG.md --- README.md | 46 ++++++++++++++++------------------------------ package.json | 2 +- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index dd5b110..4a5d824 100644 --- a/README.md +++ b/README.md @@ -144,35 +144,21 @@ 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) - } -) -``` -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 +## Changelog + +### 1.3 +Starting from this version all date-values will be converted to Date-Objects +``` javascript + tp('Tasks') - .take(1) - .pluck('CustomFields') - .then(function(err, tasks) { - console.log('my tasks', JSON.stringify(tasks)) // changed here - } -) -``` -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}]}] + .sortByDesc('StartDate') + .take(10) + .pluck('Name', 'StartDate') + .then((err, tasks) => { + if (err) return console.log('err', err); + console.log('Found:', tasks.length); + tasks.forEach(function(t) { + console.log( t.Id + ' :: ' + (t.StartDate.getMonth() + 1) + "-" + t.StartDate.getDate() + "-" + t.StartDate.getFullYear() ); + }) + }); ``` diff --git a/package.json b/package.json index 8ad5a8b..ddfec40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tp-api", - "version": "1.2.1", + "version": "1.3", "description": "An API to access entities from TargetProcess", "main": "index.js", "scripts": { From 99f9566622ffe8053a597fefb0912801e2ce9adf Mon Sep 17 00:00:00 2001 From: Mathias Schopmans Date: Thu, 27 Oct 2016 09:28:27 +0200 Subject: [PATCH 08/11] Fix version to sermver spec --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ddfec40..e0f2738 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tp-api", - "version": "1.3", + "version": "1.3.0", "description": "An API to access entities from TargetProcess", "main": "index.js", "scripts": { From d8a4ca3e5fbfd48ead6a5cdb0e8164655067a941 Mon Sep 17 00:00:00 2001 From: Mathias Schopmans Date: Thu, 27 Oct 2016 10:44:05 +0200 Subject: [PATCH 09/11] Add `convertJsonDates` option This will prevent a breaking change --- README.md | 35 ++++++++++++++++++++++------------- index.js | 9 +-------- lib/tpquery.js | 18 +++++++++++------- lib/tpsync.js | 11 ++++------- 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 4a5d824..f856c51 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) @@ -146,19 +147,27 @@ tp('Tasks'). ## Changelog -### 1.3 -Starting from this version all date-values will be converted to Date-Objects +### 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). ``` javascript - +var tp = require('tp-api')({ + domain: 'domain.tld', + token: 'abc' + convertJsonDates: true +}); tp('Tasks') - .sortByDesc('StartDate') - .take(10) + .take(3) .pluck('Name', 'StartDate') .then((err, tasks) => { if (err) return console.log('err', err); - console.log('Found:', tasks.length); tasks.forEach(function(t) { - console.log( t.Id + ' :: ' + (t.StartDate.getMonth() + 1) + "-" + t.StartDate.getDate() + "-" + t.StartDate.getFullYear() ); + 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/tpquery.js b/lib/tpquery.js index ec426b5..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); }) diff --git a/lib/tpsync.js b/lib/tpsync.js index 2a36a92..d2eae70 100644 --- a/lib/tpsync.js +++ b/lib/tpsync.js @@ -11,12 +11,9 @@ var jsonReviver = function(key, value) { } function TPSync(opts, cb) { - - // Remap options, because we want to use our own JSON.parse - if (opts.json) { - opts.headers['content-type'] = 'application/json'; - opts.json = false; - } + // (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. ¯\_(ツ)_/¯ @@ -28,7 +25,7 @@ function TPSync(opts, cb) { var json = null; try { - json = JSON.parse(body, jsonReviver); + json = JSON.parse(body, opts.convertJsonDates ? jsonReviver : null); } catch (e) { err = new Error("Can't parse JSON: \n" + body); } From a9ab5dad0eae7460718060a60d85ce9124670948 Mon Sep 17 00:00:00 2001 From: Mathias Schopmans Date: Thu, 27 Oct 2016 10:50:55 +0200 Subject: [PATCH 10/11] Improve documentation about `convertJsonDates` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f856c51..eb27fc4 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ tp('Tasks'). ## 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). +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', From 9d58eb4b66a059d5e1ef3dd32cb87f070be2fece Mon Sep 17 00:00:00 2001 From: Mathias Schopmans Date: Thu, 27 Oct 2016 10:51:44 +0200 Subject: [PATCH 11/11] Add hint related to @xmik tp-api#3 --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index eb27fc4..7bc9ff3 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,18 @@ tp('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) +``` +var util = require('util'); +tp('Tasks') + .take(10) + .then(function(err, tasks) { + console.log(util.inspect(tasks, { showHidden: true, depth: null })); + } +) +``` + ## Changelog ### Version 1.3.0