Open D4no0 opened 2 years ago
You might be able to put an identifier in the session, then read it back in a controller that serves config_url
to supply the API key at runtime?
Alternatively, you can wrap the SwaggerUI
plug in your own plug that delays the init
call to runtime.
Something like:
defmodule YourAppWeb.Plug.SwaggerUIWrapper do
@behaviour Plug
def init(opts), do: opts
def call(conn, opts) do
api_key = get_api_key(conn)
opts = Keyword.put(opts, :on_complete, "function() { ui.preauthorizeApiKey('api_key', '#{api_key}') }")
opts = OpenApiSpex.Plug.SwaggerUI.init(opts)
OpenApiSpex.Plug.SwaggerUI.call(conn, opts)
end
end
If you're open to creating a PR to improve OpenApiSpex in this area, one simple way might be to include values from a Conn.assigns
or Conn.private
field as well as the compile-time config.
That way each app can create a SwaggerUIRuntimeConfig
plug to perform any custom logic, and the results will be included in the rendered UI.
Something similar to Absinthe.Plug.assign_context
Yes the wrapper plug could work nicely as a workaround.
As for PR, I think that making a plug wrapper is not explicit enough (you lose the possibility to see the configuration inside of router). I was thinking more in the direction of adding the config as a function, that will be evaluated when the plug is called.
Something like:
on_complete: fn -> "function() { ui.preauthorizeApiKey('api_key', '#{api_key}') }" end
This should be possible without any drastic changes to swaggerui plug, since the UI is built in call function, and this also will allow for dynamic configuration per request basis, the thing that a plug wrapper could not achieve.
What do you think about this?
this also will allow for dynamic configuration per request basis, the thing that a plug wrapper could not achieve.
Can you expand on that? A plug that runs prior to SwaggerUI vs a callback invoked from the SwaggerUI plug seem equivalent to me.
Callbacks could certainly work, although I don't think anonymous functions are supported, eg in the CORS plug origin
config (https://github.com/mschae/cors_plug/tree/v3.0.3#using-a-function0-or-function1-that-returns-the-allowed-origin-as-a-string)
For an app requiring complex runtime configuration of SwaggerUI, I'd consider using a Plug.Builder
along with a hypothetical OpenApiSpex.Plug.SwaggerUI.put_config/2
function to set the config on the Conn
.
defmodule MyAppWeb.SwaggerUI do
use Plug.Builder
plug :configure_swagger_ui
plug OpenApiSpex.Plug.SwaggerUI
def configure_swagger_ui(conn, opts) do
config = [
path: System.get_env("OPEN_API_PATH", "/api/openapi"),
default_model_expand_depth: 3,
display_operation_id: true,
on_complete: on_complete(conn)
]
OpenApiSpex.Plug.SwaggerUI.put_config(conn, config)
end
defp on_complete(conn) do
api_key = MyApp.Auth.get_api_key(conn.assigns.current_user)
"""
function() { ui.preauthorizeApiKey('api_key', '#{api_key}') }
"""
end
end
The router would just route to the custom plug:
get "/swaggerui", MyAppWeb.SwaggerUI
Can you expand on that? A plug that runs prior to SwaggerUI vs a callback invoked from the SwaggerUI plug seem equivalent to me.
Yes my bad, I thought that the swaggerui plug was initialized inside of init/1
in your example.
Callbacks could certainly work, although I don't think anonymous functions are supported, eg in the CORS plug origin config (https://github.com/mschae/cors_plug/tree/v3.0.3#using-a-function0-or-function1-that-returns-the-allowed-origin-as-a-string)
Wow, quite an interesting limitation, I never knew about this.
If you are OK with possibility to send functions as configuration parameters, I could make a PR to allow this.
Runtime configuration can be especially useful today to e.g. disable highlighting on a per-operation basis.
See:
Currently applicable via:
get("/swaggerui", SwaggerUI,
path: "/api/openapi",
syntax_highlight: false
)
I am using the
on_complete
option provided by swaggerui plug to preauthorize both basic auth and api key. While I can do this with the basic auth:I cannot do the same with the api key, since the tokens are generated and tracked in database, so I have to use the database to request the api key, however this is not possible because the plug options are evaluated at compile-time.
I also noticed the option
config_url
that can be passed for swaggerui to fetch configuration from an url. The problem here however is the same, because the plug options are defined in router, there is no way for me to generate a url that I will be able to serve from this server with dynamic configuration.Is there any way around this?