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

Commit

Permalink
feat(core): http proxying via setup configuration (segmentio#202)
Browse files Browse the repository at this point in the history
extended the configuration API to allow directing analytics traffic through a hosted proxy (or
other)
  • Loading branch information
adkenyon authored Aug 17, 2020
1 parent ecce14c commit 40a3f5a
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@ package com.segment.analytics.reactnative.core

import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.net.Uri
import com.facebook.react.bridge.*
import com.segment.analytics.Analytics
import com.segment.analytics.Properties
import com.segment.analytics.Options
import com.segment.analytics.Traits
import com.segment.analytics.ValueMap
import com.segment.analytics.internal.Utils.getSegmentSharedPreferences
import java.util.concurrent.TimeUnit
import com.facebook.react.bridge.ReadableMap
import com.segment.analytics.*
import java.io.IOException
import java.net.HttpURLConnection



Expand Down Expand Up @@ -155,6 +154,37 @@ class RNAnalyticsModule(context: ReactApplicationContext): ReactContextBaseJavaM
builder.trackApplicationLifecycleEvents()
}

if(options.hasKey("proxy") && options.getType("proxy") == ReadableType.Map) {
val proxyOptions = options.getMap("proxy")!!

builder.connectionFactory(object:ConnectionFactory() {
override fun openConnection(url:String): HttpURLConnection {
val uri = Uri.parse(url)
val uriBuilder = uri.buildUpon();

if (proxyOptions.hasKey("scheme")) {
uriBuilder.scheme(proxyOptions.getString("scheme"))
}

if (proxyOptions.hasKey("host")) {
var host = proxyOptions.getString("host");

if (proxyOptions.hasKey("port")) {
host = host + ":" + proxyOptions.getInt("port");
}

uriBuilder.encodedAuthority(host)
}

if (proxyOptions.hasKey("path")) {
uriBuilder.path(proxyOptions.getString("path") + uri.path)
}

return super.openConnection(uriBuilder.toString())
}
})
}

