Skip to content

Commit e5af712

Browse files
fix(datafile-parsing): Prevent SDK from initializing if the datafile … (#305)
* fix(datafile-parsing): Prevent SDK from initializing if the datafile version is higher than the max version supported. * changed supportedDatafileVersions from class method to static property * comments addressed * covered class static objects with dispatch_once block
1 parent 550de99 commit e5af712

File tree

7 files changed

+195
-9
lines changed

7 files changed

+195
-9
lines changed

OptimizelySDKCore/OptimizelySDKCore.xcodeproj/project.pbxproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@
211211
59B9E1E320E35C9E002F732E /* OPTLYProjectConfigSwiftTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59B9E1E020E35C9E002F732E /* OPTLYProjectConfigSwiftTest.swift */; };
212212
5E3C0E701DDF7A290025DB85 /* OPTLYVariable.m in Sources */ = {isa = PBXBuildFile; fileRef = 5ECBBE671DDE6A800028FF6B /* OPTLYVariable.m */; };
213213
5E3C0E711DDF7A3C0025DB85 /* OPTLYVariable.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ECBBE661DDE6A800028FF6B /* OPTLYVariable.h */; settings = {ATTRIBUTES = (Public, ); }; };
214-
5E4C07F31DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json in Resources */ = {isa = PBXBuildFile; fileRef = 5E4C07F21DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json */; };
215-
5E4C07F41DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json in Resources */ = {isa = PBXBuildFile; fileRef = 5E4C07F21DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json */; };
214+
5E4C07F31DFF645C0042B1F8 /* UnsupportedVersionDatafile.json in Resources */ = {isa = PBXBuildFile; fileRef = 5E4C07F21DFF645C0042B1F8 /* UnsupportedVersionDatafile.json */; };
215+
5E4C07F41DFF645C0042B1F8 /* UnsupportedVersionDatafile.json in Resources */ = {isa = PBXBuildFile; fileRef = 5E4C07F21DFF645C0042B1F8 /* UnsupportedVersionDatafile.json */; };
216216
5E4C07FB1DFF66B00042B1F8 /* OPTLYNetworkServiceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E4C07FA1DFF66B00042B1F8 /* OPTLYNetworkServiceTest.m */; };
217217
5E4C07FC1DFF66B00042B1F8 /* OPTLYNetworkServiceTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E4C07FA1DFF66B00042B1F8 /* OPTLYNetworkServiceTest.m */; };
218218
5ECBBE681DDE6A800028FF6B /* OPTLYVariable.h in Headers */ = {isa = PBXBuildFile; fileRef = 5ECBBE661DDE6A800028FF6B /* OPTLYVariable.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -608,7 +608,7 @@
608608
59B9E1CE20E28D9D002F732E /* OptimizelySwiftTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelySwiftTest.swift; sourceTree = "<group>"; };
609609
59B9E1DF20E35C50002F732E /* OptimizelySDKCoreiOSTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OptimizelySDKCoreiOSTests-Bridging-Header.h"; sourceTree = "<group>"; };
610610
59B9E1E020E35C9E002F732E /* OPTLYProjectConfigSwiftTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPTLYProjectConfigSwiftTest.swift; sourceTree = "<group>"; };
611-
5E4C07F21DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = InvalidDatafileVersionDatafile.json; sourceTree = "<group>"; };
611+
5E4C07F21DFF645C0042B1F8 /* UnsupportedVersionDatafile.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = UnsupportedVersionDatafile.json; sourceTree = "<group>"; };
612612
5E4C07FA1DFF66B00042B1F8 /* OPTLYNetworkServiceTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OPTLYNetworkServiceTest.m; sourceTree = "<group>"; };
613613
5ECBBE661DDE6A800028FF6B /* OPTLYVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OPTLYVariable.h; sourceTree = "<group>"; };
614614
5ECBBE671DDE6A800028FF6B /* OPTLYVariable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OPTLYVariable.m; sourceTree = "<group>"; };
@@ -1159,7 +1159,7 @@
11591159
children = (
11601160
EA2FAB951DC6FDFA00B1D81B /* BucketerTestsDatafile.json */,
11611161
3E92800D1F26AD4700214C58 /* BucketerTestsDatafile2.json */,
1162-
5E4C07F21DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json */,
1162+
5E4C07F21DFF645C0042B1F8 /* UnsupportedVersionDatafile.json */,
11631163
3E99CF7D1FE02C1C00B16B97 /* optimizely_6372300739_v4.json */,
11641164
EA2FAB961DC6FDFA00B1D81B /* optimizely_6372300739.json */,
11651165
EA2FAB9A1DC6FDFA00B1D81B /* optimizely_7519590183.json */,
@@ -1659,7 +1659,7 @@
16591659
EA2FABD81DC6FDFA00B1D81B /* optimizely_7519590183.json in Resources */,
16601660
EA2FABD51DC6FDFA00B1D81B /* test_data_50_experiments.json in Resources */,
16611661
EA2FABCF1DC6FDFA00B1D81B /* test_data_10_experiments.json in Resources */,
1662-
5E4C07F31DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json in Resources */,
1662+
5E4C07F31DFF645C0042B1F8 /* UnsupportedVersionDatafile.json in Resources */,
16631663
3E99CF7F1FE02C2D00B16B97 /* optimizely_6372300739_v4.json in Resources */,
16641664
3EEC2AF220CB3D9E00D096E4 /* test_data_10_experimentsV3.json in Resources */,
16651665
EA2FABC91DC6FDFA00B1D81B /* BucketerTestsDatafile.json in Resources */,
@@ -1687,7 +1687,7 @@
16871687
EA2FABD01DC6FDFA00B1D81B /* test_data_10_experiments.json in Resources */,
16881688
3E99CF801FE02C2E00B16B97 /* optimizely_6372300739_v4.json in Resources */,
16891689
3EEC2AF320CB3D9E00D096E4 /* test_data_10_experimentsV3.json in Resources */,
1690-
5E4C07F41DFF645C0042B1F8 /* InvalidDatafileVersionDatafile.json in Resources */,
1690+
5E4C07F41DFF645C0042B1F8 /* UnsupportedVersionDatafile.json in Resources */,
16911691
3EF9D99120AE425000A9B002 /* V2TestDatafile.json in Resources */,
16921692
EA2FABCA1DC6FDFA00B1D81B /* BucketerTestsDatafile.json in Resources */,
16931693
EA2FABD31DC6FDFA00B1D81B /* test_data_25_experiments.json in Resources */,

OptimizelySDKCore/OptimizelySDKCore/OPTLYErrorHandlerMessages.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
extern NSString * const OPTLYErrorHandlerMessagesDomain;
2525

2626
extern NSString * const OPTLYErrorHandlerMessagesDataFileInvalid;
27+
extern NSString * const OPTLYErrorHandlerMessagesDataFileVersionInvalid;
2728
extern NSString * const OPTLYErrorHandlerMessagesEventDispatcherInvalid;
2829
extern NSString * const OPTLYErrorHandlerMessagesLoggerInvalid;
2930
extern NSString * const OPTLYErrorHandlerMessagesErrorHandlerInvalid;

OptimizelySDKCore/OptimizelySDKCore/OPTLYErrorHandlerMessages.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818

1919
NSString * const OPTLYErrorHandlerMessagesDomain = @"com.optimizely.optimizelySDK";
2020

21-
NSString * const OPTLYErrorHandlerMessagesDataFileInvalid = @"Provided 'data file' is in an invalid format.";
21+
NSString * const OPTLYErrorHandlerMessagesDataFileInvalid = @"Provided 'datafile' is in an invalid format.";
22+
NSString * const OPTLYErrorHandlerMessagesDataFileVersionInvalid = @"Provided 'datafile' version %@ is not supported.";
2223
NSString * const OPTLYErrorHandlerMessagesEventDispatcherInvalid = @"Provided 'event dispatcher' is in an invalid format.";
2324
NSString * const OPTLYErrorHandlerMessagesLoggerInvalid = @"Provided 'logger' is in an invalid format.";
2425
NSString * const OPTLYErrorHandlerMessagesErrorHandlerInvalid = @"Provided 'error handler' is in an invalid format.";

OptimizelySDKCore/OptimizelySDKCore/OPTLYProjectConfig.m

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
NSString * const kExpectedDatafileVersion = @"4";
3737
NSString * const kReservedAttributePrefix = @"$opt_";
38+
// Array representing supported datafile versions.
39+
static NSArray *supportedDatafileVersions = nil;
3840

3941
@interface OPTLYProjectConfig()
4042

@@ -64,6 +66,14 @@ + (nullable instancetype)init:(nonnull OPTLYProjectConfigBuilderBlock)builderBlo
6466
}
6567

