GithubHelp home page GithubHelp logo

keen / keen-tracking.js Goto Github PK

View Code? Open in Web Editor NEW
259.0 36.0 84.0 4.01 MB

A light, fast and flexible javascript tracking library

Home Page: https://keen.io

License: MIT License

JavaScript 94.75% HTML 5.25%
tracking tracking-pixels tracking-plugin tracking-api tracker event-tracker user-tracking customer-tracker analytics click-tracking

keen-tracking.js's Introduction

keen-tracking.js

A JavaScript tracking library for Keen. Track events, user actions, clicks, pageviews, conversions and more!

Slack

Installation

Install this package from NPM Recommended

npm install keen-tracking --save

Public CDN

<script crossorigin src="https://cdn.jsdelivr.net/npm/keen-tracking@5/dist/keen-tracking.min.js"></script>

Project ID & API Keys

Login to Keen IO to create a project and grab the Project ID and Write Key from your project's Access page.

Getting started

The following examples demonstrate how to implement rock-solid web analytics, capturing pageviews, clicks, and form submissions with robust data models.

Full documentation is available here

Using React? Check out these setup guides:

Upgrading from an earlier version of keen-js? Read this.


Record an Event

import KeenTracking from 'keen-tracking';

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY'
});

client
  .recordEvent('purchases', {
    item: 'Avocado',
    number_of_items: 10,
    user: {
      name: 'John Smith'
    }
  })
  .then((response) => {
    // handle successful responses
  })
  .catch(error => {
    // handle errors
  });

Automated Event Tracking

Automatically record pageviews, clicks, form_submissions and element_views events with robust data models:

<script>
  (function(name,path,ctx){ctx[name]=ctx[name]||{ready:function(fn){var h=document.getElementsByTagName('head')[0],s=document.createElement('script'),w=window,loaded;s.onload=s.onreadystatechange=function(){if((s.readyState&&!(/^c|loade/.test(s.readyState)))||loaded){return}s.onload=s.onreadystatechange=null;loaded=1;ctx[name].ready(fn)};s.async=1;s.src=path;h.parentNode.insertBefore(s,h)}}})
  ('KeenTracking', 'https://cdn.jsdelivr.net/npm/keen-tracking@5/dist/keen-tracking.min.js', this);

  KeenTracking.ready(function(){
    const client = new KeenTracking({
      projectId: 'YOUR_PROJECT_ID',
      writeKey: 'YOUR_WRITE_KEY'
    });
    client.initAutoTracking();
  });
</script>

Learn how to configure and customize this functionality here


Pageview Tracking

First, let's create a new client instance with your Project ID and Write Key, and use the .extendEvents() method to define a solid baseline data model that will be applied to every single event that is recorded. Consistent data models and property names make life much easier later on, when analyzing and managing several event streams. This setup also includes our data enrichment add-ons, which will populate additional information when an event is received on our end.

import KeenTracking from 'keen-tracking';

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY'
});
const helpers = KeenTracking.helpers;
const utils = KeenTracking.utils;

const sessionCookie = utils.cookie('rename-this-example-cookie');
if (!sessionCookie.get('guest_id')) {
  sessionCookie.set('guest_id', helpers.getUniqueId());
}

// optional
client.extendEvents(() => {
  return {
    geo: {
      ip_address: '${keen.ip}',
      info: {
        /* Enriched data from the API will be saved here */
        /* https://keen.io/docs/api/?javascript#ip-to-geo-parser */
      }
    },
    page: {
      title: document.title,
      url: document.location.href,
      info: { /* Enriched */ }
    },
    referrer: {
      url: document.referrer,
      info: { /* Enriched */ }
    },
    tech: {
      browser: helpers.getBrowserProfile(),
      user_agent: '${keen.user_agent}',
      info: { /* Enriched */ }
    },
    time: helpers.getDatetimeIndex(),
    visitor: {
      guest_id: sessionCookie.get('guest_id')
      /* Include additional visitor info here */
    },
    keen: {
      addons: [
        {
          name: 'keen:ip_to_geo',
          input: {
            ip: 'geo.ip_address'
          },
          output : 'geo.info'
        },
        {
          name: 'keen:ua_parser',
          input: {
            ua_string: 'tech.user_agent'
          },
          output: 'tech.info'
        },
        {
          name: 'keen:url_parser',
          input: {
            url: 'page.url'
          },
          output: 'page.info'
        },
        {
          name: 'keen:referrer_parser',
          input: {
            referrer_url: 'referrer.url',
            page_url: 'page.url'
          },
          output: 'referrer.info'
        }
      ]
    }
  }
});

