-
Notifications
You must be signed in to change notification settings - Fork 137
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
[Hack Week] ApiFaker: introduce new module and logic #13052
base: trunk
Are you sure you want to change the base?
Conversation
Generated by 🚫 Danger |
📲 You can test the changes from this Pull Request in WooCommerce-Wear Android by scanning the QR code below to install the corresponding build.
|
010ba46
to
5dc003f
Compare
📲 You can test the changes from this Pull Request in WooCommerce Android by scanning the QR code below to install the corresponding build.
|
8ea9816
to
c944700
Compare
07950ea
to
3f4341b
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## task/api-faker-prep #13052 +/- ##
=========================================================
- Coverage 40.33% 40.26% -0.07%
Complexity 6154 6154
=========================================================
Files 1287 1298 +11
Lines 74121 74247 +126
Branches 10140 10157 +17
=========================================================
Hits 29895 29895
- Misses 41634 41760 +126
Partials 2592 2592 ☔ View full report in Codecov by Sentry. |
3f4341b
to
7586064
Compare
dc8591c
to
350bcf2
Compare
185c0cb
to
c5117e3
Compare
c5117e3
to
9a5c208
Compare
This commit adds logic for extracting the method from the path, and also the logic to wrap the body using `data` property when needed
private val jsonObjectProvider: JSONObjectProvider | ||
) { | ||
fun fakeRequestIfNeeded(request: Request): Response? { | ||
// TODO match against method and query parameters too |
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 TODO here is not accurate, as part of this PR we are already matching against the HTTP method, the comment is fixed in next PRs, to avoid issues with merge conflicts, let's ignore it here.
val (path, method, body) = if (method == "GET") { | ||
Triple( | ||
url.queryParameter("path")!!.substringBefore("&"), | ||
url.queryParameter("_method") ?: "GET", |
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.
For Jetpack Tunneled GET requests, the HTTP method is sent as a query parameter _method
, see https://github.com/wordpress-mobile/WordPress-FluxC-Android/blob/64479b5648444c945dccae776f596daf192f4b87/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/jetpacktunnel/JetpackTunnelGsonRequest.kt#L249
This will generally be always GET
, but to avoid hardcoding it, I'm reading it from the parameter when it exists.
val jsonObject = jsonObjectProvider.parseString(originalBody) | ||
val pathParts = jsonObject.getString("path").split("&") | ||
val path = pathParts[0] | ||
val method = pathParts.firstOrNull { it.startsWith("_method") }?.split("=")?.getOrNull(1) ?: "POST" |
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.
For Jetpack Tunneled POST requests, the actual endpoint HTTP method will be past with the json property path
, see: https://github.com/wordpress-mobile/WordPress-FluxC-Android/blob/64479b5648444c945dccae776f596daf192f4b87/fluxc/src/main/java/org/wordpress/android/fluxc/network/rest/wpcom/jetpacktunnel/JetpackTunnelGsonRequest.kt#L265
url.encodedPath.trimEnd('/').matches(Regex(JETPACK_TUNNEL_REGEX)) && | ||
!startsWith("{\"data\":") | ||
) { | ||
"{\"data\": $this}" |
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.
Jetpack Tunnel sends responses wrapped in a data
property, this logic makes it easier for faking those without the wrap, which should work for both site credentials login and Jetpack login.
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 brings up the same question from above. What if we don't want to have JSON formatted body with data { }
as the root of if? Like the example mentioned above when simulating an error that returns html?
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.
As said in the other thread, I would love to keep this as is right now, changing it would require more testing.
Depending on the developer's needs, we can revisit this in the future.
@@ -2,7 +2,7 @@ | |||
|
|||
echo "--- 🧪 Testing" | |||
set +e | |||
./gradlew testJalapenoDebugUnitTest lib:cardreader:testDebugUnitTest lib:iap:testDebugUnitTest | |||
./gradlew testJalapenoDebugUnitTest testDebugUnitTest |
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 allows running unit tests for all modules instead of having to list all of them
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 @hichamboushaba, looking forward to see this in action in subsequent PRs. Code looks clean and well organised. I just left a couple minor questions, but nothing blocking.
includeGroup "org.wordpress" | ||
includeGroup "org.wordpress.fluxc" | ||
includeGroup "org.wordpress.fluxc.plugins" | ||
includeGroup "org.wordpress.wellsql" | ||
includeGroup "org.wordpress.mediapicker" | ||
includeGroup "com.automattic" | ||
includeGroup "com.automattic.tracks" |
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.
Do you need all these? I see you only add FluxC as dependency (which also makes sense since it's where all the requests happen). But why include tracks
, mediapicker
, wellsql
, wordpress
?
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 don't like this either, but I tried to just keep the same setup as what we have in the other modules, personally I think this code should be in the root build.gradle file, or better in a convention plugin, but since this is not the setup we have yet, I prefer to keep the same setup as the other modules here, as the includeGroup
statements won't have any impact here, they are just used for defining the repo itself.
.protocol(Protocol.HTTP_1_1) | ||
.message("Fake Response") | ||
.code(fakeResponse.statusCode) | ||
// TODO check if it's safe to always use JSON as the content type |
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.
Since the body of the faked request is anything the user inputs from the app, wouldn't it make more sense to leave MediaType
null? For example sometimes we get and html body for API errors. Haven't tested yet, but what happens if we keep this application/json
content type and enter non json body?
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 a good point; but for now I'll keep it like this, my rationale for hardcording application/json
here is that all of the API requests we have in the app use JSON responses, the HTML errors are an exception that can be caused by a specific site setup that returns an error before the site handles the error.
what happens if we keep this application/json content type and enter non json body?
It should work as you expect from the app, the upper layer will try to deserialize the content, and would fail, then return a JSON error, this is what happens when we receive HTML instead of JSON (for example this case: p1723565504566789/1720009283.216679-slack-C6H8C3G23 API org.json.JSONException: Value <!DOCTYPE of type java.lang.String cannot be converted to JSONObject
)
url.encodedPath.trimEnd('/').matches(Regex(JETPACK_TUNNEL_REGEX)) && | ||
!startsWith("{\"data\":") | ||
) { | ||
"{\"data\": $this}" |
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 brings up the same question from above. What if we don't want to have JSON formatted body with data { }
as the root of if? Like the example mentioned above when simulating an error that returns html?
Description
As part of my Hack Week, I'm bringing back the ApiFaker project paqN3M-SI-p2, and I'm splitting the feature into multiple PRs.
This PR adds the
apifaker
gradle module, and with it the following changes:EndpointProcessor
class is responsible for matching and finding the faked endpoints.OkHttp
interceptor that replaces the response when needed.Steps to reproduce
Code review and green CI should be enough.
But if you prefer to do some tests, you can use the following PR #13070
RELEASE-NOTES.txt
if necessary. Use the "[Internal]" label for non-user-facing changes.Reviewer (or Author, in the case of optional code reviews):
Please make sure these conditions are met before approving the PR, or request changes if the PR needs improvement: