Skip to content

Commit 78b9547

Browse files
zhu-xiaoweizhu-xiaowei
andauthored
feat: support record items (#5)
Co-authored-by: zhu-xiaowei <xiaoweii@amazom.com>
1 parent e8e7bc4 commit 78b9547

File tree

10 files changed

+226
-9
lines changed

10 files changed

+226
-9
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 0.2.0
2+
3+
* feat: support record items with custom attributes.
4+
5+
### Pipeline version compatible
6+
Need use pipeline version 1.1.x and above
7+
18
## 0.1.0
29

310
* feat: add basic android and swift SDK APIs.

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,33 @@ analytics.deleteGlobalAttributes(["level"]);
100100

101101
It is recommended to set global attributes after each SDK initialization, global attributes will be included in all events that occur after it is set.
102102

103+
#### Record event with items
104+
105+
You can add the following code to log an event with an item. you can add custom item attribute in `attributes` object.
106+
107+
**Note: Only pipelines from version 1.1+ can handle items with custom attribute.**
108+
109+
```dart
110+
var itemBook = ClickstreamItem(
111+
id: "123",
112+
name: "Nature",
113+
category: "book",
114+
price: 99,
115+
attributes: {
116+
"book_publisher": "Nature Research"
117+
}
118+
);
119+
120+
analytics.record(
121+
name: "view_item",
122+
attributes: {
123+
"currency": 'USD',
124+
"event_category": 'recommended'
125+
},
126+
items: [itemBook]
127+
);
128+
```
129+
103130
#### Other configurations
104131

105132
In addition to the required `appId` and `endpoint`, you can configure other information to get more customized usage:

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ android {
5252
dependencies {
5353
testImplementation 'org.jetbrains.kotlin:kotlin-test'
5454
testImplementation 'org.mockito:mockito-core:5.0.0'
55-
implementation 'software.aws.solution:clickstream:0.9.0'
55+
implementation 'software.aws.solution:clickstream:0.10.0'
5656
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10"))
5757
}
5858

android/src/main/kotlin/software/aws/solution/clickstream_analytics/ClickstreamFlutterPlugin.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import software.aws.solution.clickstream.AWSClickstreamPlugin
3232
import software.aws.solution.clickstream.ClickstreamAnalytics
3333
import software.aws.solution.clickstream.ClickstreamAttribute
3434
import software.aws.solution.clickstream.ClickstreamEvent
35+
import software.aws.solution.clickstream.ClickstreamItem
3536
import software.aws.solution.clickstream.ClickstreamUserAttribute
3637
import software.aws.solution.clickstream.client.util.ThreadUtil
3738
import java.util.Objects
@@ -159,6 +160,7 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
159160
arguments?.let {
160161
val eventName = it["eventName"] as String
161162
val attributes = it["attributes"] as HashMap<*, *>
163+
val items = it["items"] as ArrayList<*>
162164
val eventBuilder = ClickstreamEvent.builder().name(eventName)
163165
for ((key, value) in attributes) {
164166
if (value is String) {
@@ -173,6 +175,27 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
173175
eventBuilder.add(key.toString(), value)
174176
}
175177
}
178+
if (items.size > 0) {
179+
val clickstreamItems = arrayOfNulls<ClickstreamItem>(items.size)
180+
for (index in 0 until items.size) {
181+
val builder = ClickstreamItem.builder()
182+
for ((key, value) in (items[index] as HashMap<*, *>)) {
183+
if (value is String) {
184+
builder.add(key.toString(), value)
185+
} else if (value is Double) {
186+
builder.add(key.toString(), value)
187+
} else if (value is Boolean) {
188+
builder.add(key.toString(), value)
189+
} else if (value is Int) {
190+
builder.add(key.toString(), value)
191+
} else if (value is Long) {
192+
builder.add(key.toString(), value)
193+
}
194+
}
195+
clickstreamItems[index] = builder.build()
196+
}
197+
eventBuilder.setItems(clickstreamItems)
198+
}
176199
ClickstreamAnalytics.recordEvent(eventBuilder.build())
177200
}
178201
}

example/lib/main.dart

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import 'package:clickstream_analytics/clickstream_analytics.dart';
5+
import 'package:clickstream_analytics/clickstream_analytics_item.dart';
56
import 'package:flutter/foundation.dart';
67
import 'package:flutter/material.dart';
78

@@ -54,10 +55,18 @@ class _MyAppState extends State<MyApp> {
5455
minLeadingWidth: 0,
5556
),
5657
ListTile(
57-
leading: const Icon(Icons.touch_app),
58-
title: const Text('recordEvent'),
58+
leading: const Icon(Icons.circle),
59+
title: const Text('recordEventWithName'),
5960
onTap: () async {
6061
analytics.record(name: "testEventWithName");
62+
log("recorded testEvent with testEventWithName");
63+
},
64+
minLeadingWidth: 0,
65+
),
66+
ListTile(
67+
leading: const Icon(Icons.touch_app),
68+
title: const Text('recordEventWithAttributes'),
69+
onTap: () async {
6170
analytics.record(name: "testEvent", attributes: {
6271
"category": 'shoes',
6372
"currency": 'CNY',
@@ -67,7 +76,47 @@ class _MyAppState extends State<MyApp> {
6776
"boolValue": true,
6877
"value": 279.9
6978
});
70-
log("recorded testEvent and testEventWithName");
79+
log("recorded testEvent and attributes");
80+
},
81+
minLeadingWidth: 0,
82+
),
83+
ListTile(
84+
leading: const Icon(Icons.touch_app_outlined),
85+
title: const Text('recordEventWithItem'),
86+
onTap: () async {
87+
var testItem1 = ClickstreamItem(
88+
id: "1",
89+
name: "testName1",
90+
brand: "Google",
91+
currency: "CNY",
92+
category: "book",
93+
locationId: "1",
94+
attributes: {
95+
"intValue": 21,
96+
"longValue": 888888888813991919,
97+
"doubleValue": 11.1234567890121213,
98+
"boolValue": true,
99+
"value": 279.9
100+
});
101+
var testItem2 = ClickstreamItem(
102+
id: "2",
103+
name: "testName2",
104+
brand: "Sumsang",
105+
currency: "USD",
106+
category: "shoes",
107+
locationId: "2",
108+
attributes: {
109+
"intValue": 13,
110+
"longValue": 9999999913991919,
111+
"doubleValue": 22.1234567890121213,
112+
"boolValue": true,
113+
"value": 379.9
114+
});
115+
analytics.record(
116+
name: "testRecordItem",
117+
attributes: {"testKey": "testValue"},
118+
items: [testItem1, testItem2]);
119+
log("recorded testEvent with item");
71120
},
72121
minLeadingWidth: 0,
73122
),
@@ -76,6 +125,14 @@ class _MyAppState extends State<MyApp> {
76125
title: const Text('setUserId'),
77126
onTap: () async {
78127
analytics.setUserId("12345");
128+
log("setUserId");
129+
},
130+
minLeadingWidth: 0,
131+
),
132+
ListTile(
133+
leading: const Icon(Icons.no_accounts),
134+
title: const Text('setUserIdToNull'),
135+
onTap: () async {
79136
analytics.setUserId(null);
80137
log("setUserId");
81138
},

ios/Classes/ClickstreamFlutterPlugin.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,17 @@ public class ClickstreamFlutterPlugin: NSObject, FlutterPlugin {
7676
func recordEvent(_ arguments: [String: Any]) {
7777
let eventName = arguments["eventName"] as! String
7878
let attributes = arguments["attributes"] as! [String: Any]
79+
let items = arguments["items"] as! [[String: Any]]
7980
if attributes.count > 0 {
80-
ClickstreamAnalytics.recordEvent(eventName, getClickstreamAttributes(attributes))
81+
if items.count > 0 {
82+
var clickstreamItems: [ClickstreamAttribute] = []
83+
for itemObject in items {
84+
clickstreamItems.append(getClickstreamAttributes(itemObject))
85+
}
86+
ClickstreamAnalytics.recordEvent(eventName, getClickstreamAttributes(attributes), clickstreamItems)
87+
} else {
88+
ClickstreamAnalytics.recordEvent(eventName, getClickstreamAttributes(attributes))
89+
}
8190
} else {
8291
ClickstreamAnalytics.recordEvent(eventName)
8392
}

ios/Clickstream

lib/clickstream_analytics.dart

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import 'clickstream_analytics_item.dart';
45
import 'clickstream_analytics_platform_interface.dart';
56

67
class ClickstreamAnalytics {
@@ -32,9 +33,20 @@ class ClickstreamAnalytics {
3233
}
3334

3435
Future<void> record(
35-
{required String name, Map<String, Object?>? attributes}) {
36-
return ClickstreamInterface.instance
37-
.record({"eventName": name, "attributes": attributes ?? {}});
36+
{required String name,
37+
Map<String, Object?>? attributes,
38+
List<ClickstreamItem>? items}) {
39+
var itemArray = [];
40+
if (items != null) {
41+
for (ClickstreamItem item in items) {
42+
itemArray.add(item.toMap());
43+
}
44+
}
45+
return ClickstreamInterface.instance.record({
46+
"eventName": name,
47+
"attributes": attributes ?? {},
48+
"items": itemArray
49+
});
3850
}
3951

4052
Future<void> setUserId(String? userId) {

lib/clickstream_analytics_item.dart

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
class ClickstreamItem {
4+
ClickstreamItem({
5+
this.id,
6+
this.name,
7+
this.locationId,
8+
this.brand,
9+
this.currency,
10+
this.price,
11+
this.quantity,
12+
this.creativeName,
13+
this.creativeSlot,
14+
this.category,
15+
this.category2,
16+
this.category3,
17+
this.category4,
18+
this.category5,
19+
this.attributes,
20+
});
21+
22+
final String? id;
23+
final String? name;
24+
final String? locationId;
25+
final String? brand;
26+
final String? currency;
27+
final num? price;
28+
final int? quantity;
29+
final String? creativeName;
30+
final String? creativeSlot;
31+
final String? category;
32+
final String? category2;
33+
final String? category3;
34+
final String? category4;
35+
final String? category5;
36+
// used to add custom item attribute.
37+
final Map<String, Object?>? attributes;
38+
39+
Map<String, Object?> toMap() {
40+
return <String, Object?>{
41+
if (id != null) 'id': id,
42+
if (name != null) 'name': name,
43+
if (locationId != null) 'location_id': locationId,
44+
if (brand != null) 'brand': brand,
45+
if (currency != null) 'currency': currency,
46+
if (price != null) 'price': price,
47+
if (quantity != null) 'quantity': quantity,
48+
if (creativeName != null) 'creative_name': creativeName,
49+
if (creativeSlot != null) 'creative_slot': creativeSlot,
50+
if (category != null) 'category': category,
51+
if (category2 != null) 'category2': category2,
52+
if (category3 != null) 'category3': category3,
53+
if (category4 != null) 'category4': category4,
54+
if (category5 != null) 'category5': category5,
55+
if (attributes != null) ...attributes!,
56+
};
57+
}
58+
}

test/clickstream_flutter_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import 'package:clickstream_analytics/clickstream_analytics.dart';
5+
import 'package:clickstream_analytics/clickstream_analytics_item.dart';
56
import 'package:clickstream_analytics/clickstream_analytics_method_channel.dart';
67
import 'package:clickstream_analytics/clickstream_analytics_platform_interface.dart';
78
import 'package:flutter_test/flutter_test.dart';
@@ -77,6 +78,29 @@ void main() {
7778
attributes: {"category": "shoes", "currency": "CNY", "value": 279.9});
7879
expect(result, isNotNull);
7980
});
81+
82+
test('record event with item', () async {
83+
var itemBook = ClickstreamItem(
84+
id: "123",
85+
name: "Nature",
86+
category: "book",
87+
currency: "CNY",
88+
price: 99,
89+
attributes: {"book_publisher": "Nature Research"});
90+
var itemShoes = ClickstreamItem(
91+
id: "124",
92+
name: "Nike",
93+
category: "shoes",
94+
price: 65,
95+
currency: "USD",
96+
attributes: {"place_of_origin": "USA"});
97+
var result = analytics.record(
98+
name: "cart_view",
99+
attributes: {"_traffic_source_name": "Summer promotion"},
100+
items: [itemBook, itemShoes]);
101+
expect(result, isNotNull);
102+
});
103+
80104
test('setUserId', () async {
81105
var result = analytics.setUserId("11234");
82106
expect(result, isNotNull);

0 commit comments

Comments
 (0)