6668
- (instancetype)initWithBuilder:(OPTLYProjectConfigBuilder *)builder {
69+
70+
// initialize all class static objects
71+
static dispatch_once_t onceToken;
72+
dispatch_once(&onceToken, ^{
73+
// static objects which should be initialized once
74+
supportedDatafileVersions = @[@"2", @"3", @"4"];
75+
});
76+
6777
// check for valid error handler
6878
if (builder.errorHandler) {
6979
if (![OPTLYErrorHandler conformsToOPTLYErrorHandlerProtocol:[builder.errorHandler class]]) {
@@ -113,6 +123,13 @@ - (instancetype)initWithBuilder:(OPTLYProjectConfigBuilder *)builder {
113123
NSError *datafileError;
114124
OPTLYProjectConfig *projectConfig = [[OPTLYProjectConfig alloc] initWithData:builder.datafile error:&datafileError];
115125

126+
if (!datafileError && ![supportedDatafileVersions containsObject:projectConfig.version]) {
127+
NSString *description = [NSString stringWithFormat:OPTLYErrorHandlerMessagesDataFileInvalid, projectConfig.version];
128+
datafileError = [NSError errorWithDomain:OPTLYErrorHandlerMessagesDomain
129+
code:OPTLYErrorTypesDatafileInvalid
130+
userInfo:@{NSLocalizedDescriptionKey : NSLocalizedString(description, nil)}];
131+
}
132+
116133
// check if project config's datafile version matches expected datafile version
117134
if (![projectConfig.version isEqualToString:kExpectedDatafileVersion]) {
118135
NSString *logMessage = [NSString stringWithFormat:OPTLYLoggerMessagesDatafileVersion, projectConfig.version];

OptimizelySDKCore/OptimizelySDKCoreTests/OPTLYProjectConfigTest.m

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
static NSString * const kAttributeKey = @"browser_type";
5555
static NSString * const kAttributeId = @"6380961481";
5656

57-
static NSString * const kInvalidDatafileVersionDatafileName = @"InvalidDatafileVersionDatafile";
57+
static NSString * const kUnsupportedVersionDatafileName = @"UnsupportedVersionDatafile";
5858

5959
@interface OPTLYProjectConfigTest : XCTestCase
6060
@property (nonatomic, strong) OPTLYProjectConfig *projectConfig;
@@ -147,6 +147,27 @@ - (void)testInitWithBuilderBlockInvalidModulesFails {
147147
XCTAssertNil(projectConfig, @"project config should not be able to be created with invalid modules.");
148148
}
149149

150+
- (void)testInitWithBuilderBlockUnsupportedDatafile {
151+
NSData *datafile = [OPTLYTestHelper loadJSONDatafileIntoDataObject:kUnsupportedVersionDatafileName];
152+
id errorHandlerMock = OCMPartialMock([OPTLYErrorHandlerNoOp new]);
153+
NSString *description = [NSString stringWithFormat:OPTLYErrorHandlerMessagesDataFileInvalid, @"5"];
154+
NSError *datafileError = [NSError errorWithDomain:OPTLYErrorHandlerMessagesDomain
155+
code:OPTLYErrorTypesDatafileInvalid
156+
userInfo:@{NSLocalizedDescriptionKey : NSLocalizedString(description, nil)}];
157+
158+
OPTLYProjectConfig *projectConfig = [[OPTLYProjectConfig alloc] initWithBuilder:[OPTLYProjectConfigBuilder builderWithBlock:^(OPTLYProjectConfigBuilder * _Nullable builder) {
159+
builder.datafile = datafile;
160+
builder.logger = [OPTLYLoggerDefault new];
161+
builder.errorHandler = errorHandlerMock;
162+
builder.userProfileService = [OPTLYUserProfileServiceNoOp new];
163+
}]];
164+
165+
XCTAssertNil(projectConfig, @"project config should be nil.");
166+
167+
OCMVerify([errorHandlerMock handleError:datafileError]);
168+
[errorHandlerMock stopMocking];
169+
}
170+
150171
#pragma mark - Test initWithDatafile:
151172

152173
- (void)testInitWithDatafile

OptimizelySDKCore/OptimizelySDKCoreTests/TestData/InvalidDatafileVersionDatafile.json

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
{
2+
"accountId": "789",
3+
"projectId": "1234",
4+
"version": "5",
5+
"revision": "42",
6+
"experiments": [
7+
{
8+
"id": "223",
9+
"key": "etag1",
10+
"status": "Running",
11+
"layerId": "1",
12+
"percentageIncluded": 9000,
13+
"audienceIds": [],
14+
"variations": [
15+
{
16+
"id": "276",
17+
"key": "vtag1",
18+
"variables": []
19+
},
20+
{
21+
"id": "277",
22+
"key": "vtag2",
23+
"variables": []
24+
}
25+
],
26+
"forcedVariations": {
27+
"testUser1": "vtag1",
28+
"testUser2": "vtag2"
29+
},
30+
"trafficAllocation": [
31+
{
32+
"entityId": "276",
33+
"endOfRange": 3500
34+
},
35+
{
36+
"entityId": "277",
37+
"endOfRange": 9000
38+
}
39+
]
40+
},
41+
{
42+
"id": "118",
43+
"key": "etag2",
44+
"status": "Not started",
45+
"layerId": "2",
46+
"audienceIds": [],
47+
"variations": [
48+
{
49+
"id": "278",
50+
"key": "vtag3",
51+
"variables": []
52+
},
53+
{
54+
"id": "279",
55+
"key": "vtag4",
56+
"variables": []
57+
}
58+
],
59+
"forcedVariations": {},
60+
"trafficAllocation": [
61+
{
62+
"entityId": "278",
63+
"endOfRange": 4500
64+
},
65+
{
66+
"entityId": "279",
67+
"endOfRange": 9000
68+
}
69+
]
70+
},
71+
{
72+
"id": "119",
73+
"key": "etag3",
74+
"status": "Launched",
75+
"layerId": "3",
76+
"audienceIds": [],
77+
"variations": [
78+
{
79+
"id": "280",
80+
"key": "vtag5"
81+
},
82+
{
83+
"id": "281",
84+
"key": "vtag6"
85+
}
86+
],
87+
"forcedVariations": {},
88+
"trafficAllocation": [
89+
{
90+
"entityId": "280",
91+
"endOfRange": 5000
92+
},
93+
{
94+
"entityId": "281",
95+
"endOfRange": 10000
96+
}
97+
]
98+
}
99+
],
100+
"groups": [],
101+
"audiences": [],
102+
"attributes": [
103+
{
104+
"id": "134",
105+
"key": "browser_type"
106+
}
107+
],
108+
"events": [
109+
{
110+
"id": "971",
111+
"key": "clicked_cart",
112+
"experimentIds": [
113+
"223"
114+
]
115+
},
116+
{
117+
"id": "098",
118+
"key": "Total Revenue",
119+
"experimentIds": [
120+
"223"
121+
]
122+
},
123+
{
124+
"id": "099",
125+
"key": "clicked_purchase",
126+
"experimentIds": [
127+
"118",
128+
"223"
129+
]
130+
},
131+
{
132+
"id": "100",
133+
"key": "launched_exp_event",
134+
"experimentIds": [
135+
"119"
136+
]
137+
},
138+
{
139+
"id": "101",
140+
"key": "event_with_launched_and_running_experiments",
141+
"experimentIds": [
142+
"119",
143+
"223"
144+
]
145+
}
146+
]
147+
}

0 commit comments

Comments
 (0)