Skip to content

Commit f8a40a6

Browse files
@W-19368408: [MSDK] Local dev instant login (iOS) (#3944)
I've verified that tests are passing locally ✅
1 parent f71eda5 commit f8a40a6

File tree

4 files changed

+119
-45
lines changed

4 files changed

+119
-45
lines changed

.codecov.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ ignore:
8080
- "shared"
8181
- "native"
8282
- "libs/**/*Test*/*"
83+
- "**/TestSetupUtils*"
8384

8485
flag_management:
8586
default_rules: # the rules that will be followed for any flag added, generally
@@ -110,4 +111,4 @@ component_management:
110111
- "libs/SmartStore/SmartStore/"
111112
- component_id: MobileSync
112113
paths:
113-
- "libs/MobileSync/MobileSync/"
114+
- "libs/MobileSync/MobileSync/"

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager.m

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,16 @@ + (void)initialize {
195195

196196
+ (void)initializeSDK {
197197
[self initializeSDKWithClass:InstanceClass];
198+
#ifdef DEBUG
199+
// For debug app builds only, use test instant log in if applicable.
200+
NSArray<NSString *> *arguments = [[NSProcessInfo processInfo] arguments];
201+
if ([arguments containsObject:@"-creds"]) {
202+
NSString *creds = arguments[[arguments indexOfObject:@"-creds"] + 1];
203+
204+
[TestSetupUtils populateAuthCredentialsFromString:creds initializeSdk:NO];
205+
[TestSetupUtils synchronousAuthRefreshWithUserDidLoginNotification:YES];
206+
}
207+
#endif
198208
}
199209

200210
+ (void)initializeSDKWithClass:(Class)className {
@@ -231,6 +241,7 @@ + (instancetype)sharedManager {
231241
+ (void)load {
232242
static dispatch_once_t onceToken;
233243
dispatch_once(&onceToken, ^{
244+
234245
// For dev support
235246
Method sfsdkMotionEndedMethod = class_getInstanceMethod([UIWindow class], @selector(sfsdk_motionEnded:withEvent:));
236247
IMP sfsdkMotionEndedImplementation = method_getImplementation(sfsdkMotionEndedMethod);

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/TestSetupUtils.h

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,45 @@ NS_ASSUME_NONNULL_BEGIN
5353
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromConfigFileForClass:(Class)testClass;
5454

5555
/**
56-
Performs a synchronous refresh of the OAuth credentials, which will stage the remaining auth
57-
data (access token, User ID, Org ID, etc.) in SFUserAccountManager.
58-
`populateAuthCredentialsFromConfigFile` is required to run once before this method will attempt
59-
to refresh authentication.
56+
Loads a set of auth credentials from the provided JSON string, and configures
57+
SFUserAccountManager and the current account with the data from that JSON.
58+
Salesforce Mobile SDK will be initialized while populating test credentials.
59+
@param testCredentialsJsonString The test credentials JSON as a string.
60+
@return The configuration data used to configure SFUserAccountManager (useful
61+
e.g. for hybrid apps which need the data to bootstrap SFHybridViewController).
62+
*/
63+
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString;
64+
65+
/**
66+
Loads a set of auth credentials from the provided JSON string, and configures
67+
SFUserAccountManager and the current account with the data from that JSON.
68+
@param testCredentialsJsonString The test credentials JSON as a string.
69+
@param initializeSdk Indicates if Salesfore Mobile SDK should be initialized
70+
while populating test credentials.
71+
@return The configuration data used to configure SFUserAccountManager (useful
72+
e.g. for hybrid apps which need the data to bootstrap SFHybridViewController).
73+
*/
74+
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString initializeSdk:(BOOL)initializeSdk;
75+
76+
/**
77+
Performs a synchronous refresh of the OAuth credentials, which will stage the
78+
remaining auth data (access token, User ID, Org ID, etc.) in
79+
SFUserAccountManager. `populateAuthCredentialsFromConfigFile`
80+
is required to run once before this method will attempt to refresh authentication.
81+
The "user did log in" notification will not be posted.
6082
*/
6183
+ (void)synchronousAuthRefresh;
6284

85+
/**
86+
Performs a synchronous refresh of the OAuth credentials, which will stage the
87+
remaining auth data (access token, User ID, Org ID, etc.) in
88+
SFUserAccountManager. `populateAuthCredentialsFromConfigFile`
89+
is required to run once before this method will attempt to refresh authentication.
90+
@param postUserDidLogIn Indicates if the "user did log in" notification should
91+
be posted.
92+
*/
93+
+ (void)synchronousAuthRefreshWithUserDidLoginNotification:(BOOL)postUserDidLogIn;
94+
6395
+ (SFOAuthCredentials *)newClientCredentials;
6496

6597
@end

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/TestSetupUtils.m

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,71 @@ + (SFSDKTestCredentialsData *)populateAuthCredentialsFromConfigFileForClass:(Cla
5555
NSFileManager *fm = [NSFileManager defaultManager];
5656
NSData *tokenJson = [fm contentsAtPath:tokenPath];
5757
id jsonResponse = [SFJsonUtils objectFromJSONData:tokenJson];
58+
59+
return [TestSetupUtils authCredentialsFromJson:jsonResponse initializeSdk:YES];
60+
61+
}
62+
63+
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString {
64+
return [self populateAuthCredentialsFromString:testCredentialsJsonString initializeSdk:YES];
65+
}
66+
67+
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString initializeSdk:(BOOL)initializeSdk {
68+
69+
return [TestSetupUtils authCredentialsFromJson:[SFJsonUtils objectFromJSONString:testCredentialsJsonString] initializeSdk:initializeSdk];
70+
}
71+
72+
+ (void)synchronousAuthRefresh {
73+
[self synchronousAuthRefreshWithUserDidLoginNotification:NO];
74+
}
75+
76+
+ (void)synchronousAuthRefreshWithUserDidLoginNotification:(BOOL)postUserDidLogIn
77+
{
78+
// All of the setup and validation of prerequisite auth state is done in populateAuthCredentialsFromConfigFile.
79+
// Make sure that method has run before this one.
80+
NSAssert(credentials!=nil, @"You must call populateAuthCredentialsFromConfigFileForClass before synchronousAuthRefresh");
81+
__block SFSDKTestRequestListener *authListener = [[SFSDKTestRequestListener alloc] init];
82+
__block SFUserAccount *user = nil;
83+
[[SFUserAccountManager sharedInstance]
84+
refreshCredentials:credentials
85+
completion:^(SFOAuthInfo *authInfo, SFUserAccount *userAccount) {
86+
authListener.returnStatus = kTestRequestStatusDidLoad;
87+
user = userAccount;
88+
// Ensure tests don't change/corrupt the current user credentials.
89+
if (user.credentials.refreshToken == nil) {
90+
user.credentials = credentials;
91+
}
92+
if (postUserDidLogIn) {
93+
NSDictionary *userInfo = @{kSFNotificationUserInfoAccountKey: userAccount,
94+
kSFNotificationUserInfoAuthTypeKey: authInfo};
95+
[[NSNotificationCenter defaultCenter] postNotificationName:kSFNotificationUserDidLogIn
96+
object:[SFUserAccountManager sharedInstance]
97+
userInfo:userInfo];
98+
[[SFUserAccountManager sharedInstance] stopCurrentAuthentication:^(BOOL result){}];
99+
}
100+
} failure:^(SFOAuthInfo *authInfo, NSError *error) {
101+
authListener.lastError = error;
102+
authListener.returnStatus = kTestRequestStatusDidFail;
103+
}];
104+
[authListener waitForCompletion];
105+
[[SFUserAccountManager sharedInstance] setCurrentUserInternal:user];
106+
NSAssert([authListener.returnStatus isEqualToString:kTestRequestStatusDidLoad], @"After auth attempt, expected status '%@', got '%@'",
107+
kTestRequestStatusDidLoad,
108+
authListener.returnStatus);
109+
}
110+
111+
+ (SFOAuthCredentials *)newClientCredentials {
112+
113+
NSString *identifier = [[SFUserAccountManager sharedInstance] uniqueUserAccountIdentifier:[SFUserAccountManager sharedInstance].oauthClientId];
114+
SFOAuthCredentials *creds = [[SFOAuthCredentials alloc] initWithIdentifier:identifier clientId:[SFUserAccountManager sharedInstance].oauthClientId encrypted:YES];
115+
creds.clientId = [SFUserAccountManager sharedInstance].oauthClientId;
116+
creds.redirectUri = [SFUserAccountManager sharedInstance].oauthCompletionUrl;
117+
creds.domain = [SFUserAccountManager sharedInstance].loginHost;
118+
creds.accessToken = nil;
119+
return creds;
120+
}
121+
122+
+ (SFSDKTestCredentialsData *)authCredentialsFromJson:(id)jsonResponse initializeSdk:(BOOL)initializeSdk {
58123
NSAssert(jsonResponse != nil, @"Error parsing JSON from config file: %@", [SFJsonUtils lastError]);
59124
NSDictionary *dictResponse = (NSDictionary *)jsonResponse;
60125
SFSDKTestCredentialsData *credsData = [[SFSDKTestCredentialsData alloc] initWithDict:dictResponse];
@@ -65,12 +130,14 @@ + (SFSDKTestCredentialsData *)populateAuthCredentialsFromConfigFileForClass:(Cla
65130
nil != credsData.identityUrl &&
66131
nil != credsData.instanceUrl, @"config credentials are missing! %@",
67132
dictResponse);
68-
133+
69134
// check whether the test config file has never been edited
70135
NSAssert(![credsData.refreshToken isEqualToString:@"__INSERT_TOKEN_HERE__"],
71136
@"You need to obtain credentials for your test org and replace test_credentials.json");
72-
[SalesforceSDKManager initializeSDK];
73-
137+
if (initializeSdk) {
138+
[SalesforceSDKManager initializeSDK];
139+
}
140+
74141
// Note: We need to fix this inconsistency for tests in the long run.There should be a clean way to refresh appConfigs for tests. The configs should apply across all components that need the config.
75142
SFSDKAppConfig *appconfig = [[SFSDKAppConfig alloc] init];
76143
appconfig.oauthRedirectURI = credsData.redirectUri;
@@ -98,41 +165,4 @@ + (SFSDKTestCredentialsData *)populateAuthCredentialsFromConfigFileForClass:(Cla
98165
return credsData;
99166
}
100167

101-
+ (void)synchronousAuthRefresh
102-
{
103-
// All of the setup and validation of prerequisite auth state is done in populateAuthCredentialsFromConfigFile.
104-
// Make sure that method has run before this one.
105-
NSAssert(credentials!=nil, @"You must call populateAuthCredentialsFromConfigFileForClass before synchronousAuthRefresh");
106-
__block SFSDKTestRequestListener *authListener = [[SFSDKTestRequestListener alloc] init];
107-
__block SFUserAccount *user = nil;
108-
[[SFUserAccountManager sharedInstance]
109-
refreshCredentials:credentials
110-
completion:^(SFOAuthInfo *authInfo, SFUserAccount *userAccount) {
111-
authListener.returnStatus = kTestRequestStatusDidLoad;
112-
user = userAccount;
113-
// Ensure tests don't change/corrupt the current user credentials.
114-
if(user.credentials.refreshToken == nil) {
115-
user.credentials = credentials;
116-
}
117-
} failure:^(SFOAuthInfo *authInfo, NSError *error) {
118-
authListener.lastError = error;
119-
authListener.returnStatus = kTestRequestStatusDidFail;
120-
}];
121-
[authListener waitForCompletion];
122-
[[SFUserAccountManager sharedInstance] setCurrentUserInternal:user];
123-
NSAssert([authListener.returnStatus isEqualToString:kTestRequestStatusDidLoad], @"After auth attempt, expected status '%@', got '%@'",
124-
kTestRequestStatusDidLoad,
125-
authListener.returnStatus);
126-
}
127-
128-
+ (SFOAuthCredentials *)newClientCredentials {
129-
130-
NSString *identifier = [[SFUserAccountManager sharedInstance] uniqueUserAccountIdentifier:[SFUserAccountManager sharedInstance].oauthClientId];
131-
SFOAuthCredentials *creds = [[SFOAuthCredentials alloc] initWithIdentifier:identifier clientId:[SFUserAccountManager sharedInstance].oauthClientId encrypted:YES];
132-
creds.clientId = [SFUserAccountManager sharedInstance].oauthClientId;
133-
creds.redirectUri = [SFUserAccountManager sharedInstance].oauthCompletionUrl;
134-
creds.domain = [SFUserAccountManager sharedInstance].loginHost;
135-
creds.accessToken = nil;
136-
return creds;
137-
}
138168
@end

0 commit comments

Comments
 (0)