@aicore/core-analytics-client-lib 1.0.5 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -30
- package/dist/analytics.min.js +3 -3
- package/dist/analytics.min.js.map +1 -1
- package/package.json +1 -1
- package/src/analytics.js +110 -69
package/README.md
CHANGED
|
@@ -20,41 +20,31 @@ events for [Core-Analytics-Server](https://github.com/aicore/Core-Analytics-Serv
|
|
|
20
20
|
# Usage
|
|
21
21
|
|
|
22
22
|
## Load the Library
|
|
23
|
-
Embed the script in your HTML file
|
|
23
|
+
Embed the script in your HTML file and replace `your_analytics_account_ID` and `appName`
|
|
24
|
+
in the `initAnalyticsSession` call below:
|
|
24
25
|
```html
|
|
25
|
-
|
|
26
|
+
<!-- Global window.analytics object - core.ai analytics services -->
|
|
27
|
+
<script async src="https://unpkg.com/@aicore/core-analytics-client-lib/src/analytics.js"
|
|
28
|
+
onload="analyticsLibLoaded()"></script>
|
|
29
|
+
<script>
|
|
30
|
+
if(!window.analytics){ window.analytics = {
|
|
31
|
+
_initData : [], loadStartTime: new Date().getTime(),
|
|
32
|
+
event: function (){window.analytics._initData.push(arguments);}
|
|
33
|
+
};}
|
|
34
|
+
function analyticsLibLoaded() {
|
|
35
|
+
initAnalyticsSession('your_analytics_account_ID', 'appName');
|
|
36
|
+
analytics.event("core-analytics", "client-lib", "loadTime", 1, (new Date().getTime())-analytics.loadStartTime);
|
|
37
|
+
}
|
|
38
|
+
</script>
|
|
26
39
|
```
|
|
27
40
|
This will create a global `analytics` variable which can be used to access the analytics APIs.
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* `accountID`: Your analytics account id as configured in the server or core.ai analytics
|
|
33
|
-
* `appName`: The app name to log the events against. Eg: "phoenixCode"
|
|
34
|
-
* `postIntervalSeconds` (_Optional_): This defines the interval between sending analytics events to the server. Default is 10 minutes
|
|
35
|
-
* `granularitySec` (_Optional_): The smallest time period under which the events can be distinguished. Multiple
|
|
36
|
-
events happening during this time period is aggregated to a count. The default granularity is 3 Seconds, which means
|
|
37
|
-
that any events that happen within 3 seconds cannot be distinguished in ordering.
|
|
38
|
-
* `analyticsURL` (_Optional_): Provide your own analytics server address if you self-hosted the server
|
|
39
|
-
* `debug` (_Optional_): set to true if you want to see detailed debug logs.
|
|
40
|
-
|
|
41
|
-
### usageExample
|
|
42
|
-
```javascript
|
|
43
|
-
// Init with default values.
|
|
44
|
-
analytics.initSession("accountID", "appName");
|
|
45
|
-
|
|
46
|
-
// Example for custom initSession where the analytics aggregated data
|
|
47
|
-
// is posted to custom server https://localhost:3000 every 600 secs
|
|
48
|
-
// with a granularity(resolution) of 5 seconds.
|
|
49
|
-
analytics.initSession("accountID", "appName", "https://localhost:3000", 600, 5);
|
|
50
|
-
|
|
51
|
-
// To initSession in debug mode set debug arg in init to true:
|
|
52
|
-
analytics.initSession("accountID", "appName", "https://localhost:3000", 600, 5, true);
|
|
53
|
-
```
|
|
42
|
+
NB: The script is loaded async, so it will not block other js scripts. `analytics.event` api can be called anytime
|
|
43
|
+
after the above code and need not wait for the script load to complete.
|
|
54
44
|
|
|
55
45
|
## Raising analytics events
|
|
56
|
-
|
|
57
|
-
The
|
|
46
|
+
We can now start logging analytics events by calling `analytics.event` API.
|
|
47
|
+
The events will be aggregated and send to the analytics server periodically.
|
|
58
48
|
|
|
59
49
|
```javascript
|
|
60
50
|
// analyticsEvent(eventType, eventCategory, subCategory, eventCount, eventValue);
|
|
@@ -79,9 +69,87 @@ analytics.event("platform", "CPU", "coreCountsAndFrequencyMhz", 8, 2300);
|
|
|
79
69
|
* `eventCategory` - A string, required
|
|
80
70
|
* `subCategory` - A string, required
|
|
81
71
|
* `eventCount` (_Optional_) : A non-negative number indicating the number of times the event (or an event with a
|
|
82
|
-
particular value if a value is specified) happened. defaults to 1.
|
|
72
|
+
particular value if a value is specified) happened. defaults to 1.
|
|
83
73
|
* `eventValue` (_Optional_) : A number value associated with the event. defaults to 0
|
|
84
74
|
|
|
75
|
+
|
|
76
|
+
## Advanced Usages
|
|
77
|
+
|
|
78
|
+
### Pure JS loading instead of HTML scripts
|
|
79
|
+
|
|
80
|
+
There may be cases where you would want to load the script from JS alone. For Eg. you
|
|
81
|
+
may want to delay library loading till user consents GDPR. For such use cases, use the below code.
|
|
82
|
+
|
|
83
|
+
```js
|
|
84
|
+
function _initCoreAnalytics() {
|
|
85
|
+
// Load core analytics scripts
|
|
86
|
+
if(!window.analytics){ window.analytics = {
|
|
87
|
+
_initData: [], loadStartTime: new Date().getTime(),
|
|
88
|
+
event: function (){window.analytics._initData.push(arguments);}
|
|
89
|
+
};}
|
|
90
|
+
let script = document.createElement('script');
|
|
91
|
+
script.type = 'text/javascript';
|
|
92
|
+
script.async = true;
|
|
93
|
+
script.onload = function(){
|
|
94
|
+
// replace `your_analytics_account_ID` and `appName` below with your values
|
|
95
|
+
window.initAnalyticsSession('your_analytics_account_ID', 'appName'); // if you have a custom analytics server
|
|
96
|
+
window.analytics.event("core-analytics", "client-lib", "loadTime", 1,
|
|
97
|
+
(new Date().getTime())- window.analytics.loadStartTime);
|
|
98
|
+
};
|
|
99
|
+
script.src = 'https://unpkg.com/@aicore/core-analytics-client-lib/dist/analytics.min.js';
|
|
100
|
+
document.getElementsByTagName('head')[0].appendChild(script);
|
|
101
|
+
}
|
|
102
|
+
_initCoreAnalytics();
|
|
103
|
+
```
|
|
104
|
+
To load the library, just call `_initCoreAnalytics()` from JS. Note that you may not be able
|
|
105
|
+
to use `analytics.event()` APIs before `_initCoreAnalytics()` call is made.
|
|
106
|
+
|
|
107
|
+
### initAnalyticsSession: modify when, where and how analytics lib sends events
|
|
108
|
+
If you want to modify how analytics library collects and sends information, it is recommended to do so
|
|
109
|
+
with analytics server [accountConfig](https://github.com/aicore/Core-Analytics-Server#accountconfig-configuration).
|
|
110
|
+
|
|
111
|
+
Alternatively for one off development time uses, the behavior of the library can be configured
|
|
112
|
+
during the `initAnalyticsSession` call. `initAnalyticsSession()` takes the following parameters:
|
|
113
|
+
|
|
114
|
+
* `accountID`: Your analytics account id as configured in the server or core.ai analytics
|
|
115
|
+
* `appName`: The app name to log the events against. Eg: "phoenixCode"
|
|
116
|
+
* `analyticsURL` (_Optional_): Provide your own analytics server address if you self-hosted the server.
|
|
117
|
+
* `postIntervalSeconds` (_Optional_): This defines the interval between sending analytics events to the server. Default is 1 minutes or server controlled.
|
|
118
|
+
* `granularitySec` (_Optional_): The smallest time period under which the events can be distinguished. Multiple
|
|
119
|
+
events happening during this time period is aggregated to a count. The default granularity is 3 Seconds or server controlled,
|
|
120
|
+
which means that any events that happen within 3 seconds cannot be distinguished in ordering.
|
|
121
|
+
* `debug` (_Optional_): set to true if you want to see detailed debug logs.
|
|
122
|
+
|
|
123
|
+
#### usageExample
|
|
124
|
+
```javascript
|
|
125
|
+
// Init with default values and server controlled config. use the following `analyticsLibLoaded` function
|
|
126
|
+
function analyticsLibLoaded() {
|
|
127
|
+
initAnalyticsSession('your_analytics_account_ID', 'appName');
|
|
128
|
+
analytics.event("core-analytics", "client-lib", "loadTime", 1, (new Date().getTime())-analytics.loadStartTime);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
//Replace initAnalyticsSession in analyticsLibLoaded function for the below use cases.
|
|
132
|
+
|
|
133
|
+
// Example for custom initSession where the analytics aggregated data
|
|
134
|
+
// is posted to custom server https://localhost:3000 every 600 secs
|
|
135
|
+
// with a granularity(resolution) of 5 seconds.
|
|
136
|
+
initAnalyticsSession("accountID", "appName", "https://localhost:3000", 600, 5);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Debug logs
|
|
140
|
+
If you want to see detailed logs on what is happening inside analytics lib, use the below code:
|
|
141
|
+
```js
|
|
142
|
+
// To initSession in debug mode set debug arg in init to true. In debug mode, details logs
|
|
143
|
+
// about analytics library events will be emitted.
|
|
144
|
+
initAnalyticsSession("accountID", "appName", "https://localhost:3000", 600, 5, true);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
To see info level logs that shows actual analytics data being sent to server, set the below property:
|
|
148
|
+
```js
|
|
149
|
+
window.analytics.debugInfoLogsEnable = true;
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
|
|
85
153
|
# Contribute to core-analytics-client-lib
|
|
86
154
|
|
|
87
155
|
## Building
|
package/dist/analytics.min.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
typically means that you may be sending too many value events? .`),
|
|
3
|
-
postIntervalSeconds:${
|
|
1
|
+
function initAnalyticsSession(e,t,n,a,o,i){let s,r,l,u,c,f,d,v,y={};const g="aicore.analytics.userID",m="aicore.analytics.sessionID",p=1e4;let h=null;var w,b="undefined"==typeof window;let C="https://analytics.core.ai",I,S,D=0,E=!1,T=!1;function N(...e){T&&console.log("analytics client: ",...e)}function $(...e){T&&console.error("analytics client: ",...e)}if(b)throw new Error("Node environment is not currently supported");function k(){return{schemaVersion:1,accountID:s,appName:r,uuid:l,sessionID:u,unixTimestampUTC:+new Date,numEventsTotal:0,events:{}}}function A(){if(!h)throw new Error("Please call initSession before using any analytics event")}function L(e){e.backoffCount=(e.backoffCount||0)+1,N(`Failed to call core analytics server. Will retry in ${30*e.backoffCount}s: `),setTimeout(()=>{U(e)},3e4*e.backoffCount)}function O(t){var e,n=JSON.stringify(t);n.length>p&&console.warn(`Analytics event generated is very large at greater than ${n.length}B. This
|
|
2
|
+
typically means that you may be sending too many value events? .`),N("Sending Analytics data of length: ",n.length,"B"),e=["Sending data:",n],T&&window.analytics.debugInfoLogsEnable&&console.info("analytics client: ",...e),window.navigator.onLine?window.fetch(v,{method:"POST",headers:{"Content-Type":"application/json"},body:n}).then(e=>{200!==e.status&&(400!==e.status?L(t):console.error("Bad Request, this is most likely a problem with the library, update to latest version."))}).catch(e=>{$(e),L(t)}):L(t)}function U(e){E||(e||(e=h,D=0,R(),h=k()),0!==e.numEventsTotal&&O(e))}function R(e){I&&(clearInterval(I),I=null),e||(I=setInterval(()=>{D+=f},1e3*f))}function _(e){R(e),S&&(clearInterval(S),S=null),e||(S=setInterval(U,1e3*c))}function B(n,a,o,i=1,s=0){if(!E){var r=n,l=a,u=o,c=i,f=s;if(A(),!r||!l||!u)throw new Error("missing eventType or category or subCategory");if("number"!=typeof c||c<0)throw new Error("invalid count, count should be a positive number");if("number"!=typeof f)throw new Error("invalid value, value should be a number");{r=n;l=a;u=o;let e=h.events;e[r]=e[r]||{},e[r][l]=e[r][l]||{},e[r][l][u]=e[r][l][u]||{time:[],valueCount:[]}}let t=h.events;c=t[n][a][o].time;if((0<c.length?c[c.length-1]:null)===D){f=t[n][a][o].valueCount.length-1;{var r=f,l=n,u=a,c=o,f=i,d=s;let t=h.events;var e="number"==typeof t[l][u][c].valueCount[r];if(e&&0===d)t[l][u][c].valueCount[r]+=f;else if(e&&0!==d){let e={};e[d]=f,e[0]=t[l][u][c].valueCount[r],t[l][u][c].valueCount[r]=e}else if(!e){let e=t[l][u][c].valueCount[r];e[d]=(e[d]||0)+f}h.numEventsTotal+=1}}else{if(t[n][a][o].time.push(D),0===s)t[n][a][o].valueCount.push(i);else{let e={};e[s]=i,t[n][a][o].valueCount.push(e)}h.numEventsTotal+=1}}}if(!e||!t)throw new Error("accountID and appName must exist for init");d=n?n.replace(/\/$/,""):C,s=e,r=t,T=i||!1,c=a||60,f=o||3,v=d+"/ingest",l=function(){let e=localStorage.getItem(g);return e||(e=crypto.randomUUID(),localStorage.setItem(g,e)),e}(),u=function(){let e=sessionStorage.getItem(m);return e||(e=Math.random().toString(36).substr(2,10),sessionStorage.setItem(m,e)),e}(),h=k(),_(),async function(e,t){(y=await new Promise(n=>{var e;window.navigator.onLine?(e=d+(`/getAppConfig?accountID=${s}&appName=`+r),window.fetch(e).then(async e=>{switch(e.status){case 200:var t=await e.json();return void n(t);case 400:$("Bad Request, check library version compatible?",e),n({});break;default:$("Could not update from remote config. Continuing with defaults.",e),n({})}}).catch(e=>{$("Could not update from remote config. Continuing with defaults.",e),n({})})):n({})}))!=={}&&(c=e||y.postIntervalSecondsInit||60,f=t||y.granularitySecInit||3,d=y.analyticsURLInit||d||C,_(E=!0===y.disabled),N(`Init analytics Config from remote. disabled: ${E}
|
|
3
|
+
postIntervalSeconds:${c}, granularitySec: ${f} ,URL: `+d),E&&console.warn(`Core Analytics is disabled from the server for app: ${s}:`+r))}(a,o);for(w of analytics._initData)B(...w);analytics._initData=[],analytics._getCurrentAnalyticsEvent=function(){return A(),JSON.parse(JSON.stringify(h))},analytics._getAppConfig=function(){return{accountID:s,appName:r,disabled:E,uuid:l,sessionID:u,postIntervalSeconds:c,granularitySec:f,analyticsURL:d,serverConfig:y}},analytics.event=B}window.analytics||(window.analytics={_initData:[]});
|
|
4
4
|
//# sourceMappingURL=analytics.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"src/analytics.js","sources":["src/analytics.js"],"names":["
|
|
1
|
+
{"version":3,"sourceRoot":"src/analytics.js","sources":["src/analytics.js"],"names":["initAnalyticsSession","accountIDInit","appNameInit","analyticsURLInit","postIntervalSecondsInit","granularitySecInit","debug","let","accountID","appName","userID","sessionID","postIntervalSeconds","granularitySec","analyticsURL","postURL","serverConfig","USERID_LOCAL_STORAGE_KEY","SESSION_ID_LOCAL_STORAGE_KEY","POST_LARGE_DATA_THRESHOLD_BYTES","currentAnalyticsEvent","eventData","IS_NODE_ENV","window","DEFAULT_BASE_URL","granularityTimer","postTimer","currentQuantisedTime","disabled","debugMode","debugLog","args","console","log","debugError","error","Error","_createAnalyticsEvent","schemaVersion","uuid","unixTimestampUTC","Date","numEventsTotal","events","_validateCurrentState","_retryPost","eventToSend","backoffCount","setTimeout","_postCurrentAnalyticsEvent","DEFAULT_RETRY_TIME_IN_SECONDS","_postEventWithRetry","textToSend","JSON","stringify","length","warn","debugInfo","analytics","debugInfoLogsEnable","info","navigator","onLine","fetch","method","headers","Content-Type","body","then","res","status","catch","_resetGranularityTimer","disable","clearInterval","setInterval","_setupTimers","event","eventType","eventCategory","subCategory","eventCount","eventValue","_validateEvent","category","count","value","_ensureAnalyticsEventExists","time","valueCount","timeArray","modificationIndex","_updateExistingAnalyticsEvent","index","newValue","storedValueIsCount","newValueCount","storedValueObject","push","replace","localUserID","localStorage","getItem","crypto","randomUUID","setItem","_getOrCreateUserID","localSessionID","sessionStorage","Math","random","toString","substr","_getOrCreateSessionID","async","postIntervalSecondsInitial","granularitySecInitial","Promise","configURL","serverResponse","json","resolve","err","_initFromRemoteConfig","_initData","_getCurrentAnalyticsEvent","parse","_getAppConfig"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/analytics.js
CHANGED
|
@@ -1,16 +1,36 @@
|
|
|
1
1
|
// GNU AGPL-3.0 License Copyright (c) 2021 - present core.ai . All rights reserved.
|
|
2
2
|
|
|
3
3
|
// jshint ignore: start
|
|
4
|
-
/*global localStorage, sessionStorage, crypto*/
|
|
4
|
+
/*global localStorage, sessionStorage, crypto, analytics*/
|
|
5
5
|
|
|
6
|
-
var analytics = {};
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
if(!window.analytics){
|
|
8
|
+
window.analytics = {
|
|
9
|
+
_initData: []
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initialize the analytics session
|
|
15
|
+
* @param accountIDInit Your analytics account id as configured in the server or core.ai analytics
|
|
16
|
+
* @param appNameInit The app name to log the events against.
|
|
17
|
+
* @param analyticsURLInit Optional: Provide your own analytics server address if you self-hosted the server.
|
|
18
|
+
* @param postIntervalSecondsInit Optional: This defines the interval between sending analytics events to the server.
|
|
19
|
+
* Default is 1 minutes or server controlled.
|
|
20
|
+
* @param granularitySecInit Optional: The smallest time period under which the events can be distinguished. Multiple
|
|
21
|
+
* events happening during this time period is aggregated to a count. The default granularity is 3 Seconds or server
|
|
22
|
+
* controlled, which means that any events that happen within 3 seconds cannot be distinguished in ordering.
|
|
23
|
+
* @param debug set to true if you want to see detailed debug logs.
|
|
24
|
+
*/
|
|
25
|
+
function initAnalyticsSession(accountIDInit, appNameInit, analyticsURLInit,
|
|
26
|
+
postIntervalSecondsInit, granularitySecInit, debug) {
|
|
27
|
+
let accountID, appName, userID, sessionID, postIntervalSeconds,
|
|
28
|
+
granularitySec, analyticsURL, postURL, serverConfig={};
|
|
10
29
|
const DEFAULT_GRANULARITY_IN_SECONDS = 3;
|
|
11
30
|
const DEFAULT_RETRY_TIME_IN_SECONDS = 30;
|
|
12
|
-
const DEFAULT_POST_INTERVAL_SECONDS =
|
|
31
|
+
const DEFAULT_POST_INTERVAL_SECONDS = 60; // 1 minutes
|
|
13
32
|
const USERID_LOCAL_STORAGE_KEY = 'aicore.analytics.userID';
|
|
33
|
+
const SESSION_ID_LOCAL_STORAGE_KEY = 'aicore.analytics.sessionID';
|
|
14
34
|
const POST_LARGE_DATA_THRESHOLD_BYTES = 10000;
|
|
15
35
|
let currentAnalyticsEvent = null;
|
|
16
36
|
const IS_NODE_ENV = (typeof window === 'undefined');
|
|
@@ -26,14 +46,20 @@ function init() {
|
|
|
26
46
|
if(!debugMode){
|
|
27
47
|
return;
|
|
28
48
|
}
|
|
29
|
-
console.log(...args);
|
|
49
|
+
console.log("analytics client: ", ...args);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function debugInfo(...args) {
|
|
53
|
+
if(debugMode && window.analytics.debugInfoLogsEnable){
|
|
54
|
+
console.info("analytics client: ", ...args);
|
|
55
|
+
}
|
|
30
56
|
}
|
|
31
57
|
|
|
32
58
|
function debugError(...args) {
|
|
33
59
|
if(!debugMode){
|
|
34
60
|
return;
|
|
35
61
|
}
|
|
36
|
-
console.error(...args);
|
|
62
|
+
console.error("analytics client: ", ...args);
|
|
37
63
|
}
|
|
38
64
|
|
|
39
65
|
|
|
@@ -60,7 +86,7 @@ function init() {
|
|
|
60
86
|
}
|
|
61
87
|
}
|
|
62
88
|
|
|
63
|
-
function
|
|
89
|
+
function _getCurrentAnalyticsEvent() {
|
|
64
90
|
_validateCurrentState();
|
|
65
91
|
// return a clone
|
|
66
92
|
return JSON.parse(JSON.stringify(currentAnalyticsEvent));
|
|
@@ -76,10 +102,10 @@ function init() {
|
|
|
76
102
|
}
|
|
77
103
|
|
|
78
104
|
function _getOrCreateSessionID() {
|
|
79
|
-
let localSessionID = sessionStorage.getItem(
|
|
105
|
+
let localSessionID = sessionStorage.getItem(SESSION_ID_LOCAL_STORAGE_KEY);
|
|
80
106
|
if(!localSessionID){
|
|
81
107
|
localSessionID = Math.random().toString(36).substr(2, 10);
|
|
82
|
-
sessionStorage.setItem(
|
|
108
|
+
sessionStorage.setItem(SESSION_ID_LOCAL_STORAGE_KEY, localSessionID);
|
|
83
109
|
}
|
|
84
110
|
return localSessionID;
|
|
85
111
|
}
|
|
@@ -98,25 +124,20 @@ function init() {
|
|
|
98
124
|
}, DEFAULT_RETRY_TIME_IN_SECONDS * 1000 * eventToSend.backoffCount);
|
|
99
125
|
}
|
|
100
126
|
|
|
101
|
-
function
|
|
102
|
-
if(disabled){
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
if(!eventToSend){
|
|
106
|
-
eventToSend = currentAnalyticsEvent;
|
|
107
|
-
currentQuantisedTime = 0;
|
|
108
|
-
_resetGranularityTimer();
|
|
109
|
-
currentAnalyticsEvent = _createAnalyticsEvent();
|
|
110
|
-
}
|
|
111
|
-
if(eventToSend.numEventsTotal === 0 ){
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
127
|
+
function _postEventWithRetry(eventToSend) {
|
|
114
128
|
let textToSend = JSON.stringify(eventToSend);
|
|
115
129
|
if(textToSend.length > POST_LARGE_DATA_THRESHOLD_BYTES){
|
|
116
130
|
console.warn(`Analytics event generated is very large at greater than ${textToSend.length}B. This
|
|
117
131
|
typically means that you may be sending too many value events? .`);
|
|
118
132
|
}
|
|
119
133
|
debugLog("Sending Analytics data of length: ", textToSend.length, "B");
|
|
134
|
+
debugInfo("Sending data:", textToSend);
|
|
135
|
+
if(!window.navigator.onLine){
|
|
136
|
+
_retryPost(eventToSend);
|
|
137
|
+
// chrome shows all network failure requests in console. In offline mode, we don't want to bomb debug
|
|
138
|
+
// console with network failure messages for analytics. So we prevent network requests when offline.
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
120
141
|
window.fetch(postURL, {
|
|
121
142
|
method: "POST",
|
|
122
143
|
headers: {'Content-Type': 'application/json'},
|
|
@@ -128,8 +149,7 @@ function init() {
|
|
|
128
149
|
if(res.status !== 400){ // we don't retry bad requests
|
|
129
150
|
_retryPost(eventToSend);
|
|
130
151
|
} else {
|
|
131
|
-
console.error("
|
|
132
|
-
"Bad Request, this is most likely a problem with the library, update to latest version.");
|
|
152
|
+
console.error("Bad Request, this is most likely a problem with the library, update to latest version.");
|
|
133
153
|
}
|
|
134
154
|
}).catch(res => {
|
|
135
155
|
debugError(res);
|
|
@@ -137,6 +157,22 @@ function init() {
|
|
|
137
157
|
});
|
|
138
158
|
}
|
|
139
159
|
|
|
160
|
+
function _postCurrentAnalyticsEvent(eventToSend) {
|
|
161
|
+
if(disabled){
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if(!eventToSend){
|
|
165
|
+
eventToSend = currentAnalyticsEvent;
|
|
166
|
+
currentQuantisedTime = 0;
|
|
167
|
+
_resetGranularityTimer();
|
|
168
|
+
currentAnalyticsEvent = _createAnalyticsEvent();
|
|
169
|
+
}
|
|
170
|
+
if(eventToSend.numEventsTotal === 0 ){
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
_postEventWithRetry(eventToSend);
|
|
174
|
+
}
|
|
175
|
+
|
|
140
176
|
function _resetGranularityTimer(disable) {
|
|
141
177
|
if(granularityTimer){
|
|
142
178
|
clearInterval(granularityTimer);
|
|
@@ -163,7 +199,13 @@ function init() {
|
|
|
163
199
|
}
|
|
164
200
|
|
|
165
201
|
async function _getServerConfig() {
|
|
166
|
-
return new Promise((resolve
|
|
202
|
+
return new Promise((resolve)=>{
|
|
203
|
+
if(!window.navigator.onLine){
|
|
204
|
+
resolve({});
|
|
205
|
+
// chrome shows all network failure requests in console. In offline mode, we don't want to bomb debug
|
|
206
|
+
// console with network failure messages for analytics. So we prevent network requests when offline.
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
167
209
|
let configURL = analyticsURL + `/getAppConfig?accountID=${accountID}&appName=${appName}`;
|
|
168
210
|
window.fetch(configURL).then(async res=>{
|
|
169
211
|
switch (res.status) {
|
|
@@ -172,13 +214,16 @@ function init() {
|
|
|
172
214
|
resolve(serverResponse);
|
|
173
215
|
return;
|
|
174
216
|
case 400:
|
|
175
|
-
|
|
217
|
+
debugError("Bad Request, check library version compatible?", res);
|
|
218
|
+
resolve({});
|
|
176
219
|
break;
|
|
177
220
|
default:
|
|
178
|
-
|
|
221
|
+
debugError("Could not update from remote config. Continuing with defaults.", res);
|
|
222
|
+
resolve({});
|
|
179
223
|
}
|
|
180
224
|
}).catch(err => {
|
|
181
|
-
|
|
225
|
+
debugError("Could not update from remote config. Continuing with defaults.", err);
|
|
226
|
+
resolve({});
|
|
182
227
|
});
|
|
183
228
|
});
|
|
184
229
|
}
|
|
@@ -187,7 +232,7 @@ function init() {
|
|
|
187
232
|
* Returns the analytics config for the app
|
|
188
233
|
* @returns {Object}
|
|
189
234
|
*/
|
|
190
|
-
function
|
|
235
|
+
function _getAppConfig() {
|
|
191
236
|
return {
|
|
192
237
|
accountID, appName, disabled,
|
|
193
238
|
uuid: userID, sessionID,
|
|
@@ -195,13 +240,13 @@ function init() {
|
|
|
195
240
|
};
|
|
196
241
|
}
|
|
197
242
|
|
|
198
|
-
async function _initFromRemoteConfig(
|
|
243
|
+
async function _initFromRemoteConfig(postIntervalSecondsInitial, granularitySecInitial) {
|
|
199
244
|
serverConfig = await _getServerConfig();
|
|
200
245
|
if(serverConfig !== {}){
|
|
201
246
|
// User init overrides takes precedence over server overrides
|
|
202
|
-
postIntervalSeconds =
|
|
247
|
+
postIntervalSeconds = postIntervalSecondsInitial ||
|
|
203
248
|
serverConfig["postIntervalSecondsInit"] || DEFAULT_POST_INTERVAL_SECONDS;
|
|
204
|
-
granularitySec =
|
|
249
|
+
granularitySec = granularitySecInitial || serverConfig["granularitySecInit"] || DEFAULT_GRANULARITY_IN_SECONDS;
|
|
205
250
|
// For URLs, the server suggested URL takes precedence over user init values
|
|
206
251
|
analyticsURL = serverConfig["analyticsURLInit"] || analyticsURL || DEFAULT_BASE_URL;
|
|
207
252
|
disabled = serverConfig["disabled"] === true;
|
|
@@ -218,35 +263,6 @@ function init() {
|
|
|
218
263
|
return url.replace(/\/$/, "");
|
|
219
264
|
}
|
|
220
265
|
|
|
221
|
-
/**
|
|
222
|
-
* Initialize the analytics session
|
|
223
|
-
* @param accountIDInit Your analytics account id as configured in the server or core.ai analytics
|
|
224
|
-
* @param appNameInit The app name to log the events against.
|
|
225
|
-
* @param analyticsURLInit Optional: Provide your own analytics server address if you self-hosted the server
|
|
226
|
-
* @param postIntervalSecondsInit Optional: This defines the interval between sending analytics events to the server.
|
|
227
|
-
* Default is 10 minutes
|
|
228
|
-
* @param granularitySecInit Optional: The smallest time period under which the events can be distinguished. Multiple
|
|
229
|
-
* events happening during this time period is aggregated to a count. The default granularity is 3 Seconds, which means
|
|
230
|
-
* that any events that happen within 3 seconds cannot be distinguished in ordering.
|
|
231
|
-
* @param debug set to true if you want to see detailed debug logs.
|
|
232
|
-
*/
|
|
233
|
-
function initSession(accountIDInit, appNameInit, analyticsURLInit, postIntervalSecondsInit, granularitySecInit, debug) {
|
|
234
|
-
if(!accountIDInit || !appNameInit){
|
|
235
|
-
throw new Error("accountID and appName must exist for init");
|
|
236
|
-
}
|
|
237
|
-
analyticsURL = analyticsURLInit? _stripTrailingSlash(analyticsURLInit) : DEFAULT_BASE_URL;
|
|
238
|
-
accountID = accountIDInit;
|
|
239
|
-
appName = appNameInit;
|
|
240
|
-
debugMode = debug || false;
|
|
241
|
-
postIntervalSeconds = postIntervalSecondsInit || DEFAULT_POST_INTERVAL_SECONDS;
|
|
242
|
-
granularitySec = granularitySecInit || DEFAULT_GRANULARITY_IN_SECONDS;
|
|
243
|
-
postURL = analyticsURL + "/ingest";
|
|
244
|
-
_setupIDs();
|
|
245
|
-
currentAnalyticsEvent = _createAnalyticsEvent();
|
|
246
|
-
_setupTimers();
|
|
247
|
-
_initFromRemoteConfig(postIntervalSecondsInit, granularitySecInit);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
266
|
function _ensureAnalyticsEventExists(eventType, category, subCategory) {
|
|
251
267
|
let events = currentAnalyticsEvent.events;
|
|
252
268
|
events[eventType] = events[eventType] || {};
|
|
@@ -263,10 +279,10 @@ function init() {
|
|
|
263
279
|
throw new Error("missing eventType or category or subCategory");
|
|
264
280
|
}
|
|
265
281
|
if(typeof(count)!== 'number' || count <0){
|
|
266
|
-
throw new Error("invalid count");
|
|
282
|
+
throw new Error("invalid count, count should be a positive number");
|
|
267
283
|
}
|
|
268
284
|
if(typeof(value)!== 'number'){
|
|
269
|
-
throw new Error("invalid value");
|
|
285
|
+
throw new Error("invalid value, value should be a number");
|
|
270
286
|
}
|
|
271
287
|
}
|
|
272
288
|
|
|
@@ -321,10 +337,35 @@ function init() {
|
|
|
321
337
|
_updateExistingAnalyticsEvent(modificationIndex, eventType, eventCategory, subCategory, eventCount, eventValue);
|
|
322
338
|
}
|
|
323
339
|
|
|
324
|
-
|
|
325
|
-
|
|
340
|
+
// Init Analytics
|
|
341
|
+
if(!accountIDInit || !appNameInit){
|
|
342
|
+
throw new Error("accountID and appName must exist for init");
|
|
343
|
+
}
|
|
344
|
+
analyticsURL = analyticsURLInit? _stripTrailingSlash(analyticsURLInit) : DEFAULT_BASE_URL;
|
|
345
|
+
accountID = accountIDInit;
|
|
346
|
+
appName = appNameInit;
|
|
347
|
+
debugMode = debug || false;
|
|
348
|
+
postIntervalSeconds = postIntervalSecondsInit || DEFAULT_POST_INTERVAL_SECONDS;
|
|
349
|
+
granularitySec = granularitySecInit || DEFAULT_GRANULARITY_IN_SECONDS;
|
|
350
|
+
postURL = analyticsURL + "/ingest";
|
|
351
|
+
_setupIDs();
|
|
352
|
+
currentAnalyticsEvent = _createAnalyticsEvent();
|
|
353
|
+
_setupTimers();
|
|
354
|
+
_initFromRemoteConfig(postIntervalSecondsInit, granularitySecInit);
|
|
355
|
+
|
|
356
|
+
// As analytics lib load is async, our loading scripts will push event data into initData array till the lib load
|
|
357
|
+
// is complete. Now as load is done, we need to push these pending analytics events. Assuming that the async load of
|
|
358
|
+
// the lib should mostly happen under a second, we should not lose any accuracy of event times within the initial
|
|
359
|
+
// 3-second granularity. For more precision use cases, load the lib sync.
|
|
360
|
+
for(let eventData of analytics._initData){
|
|
361
|
+
event(...eventData);
|
|
362
|
+
}
|
|
363
|
+
analytics._initData = [];
|
|
364
|
+
|
|
365
|
+
// Private API for tests
|
|
366
|
+
analytics._getCurrentAnalyticsEvent = _getCurrentAnalyticsEvent;
|
|
367
|
+
analytics._getAppConfig = _getAppConfig;
|
|
368
|
+
|
|
369
|
+
// Public API
|
|
326
370
|
analytics.event = event;
|
|
327
|
-
analytics.getAppConfig = getAppConfig;
|
|
328
371
|
}
|
|
329
|
-
|
|
330
|
-
init();
|