Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit 13b663e

Browse files
Kadi Kramanf2prateek
Kadi Kraman
authored andcommitted
feat(integrations): allow setting integration options
* feat(integrations): pass integrationOptions down to Android * feat(integrations): pass integrationOptions down in iOS * feat(integrations): pass integrations in an options parameter * feat(integrations): fix test * feat(integrations): remove a rogue .only * feat(ios): fix typo * feat(integrations): update middleware to pass in integrations directly * feat(integrations): fix linting * feat(integrations): add test for middleware integrations * feat(integrations): fix linting * feat(integrations): add integrations to screen and alias * feat(integrations): code style fixes
1 parent 09e997b commit 13b663e

File tree

7 files changed

+184
-77
lines changed

7 files changed

+184
-77
lines changed

packages/core/android/src/main/java/com/segment/analytics/reactnative/core/RNAnalyticsModule.kt

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,14 @@ import android.content.pm.PackageManager
2929
import com.facebook.react.bridge.*
3030
import com.segment.analytics.Analytics
3131
import com.segment.analytics.Properties
32+
import com.segment.analytics.Options
3233
import com.segment.analytics.Traits
3334
import com.segment.analytics.ValueMap
3435
import com.segment.analytics.internal.Utils.getSegmentSharedPreferences
3536
import java.util.concurrent.TimeUnit
37+
import com.facebook.react.bridge.ReadableMap
38+
39+
3640

3741
class RNAnalyticsModule(context: ReactApplicationContext): ReactContextBaseJavaModule(context) {
3842
override fun getName() = "RNAnalytics"
@@ -42,7 +46,6 @@ class RNAnalyticsModule(context: ReactApplicationContext): ReactContextBaseJavaM
4246

4347
companion object {
4448
private var singletonJsonConfig: String? = null
45-
private var key: String? = null
4649
private var versionKey = "version"
4750
private var buildKey = "build"
4851
}
@@ -56,18 +59,41 @@ class RNAnalyticsModule(context: ReactApplicationContext): ReactContextBaseJavaM
5659
}
5760
}
5861

62+
/**
63+
* Builds Options with integrations
64+
* The format for the integrations variable is { String: Boolean | Map }
65+
*/
66+
private fun getOptions(integrations: ReadableMap?): Options {
67+
var options = Options()
68+
69+
if (integrations !== null) {
70+
val iterator = integrations.keySetIterator()
71+
while (iterator.hasNextKey()) {
72+
val nextKey = iterator.nextKey()
73+
when (integrations.getType(nextKey)) {
74+
ReadableType.Boolean -> options.setIntegration(nextKey, integrations.getBoolean(nextKey))
75+
ReadableType.Map -> {
76+
options.setIntegration(nextKey, true)
77+
options.setIntegrationOptions(nextKey, integrations.getMap(nextKey)?.toHashMap())
78+
}
79+
}
80+
}
81+
}
82+
return options
83+
}
84+
5985
/**
6086
* Tracks application lifecycle events - Application Installed, Application Updated and Application Opened
6187
* This is built to exactly mirror the application lifecycle tracking in analytics-android
6288
*/
63-
private fun trackApplicationLifecycleEvents() {
89+
private fun trackApplicationLifecycleEvents(writeKey: String?) {
6490
// Get the current version.
6591
var packageInfo = this.getPackageInfo()
6692
val currentVersion = packageInfo.versionName
6793
val currentBuild = packageInfo.versionCode
6894

6995
// Get the previous recorded version.
70-
val sharedPreferences = getSegmentSharedPreferences(reactApplicationContext, key)
96+
val sharedPreferences = getSegmentSharedPreferences(reactApplicationContext, writeKey)
7197
val previousVersion = sharedPreferences.getString(versionKey, null)
7298
val previousBuild = sharedPreferences.getInt(buildKey, -1)
7399

@@ -145,35 +171,33 @@ class RNAnalyticsModule(context: ReactApplicationContext): ReactContextBaseJavaM
145171
return promise.reject("E_SEGMENT_ERROR", e)
146172
}
147173

148-
singletonJsonConfig = json
149-
key = writeKey
150-
151174
if(options.getBoolean("trackAppLifecycleEvents")) {
152-
this.trackApplicationLifecycleEvents()
175+
this.trackApplicationLifecycleEvents(writeKey)
153176
}
154177

178+
singletonJsonConfig = json
155179
promise.resolve(null)
156180
}
157181

