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

Add Configuration Loader #1353

Merged
merged 12 commits into from
Jul 8, 2024
Merged

Add Configuration Loader #1353

merged 12 commits into from
Jul 8, 2024

Conversation

richherrera
Copy link
Contributor

Summary of changes

  • Refactor the existing logic by creating a ConfigurationLoader object, moving all the logic from the fetchOrReturnRemoteConfiguration method to this new object. The public method (fetchOrReturnRemoteConfiguration) remain unchanged to avoid requiring modifications from the clients. This will help us maintain similarities with Android and isolate the logic, allowing us to better implement a mechanism to avoid multiple calls to the v1/configuration endpoint.

Checklist

  • Added a changelog entry

Authors

@richherrera richherrera requested a review from a team as a code owner July 2, 2024 23:24
Comment on lines 11 to 19
init(http: BTHTTP) {
self.http = http
}

deinit {
http.session.finishTasksAndInvalidate()
}

func getConfig(_ authorization: ClientAuthorization, completion: @escaping (BTConfiguration?, Error?) -> Void) {
Copy link
Contributor

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

Suggested change
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) {

Copy link
Contributor Author

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
Copy link
Contributor

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

Copy link
Contributor Author

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
Comment on lines 3 to 6
## unreleased
* BraintreeCore
* Add `ConfigurationLoader` class

Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

Copy link
Contributor

@scannillo scannillo left a 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)
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor Author

@richherrera richherrera Jul 3, 2024

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

Copy link
Contributor

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).

Copy link
Contributor

@KunJeongPark KunJeongPark Jul 3, 2024

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.

Copy link
Contributor Author

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?

Copy link
Contributor

@KunJeongPark KunJeongPark Jul 3, 2024

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.

Copy link
Contributor Author

@richherrera richherrera Jul 3, 2024

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)?

Copy link
Contributor

@KunJeongPark KunJeongPark Jul 5, 2024

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.

Copy link
Contributor

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)
}
}
}
Copy link
Contributor

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!

@richherrera richherrera merged commit d93bd06 into main Jul 8, 2024
6 of 7 checks passed
@richherrera richherrera deleted the configuration-loader-object branch July 8, 2024 15:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants