Closed cjproud closed 1 month ago
Saying that, an easy solution would be to override the parameter fields above to supply a "json payload style UI" like below instead, is that possible?
I guess the issue here is that json_or_form is a webargs shortcut that is unknown to apispec and Swagger UI.
We'd need to figure out how to specify in OpenAPI that an argument is expected in several locations and add code somewhere (flask-smorest, as apispec does not include webargs specifics) to turn json_or_form into this.
But I'm afraid apispec is not ready for this (multiple locations).
This was discussed in https://github.com/marshmallow-code/apispec/issues/548 although the conversation quickly moved off-topic.
No problem, what I'm thinking is that I'll make the route a regular json
location decorated route and have some middleware to convert the form data to json data. Do you know if that's possible?
Otherwise, could I have some form of custom parser for a json
decorated route that uses the webargs json_or_form
parser instead?
Not sure I'm following you. I think what you're doing works, it just isn't documented properly in OpenAPI. I'm not sure this is achievable, at least in a reasonable way.
You can keep your json_or_form
code and live with an incomplete doc, perhaps even tweak __location_map__
in apispec to document it as json
only so as to make SwaggerUI believe it is json so that the interactive doc works (json only, better than nothing).
Not sure I'm following you. I think what you're doing works, it just isn't documented properly in OpenAPI. I'm not sure this is achievable, at least in a reasonable way.
You can keep your
json_or_form
code and live with an incomplete doc, perhaps even tweak__location_map__
in apispec to document it asjson
only so as to make SwaggerUI believe it is json so that the interactive doc works (json only, better than nothing).
Yep that's exactly what I've done, I've added a custom parser on a new Blueprint instance that changes json_or_form
to load_json
:
from webargs.flaskparser import FlaskParser
from flask_smorest import Blueprint
class MyFlaskParser(FlaskParser):
__location_map__: dict[str, str | typing.Callable] = {
"json": "load_json",
"querystring": "load_querystring",
"query": "load_querystring",
"form": "load_form",
"headers": "load_headers",
"cookies": "load_cookies",
"files": "load_files",
"json_or_form": "load_json",
}
class MyBlueprint(Blueprint):
ARGUMENTS_PARSER = MyFlaskParser()
blp2 = MyBlueprint(
"authentication", __name__, url_prefix="/auth", description="Authentication"
)
IIUC, your code does the opposite: it modifies the mapping in webargs, so it document as json_or_form
(which won't work) and parse as json
(which excludes form
). I meant modify the mapping in apispec to document as json
only but keep the parsing of both json
and form
in webargs unchanged.
(But if I misunderstood and you're happy with your changes, then that's fine.)
IIUC, your code does the opposite: it modifies the mapping in webargs, so it document as
json_or_form
(which won't work) and parse asjson
(which excludesform
). I meant modify the mapping in apispec to document asjson
only but keep the parsing of bothjson
andform
in webargs unchanged.(But if I misunderstood and you're happy with your changes, then that's fine.)
That would be this mapping correct?
__location_map__: dict[str, str | typing.Callable] = {
"json": "load_json_or_form",
"querystring": "load_querystring",
"query": "load_querystring",
"form": "load_form",
"headers": "load_headers",
"cookies": "load_cookies",
"files": "load_files",
"json_or_form": "load_json_or_form",
}
That's what I actually did haha, apologies I made a mistake in my last code snippet :)
Hi there,
I have a route that generates a JWT and it should expect either form data or JSON data. If I supply the arguments decorator as either of these:
@blp.arguments(AuthJWTLoginArgsSchema, location="json")
@blp.arguments(AuthJWTLoginArgsSchema, location="form")
I can correctly get the username and password within the
args
of thepost
method. However, if I apply the decorator like so:@blp.arguments(AuthJWTLoginArgsSchema, location="json_or_form")
the interface looks like below and the data isn't available with the request when I make the request from the UI. Note that if I manually
curl
that endpoint with either form or json payloads it works so it seems like it's a Swagger UI issue maybe?