Skip to content

Validate API key#542

Merged
tonidero merged 4 commits into
mainfrom
csdk-57
Jun 14, 2022
Merged

Validate API key#542
tonidero merged 4 commits into
mainfrom
csdk-57

Conversation

@tonidero

@tonidero tonidero commented Jun 7, 2022

Copy link
Copy Markdown
Contributor

Description

Android part of https://revenuecats.atlassian.net/browse/CSDK-57

With this change we log a series of situations:

  • When a user introduces an API key with an old format, we log a debug message prompting them to update
  • When a user introduces an API key that is not from Amazon or Google, we log an error
  • When a user introduces an API key that is from Google but configures the SDK for amazon, we log an error
  • When a user introduces an API key that is from Amazon but configures the SDK for google, we log an error

This behavior was tested using unit tests and manually through the purchase-tester app

TODO

Note that this is based on #543

@tonidero tonidero requested a review from a team June 7, 2022 10:50
Comment thread common/src/main/java/com/revenuecat/purchases/common/APIKeyValidator.kt Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As mentioned, currently I'm just logging these cases. Also lmk if there are other ways to log these messages.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Since this is inside a static function, we could pass this as a parameter to the function, but considering that's a public API, I preferred to leave it like this... It also looks like we don't have tests for this function as of yet so something to think about.

I perform the validation as early as possible, after some other validations

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤔 we don't have a visibleForTesting alternative of the configure, but we do have one for the initializer.
Maybe we could have the apiKeyValidator as a property of Purchases and pass it into the initializer?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It also looks like we don't have tests for this function as of yet so something to think about

Filed as https://revenuecats.atlassian.net/browse/CSDK-101, let's take care of it once this PR gets merged!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This could be abstracted to a @Before block... I just wasn't sure about adding one just for this. We could also use parameterized tests for these tests, but I don't like them as much since you lose the custom test name (and not sure if they are available in JUnit4..)

@aboedo aboedo Jun 7, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you just volunteered to upgrade to JUnit 5 😈

@aboedo aboedo Jun 7, 2022

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you just volunteered to upgrade to JUnit 5 😈

Comment thread common/src/main/java/com/revenuecat/purchases/common/APIKeyValidator.kt Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤔 we don't have a visibleForTesting alternative of the configure, but we do have one for the initializer.
Maybe we could have the apiKeyValidator as a property of Purchases and pass it into the initializer?

Comment thread common/src/main/java/com/revenuecat/purchases/common/APIKeyValidator.kt Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It also looks like we don't have tests for this function as of yet so something to think about

Filed as https://revenuecats.atlassian.net/browse/CSDK-101, let's take care of it once this PR gets merged!

@aboedo

aboedo commented Jun 7, 2022

Copy link
Copy Markdown
Member

Looking great @tonidero! I left a few comments regarding testing, and we should update the prefix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The API key wasn't exposed to the Purchases object itself. I thought about passing it to the constructor of the APIKeyValidator so we wouldn't need to pass it to Purchases, but it felt like that was a function parameter and not a class property. Lmk if you think otherwise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if this is a smell. Maybe it means that the validation should be outside by the configure method like in iOS? Then you wouldn't need to inject these 2 things in here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right, on a first iteration I had it there. However, the configure method is not currently tested in Android, which would leave a gap on our test to make sure that the validation actually happened. After discussing with Andy, he suggested moving the validation to the purchases constructor, which would allow for easier testing than on the configure method.

Having said that, I agree it's not ideal having to add 2 extra parameters only for this and we have already created a task to add tests for the configure method (https://revenuecats.atlassian.net/browse/CSDK-101). So we could potentially move this back there after those tests have been added. I'm working on this task today, so we can hold this to after that's done

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Did the refactor for the configure method in #543 and made this PR point to that one. Lmk what you think!

