Skip to content

Commit 99ef6e9

Browse files
author
Scott Farley
authored
Merge pull request #189 from mapbox/reduce-bundle-size
Remove dependencies for events logging
2 parents 5f2589c + d5ab277 commit 99ef6e9

File tree

4 files changed

+112
-105
lines changed

4 files changed

+112
-105
lines changed

lib/events.js

+107-99
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
'use strict';
2-
3-
var request = require('request');
4-
var crypto = require('crypto');
5-
2+
var shajs = require('sha.js')
63
/**
74
* Construct a new mapbox event client to send interaction events to the mapbox event service
85
* @param {Object} options options with which to create the service
96
* @param {String} options.accessToken the mapbox access token to make requests
107
* @private
118
*/
12-
function MapboxEventManager(options){
9+
function MapboxEventManager(options) {
1310
this.origin = options.origin || 'https://api.mapbox.com';
14-
this.endpoint = this.origin + '/events/v2';
11+
this.endpoint = '/events/v2';
1512
this.access_token = options.accessToken;
1613
this.version = '0.0.1'
1714
this.sessionID = this.generateSessionID();
1815
this.userAgent = this.getUserAgent();
1916
// parse global options to be sent with each request
20-
this.countries = (options.countries) ? options.countries.split(",") : null;
21-
this.types = (options.types) ? options.types.split(",") : null;
17+
this.countries = (options.countries) ? options.countries.split(",") : null;
18+
this.types = (options.types) ? options.types.split(",") : null;
2219
this.bbox = (options.bbox) ? options.bbox : null;
2320
this.language = (options.language) ? options.language.split(",") : null;
2421
this.limit = (options.limit) ? +options.limit : null;
@@ -32,15 +29,15 @@ function MapboxEventManager(options){
3229
MapboxEventManager.prototype = {
3330

3431
/**
35-
* Send a search.select event to the mapbox events service
36-
* This event marks the array index of the item selected by the user out of the array of possible options
37-
* @private
38-
* @param {Object} selected the geojson feature selected by the user
39-
* @param {Object} geocoder a mapbox-gl-geocoder instance
40-
* @param {Function} callback a callback function to invoke once the event has been sent (optional)
41-
* @returns {Promise}
42-
*/
43-
select: function(selected, geocoder, callback){
32+
* Send a search.select event to the mapbox events service
33+
* This event marks the array index of the item selected by the user out of the array of possible options
34+
* @private
35+
* @param {Object} selected the geojson feature selected by the user
36+
* @param {Object} geocoder a mapbox-gl-geocoder instance
37+
* @param {Function} callback a callback function to invoke once the event has been sent (optional)
38+
* @returns {Promise}
39+
*/
40+
select: function (selected, geocoder, callback) {
4441
var resultIndex = this.getSelectedIndex(selected, geocoder);
4542
var payload = this.getEventPayload('search.select', geocoder);
4643
payload.resultIndex = resultIndex;
@@ -55,36 +52,37 @@ MapboxEventManager.prototype = {
5552
},
5653

5754
/**
58-
* Send a search-start event to the mapbox events service
59-
* This turnstile event marks when a user starts a new search
60-
* @private
61-
* @param {Object} geocoder a mapbox-gl-geocoder instance
62-
* @param {Function} callback
63-
* @returns {Promise}
64-
*/
65-
start: function(geocoder, callback){
55+
* Send a search-start event to the mapbox events service
56+
* This turnstile event marks when a user starts a new search
57+
* @private
58+
* @param {Object} geocoder a mapbox-gl-geocoder instance
59+
* @param {Function} callback
60+
* @returns {Promise}
61+
*/
62+
start: function (geocoder, callback) {
6663
var payload = this.getEventPayload('search.start', geocoder);
6764
return this.send(payload, callback);
6865
},
6966

7067
/**
71-
* Send an event to the events service
72-
*
73-
* The event is skipped if the instance is not enabled to send logging events
74-
*
75-
* @private
76-
* @param {Object} payload the http POST body of the event
77-
* @returns {Promise}
78-
*/
79-
send: function(payload, callback){
80-
if (!callback) callback = function(){return};
81-
if (!this.enableEventLogging){
68+
* Send an event to the events service
69+
*
70+
* The event is skipped if the instance is not enabled to send logging events
71+
*
72+
* @private
73+
* @param {Object} payload the http POST body of the event
74+
* @returns {Promise}
75+
*/
76+
send: function (payload, callback) {
77+
if (!callback) callback = function () {
78+
return
79+
};
80+
if (!this.enableEventLogging) {
8281
return callback();
8382
}
8483
var options = this.getRequestOptions(payload);
85-
this.request(options, function(err,res){
84+
this.request(options, function (err) {
8685
if (err) return this.handleError(err, callback);
87-
if (res.statusCode != 204) return this.handleError(res, callback);
8886
if (callback) return callback();
8987
})
9088
},
@@ -93,121 +91,131 @@ MapboxEventManager.prototype = {
9391
* @private
9492
* @param {*} payload
9593
*/
96-
getRequestOptions: function(payload){
94+
getRequestOptions: function (payload) {
9795
var options = {
9896
// events must be sent with POST
99-
method: "POST",
100-
url: this.endpoint,
101-
headers:{
97+
method: "POST",
98+
host: this.origin,
99+
path: this.endpoint + "?access_token=" + this.access_token,
100+
headers: {
102101
'Content-Type': 'application/json'
103102
},
104-
qs: {
105-
access_token: this.access_token
106-
},
107-
body:JSON.stringify([payload]) //events are arrays
103+
body: JSON.stringify([payload]) //events are arrays
108104
}
109105
return options
110106
},
111107

112108
/**
113-
* Get the event payload to send to the events service
114-
* Most payload properties are shared across all events
115-
* @private
116-
* @param {String} event the name of the event to send to the events service. Valid options are 'search.start', 'search.select', 'search.feedback'.
117-
* @param {Object} geocoder a mapbox-gl-geocoder instance
118-
* @returns {Object} an event payload
119-
*/
120-
getEventPayload: function(event, geocoder){
109+
* Get the event payload to send to the events service
110+
* Most payload properties are shared across all events
111+
* @private
112+
* @param {String} event the name of the event to send to the events service. Valid options are 'search.start', 'search.select', 'search.feedback'.
113+
* @param {Object} geocoder a mapbox-gl-geocoder instance
114+
* @returns {Object} an event payload
115+
*/
116+
getEventPayload: function (event, geocoder) {
121117
var proximity;
122118
if (!geocoder.options.proximity) proximity = null;
123-
else proximity = [geocoder.options.proximity.longitude, geocoder.options.proximity.latitude ];
119+
else proximity = [geocoder.options.proximity.longitude, geocoder.options.proximity.latitude];
124120

125121
var zoom = (geocoder._map) ? geocoder._map.getZoom() : null;
126122
return {
127123
event: event,
128124
created: +new Date(),
129125
sessionIdentifier: this.sessionID,
130-
country: this.countries,
126+
country: this.countries,
131127
userAgent: this.userAgent,
132128
language: this.language,
133129
bbox: this.bbox,
134-
types: this.types,
130+
types: this.types,
135131
endpoint: 'mapbox.places',
136132
// fuzzyMatch: search.fuzzy, //todo --> add to plugin
137133
proximity: proximity,
138134
limit: geocoder.options.limit,
139135
// routing: search.routing, //todo --> add to plugin
140136
queryString: geocoder.inputString,
141137
mapZoom: zoom,
142-
keyboardLocale: this.locale
138+
keyboardLocale: this.locale
143139
}
144140
},
145141

146142
/**
147-
* Wraps the request function for easier testing
148-
* Make an http request and invoke a callback
149-
* @private
150-
* @param {Object} opts options describing the http request to be made
151-
* @param {Function} callback the callback to invoke when the http request is completed
152-
*/
153-
request: function(opts, callback){
154-
request(opts, function(err, res){
155-
return callback(err, res);
156-
})
143+
* Wraps the request function for easier testing
144+
* Make an http request and invoke a callback
145+
* @private
146+
* @param {Object} opts options describing the http request to be made
147+
* @param {Function} callback the callback to invoke when the http request is completed
148+
*/
149+
request: function (opts, callback) {
150+
var xhttp = new XMLHttpRequest();
151+
xhttp.onreadystatechange = function() {
152+
if (this.readyState == 4 ) {
153+
if (this.status == 204){
154+
//success
155+
return callback(null);
156+
}else {
157+
return callback(this.statusText);
158+
}
159+
}
160+
};
161+
xhttp.open(opts.method, opts.host + opts.path, true);
162+
for (var header in opts.headers){
163+
var headerValue = opts.headers[header];
164+
xhttp.setRequestHeader(header, headerValue)
165+
}
166+
xhttp.send(opts.body);
157167
},
158168

159169
/**
160-
* Handle an error that occurred while making a request
161-
* @param {Object} err an error instance to log
162-
* @private
163-
*/
164-
handleError: function(err, callback){
170+
* Handle an error that occurred while making a request
171+
* @param {Object} err an error instance to log
172+
* @private
173+
*/
174+
handleError: function (err, callback) {
165175
if (callback) return callback(err);
166176
},
167177

168178
/**
169-
* Generate a session ID to be returned with all of the searches made by this geocoder instance
170-
* ID is random and cannot be tracked across sessions
171-
* @private
172-
*/
173-
generateSessionID: function(){
174-
var sha = crypto.createHash('sha256');
175-
sha.update(Math.random().toString());
176-
return sha.digest('hex')
179+
* Generate a session ID to be returned with all of the searches made by this geocoder instance
180+
* ID is random and cannot be tracked across sessions
181+
* @private
182+
*/
183+
generateSessionID: function () {
184+
return new shajs.sha256().update(Math.random().toString()).digest('hex')
177185
},
178186

179187
/**
180-
* Get a user agent string to send with the request to the events service
181-
* @private
182-
*/
183-
getUserAgent: function(){
188+
* Get a user agent string to send with the request to the events service
189+
* @private
190+
*/
191+
getUserAgent: function () {
184192
return 'mapbox-gl-geocoder.' + this.version + "." + navigator.userAgent;
185193
},
186194

187195
/**
188-
* Get the 0-based numeric index of the item that the user selected out of the list of options
189-
* @private
190-
* @param {Object} selected the geojson feature selected by the user
191-
* @param {Object} geocoder a Mapbox-GL-Geocoder instance
192-
* @returns {Number} the index of the selected result
193-
*/
194-
getSelectedIndex: function(selected, geocoder){
196+
* Get the 0-based numeric index of the item that the user selected out of the list of options
197+
* @private
198+
* @param {Object} selected the geojson feature selected by the user
199+
* @param {Object} geocoder a Mapbox-GL-Geocoder instance
200+
* @returns {Number} the index of the selected result
201+
*/
202+
getSelectedIndex: function (selected, geocoder) {
195203
var results = geocoder._typeahead.data;
196204
var selectedID = selected.id;
197-
var resultIDs = results.map(function (feature){
205+
var resultIDs = results.map(function (feature) {
198206
return feature.id;
199207
});
200208
var selectedIdx = resultIDs.indexOf(selectedID);
201209
return selectedIdx;
202210
},
203211

204212
/**
205-
* Check whether events should be logged
206-
* Clients using a localGeocoder or an origin other than mapbox should not have events logged
207-
* @private
208-
*/
209-
shouldEnableLogging: function(options){
210-
if (!this.origin.includes('api.mapbox.com')) return false;
213+
* Check whether events should be logged
214+
* Clients using a localGeocoder or an origin other than mapbox should not have events logged
215+
* @private
216+
*/
217+
shouldEnableLogging: function (options) {
218+
if (this.origin.indexOf('api.mapbox.com') == -1) return false;
211219
// hard to make sense of events when a local instance is suplementing results from origin
212220
if (options.localGeocoder) return false;
213221
// hard to make sense of events when a custom filter is in use

package-lock.json

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
"dependencies": {
5454
"@mapbox/mapbox-sdk": "^0.5.0",
5555
"lodash.debounce": "^4.0.6",
56-
"request": "^2.88.0",
56+
"sha.js": "^2.4.11",
5757
"sinon": "^7.2.3",
5858
"suggestions": "^1.3.2",
5959
"xtend": "^4.0.1"

test/events.test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ test('it constructs a new event manager instance with the correct properties', f
1515
limit: 2
1616
}
1717
var eventsManager = new MapboxEventsManager(options)
18-
assert.equals(eventsManager.endpoint, 'https://api.mapbox.com/events/v2', 'the correct event endpoint is set');
18+
assert.equals(eventsManager.endpoint, '/events/v2', 'the correct event endpoint is set');
1919
assert.equals(eventsManager.access_token, options.accessToken, 'sets the right access tokens for the request');
2020
assert.deepEqual(eventsManager.countries, ['CA', 'US'], 'correctly parses the country parameter into an array');
2121
assert.deepEqual(eventsManager.language, ['en', 'fr'], 'correctly parses the language parameter into an array');
@@ -75,9 +75,9 @@ test('get requst options', function(assert){
7575
event: 'test.event'
7676
};
7777
assert.equals(eventsManager.getRequestOptions(payload).method, 'POST', 'http method is set to POST');
78-
assert.equals(eventsManager.getRequestOptions(payload).url, 'https://api.mapbox.com/events/v2', 'http request is made to the right uri');
78+
assert.equals(eventsManager.getRequestOptions(payload).host, 'https://api.mapbox.com', 'http request is made to the right host');
79+
assert.equals(eventsManager.getRequestOptions(payload).path, '/events/v2?access_token=abc123', 'http request is made to the right host');
7980
assert.deepEqual(eventsManager.getRequestOptions(payload).headers, {'Content-Type': 'application/json'}, 'content type is json');
80-
assert.equals(eventsManager.getRequestOptions(payload).qs.access_token, 'abc123', 'http request is sent with the right access token');
8181
assert.deepEqual(eventsManager.getRequestOptions(payload).body, JSON.stringify([payload]), 'the right payload is set for the request');
8282
assert.end();
8383
})

0 commit comments

Comments
 (0)