From fa7b46be9b30642a5599364dd8eeae590cb31870 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 23 Jun 2019 09:45:18 +0300 Subject: [PATCH 01/12] oAuth2 support --- lib/apiUrl.js | 6 +++++- lib/rest.js | 11 +++++++---- lib/restHandlers.js | 47 ++++++++++++++++++++++++++++++--------------- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/lib/apiUrl.js b/lib/apiUrl.js index e9c83baf..9f1eaa8c 100644 --- a/lib/apiUrl.js +++ b/lib/apiUrl.js @@ -5,11 +5,15 @@ qs = require('qs'), protocol = 'https', host = process.env.PIPEDRIVE_API_HOST || 'api.pipedrive.com', + proxyHost = process.env.PIPEDRIVE_API_HOST || 'api-proxy.pipedrive.com', version = process.env.PIPEDRIVE_API_VERSION || 'v1', baseUri = protocol + '://' + host + '/' + version; module.exports = function apiUrl(path, options, tokenNeeded) { var queryObj = {}; + if(options.useAccessToken) + return protocol + '://' + proxyHost + '/' + path; + if (tokenNeeded) { queryObj.api_token = options.apiToken; } @@ -19,4 +23,4 @@ return baseUri + '/' + path + (_.keys(queryObj).length > 0 ? '?' + qs.stringify(queryObj) : ''); }; -})(); \ No newline at end of file +})(); diff --git a/lib/rest.js b/lib/rest.js index ac125e7e..d7fc6daf 100644 --- a/lib/rest.js +++ b/lib/rest.js @@ -39,10 +39,10 @@ requestOptions; this.options = options || {}; - this.options.method = this.options.method || method || 'GET'; + this.options.method = this.options.method || method || 'GET'; if(this.options.query) { - query = typeof this.options.query == 'string' ? this.options.query : qs.stringify(this.options.query); + query = typeof this.options.query == 'string' ? this.options.query : qs.stringify(this.options.query); url += (url.match(/\?/) ? '&' : '?') + query; } @@ -55,7 +55,7 @@ _.each(this.options.data, function(value, inputName) { payload += '--'+boundary+'\r\n'+ - 'Content-Disposition: form-data;'; + 'Content-Disposition: form-data;'; if(inputName == 'file_path' && fs.existsSync(value)) { var parts = value.split(/\/|\\/); var fileName = parts[parts.length - 1]; @@ -86,6 +86,9 @@ headers['content-type'] = this.options.json ? 'application/json' : 'application/x-www-form-urlencoded'; } + if(options.accessToken) + headers['Authorization'] = 'Bearer ' + options.accessToken; + requestOptions = { method: method, maxResponseLength: 10 * 1024 * 1024, @@ -134,4 +137,4 @@ util.inherits(Request, EventEmitter); -})(); \ No newline at end of file +})(); diff --git a/lib/restHandlers.js b/lib/restHandlers.js index b81552e1..82eff145 100644 --- a/lib/restHandlers.js +++ b/lib/restHandlers.js @@ -14,6 +14,14 @@ this.options = _.extend({}, defaults, options || {}); + this.resolveOptions = function (options) { + options = options || {}; + if(this.options.useAccessToken) { + options.accessToken = this.options.apiToken; + } + return options; + }; + return this; } @@ -82,9 +90,9 @@ // GET /items RestHandlers.prototype.listItems = function(object, params, callback) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), dataObject = object == 'authorizations' ? { multipart: false, data: paramsToSupply } : { query: qs.stringify(paramsToSupply) }, - req = rest[object == 'authorizations' ? 'post' : 'get'](apiUrl(object, this.options, false), dataObject); + req = rest[object == 'authorizations' ? 'post' : 'get'](apiUrl(object, this.options, false), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('GET', object, data, callback, req, res); @@ -96,9 +104,9 @@ // GET /items/find RestHandlers.prototype.findItems = function(object, params, callback) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), dataObject = { query: qs.stringify(paramsToSupply) }, - req = rest.get(apiUrl(object + '/find', this.options, false), dataObject); + req = rest.get(apiUrl(object + '/find', this.options, false), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('GET', object, data, callback, req, res); @@ -110,9 +118,9 @@ // GET /items/timeline RestHandlers.prototype.timelineItems = function(object, params, callback) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), dataObject = { query: qs.stringify(paramsToSupply) }, - req = rest.get(apiUrl(object + '/timeline', this.options, false), dataObject); + req = rest.get(apiUrl(object + '/timeline', this.options, false), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('GET', object, data, callback, req, res); @@ -124,9 +132,9 @@ // GET /searchResults/field RestHandlers.prototype.searchFields = function(object, params, callback) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), dataObject = { query: qs.stringify(paramsToSupply) }, - req = rest.get(apiUrl(object + '/field', this.options, false), dataObject); + req = rest.get(apiUrl(object + '/field', this.options, false), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('GET', object, data, callback, req, res); @@ -138,8 +146,9 @@ // GET /items/5 RestHandlers.prototype.getItem = function(object, id, callback, params) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken ? { api_token: this.options.apiToken } : {}), - req = rest.get(apiUrl(object + '/' + id, this.options, false), { json: true, query: qs.stringify(paramsToSupply) }); + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), + dataObject = { json: true, query: qs.stringify(paramsToSupply) }, + req = rest.get(apiUrl(object + '/' + id, this.options, false), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('GET', object, data, callback, req, res); @@ -153,7 +162,8 @@ var self = this, multipart_objects = ['files'], multipart = (_.indexOf(multipart_objects, object) != -1), - req = rest.post(apiUrl(object, this.options, true), { json: true, multipart: multipart, data: params }); + dataObject = { json: true, multipart: multipart, data: params }, + req = rest.post(apiUrl(object, this.options, true), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('POST', object, data, callback, req, res); @@ -165,7 +175,8 @@ // PUT /items/5 RestHandlers.prototype.editItem = function(itemId, object, params, callback) { var self = this, - req = rest.put(apiUrl(object + '/' + itemId, this.options, true), { json: true, multipart: false, data: params }); + dataObject = { json: true, multipart: false, data: params }, + req = rest.put(apiUrl(object + '/' + itemId, this.options, true), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('PUT', object, data, callback, req, res); @@ -177,7 +188,8 @@ // DELETE /items/5 RestHandlers.prototype.removeItem = function(itemId, object, params, callback) { var self = this, - req = rest.del(apiUrl(itemId ? object + '/' + itemId : object, this.options, true), { json: true, multipart: false, data: (_.isObject(params) && !_.isFunction(params) ? params : { id: itemId }) }); + dataObject = { json: true, multipart: false, data: (_.isObject(params) && !_.isFunction(params) ? params : { id: itemId }) }, + req = rest.del(apiUrl(itemId ? object + '/' + itemId : object, this.options, true), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('DELETE', object, data, (_.isFunction(params) ? params : callback), req, res); @@ -189,7 +201,8 @@ // DELETE /items RestHandlers.prototype.removeManyItems = function(itemIds, object, params, callback) { var self = this, - req = rest.del(apiUrl(object, this.options, true), { json: true, multipart: false, data: (_.isObject(params) && !_.isFunction(params) ? params : { ids: itemIds }) }); + dataObject = { json: true, multipart: false, data: (_.isObject(params) && !_.isFunction(params) ? params : { ids: itemIds }) }, + req = rest.del(apiUrl(object, this.options, true), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('DELETE', object, data, (_.isFunction(params) ? params : callback), req, res); @@ -205,7 +218,8 @@ return false; } var self = this, - req = rest.post(apiUrl(object + '/' + whichId + '/merge', this.options, true), { json: true, multipart: false, data: { merge_with_id: withId } }); + dataObject = { json: true, multipart: false, data: { merge_with_id: withId } }, + req = rest.post(apiUrl(object + '/' + whichId + '/merge', this.options, true), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('POST', object, data, callback, req, res); @@ -220,7 +234,8 @@ } var self = this, - req = rest.post(apiUrl(object + '/' + whichId + '/duplicate', this.options, true), { json: true, multipart: false, data: {} }); + dataObject = { json: true, multipart: false, data: {} }, + req = rest.post(apiUrl(object + '/' + whichId + '/duplicate', this.options, true), this.resolveOptions(dataObject)); req.on('complete', function(data, res) { self.genericResponse('POST', object, data, callback, req, res); From 10c0135222f37be42e3b3facb392984723b27f71 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 23 Jun 2019 10:56:15 +0300 Subject: [PATCH 02/12] adding activityFields api object to blueprint --- lib/blueprint.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/blueprint.js b/lib/blueprint.js index ba122d5c..67cc8bf2 100644 --- a/lib/blueprint.js +++ b/lib/blueprint.js @@ -8,6 +8,7 @@ // Scoped in API under /v1/{objectName} standard namespaces. exports.apiObjects = [ 'activities', + 'activityFields', 'activityTypes', 'authorizations', 'companyFeatures', From f9e5b62188a5ea4e8a69703dfef042f702f44496 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 23 Jun 2019 11:07:16 +0300 Subject: [PATCH 03/12] adding userFields api object to blueprint --- lib/blueprint.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/blueprint.js b/lib/blueprint.js index 67cc8bf2..75016a61 100644 --- a/lib/blueprint.js +++ b/lib/blueprint.js @@ -33,6 +33,7 @@ exports.apiObjects = [ 'searchResults', 'stages', 'users', + 'userFields', 'webhooks' ]; From a65fec7ec6eb28100a01dc64df121623864fba33 Mon Sep 17 00:00:00 2001 From: Eero Otsus Date: Wed, 13 Nov 2019 11:38:48 +0200 Subject: [PATCH 04/12] Update supported objects list in documentation --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6921f8d0..102e158a 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ pipedrive.Deals.getAll({}, function(err, deals) { ### Supported objects * Activities + * ActivityFields * ActivityTypes * Authorizations * Currencies @@ -63,6 +64,7 @@ pipedrive.Deals.getAll({}, function(err, deals) { * CompanySettings, * Deals * DealFields + * EmailThreads * Files * Filters * Goals @@ -79,6 +81,7 @@ pipedrive.Deals.getAll({}, function(err, deals) { * SearchResults * Stages * Users + * userFields * Webhooks ### Supported operations for object collections From 010154d8faa4d008e096f24196f86dcdd14ac479 Mon Sep 17 00:00:00 2001 From: Eero Otsus Date: Wed, 13 Nov 2019 11:43:58 +0200 Subject: [PATCH 05/12] Remove unused default options --- lib/Pipedrive.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Pipedrive.js b/lib/Pipedrive.js index 6c3872a9..459f6493 100644 --- a/lib/Pipedrive.js +++ b/lib/Pipedrive.js @@ -22,8 +22,6 @@ var defaults = { strictMode: false, - apiHost: null, - apiVersion: null }; options = _.extend({}, defaults, options); From 90a11eb053a1f1d69b4d7d5c4cb56909afa70181 Mon Sep 17 00:00:00 2001 From: Eero Otsus Date: Wed, 13 Nov 2019 12:25:08 +0200 Subject: [PATCH 06/12] Changed option name to `oauth` and added documentation --- README.md | 6 ++++++ lib/Pipedrive.js | 1 + lib/apiUrl.js | 2 +- lib/restHandlers.js | 12 ++++++------ 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 102e158a..13ed69ba 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,12 @@ This Pipedrive API client is distributed under the MIT licence. - Run tests - Make PR +## Options +* `strictMode` - In strict mode `*_id` items in the responses are numeric IDs. Default is *false* in which case expanded + objects are returned. Strict mode is recommended and is likely to be the default in the future. +* `oauth` - whether the API token is to be used as OAuth bearer token instead of classic API key (default is *false*). +When setting `oauth` to true your application code must take care of fetching, storing and refershing the tokens. + # How to use With a pre-set API token: ```js diff --git a/lib/Pipedrive.js b/lib/Pipedrive.js index 459f6493..9e127e05 100644 --- a/lib/Pipedrive.js +++ b/lib/Pipedrive.js @@ -22,6 +22,7 @@ var defaults = { strictMode: false, + oauth: false, }; options = _.extend({}, defaults, options); diff --git a/lib/apiUrl.js b/lib/apiUrl.js index 9f1eaa8c..c51ca058 100644 --- a/lib/apiUrl.js +++ b/lib/apiUrl.js @@ -11,7 +11,7 @@ module.exports = function apiUrl(path, options, tokenNeeded) { var queryObj = {}; - if(options.useAccessToken) + if(options.oauth) return protocol + '://' + proxyHost + '/' + path; if (tokenNeeded) { diff --git a/lib/restHandlers.js b/lib/restHandlers.js index 82eff145..4a7cbcbf 100644 --- a/lib/restHandlers.js +++ b/lib/restHandlers.js @@ -16,7 +16,7 @@ this.resolveOptions = function (options) { options = options || {}; - if(this.options.useAccessToken) { + if(this.options.oauth) { options.accessToken = this.options.apiToken; } return options; @@ -90,7 +90,7 @@ // GET /items RestHandlers.prototype.listItems = function(object, params, callback) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.oauth ? { api_token: this.options.apiToken } : {}), dataObject = object == 'authorizations' ? { multipart: false, data: paramsToSupply } : { query: qs.stringify(paramsToSupply) }, req = rest[object == 'authorizations' ? 'post' : 'get'](apiUrl(object, this.options, false), this.resolveOptions(dataObject)); @@ -104,7 +104,7 @@ // GET /items/find RestHandlers.prototype.findItems = function(object, params, callback) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.oauth ? { api_token: this.options.apiToken } : {}), dataObject = { query: qs.stringify(paramsToSupply) }, req = rest.get(apiUrl(object + '/find', this.options, false), this.resolveOptions(dataObject)); @@ -118,7 +118,7 @@ // GET /items/timeline RestHandlers.prototype.timelineItems = function(object, params, callback) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.oauth ? { api_token: this.options.apiToken } : {}), dataObject = { query: qs.stringify(paramsToSupply) }, req = rest.get(apiUrl(object + '/timeline', this.options, false), this.resolveOptions(dataObject)); @@ -132,7 +132,7 @@ // GET /searchResults/field RestHandlers.prototype.searchFields = function(object, params, callback) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.oauth ? { api_token: this.options.apiToken } : {}), dataObject = { query: qs.stringify(paramsToSupply) }, req = rest.get(apiUrl(object + '/field', this.options, false), this.resolveOptions(dataObject)); @@ -146,7 +146,7 @@ // GET /items/5 RestHandlers.prototype.getItem = function(object, id, callback, params) { var self = this, - paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.useAccessToken ? { api_token: this.options.apiToken } : {}), + paramsToSupply = _.extend({}, _.isObject(params) ? params : {}, this.options.apiToken && !this.options.oauth ? { api_token: this.options.apiToken } : {}), dataObject = { json: true, query: qs.stringify(paramsToSupply) }, req = rest.get(apiUrl(object + '/' + id, this.options, false), this.resolveOptions(dataObject)); From 5a3ac006d9b563871d916c77bd68b05e91f3347f Mon Sep 17 00:00:00 2001 From: Eero Otsus Date: Wed, 13 Nov 2019 12:49:57 +0200 Subject: [PATCH 07/12] Keep the scope of these changes down --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 13ed69ba..c4b2a51b 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,6 @@ This Pipedrive API client is distributed under the MIT licence. - Make PR ## Options -* `strictMode` - In strict mode `*_id` items in the responses are numeric IDs. Default is *false* in which case expanded - objects are returned. Strict mode is recommended and is likely to be the default in the future. * `oauth` - whether the API token is to be used as OAuth bearer token instead of classic API key (default is *false*). When setting `oauth` to true your application code must take care of fetching, storing and refershing the tokens. From b902bd9d9a4993aaf2cfc609701f3aa3e266b9e6 Mon Sep 17 00:00:00 2001 From: Eero Otsus Date: Wed, 13 Nov 2019 13:27:48 +0200 Subject: [PATCH 08/12] Query must still be created to properly pass strict mode --- lib/apiUrl.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/apiUrl.js b/lib/apiUrl.js index c51ca058..9906c147 100644 --- a/lib/apiUrl.js +++ b/lib/apiUrl.js @@ -11,10 +11,8 @@ module.exports = function apiUrl(path, options, tokenNeeded) { var queryObj = {}; - if(options.oauth) - return protocol + '://' + proxyHost + '/' + path; - if (tokenNeeded) { + if (!options.oauth && tokenNeeded) { queryObj.api_token = options.apiToken; } if (options.strictMode === true) { From a84f01b04b26db0b754055ef031e8c1a676c415a Mon Sep 17 00:00:00 2001 From: Eero Otsus Date: Wed, 13 Nov 2019 13:28:04 +0200 Subject: [PATCH 09/12] API proxy is no longer needed --- lib/apiUrl.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/apiUrl.js b/lib/apiUrl.js index 9906c147..4ab70bde 100644 --- a/lib/apiUrl.js +++ b/lib/apiUrl.js @@ -5,7 +5,6 @@ qs = require('qs'), protocol = 'https', host = process.env.PIPEDRIVE_API_HOST || 'api.pipedrive.com', - proxyHost = process.env.PIPEDRIVE_API_HOST || 'api-proxy.pipedrive.com', version = process.env.PIPEDRIVE_API_VERSION || 'v1', baseUri = protocol + '://' + host + '/' + version; From a4d46d8d5cc248c4583b14b11e311032964a93a2 Mon Sep 17 00:00:00 2001 From: Eero Otsus Date: Wed, 13 Nov 2019 13:39:47 +0200 Subject: [PATCH 10/12] Add info about strict mode --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c4b2a51b..13ed69ba 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ This Pipedrive API client is distributed under the MIT licence. - Make PR ## Options +* `strictMode` - In strict mode `*_id` items in the responses are numeric IDs. Default is *false* in which case expanded + objects are returned. Strict mode is recommended and is likely to be the default in the future. * `oauth` - whether the API token is to be used as OAuth bearer token instead of classic API key (default is *false*). When setting `oauth` to true your application code must take care of fetching, storing and refershing the tokens. From 625a951305874c3e55c5e98b346e2ecf2faab531 Mon Sep 17 00:00:00 2001 From: Siim Kibus Date: Tue, 14 Jan 2020 17:19:43 +0200 Subject: [PATCH 11/12] tweak README, add curly braces --- README.md | 1 - lib/rest.js | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9cf10676..a41b8bc4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ npm install pipedrive ## Roadmap & known issues - [Missing async/await promise support](https://github.com/pipedrive/client-nodejs/issues/81) -- [Missing oauth 2.0 support](https://github.com/pipedrive/client-nodejs/issues/78) ## API Documentation The Pipedrive REST API documentation can be found at https://developers.pipedrive.com/v1 diff --git a/lib/rest.js b/lib/rest.js index 1c840e8c..50373883 100644 --- a/lib/rest.js +++ b/lib/rest.js @@ -76,8 +76,9 @@ headers['content-type'] = this.options.json ? 'application/json' : 'application/x-www-form-urlencoded'; } - if(options.accessToken) + if (options.accessToken) { headers['Authorization'] = 'Bearer ' + options.accessToken; + } requestOptions = { method: method, From 19a5d7c0d9bc0cc496c2b234ea8da30984d76019 Mon Sep 17 00:00:00 2001 From: Siim Kibus Date: Thu, 16 Jan 2020 12:15:02 +0200 Subject: [PATCH 12/12] 9.2.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c715d8b..58990666 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "pipedrive", - "version": "9.1.1", + "version": "9.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6b3bcf61..fa52916f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pipedrive", - "version": "9.1.1", + "version": "9.2.0", "description": "Pipedrive REST client for NodeJS", "keywords": [ "pipedrive",