-
-
Notifications
You must be signed in to change notification settings - Fork 365
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
Format URL encoded forms #323
Conversation
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.
Thanks for the PR. Having a test would definitely be beneficial giving the effort we're doing in increasing the coverage.
Moreover I was wondering if we should hook this feature inside the URL encode/decode button we already have. @vbuberen do you have any ideas about this?
@@ -94,6 +96,19 @@ internal object FormatUtils { | |||
} | |||
} | |||
|
|||
fun formatUrlEncodedForm(form: String): String { |
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.
Please add a test for this function.
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.
Sure, I'll also rewrite the method not to make any assumption on the input.
I thought about connecting the existing button, but I had some doubts and, honestly, I don't know why I didn't mention them. What I've just come up with is the following: I'd add a new button that allows to switch between two display modes: "raw" and "formatted". This button could be either the existing button or a new one, not sure what's better. I simply don't want to add request-specific parameters to But how should the share button behave? Would it follow the current display mode? Should it include both the formatted request and the raw request? Just the one currently displayed? |
Agree. The stored information should be unchanged. What we're addressing here is just on the presentation layer. If possible I would hook to the same button we already have on the URL side. The semantic is pretty clear as it's an encoded/decoded toggle. |
My bad, I didn't notice you were also exposing the original request body, which makes sense, and thought I had to do some invasive changes. Anyway, I hooked the existing button. I should probably implement something like |
Joining the conversation here - agree with @cortinico that we should attach this feature to an existing button. However, we might need to reevaluate UI in next PRs (I suggest to do it in first of 4.x releases). |
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.
Thanks for doing such a useful improvement. Some changes required before we can merge it.
@@ -196,6 +196,8 @@ internal class HttpTransaction( | |||
FormatUtils.formatJson(body) | |||
contentType != null && contentType.contains("xml", ignoreCase = true) -> | |||
FormatUtils.formatXml(body) | |||
contentType != null && contentType.contains("form-urlencoded", ignoreCase = true) -> |
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 see that we already have 3 checks for contentType != null
in this when
. Can I ask to you to refactor it to be something like this (or how you would prefer it):
return if (contentType.isNullOrBlank()) {
body
} else {
when {
contentType.contains("json", ignoreCase = true) -> FormatUtils.formatJson(body)
contentType.contains("xml", ignoreCase = true) -> FormatUtils.formatXml(body)
else -> body
}
}
hideProgress() | ||
} | ||
} | ||
) | ||
} | ||
|
||
private suspend fun updateUpdater( |
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 guess there is a typo and this fun meant to be named updateAdapter
?
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.
Definitely, I had a typo there and instead of fixing it my brain decided to make it worse.
val result = processPayload(type, transaction) | ||
payloadBinding.responseRecyclerView.adapter = TransactionBodyAdapter(result) | ||
payloadBinding.responseRecyclerView.setHasFixedSize(true) | ||
val encode = viewModel.encodeUrl.value ?: true |
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.
Why don't you call this variable the same way as you named parameter updateAdapter
fun?
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 I mentioned, I need to rename some stuff. I started with just the formatting of url encoded forms and ended up with more. I was wondering if I should stick with just the formatting of url encoded forms or continue with this more generic solution.
viewModel.encodeUrl.observe( | ||
viewLifecycleOwner, | ||
Observer { encode -> | ||
if (adapter.itemCount == 0) { |
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.
Why do you need this check if you also do the check for transaction value below?
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.
Definitely something I should have commented. There are cases in which transaction
is not null here (e.g., configuration change), so you end up notifying the adapter twice: once here and once when observing transaction
. Not a big deal, but it's unnecessary work.
I tried to think of a way not to have this here and the only thing I could think of is having a separate transaction
LiveData that under the hood is just a MediatorLiveData that combines transaction
and encodeUrl
, taking care of notifying observers only once the first time it's observed. It didn't look pretty and it was more convoluted than this simple check, which is just a micro-optimization and not something required. With or without this check everything would work just fine. But I don't really like the fact that this trick only works if you start observing this LiveData before the other.
menu.findItem(R.id.encode_url).isVisible = false | ||
menu.findItem(R.id.encode_url).apply { | ||
isVisible = true | ||
setOnMenuItemClickListener { |
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 options item already had click listener in TransactionActivity
and called exactly the same fun from ViewModel.
return form | ||
} | ||
form.split("&").joinToString(separator = "\n") { entry -> | ||
val split = entry.split("=") |
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.
Rename this val
please for more clarity.
transaction: HttpTransaction, | ||
formatted: Boolean | ||
) { | ||
data.clear() |
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.
In general this function shouldn't be in this fragment. It should be inside the adapter itself named something like setItems()
.
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.
Also, in case of having such thing as setItems()
function you can remove adapter initialisation with an empty list and make its declaration lazy for cases when we have empty payload.
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.
Definitely, I didn't like this to begin with and I guess I just didn't want to change too many things. I'll fix this. But I'm not sure I get exactly what you meant with your proposed solution. Are you suggesting the use of a lazy property or something else?
Here I can only see a nullable list as marginally better, given that the moment you attach the adapter to the RecyclerView its getItemCount()
gets called, so anything that creates the list lazily would do it immediately.
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, I suggest to have a lazy property and create it only when we have some result to show.
However, now I think it might be not the best idea, since it will cause logcat show No adapter attached
, like it does now.
We don't have to worry about injections, so disable HTML escaping and display the real values.
I rebased my changes and applied your suggestions. I also changed the way things work. The button now only encodes/decodes url encoded requests. I don't really like that For example, |
Agree with you. It is something I also suggested recently. Look at |
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.
LGTM 🚢
I would like to wait for another review from @cortinico as well, since he reviewed this PR 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.
LGTM, just left a couple of minor comments
return if (contentType.isNullOrBlank()) { | ||
body | ||
} else { | ||
when { | ||
contentType.contains("json", ignoreCase = true) -> FormatUtils.formatJson(body) | ||
contentType.contains("xml", ignoreCase = true) -> FormatUtils.formatXml(body) | ||
contentType.contains("x-www-form-urlencoded", ignoreCase = true) -> | ||
FormatUtils.formatUrlEncodedForm(body) | ||
else -> 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.
return if (contentType.isNullOrBlank()) { | |
body | |
} else { | |
when { | |
contentType.contains("json", ignoreCase = true) -> FormatUtils.formatJson(body) | |
contentType.contains("xml", ignoreCase = true) -> FormatUtils.formatXml(body) | |
contentType.contains("x-www-form-urlencoded", ignoreCase = true) -> | |
FormatUtils.formatUrlEncodedForm(body) | |
else -> body | |
} | |
return when { | |
contentType.isNullOrBlank() -> body | |
contentType.contains("json", ignoreCase = true) -> FormatUtils.formatJson(body) | |
contentType.contains("xml", ignoreCase = true) -> FormatUtils.formatXml(body) | |
contentType.contains("x-www-form-urlencoded", ignoreCase = true) -> | |
FormatUtils.formatUrlEncodedForm(body) | |
else -> body | |
} |
Please keep the single return when
style as it's easier to read and follow.
library/src/main/java/com/chuckerteam/chucker/internal/support/FormatUtils.kt
Show resolved
Hide resolved
if (transaction == null) { | ||
false | ||
} else { | ||
transaction.requestContentType?.contains("x-www-form-urlencoded", ignoreCase = true) ?: false | ||
} |
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.
if (transaction == null) { | |
false | |
} else { | |
transaction.requestContentType?.contains("x-www-form-urlencoded", ignoreCase = true) ?: false | |
} | |
transaction?.requestContentType?.contains("x-www-form-urlencoded", ignoreCase = true) ?: false |
@cortinico So, we are good to merge now? I see that PR is updated already. |
Yes 👍 |
📷 Screenshots
📄 Context
Reading encoded forms is nearly impossible, especially when dealing values have symbols in them. Unlike for XML and JSON requests, this does not simply pretty print the body and that may be undesired.