// record the event
client
  .recordEvent('pageviews', {
    // here you can add even more data
    // some_key: some_value
  })
  .then((response) => {
    // handle responses
  }).catch(error => {
    // handle errors
  });

Every event that is recorded will inherit this baseline data model. Additional properties defined in client.recordEvent() will be applied before the event is finally recorded.

What else can this SDK do?

App Frameworks:

Video Players:

Full documentation is available here


Click and Form Submit Tracking

Clicks and form submissions can be captured with .listenTo(). This example further extends the client instance defined previously, and activates a simple timer when the page the loaded. Once a click or submit event is captured, the timer's value will be recorded as visitor.time_on_page.

import KeenTracking from 'keen-tracking';

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY'
});
const helpers = KeenTracking.helpers;
const timer = KeenTracking.utils.timer();
timer.start();

KeenTracking.listenTo({
  'click .nav a': (e) => {
    return client.recordEvent('click', {
      action: {
        intent: 'navigate',
        target_path: helpers.getDomNodePath(e.target)
      },
      visitor: {
        time_on_page: timer.value()
      }
    });
  },
  'submit form#signup': (e) => {
    return client.recordEvent('form-submit', {
      action: {
        intent: 'signup',
        target_path: helpers.getDomNodePath(e.target)
      },
      visitor: {
        email_address: document.getElementById('signup-email').value,
        time_on_page: timer.value()
      }
    });
  }
});

Click events (clicks) will record specific attributes from the clicked element or its ancestor elements and pass them via the element property in the event object data:

// event object
{
    // ...

    // specific to the clicks event type
    "element": {
      "action" : undefined,                 // [DIRECT]
      "class": "cta",                       // [DIRECT]
      "href": "https://keen.io/",     // [INHERITED]
      "id": "main-cta",                     // [INHERITED]
      "event_key": "learn-more-cta",        // [INHERITED] from the `data-event-key` attribute
      "method": "learn-more-link",          // [DIRECT]
      "node_name": "A",                     // [DIRECT]
      "selector": "body > div:eq(0) > div:eq(1) > div:eq(0) > a", // [DIRECT]
      "text": "Learn More",                 // [INHERITED]
      "title": "Learn More",                // [INHERITED]
      "type": undefined,                    // [DIRECT]
      "x_position": 191,                    // [DIRECT]
      "y_position": 970                     // [DIRECT]
  }
}

In the above list of collected properties for a click event, some properties are gathered from the nearest ancestor elements if they can't be found on the immediate source element of the event. These properties are shown with [INHERITED] above.

For example, a click on the word clicked! below:

  <a href='foo.html' data-event-key='click-me-cta'>
    <span id='contrived-example'>I want to be <strong class='enhance'>clicked!</strong></span>
  </a>

Would generate an event including a mixture of immediate attributes and attributes found by traversing up the DOM tree:

{
  // ...
  "id" : "contrived-example",
  "class" : "enhance",
  "text" : "clicked!",
  "href" : "foo.html",
  "node_name" : "STRONG",
  "event_key" : "click-me-cta",
}

Note: The event_key value (data-event-key attribute) is a more explicit keen-specific identifier that gives you an option outside of href, id, and class values to group or identify and query clicks in a meaningful way without potential ID/class collisions or dual-use naming schemes.

Want to get up and running faster? This can also be achieved in the browser with automated event tracking.


Track views of the HTML elements

Use Intersection Observer to track elements that have been seen by a user. In an example the CSS selector of the HTML elements is defined as .track-element-view. Use threshold to control the sensitivity of the Observer. Note: This feature works only on the browsers that support Intersection Observer.

import KeenTracking from 'keen-tracking';

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY'
});
const helpers = KeenTracking.helpers;

if(typeof IntersectionObserver !== 'undefined'){
  const elementViewsOptions = {
    threshold: 1.0,
  }
  const elementViewsCallback = (events, observer) => {
    events.forEach(el => {
      if(el.isIntersecting){
        return client
          .recordEvent({
            event_collection: 'element_views',
            event: {
              element: helpers.getDomNodeProfile(el.target)
           }
          });
      }
    });
  }
  const observer = new IntersectionObserver(elementViewsCallback, elementViewsOptions);
  const target = document.querySelectorAll('.track-element-view');
  target.forEach(el => {
    observer.observe(el);
  });
}

Block Bots and Improve Device Recognition

Install mobile-detect.js to identify basic device types and block noisy bots and crawlers.

npm install mobile-detect --save

This example further extends the client instance defined above, inserting a new tech.device_type property with three possible values: 'desktop', 'mobile', and 'tablet'. If the user agent is determined to be a bot, it may be ideal to abort and avoid recording an event.

import MobileDetect from 'mobile-detect';

const md = new MobileDetect(window.navigator.userAgent);
if (md.is('bot')) {
  return false;
}

// extends client instance defined previously
client.extendEvents(() => {
  return {
    tech: {
      device_type: md.tablet() ? 'tablet' : md.mobile() ? 'mobile' : 'desktop'
    }
  };
});

Check out the many additional methods supported by mobile-detect.js to further enrich your data model.

This can also be used with automated event tracking.


Server-side Event Tracking

const KeenTracking = require('keen-tracking');

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY'
});

// promise
client
  .recordEvent('purchases', {
    item: 'Avocado',
    number_of_items: 10,
    user: {
      name: 'John Promise'
    }
  })
  .then((response) => {
    // handle successful responses
  })
  .catch(error => {
    // handle errors
  });

// or callback
client
  .recordEvent('purchases', {
    item: 'Avocado',
    number_of_items: 10,
    user: {
      name: 'John Callback'
    }
  }, (error, response) => {
    if (error) {
      // handle errors
      return;
    }
    // handle responses
  });

Cancel request

const KeenTracking = require('keen-tracking');

const client = new KeenTracking({
    projectId: 'PROJECT_ID',
    writeKey: 'WRITE_KEY'
});

const query = client
  .recordEvent('purchases', {
    item: 'Avocado',
    number_of_items: 10,
    user: {
      name: 'John Promise'
    }
  })

// cancel
query.abort();

// handling response and cancel
query.then(res => {
    // response
}).catch((err) => {
    if (err === 'REQUEST_ABORTED') {
        // request canceled
    }
});

In browser environment make sure to attach AbortController polyfill to support browsers which do not have that feature. You can do it by adding this script on your website:

https://cdn.jsdelivr.net/npm/[email protected]/dist/umd-polyfill.min.js


Handling connection problems

When KeenTracking encounters connection problems, it will retry to send the data.

import KeenTracking from 'keen-tracking';

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY',

  // customize the default values
  retry: {
    limit: 10, // how many times retry to record an event
    initialDelay: 200, // initial delay between consecutive calls.
    // Each next retry will be delayed by (2^retries_count * 100) milliseconds,
    retryOnResponseStatuses: [ // array of invalid http response statuses
      408,
      500,
      502,
      503,
      504
    ]
  }
});

Unique events

Save the event only once.

client
  .recordEvent({
    event_collection: 'unique_clicks',
    event: {
      some_key: 'some_value',
      // ...
    },
    unique: true, // check if the event is unique, before sending to API
    cache: {
      storage: 'indexeddb', // for persistence. Remove this property to use RAM
      hashingMethod: 'md5', // remove this property to store as a stringified json
      maxAge: 1000 * 60, // store the information about unique value for 60 seconds
    }
  })
  .then((response) => {
    console.log('ok', response);
  })
  .catch(someError => {
    console.log('error', someError);
  });

Request types

By default, we make requests using the Fetch API.

For UI interactions, consider using the BeaconAPI. It's the fastest non-invasive way to track user behaviour. Due to its nature, BeaconAPI runs requests in the background, with no possibility to handle errors. If you want to handle errors, you need to use the Fetch API.

// specify request types for all requests
const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY',
  requestType: 'fetch' // fetch, beaconAPI, img
});

// you can use different requestType for a single request
client
  .recordEvent({
    event_collection: 'clicks',
    event: {
      some_key: 'some_value',
      // ...
    },
    requestType: 'beaconAPI'
  });

Custom Host

You can set a custom domain for requests

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY',
  host: 'somehost.com'
});

Recorded Event ID

A successful response from our API does not contain the ID of the newly created event. We are using Cassandra Database (NoSQL), so there are no joins. Store all necessary data in each event you record. Denormalization and duplication of data is a fact of life with Cassandra. Read more:


Tracking Opt-Out

It's easy to build tracking opt-out functionality. If opt-out is set to true no data is recorded.

You can set up opt-out by defining client instance

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY',
  optOut: true
});

or by invoking client.setOptOut(true) method

client.setOptOut(true);

Note: The user can block tracking in the browser by doNotTrack setting. We can respect or overwrite this setting by defining client instance

const client = new KeenTracking({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY',
  respectDoNotTrack: true // it's false by default
});

Contributing

This is an open source project and we love involvement from the community! Hit us up with pull requests and issues.

Learn more about contributing to this project.


Support

Need a hand with something? Shoot us an email at [email protected]. We're always happy to help, or just hear what you're building! Here are a few other resources worth checking out:

keen-tracking.js's People

Contributors

adamkasprowicz avatar aroc avatar dabs avatar dariuszlacheta avatar dillondoyle avatar dustinlarimer avatar elof avatar eyepulp avatar greghuc avatar haikyuu avatar hinaloe avatar iroth avatar jmousseau avatar kmcgaire avatar lukechilds avatar maciejrybaniec avatar sfalkoff avatar stof avatar tesarwijaya avatar tunedmidja avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

keen-tracking.js's Issues

Build and test Keen.Client instance

What does this do? Keen.Client is the core client instance constructor for all project-specific functionality. Keen.Client.prototype will carry all of the primary methods.

// Constructor config (optional)
var client = new Keen.Client({
  projectId: "YOUR_PROJECT_ID",
  writeKey: "YOUR_WRITE_KEY",
  host: "api.keen.io/3.0",
  protocol: "https", // "http" || "auto"
  requestType: "xhr" // "jsonp" || "beacon"
});

Miscellaneous methods:

// Config accessors (optional)
client.projectId("YOUR_PROJECT_ID");
client.writeKey("YOUR_WRITE_KEY");

// Get/extend base API URL, with optional params
client.url();
client.url("/events");
client.url("/events/name", { key: "value" });

Event Emitter:

client.on("some event", function(){
});
client.once("another event", function(){
});
client.off("some event");
client.emti("another event", { something: "to", pass: true });

Todo:

  • Build and test core methods
  • Build and test accessor methods (common spec)
  • Build and test misc methods

Rules re: architecture?

Hey,

This is pretty cool! Might be able to help out here as I'm experimenting with some stuff on our own SDK.

Is it possible to setup a more firm structure around the following:

  • External libraries: e.g.are we able to use jQuery/Zepto/etc. or not?
  • Functions: a template for how we should create one of the proposed functions. (it can get pretty messy otherwise)
  • More clear goals of where you want to take this project (lots of possible overlap of where keen-js is/should be + common-web)

Nima

writePath config option ignored

The README claims there is a writePath config property:

`writePath: '/3.0/projects/YOUR_PROJECT_ID/events'`

host and writePath: these options can be overwritten to make it easier than ever to proxy events through your own intermediary host.

Yet it seems completely ignored. Searching the codebase for writePath only find matches in the README.

Suggestion: cookie.set could return value instead of this

A very common use case with cookie is to try to get it, and if it does not exist set it instead.

As there is no function getOrSet, it would be great if setwould return the value instead of thisso that we could do that:

const userID = sessionCookie.get('user_id') || sessionCookie.set('user_id', Keen.helpers.getUniqueId())

Is there any internal reason for having set return this ? I haven't found any use case for that.

disabling event logging

I'm trying to turn off event-logging in dev environment, but Keen.enabled = false doesn't seem to do the trick (as evidenced by viewing my collections in the keen dash). Is there another step I have to take?

The project is react using webpack


// keen-config.js
import Keen from 'keen-tracking'
export const initKeen = () => {
    const client = new Keen({
        projectId,
        writeKey,
    })

    const sessionCookie = Keen.utils.cookie('wayhome-cookie')
    if (!sessionCookie.get('visitorID')) {
        sessionCookie.set('visitorID', Keen.helpers.getUniqueId())
    }
    const sessionID = Keen.helpers.getUniqueId()
    const sessionTimer = Keen.utils.timer()
    sessionTimer.start()
    client.extendEvents(() => {
       // yadda yadda yadda
    })
 return client
}


// index.js
const client = initKeen()
window.Keen = client
if (process.env.NODE_ENV === 'development') {
    console.log("DISABLING KEEN") 
    Keen.enabled = false
}

The console.log statement fires, so I know my conditional is working.

Build and test Keen.helpers

What does this do? These functions return objects with commonly used sets of properties. See: https://github.com/keen/keen-tracking.js#helpers for an initial list.

Todo:

  • Build initial set of helpers
  • Test initial set of helpers where reasonably possible for v1
Keen.helpers = {
    getBrowserProfile: function(){
        return {
            "cookies" : navigator.cookieEnabled,
            "screen"  : Keen.helpers.getScreenProperties(),
            "window"  : Keen.helpers.getWindowProperties()
            // and many more...
        };
    },
    getDatetimeIndex: function(obj){
        var date = obj || new Date();
        return {
            "hour-of-day"    : date.getHours(),
            "day-of-week"   : parseInt( 1 + date.getDay() ),
            "day-of-month" : date.getDate(),
            "month"            : parseInt( 1 + date.getMonth() ),
            "year"               : date.getFullYear()
        };
    },
    getDomEventProfile: function(e){
        return {
            "innerText": e.target.innerText,
            "path": Keen.helpers.getDomPath(e.target).join(' > '),
            "tagName": e.target.tagName,
            "title": e.target.title
            // and many more...
        };
    },
    getDomNodePath: function(el){
        // http://stackoverflow.com/a/16742828/2511985
        // returns something like "body > div#nav > ul > a#signup"
    },
    getScreenProperties: function(){
      // returns a curated screen profile
    },
    getWindowProperties: function(){
      // returns a curated window profile
    },
    getUniqueId: function(){
      // returns "f342s-3425423-dfsdfsdf12-123124fdvgdf"
    }
};

recordDeferredEvents() sends queued events in bulk, but doesn't handle individual event errors

recordDeferredEvents() utilizes the bulk tracking function, but flushes the entire queue – if an individual event is malformed (or fails for any other reason), it is lost forever.

Current implementation for reference:

    self.recordEvents(currentQueue.events, function(err, res){
      if (err) {
        // Retry once
        self.recordEvents(currentQueue.events);
      }
      else {
        currentQueue = void 0;
      }
    });

A proposed solution would be to walk through the entries in the Keen response, and emit some sort of failure for each invalid event (so the developers can implement any sort of notification/handling they wish).

node utils.cookie is undefined

I try to set utils.cookie like your example in the Readme but I get the error

KeenTracking.utils.cookie is not a function

I am using it exactly like this

const express = require('express')
const app = express()
var KeenTracking = require('keen-tracking');

var client = new KeenTracking({
  projectId: 'x',
  writeKey: 'x'
});

const utils = KeenTracking.utils;

var sessionCookie = KeenTracking.utils.cookie('cookie-session-test')

It looks like cookie is not available.

Script error binding locks up browsers with selective script blocking extensions

  • Install the uBlock Origin Chrome extension.
  • Visit http://parachutehome.com with the dev tools Network tab open.
  • Observe as the page attempts to load the same script, keen-tracking-1.1.3.min.js, as many times as it can per second, completely locking up the tab process indefinitely (unless you're very patient, this usually means killing the whole browser process and all tab memory). On my MacBook Pro that's 200k times in 5-10 minutes.

I believe the reason is this line: https://github.com/keen/keen-tracking.js/blob/master/lib/browser-async.js#L8

s.onload = s.onerror = s['onreadystatechange'] = function () { ...

I think that onerror binding is causing the blocked script to attempt to load in perpetuity when something like uBlock is intercepting the request. This is obviously not okay.

I'd suggest simply killing the onerror binding, I can't think why it'd be necessary.

image

Base64 data attributes are not url encoded

Base64 strings occasionally contain special characters (most notably, +). Right now keen-tracking does not URL Encode the base64 encoded data attribute before sending it. In certain scenarios this causes the URL to break, and the Keen API barfs.

Update Dev Workflow to Node v8.6

This project fails to start or run tests when running node versions beyond v6.

TypeError: Cannot read property 'prototype' of undefined
    at Object.<anonymous> (/Users/dustinlarimer/dev/keen/keen-tracking.js/node_modules/socket.io/lib/store.js:35:41)
    at Module._compile (module.js:624:30)
    at Object.Module._extensions..js (module.js:635:10)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)
    at Module.require (module.js:568:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/dustinlarimer/dev/keen/keen-tracking.js/node_modules/socket.io/lib/manager.js:16:13)
    at Module._compile (module.js:624:30)

This most likely means dependencies are out of date and need to be updated as well.

Document that `getDatetimeIndex()` default is *not* UTC

Hello!

When I started using getDatetimeIndex(), I assumed that it would be returning the time for UTC. I had to read the MDN page for Date() to realize that it was browser-local time. I think it would be handy to document this and perhaps also show how to get the DT in UTC[1]:

var now = new Date(); 
var now_utc = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),  now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds());
var utcDatetimeIndex = Keen.helpers.getDatetimeIndex(now_utc);

