Skip to content

Commit b561e00

Browse files
xiaoweiizxkane
authored andcommitted
feat: support immediate mode
1 parent c053b7a commit b561e00

27 files changed

+3089
-239
lines changed

jest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ module.exports = {
1414
preset: 'ts-jest',
1515
testMatch: ['**/*.test.ts'],
1616
moduleFileExtensions: ['ts', 'js'],
17+
testEnvironment: 'jsdom',
1718
};

package-lock.json

Lines changed: 1544 additions & 151 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
},
1515
"dependencies": {
1616
"@aws-amplify/core": "^5.5.1",
17-
"uuid": "^9.0.0"
17+
"uuid": "^9.0.0",
18+
"@types/uuid": "^9.0.0",
19+
"@aws-crypto/sha256-browser": "^4.0.0"
1820
},
1921
"devDependencies": {
2022
"@typescript-eslint/eslint-plugin": "^5.60.0",
@@ -26,7 +28,10 @@
2628
"@types/jest": "^29.1.0",
2729
"typescript": "^4.9.5",
2830
"webpack": "^5.88.0",
29-
"webpack-cli": "^5.1.4"
31+
"webpack-cli": "^5.1.4",
32+
"@types/jsdom": "^21.1.1",
33+
"jest-environment-jsdom": "29.5.0",
34+
"fetch-mock": "^9.11.0"
3035
},
3136
"engines": {
3237
"node": ">=16.19.0"

src/ClickstreamAnalytics.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@
1111
* and limitations under the License.
1212
*/
1313
import { ConsoleLogger as Logger } from '@aws-amplify/core';
14-
import { ClickstreamProvider } from './provider/ClickstreamProvider';
15-
import { ClickstreamConfiguration } from './types';
14+
import { ClickstreamProvider } from './provider';
15+
import {
16+
ClickstreamAttribute,
17+
ClickstreamConfiguration,
18+
ClickstreamEvent,
19+
} from './types';
1620

1721
export class ClickstreamAnalytics {
1822
private static provider: ClickstreamProvider;
@@ -33,7 +37,15 @@ export class ClickstreamAnalytics {
3337
return true;
3438
}
3539

36-
public static record(params: object) {
37-
this.provider.record(params);
40+
public static record(event: ClickstreamEvent) {
41+
this.provider.record(event);
42+
}
43+
44+
public static setUserId(userId: string | null) {
45+
this.provider.setUserId(userId);
46+
}
47+
48+
public static setUserAttributes(attributes: ClickstreamAttribute) {
49+
this.provider.setUserAttributes(attributes);
3850
}
3951
}

src/browser/BrowserInfo.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
5+
* with the License. A copy of the License is located at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
10+
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*/
13+
14+
export class BrowserInfo {
15+
locale: string;
16+
system_language: string;
17+
country_code: string;
18+
make: string;
19+
userAgent: string;
20+
zoneOffset: number;
21+
hostName: string;
22+
23+
constructor() {
24+
const { product, vendor, userAgent, language } = window.navigator;
25+
this.locale = language;
26+
this.initLocalInfo(language);
27+
this.make = product || vendor;
28+
this.userAgent = userAgent;
29+
this.zoneOffset = -new Date().getTimezoneOffset() * 60000;
30+
this.hostName = window.location.hostname;
31+
}
32+
33+
initLocalInfo(locale: string) {
34+
if (locale.indexOf('-') > 0) {
35+
this.system_language = locale.split('-')[0];
36+
this.country_code = locale.split('-')[1].toUpperCase();
37+
} else {
38+
this.system_language = locale;
39+
this.country_code = '';
40+
}
41+
}
42+
}

src/browser/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
5+
* with the License. A copy of the License is located at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
10+
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*/
13+
14+
export * from './BrowserInfo';

src/network/NetRequest.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
5+
* with the License. A copy of the License is located at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
10+
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*/
13+
import { ConsoleLogger as Logger } from '@aws-amplify/core';
14+
import { ClickstreamContext } from '../provider';
15+
16+
const logger = new Logger('NetRequest');
17+
18+
export class NetRequest {
19+
static readonly REQUEST_TIMEOUT = 3000;
20+
static readonly REQUEST_RETRY_TIMES = 3;
21+
22+
static async sendRequest(
23+
eventsJson: string,
24+
clickstream: ClickstreamContext,
25+
bundleSequenceId: number,
26+
retryTimes = NetRequest.REQUEST_RETRY_TIMES
27+
): Promise<boolean> {
28+
const { configuration, browserInfo } = clickstream;
29+
const queryParams = new URLSearchParams({
30+
platform: 'Web',
31+
appId: configuration.appId,
32+
event_bundle_sequence_id: bundleSequenceId.toString(),
33+
});
34+
const url = `${configuration.endpoint}?${queryParams.toString()}`;
35+
36+
const controller = new AbortController();
37+
const timeoutId = setTimeout(() => {
38+
controller.abort();
39+
}, NetRequest.REQUEST_TIMEOUT);
40+
41+
const requestOptions: RequestInit = {
42+
method: 'POST',
43+
headers: {
44+
'Content-Type': 'application/json; charset=utf-8',
45+
cookie: configuration.authCookie,
46+
'User-Agent': browserInfo.userAgent,
47+
},
48+
body: eventsJson,
49+
};
50+
requestOptions.signal = controller.signal;
51+
52+
let retries = 0;
53+
while (retries < retryTimes) {
54+
try {
55+
const response = await fetch(url, requestOptions);
56+
if (response.ok && response.status === 200) {
57+
return true;
58+
} else {
59+
logger.error(`Request failed with status code ${response.status}`);
60+
}
61+
} catch (error) {
62+
logger.error(`Error during request: ${error}`);
63+
} finally {
64+
clearTimeout(timeoutId);
65+
retries++;
66+
}
67+
}
68+
logger.error(`Request failed after ${retryTimes} retries`);
69+
return false;
70+
}
71+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
5+
* with the License. A copy of the License is located at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
10+
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*/
13+
import { v4 as uuidV4 } from 'uuid';
14+
import { ClickstreamContext } from './ClickstreamContext';
15+
import { Event } from './Event';
16+
import {
17+
AnalyticsEvent,
18+
ClickstreamAttribute,
19+
ClickstreamEvent,
20+
UserAttribute,
21+
} from '../types';
22+
import { HashUtil } from '../util/HashUtil';
23+
import { StorageUtil } from '../util/StorageUtil';
24+
25+
export class AnalyticsEventBuilder {
26+
static async createEvent(
27+
event: ClickstreamEvent,
28+
userAttributes: UserAttribute,
29+
clickstream: ClickstreamContext
30+
): Promise<AnalyticsEvent> {
31+
const { browserInfo, configuration } = clickstream;
32+
const attributes = this.getEventAttributesWithCheck(event.attributes);
33+
const analyticEvent = {
34+
hashCode: '',
35+
event_type: event.name,
36+
event_id: uuidV4(),
37+
device_id: StorageUtil.getDeviceId(),
38+
unique_id: clickstream.userUniqueId,
39+
app_id: configuration.appId,
40+
timestamp: new Date().getTime(),
41+
host_name: browserInfo.hostName,
42+
locale: browserInfo.locale,
43+
system_language: browserInfo.system_language,
44+
country_code: browserInfo.country_code,
45+
zone_offset: browserInfo.zoneOffset,
46+
make: browserInfo.make,
47+
platform: 'Web',
48+
screen_height: window.innerHeight,
49+
screen_width: window.innerWidth,
50+
sdk_name: 'aws-solution-clickstream-sdk',
51+
sdk_version: process.env.VERSION ?? '',
52+
user: userAttributes ?? {},
53+
attributes: attributes ?? {},
54+
};
55+
analyticEvent.hashCode = await HashUtil.getHashCode(
56+
JSON.stringify(analyticEvent)
57+
);
58+
return analyticEvent;
59+
}
60+
61+
static getEventAttributesWithCheck(
62+
attributes: ClickstreamAttribute
63+
): ClickstreamAttribute {
64+
const resultAttributes: ClickstreamAttribute = {};
65+
for (const key in attributes) {
66+
const value = attributes[key];
67+
if (value !== null) {
68+
const currentNumber = Object.keys(resultAttributes).length;
69+
const { checkAttributes } = Event;
70+
const result = checkAttributes(currentNumber, key, value);
71+
const { ERROR_CODE, ERROR_MESSAGE } = Event.ReservedAttribute;
72+
if (result.error_code > 0 && !(ERROR_CODE in resultAttributes)) {
73+
resultAttributes[ERROR_CODE] = result.error_code;
74+
resultAttributes[ERROR_MESSAGE] = result.error_message;
75+
} else {
76+
resultAttributes[key] = value;
77+
}
78+
}
79+
}
80+
return resultAttributes;
81+
}
82+
}

src/provider/ClickstreamContext.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
5+
* with the License. A copy of the License is located at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
10+
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*/
13+
import { BrowserInfo } from '../browser';
14+
import { ClickstreamConfiguration } from '../types';
15+
import { StorageUtil } from '../util/StorageUtil';
16+
17+
export class ClickstreamContext {
18+
browserInfo: BrowserInfo;
19+
configuration: ClickstreamConfiguration;
20+
userUniqueId: string;
21+
22+
constructor(
23+
browserInfo: BrowserInfo,
24+
configuration: ClickstreamConfiguration
25+
) {
26+
this.browserInfo = browserInfo;
27+
this.configuration = configuration;
28+
this.userUniqueId = StorageUtil.getCurrentUserUniqueId();
29+
}
30+
}

0 commit comments

Comments
 (0)