Skip to content

feat: support record items #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.2.0

* feat: support record items with custom attributes.

### Pipeline version compatible
Need use pipeline version 1.1.x and above

## 0.1.0

* feat: add basic android and swift SDK APIs.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,33 @@ analytics.deleteGlobalAttributes(["level"]);

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.

#### Record event with items

You can add the following code to log an event with an item. you can add custom item attribute in `attributes` object.

**Note: Only pipelines from version 1.1+ can handle items with custom attribute.**

```dart
var itemBook = ClickstreamItem(
id: "123",
name: "Nature",
category: "book",
price: 99,
attributes: {
"book_publisher": "Nature Research"
}
);

analytics.record(
name: "view_item",
attributes: {
"currency": 'USD',
"event_category": 'recommended'
},
items: [itemBook]
);
```

#### Other configurations

In addition to the required `appId` and `endpoint`, you can configure other information to get more customized usage:
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ android {
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
implementation 'software.aws.solution:clickstream:0.9.0'
implementation 'software.aws.solution:clickstream:0.10.0'
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10"))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import software.aws.solution.clickstream.AWSClickstreamPlugin
import software.aws.solution.clickstream.ClickstreamAnalytics
import software.aws.solution.clickstream.ClickstreamAttribute
import software.aws.solution.clickstream.ClickstreamEvent
import software.aws.solution.clickstream.ClickstreamItem
import software.aws.solution.clickstream.ClickstreamUserAttribute
import software.aws.solution.clickstream.client.util.ThreadUtil
import java.util.Objects
Expand Down Expand Up @@ -159,6 +160,7 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
arguments?.let {
val eventName = it["eventName"] as String
val attributes = it["attributes"] as HashMap<*, *>
val items = it["items"] as ArrayList<*>
val eventBuilder = ClickstreamEvent.builder().name(eventName)
for ((key, value) in attributes) {
if (value is String) {
Expand All @@ -173,6 +175,27 @@ class ClickstreamFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware
eventBuilder.add(key.toString(), value)
}
}
if (items.size > 0) {
val clickstreamItems = arrayOfNulls<ClickstreamItem>(items.size)
for (index in 0 until items.size) {
val builder = ClickstreamItem.builder()
for ((key, value) in (items[index] as HashMap<*, *>)) {
if (value is String) {
builder.add(key.toString(), value)
} else if (value is Double) {
builder.add(key.toString(), value)
} else if (value is Boolean) {
builder.add(key.toString(), value)
} else if (value is Int) {
builder.add(key.toString(), value)
} else if (value is Long) {
builder.add(key.toString(), value)
}
}
clickstreamItems[index] = builder.build()
}
eventBuilder.setItems(clickstreamItems)
}
ClickstreamAnalytics.recordEvent(eventBuilder.build())
}
}
Expand Down
63 changes: 60 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import 'package:clickstream_analytics/clickstream_analytics.dart';
import 'package:clickstream_analytics/clickstream_analytics_item.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -54,10 +55,18 @@ class _MyAppState extends State<MyApp> {
minLeadingWidth: 0,
),
ListTile(
leading: const Icon(Icons.touch_app),
title: const Text('recordEvent'),
leading: const Icon(Icons.circle),
title: const Text('recordEventWithName'),
onTap: () async {
analytics.record(name: "testEventWithName");
log("recorded testEvent with testEventWithName");
},
minLeadingWidth: 0,
),
ListTile(
leading: const Icon(Icons.touch_app),
title: const Text('recordEventWithAttributes'),
onTap: () async {
analytics.record(name: "testEvent", attributes: {
"category": 'shoes',
"currency": 'CNY',
Expand All @@ -67,7 +76,47 @@ class _MyAppState extends State<MyApp> {
"boolValue": true,
"value": 279.9
});
log("recorded testEvent and testEventWithName");
log("recorded testEvent and attributes");
},
minLeadingWidth: 0,
),
ListTile(
leading: const Icon(Icons.touch_app_outlined),
title: const Text('recordEventWithItem'),
onTap: () async {
var testItem1 = ClickstreamItem(
id: "1",
name: "testName1",
brand: "Google",
currency: "CNY",
category: "book",
locationId: "1",
attributes: {
"intValue": 21,
"longValue": 888888888813991919,
"doubleValue": 11.1234567890121213,
"boolValue": true,
"value": 279.9
});
var testItem2 = ClickstreamItem(
id: "2",
name: "testName2",
brand: "Sumsang",
currency: "USD",
category: "shoes",
locationId: "2",
attributes: {
"intValue": 13,
"longValue": 9999999913991919,
"doubleValue": 22.1234567890121213,
"boolValue": true,
"value": 379.9
});
analytics.record(
name: "testRecordItem",
attributes: {"testKey": "testValue"},
items: [testItem1, testItem2]);
log("recorded testEvent with item");
},
minLeadingWidth: 0,
),
Expand All @@ -76,6 +125,14 @@ class _MyAppState extends State<MyApp> {
title: const Text('setUserId'),
onTap: () async {
analytics.setUserId("12345");
log("setUserId");
},
minLeadingWidth: 0,
),
ListTile(
leading: const Icon(Icons.no_accounts),
title: const Text('setUserIdToNull'),
onTap: () async {
analytics.setUserId(null);
log("setUserId");
},
Expand Down
11 changes: 10 additions & 1 deletion ios/Classes/ClickstreamFlutterPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,17 @@ public class ClickstreamFlutterPlugin: NSObject, FlutterPlugin {
func recordEvent(_ arguments: [String: Any]) {
let eventName = arguments["eventName"] as! String
let attributes = arguments["attributes"] as! [String: Any]
let items = arguments["items"] as! [[String: Any]]
if attributes.count > 0 {
ClickstreamAnalytics.recordEvent(eventName, getClickstreamAttributes(attributes))
if items.count > 0 {
var clickstreamItems: [ClickstreamAttribute] = []
for itemObject in items {
clickstreamItems.append(getClickstreamAttributes(itemObject))
}
ClickstreamAnalytics.recordEvent(eventName, getClickstreamAttributes(attributes), clickstreamItems)
} else {
ClickstreamAnalytics.recordEvent(eventName, getClickstreamAttributes(attributes))
}
} else {
ClickstreamAnalytics.recordEvent(eventName)
}
Expand Down
2 changes: 1 addition & 1 deletion ios/Clickstream
18 changes: 15 additions & 3 deletions lib/clickstream_analytics.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'clickstream_analytics_item.dart';
import 'clickstream_analytics_platform_interface.dart';

class ClickstreamAnalytics {
Expand Down Expand Up @@ -32,9 +33,20 @@ class ClickstreamAnalytics {
}

Future<void> record(
{required String name, Map<String, Object?>? attributes}) {
return ClickstreamInterface.instance
.record({"eventName": name, "attributes": attributes ?? {}});
{required String name,
Map<String, Object?>? attributes,
List<ClickstreamItem>? items}) {
var itemArray = [];
if (items != null) {
for (ClickstreamItem item in items) {
itemArray.add(item.toMap());
}
}
return ClickstreamInterface.instance.record({
"eventName": name,
"attributes": attributes ?? {},
"items": itemArray
});
}

Future<void> setUserId(String? userId) {
Expand Down
58 changes: 58 additions & 0 deletions lib/clickstream_analytics_item.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
class ClickstreamItem {
ClickstreamItem({
this.id,
this.name,
this.locationId,
this.brand,
this.currency,
this.price,
this.quantity,
this.creativeName,
this.creativeSlot,
this.category,
this.category2,
this.category3,
this.category4,
this.category5,
this.attributes,
});

final String? id;
final String? name;
final String? locationId;
final String? brand;
final String? currency;
final num? price;
final int? quantity;
final String? creativeName;
final String? creativeSlot;
final String? category;
final String? category2;
final String? category3;
final String? category4;
final String? category5;
// used to add custom item attribute.
final Map<String, Object?>? attributes;

Map<String, Object?> toMap() {
return <String, Object?>{
if (id != null) 'id': id,
if (name != null) 'name': name,
if (locationId != null) 'location_id': locationId,
if (brand != null) 'brand': brand,
if (currency != null) 'currency': currency,
if (price != null) 'price': price,
if (quantity != null) 'quantity': quantity,
if (creativeName != null) 'creative_name': creativeName,
if (creativeSlot != null) 'creative_slot': creativeSlot,
if (category != null) 'category': category,
if (category2 != null) 'category2': category2,
if (category3 != null) 'category3': category3,
if (category4 != null) 'category4': category4,
if (category5 != null) 'category5': category5,
if (attributes != null) ...attributes!,
};
}
}
24 changes: 24 additions & 0 deletions test/clickstream_flutter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import 'package:clickstream_analytics/clickstream_analytics.dart';
import 'package:clickstream_analytics/clickstream_analytics_item.dart';
import 'package:clickstream_analytics/clickstream_analytics_method_channel.dart';
import 'package:clickstream_analytics/clickstream_analytics_platform_interface.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down Expand Up @@ -77,6 +78,29 @@ void main() {
attributes: {"category": "shoes", "currency": "CNY", "value": 279.9});
expect(result, isNotNull);
});

test('record event with item', () async {
var itemBook = ClickstreamItem(
id: "123",
name: "Nature",
category: "book",
currency: "CNY",
price: 99,
attributes: {"book_publisher": "Nature Research"});
var itemShoes = ClickstreamItem(
id: "124",
name: "Nike",
category: "shoes",
price: 65,
currency: "USD",
attributes: {"place_of_origin": "USA"});
var result = analytics.record(
name: "cart_view",
attributes: {"_traffic_source_name": "Summer promotion"},
items: [itemBook, itemShoes]);
expect(result, isNotNull);
});

test('setUserId', () async {
var result = analytics.setUserId("11234");
expect(result, isNotNull);
Expand Down