158182
@ReactMethod
159-
fun track(event: String, properties: ReadableMap, context: ReadableMap) =
160-
analytics.track(event, Properties() from properties)
183+
fun track(event: String, properties: ReadableMap, integrations: ReadableMap, context: ReadableMap) =
184+
analytics.track(event, Properties() from properties, this.getOptions(integrations))
161185

162186
@ReactMethod
163-
fun screen(name: String, properties: ReadableMap, context: ReadableMap) =
164-
analytics.screen(name, Properties() from properties)
187+
fun screen(name: String, properties: ReadableMap, integrations: ReadableMap, context: ReadableMap) =
188+
analytics.screen(null, name, Properties() from properties, this.getOptions(integrations))
165189

166190
@ReactMethod
167-
fun identify(userId: String, traits: ReadableMap, context: ReadableMap) =
168-
analytics.identify(userId, Traits() from traits, null)
191+
fun identify(userId: String, traits: ReadableMap, integrations: ReadableMap, context: ReadableMap) =
192+
analytics.identify(userId, Traits() from traits, this.getOptions(integrations))
169193

170194
@ReactMethod
171-
fun group(groupId: String, traits: ReadableMap, context: ReadableMap) =
172-
analytics.group(groupId, Traits() from traits)
195+
fun group(groupId: String, traits: ReadableMap, integrations: ReadableMap, context: ReadableMap) =
196+
analytics.group(groupId, Traits() from traits, this.getOptions(integrations))
173197

174198
@ReactMethod
175-
fun alias(newId: String, context: ReadableMap) =
176-
analytics.alias(newId)
199+
fun alias(newId: String, context: ReadableMap, integrations: ReadableMap) =
200+
analytics.alias(newId, this.getOptions(integrations))
177201

178202
@ReactMethod
179203
fun reset() =

packages/core/docs/classes/analytics.client.md

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,20 @@ ___
5353

5454
### alias
5555

56-
**alias**(newId: *`string`*): `Promise`<`void`>
56+
**alias**(newId: *`string`*, options?: *[Options]()*): `Promise`<`void`>
5757

58-
*Defined in [analytics.ts:263](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L263)*
58+
*Defined in [analytics.ts:266](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L266)*
5959

6060
Merge two user identities, effectively connecting two sets of user data as one. This may not be supported by all integrations.
6161

6262
When you learn more about who the group is, you can record that information with group.
6363

6464
**Parameters:**
6565

66-
| Name | Type | Description |
67-
| ------ | ------ | ------ |
68-
| newId | `string` | The new ID you want to alias the existing ID to. The existing ID will be either the previousId if you have called identify, or the anonymous ID. |
66+
| Name | Type | Default value | Description |
67+
| ------ | ------ | ------ | ------ |
68+
| newId | `string` | - | The new ID you want to alias the existing ID to. The existing ID will be either the previousId if you have called identify, or the anonymous ID. |
69+
| `Default value` options | [Options]() | {} |
6970

7071
**Returns:** `Promise`<`void`>
7172

@@ -97,7 +98,7 @@ ___
9798

9899
**disable**(): `Promise`<`void`>
99100

100-
*Defined in [analytics.ts:302](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L302)*
101+
*Defined in [analytics.ts:305](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L305)*
101102

102103
Completely disable the sending of any analytics data.
103104

@@ -112,7 +113,7 @@ ___
112113

