Skip to content

Commit 8feee45

Browse files
chore(notifications): add push userAgent headers to analytics calls (#2861)
* chore: add userAgent headers to anayltics calls * chore: remove print and add update endpoint after settign user * chore: updated mocks and added analyzer expection rule to ignore version.dart * chore: pass testDispatcher to request permissions tes android native * chore: update test name and useragent public * chore: remove vesion dependency becuase user agent string does not need it * chore: add comment on why setUser has no userAgent added * chore: review comments
1 parent 9aa9dca commit 8feee45

File tree

5 files changed

+91
-24
lines changed

5 files changed

+91
-24
lines changed

packages/notifications/push/amplify_push_notifications/android/src/main/kotlin/com/amazonaws/amplify/amplify_push_notifications/AmplifyPushNotificationsPlugin.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,11 @@ private const val TAG = "AmplifyPushNotificationsPlugin"
2929

3030
/** AmplifyPushNotificationsPlugin */
3131
@InternalAmplifyApi
32-
open class AmplifyPushNotificationsPlugin : FlutterPlugin, ActivityAware,
32+
class AmplifyPushNotificationsPlugin(
33+
dispatcher: CoroutineDispatcher = Dispatchers.Main
34+
) : FlutterPlugin, ActivityAware,
3335
PluginRegistry.NewIntentListener, PushNotificationsHostApi {
3436
companion object {
35-
/**
36-
* The scope in which to spawn tasks which should not be awaited from the main thread.
37-
*/
38-
val scope =
39-
CoroutineScope(Dispatchers.IO) + CoroutineName("amplify_flutter.PushNotifications")
40-
4137
/**
4238
* Flutter API interface.
4339
*/
@@ -50,6 +46,11 @@ open class AmplifyPushNotificationsPlugin : FlutterPlugin, ActivityAware,
5046
var launchNotification: MutableMap<Any, Any?>? = null
5147
}
5248

49+
/**
50+
* The scope in which to spawn tasks which should not be awaited from the main thread.
51+
*/
52+
private val scope =
53+
CoroutineScope(dispatcher) + CoroutineName("amplify_flutter.PushNotifications")
5354
/**
5455
* The user's main activity.
5556
*/

packages/notifications/push/amplify_push_notifications/android/src/test/kotlin/com/amazonaws/amplify/amplify_push_notifications/AmplifyPushNotificationsPluginTest.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import androidx.lifecycle.Lifecycle
99
import com.amplifyframework.annotations.InternalAmplifyApi
1010
import com.amplifyframework.notifications.pushnotifications.NotificationContentProvider
1111
import com.amplifyframework.notifications.pushnotifications.NotificationPayload
12-
import com.amplifyframework.pushnotifications.pinpoint.permissions.PermissionRequestResult
1312
import com.amplifyframework.pushnotifications.pinpoint.permissions.PermissionRequestResult.*
1413
import com.amplifyframework.pushnotifications.pinpoint.permissions.PushNotificationPermission
1514
import com.google.android.gms.tasks.Task
@@ -22,6 +21,7 @@ import io.flutter.plugin.common.EventChannel
2221
import io.flutter.plugin.common.EventChannel.EventSink
2322
import io.mockk.*
2423
import kotlinx.coroutines.ExperimentalCoroutinesApi
24+
import kotlinx.coroutines.test.StandardTestDispatcher
2525
import kotlinx.coroutines.test.advanceUntilIdle
2626
import kotlinx.coroutines.test.runTest
2727
import org.junit.After
@@ -49,13 +49,13 @@ class AmplifyPushNotificationsPluginTest {
4949
private val mockActivityBinding = mockk<ActivityPluginBinding>(relaxed = true)
5050
private val mockResult =
5151
mockk<PushNotificationsHostApiBindings.Result<PushNotificationsHostApiBindings.GetPermissionStatusResult>>()
52+
private val mockFlutterBinding = mockk<FlutterPluginBinding>()
5253

5354
@Before
5455
fun setUp() {
5556
context = mockk()
5657
mockEventSink = mockk()
5758
val mockedBinaryMessenger = mockk<BinaryMessenger>()
58-
val mockFlutterBinding = mockk<FlutterPluginBinding>()
5959
val mockLifeCycle = mockk<Lifecycle>()
6060
val mockActivity = mockk<Activity>()
6161

@@ -237,10 +237,13 @@ class AmplifyPushNotificationsPluginTest {
237237

238238
@Test
239239
fun `requests permission`() = runTest {
240-
coEvery { anyConstructed<PushNotificationPermission>().requestPermission() } returns PermissionRequestResult.Granted
240+
coEvery { anyConstructed<PushNotificationPermission>().requestPermission() } returns Granted
241241
val mockResult = mockk<PushNotificationsHostApiBindings.Result<Boolean>>()
242242
every { mockResult.success(any()) } returns mockk()
243-
amplifyPushNotificationsPlugin.requestPermissions(mockk(), mockResult)
243+
val dispatcher = StandardTestDispatcher(testScheduler)
244+
val pluginWithDispatcher = AmplifyPushNotificationsPlugin(dispatcher)
245+
pluginWithDispatcher.onAttachedToEngine(mockFlutterBinding)
246+
pluginWithDispatcher.requestPermissions(mockk(), mockResult)
244247
advanceUntilIdle()
245248
verify { mockResult.success(true) }
246249
}
@@ -260,7 +263,10 @@ class AmplifyPushNotificationsPluginTest {
260263
)
261264
val mockResult = mockk<PushNotificationsHostApiBindings.Result<Boolean>>()
262265
every { mockResult.success(any()) } returns mockk()
263-
amplifyPushNotificationsPlugin.requestPermissions(mockk(), mockResult)
266+
val dispatcher = StandardTestDispatcher(testScheduler)
267+
val pluginWithDispatcher = AmplifyPushNotificationsPlugin(dispatcher)
268+
pluginWithDispatcher.onAttachedToEngine(mockFlutterBinding)
269+
pluginWithDispatcher.requestPermissions(mockk(), mockResult)
264270
advanceUntilIdle()
265271
verify {
266272
mockResult.success(false)

packages/notifications/push/amplify_push_notifications_pinpoint/lib/src/pinpoint_provider.dart

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ class PinpointProvider implements ServiceProviderClient {
3838
'pinpoint.campaign.treatment_id';
3939
bool _isInitialized = false;
4040

41+
/// UserAgent string for Push Notifications made public for testing
42+
@visibleForTesting
43+
static const userAgent = 'pushnotifications';
44+
45+
static R _withUserAgent<R>(R Function() fn) {
46+
final globalUserAgent =
47+
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
48+
Amplify.dependencies.getOrCreate<AmplifyUserAgent>();
49+
return globalUserAgent.runWith(
50+
updates: (ua) => ua.addComponent(userAgent),
51+
fn,
52+
);
53+
}
54+
4155
@override
4256
Future<void> init({
4357
required NotificationsPinpointPluginConfig config,
@@ -101,10 +115,15 @@ class PinpointProvider implements ServiceProviderClient {
101115
'Make sure Pinpoint service provider client is initialized before using this method.',
102116
);
103117
}
118+
119+
// setUser does not have any underlying network calls, hence not running it _withUserAgent
104120
await _analyticsClient.endpointClient.setUser(
105121
userId,
106122
userProfile,
107123
);
124+
await _withUserAgent(
125+
() async => _analyticsClient.endpointClient.updateEndpoint(),
126+
);
108127
} on Exception catch (e) {
109128
throw PushNotificationException(
110129
'Unable to identify user.',
@@ -135,9 +154,12 @@ class PinpointProvider implements ServiceProviderClient {
135154
}
136155

137156
final eventInfo = constructEventInfo(notification: notification);
138-
await _analyticsClient.eventClient.recordEvent(
139-
eventType: '${eventInfo.source}.${eventType.name}',
140-
properties: eventInfo.properties,
157+
158+
await _withUserAgent(
159+
() async => _analyticsClient.eventClient.recordEvent(
160+
eventType: '${eventInfo.source}.${eventType.name}',
161+
properties: eventInfo.properties,
162+
),
141163
);
142164
} on Exception catch (e) {
143165
_logger.error('Unable to record event: $e');
@@ -157,7 +179,9 @@ class PinpointProvider implements ServiceProviderClient {
157179
final channelType = _getChannelType();
158180
_analyticsClient.endpointClient.channelType = channelType;
159181
_analyticsClient.endpointClient.optOut = 'NONE';
160-
await _analyticsClient.endpointClient.updateEndpoint();
182+
await _withUserAgent(
183+
() async => _analyticsClient.endpointClient.updateEndpoint(),
184+
);
161185
} on AWSHttpException catch (e) {
162186
_logger.error('Network problem when registering device: ', e);
163187
}

packages/notifications/push/amplify_push_notifications_pinpoint/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ dev_dependencies:
2424
amplify_lints: ^2.0.0
2525
amplify_test:
2626
path: ../../../test/amplify_test
27-
build_runner: ^2.0.0
27+
build_runner: ^2.3.3
2828
built_value_generator: 8.4.4
2929
flutter_test:
3030
sdk: flutter

packages/notifications/push/amplify_push_notifications_pinpoint/test/pinpoint_provider_test.dart

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,45 @@ void main() {
7070
);
7171
});
7272

73+
test('identifyUser should throw exception if the underlying call throws',
74+
() async {
75+
when(mockAmplifyAuthProviderRepository.getAuthProvider(any))
76+
.thenReturn(awsIamAmplifyAuthProvider);
77+
when(
78+
mockAnalyticsClient.init(
79+
pinpointAppId: anyNamed('pinpointAppId'),
80+
region: anyNamed('region'),
81+
authProvider: anyNamed('authProvider'),
82+
),
83+
).thenAnswer((realInvocation) async {});
84+
85+
final mockEndpointClient = MockEndpointClient();
86+
87+
when(
88+
mockAnalyticsClient.endpointClient,
89+
).thenReturn(mockEndpointClient);
90+
91+
await pinpointProvider.init(
92+
config: notificationsPinpointConfig,
93+
authProviderRepo: mockAmplifyAuthProviderRepository,
94+
analyticsClient: mockAnalyticsClient,
95+
);
96+
when(mockEndpointClient.setUser(any, any)).thenThrow(Exception());
97+
expect(
98+
pinpointProvider.identifyUser(
99+
userId: 'userId',
100+
userProfile: MockUserProfile(),
101+
),
102+
throwsA(
103+
isA<PushNotificationException>().having(
104+
(e) => e.message,
105+
'Unable to identify user',
106+
contains('Unable to identify user.'),
107+
),
108+
),
109+
);
110+
});
111+
73112
test(
74113
'constructEventInfo should return journey data when there is journey details in the payload',
75114
() async {
@@ -135,13 +174,10 @@ void main() {
135174
mockAnalyticsClient.endpointClient,
136175
).thenReturn(mockEndpointClient);
137176

138-
expect(
139-
pinpointProvider.init(
140-
config: notificationsPinpointConfig,
141-
authProviderRepo: mockAmplifyAuthProviderRepository,
142-
analyticsClient: mockAnalyticsClient,
143-
),
144-
completes,
177+
await pinpointProvider.init(
178+
config: notificationsPinpointConfig,
179+
authProviderRepo: mockAmplifyAuthProviderRepository,
180+
analyticsClient: mockAnalyticsClient,
145181
);
146182

147183
expect(

0 commit comments

Comments
 (0)