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

Data Residency Changes #973

Merged
merged 3 commits into from
Dec 10, 2020
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
6 changes: 3 additions & 3 deletions Segment.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0810;
LastUpgradeCheck = 1200;
LastUpgradeCheck = 1220;
ORGANIZATIONNAME = Segment;
TargetAttributes = {
EADEB85A1DECD080005322DA = {
Expand Down Expand Up @@ -607,7 +607,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand Down Expand Up @@ -674,7 +674,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
Expand Down
2 changes: 1 addition & 1 deletion Segment.xcodeproj/xcshareddata/xcschemes/Segment.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1220"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1220"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1220"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
15 changes: 15 additions & 0 deletions Segment/Classes/SEGAnalyticsConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,33 @@ NS_SWIFT_NAME(AnalyticsConfiguration)

/**
* Creates and returns a configuration with default settings and the given write key.
* This will use the API host `https://api.segment.io/v1` as the default.
*
* @param writeKey Your project's write key from segment.io.
*/
+ (_Nonnull instancetype)configurationWithWriteKey:(NSString *_Nonnull)writeKey;

/**
* Creates and returns a configuration with default settings and the given write key.
*
* @param writeKey Your project's write key from segment.io.
* @param defaultAPIHost The default API host to be used if none are supplied from Segment.com
*/
+ (_Nonnull instancetype)configurationWithWriteKey:(NSString *_Nonnull)writeKey defaultAPIHost:(NSURL *_Nullable)defaultAPIHost;

/**
* Your project's write key from segment.io.
*
* @see +configurationWithWriteKey:
*/
@property (nonatomic, copy, readonly, nonnull) NSString *writeKey;

/**
* The API host to be used for network requests to Segment.
* This value can change based on settings obtained from Segment.com.
*/
@property (nonatomic, copy, readonly, nullable) NSURL *apiHost;

/**
* Whether the analytics client should use location services.
* If `YES` and the host app hasn't asked for permission to use location services then the user will be presented with an alert view asking to do so. `NO` by default.
Expand Down
30 changes: 28 additions & 2 deletions Segment/Classes/SEGAnalyticsConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#import "SEGAnalytics.h"
#import "SEGMiddleware.h"
#import "SEGCrypto.h"
#import "SEGHTTPClient.h"
#import "SEGUtils.h"
#if TARGET_OS_IPHONE
@import UIKit;
#elif TARGET_OS_OSX
Expand Down Expand Up @@ -41,20 +43,39 @@ @interface SEGAnalyticsConfiguration ()
@property (nonatomic, strong, readonly) NSMutableArray *factories;
@property (nonatomic, strong) SEGAnalyticsExperimental *experimental;

- (instancetype)initWithWriteKey:(NSString *)writeKey defaultAPIHost:(NSURL * _Nullable)defaultAPIHost;

@end


@implementation SEGAnalyticsConfiguration

+ (instancetype)configurationWithWriteKey:(NSString *)writeKey
{
return [[SEGAnalyticsConfiguration alloc] initWithWriteKey:writeKey];
return [[SEGAnalyticsConfiguration alloc] initWithWriteKey:writeKey defaultAPIHost:nil];
}

+ (instancetype)configurationWithWriteKey:(NSString *)writeKey defaultAPIHost:(NSURL * _Nullable)defaultAPIHost
{
return [[SEGAnalyticsConfiguration alloc] initWithWriteKey:writeKey defaultAPIHost:defaultAPIHost];
}

- (instancetype)initWithWriteKey:(NSString *)writeKey
- (instancetype)initWithWriteKey:(NSString *)writeKey defaultAPIHost:(NSURL * _Nullable)defaultAPIHost
{
if (self = [self init]) {
self.writeKey = writeKey;

// get the host we have stored
NSString *host = [SEGUtils getAPIHost];
if ([host isEqualToString:kSegmentAPIBaseHost]) {
// we're getting the generic host back. have they
// supplied something other than that?
if (defaultAPIHost && ![host isEqualToString:defaultAPIHost.absoluteString]) {
// we should use the supplied default.
host = defaultAPIHost.absoluteString;
[SEGUtils saveAPIHost:host];
}
}
}
return self;
}
Expand Down Expand Up @@ -86,6 +107,11 @@ - (instancetype)init
return self;
}

- (NSURL *)apiHost
{
return [SEGUtils getAPIHostURL];
}

- (void)use:(id<SEGIntegrationFactory>)factory
{
[self.factories addObject:factory];
Expand Down
9 changes: 2 additions & 7 deletions Segment/Classes/SEGHTTPClient.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
@import Foundation;
#import "SEGAnalytics.h"

// TODO: Make this configurable via SEGAnalyticsConfiguration
// NOTE: `/` at the end kind of screws things up. So don't use it
//#define SEGMENT_API_BASE [NSURL URLWithString:@"https://api-segment-io-5fsaj1xnikhp.runscope.net/v1"]
//#define SEGMENT_CDN_BASE [NSURL URLWithString:@"https://cdn-segment-com-5fsaj1xnikhp.runscope.net/v1"]
#define SEGMENT_API_BASE [NSURL URLWithString:@"https://api.segment.io/v1"]
#define SEGMENT_CDN_BASE [NSURL URLWithString:@"https://cdn-settings.segment.com/v1"]

NS_ASSUME_NONNULL_BEGIN

extern NSString * const kSegmentAPIBaseHost;


NS_SWIFT_NAME(HTTPClient)
@interface SEGHTTPClient : NSObject
Expand Down
7 changes: 6 additions & 1 deletion Segment/Classes/SEGHTTPClient.m
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#import "SEGHTTPClient.h"
#import "NSData+SEGGZIP.h"
#import "SEGAnalyticsUtils.h"
#import "SEGUtils.h"

#define SEGMENT_CDN_BASE [NSURL URLWithString:@"https://cdn-settings.segment.com/v1"]

static const NSUInteger kMaxBatchSize = 475000; // 475KB

NSString * const kSegmentAPIBaseHost = @"https://api.segment.io/v1";

@implementation SEGHTTPClient

+ (NSMutableURLRequest * (^)(NSURL *))defaultRequestFactory
Expand Down Expand Up @@ -72,7 +77,7 @@ - (nullable NSURLSessionUploadTask *)upload:(NSDictionary *)batch forWriteKey:(N
// batch = SEGCoerceDictionary(batch);
NSURLSession *session = [self sessionForWriteKey:writeKey];

NSURL *url = [SEGMENT_API_BASE URLByAppendingPathComponent:@"batch"];
NSURL *url = [[SEGUtils getAPIHostURL] URLByAppendingPathComponent:@"batch"];
NSMutableURLRequest *request = self.requestFactory(url);

// This is a workaround for an IOS 8.3 bug that causes Content-Type to be incorrectly set
Expand Down
2 changes: 0 additions & 2 deletions Segment/Classes/SEGSegmentIntegration.m
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ @interface SEGSegmentIntegration ()
@property (nonatomic, assign) SEGAnalyticsConfiguration *configuration;
@property (atomic, copy) NSDictionary *referrer;
@property (nonatomic, copy) NSString *userId;
@property (nonatomic, strong) NSURL *apiURL;
@property (nonatomic, strong) SEGHTTPClient *httpClient;
@property (nonatomic, strong) id<SEGStorage> fileStorage;
@property (nonatomic, strong) id<SEGStorage> userDefaultsStorage;
Expand Down Expand Up @@ -69,7 +68,6 @@ - (id)initWithAnalytics:(SEGAnalytics *)analytics httpClient:(SEGHTTPClient *)ht
self.httpClient.httpSessionDelegate = analytics.oneTimeConfiguration.httpSessionDelegate;
self.fileStorage = fileStorage;
self.userDefaultsStorage = userDefaultsStorage;
self.apiURL = [SEGMENT_API_BASE URLByAppendingPathComponent:@"import"];
self.reachability = [SEGReachability reachabilityWithHostname:@"google.com"];
[self.reachability startNotifier];
self.serialQueue = seg_dispatch_queue_create_specific("io.segment.analytics.segmentio", DISPATCH_QUEUE_SERIAL);
Expand Down
44 changes: 36 additions & 8 deletions Segment/Internal/SEGIntegrationsManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,12 @@ - (void)setCachedSettings:(NSDictionary *)settings

- (void)updateIntegrationsWithSettings:(NSDictionary *)projectSettings
{
// see if we have a new segment API host and set it.
NSString *apiHost = projectSettings[@"Segment.io"][@"apiHost"];
if (apiHost) {
[SEGUtils saveAPIHost:apiHost];
}

seg_dispatch_specific_sync(_serialQueue, ^{
if (self.initialized) {
return;
Expand Down Expand Up @@ -443,8 +449,29 @@ - (void)configureEdgeFunctions:(NSDictionary *)settings
}
}

- (NSDictionary *)defaultSettings
{
return @{
@"integrations" : @{
@"Segment.io" : @{
@"apiKey" : self.configuration.writeKey,
@"apiHost" : [SEGUtils getAPIHost]
},
},
@"plan" : @{@"track" : @{}}
};
}

- (void)refreshSettings
{
// look at our cache immediately, lets try to get things running
// with the last values while we wait to see about any updates.
NSDictionary *previouslyCachedSettings = [self cachedSettings];
if (previouslyCachedSettings && [previouslyCachedSettings count] > 0) {
[self setCachedSettings:previouslyCachedSettings];
[self configureEdgeFunctions:previouslyCachedSettings];
}

seg_dispatch_specific_async(_serialQueue, ^{
if (self.settingsRequest) {
return;
Expand All @@ -459,23 +486,24 @@ - (void)refreshSettings
NSDictionary *previouslyCachedSettings = [self cachedSettings];
if (previouslyCachedSettings && [previouslyCachedSettings count] > 0) {
[self setCachedSettings:previouslyCachedSettings];
[self configureEdgeFunctions:settings];
[self configureEdgeFunctions:previouslyCachedSettings];
} else if (self.configuration.defaultSettings != nil) {
// If settings request fail, load a user-supplied version if present.
// but make sure segment.io is in the integrations
NSMutableDictionary *newSettings = [self.configuration.defaultSettings serializableMutableDeepCopy];
newSettings[@"integrations"][@"Segment.io"][@"apiKey"] = self.configuration.writeKey;
NSMutableDictionary *integrations = newSettings[@"integrations"];
if (integrations != nil) {
integrations[@"Segment.io"] = @{@"apiKey": self.configuration.writeKey, @"apiHost": [SEGUtils getAPIHost]};
} else {
newSettings[@"integrations"] = @{@"integrations": @{@"apiKey": self.configuration.writeKey, @"apiHost": [SEGUtils getAPIHost]}};
}

[self setCachedSettings:newSettings];
// don't configure edge functions here. it'll do the right thing on it's own.
} else {
// If settings request fail, fall back to using just Segment integration.
// Doesn't address situations where this callback never gets called (though we don't expect that to ever happen).
[self setCachedSettings:@{
@"integrations" : @{
@"Segment.io" : @{@"apiKey" : self.configuration.writeKey},
},
@"plan" : @{@"track" : @{}}
}];
[self setCachedSettings:[self defaultSettings]];
// don't configure edge functions here. it'll do the right thing on it's own.
}
}
Expand Down
1 change: 0 additions & 1 deletion Segment/Internal/SEGState.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)sharedInstance;
- (instancetype)init __unavailable;

- (void)setUserInfo:(SEGUserInfo *)userInfo;
@end

NS_ASSUME_NONNULL_END
4 changes: 4 additions & 0 deletions Segment/Internal/SEGUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Utilities)
@interface SEGUtils : NSObject

+ (void)saveAPIHost:(nonnull NSString *)apiHost;
+ (nonnull NSString *)getAPIHost;
+ (nullable NSURL *)getAPIHostURL;

+ (NSData *_Nullable)dataFromPlist:(nonnull id)plist;
+ (id _Nullable)plistFromData:(NSData *)data;

Expand Down
30 changes: 30 additions & 0 deletions Segment/Internal/SEGUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#import "SEGAnalyticsConfiguration.h"
#import "SEGReachability.h"
#import "SEGAnalytics.h"
#import "SEGHTTPClient.h"

#include <sys/sysctl.h>

Expand All @@ -15,8 +16,37 @@
static CTTelephonyNetworkInfo *_telephonyNetworkInfo;
#endif

const NSString *segment_apiHost = @"segment_apihost";

@implementation SEGUtils

+ (void)saveAPIHost:(nonnull NSString *)apiHost
{
if (!apiHost) {
return;
}
if (![apiHost containsString:@"https://"]) {
apiHost = [NSString stringWithFormat:@"https://%@", apiHost];
}
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:apiHost forKey:[segment_apiHost copy]];
}

+ (nonnull NSString *)getAPIHost
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *result = [defaults stringForKey:[segment_apiHost copy]];
if (!result) {
result = kSegmentAPIBaseHost;
}
return result;
}

+ (nullable NSURL *)getAPIHostURL
{
return [NSURL URLWithString:[SEGUtils getAPIHost]];
}

+ (NSData *_Nullable)dataFromPlist:(nonnull id)plist
{
NSError *error = nil;
Expand Down
Loading