youtube-feeds
Advanced tools
Comparing version 2.3.0 to 2.4.0
@@ -9,3 +9,3 @@ { | ||
"description": "Access public YouTube Data API v2 feeds, mostly with the clean JSON-C results", | ||
"version": "2.3.0", | ||
"version": "2.4.0", | ||
"repository": { | ||
@@ -25,4 +25,7 @@ "type": "git", | ||
"engines": { | ||
"node": "<=0.8.3 >=0.10.0" | ||
"node": ">=0.8.28" | ||
}, | ||
"scripts": { | ||
"test": "node test.js" | ||
}, | ||
"keywords": ["youtube", "youtubev2", "video", "feeds", "api"], | ||
@@ -29,0 +32,0 @@ "license": { |
164
README.md
@@ -1,3 +0,3 @@ | ||
nodejs-youtube | ||
============== | ||
youtube-feeds | ||
============= | ||
@@ -7,3 +7,5 @@ | ||
[![Build Status](https://travis-ci.org/fvdm/nodejs-youtube.svg?branch=master)](https://travis-ci.org/fvdm/nodejs-youtube) | ||
End of life notice | ||
@@ -18,35 +20,14 @@ ------------------ | ||
Suggestion: [youtube-api](https://www.npmjs.org/package/youtube-api) on npm ([Github](https://github.com/IonicaBizau/youtube-api)) | ||
> See the Wiki for a working example: <https://github.com/fvdm/nodejs-youtube/wiki> | ||
Installation | ||
------------ | ||
### From npm registry | ||
Stable: `npm install youtube-feeds` | ||
The npm release is always the recent *stable* version. | ||
Develop: `npm install fvdm/youtube-feeds#develop` | ||
``` | ||
npm install youtube-feeds | ||
``` | ||
```js | ||
var youtube = require('youtube-feeds') | ||
``` | ||
### From Github | ||
The code on Github is the most recent version, but may be untested. | ||
``` | ||
git clone https://github.com/fvdm/nodejs-youtube | ||
cd nodejs-youtube | ||
npm install | ||
``` | ||
```js | ||
var youtube = require('/path/to/nodejs-youtube') | ||
``` | ||
Usage | ||
@@ -67,23 +48,19 @@ ----- | ||
* `httpProtocol` (string) | ||
param | type | default | description | ||
-------------|---------|---------|------------ | ||
httpProtocol | string | http | Which HTTP protocol to use | ||
timeout | integer | 30000 | Request wait timeout in ms | ||
developerKey | string | | Your YouTube [developer key](http://code.google.com/apis/youtube/dashboard/) | ||
Which HTTP protocol to use: `http` (default) or `https` | ||
* `timeout` (integer) | ||
> `developerKey` is required for some methods, ie. [user.activity](#useractivity). | ||
> You can also temporarily override the global setting with the `key` property in a method's vars. | ||
Destroy the request after this number of *milliseconds*. Default: 30000 (30 sec). | ||
* `developerKey` (string) | ||
Set your YouTube developer key ([manage](http://code.google.com/apis/youtube/dashboard/)). | ||
This is required for some methods, ie. [user.activity](#useractivity). | ||
You can also temporarily override the global setting with the `key` property in a method's vars. | ||
### Example: | ||
```js | ||
var youtube = require('youtube-feeds') | ||
youtube.httpProtocol = 'https' | ||
youtube.feeds.videos( {q:'keywords'}, callback ) | ||
var youtube = require ('youtube-feeds'); | ||
youtube.httpProtocol = 'https'; | ||
youtube.feeds.videos ({q: 'keywords'}, callback); | ||
``` | ||
@@ -98,7 +75,7 @@ | ||
```js | ||
function( err, data ) { | ||
if( err instanceof Error ) { | ||
console.log( err ) | ||
function (err, data) { | ||
if (err) { | ||
console.log (err); | ||
} else { | ||
console.log( data ) | ||
console.log (data); | ||
} | ||
@@ -108,26 +85,31 @@ } | ||
Properties: | ||
#### Error Properties | ||
err.message : the error message | ||
err.stack : stack trace | ||
err.origin : what it relates to (api, method, request) | ||
err.details : api response or other information when available, or `null` | ||
property | type | description | ||
------------|--------|---------------------------------- | ||
err.message | string | the error message | ||
err.stack | string | stack trace | ||
err.origin | string | Context; api, method, request | ||
err.details | mixed | API response or other information | ||
Messages: | ||
Error: invalid response api API response can't be parsed | ||
Error: not json api Expected JSON, received something else | ||
Error: not found method Requested data was not found | ||
Error: not allowed method No permission to requested data | ||
Error: invalid id method Requested video ID is invalid | ||
Error: connection closed api Connection dropped early | ||
Error: connection error request Can't connect to API | ||
Error: request timeout request The request took too long to connect or process | ||
Error: error api API returned an error, see err.details | ||
Error: developer key missing api developerKey is not set, see Configuration. | ||
#### Error Messages | ||
message | origin | description | ||
----------------------|---------|------------------------------------------------ | ||
invalid response | api | API response can't be parsed | ||
not json | api | Expected JSON, received something else | ||
not found | method | Requested data was not found | ||
not allowed | method | No permission to requested data | ||
invalid id | method | Requested video ID is invalid | ||
connection closed | api | Connection dropped early | ||
connection error | request | Can't connect to API | ||
request timeout | request | The request took too long to connect or process | ||
error | api | API returned an error, see err.details | ||
developer key missing | api | developerKey is not set, see Configuration. | ||
=========================================================================================== | ||
================================================================================= | ||
Feeds | ||
@@ -148,3 +130,3 @@ ----- | ||
```js | ||
youtube.feeds.videos( | ||
youtube.feeds.videos ( | ||
{ | ||
@@ -156,3 +138,3 @@ q: 'parkour', | ||
console.log | ||
) | ||
); | ||
``` | ||
@@ -242,3 +224,3 @@ | ||
```js | ||
youtube.feeds.standard( 'most_recent', console.log ) | ||
youtube.feeds.standard ('most_recent', console.log); | ||
``` | ||
@@ -250,3 +232,3 @@ | ||
```js | ||
youtube.feeds.standard( 'NL/top_rated_News', {time: 'today'}, console.log ) | ||
youtube.feeds.standard ('NL/top_rated_News', {time: 'today'}, console.log); | ||
``` | ||
@@ -262,3 +244,3 @@ | ||
=========================================================================================== | ||
======================================================================= | ||
@@ -269,3 +251,2 @@ | ||
The **video** function provides shorthand methods for one specific video. | ||
@@ -281,3 +262,3 @@ | ||
```js | ||
youtube.video( 'ern37eWDnT0', console.log ) | ||
youtube.video ('ern37eWDnT0', console.log); | ||
``` | ||
@@ -293,3 +274,3 @@ | ||
```js | ||
youtube.video( 'ern37eWDnT0' ).details( console.log ) | ||
youtube.video ('ern37eWDnT0').details (console.log); | ||
``` | ||
@@ -305,3 +286,3 @@ | ||
```js | ||
youtube.video( 'ern37eWDnT0' ).related( {'max-results': 2}, console.log ) | ||
youtube.video ('ern37eWDnT0').related ({'max-results': 2}, console.log); | ||
``` | ||
@@ -317,3 +298,3 @@ | ||
```js | ||
youtube.video( 'ern37eWDnT0' ).responses( {'max-results': 2}, console.log ) | ||
youtube.video ('ern37eWDnT0').responses ({'max-results': 2}, console.log); | ||
``` | ||
@@ -329,7 +310,7 @@ | ||
```js | ||
youtube.video( 'ern37eWDnT0' ).comments( {'max-results': 2}, console.log ) | ||
youtube.video ('ern37eWDnT0').comments ({'max-results': 2}, console.log); | ||
``` | ||
=========================================================================================== | ||
======================================================================= | ||
@@ -350,3 +331,3 @@ | ||
```js | ||
youtube.user( 'user', console.log ) | ||
youtube.user ('user', console.log); | ||
``` | ||
@@ -362,3 +343,3 @@ | ||
```js | ||
youtube.user( 'user' ).profile( console.log ) | ||
youtube.user ('user').profile (console.log); | ||
``` | ||
@@ -374,5 +355,6 @@ | ||
```js | ||
youtube.user( 'user' ).favorites( console.log ) | ||
youtube.user ('user').favorites (console.log); | ||
``` | ||
user.playlists | ||
@@ -392,8 +374,9 @@ -------------- | ||
```js | ||
youtube.user( 'user' ).uploads( console.log ) | ||
youtube.user ('user').uploads (console.log); | ||
``` | ||
=========================================================================================== | ||
======================================================================= | ||
talk | ||
@@ -406,16 +389,13 @@ ---- | ||
Param Type Description | ||
---------- -------- ---------------------------------------------------- | ||
path string full method path without leading slash | ||
fields object GET parameters | ||
callback function callback function to receive results | ||
oldJsonKey boolean force old XML-to-JSON format instead of clean JSON-C | ||
its value is the key containing the expected results | ||
Param | Type | Description | ||
-----------|----------|------------------------------------------------ | ||
path | string | full method path without leading slash | ||
fields | object | GET parameters | ||
callback | function | callback function to receive results | ||
oldJsonKey | boolean | force old XML-to-JSON format instead of clean JSON-C its value is the key containing the expected results | ||
Unlicense / Public Domain | ||
------------------------- | ||
This is free and unencumbered software released into the public domain. | ||
@@ -445,1 +425,9 @@ | ||
For more information, please refer to <http://unlicense.org> | ||
Author | ||
------ | ||
Franklin van de Meent | ||
| [Website](https://frankl.in) | ||
| [Github](https://github.com/fvdm) |
@@ -10,10 +10,10 @@ /* | ||
var xml2json = require('node-xml2json') | ||
var querystring = require('querystring') | ||
var xml2json = require ('node-xml2json'); | ||
var querystring = require ('querystring'); | ||
var app = { | ||
httpProtocol: 'http', // http, https | ||
timeout: 5000, // max execution time in milliseconds | ||
developerKey: null // YouTube developer key | ||
} | ||
httpProtocol: 'http', // http, https | ||
timeout: 5000, // max execution time in milliseconds | ||
developerKey: null // YouTube developer key | ||
}; | ||
@@ -26,39 +26,37 @@ | ||
app.feeds = { | ||
// Videos | ||
videos: function( vars, cb ) { | ||
app.talk( 'feeds/api/videos', vars, cb ) | ||
}, | ||
// Related videos | ||
related: function( videoid, vars, cb ) { | ||
app.talk( 'feeds/api/videos/'+ videoid +'/related', vars, cb ) | ||
}, | ||
// Responses | ||
responses: function( videoid, vars, cb ) { | ||
app.talk( 'feeds/api/videos/'+ videoid +'/responses', vars, cb ) | ||
}, | ||
// Comments | ||
comments: function( videoid, vars, cb ) { | ||
app.talk( 'feeds/api/videos/'+ videoid +'/comments', vars, cb, 'feed' ) | ||
}, | ||
// Standard feed | ||
// https://developers.google.com/youtube/2.0/reference#Standard_feeds | ||
// feeds.standard( 'most_recent', console.log ) | ||
// feeds.standard( 'NL/top_rated_News', {time: 'today'}, console.log ) | ||
standard: function( feed, vars, cb ) { | ||
app.talk( 'feeds/api/standardfeeds/'+ feed, vars, cb ) | ||
}, | ||
// Playlist | ||
playlist: function( playlistid, vars, cb ) { | ||
app.talk( 'feeds/api/playlists/'+ playlistid, vars, cb ) | ||
} | ||
} | ||
// Videos | ||
videos: function (vars, cb) { | ||
app.talk ('feeds/api/videos', vars, cb); | ||
}, | ||
// Related videos | ||
related: function (videoid, vars, cb) { | ||
app.talk ('feeds/api/videos/'+ videoid +'/related', vars, cb); | ||
}, | ||
// Responses | ||
responses: function (videoid, vars, cb) { | ||
app.talk ('feeds/api/videos/'+ videoid +'/responses', vars, cb); | ||
}, | ||
// Comments | ||
comments: function (videoid, vars, cb) { | ||
app.talk ('feeds/api/videos/'+ videoid +'/comments', vars, cb, 'feed'); | ||
}, | ||
// Standard feed | ||
// https://developers.google.com/youtube/2.0/reference#Standard_feeds | ||
// feeds.standard ('most_recent', console.log) | ||
// feeds.standard ('NL/top_rated_News', {time: 'today'}, console.log) | ||
standard: function (feed, vars, cb) { | ||
app.talk ('feeds/api/standardfeeds/'+ feed, vars, cb); | ||
}, | ||
// Playlist | ||
playlist: function (playlistid, vars, cb) { | ||
app.talk ('feeds/api/playlists/'+ playlistid, vars, cb); | ||
} | ||
}; | ||
/////////// | ||
@@ -68,74 +66,67 @@ // VIDEO // | ||
app.video = function( videoid, cb ) { | ||
if( typeof cb === 'function' ) { | ||
app.talk( 'feeds/api/videos/'+ videoid, cb ) | ||
} | ||
// video shortcuts | ||
return { | ||
details: function( cb ) { | ||
app.video( videoid, cb ) | ||
}, | ||
related: function( vars, cb ) { | ||
app.feeds.related( videoid, vars, cb ) | ||
}, | ||
responses: function( vars, cb ) { | ||
app.feeds.responses( videoid, vars, cb ) | ||
}, | ||
comments: function( vars, cb ) { | ||
app.feeds.comments( videoid, vars, cb ) | ||
} | ||
} | ||
} | ||
app.video = function (videoid, cb) { | ||
if (typeof cb === 'function') { | ||
app.talk ('feeds/api/videos/'+ videoid, cb); | ||
} | ||
// video shortcuts | ||
return { | ||
details: function (cb) { | ||
app.video (videoid, cb); | ||
}, | ||
related: function (vars, cb) { | ||
app.feeds.related (videoid, vars, cb); | ||
}, | ||
responses: function (vars, cb) { | ||
app.feeds.responses (videoid, vars, cb); | ||
}, | ||
comments: function (vars, cb) { | ||
app.feeds.comments (videoid, vars, cb); | ||
} | ||
}; | ||
}; | ||
////////// | ||
// USER // | ||
////////// | ||
// User | ||
app.user = function( userid, cb ) { | ||
if( cb && typeof cb === 'function' ) { | ||
app.user( userid ).profile( cb ) | ||
} | ||
return { | ||
// Favorites | ||
favorites: function( vars, cb ) { | ||
app.talk( 'feeds/api/users/'+ userid +'/favorites', vars, cb ) | ||
}, | ||
// Playlists | ||
playlists: function( vars, cb ) { | ||
app.talk( 'feeds/api/users/'+ userid +'/playlists', vars, cb ) | ||
}, | ||
// Profile | ||
profile: function( cb ) { | ||
app.talk( 'feeds/api/users/'+ userid, {}, cb, 'entry' ) | ||
}, | ||
// Uploads | ||
uploads: function( vars, cb ) { | ||
app.talk( 'feeds/api/users/'+ userid +'/uploads', vars, cb ) | ||
}, | ||
// New subscription videos | ||
newsubscriptionvideos: function( vars, cb ) { | ||
app.talk( 'feeds/api/users/'+ userid +'/newsubscriptionvideos', vars, cb ) | ||
} | ||
} | ||
} | ||
app.user = function (userid, cb) { | ||
if (cb && typeof cb === 'function') { | ||
app.user (userid).profile (cb); | ||
} | ||
return { | ||
// Favorites | ||
favorites: function (vars, cb) { | ||
app.talk ('feeds/api/users/'+ userid +'/favorites', vars, cb); | ||
}, | ||
// Playlists | ||
playlists: function (vars, cb) { | ||
app.talk ('feeds/api/users/'+ userid +'/playlists', vars, cb); | ||
}, | ||
// Profile | ||
profile: function (cb) { | ||
app.talk ('feeds/api/users/'+ userid, {}, cb, 'entry'); | ||
}, | ||
// Uploads | ||
uploads: function (vars, cb) { | ||
app.talk ('feeds/api/users/'+ userid +'/uploads', vars, cb); | ||
}, | ||
// New subscription videos | ||
newsubscriptionvideos: function (vars, cb) { | ||
app.talk ('feeds/api/users/'+ userid +'/newsubscriptionvideos', vars, cb); | ||
} | ||
}; | ||
}; | ||
///////////////// | ||
@@ -145,231 +136,201 @@ // COMMUNICATE // | ||
app.talk = function( path, fields, cb, oldJsonKey ) { | ||
var complete = false | ||
// fix callback | ||
if( !cb && typeof fields === 'function' ) { | ||
var cb = fields | ||
var fields = {} | ||
} | ||
// fix fields | ||
if( !fields || typeof fields !== 'object' ) { | ||
var fields = {} | ||
} | ||
// force JSON-C and version | ||
fields.alt = oldJsonKey !== undefined ? 'json' : 'jsonc' | ||
fields.v = 2 | ||
// prepare | ||
var options = { | ||
hostname: 'gdata.youtube.com', | ||
path: '/'+ path +'?'+ querystring.stringify( fields ), | ||
headers: { | ||
'User-Agent': 'youtube-feeds.js (https://github.com/fvdm/nodejs-youtube)', | ||
'Accept': 'application/json', | ||
'GData-Version': '2' | ||
}, | ||
method: 'GET' | ||
} | ||
// use X-GData-Key instead of adding it to the url, as per http://goo.gl/HEiCj | ||
// basically more secure in headers than in query string | ||
if ( fields.key || app.developerKey ) { | ||
options.headers['X-GData-Key'] = 'key=' + fields.key || app.developerKey | ||
delete fields.key | ||
} | ||
if( app.httpProtocol === 'https' ) { | ||
var request = require('https').request( options ) | ||
} else { | ||
var request = require('http').request( options ) | ||
} | ||
// response | ||
request.on( 'response', function( response ) { | ||
var data = [] | ||
var size = 0 | ||
response.on( 'data', function( chunk ) { | ||
data.push( chunk ) | ||
size += chunk.length | ||
}) | ||
response.on( 'end', function() { | ||
if( complete ) { | ||
return | ||
} else { | ||
complete = true | ||
} | ||
// process buffer and clear mem | ||
var buf = new Buffer( size ) | ||
var pos = 0 | ||
for( var i = 0; i < data.length; ++i ) { | ||
data[i].copy( buf, pos ) | ||
pos += data[i].length | ||
} | ||
data = buf.toString('utf8').trim() | ||
var error = null | ||
// validate | ||
if( data.match( /^(\{.*\}|\[.*\])$/ ) ) { | ||
// ok | ||
data = JSON.parse( data ) | ||
if( data.data !== undefined ) { | ||
data = data.data | ||
} else if( data.error !== undefined ) { | ||
complete = true | ||
error = new Error('error') | ||
error.origin = 'api' | ||
error.details = data.error | ||
} else if( oldJsonKey !== undefined ) { | ||
if( data[ oldJsonKey ] === undefined ) { | ||
complete = true | ||
error = new Error('invalid response') | ||
error.origin = 'api' | ||
} else { | ||
data = data[ oldJsonKey ] | ||
} | ||
} | ||
} else if( data.match( /^<errors .+<\/errors>$/ ) || data.match( /^<\?xml version.+<\/errors>$/ ) ) { | ||
// xml error response | ||
data = xml2json.parser( data ) | ||
// fix for JSONC compatibility | ||
complete = true | ||
error = new Error('error') | ||
error.origin = 'api' | ||
error.details = data.errors.error !== undefined ? [data.errors.error] : data.errors | ||
error.details.forEach( function( err, errk ) { | ||
if( err.internalreason !== undefined ) { | ||
error.details[ errk ].internalReason = err.internalreason | ||
delete error.details[ errk ].internalreason | ||
} | ||
}) | ||
} else if( ~data.indexOf('<H2>Error ') ) { | ||
// html error response | ||
complete = true | ||
var error = new Error('error') | ||
data.replace( /<H1>([^<]+)<\/H1>\n<H2>Error (\d+)<\/H2>/, function( s, reason, code ) { | ||
error.origin = 'api' | ||
error.details = { | ||
internalReason: reason, | ||
code: code | ||
} | ||
}) | ||
} else { | ||
// not json | ||
complete = true | ||
error = new Error('not json') | ||
error.origin = 'api' | ||
} | ||
// parse error | ||
if( error && error.origin === 'api' && error.message === 'error' ) { | ||
var errorDetails = error.details | ||
if( | ||
error.details[0] !== undefined | ||
&& error.details[0].code !== undefined | ||
&& error.details[0].code === 'ResourceNotFoundException' | ||
) { | ||
complete = true | ||
error = new Error('not found') | ||
error.origin = 'method' | ||
error.details = errorDetails | ||
} else if( error.details.code == 403 ) { | ||
complete = true | ||
error = new Error('not allowed') | ||
error.origin = 'method' | ||
error.details = errorDetails | ||
} else if( error.details.message === 'Invalid id' ) { | ||
complete = true | ||
error = new Error('invalid id') | ||
error.origin = 'method' | ||
error.details = errorDetails | ||
} else if( error.details[0] && error.details[0].internalReason === 'Developer key required for this operation' ) { | ||
complete = true | ||
error = new Error('developer key missing') | ||
error.origin = 'api' | ||
error.details = errorDetails | ||
} | ||
} | ||
// parse response | ||
if( data.totalItems !== undefined && data.totalItems == 0 ) { | ||
complete = true | ||
error = new Error('not found') | ||
error.origin = 'method' | ||
} else if( | ||
data.feed !== undefined | ||
&& data.feed['openSearch$totalResults'] !== undefined | ||
&& data.feed['openSearch$totalResults']['$t'] !== undefined | ||
&& data.feed['openSearch$totalResults']['$t'] == 0 | ||
) { | ||
complete = true | ||
error = new Error('not found') | ||
error.origin = 'method' | ||
} | ||
// do callback | ||
cb( error, data ) | ||
}) | ||
// early disconnect | ||
response.on( 'close', function() { | ||
if( ! complete ) { | ||
complete = true | ||
var err = new Error( 'connection closed' ) | ||
err.origin = 'api' | ||
cb( err ) | ||
} | ||
}) | ||
}) | ||
// no endless waiting | ||
request.setTimeout( app.timeout, function() { | ||
if( ! complete ) { | ||
complete = true | ||
var err = new Error('request timeout') | ||
err.origin = 'request' | ||
cb( err ) | ||
request.destroy() | ||
} | ||
}) | ||
// connection error | ||
request.on( 'error', function( error ) { | ||
if( ! complete ) { | ||
complete = true | ||
var err = new Error( 'connection error' ) | ||
err.origin = 'request' | ||
err.details = error | ||
cb( err ) | ||
} | ||
}) | ||
// perform and finish request | ||
request.end() | ||
} | ||
app.talk = function (path, fields, cb, oldJsonKey) { | ||
var complete = false; | ||
// fix callback | ||
if (!cb && typeof fields === 'function') { | ||
cb = fields; | ||
fields = {}; | ||
} | ||
// fix fields | ||
if (!fields || typeof fields !== 'object') { | ||
fields = {}; | ||
} | ||
// force JSON-C and version | ||
fields.alt = oldJsonKey ? 'json' : 'jsonc'; | ||
fields.v = 2; | ||
// prepare | ||
var options = { | ||
hostname: 'gdata.youtube.com', | ||
path: '/'+ path +'?'+ querystring.stringify (fields), | ||
headers: { | ||
'User-Agent': 'youtube-feeds.js (https://github.com/fvdm/nodejs-youtube)', | ||
'Accept': 'application/json', | ||
'GData-Version': '2' | ||
}, | ||
method: 'GET' | ||
}; | ||
// use X-GData-Key instead of adding it to the url, as per http://goo.gl/HEiCj | ||
// basically more secure in headers than in query string | ||
if (fields.key || app.developerKey) { | ||
options.headers['X-GData-Key'] = 'key=' + (fields.key || app.developerKey); | ||
delete fields.key; | ||
} | ||
var http = require ('http'); | ||
if (app.httpProtocol === 'https') { | ||
http = require ('https'); | ||
} | ||
var request = http.request (options); | ||
// response | ||
request.on ('response', function (response) { | ||
var data = []; | ||
var size = 0; | ||
response.on ('data', function (chunk) { | ||
data.push (chunk); | ||
size += chunk.length; | ||
}); | ||
response.on ('end', function () { | ||
if (complete) { | ||
return; | ||
} else { | ||
complete = true; | ||
} | ||
// process buffer and clear mem | ||
data = new Buffer.concat (data, size).toString ('utf8').trim (); | ||
var error = null; | ||
// validate | ||
if (data.match (/^(\{.*\}|\[.*\])$/)) { | ||
// ok | ||
data = JSON.parse (data); | ||
if (data.data) { | ||
data = data.data; | ||
} else if (data.error) { | ||
complete = true; | ||
error = new Error ('error'); | ||
error.origin = 'api'; | ||
error.details = data.error; | ||
} else if (oldJsonKey) { | ||
if (!data[oldJsonKey]) { | ||
complete = true; | ||
error = new Error ('invalid response'); | ||
error.origin = 'api'; | ||
} else { | ||
data = data[oldJsonKey]; | ||
} | ||
} | ||
} else if (data.match (/^<errors .+<\/errors>$/) || data.match (/^<\?xml version.+<\/errors>$/)) { | ||
data = xml2json.parser (data); | ||
// fix for JSONC compatibility | ||
complete = true; | ||
error = new Error ('error'); | ||
error.origin = 'api'; | ||
error.details = data.errors.error ? [data.errors.error] : data.errors; | ||
error.details.forEach (function (err, errk) { | ||
if (err.internalreason) { | ||
error.details[errk].internalReason = err.internalreason; | ||
delete error.details[errk].internalreason; | ||
} | ||
}); | ||
} else if (data.match (/<H2>Error /)) { | ||
// html error response | ||
complete = true; | ||
error = new Error ('error'); | ||
data.replace (/<H1>([^<]+)<\/H1>\n<H2>Error (\d+)<\/H2>/, function (s, reason, code) { | ||
error.origin = 'api'; | ||
error.details = { | ||
internalReason: reason, | ||
code: code | ||
}; | ||
}); | ||
} else { | ||
// not json | ||
complete = true; | ||
error = new Error ('not json'); | ||
error.origin = 'api'; | ||
} | ||
// parse error | ||
if (error && error.origin === 'api' && error.message === 'error') { | ||
var errorDetails = error.details; | ||
if (error.details[0] && error.details[0].code && error.details[0].code === 'ResourceNotFoundException') { | ||
complete = true; | ||
error = new Error ('not found'); | ||
error.origin = 'method'; | ||
error.details = errorDetails; | ||
} else if (error.details.code === 403) { | ||
complete = true; | ||
error = new Error ('not allowed'); | ||
error.origin = 'method'; | ||
error.details = errorDetails; | ||
} else if (error.details.message === 'Invalid id') { | ||
complete = true; | ||
error = new Error ('invalid id'); | ||
error.origin = 'method'; | ||
error.details = errorDetails; | ||
} else if (error.details[0] && error.details[0].internalReason === 'Developer key required for this operation') { | ||
complete = true; | ||
error = new Error ('developer key missing'); | ||
error.origin = 'api'; | ||
error.details = errorDetails; | ||
} | ||
} | ||
// parse response | ||
if (typeof data.totalItems !== 'undefined' && data.totalItems === 0) { | ||
complete = true; | ||
error = new Error ('not found'); | ||
error.origin = 'method'; | ||
} else if (data.feed && data.feed.openSearch$totalResults && data.feed.openSearch$totalResults.$t && data.feed.openSearch$totalResults.$t === 0) { | ||
complete = true; | ||
error = new Error ('not found'); | ||
error.origin = 'method'; | ||
} | ||
// do callback | ||
cb (error, data); | ||
}); | ||
// early disconnect | ||
response.on ('close', function () { | ||
if (!complete) { | ||
complete = true; | ||
var err = new Error ('connection closed'); | ||
err.origin = 'api'; | ||
cb (err); | ||
} | ||
}); | ||
}); | ||
// no endless waiting | ||
request.setTimeout (parseInt (app.timeout), function () { | ||
if (!complete) { | ||
complete = true; | ||
var err = new Error ('request timeout'); | ||
err.origin = 'request'; | ||
cb (err); | ||
request.destroy (); | ||
} | ||
}); | ||
// connection error | ||
request.on ('error', function (error) { | ||
if (!complete) { | ||
complete = true; | ||
var err = new Error ('connection error'); | ||
err.origin = 'request'; | ||
err.details = error; | ||
cb (err); | ||
} | ||
}); | ||
// perform and finish request | ||
request.end(); | ||
}; | ||
// ready | ||
module.exports = app | ||
module.exports = app; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
24185
6
376
412
3
3