Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,16 @@ + (void)initialize {

+ (void)initializeSDK {
[self initializeSDKWithClass:InstanceClass];
#ifdef DEBUG
// For debug app builds only, use test instant log in if applicable.
NSArray<NSString *> *arguments = [[NSProcessInfo processInfo] arguments];
if ([arguments containsObject:@"-creds"]) {
NSString *creds = arguments[[arguments indexOfObject:@"-creds"] + 1];

[TestSetupUtils populateAuthCredentialsFromString:creds initializeSdk:NO];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bbirman - Now we have Local Dev Instant Login hosted in SalesforceSDKManager.init so we can avoid using load. Notice the two new boolean parameters on these two statements which allow TestSetupUtils to execute correctly here in initializeSDK while still running exactly the same in existing tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

[TestSetupUtils synchronousAuthRefreshWithUserDidLoginNotification:YES];
}
#endif
}

+ (void)initializeSDKWithClass:(Class)className {
Expand Down Expand Up @@ -231,6 +241,7 @@ + (instancetype)sharedManager {
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

// For dev support
Method sfsdkMotionEndedMethod = class_getInstanceMethod([UIWindow class], @selector(sfsdk_motionEnded:withEvent:));
IMP sfsdkMotionEndedImplementation = method_getImplementation(sfsdkMotionEndedMethod);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,45 @@ NS_ASSUME_NONNULL_BEGIN
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromConfigFileForClass:(Class)testClass;

/**
Performs a synchronous refresh of the OAuth credentials, which will stage the remaining auth
data (access token, User ID, Org ID, etc.) in SFUserAccountManager.
`populateAuthCredentialsFromConfigFile` is required to run once before this method will attempt
to refresh authentication.
Loads a set of auth credentials from the provided JSON string, and configures
SFUserAccountManager and the current account with the data from that JSON.
Salesforce Mobile SDK will be initialized while populating test credentials.
@param testCredentialsJsonString The test credentials JSON as a string.
@return The configuration data used to configure SFUserAccountManager (useful
e.g. for hybrid apps which need the data to bootstrap SFHybridViewController).
*/
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new method lets us reuse the non-file logic around ingesting the JSON.


/**
Loads a set of auth credentials from the provided JSON string, and configures
SFUserAccountManager and the current account with the data from that JSON.
@param testCredentialsJsonString The test credentials JSON as a string.
@param initializeSdk Indicates if Salesfore Mobile SDK should be initialized
while populating test credentials.
@return The configuration data used to configure SFUserAccountManager (useful
e.g. for hybrid apps which need the data to bootstrap SFHybridViewController).
*/
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString initializeSdk:(BOOL)initializeSdk;

/**
Performs a synchronous refresh of the OAuth credentials, which will stage the
remaining auth data (access token, User ID, Org ID, etc.) in
SFUserAccountManager. `populateAuthCredentialsFromConfigFile`
is required to run once before this method will attempt to refresh authentication.
The "user did log in" notification will not be posted.
*/
+ (void)synchronousAuthRefresh;

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

+ (SFOAuthCredentials *)newClientCredentials;

@end
Expand Down
110 changes: 70 additions & 40 deletions libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/TestSetupUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,71 @@ + (SFSDKTestCredentialsData *)populateAuthCredentialsFromConfigFileForClass:(Cla
NSFileManager *fm = [NSFileManager defaultManager];
NSData *tokenJson = [fm contentsAtPath:tokenPath];
id jsonResponse = [SFJsonUtils objectFromJSONData:tokenJson];

return [TestSetupUtils authCredentialsFromJson:jsonResponse initializeSdk:YES];

}

+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString {
return [self populateAuthCredentialsFromString:testCredentialsJsonString initializeSdk:YES];
}

+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString initializeSdk:(BOOL)initializeSdk {

return [TestSetupUtils authCredentialsFromJson:[SFJsonUtils objectFromJSONString:testCredentialsJsonString] initializeSdk:initializeSdk];
}

+ (void)synchronousAuthRefresh {
[self synchronousAuthRefreshWithUserDidLoginNotification:NO];
}

+ (void)synchronousAuthRefreshWithUserDidLoginNotification:(BOOL)postUserDidLogIn
{
// All of the setup and validation of prerequisite auth state is done in populateAuthCredentialsFromConfigFile.
// Make sure that method has run before this one.
NSAssert(credentials!=nil, @"You must call populateAuthCredentialsFromConfigFileForClass before synchronousAuthRefresh");
__block SFSDKTestRequestListener *authListener = [[SFSDKTestRequestListener alloc] init];
__block SFUserAccount *user = nil;
[[SFUserAccountManager sharedInstance]
refreshCredentials:credentials
completion:^(SFOAuthInfo *authInfo, SFUserAccount *userAccount) {
authListener.returnStatus = kTestRequestStatusDidLoad;
user = userAccount;
// Ensure tests don't change/corrupt the current user credentials.
if (user.credentials.refreshToken == nil) {
user.credentials = credentials;
}
if (postUserDidLogIn) {
NSDictionary *userInfo = @{kSFNotificationUserInfoAccountKey: userAccount,
kSFNotificationUserInfoAuthTypeKey: authInfo};
[[NSNotificationCenter defaultCenter] postNotificationName:kSFNotificationUserDidLogIn
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two lines allow TestSetupUtils to stop the default login from being shown and deliver the test app directly to its authenticated view.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That matches the behavior for Local Dev Instant Login for Android.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is similar notification login in the user account manager, but its only a few lines and not visible to this scope, so I thought to leave that alone and replicate the notification here so we minimize authentication regression risk. All the changes in this pull request are non-production code so far.

object:[SFUserAccountManager sharedInstance]
userInfo:userInfo];
[[SFUserAccountManager sharedInstance] stopCurrentAuthentication:^(BOOL result){}];
}
} failure:^(SFOAuthInfo *authInfo, NSError *error) {
authListener.lastError = error;
authListener.returnStatus = kTestRequestStatusDidFail;
}];
[authListener waitForCompletion];
[[SFUserAccountManager sharedInstance] setCurrentUserInternal:user];
NSAssert([authListener.returnStatus isEqualToString:kTestRequestStatusDidLoad], @"After auth attempt, expected status '%@', got '%@'",
kTestRequestStatusDidLoad,
authListener.returnStatus);
}

+ (SFOAuthCredentials *)newClientCredentials {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method did not change. This method didn't change, though Github marked it as a diff due to other logic moving.


NSString *identifier = [[SFUserAccountManager sharedInstance] uniqueUserAccountIdentifier:[SFUserAccountManager sharedInstance].oauthClientId];
SFOAuthCredentials *creds = [[SFOAuthCredentials alloc] initWithIdentifier:identifier clientId:[SFUserAccountManager sharedInstance].oauthClientId encrypted:YES];
creds.clientId = [SFUserAccountManager sharedInstance].oauthClientId;
creds.redirectUri = [SFUserAccountManager sharedInstance].oauthCompletionUrl;
creds.domain = [SFUserAccountManager sharedInstance].loginHost;
creds.accessToken = nil;
return creds;
}

+ (SFSDKTestCredentialsData *)authCredentialsFromJson:(id)jsonResponse initializeSdk:(BOOL)initializeSdk {
NSAssert(jsonResponse != nil, @"Error parsing JSON from config file: %@", [SFJsonUtils lastError]);
NSDictionary *dictResponse = (NSDictionary *)jsonResponse;
SFSDKTestCredentialsData *credsData = [[SFSDKTestCredentialsData alloc] initWithDict:dictResponse];
Expand All @@ -65,12 +130,14 @@ + (SFSDKTestCredentialsData *)populateAuthCredentialsFromConfigFileForClass:(Cla
nil != credsData.identityUrl &&
nil != credsData.instanceUrl, @"config credentials are missing! %@",
dictResponse);

// check whether the test config file has never been edited
NSAssert(![credsData.refreshToken isEqualToString:@"__INSERT_TOKEN_HERE__"],
@"You need to obtain credentials for your test org and replace test_credentials.json");
[SalesforceSDKManager initializeSDK];

if (initializeSdk) {
[SalesforceSDKManager initializeSDK];
}

// 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.
SFSDKAppConfig *appconfig = [[SFSDKAppConfig alloc] init];
appconfig.oauthRedirectURI = credsData.redirectUri;
Expand Down Expand Up @@ -98,41 +165,4 @@ + (SFSDKTestCredentialsData *)populateAuthCredentialsFromConfigFileForClass:(Cla
return credsData;
}

+ (void)synchronousAuthRefresh
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method didn't change, though Github marked it as a diff due to other logic moving.

{
// All of the setup and validation of prerequisite auth state is done in populateAuthCredentialsFromConfigFile.
// Make sure that method has run before this one.
NSAssert(credentials!=nil, @"You must call populateAuthCredentialsFromConfigFileForClass before synchronousAuthRefresh");
__block SFSDKTestRequestListener *authListener = [[SFSDKTestRequestListener alloc] init];
__block SFUserAccount *user = nil;
[[SFUserAccountManager sharedInstance]
refreshCredentials:credentials
completion:^(SFOAuthInfo *authInfo, SFUserAccount *userAccount) {
authListener.returnStatus = kTestRequestStatusDidLoad;
user = userAccount;
// Ensure tests don't change/corrupt the current user credentials.
if(user.credentials.refreshToken == nil) {
user.credentials = credentials;
}
} failure:^(SFOAuthInfo *authInfo, NSError *error) {
authListener.lastError = error;
authListener.returnStatus = kTestRequestStatusDidFail;
}];
[authListener waitForCompletion];
[[SFUserAccountManager sharedInstance] setCurrentUserInternal:user];
NSAssert([authListener.returnStatus isEqualToString:kTestRequestStatusDidLoad], @"After auth attempt, expected status '%@', got '%@'",
kTestRequestStatusDidLoad,
authListener.returnStatus);
}

+ (SFOAuthCredentials *)newClientCredentials {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method didn't change, though Github marked it as a diff due to other logic moving.


NSString *identifier = [[SFUserAccountManager sharedInstance] uniqueUserAccountIdentifier:[SFUserAccountManager sharedInstance].oauthClientId];
SFOAuthCredentials *creds = [[SFOAuthCredentials alloc] initWithIdentifier:identifier clientId:[SFUserAccountManager sharedInstance].oauthClientId encrypted:YES];
creds.clientId = [SFUserAccountManager sharedInstance].oauthClientId;
creds.redirectUri = [SFUserAccountManager sharedInstance].oauthCompletionUrl;
creds.domain = [SFUserAccountManager sharedInstance].loginHost;
creds.accessToken = nil;
return creds;
}
@end
Loading