Skip to content

Commit a7f3632

Browse files
committed
feat: handle migration states to CleverTap
2 parents e20df57 + d7c8bff commit a7f3632

20 files changed

+1112
-134
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module.exports = {
1515
rules: {
1616
'@typescript-eslint/explicit-function-return-type': ['error', { allowExpressions: true }],
1717
'@typescript-eslint/no-use-before-define': 'off',
18+
'@typescript-eslint/no-explicit-any': 'off',
1819
'comma-dangle': ['error', 'always-multiline'],
1920
'eqeqeq': 'error',
2021
'max-len': ['warn', { code: 120, tabWidth: 2 }],

dist/leanplum.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export type EventType = 'start' | 'resume' | 'track' | 'setUserAttribute' | 'adv
156156
export interface WebPushOptions {
157157
serviceWorkerUrl?: string;
158158
scope?: string;
159+
clientUrl?: string;
159160
}
160161
export enum ActionParameterType {
161162
Integer = "integer",

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"devDependencies": {
1616
"@semantic-release/exec": "^6.0.3",
1717
"@types/bootstrap": "^4.5.0",
18+
"@types/clevertap-web-sdk": "^1.1.0",
1819
"@types/jest": "^25.2.1",
1920
"@types/jquery": "^3.3.38",
2021
"@types/node": "^13.13.4",
@@ -106,6 +107,7 @@
106107
},
107108
"homepage": "https://github.com/Leanplum/Leanplum-JavaScript-SDK#readme",
108109
"dependencies": {
109-
"@qiwi/semrel-metabranch": "^3.1.2"
110+
"@qiwi/semrel-metabranch": "^3.1.2",
111+
"clevertap-web-sdk": "^1.3.3"
110112
}
111113
}

src/Constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export default {
4444
APP_ID: 'appId',
4545
CLIENT: 'client',
4646
CLIENT_KEY: 'clientKey',
47+
CT: 'ct',
4748
CURRENCY_CODE: 'currencyCode',
4849
DEVICE_ID: 'deviceId',
4950
SDK_VERSION: 'sdkVersion',
@@ -113,6 +114,7 @@ export default {
113114
SESSION: '__leanplum_session',
114115
MESSAGE_OCCURRENCES: '__leanplum_message_occurrences',
115116
MESSAGE_CACHE: '__leanplum_message_cache',
117+
MIGRATION_STATE: '__leanplum_migration_state',
116118
PUSH_SUBSCRIPTION: '__leanplum_push_subscription',
117119
},
118120

src/LeanplumInternal.ts

Lines changed: 120 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,3 @@
1-
/*
2-
* Copyright 2020 Leanplum Inc. All rights reserved.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at:
7-
*
8-
* https://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
16-
171
import ArgsBuilder from './ArgsBuilder'
182
import BrowserDetector from './BrowserDetector'
193
import Constants from './Constants'
@@ -25,6 +9,7 @@ import StorageManager from './StorageManager'
259
import PushManager from './PushManager'
2610
import Messages from './Messages'
2711
import EventEmitter from './EventEmitter'
12+
import MigrationManager from './MigrationManager'
2813
import {
2914
Action,
3015
EventType,
@@ -36,6 +21,7 @@ import {
3621
WebPushOptions,
3722
UserAttributes,
3823
} from './types/public'
24+
import { BatchResponse, MigrationState } from './types/internal'
3925
import VarCache from './VarCache'
4026

4127
/* eslint-disable @typescript-eslint/ban-types */
@@ -44,6 +30,9 @@ import VarCache from './VarCache'
4430
const SESSION_KEY = Constants.DEFAULT_KEYS.SESSION
4531

4632
export default class LeanplumInternal {
33+
private _migration = new MigrationManager(
34+
this.createRequest.bind(this)
35+
)
4736
private _events: EventEmitter = new EventEmitter();
4837
private _browserDetector: BrowserDetector
4938
private _internalState: InternalState = new InternalState()
@@ -59,7 +48,10 @@ export default class LeanplumInternal {
5948
this._lpRequest.getLastResponse.bind(this._lpRequest),
6049
this._events
6150
)
62-
private _pushManager: PushManager = new PushManager(this.createRequest.bind(this))
51+
private _pushManager: PushManager = new PushManager(
52+
this._events,
53+
this.createRequest.bind(this)
54+
)
6355
private _webPushOptions: WebPushOptions
6456
private _messages: Messages = new Messages(
6557
this._events,
@@ -74,6 +66,8 @@ export default class LeanplumInternal {
7466
private _systemVersion: string
7567
private _sessionLength: number
7668

69+
private _ct: any
70+
7771
constructor(private wnd: Window) {
7872
this._browserDetector = new BrowserDetector(wnd)
7973
this._events.on('navigationChange', (url: string) => {
@@ -90,6 +84,18 @@ export default class LeanplumInternal {
9084
this._events.on('registerForPush', () => this.registerForWebPush())
9185
this._events.on('updateDevServerHost',
9286
(host: string) => this.setSocketHost(host))
87+
this._events.on('migrateStateReceived',
88+
(sha: string) => this._migration.verifyState(sha))
89+
90+
this._events.on('webPushSubscribed',
91+
() => {
92+
this._ct && this._ct.notifications.push({
93+
titleText: '',
94+
bodyText: '',
95+
okButtonText: '',
96+
rejectButtonText: '',
97+
})
98+
})
9399
}
94100

95101
setApiPath(apiPath: string): void {
@@ -245,7 +251,7 @@ export default class LeanplumInternal {
245251
this.createRequest(Constants.METHODS.GET_VARS, args, {
246252
queued: false,
247253
sendNow: true,
248-
response: (response: Array<Object>) => {
254+
response: (response: BatchResponse) => {
249255
const getVarsResponse = this._lpRequest.getLastResponse(response)
250256
const isSuccess = this._lpRequest.isResponseSuccess(getVarsResponse)
251257
if (isSuccess) {
@@ -290,79 +296,102 @@ export default class LeanplumInternal {
290296
return this.startFromCache(userId, userAttributes, callback)
291297
}
292298

293-
this._lpRequest.userId = userId
294-
295-
if (callback) {
296-
this.addStartResponseHandler(callback)
297-
}
298-
299-
this._varCache.onUpdate = () => {
300-
this._varCache.triggerVariablesChangedHandlers()
301-
}
299+
this._migration.getState((state: MigrationState) => {
300+
if (state === MigrationState.DUPLICATE) {
301+
this._ct = this._migration.initCleverTap()
302302

303-
const args = new ArgsBuilder()
304-
.add(Constants.PARAMS.USER_ATTRIBUTES, JSON.stringify(userAttributes))
305-
.add(Constants.PARAMS.COUNTRY, Constants.VALUES.DETECT)
306-
.add(Constants.PARAMS.REGION, Constants.VALUES.DETECT)
307-
.add(Constants.PARAMS.CITY, Constants.VALUES.DETECT)
308-
.add(Constants.PARAMS.LOCATION, Constants.VALUES.DETECT)
309-
.add(Constants.PARAMS.SYSTEM_NAME, this._systemName || this._browserDetector.OS)
310-
.add(Constants.PARAMS.SYSTEM_VERSION, (this._systemVersion || '').toString())
311-
.add(Constants.PARAMS.BROWSER_NAME, this._browserDetector.browser)
312-
.add(Constants.PARAMS.BROWSER_VERSION, this._browserDetector.version.toString())
313-
.add(Constants.PARAMS.LOCALE, this._locale || Constants.VALUES.DETECT)
314-
.add(Constants.PARAMS.DEVICE_NAME, this._deviceName ||
315-
`${this._browserDetector.browser} ${this._browserDetector.version}`)
316-
.add(Constants.PARAMS.DEVICE_MODEL, this._deviceModel || 'Web Browser')
317-
.add(Constants.PARAMS.NEWSFEED_MESSAGES, this._lpInbox.messageIds())
318-
.add(Constants.PARAMS.INCLUDE_DEFAULTS, false)
319-
.add(Constants.PARAMS.INCLUDE_VARIANT_DEBUG_INFO, this._internalState.variantDebugInfoEnabled)
303+
// silently register subscription in CT
304+
this.isWebPushSubscribed().then((isSubscribed) => {
305+
if (isSubscribed) {
306+
this._events.emit('webPushSubscribed')
307+
}
308+
})
309+
} else if (state === MigrationState.CLEVERTAP) {
310+
this._ct = this._migration.initCleverTap()
311+
312+
Object.values(Constants.DEFAULT_KEYS)
313+
.filter(key => ![
314+
Constants.DEFAULT_KEYS.USER_ID,
315+
Constants.DEFAULT_KEYS.DEVICE_ID,
316+
Constants.DEFAULT_KEYS.TOKEN,
317+
].includes(key))
318+
.forEach(key => StorageManager.remove(key))
319+
}
320320

321-
this.createRequest(Constants.METHODS.START, args, {
322-
queued: true,
323-
sendNow: true,
324-
response: (response: Array<Object>) => {
325-
this._internalState.hasStarted = true
326-
const startResponse = this._lpRequest.getLastResponse(response)
327-
const isSuccess = this._lpRequest.isResponseSuccess(startResponse)
328-
this._internalState.startSuccessful = isSuccess
321+
this._lpRequest.userId = userId
329322

330-
if (isSuccess) {
323+
if (callback) {
324+
this.addStartResponseHandler(callback)
325+
}
331326

332-
this.updateSession()
327+
this._varCache.onUpdate = () => {
328+
this._varCache.triggerVariablesChangedHandlers()
329+
}
333330

334-
const messages = startResponse[Constants.KEYS.MESSAGES]
335-
if (startResponse.actionDefinitions) {
336-
messages.actionDefinitions = startResponse.actionDefinitions
337-
}
338-
this._events.emit('messagesReceived', messages)
331+
const args = new ArgsBuilder()
332+
.add(Constants.PARAMS.USER_ATTRIBUTES, JSON.stringify(userAttributes))
333+
.add(Constants.PARAMS.COUNTRY, Constants.VALUES.DETECT)
334+
.add(Constants.PARAMS.REGION, Constants.VALUES.DETECT)
335+
.add(Constants.PARAMS.CITY, Constants.VALUES.DETECT)
336+
.add(Constants.PARAMS.LOCATION, Constants.VALUES.DETECT)
337+
.add(Constants.PARAMS.SYSTEM_NAME, this._systemName || this._browserDetector.OS)
338+
.add(Constants.PARAMS.SYSTEM_VERSION, (this._systemVersion || '').toString())
339+
.add(Constants.PARAMS.BROWSER_NAME, this._browserDetector.browser)
340+
.add(Constants.PARAMS.BROWSER_VERSION, this._browserDetector.version.toString())
341+
.add(Constants.PARAMS.LOCALE, this._locale || Constants.VALUES.DETECT)
342+
.add(Constants.PARAMS.DEVICE_NAME, this._deviceName ||
343+
`${this._browserDetector.browser} ${this._browserDetector.version}`)
344+
.add(Constants.PARAMS.DEVICE_MODEL, this._deviceModel || 'Web Browser')
345+
.add(Constants.PARAMS.NEWSFEED_MESSAGES, this._lpInbox.messageIds())
346+
.add(Constants.PARAMS.INCLUDE_DEFAULTS, false)
347+
.add(Constants.PARAMS.INCLUDE_VARIANT_DEBUG_INFO, this._internalState.variantDebugInfoEnabled)
348+
349+
this.createRequest(Constants.METHODS.START, args, {
350+
queued: true,
351+
sendNow: true,
352+
response: (response: BatchResponse) => {
353+
this._internalState.hasStarted = true
354+
const startResponse = this._lpRequest.getLastResponse(response)
355+
const isSuccess = this._lpRequest.isResponseSuccess(startResponse)
356+
this._internalState.startSuccessful = isSuccess
357+
358+
if (isSuccess) {
359+
360+
this.updateSession()
361+
362+
const messages = startResponse[Constants.KEYS.MESSAGES]
363+
if (startResponse.actionDefinitions) {
364+
messages.actionDefinitions = startResponse.actionDefinitions
365+
}
366+
this._events.emit('messagesReceived', messages)
339367

340-
if (startResponse[Constants.KEYS.SYNC_INBOX]) {
341-
this._lpInbox.downloadMessages()
342-
}
368+
if (startResponse[Constants.KEYS.SYNC_INBOX]) {
369+
this._lpInbox.downloadMessages()
370+
}
343371

344-
if (this._internalState.devMode) {
345-
const latestVersion = startResponse[Constants.KEYS.LATEST_VERSION]
346-
if (latestVersion) {
347-
console.log(`A newer version of the Leanplum SDK, ${latestVersion}, is available.
348-
Use "npm update leanplum-sdk" or go to https://docs.leanplum.com/reference#javascript-setup to download it.`)
372+
if (this._internalState.devMode) {
373+
const latestVersion = startResponse[Constants.KEYS.LATEST_VERSION]
374+
if (latestVersion) {
375+
console.log(`A newer version of the Leanplum SDK, ${latestVersion}, is available.
376+
Use "npm update leanplum-sdk" or go to https://docs.leanplum.com/reference#javascript-setup to download it.`)
377+
}
378+
this.connectSocket()
349379
}
350-
this.connectSocket()
351-
}
352380

353-
this._varCache.applyDiffs(
354-
startResponse[Constants.KEYS.VARS],
355-
startResponse[Constants.KEYS.VARIANTS],
356-
startResponse[Constants.KEYS.ACTION_DEFINITIONS])
357-
this._varCache.setVariantDebugInfo(startResponse[Constants.KEYS.VARIANT_DEBUG_INFO])
358-
this._varCache.token = startResponse[Constants.KEYS.TOKEN]
359-
} else {
360-
this._varCache.loadDiffs()
361-
}
381+
this._varCache.applyDiffs(
382+
startResponse[Constants.KEYS.VARS],
383+
startResponse[Constants.KEYS.VARIANTS],
384+
startResponse[Constants.KEYS.ACTION_DEFINITIONS])
385+
this._varCache.setVariantDebugInfo(startResponse[Constants.KEYS.VARIANT_DEBUG_INFO])
386+
this._varCache.token = startResponse[Constants.KEYS.TOKEN]
387+
} else {
388+
this._varCache.loadDiffs()
389+
}
362390

363-
this._events.emit('start', { success: isSuccess })
364-
this._internalState.triggerStartHandlers()
365-
},
391+
this._events.emit('start', { success: isSuccess })
392+
this._internalState.triggerStartHandlers()
393+
},
394+
})
366395
})
367396
}
368397

@@ -524,6 +553,7 @@ Use "npm update leanplum-sdk" or go to https://docs.leanplum.com/reference#javas
524553

525554
this.createRequest(Constants.METHODS.TRACK, args, {
526555
queued: true,
556+
isPurchase: true,
527557
})
528558
}
529559

@@ -618,10 +648,15 @@ Use "npm update leanplum-sdk" or go to https://docs.leanplum.com/reference#javas
618648
}
619649

620650
private createRequest(action: string, args: ArgsBuilder, options: any = {}): void {
621-
this._lpRequest.request(action, args, {
622-
devMode: this._internalState.devMode,
623-
...options,
624-
})
651+
652+
const suppress = this._migration.duplicateRequest(action, args, options)
653+
654+
if (!suppress) {
655+
this._lpRequest.request(action, args, {
656+
devMode: this._internalState.devMode,
657+
...options,
658+
})
659+
}
625660
}
626661

627662
private connectSocket(): void {

0 commit comments

Comments
 (0)