Skip to content

Commit 3730048

Browse files
Simon Wikandersimplish
authored andcommitted
Changes for StudAdm
* Imroved naming in connections.js and changed default value handling * Made api key optional for clients * Made it possible to get paths either from remote _path endpoint OR from the config file. * Made it possible to set a status-check-path in the config file.
1 parent 604c6ae commit 3730048

File tree

3 files changed

+139
-105
lines changed

3 files changed

+139
-105
lines changed

connections.js

Lines changed: 137 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,187 @@
11
const BasicAPI = require('./basic')
2+
const path = require('path')
23

34
// default logger if none is provided in the opts object to _setup
45
const defaultLog = {}
56
defaultLog.error = defaultLog.info = defaultLog.debug = defaultLog.warn = console.log
67
const defaultTimeout = 30000
78

89
module.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)

index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
// To keep backward compatibility
2-
module.exports = require('./api')
31
module.exports = {
2+
api: require('./api'),
43
Connections: require('./connections'),
54
oidcApi: require('./oidcApi'),
65
cachedApi: require('./cachedApi'),

oidcApi.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ module.exports = (function () {
156156
onError(err)
157157
}
158158
159-
onSuccess(json);*/
159+
onSuccess(json); */
160160
})
161161
}
162162
}

0 commit comments

Comments
 (0)