Skip to content
This repository has been archived by the owner on Feb 7, 2022. It is now read-only.

Commit

Permalink
Feature/statefull version (#3)
Browse files Browse the repository at this point in the history
* implement queue
* improve public api
* implement batching
* implement test mocks
  • Loading branch information
turboMaCk committed Mar 31, 2017
1 parent f11c2ae commit 2e3b66a
Show file tree
Hide file tree
Showing 12 changed files with 1,167 additions and 199 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# EditorConfig is awesome: http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_size = 2
indent_style = space
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6.9
15 changes: 15 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ISC License

Copyright (c) 2017, GlobalWebIndex

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
139 changes: 122 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,65 @@
# Segment

A Segment analytics library using the HTTP api directly. The reason for this library is to delegate the responsibility of distributing data across integrations to Segment itself after having countless issues with data inconsistencies, because the official Segment way is to let the browser itself take care of all the integrations. That of course creates problems with ad-blockers.
This is an unofficial alternative to [analytics.js](https://segment.com/docs/sources/website/analytics.js/) by [segment.io](https://segment.io).
Unlike original implementation this library speaks with Segment's API directly and delegates responsibility of data distribution to back-end (as official Ruby, Java, Clojure, Python and many others do).
This helps you to prevent many issues with data inconsistency between various back-ends and optimize number of HTTP payloads required for tracks.
This library also comes with few other improvements over official one namely it uses **batch api** to prevent issues with [rate limits](https://segment.com/docs/sources/server/http/#rate-limits),
prevents tracks without `userId` by using internal queue, has **Promise** API to make easy to reason about async actions and **comes with test mocks** you can use in your test suit.

## What if I actually want to track event before calling identify?

No problem with that. Just use `anonymousTrack` or `anonymousPage`.

## Demo

![demo](http://i.imgur.com/EGNqJLS.gif)

## Usage

### Installation
At first install this using npm:

```
$ npm install gwi-segment --save
```

### Client initialization
### The Most Simple Use-Case

In most cases you want to just initialize library and store instance to global singleton object like this:

```javascript
const Segment = require('gwi-segment');

// Optional
const additionContext = {
app: 'My Supreme Application'
};
window.segment = Segment.getClient('YOUR_SEGMENT_WRITE_KEY');
```

// Optional
// window.btoa is the default if you do not provide this argument
// and it is used to generate the right authentication header from your key
const btoa = window.btoa
in case you need more instances or fancier implementation of static object you're free to do it as you wish!

var segment = Segment.getClient(YOUR_SEGMENT_WRITE_KEY, additionalContext, btoa);
```
## Api Reference

This is all you have available for production usage:

### Static Initialization

User static `getClient()` function to initialize instance of client. This is api of that function:

- `writeKey` [string] - your segment.io write key. **required**
- `options` [object] - custom settings. **optional**
- `context` [object] - custom meta to be added to segment's context record.
- `timeout` [number] - bulk "debounce" timeout in ms. default: 100; use `-1` for instant (sync) requests.

Calling `getClient()` returns instance of api client. This object implements this public interface:

### #identify

Used for identifying user. Id of user is than used in all `track` and `page` calls.

- `userId` [string] - identification of user **required**
- `traits` [object] - additional user information **optional**

**returns promise.**

#### Example

```javascript
const userId = 'someId';

Expand All @@ -42,19 +72,37 @@ segment.identify(userId, traits);

### #track

Main method for tracking user's action in your application.

- `event` [string] - name/id of event **required**
- `properties` - additional information to event **optional**

**returns promise.**

#### Example

```javascript
const userId = 'someId';
const event = 'Clicked a CTA'

const properties = {
ctaId: 'foo'
};

segment.track(userId, event, properties);
segment.track(event, properties);
```

### #anonymousTrack

Same as track but doesn't require `identify` call (to get `userId` to track).

- `anonymousId` [string] - value that will be send as user id just for this track
- `event` [string] - name/id of event **required**
- `properties` - additional information to event **optional**

**returns promise.**

#### Example

```javascript
const anonymousId = '123abc';
const event = 'Clicked a CTA'
Expand All @@ -68,19 +116,35 @@ segment.anonymousTrack(anonymousId, event, properties);

### #page

Used for tracking page event.

- `name` [string] - name/id of page displayed **required**
- `properties` [object] - addition information **optional**

**returns promise.**

#### Example

```javascript
const userId = 'someId';
const name = 'Index Page'

const properties = {
referrer: 'www.google.com'
};

segment.page(userId, name, properties);
segment.page(name, properties);
```

### #anonymousPage

Same as `page` but doesn't require `identify` call (to get `userId` for page event).

- `anonymousId` [string] - value that will be send as user id just for this track
- `name` [string] - name/id of page displayed **required**
- `properties` [object] - addition information **optional**

**returns promise.**

```javascript
const anonymousId = '123abc';
const name = 'Index Page'
Expand All @@ -92,6 +156,47 @@ const properties = {
segment.anonymousPage(anonymousId, name, properties);
```

## How Queue and Waiting For UserId Works

No `#track` nor `#page` call is proceed before `#identify` is called. This is to prevent events with missing `userId` to go through the system.
All events happened before `#identify` are added to queue and are waiting for userId. Once `#identify` is called `userId` from this call is used
for all waiting events which are waiting. Events are then tracked in order they were added to queue.

## How Batching Works

By default there is `100`ms timeout for collecting all tracks into single [batch](https://segment.com/docs/sources/server/http/#batch) request. This means events are not sent immediately
but are collected into one single request. This helps you to overcome issue with [rate limits](https://segment.com/docs/sources/server/http/#rate-limits) and optimize requests from app or website.
You can change default timeout using `options.timeout` or disable it completely by passing `-1` as timeout value.

## Test Mocking

Similarly to main client you can initialize test mock instead. This is done using `getTestMockClient()` static function and returns instance implementing default API.
However this client doesn't really speaks to API but instead pushes events into internal stack. Also this client doesn't perform any merging to batch API
but rather keeps all events isolated to make it easier to use in test suit. Public api still uses promises as production one but in fact works synchronously to make your tests simpler and faster.
It also contains extra `inspect` name-space for checking state of tracks.

### #inspect.allEvents

Returns array of all events tracked (including identify and page).

*no arguments*

### #inspect.lastEvent

Returns last event tracked (including identify and page).

*no arguments*

### #inspect.clearEvents

Clears state of test mock.

*no arguments*

## Support

This package supports Node 0.12 and higher.

## Licence

[ISC](https://en.wikipedia.org/wiki/ISC_license)
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gwi-segment",
"version": "1.0.1",
"version": "2.0.0",
"description": "Tracking library using Segment's HTTP API directly",
"scripts": {
"test": "jasmine"
Expand All @@ -21,11 +21,11 @@
},
"homepage": "https://github.com/GlobalWebIndex/segment#readme",
"devDependencies": {
"btoa": "^1.1.2",
"fetch-mock": "^5.9.4",
"jasmine": "^2.5.3"
},
"dependencies": {
"btoa": "^1.1.2",
"es6-promise": "^4.1.0",
"isomorphic-fetch": "^2.2.1"
}
Expand Down
Loading

0 comments on commit 2e3b66a

Please sign in to comment.