-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
137 lines (119 loc) · 4.36 KB
/
index.js
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
const yaml = require('js-yaml');
const fs = require('fs');
const info = require('./package.json');
const BBPromise = require('bluebird');
const Err = require('@kartotherian/err');
const postgres = require('pg-promise')({ promiseLib: BBPromise });
const GeoShapes = require('./lib/geoshapes');
/**
* Web server (express) route handler to get geoshapes
* @param {string} type
* @param {object} req request object
* @param {object} req.query request object's query
* @param {object} res response object
* @param {Function} next will be called if request is not handled
*/
function handler(core, config, type, req, res, next) {
const start = Date.now();
let geoshape;
return BBPromise.try(() => {
geoshape = new GeoShapes(type, req.query, config);
const lowerHeaders = Object.keys(req.headers).reduce((newHeaders, key) => {
newHeaders[key.toLowerCase()] = req.headers[key];
return newHeaders;
}, {});
return geoshape.execute(lowerHeaders['x-client-ip']);
}).then((geodata) => {
core.setResponseHeaders(res);
res.type('application/vnd.geo+json').json(geodata);
core.metrics.endTiming(geoshape.metric, start);
}).catch(err => core.reportRequestError(err, res)).catch(next);
}
function loadServiceConfig(core) {
return BBPromise.try(() => {
const config = core.getConfiguration().geoshapes;
// TODO: we shouldn't use it directly,
// but instead refactor out mwapi lib from node_service_template
// and use the proper host
const mwapi = core.getConfiguration().mwapi_req;
config.mwapi = (mwapi && mwapi.uri) || 'https://en.wikipedia.org/w/api.php';
// Load queries from yaml file
config.queries = yaml.load(fs.readFileSync(`${__dirname}/queries.yaml`, 'utf8'));
return config;
});
}
function loadDBHandler(config) {
return BBPromise.try(() => {
if (!config.database || !/^[a-zA-Z][a-zA-Z0-9]*$/.test(config.database)) {
throw new Err('"geoshapes" parameters must specify "database"');
}
config.db = postgres({
host: config.host,
port: config.port,
database: config.database,
user: config.user,
password: config.password,
});
return config;
});
}
function checkValidStructure(config) {
// Check the valid structure of the table - use invalid id
return BBPromise.all([
new GeoShapes('geoshape', { ids: 'Q123456789' }, config).execute(),
new GeoShapes('geoline', { ids: 'Q123456789' }, config).execute(),
]).then(() => config);
}
function initService(config) {
return BBPromise.try(() => {
const userAgent = `${info.name}/${info.version} (https://mediawiki.org/Maps)`;
if (!config) {
throw new Err('"geoshapes" parameter block is not set up in the config');
}
if (!config.wikidataQueryService) {
throw new Err('"geoshapes" parameters must specify "wikidataQueryService"');
}
config.sparqlHeaders = {
'User-Agent': userAgent,
Accept: 'application/sparql-results+json',
};
config.mwapiHeaders = {
'User-Agent': userAgent,
Host: 'en.wikipedia.org',
};
config.maxidcount = config.maxidcount !== undefined ? parseInt(config.maxidcount, 10) : 500;
if (config.maxidcount <= 0) {
throw new Err('"geoshapes.maxidcount" must be a positive integer');
}
// Which query to use by default
const defaultQ = config.queries.simplifyarea;
if (config.allowUserQueries) {
config.queries.default = defaultQ;
} else {
// Delete all queries except the default one, and remove parameter
// names to prevent user parameters
config.queries = { default: defaultQ };
if (defaultQ.params) {
defaultQ.params.forEach((param) => {
delete param.name;
});
}
}
return config;
});
}
module.exports = (core, router) => {
loadServiceConfig(core)
.then(loadDBHandler)
.then(initService)
.then(checkValidStructure)
.then((config) => { // Load routes
router.get('/shape', (req, res, next) => handler(core, config, 'geoshape', req, res, next)); // obsolete
router.get('/geoshape', (req, res, next) => handler(core, config, 'geoshape', req, res, next));
router.get('/geoline', (req, res, next) => handler(core, config, 'geoline', req, res, next));
})
.catch((err) => {
core.log('error', `geoshapes support failed to load, skipping: ${err}\n${err.stack}`);
// still allow loading
});
};