-
Notifications
You must be signed in to change notification settings - Fork 731
/
Copy pathtelemetry.ts
137 lines (118 loc) · 3.3 KB
/
telemetry.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { Fyo } from 'fyo';
import { Noun, Telemetry, Verb } from './types';
import { ModelNameEnum } from 'models/types';
/**
* # Telemetry
* Used to check if people are using Books or not. All logging
* happens using navigator.sendBeacon
*
* ## `start`
* Used to initialize state. It should be called before any logging and after an
* instance has loaded.
* It is called on three events:
* 1. When Desk is opened, i.e. when the usage starts, this also sends a
* Opened instance log.
* 2. On visibility change if not started, eg: when user minimizes Books and
* then comes back later.
* 3. When `log` is called, but telemetry isn't initialized.
*
* ## `log`
* Used to log activity.
*
* ## `stop`
* This is to be called when a session is being stopped. It's called on two events
* 1. When the db is being changed.
* 2. When the visiblity has changed which happens when either the app is being shut or
* the app is hidden.
*/
const ignoreList: string[] = [
ModelNameEnum.AccountingLedgerEntry,
ModelNameEnum.StockLedgerEntry,
];
export class TelemetryManager {
#url = '';
#token = '';
#started = false;
fyo: Fyo;
constructor(fyo: Fyo) {
this.fyo = fyo;
}
get hasCreds() {
return !!this.#url && !!this.#token;
}
get started() {
return this.#started;
}
async start(isOpened?: boolean) {
this.#started = true;
await this.#setCreds();
if (isOpened) {
this.log(Verb.Opened, 'instance');
} else {
this.log(Verb.Resumed, 'instance');
}
}
stop() {
if (!this.started) {
return;
}
this.log(Verb.Closed, 'instance');
this.#started = false;
}
log(verb: Verb, noun: Noun, more?: Record<string, unknown>) {
if (!this.#started && this.fyo.db.isConnected) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.start().then(() => this.#sendBeacon(verb, noun, more));
return;
}
this.#sendBeacon(verb, noun, more);
}
async logOpened() {
await this.#setCreds();
this.#sendBeacon(Verb.Opened, 'app');
}
#sendBeacon(verb: Verb, noun: Noun, more?: Record<string, unknown>) {
if (
!this.hasCreds ||
this.fyo.store.skipTelemetryLogging ||
ignoreList.includes(noun)
) {
return;
}
const telemetryData: Telemetry = this.#getTelemtryData(verb, noun, more);
const data = JSON.stringify({
token: this.#token,
telemetryData,
});
navigator.sendBeacon(this.#url, data);
}
async #setCreds() {
if (this.hasCreds) {
return;
}
const { telemetryUrl, tokenString } = await this.fyo.auth.getCreds();
this.#url = telemetryUrl;
this.#token = tokenString;
}
#getTelemtryData(
verb: Verb,
noun: Noun,
more?: Record<string, unknown>
): Telemetry {
const countryCode = this.fyo.singles.SystemSettings?.countryCode;
return {
country: countryCode ?? '',
language: this.fyo.store.language,
deviceId:
this.fyo.store.deviceId || (this.fyo.config.get('deviceId') ?? '-'),
instanceId: this.fyo.store.instanceId,
version: this.fyo.store.appVersion,
openCount: this.fyo.store.openCount,
timestamp: new Date().toISOString().replace('T', ' ').slice(0, -1),
platform: this.fyo.store.platform,
verb,
noun,
more,
};
}
}