Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mixpanel

Package Overview
Dependencies
Maintainers
2
Versions
47
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mixpanel - npm Package Compare versions

Comparing version 0.1.1 to 0.2.0

20

example.js

@@ -74,1 +74,21 @@ // grab the Mixpanel factory

});
// import multiple events at once
mixpanel_importer.import_batch([
{
event: 'old event',
properties: {
time: new Date(2012, 4, 20, 12, 34, 56),
distinct_id: 'billybob',
gender: 'male'
}
},
{
event: 'another old event',
properties: {
time: new Date(2012, 4, 21, 11, 33, 55),
distinct_id: 'billybob',
color: 'red'
}
}
]);

116

lib/mixpanel-node.js

@@ -135,2 +135,11 @@ /*

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);
}
return time;
};
/**

@@ -162,11 +171,106 @@ import(event, properties, callback)

if (time === void 0) {
throw new Error("The import method requires you to specify the time of the event");
} else if (Object.prototype.toString.call(time) === '[object Date]') {
time = Math.floor(time.getTime() / 1000);
properties.time = parse_time(time);
metrics.track(event, properties, callback);
};
/**
import_batch(event_list, options, callback)
---
This function sends a list of events to mixpanel using the import
endpoint. The format of the event array should be:
[
{
"event": "event name",
"properties": {
"time": new Date(), // Number or Date; required for each event
"key": "val",
...
}
},
{
"event": "event name",
"properties": {
"time": new Date() // Number or Date; required for each event
}
},
...
]
See import() for further information about the import endpoint.
Options:
max_batch_size: the maximum number of events to be transmitted over
the network simultaneously. useful for capping bandwidth
usage.
N.B.: the Mixpanel API only accepts 50 events per request, so regardless
of max_batch_size, larger lists of events will be chunked further into
groups of 50.
event_list:array list of event names and properties
options:object optional batch configuration
callback:function(error_list:array) callback is called when the request is
finished or an error occurs
*/
metrics.import_batch = function(event_list, options, callback) {
var batch_size = 50, // default: Mixpanel API permits 50 events per request
total_events = event_list.length,
max_simultaneous_events = total_events,
completed_events = 0,
event_group_idx = 0,
request_errors = [];
if (typeof(options) === 'function' || !options) {
callback = options;
options = {};
}
if (options.max_batch_size) {
max_simultaneous_events = options.max_batch_size;
if (options.max_batch_size < batch_size) {
batch_size = options.max_batch_size;
}
}
properties.time = time;
var send_next_batch = function() {
var properties,
event_batch = [];
metrics.track(event, properties, callback);
// prepare batch with required props
for (var ei = event_group_idx; ei < total_events && ei < event_group_idx + batch_size; ei++) {
properties = event_list[ei].properties;
properties.time = parse_time(properties.time);
if (!properties.token) {
properties.token = metrics.token;
}
event_batch.push(event_list[ei]);
}
if (event_batch.length > 0) {
metrics.send_request('/import', event_batch, function(e) {
completed_events += event_batch.length;
if (e) {
request_errors.push(e);
}
if (completed_events < total_events) {
send_next_batch();
} else if (callback) {
callback(request_errors);
}
});
event_group_idx += batch_size;
}
};
if (metrics.config.debug) {
console.log(
"Sending " + event_list.length + " events to Mixpanel in " +
Math.ceil(total_events / batch_size) + " requests"
);
}
for (var i = 0; i < max_simultaneous_events; i += batch_size) {
send_next_batch();
}
};

@@ -173,0 +277,0 @@

2

package.json

@@ -10,3 +10,3 @@ {

],
"version": "0.1.1",
"version": "0.2.0",
"homepage": "https://github.com/mixpanel/mixpanel-node",

@@ -13,0 +13,0 @@ "author": "Carl Sverre",

