Note
Segment has paused maintenance on this project, but may return it to an active status in the future. Issues and pull requests from external contributors are not being considered, although internal contributions may appear from time to time. The project remains available under its open source license for anyone to use.
Segment integration base prototype.
Interested in integrating your service with us? Start on our Partners page.
Create an Integration
constructor with name
.
var MyIntegration = integration('My Integration');
Once the integration is initialized (new MyIntegration()
) it's .initialize()
method will be called
so you can do all sorts of fancy stuff if you need to, for example:
MyIntegration.prototype.initialize = function(){
if (this.settings.version == 'v2') {
this.track = this.trackV2;
} else {
this.track = this.trackV1;
}
};
Add a new mapping option by key
. The option will be an array that the user can pass in of key -> value
mappings. This will also generated a #KEY
method on the integration's prototype for easily accessing the mapping.
For example if your integration only supports a handful of events like Signed Up
and Completed Order
, you might create an mapping option called events
that the user would pass in, like so:
var MyIntegration = Integration('MyIntegration')
.mapping('events');
Which means that when the integration is initialized, it would be passed a mapping of events
to use, like so:
new MyIntegration({
events: [
{ key: 'Signed Up', value: 'Register' },
{ key: 'Completed Order', value: 'Purchase' }
]
});
Then later on, you can easily get all of the entries with a specific key, by calling this.events(key)
. For example:
MyIntegration.prototype.track = function(msg, fn){
var matches = this.events(msg.event());
var batch = new Batch;
var self = this;
each(matches, function(value){
batch.push(function(done){
self
.post('/track')
.send({ event: value })
.send({ props: msg.properties() })
.end(done);
});
});
batch.end(fn);
};
Map the given array of methods
to track()
calls.
Some integration don't support page views, but they support events
so you can .mapToTrack(['page'])
and the integration will start transforming
any page calls to events and pass them to track()
.
There are 3 settings users can turn on:
- .trackAllPages
- .trackNamedPages
- .trackCategorizedPages
.trackAllPages
will transform any Page
to Track(event: "Loaded a Page")
.
.trackNamedPages
will transform any named page to Track(event: "Viewed {category} {name} Page")
.
.trackCategorizedPages
will transform any categorized page to Track(event: "Viewed {category} Page")
.
Example:
var MyIntegration = Integration('My Integration')
.mapToTrack(['page']);
MyIntegration.prototype.track = function(track, done){
send(track.event(), track.properties(), done);
};
Ensure type
(settings
/ message
) with path
exists.
.ensure('settings.apiKey');
.ensure('message.userId');
.ensure('message.context.ip');
.ensure('message.traits.firstName');
Add a custom validation with fn(msg, settings) -> Error
Dynamically validate settings (taken from Mixpanel):
Mixpanel.ensure(function(msg, settings){
if (settings.apiKey) return;
if ('track' != msg.type()) return;
if (!shouldImport(msg)) return;
return this.invalid('.apiKey is required if "track" message is older than 5 days.');
});
Dynamically validate message:
Integration.ensure(function(msg, _){
if (msg.userId() && msg.proxy('message.context.ip')) return;
return this.reject('message.userId is required');
});
Set the default endpoint for all requests.
.endpoint('https://api.integration.io/v1');
Set the request timeout to be ms
.timeout(3000);
.timeout('3s');
Set how many times the integration should retry a request
.retries(2);
Enable the integration on all channels in array
.
.channels(['server', 'client', 'mobile']);
Reject a msg
with reason
, returns a MessageRejectedError
.
if (something) return fn(this.reject('some reason'));
Reject settings
with reason
.
if (something) return fn(this.invalid('some reason'));
Error with reason
.
if (200 != res.status) return fn(this.error('expected 200 but got %d', res.status));
Adds fn
to be called when determining
if a request error should be retried. The fn
will be pass the error from the request or
the response.
Example.retry(function(err){
return 429 == err.status;
});
By default, it already checks for:
err.status = 500
err.status = 502
err.status = 503
err.status = 504
err.code = "ETIMEDOUT"
err.code = "EADDRINFO"
err.code = "EADDRINFO"
err.code = "ECONNRESET"
err.code = "ECONNREFUSED"
err.code = "ECONNABORTED"
err.code = "EHOSTUNREACH"
err.code = "ENOTFOUND"
Any methods added with .retry
will be checked in addition to the list above.
Get a list of events with obj
and event
.
events = { my_event: 'a4991b88' }
.map(events, 'My Event');
// => ["a4991b88"]
.map(events, 'whatever');
// => []
events = [{ key: 'my event', value: '9b5eb1fa' }]
.map(events, 'my_event');
// => ["9b5eb1fa"]
.map(events, 'whatever');
// => []
Lock a key
with fn
.
This is used because some API's create duplicates.
var self = this;
var key = [this.settings.apiKey, msg.userId()].join(':');
this.lock(key, function(){
createUser(function(err, res){
self.unlock(key, function(){
if (err) return fn(err);
fn(null, res);
});
});
});
Unlocks key
.
Create a new superagent.Request with url
. See superagent docs for available methods (auth, headers, etc.).
this
.post('/some-path')
.send(payload)
.end(fn);
Handle HTTP response, errors if the response is not 2xx, 3xx
.
this
.post('/some-path')
.send(payload)
.end(this.handle(fn));
Set / Get redis instance.
Set / Get the logger instance.
Set / Get jstrace instance.
Trace str
with optional obj
.
Check if err
should be retried or not.
(The MIT License)
Copyright (c) 2013 Segment.io <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.