Comment thread purchases/src/test/java/com/revenuecat/purchases/PurchasesTest.kt Outdated
Comment thread purchases/src/test/java/com/revenuecat/purchases/PurchasesTest.kt Outdated
@tonidero tonidero requested a review from aboedo June 7, 2022 15:00
@aboedo aboedo requested a review from vegaro June 7, 2022 15:10
Comment thread purchases/src/test/java/com/revenuecat/purchases/PurchasesTest.kt Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

good touch on the \ns

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In all honesty, I copied the message from iOS, but yeah, it improves readability I would say 👍

Comment thread common/src/main/java/com/revenuecat/purchases/common/APIKeyValidator.kt Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I need to double check something with you... If we don't make this class internal, is it accessible for devs? I don't think so right? They would have to do implementation on the common library, or we would have to be doing api on common from the purchases module, right?

Sorry I am asking this but I always get confused haha

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No worries! And that's correct, devs shouldn't have access to this class since purchases uses implementation on common. Devs shouldn't have access to it unless we used api instead of implementation on the purchases module.

Comment thread common/src/main/java/com/revenuecat/purchases/common/APIKeyValidator.kt Outdated
Comment thread common/src/main/java/com/revenuecat/purchases/common/APIKeyValidator.kt Outdated
@tonidero tonidero changed the base branch from main to csdk-101 June 8, 2022 09:35
@tonidero tonidero requested review from NachoSoto and vegaro June 8, 2022 09:54
"configure the SDK to use Google.\nSee https://rev.cat/auth for more details."
const val INVALID_API_KEY = "The specified API Key is not recognized.\n" +
"Ensure that you are using the public app-specific API key, " +
" which should look like 'goog_1a2b3c4d5e6f7h' or 'amzn_1a2b3c4d5e6f7h'.\n" +

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It looks like we have a double space here - one after the comma, one before the next line

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good eye! @NachoSoto I copied this message from iOS, so we probably would need to fix this there as well.

@NachoSoto NachoSoto left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Much cleaner! I love it.


require(context.applicationContext is Application) { "Needs an application context." }

apiKeyValidator.validateAndLog(apiKey, store)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

😍

OTHER_PLATFORM
}

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Whoa 🤯

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This annotation is really useful since it gives you warnings if you try to go against the marked visibility. Though I think we should avoid using it as much as possible, since I believe we should test classes only with their public API. In this case, since the side effects of the public API are only logging, this was much easier though.

Comment on lines +8 to +9
private const val GOOGLE_API_KEY_PREFIX = "goog_"
private const val AMAZON_API_KEY_PREFIX = "amzn_"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can these be namespaced inside of APIKeyValidator? Not sure if that's the Kotlin thing to do though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So in order to make this a compile time constant, the options are to declare it as a top level constant (like this) or in a companion object inside the class. Both would work, but the companion object approach is slightly dirtier IMO.

If we had to make a public compile time constant and tie it to the class, then using a companion object would be the way to go but for
a private one I prefer this approach. Don't know of any best practices about this though, but this answer thinks the same way I do https://stackoverflow.com/a/49977253

@tonidero

Copy link
Copy Markdown
Contributor Author

Bump to this for review @aboedo @beylmk (this is based on #543)

@tonidero tonidero requested review from aboedo and beylmk June 10, 2022 16:10

@aboedo aboedo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

:chefskiss:

@tonidero

Copy link
Copy Markdown
Contributor Author

Will hold merging this until #543 is reviewed as well, since this is based on that PR @aboedo

Base automatically changed from csdk-101 to main June 14, 2022 14:48
@tonidero tonidero merged commit 75ac90f into main Jun 14, 2022
@tonidero tonidero deleted the csdk-57 branch June 14, 2022 15:19
@tonidero tonidero mentioned this pull request Jun 22, 2022
1 task
@tonidero tonidero mentioned this pull request Jul 7, 2022
@aboedo aboedo mentioned this pull request Jul 13, 2022
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