-
Notifications
You must be signed in to change notification settings - Fork 341
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement OpenAPI style params for PathParams
This has been extracted from a version we created for internal use. This keeps the current support for Phoenix-style params (`:id`) and adds support for OpenAPI-style params (`{id}`). Note that the test names have changed as I extracted/adapted this from a version we developed for internal use and confusion was expressed about what the tests were actually testing. --- This has been opened as a *draft* pull request because there are some corner cases that I think should be dealt with, but would prefer some collaborative guidance. 1. This *mostly* follows the Phoenix implementation for path parameters, excepting that capital letters are permitted. This matches the previous version's implementation. I would *personally* want to have different parameter identifier matching rules for `:id` vs `{id}` formats, but that gets complicated. 2. This handles "nested" path parameters for Phoenix-style *only* (e.g., `:id_post` when there is also `:id`), and it does so with applying `sort_by` on the parameter key length. There is, at the moment, an unavoidable `{to_string(name), value}` map operation, because otherwise we are doing *two* `to_string(name)` applications (once in `sort_by` and once in the `reduce`) 3. This does *not* handle "nested" OpenAPI-style parameters well (that is, `{post{id}}` or `{id{post}}`), and I'm not sure what the behaviour here should be. Strictly speaking, we should *probably* be doing this somewhat differently than the logic currently implemented *or* the previous regex-based logic: 1. The parameters should be converted to a map with string keys at all times. 2. We should *parse the URL* and apply parameters over the parts, and then re-encode the URL. Legal characters/encoding is *different* for hostnames (`https://{client}.api.com`) vs paths (`/users/{userId}`) vs query parameters (`?query={query}`), although some of the escaping for `URI.encode` does not seem to be correct for paths (for `/users/{userId}` with %{userId: "user#1"}, the substitution is `/users/user#1` which *absolutely* requires the `#` to be escaped because `#` is special, but it is not percent encoded; this is likely an indicator of a missing function (`URI.encode_path/1`?) in Elixir. 3. Parsing the URL should be *much* closer to the actual implementation of Plug.Router.Utils.parse_suffix/2, and should have separate paths for `{OpenAPI}` vs `:phoenix` parameters. My biggest worry for the above approach is that parsing the URL each time *may be expensive*. Phoenix and Plug get away with it mostly because they are building compile-time representations that get handled. It would be possible to build a `parameterized_path` macro that would pass an iodata-ish tokenized path to Tesla: ```elixir defmodule MyClient do use Tesla plug Tesla.Middleware.BaseUrl, "https://api.example.com" plug Tesla.Middleware.Logger # or some monitoring middleware plug Tesla.Middleware.PathParams import Tesla.Middleware.PathParams, only: [parameterized_path: 1] @user_path parameterized_path("/users/{id}") @user_posts_path parameterized_path("/users/:id/posts/:post_id") def user(id) do params = [id: id] get(@user_path, opts: [path_params: params]) end def posts(id, post_id) do params = [id: id, post_id: post_id] get(@user_posts_path, opts: [path_params: params]) end end ``` This would return something like `["", "users"`, {"{id}", {"id", :id}]` and looping over that looking for a tuple would let us then do something like (assuming Access) `params[elem(param_names, 0)] || params[elem(param_names, 1)]`, although we would be generating extra atoms at compile time that way. Any thoughts?
- Loading branch information
1 parent
189ffa5
commit 7f8c19b
Showing
2 changed files
with
260 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters