From 76539c8bca455607d3f2aced0b20d91a13d1b936 Mon Sep 17 00:00:00 2001 From: Phillip Johnsen Date: Mon, 17 Apr 2017 00:12:44 +0200 Subject: [PATCH] feat: allow custom response parser to be provided upon instantiation To enable more extensibility and future proofing, this allows a custom response parser function to be provided upon instantiation. This will make it easier for others to make their own parsers if they have the need and possibly allow the community to contribute improved parsers. Refs https://github.com/phillipj/node-plex-api/issues/75 --- Readme.md | 17 +++++++++++++++++ lib/api.js | 31 ++++++++++++++----------------- test/query-test.js | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/Readme.md b/Readme.md index 4e1aac2..fe26691 100644 --- a/Readme.md +++ b/Readme.md @@ -25,6 +25,7 @@ Options: - **pin**: optional pin code for the managed user - **token**: plex.tv authentication token (optional) - **timeout**: timeout value in milliseconds to use when making requests (optional) +- **responseParser**: custom function to be used parsing all responses from Plex Server (optional) - **options**: override additional PlexHome options (optional, but recommended for PlexHome) - **identifier**: A unique client identifier. Default is a `generated uuid v4`. *Note: you should really provide this rather than let it get generated. Every time your app runs, a new "device" will get registered on your Plex account, which can lead to poor performance once hundreds or thousands of them get created. Trust me!* - **product**: The name of your application. Official Plex examples: `Plex Web`, `Plex Home Theater`, `Plex for Xbox One`. Default `Node.js App` @@ -208,6 +209,22 @@ An optional method `initialize()` could be implemented if you need reference to } ``` +## Response parsing + +You can provide a custom function responsible for parsing all Plex Server responses if you need to, by providing it in the `responseParser` when instantiating a Plex API client. + +The default implementation either parses the JSON in the response from the Plex Server, converts XML to a JavaScript object or returns the response as is, depending on the response Content-Type header. + +A response parsing function gets two arguments provided: `response`, `body` and is expected to return a `Promise`. + +```js +function myCustomJsonResponseParser(response, body) { + const bodyAsString = body.toString('ut8'); + + return Promise.resolve(bodyAsString).then(JSON.parse); +} +``` + ## HTTP API Documentation For more information about the API capabilities, see the [unofficial Plex API documentation](https://github.com/Arcanemagus/plex-api/wiki). The [PlexInc's desktop client wiki](https://github.com/plexinc/plex-media-player/wiki/Remote-control-API) might also be valueable. diff --git a/lib/api.js b/lib/api.js index 9a6a937..bcb454a 100644 --- a/lib/api.js +++ b/lib/api.js @@ -23,6 +23,7 @@ function PlexAPI(options, deprecatedPort) { this.managedUser = opts.managedUser; this.authToken = opts.token; this.authenticator = opts.authenticator || this._credentialsAuthenticator(); + this.responseParser = opts.responseParser || this._defaultResponseParser; this.options = opts.options || {}; this.options.identifier = this.options.identifier || uuid.v4(); this.options.product = this.options.product || 'Node.js App'; @@ -160,14 +161,10 @@ PlexAPI.prototype._request = function _request(options) { return new Promise((resolve, reject) => { request(reqOpts, function onResponse(err, response, body) { - var resolveValue; - if (err) { return reject(err); } - resolveValue = body; - // 403 forbidden when managed user does not have sufficient permission if (response.statusCode === 403) { return reject(new Error('Plex Server denied request due to lack of managed user permissions!')); @@ -203,19 +200,7 @@ PlexAPI.prototype._request = function _request(options) { // releasing socket back to the agent connection pool: http://nodejs.org/api/http.html#http_agent_maxsockets response.on('data', function onData() {}); - if (!parseResponse) { - return resolve(); - } - - if (response.headers['content-type'] === 'application/json') { - resolveValue = JSON.parse(body.toString('utf8')); - } else if (response.headers['content-type'].indexOf('xml') > -1) { - resolveValue = xmlToJSON(body.toString('utf8'), { - attrkey: 'attributes' - }); - } - - return resolve(resolveValue); + return parseResponse ? resolve(self.responseParser(response, body)) : resolve(); }); }); }; @@ -273,6 +258,18 @@ PlexAPI.prototype._serverScheme = function _serverScheme() { return this.port === 443 ? 'https://' : 'http://'; }; +PlexAPI.prototype._defaultResponseParser = function _defaultResponseParser(response, body) { + if (response.headers['content-type'] === 'application/json') { + return Promise.resolve(body.toString('utf8')).then(JSON.parse); + } else if (response.headers['content-type'].indexOf('xml') > -1) { + return xmlToJSON(body.toString('utf8'), { + attrkey: 'attributes' + }); + } + + return Promise.resolve(body); +}; + function xmlToJSON(str, options) { return new Promise((resolve, reject) => { xml2js.parseString(str, options, (err, jsonObj) => { diff --git a/test/query-test.js b/test/query-test.js index 5a121b0..f387b79 100644 --- a/test/query-test.js +++ b/test/query-test.js @@ -143,4 +143,19 @@ describe('query()', function() { }); }); }); + + describe('response parser', () => { + it('allows response parser to be provided upon client instantiation', () => { + const staticResponseParser = () => Promise.resolve('Response parsing has been overriden'); + + api = new PlexAPI({ + hostname: 'localhost', + responseParser: staticResponseParser + }); + + return api.query('/').then(result => { + expect(result).to.be('Response parsing has been overriden'); + }); + }); + }); });