Comparing version 0.5.0 to 0.6.0
@@ -0,1 +1,5 @@ | ||
0.6.0 / 2017-01-03 | ||
=================== | ||
* support for `time` field in `mixpanel.track()` (thanks cruzanmo) | ||
0.5.0 / 2016-09-15 | ||
@@ -2,0 +6,0 @@ =================== |
@@ -119,20 +119,9 @@ /* | ||
/** | ||
track(event, properties, callback) | ||
--- | ||
this function sends an event to mixpanel. | ||
event:string the event name | ||
properties:object additional event properties to send | ||
callback:function(err:Error) callback is called when the request is | ||
finished or an error occurs | ||
*/ | ||
metrics.track = function(event, properties, callback) { | ||
if (typeof(properties) === 'function' || !properties) { | ||
callback = properties; | ||
properties = {}; | ||
} | ||
// if properties.time exists, use import endpoint | ||
var endpoint = (typeof(properties.time) === 'number') ? '/import' : '/track'; | ||
* Send an event to Mixpanel, using the specified endpoint (e.g., track/import) | ||
* @param {string} endpoint - API endpoint name | ||
* @param {string} event - event name | ||
* @param {object} properties - event properties | ||
* @param {Function} [callback] - callback for request completion/error | ||
*/ | ||
metrics.send_event_request = function(endpoint, event, properties, callback) { | ||
properties.token = metrics.token; | ||
@@ -142,9 +131,8 @@ properties.mp_lib = "node"; | ||
var data = { | ||
'event' : event, | ||
'properties' : properties | ||
event: event, | ||
properties: properties | ||
}; | ||
if (metrics.config.debug) { | ||
console.log("Sending the following event to Mixpanel:"); | ||
console.log(data); | ||
console.log("Sending the following event to Mixpanel:\n", data); | ||
} | ||
@@ -155,13 +143,44 @@ | ||
var parse_time = function(time) { | ||
if (time === void 0) { | ||
throw new Error("Import methods require you to specify the time of the event"); | ||
} else if (Object.prototype.toString.call(time) === '[object Date]') { | ||
time = Math.floor(time.getTime() / 1000); | ||
/** | ||
* Validate type of time property, and convert to Unix timestamp if necessary | ||
* @param {Date|number} time - value to check | ||
* @returns {number} Unix timestamp | ||
*/ | ||
var ensure_timestamp = function(time) { | ||
if (!(time instanceof Date || typeof time === "number")) { | ||
throw new Error("`time` property must be a Date or Unix timestamp and is only required for `import` endpoint"); | ||
} | ||
return time; | ||
return time instanceof Date ? Math.floor(time.getTime() / 1000) : time; | ||
}; | ||
/** | ||
import(event, properties, callback) | ||
track(event, properties, callback) | ||
--- | ||
this function sends an event to mixpanel. | ||
event:string the event name | ||
properties:object additional event properties to send | ||
callback:function(err:Error) callback is called when the request is | ||
finished or an error occurs | ||
*/ | ||
var TRACK_AGE_LIMIT = 60 * 60 * 24 * 5; // 5 days in seconds | ||
metrics.track = function(event, properties, callback) { | ||
if (!properties || typeof properties === "function") { | ||
callback = properties; | ||
properties = {}; | ||
} | ||
// time is optional for `track` but must be less than 5 days old if set | ||
if (properties.time) { | ||
properties.time = ensure_timestamp(properties.time); | ||
if (properties.time < Date.now() / 1000 - TRACK_AGE_LIMIT) { | ||
throw new Error("`track` not allowed for event more than 5 days old; use `mixpanel.import()`"); | ||
} | ||
} | ||
metrics.send_event_request("/track", event, properties, callback); | ||
}; | ||
/** | ||
import(event, time, properties, callback) | ||
--- | ||
@@ -186,3 +205,3 @@ This function sends an event to mixpanel using the import | ||
metrics.import = function(event, time, properties, callback) { | ||
if (typeof(properties) === 'function' || !properties) { | ||
if (!properties || typeof properties === "function") { | ||
callback = properties; | ||
@@ -192,5 +211,5 @@ properties = {}; | ||
properties.time = parse_time(time); | ||
properties.time = ensure_timestamp(time); | ||
metrics.track(event, properties, callback); | ||
metrics.send_event_request("/import", event, properties, callback); | ||
}; | ||
@@ -264,3 +283,3 @@ | ||
properties = event_list[ei].properties; | ||
properties.time = parse_time(properties.time); | ||
properties.time = ensure_timestamp(properties.time); | ||
if (!properties.token) { | ||
@@ -796,3 +815,3 @@ properties.token = metrics.token; | ||
if (modifiers.hasOwnProperty("$time")) { | ||
data.$time = parse_time(modifiers.$time); | ||
data.$time = ensure_timestamp(modifiers.$time); | ||
} | ||
@@ -799,0 +818,0 @@ } |
@@ -10,3 +10,3 @@ { | ||
], | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"homepage": "https://github.com/mixpanel/mixpanel-node", | ||
@@ -13,0 +13,0 @@ "author": "Carl Sverre", |
@@ -37,2 +37,9 @@ Mixpanel-node | ||
// set an IP address to get automatic geolocation info | ||
mixpanel.track('my event', {ip: '127.0.0.1'}); | ||
// track an event with a specific timestamp (up to 5 days old; | ||
// use mixpanel.import() for older events) | ||
mixpanel.track('timed event', {time: new Date()}); | ||
// create or update a user in Mixpanel Engage | ||
@@ -57,2 +64,10 @@ mixpanel.people.set('billybob', { | ||
// set a user profile's IP address to get automatic geolocation info | ||
mixpanel.people.set('billybob', { | ||
plan: 'premium', | ||
games_played: 1 | ||
}, { | ||
$ip: '127.0.0.1' | ||
}); | ||
// set a single property on a user | ||
@@ -168,2 +183,9 @@ mixpanel.people.set('billybob', 'plan', 'free'); | ||
Alternative Clients and Related Tools | ||
------------------------------------- | ||
- [Mixpanel-CLI](https://github.com/FGRibreau/mixpanel-cli) - CLI for Mixpanel API (currently only supports tracking functions) | ||
- [Mixpanel Data Export](https://github.com/michaelcarter/mixpanel-data-export-js) - Supports various query and data-management APIs; runs in both Node.js and browser | ||
- [Mixpanel Data Export (strawbrary)](https://github.com/strawbrary/mixpanel-data-export-js) - Fork of previous library, optimized for Node.js with support for streaming large raw exports | ||
Attribution/Credits | ||
@@ -198,2 +220,3 @@ ------------------- | ||
- [Frank Chiang](https://github.com/chiangf) | ||
- [Morgan Croney](https://github.com/cruzanmo) | ||
@@ -200,0 +223,0 @@ License |
var Mixpanel = require('../lib/mixpanel-node'), | ||
Sinon = require('sinon'), | ||
http = require('http'), | ||
events = require('events'); | ||
events = require('events'), | ||
mock_now_time = new Date(2016, 1, 1).getTime(), | ||
six_days_ago_timestamp = Math.floor(mock_now_time / 1000) - 60 * 60 * 24 * 6; | ||
@@ -9,3 +11,3 @@ exports.import = { | ||
this.mixpanel = Mixpanel.init('token', { key: 'key' }); | ||
this.clock = Sinon.useFakeTimers(); | ||
this.clock = Sinon.useFakeTimers(mock_now_time); | ||
@@ -26,3 +28,3 @@ Sinon.stub(this.mixpanel, 'send_request'); | ||
var event = "test", | ||
time = 500, | ||
time = six_days_ago_timestamp, | ||
props = { key1: 'val1' }, | ||
@@ -35,3 +37,3 @@ expected_endpoint = "/import", | ||
token: 'token', | ||
time: 500 | ||
time: time | ||
} | ||
@@ -50,5 +52,5 @@ }; | ||
"supports a Date instance": function(test) { | ||
"supports a Date instance greater than 5 days old": function(test) { | ||
var event = "test", | ||
time = new Date, | ||
time = new Date(six_days_ago_timestamp * 1000), | ||
props = { key1: 'val1' }, | ||
@@ -61,3 +63,3 @@ expected_endpoint = "/import", | ||
token: 'token', | ||
time: 0 | ||
time: six_days_ago_timestamp | ||
} | ||
@@ -76,6 +78,61 @@ }; | ||
"requires the time argument": function(test) { | ||
"supports a Date instance less than 5 days old": function(test) { | ||
var event = "test", | ||
time = new Date(mock_now_time), | ||
props = { key1: 'val1' }, | ||
expected_endpoint = "/import", | ||
expected_data = { | ||
event: 'test', | ||
properties: { | ||
key1: 'val1', | ||
token: 'token', | ||
time: Math.floor(mock_now_time / 1000) | ||
} | ||
}; | ||
this.mixpanel.import(event, time, props); | ||
test.ok( | ||
this.mixpanel.send_request.calledWithMatch(expected_endpoint, expected_data), | ||
"import didn't call send_request with correct arguments" | ||
); | ||
test.done(); | ||
}, | ||
"supports a unix timestamp": function(test) { | ||
var event = "test", | ||
time = mock_now_time / 1000, | ||
props = { key1: 'val1' }, | ||
expected_endpoint = "/import", | ||
expected_data = { | ||
event: 'test', | ||
properties: { | ||
key1: 'val1', | ||
token: 'token', | ||
time: time | ||
} | ||
}; | ||
this.mixpanel.import(event, time, props); | ||
test.ok( | ||
this.mixpanel.send_request.calledWithMatch(expected_endpoint, expected_data), | ||
"import didn't call send_request with correct arguments" | ||
); | ||
test.done(); | ||
}, | ||
"requires the time argument to be a number or Date": function(test) { | ||
test.doesNotThrow(this.mixpanel.import.bind(this, 'test', new Date())); | ||
test.doesNotThrow(this.mixpanel.import.bind(this, 'test', Date.now()/1000)); | ||
test.throws( | ||
function() { this.mixpanel.import('test'); }, | ||
"Import methods require you to specify the time of the event", | ||
this.mixpanel.import.bind(this, 'test', 'not a number or Date'), | ||
/`time` property must be a Date or Unix timestamp/, | ||
"import didn't throw an error when time wasn't a number or Date" | ||
); | ||
test.throws( | ||
this.mixpanel.import.bind(this, 'test'), | ||
/`time` property must be a Date or Unix timestamp/, | ||
"import didn't throw an error when time wasn't specified" | ||
@@ -82,0 +139,0 @@ ); |
var Mixpanel = require('../lib/mixpanel-node'), | ||
Sinon = require('sinon'); | ||
Sinon = require('sinon'), | ||
mock_now_time = new Date(2016, 1, 1).getTime(); | ||
@@ -7,2 +8,3 @@ exports.track = { | ||
this.mixpanel = Mixpanel.init('token'); | ||
this.clock = Sinon.useFakeTimers(mock_now_time); | ||
@@ -16,2 +18,3 @@ Sinon.stub(this.mixpanel, 'send_request'); | ||
this.mixpanel.send_request.restore(); | ||
this.clock.restore(); | ||
@@ -77,3 +80,82 @@ next(); | ||
}); | ||
}, | ||
"supports Date object for time": function(test) { | ||
var event = 'test', | ||
time = new Date(mock_now_time), | ||
props = { time: time }, | ||
expected_endpoint = "/track", | ||
expected_data = { | ||
event: 'test', | ||
properties: { | ||
token: 'token', | ||
time: time.getTime() / 1000, | ||
mp_lib: 'node' | ||
} | ||
}; | ||
this.mixpanel.track(event, props); | ||
test.ok( | ||
this.mixpanel.send_request.calledWithMatch(expected_endpoint, expected_data), | ||
"track didn't call send_request with correct arguments" | ||
); | ||
test.done(); | ||
}, | ||
"supports unix timestamp for time": function(test) { | ||
var event = 'test', | ||
time = mock_now_time / 1000, | ||
props = { time: time }, | ||
expected_endpoint = "/track", | ||
expected_data = { | ||
event: 'test', | ||
properties: { | ||
token: 'token', | ||
time: time, | ||
mp_lib: 'node' | ||
} | ||
}; | ||
this.mixpanel.track(event, props); | ||
test.ok( | ||
this.mixpanel.send_request.calledWithMatch(expected_endpoint, expected_data), | ||
"track didn't call send_request with correct arguments" | ||
); | ||
test.done(); | ||
}, | ||
"throws error if time property is older than 5 days": function(test) { | ||
var event = 'test', | ||
time = (mock_now_time - 1000 * 60 * 60 * 24 * 6) / 1000, | ||
props = { time: time }; | ||
test.throws( | ||
this.mixpanel.track.bind(this, event, props), | ||
/`track` not allowed for event more than 5 days old/, | ||
"track didn't throw an error when time was more than 5 days ago" | ||
); | ||
test.done(); | ||
}, | ||
"throws error if time is not a number or Date": function(test) { | ||
var event = 'test', | ||
props = { time: 'not a number or Date' }; | ||
test.throws( | ||
this.mixpanel.track.bind(this, event, props), | ||
/`time` property must be a Date or Unix timestamp/, | ||
"track didn't throw an error when time wasn't a number or Date" | ||
); | ||
test.done(); | ||
}, | ||
"does not require time property": function(test) { | ||
var event = 'test', | ||
props = {}; | ||
test.doesNotThrow(this.mixpanel.track.bind(this, event, props)); | ||
test.done(); | ||
} | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
87734
1880
224