Skip to content
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

feat: add preset traffic source attributes #10

Merged
merged 3 commits into from
Apr 11, 2024
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.4.0

* feat: add preset traffic source attributes

## 0.3.0

* feat: add record screen view api
Expand Down
22 changes: 15 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ analytics.setUserId(null);

```dart
analytics.setUserAttributes({
"userName":"carl",
"userName": "carl",
"userAge": 22
});
```
Expand All @@ -90,23 +90,30 @@ Current login user's attributes will be cached in disk, so the next time app lau
#### Add global attribute

1. Add global attributes when initializing the SDK


The following example code shows how to add traffic source fields as global attributes when initializing the SDK.
```dart
analytics.init({
appId: "your appId",
endpoint: "https://example.com/collect",
globalAttributes: {
"_traffic_source_medium": "Search engine",
"_traffic_source_name": "Summer promotion",
Attr.TRAFFIC_SOURCE_SOURCE: "amazon",
Attr.TRAFFIC_SOURCE_MEDIUM: "cpc",
Attr.TRAFFIC_SOURCE_CAMPAIGN: "summer_promotion",
Attr.TRAFFIC_SOURCE_CAMPAIGN_ID: "summer_promotion_01",
Attr.TRAFFIC_SOURCE_TERM: "running_shoes",
Attr.TRAFFIC_SOURCE_CONTENT: "banner_ad_1",
Attr.TRAFFIC_SOURCE_CLID: "amazon_ad_123",
Attr.TRAFFIC_SOURCE_CLID_PLATFORM: "amazon_ads",
Attr.APP_INSTALL_CHANNEL: "amazon_store"
}
});
```

2. Add global attributes after initializing the SDK
```dart
analytics.addGlobalAttributes({
"_traffic_source_medium": "Search engine",
"_traffic_source_name": "Summer promotion",
Attr.TRAFFIC_SOURCE_MEDIUM: "Search engine",
"level": 10
});
```
Expand Down Expand Up @@ -138,7 +145,8 @@ var itemBook = ClickstreamItem(
analytics.record(
name: "view_item",
attributes: {
"currency": "USD",
Attr.VALUE: 99,
Attr.CURRENCY: "USD"
"event_category": "recommended"
},
items: [itemBook]
Expand Down
3 changes: 3 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ include: package:flutter_lints/flutter.yaml

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
linter:
rules:
// ignore: non_constant_identifier_name
15 changes: 12 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,15 @@ class _MyAppState extends State<MyApp> {
isCompressEvents: false,
sessionTimeoutDuration: 30000,
globalAttributes: {
"channel": "Samsung",
Attr.TRAFFIC_SOURCE_SOURCE: "amazon",
Attr.TRAFFIC_SOURCE_MEDIUM: "cpc",
Attr.TRAFFIC_SOURCE_CAMPAIGN: "summer_promotion",
Attr.TRAFFIC_SOURCE_CAMPAIGN_ID: "summer_promotion_01",
Attr.TRAFFIC_SOURCE_TERM: "running_shoes",
Attr.TRAFFIC_SOURCE_CONTENT: "banner_ad_1",
Attr.TRAFFIC_SOURCE_CLID: "amazon_ad_123",
Attr.TRAFFIC_SOURCE_CLID_PLATFORM: "amazon_ads",
Attr.APP_INSTALL_CHANNEL: "amazon_store",
"Class": 5,
"isTrue": true,
"Score": 24.32
Expand Down Expand Up @@ -180,7 +188,7 @@ class _MyAppState extends State<MyApp> {
title: const Text('addGlobalAttributes'),
onTap: () async {
analytics.addGlobalAttributes({
"channel": "Samsung",
Attr.APP_INSTALL_CHANNEL: "amazon_store",
"Class": 5,
"isTrue": true,
"Score": 24.32
Expand All @@ -193,7 +201,8 @@ class _MyAppState extends State<MyApp> {
leading: const Icon(Icons.delete_rounded),
title: const Text('deleteGlobalAttributes'),
onTap: () async {
analytics.deleteGlobalAttributes(["Score", "channel"]);
analytics.deleteGlobalAttributes(
["Score", Attr.APP_INSTALL_CHANNEL]);
log("deleteGlobalAttributes Score and channel");
},
minLeadingWidth: 0,
Expand Down
16 changes: 16 additions & 0 deletions lib/clickstream_analytics.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,19 @@ class ClickstreamAnalytics {
return ClickstreamInterface.instance.enable();
}
}

class Attr {
static const String TRAFFIC_SOURCE_SOURCE = '_traffic_source_source';
static const String TRAFFIC_SOURCE_MEDIUM = '_traffic_source_medium';
static const String TRAFFIC_SOURCE_CAMPAIGN = '_traffic_source_campaign';
static const String TRAFFIC_SOURCE_CAMPAIGN_ID =
'_traffic_source_campaign_id';
static const String TRAFFIC_SOURCE_TERM = '_traffic_source_term';
static const String TRAFFIC_SOURCE_CONTENT = '_traffic_source_content';
static const String TRAFFIC_SOURCE_CLID = '_traffic_source_clid';
static const String TRAFFIC_SOURCE_CLID_PLATFORM =
'_traffic_source_clid_platform';
static const String APP_INSTALL_CHANNEL = '_app_install_channel';
static const String VALUE = '_value';
static const String CURRENCY = '_currency';
}
85 changes: 85 additions & 0 deletions test/clickstream_flutter_platform_interface_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'package:flutter_test/flutter_test.dart';

import 'mock_method_channel.dart';

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

MockMethodChannel platform = MockMethodChannel();

test('init method for UnimplementedError', () async {
Map<String, Object?> initConfig = {
'appId': 'testApp',
'endpoint': "",
};
expect(() async {
await platform.init(initConfig);
}, throwsA(isInstanceOf<UnimplementedError>()));
});

test('record method for UnimplementedError', () async {
Map<String, Object?> attributes = {
"category": "shoes",
"currency": "CNY",
"value": 279.9
};
expect(platform.record(attributes),
throwsA(isInstanceOf<UnimplementedError>()));
});

test('setUserId method for UnimplementedError', () async {
Map<String, Object?> attributes = {
"userId": "1234",
};
expect(platform.setUserId(attributes),
throwsA(isInstanceOf<UnimplementedError>()));
});

test('setUserAttributes method for UnimplementedError', () async {
Map<String, Object?> attributes = {"_user_age": 21, "_user_name": "carl"};
expect(platform.setUserAttributes(attributes),
throwsA(isInstanceOf<UnimplementedError>()));
});

test('addGlobalAttributes method for UnimplementedError', () async {
Map<String, Object?> attributes = {
"channel": "Play Store",
"level": 5.1,
"class": 6
};
expect(platform.addGlobalAttributes(attributes),
throwsA(isInstanceOf<UnimplementedError>()));
});

test('deleteGlobalAttributes method for UnimplementedError', () async {
Map<String, Object?> attributes = {
"attributes": ["attr1", "attr2"],
};
expect(platform.deleteGlobalAttributes(attributes),
throwsA(isInstanceOf<UnimplementedError>()));
});

test('updateConfigure method for UnimplementedError', () async {
Map<String, Object?> attributes = {
"appId": "newAppId",
"endpoint": "https://example.com/collect",
};
expect(platform.updateConfigure(attributes),
throwsA(isInstanceOf<UnimplementedError>()));
});

test('flushEvents method for UnimplementedError', () async {
expect(platform.flushEvents(), throwsA(isInstanceOf<UnimplementedError>()));
});

test('disable method for UnimplementedError', () async {
expect(platform.disable(), throwsA(isInstanceOf<UnimplementedError>()));
});

test('enable method for UnimplementedError', () async {
expect(platform.enable(), throwsA(isInstanceOf<UnimplementedError>()));
});
}
39 changes: 32 additions & 7 deletions test/clickstream_flutter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,28 @@ void main() {
appId: 'testApp',
endpoint: "https://example.com/collect",
globalAttributes: {
"channel": "Samsung",
Attr.APP_INSTALL_CHANNEL: "amazon_store",
"Class": 5,
"isTrue": true,
"Score": 24.32
});
expect(result, true);
});

test('init SDK with traffic source using global attributes', () async {
var result = await analytics.init(
appId: 'testApp',
endpoint: "https://example.com/collect",
globalAttributes: {
Attr.TRAFFIC_SOURCE_SOURCE: "amazon",
Attr.TRAFFIC_SOURCE_MEDIUM: "cpc",
Attr.TRAFFIC_SOURCE_CAMPAIGN: "summer_promotion",
Attr.TRAFFIC_SOURCE_CAMPAIGN_ID: "summer_promotion_01",
Attr.TRAFFIC_SOURCE_TERM: "running_shoes",
Attr.TRAFFIC_SOURCE_CONTENT: "banner_ad_1",
Attr.TRAFFIC_SOURCE_CLID: "amazon_ad_123",
Attr.TRAFFIC_SOURCE_CLID_PLATFORM: "amazon_ads",
Attr.APP_INSTALL_CHANNEL: "amazon_store",
"Class": 5,
"isTrue": true,
"Score": 24.32
Expand All @@ -93,7 +114,7 @@ void main() {
isTrackUserEngagementEvents: false,
isTrackAppExceptionEvents: true,
globalAttributes: {
"channel": "Samsung",
Attr.APP_INSTALL_CHANNEL: "amazon_store",
});
expect(result, true);
});
Expand Down Expand Up @@ -125,10 +146,14 @@ void main() {
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]);
var result = analytics.record(name: "cart_view", attributes: {
Attr.TRAFFIC_SOURCE_CAMPAIGN: "Summer promotion",
Attr.VALUE: 164,
Attr.CURRENCY: "USD"
}, items: [
itemBook,
itemShoes
]);
expect(result, isNotNull);
});

Expand All @@ -155,7 +180,7 @@ void main() {

test('setGlobalAttributes', () async {
var result = analytics.addGlobalAttributes(
{"channel": "Play Store", "level": 5.1, "class": 6});
{Attr.APP_INSTALL_CHANNEL: "amazon_store", "level": 5.1, "class": 6});
var result1 = analytics.addGlobalAttributes({});
expect(result, isNotNull);
expect(result1, isNotNull);
Expand Down
63 changes: 63 additions & 0 deletions test/mock_method_channel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import 'package:clickstream_analytics/clickstream_analytics_platform_interface.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';

/// An implementation of [ClickstreamFlutterPlatform] that uses method channels.
class MockMethodChannel extends ClickstreamInterface {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodChannel = const MethodChannel('clickstream_flutter');

@override
Future<bool> init(Map<String, Object?> configure) async {
return super.init(configure);
}

@override
Future<void> record(Map<String, Object?> attributes) async {
return super.record(attributes);
}

@override
Future<void> setUserId(Map<String, Object?> userId) async {
return super.setUserId(userId);
}

@override
Future<void> setUserAttributes(Map<String, Object?> attributes) async {
return super.setUserAttributes(attributes);
}

@override
Future<void> addGlobalAttributes(Map<String, Object?> attributes) async {
return super.addGlobalAttributes(attributes);
}

@override
Future<void> deleteGlobalAttributes(Map<String, Object?> attributes) async {
return super.deleteGlobalAttributes(attributes);
}

@override
Future<void> updateConfigure(Map<String, Object?> configure) async {
return super.updateConfigure(configure);
}

@override
Future<void> flushEvents() async {
return super.flushEvents();
}

@override
Future<void> disable() async {
return super.disable();
}

@override
Future<void> enable() async {
return super.enable();
}
}
Loading