@@ -87,2 +87,22 @@ Mixpanel-node

});
// import multiple events at once
mixpanel_importer.import_batch([
{
event: 'old event',
properties: {
time: new Date(2012, 4, 20, 12, 34, 56),
distinct_id: 'billybob',
gender: 'male'
}
},
{
event: 'another old event',
properties: {
time: new Date(2012, 4, 21, 11, 33, 55),
distinct_id: 'billybob',
color: 'red'
}
}
]);
```

@@ -89,0 +109,0 @@

@@ -1,3 +0,5 @@

var Mixpanel = require('../lib/mixpanel-node'),
Sinon = require('sinon');
var Mixpanel = require('../lib/mixpanel-node'),
Sinon = require('sinon'),
http = require('http'),
events = require('events');

@@ -72,3 +74,3 @@ exports.import = {

function() { this.mixpanel.import('test'); },
"The import method requires you to specify the time of the event",
"Import methods require you to specify the time of the event",
"import didn't throw an error when time wasn't specified"

@@ -80,1 +82,195 @@ );

};
exports.import_batch = {
setUp: function(next) {
this.mixpanel = Mixpanel.init('token', { key: 'key' });
this.clock = Sinon.useFakeTimers();
Sinon.stub(this.mixpanel, 'send_request');
next();
},
tearDown: function(next) {
this.mixpanel.send_request.restore();
this.clock.restore();
next();
},
"calls send_request with correct endpoint and data": function(test) {
var expected_endpoint = "/import",
event_list = [
{event: 'test', properties: {key1: 'val1', time: 500 }},
{event: 'test', properties: {key2: 'val2', time: 1000}},
{event: 'test2', properties: {key2: 'val2', time: 1500}}
],
expected_data = [
{event: 'test', properties: {key1: 'val1', time: 500, token: 'token'}},
{event: 'test', properties: {key2: 'val2', time: 1000, token: 'token'}},
{event: 'test2', properties: {key2: 'val2', time: 1500, token: 'token'}}
];
this.mixpanel.import_batch(event_list);
test.ok(
this.mixpanel.send_request.calledWithMatch(expected_endpoint, expected_data),
"import_batch didn't call send_request with correct arguments"
);
test.done();
},
"requires the time argument for every event": function(test) {
var event_list = [
{event: 'test', properties: {key1: 'val1', time: 500 }},
{event: 'test', properties: {key2: 'val2', time: 1000}},
{event: 'test2', properties: {key2: 'val2' }}
];
test.throws(
function() { this.mixpanel.import_batch(event_list); },
"Import methods require you to specify the time of the event",
"import didn't throw an error when time wasn't specified"
);
test.done();
},
"batches 50 events at a time": function(test) {
var event_list = [];
for (var ei = 0; ei < 130; ei++) { // 3 batches: 50 + 50 + 30
event_list.push({event: 'test', properties: {key1: 'val1', time: 500 + ei }});
}
this.mixpanel.import_batch(event_list);
test.equals(
3, this.mixpanel.send_request.callCount,
"import_batch didn't call send_request correct number of times"
);
test.done();
}
};
exports.import_batch_integration = {
setUp: function(next) {
this.mixpanel = Mixpanel.init('token', { key: 'key' });
this.clock = Sinon.useFakeTimers();
Sinon.stub(http, 'get');
this.http_emitter = new events.EventEmitter();
// stub sequence of http responses
this.res = [];
for (var ri = 0; ri < 5; ri++) {
this.res.push(new events.EventEmitter());
http.get
.onCall(ri)
.callsArgWith(1, this.res[ri])
.returns(this.http_emitter);
}
this.event_list = [];
for (var ei = 0; ei < 130; ei++) { // 3 batches: 50 + 50 + 30
this.event_list.push({event: 'test', properties: {key1: 'val1', time: 500 + ei }});
}
next();
},
tearDown: function(next) {
http.get.restore();
this.clock.restore();
next();
},
"calls provided callback after all requests finish": function(test) {
test.expect(2);
this.mixpanel.import_batch(this.event_list, function(error_list) {
test.equals(
3, http.get.callCount,
"import_batch didn't call send_request correct number of times before callback"
);
test.equals(
0, error_list.length,
"import_batch returned errors in callback unexpectedly"
);
test.done();
});
for (var ri = 0; ri < 3; ri++) {
this.res[ri].emit('data', '1');
this.res[ri].emit('end');
}
},
"passes error list to callback": function(test) {
test.expect(1);
this.mixpanel.import_batch(this.event_list, function(error_list) {
test.equals(
3, error_list.length,
"import_batch didn't return errors in callback"
);
test.done();
});
for (var ri = 0; ri < 3; ri++) {
this.res[ri].emit('data', '0');
this.res[ri].emit('end');
}
},
"calls provided callback when options are passed": function(test) {
test.expect(2);
this.mixpanel.import_batch(this.event_list, {max_batch_size: 100}, function(error_list) {
test.equals(
3, http.get.callCount,
"import_batch didn't call send_request correct number of times before callback"
);
test.equals(
0, error_list.length,
"import_batch returned errors in callback unexpectedly"
);
test.done();
});
for (var ri = 0; ri < 3; ri++) {
this.res[ri].emit('data', '1');
this.res[ri].emit('end');
}
},
"sends more requests when max_batch_size < 50": function(test) {
test.expect(2);
this.mixpanel.import_batch(this.event_list, {max_batch_size: 30}, function(error_list) {
test.equals(
5, http.get.callCount, // 30 + 30 + 30 + 30 + 10
"import_batch didn't call send_request correct number of times before callback"
);
test.equals(
0, error_list.length,
"import_batch returned errors in callback unexpectedly"
);
test.done();
});
for (var ri = 0; ri < 5; ri++) {
this.res[ri].emit('data', '1');
this.res[ri].emit('end');
}
},
"behaves well without a callback": function(test) {
test.expect(2);
this.mixpanel.import_batch(this.event_list);
test.equals(
3, http.get.callCount,
"import_batch didn't call send_request correct number of times"
);
this.mixpanel.import_batch(this.event_list, {max_batch_size: 100});
test.equals(
5, http.get.callCount, // 3 + 100 / 50; last request starts async
"import_batch didn't call send_request correct number of times"
);
test.done();
}
};

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc