Skip to content

Commit f333f71

Browse files
committed
Merge branch 'staging' of github.com:Countly/countly-sdk-flutter-bridge into staging-np
2 parents 875f808 + a421556 commit f333f71

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1384
-99
lines changed

CHANGELOG.md

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
1-
## xx.x.x
2-
* Fixed an issue where the 'reportFeedbackWidgetManually' function would await indefinitely on iOS
3-
* Resolved an issue where nonfatal exceptions were treated as fatal and vice versa
4-
5-
* Underlying Android SDK version is 24.1.1
6-
* Underlying iOS SDK version is 24.1.0
7-
8-
## xx.x.x-np
9-
* Fixed an issue where the 'reportFeedbackWidgetManually' function would await indefinitely on iOS
10-
* Resolved an issue where nonfatal exceptions were treated as fatal and vice versa
11-
12-
* Underlying Android SDK version is 24.1.1
13-
* Underlying iOS SDK version is 24.1.0
1+
## 24.4.0
2+
* Mitigated an issue where the 'reportFeedbackWidgetManually' function would await indefinitely on iOS
3+
* Mitigated an issue where nonfatal exceptions were treated as fatal and vice versa
4+
* Mitigated an issue that caused session duration inconsistencies
5+
6+
* Added six new configuration options under the 'sdkInternalLimits' interface of 'CountlyConfig':
7+
* 'setMaxKeyLength' for limiting the maximum size of all user provided string keys
8+
* 'setMaxValueSize' for limiting the size of all values in user provided segmentation key-value pairs
9+
* 'setMaxSegmentationValues' for limiting the max amount of user provided segmentation key-value pair count in one event
10+
* 'setMaxBreadcrumbCount' for limiting the max amount of breadcrumbs that can be recorded before the oldest one is deleted
11+
* 'setMaxStackTraceLinesPerThread' for limiting the max amount of stack trace lines to be recorded per thread
12+
* 'setMaxStackTraceLineLength' for limiting the max characters allowed per stack trace lines
13+
14+
* Updated underlying Android SDK version to 24.4.0
15+
* Updated underlying iOS SDK version to 24.4.0
16+
17+
## 24.4.0-np
18+
* Mitigated an issue where the 'reportFeedbackWidgetManually' function would await indefinitely on iOS
19+
* Mitigated an issue where nonfatal exceptions were treated as fatal and vice versa
20+
* Mitigated an issue that caused session duration inconsistencies
21+
22+
* Added six new configuration options under the 'sdkInternalLimits' interface of 'CountlyConfig':
23+
* 'setMaxKeyLength' for limiting the maximum size of all user provided string keys
24+
* 'setMaxValueSize' for limiting the size of all values in user provided segmentation key-value pairs
25+
* 'setMaxSegmentationValues' for limiting the max amount of user provided segmentation key-value pair count in one event
26+
* 'setMaxBreadcrumbCount' for limiting the max amount of breadcrumbs that can be recorded before the oldest one is deleted
27+
* 'setMaxStackTraceLinesPerThread' for limiting the max amount of stack trace lines to be recorded per thread
28+
* 'setMaxStackTraceLineLength' for limiting the max characters allowed per stack trace lines
29+
30+
* Updated underlying Android SDK version to 24.4.0
31+
* Updated underlying iOS SDK version to 24.4.0
1432

1533
## 24.1.1
1634
* Added a new metric for detecting whether or not a device has a hinge for Android

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ android {
3434
}
3535

3636
dependencies {
37-
implementation 'ly.count.android:sdk:24.1.1'
37+
implementation 'ly.count.android:sdk:24.4.0'
3838
}

android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
*/
6161
public class CountlyFlutterPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware, DefaultLifecycleObserver {
6262
private static final String TAG = "CountlyFlutterPlugin";
63-
private final String COUNTLY_FLUTTER_SDK_VERSION_STRING = "24.1.1";
63+
private final String COUNTLY_FLUTTER_SDK_VERSION_STRING = "24.4.0";
6464
private final String COUNTLY_FLUTTER_SDK_NAME = "dart-flutterb-android";
6565
private final String COUNTLY_FLUTTER_SDK_NAME_NO_PUSH = "dart-flutterbnp-android";
6666

@@ -1170,6 +1170,7 @@ public void onFinished(JSONObject retrievedWidgetData, String error) {
11701170

11711171
String widgetId = widgetInfo.getString(0);
11721172

1173+
// TODO: for testing purposes we might want to bypass this check as it prevents us from using reportFeedbackWidgetManually directly
11731174
CountlyFeedbackWidget feedbackWidget = getFeedbackWidget(widgetId);
11741175
if (feedbackWidget == null) {
11751176
String errorMessage = "[reportFeedbackWidgetManually], No feedbackWidget is found against widget id : '" + widgetId + "' , always call 'getFeedbackWidgets' to get updated list of feedback widgets.";
@@ -1553,6 +1554,26 @@ private void populateConfig(JSONObject _config) throws JSONException {
15531554
this.config.setRecordAppStartTime(_config.getBoolean("recordAppStartTime"));
15541555
}
15551556
// APM END --------------------------------------------
1557+
// Internal Limits ------------------------------------
1558+
if (_config.has("maxKeyLength")) {
1559+
this.config.sdkInternalLimits.setMaxKeyLength(_config.getInt("maxKeyLength"));
1560+
}
1561+
if (_config.has("maxValueSize")) {
1562+
this.config.sdkInternalLimits.setMaxValueSize(_config.getInt("maxValueSize"));
1563+
}
1564+
if (_config.has("maxSegmentationValues")) {
1565+
this.config.sdkInternalLimits.setMaxSegmentationValues(_config.getInt("maxSegmentationValues"));
1566+
}
1567+
if (_config.has("maxBreadcrumbCount")) {
1568+
this.config.sdkInternalLimits.setMaxBreadcrumbCount(_config.getInt("maxBreadcrumbCount"));
1569+
}
1570+
if (_config.has("maxStackTraceLineLength")) {
1571+
this.config.sdkInternalLimits.setMaxStackTraceLineLength(_config.getInt("maxStackTraceLineLength"));
1572+
}
1573+
if (_config.has("maxStackTraceLinesPerThread")) {
1574+
this.config.sdkInternalLimits.setMaxStackTraceLinesPerThread(_config.getInt("maxStackTraceLinesPerThread"));
1575+
}
1576+
// Internal Limits END --------------------------------
15561577

15571578
if (_config.has("enableUnhandledCrashReporting") && _config.getBoolean("enableUnhandledCrashReporting")) {
15581579
this.config.enableCrashReporting();

example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ dependencies {
5959
testImplementation 'junit:junit:4.12'
6060
androidTestImplementation 'com.android.support.test:runner:1.0.2'
6161
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
62-
implementation 'ly.count.android:sdk:24.1.1'
62+
implementation 'ly.count.android:sdk:24.4.0'
6363
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
6464
}
6565
apply plugin: 'com.google.gms.google-services' // Google Play services Gradle plugin
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import 'dart:convert';
2+
import 'dart:io';
3+
4+
import 'package:countly_flutter/countly_flutter.dart';
5+
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:integration_test/integration_test.dart';
7+
import '../utils.dart';
8+
9+
/// Check if setting setMaxKeyLength to 1 truncates the keys (except internal keys)
10+
/// Tested keys are:
11+
/// - Event names and Event segmentation keys
12+
/// - View names and View segmentation keys
13+
/// - Custom APM trace keys and their segmentation keys
14+
/// - Custom Crash segmentation keys
15+
/// - Global View segmentation keys
16+
/// - Custom User Property names and their modifications (with mul, push, pull, set, increment, etc)
17+
18+
const int MAX_KEY_LENGTH = 1;
19+
void main() {
20+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
21+
testWidgets('Init SDK with setMaxKeyLength', (WidgetTester tester) async {
22+
// Initialize the SDK
23+
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY).setLoggingEnabled(true);
24+
config.sdkInternalLimits.setMaxKeyLength(MAX_KEY_LENGTH);
25+
await Countly.initWithConfig(config);
26+
27+
// Create truncable events
28+
await createTruncableEvents();
29+
30+
// Get request and event queues from native side
31+
List<String> requestList = await getRequestQueue();
32+
List<String> eventList = await getEventQueue();
33+
34+
// Some logs for debugging
35+
print('RQ: $requestList');
36+
print('EQ: $eventList');
37+
print('RQ length: ${requestList.length}');
38+
print('EQ length: ${eventList.length}');
39+
40+
expect(requestList.length, Platform.isIOS ? 8 : 7); // user properties and custom user properties are separately sent in iOS
41+
expect(eventList.length, 0);
42+
43+
// TODO: refactor this part (mote to utils and make it more generic)
44+
// 0: begin session
45+
// 1: custom APM with segmentation
46+
// 2: network Trace
47+
// 3: custom fatality crash with segmentation
48+
// 4: custom mortal crash with segmentation
49+
// 5: custom view and events with segmentation
50+
// 6: custom user properties
51+
var a = 0;
52+
for (var element in requestList) {
53+
Map<String, List<String>> queryParams = Uri.parse('?' + element).queryParametersAll;
54+
testCommonRequestParams(queryParams); // checks general params
55+
if (a == 1) {
56+
Map<String, dynamic> apm = json.decode(queryParams['apm']![0]);
57+
expect(apm['name'], 'Trace'.substring(0, MAX_KEY_LENGTH));
58+
} else if (a == 2) {
59+
Map<String, dynamic> apm = json.decode(queryParams['apm']![0]);
60+
expect(apm['name'], 'Network Trace'.substring(0, MAX_KEY_LENGTH));
61+
} else if (a == 3 || a == 4) {
62+
Map<String, dynamic> crash = json.decode(queryParams['crash']![0]);
63+
expect(crash['_custom']['Cats'.substring(0, MAX_KEY_LENGTH)], '12345');
64+
expect(crash['_custom']['Moose'.substring(0, MAX_KEY_LENGTH)], 'Deer');
65+
expect(crash['_custom']['Moons'.substring(0, MAX_KEY_LENGTH)], '9.9866');
66+
} else if (a == 5) {
67+
// 0) Custom Event
68+
Map<String, dynamic> event = json.decode(queryParams['events']![0]);
69+
expect(event['key'], 'Event With Sum And Segment'.substring(0, MAX_KEY_LENGTH));
70+
expect(event['segmentation']['Country'.substring(0, MAX_KEY_LENGTH)], 'Turkey');
71+
expect(event['segmentation']['Age'.substring(0, MAX_KEY_LENGTH)], '28884');
72+
// 1) View Start (legacy)
73+
Map<String, dynamic> view = json.decode(queryParams['events']![1]);
74+
expect(view['key'], '[CLY]_view');
75+
expect(view['segmentation']['Cats'.substring(0, MAX_KEY_LENGTH)], '12345');
76+
expect(view['segmentation']['Moons'.substring(0, MAX_KEY_LENGTH)], '9.9866');
77+
expect(view['segmentation']['segment'], Platform.isIOS ? 'iOS' : 'Android');
78+
expect(view['segmentation']['start'], '1');
79+
expect(view['segmentation']['name'], 'HomePage'.substring(0, MAX_KEY_LENGTH));
80+
expect(view['segmentation']['Camel'.substring(0, MAX_KEY_LENGTH)], 666);
81+
expect(view['segmentation']['visit'], '1');
82+
expect(view['segmentation']['NotCamel'], 'Deerz');
83+
expect(view['segmentation']['Moose'], 'Deer');
84+
// 2) View End (legacy)
85+
view = json.decode(queryParams['events']![2]);
86+
expect(view['key'], '[CLY]_view');
87+
expect(view['segmentation']['segment'], Platform.isIOS ? 'iOS' : 'Android');
88+
expect(view['segmentation']['name'], 'HomePage'.substring(0, MAX_KEY_LENGTH));
89+
expect(view['segmentation']['Camel'.substring(0, MAX_KEY_LENGTH)], 666);
90+
expect(view['segmentation']['NotCamel'], 'Deerz');
91+
// 3) View Start (AutoStopped)
92+
view = json.decode(queryParams['events']![3]);
93+
expect(view['key'], '[CLY]_view');
94+
expect(view['segmentation']['Cats'.substring(0, MAX_KEY_LENGTH)], 12345);
95+
expect(view['segmentation']['Moons'.substring(0, MAX_KEY_LENGTH)], 9.9866);
96+
expect(view['segmentation']['segment'], Platform.isIOS ? 'iOS' : 'Android');
97+
expect(view['segmentation']['name'], 'hawk'.substring(0, MAX_KEY_LENGTH));
98+
expect(view['segmentation']['Camel'.substring(0, MAX_KEY_LENGTH)], 666);
99+
expect(view['segmentation']['visit'], '1');
100+
expect(view['segmentation']['NotCamel'], 'Deerz');
101+
expect(view['segmentation']['Moose'], 'Deer');
102+
// 4) View End (AutoStopped)
103+
view = json.decode(queryParams['events']![4]);
104+
expect(view['key'], '[CLY]_view');
105+
expect(view['segmentation']['segment'], Platform.isIOS ? 'iOS' : 'Android');
106+
expect(view['segmentation']['name'], 'hawk'.substring(0, MAX_KEY_LENGTH));
107+
expect(view['segmentation']['Camel'.substring(0, MAX_KEY_LENGTH)], 666);
108+
expect(view['segmentation']['NotCamel'], 'Deerz');
109+
} else if (a == 6) {
110+
Map<String, dynamic> userDetails = json.decode(queryParams['user_details']![0]);
111+
checkUnchangingUserPropeties(userDetails);
112+
expect(userDetails['custom']['special_value'.substring(0, MAX_KEY_LENGTH)], 'something special');
113+
expect(userDetails['custom']['not_special_value'.substring(0, MAX_KEY_LENGTH)], 'something special cooking');
114+
115+
// TODO: this should be in a==7 for ios
116+
expect(userDetails['custom']['setProperty'.substring(0, MAX_KEY_LENGTH)], 'My Property');
117+
}
118+
119+
// some logs for debugging
120+
print('RQ.$a: $queryParams');
121+
print('========================');
122+
a++;
123+
}
124+
});
125+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import 'dart:convert';
2+
import 'dart:io';
3+
4+
import 'package:countly_flutter/countly_flutter.dart';
5+
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:integration_test/integration_test.dart';
7+
import '../utils.dart';
8+
9+
/// Check if setting setMaxValueSize to 1 truncates the values
10+
/// Tested values are:
11+
/// - Event and View segmentation values
12+
/// - Custom Crash segmentation values
13+
/// - Global View and Crash segmentation values
14+
/// - Custom User Property values and their modifications (with mul, push, pull, set, increment, etc)
15+
/// - User Profile named key (username, email, etc) values (except the "picture" field, which has a limit of 4096 chars)
16+
/// - Breadcrumb value (text)
17+
/// - TODO: Manual Feedback and Rating Widgets reporting fields
18+
19+
const int MAX_VALUE_SIZE = 1;
20+
void main() {
21+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
22+
testWidgets('Init SDK with setMaxValueSize', (WidgetTester tester) async {
23+
// Initialize the SDK
24+
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY).setLoggingEnabled(true);
25+
config.sdkInternalLimits.setMaxValueSize(MAX_VALUE_SIZE);
26+
await Countly.initWithConfig(config);
27+
28+
// Create truncable events
29+
await createTruncableEvents();
30+
31+
// Get request and event queues from native side
32+
List<String> requestList = await getRequestQueue();
33+
List<String> eventList = await getEventQueue();
34+
35+
// Some logs for debugging
36+
print('RQ: $requestList');
37+
print('EQ: $eventList');
38+
print('RQ length: ${requestList.length}');
39+
print('EQ length: ${eventList.length}');
40+
41+
expect(requestList.length, Platform.isIOS ? 8 : 7); // user properties and custom user properties are separately sent in iOS
42+
expect(eventList.length, 0);
43+
44+
// TODO: refactor this part (move to utils and make it more generic)
45+
// 0: begin session
46+
// 1: custom APM with segmentation
47+
// 2: network Trace
48+
// 3: custom fatality crash with segmentation
49+
// 4: custom mortal crash with segmentation
50+
// 5: custom view and events with segmentation
51+
// 6: custom user properties
52+
var a = 0;
53+
for (var element in requestList) {
54+
Map<String, List<String>> queryParams = Uri.parse('?' + element).queryParametersAll;
55+
testCommonRequestParams(queryParams); // checks general params
56+
if (a == 1) {
57+
Map<String, dynamic> apm = json.decode(queryParams['apm']![0]);
58+
expect(apm['name'], 'Trace');
59+
expect(apm['apm_metrics']['C44CCC'], 1337);
60+
expect(apm['apm_metrics']['ABCDEF'], 1233);
61+
} else if (a == 2) {
62+
Map<String, dynamic> apm = json.decode(queryParams['apm']![0]);
63+
expect(apm['name'], 'Network Trace');
64+
} else if (a == 3 || a == 4) {
65+
Map<String, dynamic> crash = json.decode(queryParams['crash']![0]);
66+
expect(crash['_custom']['Cats'], '12345'.substring(0, MAX_VALUE_SIZE));
67+
expect(crash['_custom']['Moose'], 'Deer'.substring(0, MAX_VALUE_SIZE));
68+
expect(crash['_custom']['Moons'], '9.9866'.substring(0, MAX_VALUE_SIZE));
69+
expect(crash['_logs'], 'User Performed Step A'.substring(0, MAX_VALUE_SIZE) + '\n');
70+
} else if (a == 5) {
71+
// 0) Custom Event
72+
List<dynamic> eventList = json.decode(queryParams['events']![0]);
73+
var event = eventList[0];
74+
expect(event['key'], 'Event With Sum And Segment');
75+
expect(event['segmentation']['Country'], 'Turkey'.substring(0, MAX_VALUE_SIZE));
76+
expect(event['segmentation']['Age'], '28884'.substring(0, MAX_VALUE_SIZE));
77+
// 1) View Start (legacy)
78+
var view = eventList[1];
79+
expect(view['key'], '[CLY]_view');
80+
expect(view['segmentation']['Cats'], '12345'.substring(0, MAX_VALUE_SIZE));
81+
expect(view['segmentation']['Moons'], '9.9866'.substring(0, MAX_VALUE_SIZE));
82+
expect(view['segmentation']['segment'], Platform.isIOS ? 'iOS' : 'Android');
83+
expect(view['segmentation']['start'], '1');
84+
expect(view['segmentation']['name'], 'HomePage');
85+
expect(view['segmentation']['Camel'], 666);
86+
expect(view['segmentation']['visit'], '1');
87+
expect(view['segmentation']['NotCamel'], 'Deerz'.substring(0, MAX_VALUE_SIZE));
88+
expect(view['segmentation']['Moose'], 'Deer'.substring(0, MAX_VALUE_SIZE));
89+
// 2) View End (legacy)
90+
view = eventList[2];
91+
expect(view['key'], '[CLY]_view');
92+
expect(view['segmentation']['segment'], Platform.isIOS ? 'iOS' : 'Android');
93+
expect(view['segmentation']['name'], 'HomePage');
94+
expect(view['segmentation']['Camel'], 666);
95+
expect(view['segmentation']['NotCamel'], 'Deerz'.substring(0, MAX_VALUE_SIZE));
96+
// 3) View Start (AutoStopped)
97+
view = eventList[3];
98+
expect(view['key'], '[CLY]_view');
99+
expect(view['segmentation']['Cats'], 12345);
100+
expect(view['segmentation']['Moons'], 9.9866);
101+
expect(view['segmentation']['segment'], Platform.isIOS ? 'iOS' : 'Android');
102+
expect(view['segmentation']['name'], 'hawk');
103+
expect(view['segmentation']['Camel'], 666);
104+
expect(view['segmentation']['visit'], '1');
105+
expect(view['segmentation']['NotCamel'], 'Deerz'.substring(0, MAX_VALUE_SIZE));
106+
expect(view['segmentation']['Moose'], 'Deer'.substring(0, MAX_VALUE_SIZE));
107+
// 4) View End (AutoStopped)
108+
view = eventList[4];
109+
expect(view['key'], '[CLY]_view');
110+
expect(view['segmentation']['segment'], Platform.isIOS ? 'iOS' : 'Android');
111+
expect(view['segmentation']['name'], 'hawk');
112+
expect(view['segmentation']['Camel'], 666);
113+
expect(view['segmentation']['NotCamel'], 'Deerz'.substring(0, MAX_VALUE_SIZE));
114+
} else if (a == 6) {
115+
Map<String, dynamic> userDetails = json.decode(queryParams['user_details']![0]);
116+
checkUnchangingUserPropeties(userDetails);
117+
expect(userDetails['custom']['special_value'], 'something special'.substring(0, MAX_VALUE_SIZE));
118+
expect(userDetails['custom']['not_special_value'], 'something special cooking'.substring(0, MAX_VALUE_SIZE));
119+
120+
// TODO: this should be in a==7 for ios
121+
expect(userDetails['custom']['setProperty'], 'My Property'.substring(0, MAX_VALUE_SIZE));
122+
}
123+
124+
// some logs for debugging
125+
print('RQ.$a: $queryParams');
126+
print('========================');
127+
a++;
128+
}
129+
});
130+
}

0 commit comments

Comments
 (0)