Skip to content

Commit deed372

Browse files
zhu-xiaoweixiaoweii
andauthored
feat: support items record (#8)
Co-authored-by: xiaoweii <xiaoweii@amazom.com>
1 parent 16379d3 commit deed372

13 files changed

+459
-203
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,26 @@ ClickstreamAnalytics.setUserAttributes({
6767

6868
Current login user's attributes will be cached in localStorage, so the next time browser open you don't need to set all user's attribute again, of course you can use the same api `ClickstreamAnalytics.setUserAttributes()` to update the current user's attribute when it changes.
6969

70+
#### Record event with items
71+
72+
You can add the following code to log an event with an item.
73+
74+
```typescript
75+
import { ClickstreamAnalytics } from '@aws/clickstream-web';
76+
77+
const item_product = {
78+
id: '1',
79+
name: 'Nature',
80+
category: 'book',
81+
price: 56.5,
82+
};
83+
ClickstreamAnalytics.record({
84+
name: 'buttonClick',
85+
attributes: { _channel: 'SMS', Successful: true },
86+
items: [item_product],
87+
});
88+
```
89+
7090
#### Other configurations
7191
In addition to the required `appId` and `endpoint`, you can configure other information to get more customized usage:
7292

src/provider/AnalyticsEventBuilder.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
import { v4 as uuidV4 } from 'uuid';
1414
import { ClickstreamContext } from './ClickstreamContext';
1515
import { Event } from './Event';
16+
import { EventChecker } from './EventChecker';
1617
import { BrowserInfo } from '../browser';
1718
import config from '../config';
1819
import { Session } from '../tracker';
1920
import {
2021
AnalyticsEvent,
2122
ClickstreamAttribute,
2223
ClickstreamEvent,
24+
Item,
2325
UserAttribute,
2426
} from '../types';
2527
import { HashUtil } from '../util/HashUtil';
@@ -49,6 +51,8 @@ export class AnalyticsEventBuilder {
4951
attributes[Event.ReservedAttribute.PAGE_URL] =
5052
BrowserInfo.getCurrentPageUrl();
5153

54+
const items = this.getEventItemsWithCheck(event.items, attributes);
55+
5256
const analyticEvent = {
5357
hashCode: '',
5458
event_type: event.name,
@@ -68,6 +72,7 @@ export class AnalyticsEventBuilder {
6872
screen_width: window.innerWidth,
6973
sdk_name: 'aws-solution-clickstream-sdk',
7074
sdk_version: sdkVersion,
75+
items: items,
7176
user: userAttributes ?? {},
7277
attributes: attributes ?? {},
7378
};
@@ -81,14 +86,14 @@ export class AnalyticsEventBuilder {
8186
attributes: ClickstreamAttribute
8287
): ClickstreamAttribute {
8388
const resultAttributes: ClickstreamAttribute = {};
89+
const { checkAttributes } = EventChecker;
8490
for (const key in attributes) {
8591
const value = attributes[key];
8692
if (value !== null) {
8793
const currentNumber = Object.keys(resultAttributes).length;
88-
const { checkAttributes } = Event;
8994
const result = checkAttributes(currentNumber, key, value);
9095
const { ERROR_CODE, ERROR_MESSAGE } = Event.ReservedAttribute;
91-
if (result.error_code > 0 && !(ERROR_CODE in resultAttributes)) {
96+
if (result.error_code > 0) {
9297
resultAttributes[ERROR_CODE] = result.error_code;
9398
resultAttributes[ERROR_MESSAGE] = result.error_message;
9499
} else {
@@ -98,4 +103,27 @@ export class AnalyticsEventBuilder {
98103
}
99104
return resultAttributes;
100105
}
106+
107+
static getEventItemsWithCheck(
108+
items: Item[],
109+
attributes: ClickstreamAttribute
110+
): Item[] {
111+
let resultItems = undefined;
112+
if (items !== undefined) {
113+
resultItems = [];
114+
const { checkItems } = EventChecker;
115+
for (const item of items) {
116+
const result = checkItems(resultItems.length, item);
117+
const { ERROR_CODE, ERROR_MESSAGE } = Event.ReservedAttribute;
118+
if (result.error_code > 0) {
119+
attributes[ERROR_CODE] = result.error_code;
120+
attributes[ERROR_MESSAGE] = result.error_message;
121+
}
122+
if (result.error_code !== Event.ErrorCode.ITEM_SIZE_EXCEED) {
123+
resultItems.push(item);
124+
}
125+
}
126+
}
127+
return resultItems;
128+
}
101129
}

src/provider/ClickstreamProvider.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { LOG_TYPE } from '@aws-amplify/core/lib/Logger';
1515
import { AnalyticsEventBuilder } from './AnalyticsEventBuilder';
1616
import { ClickstreamContext } from './ClickstreamContext';
1717
import { Event } from './Event';
18+
import { EventChecker } from './EventChecker';
1819
import { EventRecorder } from './EventRecorder';
1920
import { BrowserInfo } from '../browser';
2021
import { PageViewTracker, SessionTracker } from '../tracker';
@@ -109,7 +110,7 @@ export class ClickstreamProvider implements AnalyticsProvider {
109110
}
110111

111112
record(event: ClickstreamEvent) {
112-
const result = Event.checkEventName(event.name);
113+
const result = EventChecker.checkEventName(event.name);
113114
if (result.error_code > 0) {
114115
logger.error(result.error_message);
115116
return;
@@ -158,7 +159,7 @@ export class ClickstreamProvider implements AnalyticsProvider {
158159
delete this.userAttribute[key];
159160
} else {
160161
const currentNumber = Object.keys(this.userAttribute).length;
161-
const { checkUserAttribute } = Event;
162+
const { checkUserAttribute } = EventChecker;
162163
const result = checkUserAttribute(currentNumber, key, value);
163164
if (result.error_code > 0) {
164165
const { ERROR_CODE, ERROR_MESSAGE } = Event.ReservedAttribute;

src/provider/Event.ts

Lines changed: 4 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -10,180 +10,7 @@
1010
* OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
1111
* and limitations under the License.
1212
*/
13-
import { ConsoleLogger as Logger } from '@aws-amplify/core';
14-
import { EventError } from '../types';
15-
16-
const logger = new Logger('ClickstreamProvider');
17-
1813
export class Event {
19-
static checkEventName(eventName: string): EventError {
20-
const { EVENT_NAME_INVALID, EVENT_NAME_LENGTH_EXCEED, NO_ERROR } =
21-
Event.ErrorCode;
22-
const { MAX_EVENT_TYPE_LENGTH } = Event.Limit;
23-
if (!Event.isValidName(eventName)) {
24-
return {
25-
error_code: EVENT_NAME_INVALID,
26-
error_message:
27-
`Event name can only contains uppercase and lowercase letters, ` +
28-
`underscores, number, and is not start with a number. event name: ${eventName}`,
29-
};
30-
} else if (eventName.length > MAX_EVENT_TYPE_LENGTH) {
31-
return {
32-
error_code: EVENT_NAME_LENGTH_EXCEED,
33-
error_message:
34-
`Event name is too long, the max event type length is ` +
35-
`${MAX_EVENT_TYPE_LENGTH} characters. event name: ${eventName}`,
36-
};
37-
}
38-
return {
39-
error_code: NO_ERROR,
40-
};
41-
}
42-
43-
static isValidName(name: string): boolean {
44-
const regex = /^(?![0-9])[0-9a-zA-Z_]+$/;
45-
return regex.test(name);
46-
}
47-
48-
static checkAttributes(
49-
currentNumber: number,
50-
key: string,
51-
value: string | number | boolean
52-
): EventError {
53-
const { MAX_NUM_OF_ATTRIBUTES, MAX_LENGTH_OF_NAME, MAX_LENGTH_OF_VALUE } =
54-
Event.Limit;
55-
const {
56-
NO_ERROR,
57-
ATTRIBUTE_SIZE_EXCEED,
58-
ATTRIBUTE_NAME_INVALID,
59-
ATTRIBUTE_NAME_LENGTH_EXCEED,
60-
ATTRIBUTE_VALUE_LENGTH_EXCEED,
61-
} = Event.ErrorCode;
62-
if (currentNumber >= MAX_NUM_OF_ATTRIBUTES) {
63-
const errorMsg =
64-
`reached the max number of user attributes limit ${MAX_NUM_OF_ATTRIBUTES}. ` +
65-
`and the user attribute: ${key} will not be recorded`;
66-
logger.error(errorMsg);
67-
const errorString = `attribute name: ${key}`;
68-
return {
69-
error_message: Event.getLimitString(errorString),
70-
error_code: ATTRIBUTE_SIZE_EXCEED,
71-
};
72-
}
73-
if (key.length > MAX_LENGTH_OF_NAME) {
74-
const errorMsg =
75-
`attribute : ${key}, reached the max length of attributes name ` +
76-
`limit(${MAX_LENGTH_OF_NAME}). current length is: (${key.length}) ` +
77-
`and the attribute will not be recorded`;
78-
logger.error(errorMsg);
79-
const errorString = `attribute name length is: (${key.length}) name is: ${key}`;
80-
return {
81-
error_message: Event.getLimitString(errorString),
82-
error_code: ATTRIBUTE_NAME_LENGTH_EXCEED,
83-
};
84-
}
85-
if (!Event.isValidName(key)) {
86-
const errorMsg =
87-
`attribute : ${key}, was not valid, attribute name can only ` +
88-
`contains uppercase and lowercase letters, underscores, number, and is not ` +
89-
`start with a number, so the attribute will not be recorded`;
90-
logger.error(errorMsg);
91-
return {
92-
error_message: Event.getLimitString(key),
93-
error_code: ATTRIBUTE_NAME_INVALID,
94-
};
95-
}
96-
const valueLength = String(value).length;
97-
if (valueLength > MAX_LENGTH_OF_VALUE) {
98-
const errorMsg =
99-
`attribute : ${key}, reached the max length of attributes value limit ` +
100-
`(${MAX_LENGTH_OF_VALUE}). current length is: (${valueLength}). ` +
101-
`and the attribute will not be recorded, attribute value: ${value}`;
102-
logger.error(errorMsg);
103-
const errorString = `attribute name: ${key}, attribute value: ${value}`;
104-
return {
105-
error_message: Event.getLimitString(errorString),
106-
error_code: ATTRIBUTE_VALUE_LENGTH_EXCEED,
107-
};
108-
}
109-
return {
110-
error_code: NO_ERROR,
111-
};
112-
}
113-
114-
static getLimitString(str: string): string {
115-
return str.substring(0, Event.Limit.MAX_LENGTH_OF_ERROR_VALUE);
116-
}
117-
118-
static checkUserAttribute(
119-
currentNumber: number,
120-
key: string,
121-
value: string | number | boolean
122-
): EventError {
123-
const {
124-
MAX_NUM_OF_USER_ATTRIBUTES,
125-
MAX_LENGTH_OF_NAME,
126-
MAX_LENGTH_OF_USER_VALUE,
127-
} = Event.Limit;
128-
const {
129-
NO_ERROR,
130-
USER_ATTRIBUTE_SIZE_EXCEED,
131-
USER_ATTRIBUTE_NAME_LENGTH_EXCEED,
132-
USER_ATTRIBUTE_NAME_INVALID,
133-
USER_ATTRIBUTE_VALUE_LENGTH_EXCEED,
134-
} = Event.ErrorCode;
135-
if (currentNumber >= MAX_NUM_OF_USER_ATTRIBUTES) {
136-
const errorMsg =
137-
`reached the max number of user attributes limit (${MAX_NUM_OF_USER_ATTRIBUTES}). ` +
138-
`and the user attribute: ${key} will not be recorded`;
139-
logger.error(errorMsg);
140-
const errorString = `attribute name:${key}`;
141-
return {
142-
error_message: Event.getLimitString(errorString),
143-
error_code: USER_ATTRIBUTE_SIZE_EXCEED,
144-
};
145-
}
146-
if (key.length > MAX_LENGTH_OF_NAME) {
147-
const errorMsg =
148-
`user attribute : ${key}, reached the max length of attributes name limit ` +
149-
`(${MAX_LENGTH_OF_NAME}). current length is: (${key.length}) ` +
150-
`and the attribute will not be recorded`;
151-
logger.error(errorMsg);
152-
const errorString = `user attribute name length is: (${key.length}) name is: ${key}`;
153-
return {
154-
error_message: Event.getLimitString(errorString),
155-
error_code: USER_ATTRIBUTE_NAME_LENGTH_EXCEED,
156-
};
157-
}
158-
if (!Event.isValidName(key)) {
159-
const errorMsg =
160-
`user attribute : ${key}, was not valid, user attribute name can only ` +
161-
`contains uppercase and lowercase letters, underscores, number, and is not ` +
162-
`start with a number. so the attribute will not be recorded`;
163-
logger.error(errorMsg);
164-
return {
165-
error_message: Event.getLimitString(key),
166-
error_code: USER_ATTRIBUTE_NAME_INVALID,
167-
};
168-
}
169-
const valueLength = String(value).length;
170-
if (valueLength > MAX_LENGTH_OF_USER_VALUE) {
171-
const errorMsg =
172-
`user attribute : ${key}, reached the max length of attributes value limit ` +
173-
`(${MAX_LENGTH_OF_USER_VALUE}). current length is: (${valueLength}). ` +
174-
`and the attribute will not be recorded, attribute value: ${value}`;
175-
logger.error(errorMsg);
176-
const errorString = `attribute name: ${key}, attribute value: ${value}`;
177-
return {
178-
error_message: Event.getLimitString(errorString),
179-
error_code: USER_ATTRIBUTE_VALUE_LENGTH_EXCEED,
180-
};
181-
}
182-
return {
183-
error_code: NO_ERROR,
184-
};
185-
}
186-
18714
static readonly Limit = {
18815
MAX_EVENT_TYPE_LENGTH: 50,
18916
MAX_NUM_OF_ATTRIBUTES: 500,
@@ -193,6 +20,8 @@ export class Event {
19320
MAX_LENGTH_OF_USER_VALUE: 256,
19421
MAX_EVENT_NUMBER_OF_BATCH: 100,
19522
MAX_LENGTH_OF_ERROR_VALUE: 256,
23+
MAX_NUM_OF_ITEMS: 100,
24+
MAX_LENGTH_OF_ITEM_VALUE: 256,
19625
};
19726

19827
static readonly ErrorCode = {
@@ -207,6 +36,8 @@ export class Event {
20736
USER_ATTRIBUTE_NAME_LENGTH_EXCEED: 3002,
20837
USER_ATTRIBUTE_NAME_INVALID: 3003,
20938
USER_ATTRIBUTE_VALUE_LENGTH_EXCEED: 3004,
39+
ITEM_SIZE_EXCEED: 4001,
40+
ITEM_VALUE_LENGTH_EXCEED: 4002,
21041
};
21142

21243
static readonly ReservedAttribute = {

0 commit comments

Comments
 (0)