Skip to content

Commit a636930

Browse files
committed
fix(geocoder): do not send Pelias query sources by default
Fixes ibi-group/trimet-mod-otp#239
1 parent 1b32d03 commit a636930

File tree

3 files changed

+160
-31
lines changed

3 files changed

+160
-31
lines changed

__tests__/util/__snapshots__/geocoder.js.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,6 @@ Object {
215215
},
216216
"isomorphicMapzenSearchQuery": Object {
217217
"api_key": "dummy-mapzen-key",
218-
"sources": "gn,oa,osm,wof",
219218
"text": "Mill Ends",
220219
},
221220
"type": "FeatureCollection",

__tests__/util/geocoder.js

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import nock from 'nock'
22

3-
import getGeocoder from '../../lib/util/geocoder'
3+
import getGeocoder, { PeliasGeocoder } from '../../lib/util/geocoder'
44

55
function mockResponsePath (geocoder, file) {
66
return `__tests__/test-utils/fixtures/geocoding/${geocoder}/${file}`
@@ -118,6 +118,71 @@ describe('geocoder', () => {
118118
const result = await getGeocoder(geocoder).getLocationFromGeocodedFeature(mockFeature)
119119
expect(result).toMatchSnapshot()
120120
})
121+
122+
// geocoder-specific tests
123+
if (geocoderType === 'PELIAS') {
124+
const mockSources = 'gn,oa,osm,wof'
125+
126+
// sources should not be sent unless they are explicitly defined in the
127+
// query. See https://github.com/ibi-group/trimet-mod-otp/issues/239
128+
it('should not send sources in autocomplete by default', () => {
129+
// create mock API to check query
130+
const mockPeliasAPI = {
131+
autocomplete: query => {
132+
expect(query.sources).not.toBe(expect.anything())
133+
return Promise.resolve()
134+
}
135+
}
136+
const pelias = new PeliasGeocoder(mockPeliasAPI, geocoder)
137+
pelias.autocomplete({ text: 'Mill Ends' })
138+
})
139+
140+
// should send sources if they're defined in the config
141+
it('should send sources in autocomplete if defined in config', () => {
142+
// create mock API to check query
143+
const mockPeliasAPI = {
144+
autocomplete: query => {
145+
expect(query.sources).toBe(mockSources)
146+
return Promise.resolve()
147+
}
148+
}
149+
const pelias = new PeliasGeocoder(
150+
mockPeliasAPI,
151+
{ ...geocoder, sources: mockSources }
152+
)
153+
pelias.autocomplete({ text: 'Mill Ends' })
154+
})
155+
156+
// sources should not be sent unless they are explicitly defined in the
157+
// query. See https://github.com/ibi-group/trimet-mod-otp/issues/239
158+
it('should not send sources in search by default', () => {
159+
// create mock API to check query
160+
const mockPeliasAPI = {
161+
search: query => {
162+
expect(query.sources).not.toBe(expect.anything())
163+
return Promise.resolve()
164+
}
165+
}
166+
const pelias = new PeliasGeocoder(mockPeliasAPI, geocoder)
167+
pelias.search({ text: 'Mill Ends' })
168+
})
169+
170+
// should send sources if they're defined in the config
171+
it('should send sources in search if defined in config', () => {
172+
// create mock API to check query
173+
const mockPeliasAPI = {
174+
search: query => {
175+
expect(query.sources).toBe(mockSources)
176+
return Promise.resolve()
177+
}
178+
}
179+
const pelias = new PeliasGeocoder(
180+
mockPeliasAPI,
181+
{ ...geocoder, sources: mockSources }
182+
)
183+
pelias.search({ text: 'Mill Ends' })
184+
})
185+
}
121186
})
122187
})
123188
})

lib/util/geocoder.js

