Open markchoward7 opened 1 year ago
Continuing to work on this, I am getting closer to finding a working solution, although it requires an update to APIDocsView
class.
I can set endpoint
on the swagger_config
objects to get past the original error. This causes a new error in the APIDocsView
where it can't find the appropriate view because it will look for flassger.static
instead of <endpoint>.static
.
Updating that is a simple fix, which prevents the application from erroring out, but both hosted docs show all endpoints instead of just their respective endpoints. This is true even if I remove all code relating to the v2_spec
, v2_template
, and v2_swagger_config
. Using the config["specs"][0]["rule_filter"]
fixes it while the v2 code is still removed, but when it comes back then it is back to showing all endpoints.
For reference this is roughly the code I am trying:
v1_spec = APISpec(title="myapp", version="1", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()]
v2_spec = APISpec(title="myapp", version="2", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()]
with app.test_request_context():
for rule in app.url_map.iter_rules():
if "v1" in rule.endpoint:
v1_spec.path(view=app.view_functions[rule.endpoint])
if "v2" in rule.endpoint:
v2_spec.path(view=app.view_functions[rule.endpoint])
v1_template = v1_spec.to_flasgger(app)
v2_template = v2_spec.to_flasgger(app)
v1_swagger_config = Swagger.DEFAULT_CONFIG
v1_swagger_config["specs_route"] = "/api/v1"
v1_swagger_config["endpoint"] = "flasgger-v1"
v1_swagger_config["specs"][0]["rule_filter"] = lambda rule: "v1" in rule.endpoint
Swagger(app, template=v1_template, config=v1_swagger_config)
v2_swagger_config = Swagger.DEFAULT_CONFIG
v2_swagger_config["specs_route"] = "/api/v2"
v2_swagger_config["endpoint"] = "flasgger-v2"
v2_swagger_config["specs"][0]["rule_filter"] = lambda rule: "v2" in rule.endpoint
Swagger(app, template=v2_template, config=v2_swagger_config)
And the update for APIDocsView
is simply replacing any of the references to "flasgger.static"
to "{0}.static".format(base_endpoint)
With some more config options, it gets closer. Setting ["specs"][0]["endpoint"]
uniquely for each as well as ["specs"][0]["route"]
(which seems to have to start with apispec_
). This appears to properly separate the endpoints, but the second Swagger
call seems to overwrite the first as either endpoint I go to will only return the second's spec.
v1_spec = APISpec(title="myapp", version="1", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()]
v2_spec = APISpec(title="myapp", version="2", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()]
with app.test_request_context():
for rule in app.url_map.iter_rules():
if "v1" in rule.endpoint:
v1_spec.path(view=app.view_functions[rule.endpoint])
if "v2" in rule.endpoint:
v2_spec.path(view=app.view_functions[rule.endpoint])
v1_template = v1_spec.to_flasgger(app)
v2_template = v2_spec.to_flasgger(app)
v1_swagger_config = Swagger.DEFAULT_CONFIG
v1_swagger_config["specs_route"] = "/api/v1"
v1_swagger_config["endpoint"] = "flasgger-v1"
v1_swagger_config["specs"][0]["rule_filter"] = lambda rule: "v1" in rule.endpoint
v1_swagger_config["specs"][0]["endpoint"] = "apispec_v1"
v1_swagger_config["specs"][0]["route"] = "/apispec_v1.json"
Swagger(app, template=v1_template, config=v1_swagger_config)
v2_swagger_config = Swagger.DEFAULT_CONFIG
v2_swagger_config["specs_route"] = "/api/v2"
v2_swagger_config["endpoint"] = "flasgger-v2"
v2_swagger_config["specs"][0]["rule_filter"] = lambda rule: "v2" in rule.endpoint
v2_swagger_config["specs"][0]["endpoint"] = "apispec_v2"
v2_swagger_config["specs"][0]["route"] = "/apispec_v2.json"
Swagger(app, template=v2_template, config=v2_swagger_config)
After some more investigation, looks like the second Swagger
instance was overriding the first's config. I tried to fix it using .copy()
and dict()
, but it seemed to overwrite either way. I ended up fixing it by just declaring each config as its own dictionary entirely.
The following code, along with my changes to APIDocsView (PR incoming) works:
v1_spec = APISpec(title="myapp", version="1", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()]
v2_spec = APISpec(title="myapp", version="2", openapi_version="3.0.2", plugins=[FlaskPlugin(), MarshmallowPlugin()]
with app.test_request_context():
for rule in app.url_map.iter_rules():
if "v1" in rule.endpoint:
v1_spec.path(view=app.view_functions[rule.endpoint])
if "v2" in rule.endpoint:
v2_spec.path(view=app.view_functions[rule.endpoint])
v1_template = v1_spec.to_flasgger(app)
v2_template = v2_spec.to_flasgger(app)
v2_swagger_config = {
"headers": [],
"specs_route": "/api/v1",
"endpoint": "flasgger-v1",
"specs": [
{
"endpoint": "apispec_v1",
"route": "/apispec_v1.json",
"rule_filter": lambda rule: "v1" in rule.endpoint,
"model_filter": lambda tag: True,
}
],
}
Swagger(app, template=v1_template, config=v1_swagger_config)
v2_swagger_config = {
"headers": [],
"specs_route": "/api/v2",
"endpoint": "flasgger-v2",
"specs": [
{
"endpoint": "apispec_v2",
"route": "/apispec_v2.json",
"rule_filter": lambda rule: "v2" in rule.endpoint,
"model_filter": lambda tag: True,
}
],
}
Swagger(app, template=v2_template, config=v2_swagger_config)
Is it possible to have multiple flasgger pages? For instance, if my API has multiple versions, I would like to have one API spec at
/api/v1
and another at/api/v2
.In my attempts to get it working currently, I get the following error:
ValueError: The name 'flasgger' is already registered for a different blueprint. Use 'name=' to provide a unique name.
This is roughly the code that I am trying: