Skip to content

Commit

Permalink
WIP: feat: add core interfaces and classes
Browse files Browse the repository at this point in the history
  • Loading branch information
yangchristian committed Jul 11, 2017
1 parent c994174 commit 62e0030
Show file tree
Hide file tree
Showing 14 changed files with 170 additions and 28 deletions.
42 changes: 42 additions & 0 deletions src/core/crosslytics.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import test from "ava";
import { Crosslytics } from "./crosslytics";
import { TrackedEvent } from "./trackedEvent";
import { Tracker } from "./tracker";
import { Identity } from "./identity";

type TestEventArgs = {
"Color": string;
};

class TestEvent extends TrackedEvent<TestEventArgs> {
name = "Test Event";
category = "Test Category";
organizationId = "abc123";
argPriority = new Array<keyof TestEventArgs>();
}

class TestTracker implements Tracker {
public track<T>(identity: Identity, event: TrackedEvent<T>) {
return Promise.resolve();
}
}

test("Should only register a Tracker once", t => {
const cl = new Crosslytics();
const tracker = new TestTracker();
cl.registerTracker(tracker);
cl.registerTracker(tracker);
t.is(cl.trackers.length, 1);
});

test("Should deregister a Tracker", t => {
const cl = new Crosslytics();
const trackerA = new TestTracker();
const trackerB = new TestTracker();
cl.registerTracker(trackerA);
cl.registerTracker(trackerB);
t.is(cl.trackers.length, 2);
cl.deregisterTracker(trackerB);
cl.deregisterTracker(trackerB);
t.is(cl.trackers.length, 1);
});
30 changes: 30 additions & 0 deletions src/core/crosslytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Identity } from "./identity";
import { TrackedEvent } from "./trackedEvent";
import { Tracker } from "./tracker";

/**
* Main API entry point
*/
export class Crosslytics {
public identity: Identity;

public readonly trackers = new Array<Tracker>();
public registerTracker(tracker: Tracker) {
if (~this.trackers.indexOf(tracker)) {
return;
}

this.trackers.push(tracker);
}

public deregisterTracker(tracker: Tracker) {
const index = this.trackers.indexOf(tracker);
if (~index) {
this.trackers.splice(index, 1);
}
}

public async track<T>(event: TrackedEvent<T>) {
return this.trackers.map(t => t.track(this.identity, event));
}
}
16 changes: 16 additions & 0 deletions src/core/identity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Organization } from "./organization";
import { Value } from "./value";

/**
* A user that you're tracking. Logically equivalent to an Identity in the Segment spec.
* @see {@link https://segment.com/docs/spec/identify/#identities}
*/
export interface Identity {
userId: string;
organization?: Organization;
traits?: {
email: string;
name: string;
[key: string]: Value;
};
}
13 changes: 13 additions & 0 deletions src/core/organization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Value } from "./value";

/**
* A group of users. Logically equivalent to a Group in the Segment spec.
* @see {@link https://segment.com/docs/spec/group/}
*/
export interface Organization {
organizationId: string;
traits?: {
name: string;
[key: string]: Value;
};
}
45 changes: 45 additions & 0 deletions src/core/trackedEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Value } from "./value";

/**
* A user action. Logically equivalent to an Event in the Segment spec.
* Pass in a type defining your event's arguments.
* @example
* type DashboardPanelEventArgs = {
* 'Panel ID': string,
* 'Panel Color'?: string,
* 'Panel Type'?: number,
* 'Panel Name'?: string
* };
* class DashboardPanelCreated extends TrackedEvent<DashboardPanelEventArgs> {
* readonly name = 'DashboardPanel Created';
* readonly category = 'Dashboard';
* readonly argPriority: (keyof DashboardPanelEventArgs)[] = [
* 'Panel ID',
* 'Panel Type',
* 'Panel Name',
* 'Panel Color'
* ];
* }
* @see {@link https://segment.com/docs/spec/track/#event}
*/
export abstract class TrackedEvent<T> {
/**
* We suggest human readable names consisting of noun + past tense verb.
* @see {@link https://segment.com/academy/collecting-data/naming-conventions-for-clean-data/}
*/
public abstract readonly name: string;
public abstract readonly category: string;
public organizationId: string;

/**
* Many trackers only support a limited number of arguments. For example,
* Google Analytics only supports 2: a string Event Label and an integer
* Event Value. By defining a priority to your arguments here, trackers
* will submit the highest priority args satisfying their constraints. In
* the Google Analytics case, the tracker will submit the first string match
* as the Label and the first integer match as the Value.
*/
public abstract readonly argPriority: Array<keyof T>;

constructor(public args: T) {}
}
10 changes: 10 additions & 0 deletions src/core/tracker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Identity } from "./identity";
import { TrackedEvent } from "./trackedEvent";

/**
* A Tracker is a 3rd party analytics service such as Google Analytics or Intercom
* that ultimately receives your `TrackedEvent`s.
*/
export interface Tracker {
track<T>(identity: Identity, event: TrackedEvent<T>): Promise<void>;
}
1 change: 1 addition & 0 deletions src/core/value.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Value = string | number;
7 changes: 0 additions & 7 deletions src/greeter.spec.ts

This file was deleted.

11 changes: 0 additions & 11 deletions src/greeter.ts

This file was deleted.

6 changes: 0 additions & 6 deletions src/index.spec.ts

This file was deleted.

6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export * from "./greeter";
export * from "./core/crosslytics";
export * from "./core/identity";
export * from "./core/organization";
export * from "./core/trackedEvent";
export * from "./core/tracker";
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"lib": [
"esnext"
],
"target": "es2015",
"target": "es6",
"noImplicitAny": true,
"strictNullChecks": true,
"outDir": "./lib",
"preserveConstEnums": true,
"removeComments": true,
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"target": "es6",
"outDir": "lib_test",
"declaration": false,
"noImplicitAny": true,
Expand Down
6 changes: 5 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@
"extends": [
"tslint:latest",
"tslint-config-prettier"
]
],
"rules": {
"interface-name": [true, "never-prefix"],
"quotemark": [ true, "single" ]
}
}

0 comments on commit 62e0030

Please sign in to comment.