Lines changed: 94 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,8 @@ class Geocoder {
2020
* address or POI, attempt to find possible matches.
2121
*/
2222
autocomplete (query) {
23-
const {apiKey, baseUrl, boundary, focusPoint} = this.geocoderConfig
24-
return this.api.autocomplete({
25-
apiKey,
26-
boundary,
27-
focusPoint,
28-
url: baseUrl ? `${baseUrl}/autocomplete` : undefined,
29-
...query
30-
}).then(this._rewriteAutocompleteResponse)
23+
return this.api.autocomplete(this.getAutocompleteQuery(query))
24+
.then(this.rewriteAutocompleteResponse)
3125
}
3226

3327
/**
@@ -50,46 +44,76 @@ class Geocoder {
5044
* GPS coordiante.
5145
*/
5246
reverse (query) {
47+
return this.api.reverse(this.getReverseQuery(query))
48+
.then(this.rewriteReverseResponse)
49+
}
50+
51+
/**
52+
* Perform a search query. A search query is different from autocomplete in
53+
* that it is assumed that the text provided is more or less a complete
54+
* well-fromatted address.
55+
*/
56+
search (query) {
57+
return this.api.search(this.getSearchQuery(query))
58+
.then(this.rewriteSearchResponse)
59+
}
60+
61+
/**
62+
* Default autocomplete query generator
63+
*/
64+
getAutocompleteQuery (query) {
65+
const {apiKey, baseUrl, boundary, focusPoint} = this.geocoderConfig
66+
return {
67+
apiKey,
68+
boundary,
69+
focusPoint,
70+
url: baseUrl ? `${baseUrl}/autocomplete` : undefined,
71+
...query
72+
}
73+
}
74+
75+
/**
76+
* Default reverse query generator
77+
*/
78+
getReverseQuery (query) {
5379
const {apiKey, baseUrl} = this.geocoderConfig
54-
return this.api.reverse({
80+
return {
5581
apiKey,
5682
format: true,
5783
url: baseUrl ? `${baseUrl}/reverse` : undefined,
5884
...query
59-
}).then(this._rewriteReverseResponse)
85+
}
6086
}
6187

6288
/**
63-
* Perform a search query. This query assumes that the text being searched
64-
* is more-or-less an exact address or POI.
89+
* Default search query generator.
6590
*/
66-
search (query) {
91+
getSearchQuery (query) {
6792
const {apiKey, baseUrl, boundary, focusPoint} = this.geocoderConfig
68-
return this.api.search({
93+
return {
6994
apiKey,
7095
boundary,
7196
focusPoint,
72-
sources: null,
7397
url: baseUrl ? `${baseUrl}/search` : undefined,
7498
format: false, // keep as returned GeoJSON,
7599
...query
76-
}).then(this._rewriteSearchResponse)
100+
}
77101
}
78102

79103
/**
80104
* Default rewriter for autocomplete responses
81105
*/
82-
_rewriteAutocompleteResponse (response) { return response }
106+
rewriteAutocompleteResponse (response) { return response }
83107

84108
/**
85109
* Default rewriter for reverse responses
86110
*/
87-
_rewriteReverseResponse (response) { return response }
111+
rewriteReverseResponse (response) { return response }
88112

89113
/**
90114
* Default rewriter for search responses
91115
*/
92-
_rewriteSearchResponse (response) { return response }
116+
rewriteSearchResponse (response) { return response }
93117
}
94118

95119
/**
@@ -118,7 +142,7 @@ class ArcGISGeocoder extends Geocoder {
118142
* Rewrite an autocomplete response into an application specific data format.
119143
* Also, filter out any results that are collections.
120144
*/
121-
_rewriteAutocompleteResponse (response) {
145+
rewriteAutocompleteResponse (response) {
122146
return {
123147
// remove any autocomplete results that are collections
124148
// (eg multiple Starbucks)
@@ -137,7 +161,7 @@ class ArcGISGeocoder extends Geocoder {
137161
* Rewrite the response into an application-specific data format using the
138162
* first feature returned from the geocoder.
139163
*/
140-
_rewriteReverseResponse (response) {
164+
rewriteReverseResponse (response) {
141165
const { features, query } = response
142166
const { lat, lon } = query
143167
return {
@@ -159,31 +183,31 @@ class NoApiGeocoder extends Geocoder {
159183
* Use coordinate string parser.
160184
*/
161185
autocomplete (query) {
162-
return this._parseCoordinateString(query.text)
186+
return this.parseCoordinateString(query.text)
163187
}
164188

165189
/**
166190
* Always return the lat/lon.
167191
*/
168192
reverse (query) {
169193
let { lat, lon } = query.point
170-
lat = this._roundGPSDecimal(lat)
171-
lon = this._roundGPSDecimal(lon)
194+
lat = this.roundGPSDecimal(lat)
195+
lon = this.roundGPSDecimal(lon)
172196
return Promise.resolve({ lat, lon, name: `${lat}, ${lon}` })
173197
}
174198

175199
/**
176200
* Use coordinate string parser.
177201
*/
178202
search (query) {
179-
return this._parseCoordinateString(query.text)
203+
return this.parseCoordinateString(query.text)
180204
}
181205

182206
/**
183207
* Attempt to parse the input as a GPS coordinate. If parseable, return a
184208
* feature.
185209
*/
186-
_parseCoordinateString (string) {
210+
parseCoordinateString (string) {
187211
let feature
188212
try {
189213
feature = {
@@ -201,7 +225,7 @@ class NoApiGeocoder extends Geocoder {
201225
return Promise.resolve({ features: [feature] })
202226
}
203227

204-
_roundGPSDecimal (number) {
228+
roundGPSDecimal (number) {
205229
const roundFactor = 100000
206230
return Math.round(number * roundFactor) / roundFactor
207231
}
@@ -211,14 +235,55 @@ class NoApiGeocoder extends Geocoder {
211235
* Geocoder implementation for the Pelias geocoder.
212236
* See https://pelias.io
213237
*
238+
* This is exported for testing purposes only.
239+
*
214240
* @extends Geocoder
215241
*/
216-
class PeliasGeocoder extends Geocoder {
242+
export class PeliasGeocoder extends Geocoder {
243+
/**
244+
* Generate an autocomplete query specifically for the Pelias API. The
245+
* `sources` parameter is a Pelias-specific option.
246+
*/
247+
getAutocompleteQuery (query) {
248+
const {apiKey, baseUrl, boundary, focusPoint, sources} = this.geocoderConfig
249+
return {
250+
apiKey,
251+
boundary,
252+
focusPoint,
253+
// explicitly send over null for sources if provided sources is not truthy
254+
// in order to avoid default isomorphic-mapzen-search sources form being
255+
// applied
256+
sources: sources || null,
257+
url: baseUrl ? `${baseUrl}/autocomplete` : undefined,
258+
...query
259+
}
260+
}
261+
262+
/**
263+
* Generate a search query specifically for the Pelias API. The
264+
* `sources` parameter is a Pelias-specific option.
265+
*/
266+
getSearchQuery (query) {
267+
const {apiKey, baseUrl, boundary, focusPoint, sources} = this.geocoderConfig
268+
return {
269+
apiKey,
270+
boundary,
271+
focusPoint,
272+
// explicitly send over null for sources if provided sources is not truthy
273+
// in order to avoid default isomorphic-mapzen-search sources form being
274+
// applied
275+
sources: sources || null,
276+
url: baseUrl ? `${baseUrl}/search` : undefined,
277+
format: false, // keep as returned GeoJSON,
278+
...query
279+
}
280+
}
281+
217282
/**
218283
* Rewrite the response into an application-specific data format using the
219284
* first feature returned from the geocoder.
220285
*/
221-
_rewriteReverseResponse (response) {
286+
rewriteReverseResponse (response) {
222287
const { 'point.lat': lat, 'point.lon': lon } = response.isomorphicMapzenSearchQuery
223288
return {
224289
lat,

0 commit comments

Comments
 (0)