@kano/kbc-telemetry
Advanced tools
Comparing version 4.3.0-alpha.0 to 4.4.0-alpha.0
@@ -18,2 +18,3 @@ "use strict"; | ||
const react_tracking_1 = require("react-tracking"); | ||
const react_detect_offline_1 = require("react-detect-offline"); | ||
const telemetry_1 = require("./utils/telemetry"); | ||
@@ -23,8 +24,14 @@ let Tracking = class Tracking extends React.Component { | ||
super(props); | ||
this.telemetryClient = new telemetry_1.TelemetryClient(this.props.config); | ||
} | ||
componentDidMount() { | ||
telemetry_1.initialiseTracking(this.props.config); | ||
updateStatus(online) { | ||
this.telemetryClient.updateOnline(online); | ||
} | ||
render() { | ||
return (React.createElement(React.Fragment, null, this.props.children)); | ||
return (React.createElement(React.Fragment, null, | ||
React.createElement(react_detect_offline_1.Detector, { render: ({ online }) => { | ||
this.updateStatus(online); | ||
return (React.createElement(React.Fragment, null)); | ||
} }), | ||
this.props.children)); | ||
} | ||
@@ -31,0 +38,0 @@ }; |
@@ -1,5 +0,22 @@ | ||
export declare const initialiseTracking: (config: { | ||
interface IConfig { | ||
url: string; | ||
env: string; | ||
interval: number; | ||
}) => void; | ||
} | ||
export declare class TelemetryClient { | ||
config: IConfig; | ||
sendingInProgress: boolean; | ||
storeName: string; | ||
acceptedEnv: boolean; | ||
online: boolean; | ||
constructor(config: any); | ||
updateOnline(online: boolean): Promise<void>; | ||
updateLocalStorage(data: any): void; | ||
initialiseTracking(): Promise<void>; | ||
triggerDataSend(data: any, interval?: any): Promise<void>; | ||
sendData(url: string, batch: { | ||
data: any; | ||
collection: string; | ||
}): Promise<any>; | ||
} | ||
export default TelemetryClient; |
@@ -10,67 +10,120 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.initialiseTracking = (config) => { | ||
const { url, env, interval } = config; | ||
let trackingSending = false; | ||
if (url && (env === 'staging' || env === 'production')) { | ||
const trackingFunc = setInterval(() => __awaiter(this, void 0, void 0, function* () { | ||
if (window.dataLayer && window.dataLayer.length > 0 && !trackingSending) { | ||
trackingSending = true; | ||
const data = [...window.dataLayer]; | ||
window.dataLayer = []; | ||
try { | ||
yield sendData(url, { data, collection: env }); | ||
trackingSending = false; | ||
const local_storage_1 = __importDefault(require("./local-storage")); | ||
class TelemetryClient { | ||
constructor(config) { | ||
this.config = config; | ||
this.sendingInProgress = false; | ||
this.storeName = `kano-telemetry-${config.env}`; | ||
this.acceptedEnv = config.env === 'staging' || config.env === 'production'; | ||
this.initialiseTracking(); | ||
} | ||
updateOnline(online) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (this.online && !online) { | ||
this.online = online; | ||
} | ||
else if (!this.online && online) { | ||
this.online = online; | ||
if (this.acceptedEnv) { | ||
const store = JSON.parse(local_storage_1.default.read(this.storeName)); | ||
if (store) { | ||
yield this.triggerDataSend(store); | ||
local_storage_1.default.remove(this.storeName); | ||
} | ||
} | ||
catch (error) { | ||
console.error(error); | ||
clearInterval(trackingFunc); | ||
trackingSending = false; | ||
} | ||
} | ||
}), interval); | ||
}); | ||
} | ||
}; | ||
const sendData = (url = '', batch) => __awaiter(this, void 0, void 0, function* () { | ||
const events = batch.data.map((e) => { | ||
if (e.error) { | ||
e.event = 'error'; | ||
e.data = e.error; | ||
} | ||
return JSON.stringify({ | ||
page_path: e.page || window.location.pathname, | ||
name: e.event, | ||
properties: e.data, | ||
action: e.action, | ||
module: e.module, | ||
time: Math.round(e.date.getTime()), | ||
timezone_offset: e.date.getTimezoneOffset(), | ||
session_id: e.sessionId, | ||
user_id: e.userId, | ||
version: e.appVersion, | ||
updateLocalStorage(data) { | ||
const current = local_storage_1.default.read(this.storeName); | ||
const store = current ? [...JSON.parse(current), ...data] : [...data]; | ||
local_storage_1.default.write(this.storeName, JSON.stringify(store)); | ||
} | ||
initialiseTracking() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const { url, interval } = this.config; | ||
if (url && this.acceptedEnv) { | ||
const trackingInterval = setInterval(() => __awaiter(this, void 0, void 0, function* () { | ||
if (window.dataLayer && window.dataLayer.length > 0 && !this.sendingInProgress) { | ||
const data = [...window.dataLayer]; | ||
window.dataLayer = []; | ||
if (!this.online) { | ||
return this.updateLocalStorage(data); | ||
} | ||
yield this.triggerDataSend(data, trackingInterval); | ||
} | ||
}), interval); | ||
} | ||
}); | ||
}); | ||
const packet = { | ||
n: batch.data[0].app, | ||
d: events, | ||
c: batch.collection, | ||
v: batch.data[0].appVersion, | ||
}; | ||
const proxyurl = 'https://cors-anywhere.herokuapp.com/'; | ||
const response = yield fetch(proxyurl + url, { | ||
method: 'PUT', | ||
body: JSON.stringify(packet), | ||
headers: new Headers({ | ||
'Content-Type': 'application/json', | ||
}), | ||
}).then((res) => { | ||
if (res.status === 200) { | ||
return res.json(); | ||
} | ||
return res.text().then((text) => { | ||
throw new Error(text); | ||
} | ||
triggerDataSend(data, interval) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const { url, env } = this.config; | ||
this.sendingInProgress = true; | ||
try { | ||
yield this.sendData(url, { data, collection: env }); | ||
this.sendingInProgress = false; | ||
} | ||
catch (error) { | ||
console.error(error); | ||
if (interval) | ||
clearInterval(interval); | ||
this.sendingInProgress = false; | ||
} | ||
}); | ||
}); | ||
return response; | ||
}); | ||
} | ||
sendData(url = '', batch) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const events = batch.data.map((e) => { | ||
if (e.error) { | ||
e.event = 'error'; | ||
e.data = e.error; | ||
} | ||
e.date = new Date(e.date); | ||
return JSON.stringify({ | ||
page_path: e.page || window.location.pathname, | ||
name: e.event, | ||
properties: e.data, | ||
action: e.action, | ||
module: e.module, | ||
time: Math.round(e.date.getTime()), | ||
timezone_offset: e.date.getTimezoneOffset(), | ||
session_id: e.sessionId, | ||
user_id: e.userId, | ||
version: e.appVersion, | ||
}); | ||
}); | ||
const packet = { | ||
n: batch.data[0].app, | ||
d: events, | ||
c: batch.collection, | ||
v: batch.data[0].appVersion, | ||
}; | ||
const proxyurl = 'https://cors-anywhere.herokuapp.com/'; | ||
const response = yield fetch(proxyurl + url, { | ||
method: 'PUT', | ||
body: JSON.stringify(packet), | ||
headers: new Headers({ | ||
'Content-Type': 'application/json', | ||
}), | ||
}).then((res) => { | ||
if (res.status === 200) { | ||
return res.json(); | ||
} | ||
return res.text().then((text) => { | ||
throw new Error(text); | ||
}); | ||
}).catch((err) => { | ||
console.log(err); | ||
}); | ||
return response; | ||
}); | ||
} | ||
} | ||
exports.TelemetryClient = TelemetryClient; | ||
exports.default = TelemetryClient; | ||
//# sourceMappingURL=telemetry.js.map |
{ | ||
"name": "@kano/kbc-telemetry", | ||
"version": "4.3.0-alpha.0", | ||
"version": "4.4.0-alpha.0", | ||
"description": "Telemetry module for boilerplate apps, using react-tracking", | ||
@@ -25,2 +25,3 @@ "author": "Kano Computing", | ||
"dependencies": { | ||
"react-detect-offline": "^2.4.0", | ||
"react-tracking": "^7.3.0" | ||
@@ -35,3 +36,3 @@ }, | ||
}, | ||
"gitHead": "617c06be6752af2dd3fe027a87ba4bbb10927c83" | ||
"gitHead": "73448be5976f9c674878d483d7d51ed8e33bae30" | ||
} |
106
README.md
@@ -1,7 +0,11 @@ | ||
# `kbc-telemetry` | ||
# kbc-telemetry | ||
Telemetry module for boilerplate apps, using react-tracking | ||
Telemetry module for boilerplate apps, using `react-tracking`. For more information, see [NYTimes React Tracking](https://github.com/nytimes/react-tracking). | ||
## Usage | ||
### Setup | ||
Import and use TelemetryProvider component as a wrapper for your app: | ||
``` | ||
@@ -11,5 +15,7 @@ import * as Telemetry from '@kano/kbc-telemetry'; | ||
const Tracking = Telemetry.TelemetryProvider; | ||
``` | ||
// Wrap your app in <Tracking> and pass config info | ||
Wrap your app in `<Tracking>` and pass config information. | ||
``` | ||
const config = { | ||
@@ -33,60 +39,77 @@ app: app.name, | ||
Tracking data can be sent as decorators in container/component files: | ||
See https://github.com/nytimes/react-tracking README | ||
### Within components | ||
Tracking data can be sent in multiple ways: | ||
#### Decorators | ||
Either on component level or at method level. | ||
``` | ||
import React, { Component } from 'react'; | ||
import * as Telemetry from '@kano/kbc-telemetry'; | ||
@Telemetry.track({ event: 'name-of-your-event' }) | ||
@Telemetry.track({ event: 'name_of_your_event' }) | ||
class MyComponent extends Component { | ||
@Telemetry.track({ event: 'I-did-something', action: 'click' }) | ||
@Telemetry.track({ event: 'I_did_something', action: 'click' }) | ||
handleSomething() { | ||
//I run something | ||
// I run something | ||
} | ||
} | ||
``` | ||
render() { | ||
return <div></div>; | ||
} | ||
Above shows the initial parameter as an object. But it can also be sent as a function, which accesses 3 parameters component `props`, component `state`, function arguments as an array, for example: | ||
``` | ||
@Telemetry.track((props, state, args) => ({ event: props.event, data: { something: state.something, args: args[0] } }) | ||
handleSomething(iAmArgsZero) { | ||
// I run something | ||
} | ||
``` | ||
OR from props as `this.props.tracking.trackEvent({})`, as below: | ||
For decorators, there are explict fields that can be set as follows: | ||
| Decorator | Field Options | | ||
| --------------------------- | ---------------- | | ||
| `@Telemetry.track()` | `event?: String` <br/> `data?: Any` <br/> `action?: String` <br/> `module?: String` <br/> `userId?: String` <br/> `page?: String` | | ||
| `@Telemetry.trackError()` | `error: ` <br/> `{ name: String, stack: String, message: String }` | | ||
#### Props | ||
You can also use as props: `this.props.tracking.trackEvent({})`. This currently doesn't enforce the field options above. | ||
To use tracking in props, the component **must** be either exported by wrapping it in `track()`, as follows: | ||
``` | ||
@Telemetry.track({}) // This only needs to be included here if tracking is not being passed from a higher level component. | ||
class MyComponent extends Component { | ||
const FooPage = (props) => { | ||
return <div onClick={() => props.tracking.trackEvent({ action: 'click' })} />; | ||
} | ||
handleSomething() { | ||
this.props.tracking.trackEvent({ event: 'i-handle-something' }) | ||
} | ||
export default track({ | ||
page: 'FooPage', | ||
})(FooPage); | ||
render() { | ||
return <div></div>; | ||
} | ||
} | ||
``` | ||
### Errors | ||
##### OR | ||
Currently `trackError` can only be used as a decorator. Like so in 2 ways: | ||
Wrapped in a decorator: | ||
``` | ||
@Telemetry.track({ page: 'MyComponent' }) | ||
class MyComponent extends Component { | ||
handleSomething() { | ||
this.props.tracking.trackEvent({ event: 'i_handle_something' }) | ||
} | ||
} | ||
``` | ||
@Telemetry.trackError({ error: { name: 'handle-error', stack: 'stack', message: 'message' } }) | ||
handleError() { | ||
this.props.tracking.trackEvent({ event: 'i-handle-something' }) | ||
} | ||
### Tracking Errors | ||
// If you want to send error stacks/messages, these can be accessed by the third arguement | ||
@Telemetry.trackError((props, state, args) => { error: { name: 'handle-error', stack: args[0].stack, message: args[0].message } }) | ||
handleDifferentError(err) { | ||
this.props.tracking.trackEvent({ event: 'i-handle-something' }) | ||
} | ||
Currently `trackError` can only be used as a decorator. See below and see field options in the table above. | ||
render() { | ||
return <div></div>; | ||
} | ||
``` | ||
@Telemetry.trackError({ error: { name: 'handle_error', stack: 'stack', message: 'message' } }) | ||
handleError() { | ||
// I handle an error | ||
} | ||
@@ -98,12 +121,5 @@ ``` | ||
``` | ||
class MyComponent extends Component { | ||
handleSomething() { | ||
this.props.tracking.trackEvent({ error: { name: 'handle-error', stack: 'stack', message: 'message' } }) | ||
} | ||
render() { | ||
return <div></div>; | ||
} | ||
handleSomething() { | ||
this.props.tracking.trackEvent({ error: { name: 'handle_error', stack: 'stack', message: 'message' } }) | ||
} | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21450
17
264
123
2
+ Addedreact-detect-offline@^2.4.0
+ Addedreact-detect-offline@2.4.5(transitive)