113114
**enable**(): `Promise`<`void`>
114115

115-
*Defined in [analytics.ts:292](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L292)*
116+
*Defined in [analytics.ts:295](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L295)*
116117

117118
Enable the sending of analytics data. Enabled by default.
118119

@@ -127,7 +128,7 @@ ___
127128

128129
**flush**(): `Promise`<`void`>
129130

130-
*Defined in [analytics.ts:283](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L283)*
131+
*Defined in [analytics.ts:286](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L286)*
131132

132133
Trigger an upload of all queued events.
133134

@@ -142,7 +143,7 @@ ___
142143

143144
**getAnonymousId**(): `Promise`<`string`>
144145

145-
*Defined in [analytics.ts:307](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L307)*
146+
*Defined in [analytics.ts:310](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L310)*
146147

147148
Retrieve the anonymousId.
148149

@@ -153,9 +154,9 @@ ___
153154

154155
### group
155156

156-
**group**(groupId: *`string`*, traits?: *[JsonMap]()*): `Promise`<`void`>
157+
**group**(groupId: *`string`*, traits?: *[JsonMap]()*, options?: *[Options]()*): `Promise`<`void`>
157158

158-
*Defined in [analytics.ts:250](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L250)*
159+
*Defined in [analytics.ts:253](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L253)*
159160

160161
Associate a user with a group, organization, company, project, or w/e _you_ call them.
161162

@@ -167,6 +168,7 @@ When you learn more about who the group is, you can record that information with
167168
| ------ | ------ | ------ | ------ |
168169
| groupId | `string` | - | A database ID for this group. |
169170
| `Default value` traits | [JsonMap]() | {} | A dictionary of traits you know about the group. Things like: name, employees, etc. |
171+
| `Default value` options | [Options]() | {} | A dictionary of options, e.g. integrations (thigh analytics integration to forward the event to) |
170172

171173
**Returns:** `Promise`<`void`>
172174

@@ -175,9 +177,9 @@ ___
175177

176178
### identify
177179

178-
**identify**(user: *`string`*, traits?: *[JsonMap]()*): `Promise`<`void`>
180+
**identify**(user: *`string`*, traits?: *[JsonMap]()*, options?: *[Options]()*): `Promise`<`void`>
179181

180-
*Defined in [analytics.ts:238](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L238)*
182+
*Defined in [analytics.ts:240](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L240)*
181183

182184
Associate a user with their unique ID and record traits about them.
183185

@@ -189,6 +191,7 @@ When you learn more about who your user is, you can record that information with
189191
| ------ | ------ | ------ | ------ |
190192
| user | `string` | - | database ID (or email address) for this user. If you don't have a userId but want to record traits, you should pass nil. For more information on how we generate the UUID and Apple's policies on IDs, see [https://segment.io/libraries/ios#ids](https://segment.io/libraries/ios#ids) |
191193
| `Default value` traits | [JsonMap]() | {} | A dictionary of traits you know about the user. Things like: email, name, plan, etc. |
194+
| `Default value` options | [Options]() | {} | A dictionary of options, e.g. integrations (thigh analytics integration to forward the event to) |
192195

193196
**Returns:** `Promise`<`void`>
194197

@@ -237,7 +240,7 @@ ___
237240

238241
**reset**(): `Promise`<`void`>
239242

240-
*Defined in [analytics.ts:273](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L273)*
243+
*Defined in [analytics.ts:276](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L276)*
241244

242245
Reset any user state that is cached on the device.
243246

@@ -250,9 +253,9 @@ ___
250253

251254
### screen
252255

253-
**screen**(name: *`string`*, properties?: *[JsonMap]()*): `Promise`<`void`>
256+
**screen**(name: *`string`*, properties?: *[JsonMap]()*, options?: *[Options]()*): `Promise`<`void`>
254257

255-
*Defined in [analytics.ts:224](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L224)*
258+
*Defined in [analytics.ts:225](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L225)*
256259

257260
Record the screens or views your users see.
258261

@@ -264,6 +267,7 @@ When a user views a screen in your app, you'll want to record that here. For som
264267
| ------ | ------ | ------ | ------ |
265268
| name | `string` | - | The title of the screen being viewed. We recommend using human-readable names like 'Photo Feed' or 'Completed Purchase Screen'. |
266269
| `Default value` properties | [JsonMap]() | {} | A dictionary of properties for the screen view event. If the event was 'Added to Shopping Cart', it might have properties like price, productType, etc. |
270+
| `Default value` options | [Options]() | {} |
267271

268272
**Returns:** `Promise`<`void`>
269273

@@ -302,9 +306,9 @@ ___
302306

303307
### track
304308

305-
**track**(event: *`string`*, properties?: *[JsonMap]()*): `Promise`<`void`>
309+
**track**(event: *`string`*, properties?: *[JsonMap]()*, options?: *[Options]()*): `Promise`<`void`>
306310

307-
*Defined in [analytics.ts:206](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L206)*
311+
*Defined in [analytics.ts:207](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L207)*
308312

309313
Record the actions your users perform.
310314

@@ -316,6 +320,7 @@ When a user performs an action in your app, you'll want to track that action for
316320
| ------ | ------ | ------ | ------ |
317321
| event | `string` | - | The name of the event you're tracking. We recommend using human-readable names like \`Played a Song\` or \`Updated Status\`. |
318322
| `Default value` properties | [JsonMap]() | {} | A dictionary of properties for the event. If the event was 'Added to Shopping Cart', it might have properties like price, productType, etc. |
323+
| `Default value` options | [Options]() | {} | A dictionary of options, e.g. integrations (thigh analytics integration to forward the event to) |
319324

320325
**Returns:** `Promise`<`void`>
321326

packages/core/ios/RNAnalytics/RNAnalytics.m

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,39 +88,40 @@ +(void)initialize {
8888
resolver(nil);
8989
}
9090

91-
#define withContext(context) @{@"context": context}
91+
- (NSDictionary*)withContextAndIntegrations :(NSDictionary*)context :(NSDictionary*)integrations {
92+
return @{ @"context": context, @"integrations": integrations ?: @{}};
93+
}
94+
9295

93-
RCT_EXPORT_METHOD(track:(NSString*)name :(NSDictionary*)properties :(NSDictionary*)context) {
96+
RCT_EXPORT_METHOD(track:(NSString*)name :(NSDictionary*)properties :(NSDictionary*)integrations :(NSDictionary*)context) {
9497
[SEGAnalytics.sharedAnalytics track:name
9598
properties:properties
96-
options:withContext(context)];
99+
options:[self withContextAndIntegrations :context :integrations]];
97100
}
98101

99-
RCT_EXPORT_METHOD(screen:(NSString*)name :(NSDictionary*)properties :(NSDictionary*)context) {
102+
RCT_EXPORT_METHOD(screen:(NSString*)name :(NSDictionary*)properties :(NSDictionary*)integrations :(NSDictionary*)context) {
100103
[SEGAnalytics.sharedAnalytics screen:name
101104
properties:properties
102-
options:withContext(context)];
105+
options:[self withContextAndIntegrations :context :integrations]];
103106
}
104107

105-
RCT_EXPORT_METHOD(identify:(NSString*)userId :(NSDictionary*)traits :(NSDictionary*)context) {
108+
RCT_EXPORT_METHOD(identify:(NSString*)userId :(NSDictionary*)traits :(NSDictionary*)integrations :(NSDictionary*)context) {
106109
[SEGAnalytics.sharedAnalytics identify:userId
107110
traits:traits
108-
options:withContext(context)];
111+
options:[self withContextAndIntegrations :context :integrations]];
109112
}
110113

111-
RCT_EXPORT_METHOD(group:(NSString*)groupId :(NSDictionary*)traits :(NSDictionary*)context) {
114+
RCT_EXPORT_METHOD(group:(NSString*)groupId :(NSDictionary*)traits :(NSDictionary*)integrations :(NSDictionary*)context) {
112115
[SEGAnalytics.sharedAnalytics group:groupId
113116
traits:traits
114-
options:withContext(context)];
117+
options:[self withContextAndIntegrations :context :integrations]];
115118
}
116119

117-
RCT_EXPORT_METHOD(alias:(NSString*)newId :(NSDictionary*)context) {
120+
RCT_EXPORT_METHOD(alias:(NSString*)newId :(NSDictionary*)integrations :(NSDictionary*)context) {
118121
[SEGAnalytics.sharedAnalytics alias:newId
119-
options:withContext(context)];
122+
options:[self withContextAndIntegrations :context :integrations]];
120123
}
121124

122-
#undef withContext
123-
124125
RCT_EXPORT_METHOD(reset) {
125126
[SEGAnalytics.sharedAnalytics reset];
126127
}

packages/core/src/__tests__/analytics.spec.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,22 @@ it('waits for .setup()', async () => {
5757
expect(Bridge.track).not.toHaveBeenCalled()
5858
await client.setup('key')
5959

60-
expect(Bridge.track).toHaveBeenNthCalledWith(1, 'test 1', {}, ctx)
61-
expect(Bridge.track).toHaveBeenNthCalledWith(2, 'test 2', {}, ctx)
60+
expect(Bridge.track).toHaveBeenNthCalledWith(1, 'test 1', {}, {}, ctx)
61+
expect(Bridge.track).toHaveBeenNthCalledWith(2, 'test 2', {}, {}, ctx)
6262
})
6363

6464
it('does .track()', () =>
65-
testCall('track')('Added to cart', { productId: 'azertyuiop' }, ctx))
65+
testCall('track')('Added to cart', { productId: 'azertyuiop' }, {}, ctx))
6666

6767
it('does .screen()', () =>
68-
testCall('screen')('Shopping cart', { from: 'Product page' }, ctx))
68+
testCall('screen')('Shopping cart', { from: 'Product page' }, {}, ctx))
6969

7070
it('does .identify()', () =>
71-
testCall('identify')('sloth', { eats: 'leaves' }, ctx))
71+
testCall('identify')('sloth', { eats: 'leaves' }, {}, ctx))
7272

73-
it('does .group()', () => testCall('group')('bots', { humans: false }, ctx))
73+
it('does .group()', () => testCall('group')('bots', { humans: false }, {}, ctx))
7474

75-
it('does .alias()', () => testCall('alias')('new alias', ctx))
75+
it('does .alias()', () => testCall('alias')('new alias', {}, ctx))
7676

7777
it('does .reset()', testCall('reset'))
7878
it('does .flush()', testCall('flush'))
@@ -103,3 +103,24 @@ function testCall<K extends keyof typeof Bridge>(name: K) {
103103
expect(Bridge[name]).toHaveBeenNthCalledWith(1, ...args)
104104
}) as (typeof Bridge)[K]
105105
}
106+
107+
it('enables setting integrations from the middleware', async () => {
108+
const integrations = {
109+
'Google Analytics': false,
110+
Mixpanel: { foo: 'bar' }
111+
}
112+
113+
analytics.middleware(async ({ next, context, data }) =>
114+
// @ts-ignore ts is expecting newId for some reasons
115+
next(context, { ...data, integrations })
116+
)
117+
118+
const trackSpy = jest.fn()
119+
getBridgeStub('track').mockImplementationOnce(trackSpy)
120+
analytics.track('test')
121+
await nextTick()
122+
123+
expect(trackSpy).toBeCalledWith('test', {}, integrations, {
124+
library: ctx.library
125+
})
126+
})

0 commit comments

Comments
 (0)