Skip to content

Workaround bug in android 4 for JSON objects with List<String>#942

Merged
aboedo merged 14 commits into
mainfrom
andy/sdk-3052-400-from-post-receipts-because-product
May 19, 2023
Merged

Workaround bug in android 4 for JSON objects with List<String>#942
aboedo merged 14 commits into
mainfrom
andy/sdk-3052-400-from-post-receipts-because-product

Conversation

@aboedo

@aboedo aboedo commented Apr 4, 2023

Copy link
Copy Markdown
Member

Fixes https://linear.app/revenuecat/issue/SDK-3052/400-from-post-receipts-because-product-did-not-have-quotes
https://revenuecats.atlassian.net/browse/CSDK-681

Works around the following bug in android 4:
https://stackoverflow.com/q/37317669
http://fupeg.blogspot.com/2011/07/android-json-bug.html

Essentially, this bug means that when you get a list of product_ids, instead of sending a list of strings, it sends a list with a string in it.

Before After
"product_ids": "[item1, item2]" "product_ids": ["item1, "item2"]

@aboedo aboedo self-assigned this Apr 4, 2023
@aboedo aboedo added the pr:fix A bug fix label Apr 4, 2023
@codecov

codecov Bot commented Apr 5, 2023

Copy link
Copy Markdown

Codecov Report

Merging #942 (1344e6a) into main (82e7c20) will decrease coverage by 0.63%.
The diff coverage is 33.33%.

❗ Current head 1344e6a differs from pull request most recent head 5eea593. Consider uploading reports for the commit 5eea593 to get more accurate results

@@            Coverage Diff             @@
##             main     #942      +/-   ##
==========================================
- Coverage   85.07%   84.45%   -0.63%     
==========================================
  Files         165      160       -5     
  Lines        5824     5520     -304     
  Branches      801      774      -27     
==========================================
- Hits         4955     4662     -293     
- Misses        550      561      +11     
+ Partials      319      297      -22     
Impacted Files Coverage Δ
...java/com/revenuecat/purchases/common/HTTPClient.kt 85.50% <33.33%> (-2.38%) ⬇️

... and 25 files with indirect coverage changes

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@vegaro

vegaro commented Apr 5, 2023

Copy link
Copy Markdown
Member

Btw, I found the similar issue we had in the past #144

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.

This would need to be removed I think?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

could you clarify? this was added to fix a separate but similar issue (the one that César posted, #144)

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.

We should add a comment explaining this.

@aboedo aboedo force-pushed the andy/sdk-3052-400-from-post-receipts-because-product branch 2 times, most recently from 2a95f56 to dd17b2b Compare April 6, 2023 14:49
.filterNotNullValues()
}

private fun Map<String, Any?>.convert(): JSONObject {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

moved to its own class to make testing easier and more focused

return JSONObject(inputMap)
}

/** To avoid Java type erasure, we use a Kotlin inline function with a reified parameter

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this bit was moved from HTTPClient

Comment on lines +21 to +23
if (inputMap == null) {
return JSONObject()
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

todo: make sure that this matches the previous behavior exactly.

Comment on lines +128 to +131
val jsonBody = body?.convert()
val jsonBody = body?.let { mapConverter.convertToJSON(body) }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

just double-checking, android folks: this should be equivalent, right?

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 would do

        val jsonBody = body?.let { mapConverter.convertToJSON(it) }

But yes. The difference is that with it, you are not using smart casting from kotlin. But it's the same.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

could you clarify? this was added to fix a separate but similar issue (the one that César posted, #144)

@aboedo aboedo marked this pull request as ready for review April 6, 2023 15:50
@aboedo aboedo requested review from tonidero and vegaro April 6, 2023 15:50
@aboedo

aboedo commented Apr 6, 2023

Copy link
Copy Markdown
Member Author

I won't merge until I've managed to reproduce with an instrumented test, but it should be ready to review in the meantime.

* (i.e.: "[\"value1\", \"value2\"]" instead of "[value1, value2]")
*/
@Test
fun `test map conversion fixes wrong treatment of arrays of strings in JSON library`() {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

the old code fails this test

@tonidero tonidero 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.

LGTM!

*/
internal fun convertToJSON(inputMap: Map<String, Any?>): JSONObject {
val mapWithoutInnerMaps = inputMap.mapValues { (_, value) ->
value.tryCast<Map<String, Any?>>(ifSuccess = { convertToJSON(this) })

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.

Unless I'm reading this wrong, this line doesn't do anything. It will convert to JSON but we are ignoring the return value I think? We moved this line to the else inside the when, so I think this line can be removed if I'm not wrong.

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.

yes, I was thinking the same

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

oops, leftover from refactor, thanks

.put("key1", "value1")
.put("key2", JSONArray(listOf("value2", "value3")))
.put("key3", JSONObject().put("nestedKey", "nestedValue"))
.put("key4", JSONArray(listOf("value4", "value5")))

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.

Hmm should this be a JSONObject with a JSONArray value?

@aboedo aboedo Apr 11, 2023

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

ohh oops this isn't nested 😅 Great catch!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

updated, lmk what you think!

*/
@Test
fun `test map conversion fixes wrong treatment of arrays of strings in JSON library`() {
val mapConverterMock = spyk<MapConverter>()

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.

Maybe rename to mapConverterSpy? Since it's not technically a mock

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.

ohh spyk nice, I've never used this

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not technically using it as a spy either, more like a partial mock, since I do modify the return value for some circumstances. Perhaps renaming it to that would be clearer?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

trying it this way, let me know what you think

*/
@Test
fun `test map conversion fixes wrong treatment of arrays of strings in JSON library`() {
val mapConverterMock = spyk<MapConverter>()

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.

ohh spyk nice, I've never used this

*/
internal fun convertToJSON(inputMap: Map<String, Any?>): JSONObject {
val mapWithoutInnerMaps = inputMap.mapValues { (_, value) ->
value.tryCast<Map<String, Any?>>(ifSuccess = { convertToJSON(this) })

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.

yes, I was thinking the same

Comment on lines +128 to +131
val jsonBody = body?.convert()
val jsonBody = body?.let { mapConverter.convertToJSON(body) }

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 would do

        val jsonBody = body?.let { mapConverter.convertToJSON(it) }

But yes. The difference is that with it, you are not using smart casting from kotlin. But it's the same.

@tonidero tonidero 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.

LGTM!

@tonidero

Copy link
Copy Markdown
Contributor

Ah linter failed.

@aboedo aboedo force-pushed the andy/sdk-3052-400-from-post-receipts-because-product branch from 57b8d2f to c2a7709 Compare April 12, 2023 13:03
@aboedo

aboedo commented Apr 12, 2023

Copy link
Copy Markdown
Member Author

We might wanna separate that linter step so it's clearer that it's not a test failure but rather linting, very different priorities there.
The linter actually failed on an entirely separate part of the codebase, though - I suppose it was failing on master on the commit this was based upon. I'm rebasing to see if that makes a difference.

@vegaro

vegaro commented Apr 12, 2023

Copy link
Copy Markdown
Member

@aboedo I extracted detektAll into its own job in #965

@aboedo

aboedo commented Apr 12, 2023

Copy link
Copy Markdown
Member Author

@vegaro awesome thanks!! 🙌🙌🙌

@NachoSoto

Copy link
Copy Markdown
Contributor

Is this ready to be merged?

@aboedo

aboedo commented May 19, 2023

Copy link
Copy Markdown
Member Author

oops forgot about this, yeah, it's ready to go

@aboedo aboedo merged commit c21e0c7 into main May 19, 2023
@aboedo aboedo deleted the andy/sdk-3052-400-from-post-receipts-because-product branch May 19, 2023 20:42
This was referenced May 24, 2023
tonidero added a commit that referenced this pull request May 25, 2023
**This is an automatic release.**

### New Features
* Support DEFERRED mode (#985) via swehner (@swehner)
* Add completion callback to syncPurchases API (#1002) via Toni Rico
(@tonidero)
### Bugfixes
* Workaround bug in android 4 for JSON objects with List<String> (#942)
via Andy Boedo (@aboedo)
### Dependency Updates
* Bump fastlane-plugin-revenuecat_internal from `fe45299` to `13773d2`
(#1015) via dependabot[bot] (@dependabot[bot])
### Other Changes
* Bump dokka to 1.8.10 to support Gradle 8 (#1009) via Toni Rico
(@tonidero)
* Disable offline entitlements temporarily (#1023) via Toni Rico
(@tonidero)
* Fix integration tests in CI (#1019) via Toni Rico (@tonidero)
* Add offline entitlements integration tests (#1006) via Toni Rico
(@tonidero)
* Disable offline entitlements in observer mode (#1014) via Toni Rico
(@tonidero)
* Extracts setup and teardown to BasePurchasesTest (#1011) via Cesar de
la Vega (@vegaro)
* Support forcing server errors for tests (#1008) via Toni Rico
(@tonidero)

---------

Co-authored-by: revenuecat-ops <ops@revenuecat.com>
Co-authored-by: Toni Rico <antonio.rico.diez@revenuecat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:fix A bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants