kv-test-lib 1.0.0

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.
@@ -0,0 +1,220 @@
1
+ /*
2
+ Authored by Brett Barinaga on 11/17/21.
3
+ Copyright (c) Kochava, Inc. All rights reserved.
4
+ */
5
+ import * as Event from "./payloads/event";
6
+ import * as IdLink from "./payloads/identityLink";
7
+ import { Log } from "./utils/log";
8
+ import { uuidv4 } from "./utils/utils";
9
+ import { addToPersistedEventQueue, PersistKey, removeFromEventPersistedQueue, removeFromIdLinkPersistedQueue, updateOrAddPersistedIdLinkQueue, updatePersistedValue } from "./browser/persist";
10
+ function jobIsEventJob(obj) {
11
+ return 'eventName' in obj;
12
+ }
13
+ function jobIsIdLinkJob(obj) {
14
+ return 'idLink' in obj;
15
+ }
16
+ export default class JobQueue {
17
+ constructor() {
18
+ this.eventQueue = [];
19
+ this.idLinkQueue = [];
20
+ this.processing = false;
21
+ this.stopped = false;
22
+ this.paused = false;
23
+ }
24
+ async start(instance) {
25
+ this.eventQueue = JSON.parse(localStorage.getItem(PersistKey.EventQueue)) || [];
26
+ this.idLinkQueue = JSON.parse(localStorage.getItem(PersistKey.IdLinkQueue)) || [];
27
+ this.updateEventJobs(instance);
28
+ this.updateIdLinkJobs(instance);
29
+ Log.trace("Starting Event Queue", JSON.parse(JSON.stringify(this.eventQueue)));
30
+ Log.trace("Starting IdLink Queue", JSON.parse(JSON.stringify(this.idLinkQueue)));
31
+ this.stopped = false;
32
+ this.paused = false;
33
+ await this.dequeueJob(instance);
34
+ }
35
+ stop() {
36
+ this.stopped = true;
37
+ if (this.timeOut)
38
+ clearTimeout(this.timeOut);
39
+ this.processing = false;
40
+ }
41
+ pause() {
42
+ this.paused = true;
43
+ }
44
+ async enqueueEvent(instance, args) {
45
+ const eventName = args[0];
46
+ const eventData = args[1];
47
+ const eventPreStartBody = Event.constructPreStart(instance, eventName, eventData);
48
+ if (instance.installDone && instance.kvinitDone) {
49
+ const postStartBody = Event.constructPostStart(instance, eventPreStartBody);
50
+ const newJob = {
51
+ id: uuidv4(),
52
+ queuedBeforeStart: false,
53
+ preStartBody: eventPreStartBody,
54
+ postStartBody,
55
+ retries: 0,
56
+ eventName,
57
+ };
58
+ this.eventQueue.push(newJob);
59
+ addToPersistedEventQueue(newJob);
60
+ await this.dequeueJob(instance);
61
+ return;
62
+ }
63
+ const newEventJob = {
64
+ id: uuidv4(),
65
+ queuedBeforeStart: true,
66
+ preStartBody: eventPreStartBody,
67
+ postStartBody: undefined,
68
+ retries: 0,
69
+ eventName,
70
+ };
71
+ this.eventQueue.push(newEventJob);
72
+ addToPersistedEventQueue(newEventJob);
73
+ }
74
+ async enqueueIdLink(instance, idLink) {
75
+ const idLinkPreStartBody = IdLink.constructPreStart(instance, idLink);
76
+ if (instance.installDone && instance.kvinitDone) {
77
+ const postStartBody = IdLink.constructPostStart(instance, idLinkPreStartBody);
78
+ const newJob = {
79
+ id: uuidv4(),
80
+ queuedBeforeStart: false,
81
+ preStartBody: idLinkPreStartBody,
82
+ postStartBody,
83
+ retries: 0,
84
+ idLink,
85
+ };
86
+ updateOrAddPersistedIdLinkQueue(newJob);
87
+ this.idLinkQueue.push(newJob);
88
+ await this.dequeueJob(instance);
89
+ return;
90
+ }
91
+ const newJob = {
92
+ id: uuidv4(),
93
+ queuedBeforeStart: true,
94
+ preStartBody: idLinkPreStartBody,
95
+ postStartBody: undefined,
96
+ retries: 0,
97
+ idLink,
98
+ };
99
+ updateOrAddPersistedIdLinkQueue(newJob);
100
+ this.idLinkQueue.push(newJob);
101
+ }
102
+ async dequeueJob(instance) {
103
+ // If queue is busy, prev job not finished
104
+ if (this.processing)
105
+ return false;
106
+ // If the queue is paused, do not dequeue a new job
107
+ if (this.paused)
108
+ return false;
109
+ // If the queue is stopped do not dequeue a new job
110
+ if (this.stopped) {
111
+ return false;
112
+ }
113
+ // Prioritize sending identityLinks first
114
+ // Remove first job from queue
115
+ const idLinkJob = this.idLinkQueue.shift();
116
+ if (idLinkJob) {
117
+ // handle idlinkjob
118
+ Log.trace("Dequeued Job: ", idLinkJob);
119
+ this.processing = true;
120
+ const result = await this.processJob(instance, idLinkJob);
121
+ if (this.stopped) {
122
+ return true;
123
+ }
124
+ this.processing = false;
125
+ // If the job succeeded, dequeue the next job
126
+ if (result) {
127
+ removeFromIdLinkPersistedQueue(idLinkJob);
128
+ return await this.dequeueJob(instance);
129
+ }
130
+ }
131
+ const eventJob = this.eventQueue.shift();
132
+ if (eventJob) {
133
+ //handle eventJob
134
+ Log.trace("Dequeued Job: ", eventJob);
135
+ // Process the job
136
+ this.processing = true;
137
+ const result = await this.processJob(instance, eventJob);
138
+ if (this.stopped) {
139
+ return true;
140
+ }
141
+ this.processing = false;
142
+ // If the job succeeded, dequeue the next job
143
+ if (result) {
144
+ removeFromEventPersistedQueue(eventJob);
145
+ return await this.dequeueJob(instance);
146
+ }
147
+ }
148
+ // If neither queue had a job, break out of recursion
149
+ if (!idLinkJob && !eventJob)
150
+ return false;
151
+ return true;
152
+ }
153
+ async processJob(instance, job) {
154
+ if (jobIsEventJob(job)) {
155
+ for (const denyName of instance.kochavaConfig.privacy.deny_event_names) {
156
+ if (denyName === job.eventName) {
157
+ Log.debug(`Denied event_name ${denyName}, dropping request.`);
158
+ return true;
159
+ }
160
+ }
161
+ }
162
+ else if (jobIsIdLinkJob(job)) {
163
+ for (const denyIdLinkKey of instance.kochavaConfig.privacy.deny_identity_links) {
164
+ if (denyIdLinkKey === Object.keys(job.idLink)[0]) {
165
+ Log.debug(`Denied identity_link ${denyIdLinkKey}, dropping request.`);
166
+ return true;
167
+ }
168
+ }
169
+ }
170
+ let success = false;
171
+ do {
172
+ success = await this.attemptJob(instance, job);
173
+ // If our job succeeded
174
+ if (success) {
175
+ // Job Done
176
+ Log.trace("Job processed successfully:", job);
177
+ return true;
178
+ }
179
+ // If it didnt succeed, but we our queue isnt stopped
180
+ if (!this.stopped) {
181
+ //retry the job
182
+ const retryWaterfall = instance.kochavaConfig.networking.retry_waterfall;
183
+ const retryIndex = (job.retries > retryWaterfall.length - 1) ?
184
+ retryWaterfall.length - 1 : job.retries;
185
+ const retrySec = retryWaterfall[retryIndex];
186
+ Log.error(`Job failed, attempting again in ${retrySec} seconds`);
187
+ await new Promise(resolve => this.timeOut = setTimeout(resolve, retrySec * 1000));
188
+ job.retries++;
189
+ }
190
+ } while (!success && !this.stopped);
191
+ // Job Canceled
192
+ return true;
193
+ }
194
+ async attemptJob(instance, job) {
195
+ if (job.preStartBody.action === "event")
196
+ return await Event.send(instance, job.preStartBody, job.postStartBody);
197
+ else if (job.preStartBody.action === "identityLink")
198
+ return await IdLink.send(instance, job);
199
+ else {
200
+ Log.warn("Invalid action in job from jobqueue, cancelling.");
201
+ return true;
202
+ }
203
+ }
204
+ updateEventJobs(instance) {
205
+ for (const job of this.eventQueue) {
206
+ if (job.queuedBeforeStart) {
207
+ job.postStartBody = Event.constructPostStart(instance, job.preStartBody);
208
+ }
209
+ }
210
+ updatePersistedValue(PersistKey.EventQueue, JSON.stringify(this.eventQueue), false);
211
+ }
212
+ updateIdLinkJobs(instance) {
213
+ for (const job of this.idLinkQueue) {
214
+ if (job.queuedBeforeStart) {
215
+ job.postStartBody = IdLink.constructPostStart(instance, job.preStartBody);
216
+ }
217
+ }
218
+ updatePersistedValue(PersistKey.IdLinkQueue, JSON.stringify(this.idLinkQueue), false);
219
+ }
220
+ }
@@ -0,0 +1,58 @@
1
+ import { Json, Urls, CustomValue, KvConfig } from "./interfaces";
2
+ declare global {
3
+ interface Window {
4
+ kochava: Kochava;
5
+ }
6
+ }
7
+ export interface KochavaInstance {
8
+ appGuid: string;
9
+ started: boolean;
10
+ installStarted: boolean;
11
+ kvinitDone: boolean;
12
+ installDone: boolean;
13
+ disableAutoPage: boolean;
14
+ useCookies: boolean;
15
+ sleep: boolean;
16
+ version: string;
17
+ buildDate: string;
18
+ overrideUrls: Urls;
19
+ customValues: CustomValue[];
20
+ kochavaSession: string;
21
+ retryWaterfall: number[];
22
+ startTimeMS: number;
23
+ utm: string;
24
+ kochavaDeviceId: string;
25
+ kochavaInstallId: string;
26
+ kochavaInstallDate: number;
27
+ kochavaSessionCount: number;
28
+ kochavaConfig?: KvConfig;
29
+ }
30
+ export declare class Kochava {
31
+ private instance;
32
+ private jobQueue;
33
+ constructor();
34
+ useCookies(condition?: boolean): void;
35
+ disableAutoPage(condition?: boolean): void;
36
+ startWithAppGuid(appGuid: string): void;
37
+ shutdown(deleteData: boolean): void;
38
+ setLogLevel(logLevel: string): void;
39
+ executeAdvancedInstruction(key: string, valueStr: string, callback?: (input: string) => void): void;
40
+ sendEvent(name: string, data?: Json | string): void;
41
+ sendPageEvent(pageName?: string, additionalData?: Json): void;
42
+ registerIdentityLink(name: string, identifier: string): void;
43
+ registerCustomValue(name: string, value: string): void;
44
+ registerCustomDeviceIdentifier(name: string, value: string): void;
45
+ getStarted(): boolean;
46
+ getDeviceId(): string;
47
+ setSleep(sleep: boolean): void;
48
+ private resetInstance;
49
+ private initInstance;
50
+ private checkFirstLaunchAndMigrate;
51
+ private checkPersistedKvinit;
52
+ private checkPersistedState;
53
+ private printStartupMsgs;
54
+ private beginStart;
55
+ private performNewKvinit;
56
+ private checkResendId;
57
+ private performInstall;
58
+ }