-
Notifications
You must be signed in to change notification settings - Fork 295
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
Add Configuration Loader #1353
Add Configuration Loader #1353
Conversation
init(http: BTHTTP) { | ||
self.http = http | ||
} | ||
|
||
deinit { | ||
http.session.finishTasksAndInvalidate() | ||
} | ||
|
||
func getConfig(_ authorization: ClientAuthorization, completion: @escaping (BTConfiguration?, Error?) -> Void) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few more MARKs we usually use, but open to if folks don't find these useful
init(http: BTHTTP) { | |
self.http = http | |
} | |
deinit { | |
http.session.finishTasksAndInvalidate() | |
} | |
func getConfig(_ authorization: ClientAuthorization, completion: @escaping (BTConfiguration?, Error?) -> Void) { | |
// MARK: - Intitializer | |
init(http: BTHTTP) { | |
self.http = http | |
} | |
deinit { | |
http.session.finishTasksAndInvalidate() | |
} | |
// MARK: - Internal Methods | |
func getConfig(_ authorization: ClientAuthorization, completion: @escaping (BTConfiguration?, Error?) -> Void) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like them, added: 418b39d
} | ||
|
||
func getConfig(_ authorization: ClientAuthorization, completion: @escaping (BTConfiguration?, Error?) -> Void) { | ||
// Fetches or returns the configuration and caches the response in the GET BTHTTP call if successful |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit - this should probably be a docstring instead of a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats a good idea, updated: 418b39d
CHANGELOG.md
Outdated
## unreleased | ||
* BraintreeCore | ||
* Add `ConfigurationLoader` class | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this doesn't need a CHANGELOG entry since it's not merchant facing and isn't really a functional change that we'll want a reference to in the future. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, updated: 646a1ef
@@ -22,6 +22,8 @@ import Foundation | |||
var graphQLHTTP: BTGraphQLHTTP? | |||
var payPalHTTP: BTHTTP? | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏 Some minor comments, but this looks great!
} catch { | ||
return nil | ||
} | ||
} | ||
|
||
let btHttp = BTHTTP(authorization: self.authorization) | ||
http = btHttp | ||
configurationLoader = ConfigurationLoader(http: btHttp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am thinking about the fact that ConfigurationCache is a singleton and thought of ConfigurationLoader as being one as well. In a weird scenario that there are multiple instances of BTAPIClient. wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something we want to do on Android too. Making ConfigurationLoader
a singleton makes the most sense for our architecture as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something that happened in MXO iOS was that almost everything was a singleton. At least in iOS, we need to define the architecture. I think most of the code was a migration from Objective-C to Swift (IMO there are several areas of opportunity that we could refactor.). For now, I think we can proceed this way and discuss whether it would be necessary to convert it to a singleton in the future. Like any design pattern, it has its pros and cons. I'm not entirely a fan because it violates the SRP, and we have to think of a creative way to mock
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@richherrera I'm 100% onboard with that concern. The one thing I noticed with MXO though is that the interface was static e.g. ConfigurationLoader.load()
. If we go for ConfigurationLoader.shared.load()
we'll preserve the flexibility needed for mocking if we inject the singleton reference in the constructor (we do this quite a bit in Android with getInstance() accessors).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, passing it in a protocol with default value in initializer makes testing possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, do you think having two singletons (Cache and Loader) is necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Necessary, not sure, but I think there might be some weirdness in possibly having multiple instances of BTAPIClient and multiple instances of Loader, single Cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sshropshire yeah DIP rules, i like that, something I’d like to know is how we could use the same instance of BTHTTP in both BTAPIClient and ConfigurationLoader. Are there two different instances in Android (for BraintreeClient and ConfigurationLoader)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for as long as there can be multiple instances of BTAPIClient, we should address those issues.
I guess with singleton ConfigurationLoader, you'd need to keep track of BTHTTP instance creation in ConfigurationLoader by authorization or something and check with the getConfiguration function.
I think maybe considering multiple instances of BTAPIClient and configurationLoader, we might need to synchronize ConfigurationCache functions as well some time.
I think we have discussed, for future, making sure that there is only one instance of BTAPIClient.
Great work on this Rich.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there two different instances in Android (for BraintreeClient and ConfigurationLoader)?
@richherrera On Android we have a single BraintreeHttpClient
instance that's shared between BraintreeClient
and ConfigurationLoader
. That would be equivalent to BTAPIClient
and ConfigurationLoader
sharing a BTHTTP
instance on iOS.
XCTAssertEqual(error as NSError, mockError) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job on these tests! Very thorough!
Summary of changes
ConfigurationLoader
object, moving all the logic from thefetchOrReturnRemoteConfiguration
method to this new object. The public method (fetchOrReturnRemoteConfiguration
) remain unchanged to avoid requiring modifications from theclients
. This will help us maintain similarities withAndroid
and isolate the logic, allowing us to better implement a mechanism to avoid multiple calls to thev1/configuration
endpoint.Checklist
Authors