try {
Analytics.setSingletonInstance(
RNAnalytics.buildWithIntegrations(builder)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

**Ƭ Integration**: *`function` \| `object`*

*Defined in [analytics.ts:8](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L8)*
*Defined in [analytics.ts:8](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L8)*

___

30 changes: 15 additions & 15 deletions packages/core/docs/classes/analytics.client.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

**● ready**: *`false`* = false

*Defined in [analytics.ts:104](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L104)*
*Defined in [analytics.ts:147](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L147)*

Whether the client is ready to send events to Segment.

Expand All @@ -55,7 +55,7 @@ ___

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

*Defined in [analytics.ts:274](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L274)*
*Defined in [analytics.ts:317](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L317)*

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

Expand All @@ -77,7 +77,7 @@ ___

**catch**(handler: *[ErrorHandler]()*): `this`

*Defined in [analytics.ts:119](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L119)*
*Defined in [analytics.ts:162](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L162)*

Catch React-Native bridge errors

Expand All @@ -98,7 +98,7 @@ ___

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

*Defined in [analytics.ts:313](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L313)*
*Defined in [analytics.ts:356](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L356)*

Completely disable the sending of any analytics data.

Expand All @@ -113,7 +113,7 @@ ___

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

*Defined in [analytics.ts:303](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L303)*
*Defined in [analytics.ts:346](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L346)*

Enable the sending of analytics data. Enabled by default.

Expand All @@ -128,7 +128,7 @@ ___

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

*Defined in [analytics.ts:294](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L294)*
*Defined in [analytics.ts:337](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L337)*

Trigger an upload of all queued events.

Expand All @@ -143,7 +143,7 @@ ___

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

*Defined in [analytics.ts:318](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L318)*
*Defined in [analytics.ts:361](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L361)*

Retrieve the anonymousId.

Expand All @@ -156,7 +156,7 @@ ___

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

*Defined in [analytics.ts:261](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L261)*
*Defined in [analytics.ts:304](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L304)*

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

Expand All @@ -179,7 +179,7 @@ ___

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

*Defined in [analytics.ts:248](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L248)*
*Defined in [analytics.ts:291](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L291)*

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

Expand All @@ -202,7 +202,7 @@ ___

**middleware**(middleware: *[Middleware]()*): `this`

*Defined in [analytics.ts:157](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L157)*
*Defined in [analytics.ts:200](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L200)*

Append a new middleware to the middleware chain.

Expand Down Expand Up @@ -240,7 +240,7 @@ ___

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

*Defined in [analytics.ts:284](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L284)*
*Defined in [analytics.ts:327](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L327)*

Reset any user state that is cached on the device.

Expand All @@ -255,7 +255,7 @@ ___

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

*Defined in [analytics.ts:233](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L233)*
*Defined in [analytics.ts:276](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L276)*

Record the screens or views your users see.

Expand All @@ -278,7 +278,7 @@ ___

**setup**(writeKey: *`string`*, configuration?: *[Configuration](../interfaces/analytics.configuration.md)*): `Promise`<`void`>

*Defined in [analytics.ts:196](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L196)*
*Defined in [analytics.ts:239](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L239)*

Setup the Analytics module. All calls made before are queued and only executed if the configuration was successful.

Expand Down Expand Up @@ -308,7 +308,7 @@ ___

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

*Defined in [analytics.ts:215](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L215)*
*Defined in [analytics.ts:258](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L258)*

Record the actions your users perform.

Expand All @@ -331,7 +331,7 @@ ___

**useNativeConfiguration**(): `this`

*Defined in [analytics.ts:169](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L169)*
*Defined in [analytics.ts:212](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L212)*

Use the native configuration.

Expand Down
32 changes: 23 additions & 9 deletions packages/core/docs/interfaces/analytics.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* [defaultProjectSettings](analytics.configuration.md#defaultprojectsettings)
* [flushAt](analytics.configuration.md#flushat)
* [ios](analytics.configuration.md#ios)
* [proxy](analytics.configuration.md#proxy)
* [recordScreenViews](analytics.configuration.md#recordscreenviews)
* [trackAppLifecycleEvents](analytics.configuration.md#trackapplifecycleevents)
* [trackAttributionData](analytics.configuration.md#trackattributiondata)
Expand All @@ -30,7 +31,7 @@

**● android**: *`undefined` \| `object`*

*Defined in [analytics.ts:77](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L77)*
*Defined in [analytics.ts:120](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L120)*

Android specific settings.

Expand All @@ -41,7 +42,7 @@ ___

**● debug**: *`undefined` \| `false` \| `true`*

*Defined in [analytics.ts:38](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L38)*
*Defined in [analytics.ts:38](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L38)*

___
<a id="defaultprojectsettings"></a>
Expand All @@ -50,7 +51,7 @@ ___

**● defaultProjectSettings**: *`undefined` \| `object`*

*Defined in [analytics.ts:46](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L46)*
*Defined in [analytics.ts:46](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L46)*

Default project settings to use, if Segment.com cannot be reached. An example configuration can be found here, using your write key: [](https://cdn-settings.segment.com/v1/projects/YOUR_WRITE_KEY/settings)[https://cdn-settings.segment.com/v1/projects/YOUR\_WRITE\_KEY/settings](https://cdn-settings.segment.com/v1/projects/YOUR_WRITE_KEY/settings)

Expand All @@ -61,7 +62,7 @@ ___

**● flushAt**: *`undefined` \| `number`*

*Defined in [analytics.ts:54](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L54)*
*Defined in [analytics.ts:54](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L54)*

The number of queued events that the analytics client should flush at. Setting this to `1` will not queue any events and will use more battery.

Expand All @@ -74,18 +75,31 @@ ___

**● ios**: *`undefined` \| `object`*

*Defined in [analytics.ts:59](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L59)*
*Defined in [analytics.ts:102](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L102)*

iOS specific settings.

___
<a id="proxy"></a>

### `<Optional>` proxy

**● proxy**: *`undefined` \| `object`*

*Defined in [analytics.ts:72](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L72)*

Whether the analytics client should send all requests through your own hosted proxy rather than directly to Segment. See: iOS: [https://segment.com/docs/connections/sources/catalog/libraries/mobile/ios/#proxy-http-calls](https://segment.com/docs/connections/sources/catalog/libraries/mobile/ios/#proxy-http-calls) android: [https://segment.com/docs/connections/sources/catalog/libraries/mobile/android/#proxy-http-calls](https://segment.com/docs/connections/sources/catalog/libraries/mobile/android/#proxy-http-calls)

Ex. For a desired proxy through `http://localhost:64000/segment` the configuration would look like such { scheme: 'http', host: 'localhost', port: 64000, path: '/segment' }

___
<a id="recordscreenviews"></a>

### `<Optional>` recordScreenViews

**● recordScreenViews**: *`undefined` \| `false` \| `true`*

*Defined in [analytics.ts:19](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L19)*
*Defined in [analytics.ts:19](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L19)*

Whether the analytics client should automatically make a screen call when a view controller is added to a view hierarchy. Because the iOS underlying implementation uses method swizzling, we recommend initializing the analytics client as early as possible.

Expand All @@ -98,7 +112,7 @@ ___

**● trackAppLifecycleEvents**: *`undefined` \| `false` \| `true`*

*Defined in [analytics.ts:26](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L26)*
*Defined in [analytics.ts:26](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L26)*

Whether the analytics client should automatically track application lifecycle events, such as "Application Installed", "Application Updated" and "Application Opened".

Expand All @@ -111,7 +125,7 @@ ___

**● trackAttributionData**: *`undefined` \| `false` \| `true`*

*Defined in [analytics.ts:32](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L32)*
*Defined in [analytics.ts:32](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L32)*

Whether the analytics client should automatically track attribution data from enabled providers using the mobile service.

Expand All @@ -124,7 +138,7 @@ ___

**● using**: *[Integration](../#integration)[]*

*Defined in [analytics.ts:37](https://github.com/segmentio/analytics-react-native/blob/master/packages/core/src/analytics.ts#L37)*
*Defined in [analytics.ts:37](https://github.com/adkenyon/analytics-react-native/blob/master/packages/core/src/analytics.ts#L37)*

Register a set of integrations to be used with this Analytics instance.

Expand Down
27 changes: 27 additions & 0 deletions packages/core/ios/RNAnalytics/RNAnalytics.m
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,33 @@ +(void)initialize {
config.enableAdvertisingTracking = [options[@"ios"][@"trackAdvertising"] boolValue];
config.defaultSettings = options[@"defaultProjectSettings"];

if ([options valueForKey:@"proxy"]) {
NSDictionary *proxyOptions = (NSDictionary *)[options valueForKey:@"proxy"];

config.requestFactory = ^(NSURL *url) {
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];

if ([proxyOptions valueForKey:@"scheme"]) {
components.scheme = [proxyOptions[@"scheme"] stringValue];
}

if ([proxyOptions valueForKey:@"host"]) {
components.host = [proxyOptions[@"host"] stringValue];
}

if ([proxyOptions valueForKey:@"port"]) {
components.port = [NSNumber numberWithInt:[proxyOptions[@"port"] intValue]];
}

if ([proxyOptions valueForKey:@"path"]) {
components.path = [[proxyOptions[@"path"] stringValue] stringByAppendingString:components.path];
}

NSURL *transformedURL = components.URL;
return [NSMutableURLRequest requestWithURL:transformedURL];
};
}

for(id factory in RNAnalyticsIntegrations) {
[config use:factory];
}
Expand Down
43 changes: 43 additions & 0 deletions packages/core/src/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,49 @@ export module Analytics {
*/
flushAt?: number

/**
* Whether the analytics client should send all requests through your own hosted
* proxy rather than directly to Segment.
* See:
* iOS: https://segment.com/docs/connections/sources/catalog/libraries/mobile/ios/#proxy-http-calls
* android: https://segment.com/docs/connections/sources/catalog/libraries/mobile/android/#proxy-http-calls
*
* Ex. For a desired proxy through `http://localhost:64000/segment` the configuration would look like such
* {
* scheme: 'http',
* host: 'localhost',
* port: 64000,
* path: '/segment'
* }
*
*/
proxy?: {

/**
* The proxy scheme, ex: http, https
*
* `https` by default.
*/
scheme?: string,

/**
* The proxy host name, ex: api.segment.io, cdn.segment.io
*
* Note: When using localhost with an Android device or simulator use `adb reverse tcp:<port> tcp:<port>`
*/
host?: string,

/**
* The proxy port number, ex: 80
*/
port?: number,

/**
* The proxy path, ex: /path/to/proxy
*/
path?: string,
},

/**
* iOS specific settings.
*/
Expand Down
Loading

0 comments on commit 40a3f5a

Please sign in to comment.