-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
UI: [BUGFIX] Decode/encode urls #5206
Conversation
In 858b05f#diff-46ef88aa04507fb9b039344277531584 we removed encoding values in pathnames as we thought they were eventually being encoded by `ember`. It looks like this isn't the case. This commit adds url encoding/decoding 'around' ember so the end user doesn't have to think about it. This is done in 2 places, in addition to the reverting of the above commit. Firstly we add url encoding to the `{{href-to}}` helper/addon. The reasoning here is, if we are asking for a 'href/url' then whatever we receive back should be urlencoded. We've done this by reusing as much code from the original `ember-href-to` addon as possible, after this change every call to the `{{href-to}}` helper will be urlencoded. We also added the ability to pass arrays as the 'slug' here for if you need to pass the helper something with slashes in them (for example kv keys). If you pass an array, every value in the array will be url encoded and then joined by a slash to produce the url with url encoded parts. As all links using `{{href-to}}` are now properly urlencoded. We also need to decode them in the correct place 'on the other end', so.. Secondly, we override the default `Route.paramsFor` method to decode all params before passing them to the `Route.model` hook. Lastly (the above revert), as we almost consistently use url params to construct API calls, we make sure we re-encode any slugs that have been passed in by the user/developer. The original API for the `createURL` function was to allow you to pass values that didn't need encoding, values that **did** need encoding, followed by query params (which again require url encoding) All in all this should make the entire ember app url encode/decode safe.
I've just been doubled checking all this again, and it seems like there is more to it than I originally thought. It looks like ember does encode paths sometimes. I need to look into this further before this gets merged but it looks like if you specify what ember calls a 'dynamic segment' in your router it will encode it for you. If you use 'wildcard/globbing' then it won't. I personally haven't seen documentation that explains this so I could be wrong. We are currently using ember 2.18, and I am aware that there has been work on the routing system in ember in later versions, so it may or may not have been addressed there. I'm assuming this is down to being able to specify a parameter of your route configuration as something containing a '/' without that getting encoded. In our case historically we have always required this for our KV store which uses slashes for the key names. We also fairly recently added this to the service routes when we realized people where using slashes in their service names also. You can see this in the following 2 links: Line 15 in 59c010e
Lines 29 to 32 in 59c010e
This means that as a template editor if you create a 'href-to' something in your templates sometimes the parameters passed to it will be url encoded and sometimes they won't, depending on the configuration you have in your router.js file. Right now I've traced the code back to here: Without knowing the reasons behind this code, ideally this would split the 'star' param by slash and then still encode all the parts between the slashes (to encode things like consul/ui-v2/app/utils/createURL.js Line 15 in d86e753
I'm still looking into this as I'm not 100% sure I'm correct, but I doubt the PR here is the entire solution, if even the ideal way to go about this (quick ping to @Anricx so you know this isn't complete). I would like url encoding issues to be 100% transparent to the user of the framework - it's something that as a framework user I don't want to have to worry about, so I'm going to see if I can amend the PR here to take into account my findings today. On my travels I also noticed other areas that might cause similar confusion here: Unless I'm mistaken here sometimes keynames will get encoded and sometimes they won't, there may be other areas, but incase anyone else hits upon any similar problems this is where to look. |
P.S. It looks like I can make it never encode any path segments using:
..which would give me consistency and would mean that the rest of the code in this PR would work as expected. Right now it looks like whilst this static variable is exposed via the The alternative would be to go with my initial thought and try to detect whether a route has been defined with a dynamic segment or a wildcard segment and then encode/skip encoding depending on that. First glance at this seemed complicated. @meirish @DingoEatingFuzz any suggestions/recommendations here? I might be getting too far into a rabbit hole and there is a simpler approach? We could just stop making KV urls look nice and use %2F's everywhere instead, but I'd rather avoid that if possible. I suppose I could just turn all my router definitions into wildcard ones? Not sure what impact that might have now or in the future though. (edit: bad idea 😄 ) |
Hi @johncowen sorry for the delay on this - I'm wondering if instead of trying to do this application-wide, maybe you do it locally in the href when you know there's going to be a splat or where you know you're allowing slashes. Currently you said it's just in KV ids and services and I see there's some template helpers where the kv is passed to It seems like splats aren't common enough to worry about the general purpose solution and disabling encoding globally feels like a really big hammer. I also think it might get tricky / impossible if you ever needed more than one wildcard in various URL patterns. |
Hey @meirish No prob! Yeah I think I agree with you there on the really big hammer thing. I think that little niggle in my head was what made me come back here and triple check things, it's potentially one of those changes that affects the entire UI. I got a 100% working thing together yesterday, but it works 'above' the issue. It doesn't disable anything globally, which is better, but as I couldn't find a path within ember to figure out whether the route is a dynamic one or a wildcard one, I worked something over ember that let me figure that out and encode or not depending on the route type. I was thinking last night that I'm pretty sure I always need to pass an array of a '/' split key name when I'm working with KVs, so I could detect that and only encode those when I do that, which might be better than what I did yesterday. I'm not entirely sure if that will work for services though - it might/should I think, I'm going to try another branch with that I think and see if that works out. Thanks for the advice, I may ping you later for a sanity check on this. John |
This approach adds a more declarative route configuration, above the usual ember `Router.map`. This means that I can reuse this route configuration to figure out the type of route within the href-to helper, _without_ having to figure out how I can get to it with ember only methods (I'm not even sure doing that is possible)
A bit more info on this, I was looking into why the service route was a wildcard route, and it looks as if it's come from the v1 ui refactor: consul/ui/javascripts/app/router.js Line 24 in 8c87238
I'm thinking that making this a dynamic route would be best, rather than a wildcard route instead of the change we made here: That would mean that the URLs for services with slashes in them would look like:
That would be a change to how the URLs are constructed currently (and I think where being constructed in the v1 ui)
That would only leave us with wildcards being used for KVs, meaning we can potentially restrict all of this to KVs only (including the extra I still would ideally like this to happen automatically within the framework, without having to take into account whether a route is a wildcard or not, I'm just not sure considering everything thats the correct thing to do. @meirish would love to get a short chat in with you today at somepoint if possible, will ping you in a bit |
This PR is getting a little intricate so I'm going to 'briefly' sum up here: The ProblemThe ember framework we use doesn't always deal with URL encoding/decoding for you. Sometimes, when using dynamic routes e.g. We currently use wildcard routes for within the Services and the KV areas of the UI, therefore these areas currently have URL encoding bugs as we weren't aware of this undocumented detail. Possible solutions
We've chosen option 4. Potential SolutionWe define our routes in a declarative format (for the moment at least JSON) outside of Partly related to this is a decision on whether we urlencode the slashes within service names or not. Whilst historically we haven't done this, we feel its a good time to change this behaviour, so we'll also be changing services to use dynamic routes instead of wildcard routes. So service links will then look like We have a solution already for this and everything seems to work as planned, and after speaking to @meirish and going over the possible solutions - I feel this is the best 'route' 😄 :dadjoke: I'm going to clean up a little and make sure I'm as happy as I can be with the code and push onto the end of this PR - whoever gets here, thanks for getting through it all! |
I've added a further commit onto the end of here that implements what is explained above. Few things to note:
Lastly, there are a couple of things I'd like to add here, but probably in a future PR.
Lastly, please feel free to ask if any of this isn't clear, appreciate this entire PR might be a little difficult to follow now. @meirish @DingoEatingFuzz this is ready for a review again now. |
I decided that the I decided this based on the fact that I want url encoding/decoding to be entirely transparent to the user/engineer/template author, the slash splitting/encoding/joining should be done in the helper. They shouldn't have to think 'oh I need to split the string into an array here as the route is a wildcard'. The url-encode util I'd previously written to cope with strings or multidimensional arrays is now no longer needed so I deleted that plus its tests here also. Sorry definitely finished with this for the moment! Apologies for the messy PR! |
} | ||
this.route(item, options, cb); | ||
}); | ||
if (typeof routes.index === 'undefined') { |
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 branch isn't necessary - Ember will create index routes for you if nest callbacks.
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 think I added this as it's needed from the other side i.e. if I try isWildcard('kv.index')
it needs to return false
, not throw. Come to think of it, I think it would be caught anyway. I added a try/catch to catch any other auto routes I wasn't aware of, the application
route also caught me out whilst testing, so I added this then. I'd rather not have a try/catch but I don't know enough about ember to be sure I auto add all its auto created routes.
Just considering whether I'd remove it now I've realized that, even from the other side, any error would be caught.
I think if anything I'd rather replicate ember's 'auto' routes in in the JSON blob rather than catch them - if anything I'd rather automatically add application
in here also if you haven't defined it.
@@ -17,5 +17,5 @@ Feature: dc / services / show-with-slashes: Show Service that has slashes in its | |||
Then the url should be /dc1/services | |||
Then I see 1 service model | |||
And I click service on the services | |||
Then the url should be /dc1/services/hashicorp/service/service-0 | |||
Then the url should be /dc1/services/hashicorp%2Fservice%2Fservice-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.
A little sad about encoded slashes in the URL, but get why it's there.
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.
Yeah same here. The only way I can make myself feel better about it is that slashes in service names aren't as 'natural feeling' as they are for KV, so I figure it's 'ok' here. I think here it's even possibly the 'correct' thing to do. Kind of like waving your kids off to university - sad but inside you know it's best in the long run 😂
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.
Nice work digging around to get to the bottom of this one! I left a comment about one branch that's not necessary in the walk function. I know we talked about this and you're pretty set on doing things like this in one place, but I thought of a couple of things that may make this turn into long-term technical debt instead of a "all in one place" solution.
These are both hypotheticals but I thought they were worth raising for thinking about how the application will grow in the future:
- If you ever have to split the app apart using ember-engines, and you have routes in your engines, it may prove difficult to continue defining routes as JSON (caveat: I've not looked into this too deeply, I'm just assuming that it wouldn't come for free / would require more integration at the engine layer).
- This couples the app's routing functionality to the usage of
href-to
and I wanted to note that both Nomad and Vault had to removehref-to
when we started using query params more heavily (I think it was when both apps added namespaces). If you ever need to remove it, you'll have to rework this as well.
For both of these reasons, if you want to, I think it may still be worth looking at a model param or template helper that passes encoded values to the link-to / href-to helper (possibly mixed with usage of the serialize
route hook). I understand that you may not want to do that though since this works and is nicely packaged up (code looks 👌✨), but just wanted to mention the above as something to ponder 🤔 before moving ahead!
Hey! Cool, thanks for the review and for sharing potential problems with this. I'd definitely not thought of the impact of using engines. I think overall, it would be relatively simple (famous last words!) to revert this entire thing and approach it differently (whether thats making changes just in the KV templates or something else). I included a commented out Apart from that:
I just had another look at I'll reply inline to the other stuff, and probably give this entire thing a bit more of a tyre-kick before I merge it in later today, thanks again for looking at this short notice 🙌 |
😂 Just found a tiny problem with this! Doesn't affect users though, only engineers.
There is a |
export default Router; | ||
// settings: { | ||
// _options: { path: '/setting' }, | ||
// }, |
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.
@kaxcode here's that thing you spotted yesterday! Notice I must have mistakenly changed it from settings
to setting
at some point 😬 !! I'm still trying to remember/find why its commented out here and not commented out currently.
I want to bounce around ideas for changing this anyway, ideally the URL would be /~nspace/dc-1/somewhere/in/the/ui#settings
as the settings page is almost a modal, I remember trying this at the very start and ember didn't like using #
s in URLs. I have bigger ember muscles now though so maybe we could try this.
Another option would be to treat the settings page in the same way that we treat all the other URLs so just /~nspace/dc-1/settings
. That way we keep all the info we need in the URL and not in localStorage.
Lets chat later
In 858b05f#diff-46ef88aa04507fb9b039344277531584
we removed encoding values in pathnames as we thought they were
eventually being encoded by
ember
. It looks like this isn't the case.This commit adds url encoding/decoding 'around' ember so the end user
doesn't have to think about it. This is done in 2 places, in addition to
the reverting of the above commit.
Firstly we add url encoding to the
{{href-to}}
helper/addon. Thereasoning here is, if we are asking for a 'href/url' then whatever we
receive back should be urlencoded. We've done this by reusing as much
code from the original
ember-href-to
addon as possible, after thischange every call to the
{{href-to}}
helper will be urlencoded. Wealso added the ability to pass arrays as the 'slug' here for if you need
to pass the helper something with slashes in them (for example kv keys).
If you pass an array, every value in the array will be url encoded and
then joined by a slash to produce the url with url encoded parts.
As all links using
{{href-to}}
are now properly urlencoded. We alsoneed to decode them in the correct place 'on the other end', so..
Secondly, we override the default
Route.paramsFor
method to decode allparams before passing them to the
Route.model
hook.Lastly (the above revert), as we almost consistently use url params to
construct API calls, we make sure we re-encode any slugs that have been
passed in by the user/developer. The original API for the
createURL
function was to allow you to pass values that didn't need encoding,
values that did need encoding, followed by query params (which again
require url encoding)
All in all this should make the entire ember app url encode/decode safe.
Unit tests are added for the underlying functionality, and acceptance tests are
added for KV's only for the moment. I'll probably add more acceptance testing
across other models once this has been approved, probably on top of this
branch.
Fixes #5194