Thank you for your time and effort on this!

Cheers,
Fitz

[1] Adapted from: http://stackoverflow.com/a/6777470

Missing tag name

I think you are missing the name of the tag here in the text below on README.md. Above which tag should the snippet be pasted?

Copy/paste this snippet of JavaScript above the tag of your page to load the tracking library asynchronously.

Add keen-web-autocollector to this repo

I'm not seeing the web auto collector in any of the keen repos. Maybe I'm blind. I think it just sits on top of keen-tracking. It works great, but I think I need to extend it a bit to add some autofill type features. keen-tracking can pretty much be setup to do what it is doing, but it'd be nice to start somewhere a little higher up.

I can see the non minified code at https://d26b395fwzu5fz.cloudfront.net/keen-web-autocollector-1.0.8.js , but it is all webpackified. Be nice to see the source source.

Thanks!

minor: inconsistent use of snake_case and camelCase in library

Hello!

Most of your documentation uses snake_case key names in events. The getDatetimeIndex() helper, and the ip_to_geo, ua_parser, and url_parser addons all add fields to the event with snake_cased names. However, the getScreenProfile() helper adds camelCased keys (colorDepth, availHeight, etc.) to the event. It's a minor thing, but it would be nice if there was one consistent casing used for documentation and keen-provided addons and helpers, so that when we're writing queries, we don't have to remember which casing applies to which keys. Thank you!

Cheers,
@felliott

IP to Geo parser and dynamic IPs don't play ball

As per documentation:

input: An object with a key of “ip” and a value of the name of the property containing the IP address to parse.
This add-on can be used with the dynamic IP address placeholder.

This doesn't work and results in a 400:

