-
Notifications
You must be signed in to change notification settings - Fork 78
Expand file tree
/
Copy pathproviders.js
More file actions
156 lines (130 loc) · 4.13 KB
/
providers.js
File metadata and controls
156 lines (130 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/* eslint-disable global-require */
/* eslint-disable consistent-return */
const fs = require('fs');
const { join } = require('path');
const common = require('./common');
const logger = common.logger.prefix('providers');
const { exceptions } = common;
const hooks = require('./hooks');
const providersPath = join(__dirname, 'providers');
let reports;
let gettersList;
const files = [];
const callbacks = {};
const loadProvider = (name) => {
try {
// eslint-disable-next-line import/no-dynamic-require, global-require
return require(join(providersPath, name));
} catch (e) {
hooks.trigger('error', e);
}
};
/**
* Given a provider module, check its keys for a functions that begin with 'get'
* and return an array of {name, func} pairs, so that the function
* can be called applied to its module.
* */
const findGetters = (module) => Object.keys(module)
.filter((k) => k.startsWith('get') && typeof module[k] === 'function')
.map((k) => ({ name: k.slice(4), fn: module[k] }));
/**
* Traverse the providers path and extract all getter function into the getters
* object. Function name used as index has the get_ stripped.
* */
exports.map = (cb) => {
if (gettersList) return cb(null, gettersList);
fs.readdir(providersPath, (err, filesData) => {
if (err) return cb(err);
gettersList = {};
filesData.forEach((providerName) => {
const module = loadProvider(providerName);
if (!module) return;
findGetters(module).forEach((getter) => {
gettersList[getter.name] = getter.fn;
});
});
cb(null, gettersList);
});
};
/**
* providers.get takes care of calling a specified provider and either
* calling the callback with the result or firing an error or data event
* depending on the result.
*
* Examples:
* get('users', callback)
* get('tree', {user: 'someuser'})
* get('tree', {user: 'someuser'}, callback)
*/
function get(nameGet, extra, extra2, timesLimit = 0) {
let name = nameGet;
let cb;
// eslint-disable-next-line prefer-rest-params
const args = arguments;
let options;
if (typeof (extra) === 'function') cb = extra;
else {
options = extra;
// in case there's a third argument and it's a cb
if (args[2] && typeof (args[2]) === 'function') [, cb] = args;
}
const fireCallbacks = (nameCalled, err, result) => {
const list = callbacks[nameCalled];
callbacks[nameCalled] = [];
list.forEach((fn) => {
fn(err, result, nameCalled, timesLimit + 1);
});
};
if (name === 'report') name = 'stolen';
// eslint-disable-next-line array-callback-return
exports.map((err, getters) => {
if (err && cb) return cb(err, null, null, timesLimit + 1);
if (getters[name]) {
callbacks[name] = callbacks[name] || [];
if (cb) callbacks[name].push(cb);
if (callbacks[name].length > 1 && name !== 'location') {
return logger.info(`${name} already in progress.`);
}
logger.debug(`Fetching ${name}`);
const getterCb = (errCb, result) => {
fireCallbacks(name, errCb, result);
if (!cb) { // only emit when no callback passed
if (errCb) hooks.trigger('error', errCb);
else hooks.trigger('data', name, result);
}
if (result && result.file && result.content_type) {
files.push(result.file); // keep a record so we remove it afterwards
}
};
try {
if (options) {
getters[name](options, getterCb);
} else {
getters[name](getterCb);
}
} catch (e) {
logger.error(`Error executing provider '${name}': ${e.message}`);
exceptions.send(e);
getterCb(e);
}
} else {
if (!reports) reports = require('./reports');
reports.get(name, args[1], args[2], timesLimit); // pass original arguments
}
});
}
exports.remove_files = (cb) => {
let lastError;
let count = files.length;
const done = (err) => {
if (err) lastError = err;
count -= 1;
if (count <= 0) {
if (typeof cb === 'function') return cb(lastError);
}
};
files.forEach((entry) => {
if (entry.file && entry.content_type) fs.unlink(entry.file, done);
});
};
exports.get = get;