11const BasicAPI = require ( './basic' )
2+ const path = require ( 'path' )
23
34// default logger if none is provided in the opts object to _setup
45const defaultLog = { }
56defaultLog . error = defaultLog . info = defaultLog . debug = defaultLog . warn = console . log
67const defaultTimeout = 30000
78
89module . exports = {
9- setup : _setup
10+ setup
1011}
1112
1213// populate an object with all api configurations and paths
13- function _setup ( apis , keys , opts ) {
14+ function setup ( apisConfig , apisKeyConfig , opts ) {
15+ if ( ! apisConfig || typeof apisConfig !== 'object' ) {
16+ throw new Error ( 'Apis config is required.' )
17+ }
18+ if ( ! apisKeyConfig ) apisKeyConfig = { }
1419 if ( ! opts ) opts = { }
15- const endpoints = _createClients ( apis , keys )
20+ if ( ! opts . log ) opts . log = defaultLog
21+ if ( ! opts . timeout ) opts . timeout = defaultTimeout
1622 const output = { }
1723
18- _getPaths ( endpoints , opts )
19- . then ( ( results ) => {
20- results . forEach ( ( endpoint ) => {
21- if ( endpoint ) {
22- output [ endpoint . key ] = endpoint
23- }
24-
24+ const apis = createApis ( apisConfig , apisKeyConfig )
25+
26+ const connectedApis = apis
27+ . filter ( api => api . paths )
28+ . map ( api => {
29+ api . connected = true
30+ return Promise . resolve ( api )
2531 } )
26- return output
27- } )
28- . then ( clients => {
29- return configureApiCache ( clients , opts )
30- } )
31- . catch ( ( err ) => {
32- throw err
33- } )
32+
33+ const apisWithoutPaths = apis . filter ( api => ! api . paths )
34+ const remoteConnectedApis = getPathsRemote ( apisWithoutPaths , opts )
35+
36+ const allConnectedApis = Promise . all ( remoteConnectedApis . concat ( connectedApis ) )
37+
38+ allConnectedApis
39+ . then ( ( connectedApis ) => {
40+ connectedApis . forEach ( ( connectedApi ) => {
41+ if ( connectedApi ) {
42+ if ( opts . checkAPIs ) {
43+ checkAPI ( connectedApi , opts . log )
44+ }
45+ configureApiCache ( connectedApi , opts )
46+ output [ connectedApi . key ] = connectedApi
47+ }
48+ } )
49+ opts . log . info ( 'API setup done.' + JSON . stringify ( connectedApis ) )
50+ } )
51+ . catch ( err => {
52+ opts . log . error ( `API setup failed: ${ err . stack } ` )
53+ process . exit ( 1 )
54+ } )
55+
3456 return output
3557}
3658
3759// check API key and kill if api is required
38- function _checkAPI ( endpoint , log ) {
39- const config = endpoint . config
40- const apiName = endpoint . key
41- const uri = `${ config . proxyBasePath } /_checkAPIkey` // get the proxyBasePath eg. api/publications
42- endpoint . client . getAsync ( { uri : uri } )
43- . then ( res => {
44- if ( res . statusCode === 401 ) {
45- log . error ( 'Bad API key for ' + apiName )
60+ function checkAPI ( api , log ) {
61+ const config = api . config
62+ const apiName = api . key
63+
64+ const statusCheckPath = api . config . statusCheckPath || '_checkAPIkey'
65+ const uri = path . join ( config . proxyBasePath , statusCheckPath )
66+ api . client . getAsync ( { uri} )
67+ . then ( res => {
68+ if ( config . useApiKey !== false ) {
69+ if ( res . statusCode === 401 ) {
70+ throw new Error ( `Bad API key for ${ apiName } ` )
71+ } else if ( res . statusCode === 404 ) {
72+ throw new Error ( `Check API functionality not implemented on ${ apiName } ` )
73+ } else if ( res . statusCode === 500 ) {
74+ throw new Error ( `Got 500 response on checkAPI call, most likely a bad API key for ${ apiName } ` )
75+ }
76+ } else {
77+ if ( res . statusCode < 200 || res . statusCode >= 300 ) {
78+ throw new Error ( `API check failed for ${ apiName } , got status ${ res . statusCode } ` )
79+ }
80+ }
81+ } )
82+ . catch ( err => {
83+ log . error ( `Error while checking API: ${ err . message } ` )
4684 if ( config . required ) {
47- log . error ( 'Required API misconfigured , EXITING' )
85+ log . error ( 'Required API call failed , EXITING' )
4886 process . exit ( 1 )
4987 }
50- } else if ( res . statusCode === 404 ) {
51- log . error ( 'Check API functionality not implemented on ' + apiName )
52- } else if ( res . statusCode === 500 ) {
53- log . error ( 'Got 500 response on checkAPI call, most likely a bad API key for ' + apiName )
54- }
55- } )
88+ } )
5689}
5790
5891// unpack nodeApi:s and pair with keys, returns BasicAPI objects
59- function _createClients ( nodeApi , apiKey ) {
60- return Object . keys ( nodeApi ) . map ( ( key ) => {
61- const api = nodeApi [ key ]
92+ function createApis ( apisConfig , apisKeyConfig ) {
93+ return Object . keys ( apisConfig ) . map ( ( key ) => {
94+ const apiConfig = apisConfig [ key ]
6295 const opts = {
63- hostname : api . host ,
64- port : api . port ,
65- https : api . https ,
66- headers : {
67- 'api_key' : apiKey [ key ]
68- } ,
96+ hostname : apiConfig . host ,
97+ port : apiConfig . port ,
98+ https : apiConfig . https ,
6999 json : true ,
70- defaultTimeout : api . defaultTimeout
100+ defaultTimeout : apiConfig . defaultTimeout
71101 }
72- return {
102+
103+ if ( apiConfig . useApiKey !== false ) {
104+ const k = apisKeyConfig [ key ]
105+ if ( ! k ) throw new Error ( `nodeApi ${ key } has no api key set.` )
106+
107+ opts . headers = {
108+ 'api_key' : apisKeyConfig [ key ]
109+ }
110+ }
111+
112+ const api = {
73113 key : key ,
74- config : api ,
114+ config : apiConfig ,
75115 connected : false ,
76116 client : new BasicAPI ( opts )
77117 }
118+
119+ if ( apiConfig . paths && typeof apiConfig . paths === 'object' ) {
120+ api . paths = apiConfig . paths
121+ }
122+
123+ return api
78124 } )
79125}
80126
81- // get connect to all configured api endpoints
82- function _getPaths ( endpoints , opts ) {
83- if ( ! opts . log ) opts . log = defaultLog
84- if ( ! opts . timeout ) opts . timeout = defaultTimeout
85- const tasks = endpoints . map ( ( api ) => { // for each endpoint
86- return _connect ( api , opts )
127+ // retrieve paths from remote /_paths endpoint
128+ function getPathsRemote ( apis , opts ) {
129+ const connectedApiPromises = apis . map ( ( api ) => {
130+ return connect ( api , opts )
87131 } )
88- return Promise . all ( tasks )
132+ return connectedApiPromises
89133}
90134
91- // connect to an api endpoint and get _paths
92- function _connect ( api , opts ) {
135+ // get all api-paths from the / _paths endpoint
136+ function connect ( api , opts ) {
93137 const uri = `${ api . config . proxyBasePath } /_paths` // get the proxyBasePath eg. api/publications
94138 return api . client . getAsync ( uri ) // return the api paths for the api
95- . then ( ( data ) => {
96- if ( data . statusCode === 200 ) {
97- api . paths = data . body . api
98- api . connected = true
99- opts . log . info ( 'Connected to api: ' + api . key )
100- if ( opts . checkAPIs ) {
101- _checkAPI ( api , opts . log || defaultLog )
139+ . then ( ( data ) => {
140+ if ( data . statusCode === 200 ) {
141+ api . paths = data . body . api
142+ api . connected = true
143+ opts . log . info ( 'Connected to api: ' + api . key )
144+ return api
145+ } else {
146+ throw new Error ( data . statusCode + ' We can\'t access this API server. Check path and keys' )
102147 }
148+ } )
149+ . catch ( ( err ) => {
150+ opts . log . error ( { err : err } , 'Failed to get API paths from API: ' , api . key , 'host: ' , api . config . host , ' proxyBasePath: ' , api . config . proxyBasePath )
151+ setTimeout ( function ( ) {
152+ opts . log . info ( 'Reconnecting to api: ' + api . key )
153+ connect ( api , opts )
154+ } , opts . timeout )
103155 return api
104- } else {
105- throw new Error ( data . statusCode + ' We can\'t access this API server. Check path and keys' )
106- }
107- } )
108- . catch ( ( err ) => {
109- opts . log . error ( { err : err } , 'Failed to get API paths from API: ' , api . key , 'host: ' , api . config . host , ' proxyBasePath: ' , api . config . proxyBasePath )
110- setTimeout ( function ( ) {
111- opts . log . info ( 'Reconnecting to api: ' + api . key )
112- _connect ( api , opts )
113- } , opts . timeout )
114- return api
115- } )
156+ } )
116157}
117158
118159// configure caching if specified in opts object
119- function configureApiCache ( clients , opts ) {
120- let log = defaultLog
121- if ( opts . log ) log = opts . log
122-
123- Object . keys ( clients ) . map ( apiName => {
124- if ( _getRedisConfig ( apiName , opts . cache ) ) {
125- _getRedisClient ( apiName , opts )
126- . then ( redisClient => {
127- clients [ apiName ] . client . _hasRedis = true
128- clients [ apiName ] . client . _redis = {
129- prefix : apiName ,
130- client : redisClient ,
131- expire : _getRedisConfig ( apiName , opts . cache ) . expireTime
132- }
133- } )
134- . catch ( err => {
135- log . error ( 'Unable to create redisClient' , { error : err } )
136- clients [ apiName ] . client . _hasRedis = false
137- } )
138- log . debug ( `API configured to use redis cache: ${ apiName } ` )
139- }
140- } )
141-
142- return clients
160+ function configureApiCache ( connectedApi , opts ) {
161+ const apiName = connectedApi . key
162+ if ( getRedisConfig ( apiName , opts . cache ) ) {
163+ getRedisClient ( apiName , opts )
164+ . then ( redisClient => {
165+ connectedApi . client . _hasRedis = true
166+ connectedApi . client . _redis = {
167+ prefix : apiName ,
168+ client : redisClient ,
169+ expire : getRedisConfig ( apiName , opts . cache ) . expireTime
170+ }
171+ } )
172+ . catch ( err => {
173+ opts . log . error ( 'Unable to create redisClient' , { error : err } )
174+ connectedApi . client . _hasRedis = false
175+ } )
176+ opts . log . debug ( `API configured to use redis cache: ${ apiName } ` )
177+ }
178+ return connectedApi
143179}
144180
145181/*
146182 * Check if there is a cache configured for this api
147183 */
148- function _getRedisConfig ( apiName , cache ) {
184+ function getRedisConfig ( apiName , cache ) {
149185 if ( cache && cache [ apiName ] ) {
150186 return cache [ apiName ]
151187 }
@@ -157,17 +193,16 @@ function _getRedisConfig (apiName, cache) {
157193 * where the public URL is published.
158194 * Will download api specification from api and expose its methods internally under "/api" as paths objects
159195 */
160- function _getRedisClient ( apiName , opts ) {
196+ function getRedisClient ( apiName , opts ) {
161197 let cache = opts . cache ? opts . cache : { }
162198 let redis = opts . redis
163- let log = opts . log || defaultLog
164199 try {
165200 if ( cache [ apiName ] ) {
166- const cacheConfig = _getRedisConfig ( apiName , cache )
201+ const cacheConfig = getRedisConfig ( apiName , cache )
167202 return redis ( apiName , cacheConfig . redis )
168203 }
169204 } catch ( err ) {
170- log . error ( 'Error creating Redis client' , err )
205+ opts . log . error ( 'Error creating Redis client' , err )
171206 }
172207
173208 return Promise . reject ( false )
0 commit comments