{
   ...anyEvents,
    keen: {
      addons: [
        {
          name: 'keen:ip_to_geo',
          input: {
            ip: '${keen.ip}',
          },
          output: 'geo',
        },
]

This works:

{
   ...anyEvents,
    my_ip: '${keen.ip}',
    keen: {
      addons: [
        {
          name: 'keen:ip_to_geo',
          input: {
            ip: 'my_ip',
          },
          output: 'geo',
        },
]

am I doing it wrong?

Build and test recordEvent(s) functionality

What does this do? #recordEvent and #recordEvents are the core event transmission methods, and do all of the heavy-HTTP-lifting.

// var client = new Keen.Client({ .. });
client.recordEvent("collection", {}, function(err, res){ });
client.recordEvents({ "collection": [{}] }, function(err, res){ });

Todo:

  • Port addEvent(s) functionality over from keen-js
  • Set up browser testing (karma, phantomjs)
  • Set up server testing
  • Set up Travis integration for master branch and activate badge
  • Set up SauceLabs integration (local)
  • Ensure support for IE8-latest

Support HTTP rather than HTTPS

Looks like keen-core supports a 'protocol' option for clients, but keen-tracking enforces HTTPS even if the protocol is set to 'HTTP':

const client = new Keen({
  projectId: 'PROJECT_ID',
  writeKey: 'WRITE_KEY',
  host: 'localhost',
  protocol: 'http'
});

Can you add support for HTTP? Would be very useful for using the keen client against an internal proxy that might not support HTTPS.

JSON.parse is called on invalid json (response from sendEventData)

I got the following error on my production server today:

<html> 
^ 
SyntaxError: Unexpected token < in JSON at position 0 
    at JSON.parse (<anonymous>) 
    at IncomingMessage.<anonymous> (/app/node_modules/keen-tracking/lib/record-events-server.js:206:22) 

The error occurs at /app/node_modules/keen-tracking/lib/record-events-server.js:206:22 where you have: var res = JSON.parse(body), error;

It's been my experience that you should always wrap any call to JSON.parse in a try/catch block.

I'm sure that's not enough for a fix - seems like the keen server is returning an html body instead of json - so you might have a bigger issue here, and I'm not even sure if I was sending something invalid in my request (because the error, as you can see, cannot be parsed).

recordDeferredEvents() flushes the entire current queue, drops them completely recordEvents() fails twice

recordDeferredEvents() utilizes the bulk tracking function, but flushes the entire queue – if the XHR fails twice in a row, these events are lost forever. This is not ideal for clients with potential connectivity issues, such as mobile devices.

Current implementation for reference:

function recordDeferredEvents(){
  var self = this, currentQueue;
  if (self.queue.capacity > 0) {
    currentQueue = JSON.parse(JSON.stringify(self.queue));
    self.queue = queue();
    self.queue.options = currentQueue.options;

    self.emit('recordDeferredEvents', currentQueue.events);
    self.recordEvents(currentQueue.events, function(err, res){
      if (err) {
        // Retry once
        self.recordEvents(currentQueue.events);
      }
      else {
        currentQueue = void 0;
      }
    });
  }
  return self;
}

A proposed solution would be to add the events back into the queue upon 2x failures, rather than simply moving on.

Queue still prevents process from exiting

The internal event queue used by client.defefEvent() and client.deferEvents() still prevents processes from exiting in NodeJS, because the default queueInterval is greater than 0. This hanging behavior was presumed to be fixed in the previous release, but the issue is still present for use cases where the queue is not actually used.

As a temporary workaround, the following setting will allow processes to exit properly:

const client = new Keen({ 
  /* Configure*/ 
});
client.queueInterval(0);

clearing/checking extendEvents

Is it possible to clear the data set with a call to extendEvents()?

And, related: is it possible to check & see what the current extendEvents() inclusions are? eg

client.extendEvents(() => {
  return {
    time: Keen.helpers.getDateTimeIndex(),
    userId: a9sjkll3,
    sessionID: Keen.helpers.getUniqueId()
  }
})

// and then:
client.getEventExtensions()
/* returns: 
{
         time: 12345134134,
         userId: a9sjkll3,
         sessionID: <random uuid>
}
*/

Remove json3 dependency

It is not up to keen-tracking to attempt to patch the working environment. json3 can be a peer dependency, but it really shouldn't be even that.

Add a note to the ./README.md that for keen-tracking to work, window.JSON must be present.

Now you are adding an extra 42.28kB to everyones bundle because some (what? I don't know a single browser that does not implement window.JSON) browsers do not implement it.

Build top-level Keen settings and functions, utils

What does this do? The top-level Keen namespace will carry several sets of goodies that make robust event modeling super easy.

// Set debug to true to see console messages (if supported)
Keen.debug = false;
// Set enabled to false to disable event transmission
Keen.enabled = true;
// Used internally to log messages to the console
Keen.log( /* say something :) */ );
// Relocates current Keen namespace to a given variable and restores previous version
var K2 = Keen.noConflict();

Utilities:

Keen.utils.each(objectOrArray, function(item, indexOrKey){
});
Keen.utils.extend(primaryObject, objectToInherit);
Keen.utils.parseParams("?urlQueryString=true&value=123");

Version 1.0.5 in the CDN

Hello,

The new version 1.0.5 doesn't seem to be available in a Keen CDN, or at least I haven't found any link to it (https://d26b395fwzu5fz.cloudfront.net/keen-tracking-1.0.5.min.js returns a 403 for instance).

Would be nice if the new version was available and the documentation updated in consequence.

Give us a way to stop the queue interval

I'm writing a seed script that records some predefined events for development purpose. But the script never finishes, and I was stuck for a few hours until I find out that this lib sets an interval and that prevents node process from exiting.

It would be great if I could stop that interval when my script finishes sending the events so that the process can exit normally.

For now I'm forcing process.exit() when I'm done.

initAutoTracking not setting user cookie

What did I do?
I include the code from the Readme in my page to enable autotracking.

What happens?
Each pageview creates a new user unique id. keen cookie is set to {}.

What do I expect to happen?
Cookie should be set once for each user, same unique id should be used for same user.

What I think is happening
Looking at the code for cookie.prototype.set, it doesn't seem to be handling the case where 3 arguments are passed in correctly.

input with name of 'submit' generates error

this is an odd one :)

When using keen-tracking-1.4.0.js for general web tracking, an element of type submit with name of submit (<input name="submit" type="submit" value="Go">) generates following console error and then prevents form from submitting:

Uncaught TypeError: t.submit is not a function at keen-tracking-1.4.0.min.js:1

as a side note, if the name is Submit (note capital 'S') then it works fine.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.