-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathindex.js
215 lines (204 loc) · 6.52 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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
const FeatureServer = require('featureserver')
const koopConfig = require("config");
const Logger = require('@koopjs/logger')
const log = new Logger()
console.log('WARNING: "/MapServer" routes will be registered, but only for specialized 404 handling in FeatureServer.')
function Geoservices () {}
/**
* Helper for sending error responses
* @param {*} req
* @param {*} res
*/
function sendError(req, res, error) {
if (!error.error) {
const err = normalizeError(error)
if (err.code === 401) FeatureServer.error.authentication(req, res)
else res.status(err.code || 500).json({ error: err.message })
// if the error is already in the esri REST API format send back with 200 code, e.g.
// {
// "error" : {
// "code": 499,
// "message":"Token Required",
// "details":[]
// }
// }
// This is required for ArcGIS Enterprise apps to handle many errors
} else res.status(200).json(error);
}
/**
* Helper for pulling data and routing to FeatureServer
* @param {object} model provider's model
* @param {object} req request object
* @param {object} res response object
*/
function pullDataAndRoute (model, req, res) {
model.pull(req, function (error, data) {
if (error) sendError(req, res, error)
else FeatureServer.route(req, res, data)
})
}
/**
* Handler for service, layer, and query routes
* @param {object} req request object
* @param {object} res response object
*/
Geoservices.prototype.featureServer = function (req, res) {
// Is model configured for token-authorization?
if (typeof this.model.authorize === 'function') {
this.model.authorize(req)
.then(valid => {
// model will be available when this is instantiated with the Koop controller
pullDataAndRoute(this.model, req, res)
})
.catch(error => {
const err = normalizeError(error)
if (err.code === 401) FeatureServer.error.authorization(req, res)
else res.status(err.code || 500).json({ error: err.message })
})
} else {
pullDataAndRoute(this.model, req, res)
}
}
/**
* Handler for the $namepace/rest/info route. Inspects model for authentation info and passes any on to the
* FeatureServer handler
* @param {object} req request object
* @param {object} res response object
*/
Geoservices.prototype.featureServerRestInfo = function (req, res) {
const authInfo = koopConfig && koopConfig.authInfo || {};
const authSpec = this.model.authenticationSpecification
if (authSpec) {
authInfo.isTokenBasedSecurity = true
// Use https by default, unless KOOP_AUTH_HTTP or authSpec.useHttp are defined and set to true
const protocol = (authSpec.useHttp === true || process.env.KOOP_AUTH_HTTP === 'true') ? 'http' : 'https'
authInfo.tokenServicesUrl = `${protocol}://${req.headers.host}${req.baseUrl}/${authSpec.provider}/tokens/`
}
FeatureServer.route(req, res, { authInfo })
}
/**
* Handler for $namespace/authenticate route. Passes request and response object to the model's "authenticate" function
* @param {object} req request object
* @param {object} res response object
*/
Geoservices.prototype.generateToken = function (req, res) {
// Is model configured for authentication?
if (typeof this.model.authenticate === 'function') {
this.model.authenticate(req)
.then(tokenJson => {
FeatureServer.authenticate(res, tokenJson)
})
.catch(error => {
const err = normalizeError(error)
if (err.code === 401) FeatureServer.error.authentication(req, res)
else res.status(err.code || 500).json({ error: err.message })
})
} else {
res.status(500).json({ error: '"authenticate" not implemented for this provider' })
}
}
function normalizeError (error) {
const { code, message, stack } = error
let normalizedErrorCode = code
if (code === 'COM_0019') {
normalizedErrorCode = 401
} else if (typeof code !== 'number') {
normalizedErrorCode = 500
}
if (normalizedErrorCode === 500) {
// Log error then make generic for response
log.error('error', error)
return { message: 'Internal Server Error', code: 500}
}
return { message, stack, code: normalizedErrorCode }
}
/**
* Collection of route objects that define geoservices
*
* These routes are bound to the Koop API for each provider. Note that FeatureServer,
* FeatureServer/layers, FeatureServer/:layer, and FeatureServer/:layer/:method are found
* in the collection with and without the "$namespace/rest/services/$providerParams" prefix.
* These prefixed routes have been added due to some clients requiring the "rest/services"
* URL fragment in geoservices routes. The $namespace and $providerParams are placeholders
* that koop-core replaces with provider-specific settings.
*/
Geoservices.routes = [
{
path: '$namespace/rest/info',
methods: ['get', 'post'],
handler: 'featureServerRestInfo'
},
{
path: '$namespace/tokens/:method',
methods: ['get', 'post'],
handler: 'generateToken'
},
{
path: '$namespace/tokens/',
methods: ['get', 'post'],
handler: 'generateToken'
},
{
path: '$namespace/rest/services/$providerParams/FeatureServer/:layer/:method',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: '$namespace/rest/services/$providerParams/FeatureServer/layers',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: '$namespace/rest/services/$providerParams/FeatureServer/:layer',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: '$namespace/rest/services/$providerParams/FeatureServer',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: 'FeatureServer/:layer/:method',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: 'FeatureServer/layers',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: 'FeatureServer/:layer',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: 'FeatureServer',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: '$namespace/rest/services/$providerParams/FeatureServer*',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: 'FeatureServer*',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: '$namespace/rest/services/$providerParams/MapServer*',
methods: ['get', 'post'],
handler: 'featureServer'
},
{
path: 'MapServer*',
methods: ['get', 'post'],
handler: 'featureServer'
}
]
Geoservices.type = 'output'
Geoservices.version = require('./package.json').version
module.exports = Geoservices