-
-
Couldn't load subscription status.
- Fork 778
Closed
Description
Description
Connexion fully supports OpenAPI 3.x and injects features like propertyNames or patternProperties into the generated schema at runtime. These are valid under JSON Schema (especially in OpenAPI 3.1), and Connexion accepts and uses them.
However, Connexion fails at runtime when serving the Swagger UI.
This causes errors like:
must NOT have additional properties
must match exactly one schema in oneOf
Concerns:
- Users expect their runtime schema and documentation schema to align.
Expected Behavior
Connexion should allow serving Swagger UI for OpenAPI specs containing modern JSON Schema keywords without crashing.
At minimum, it could provide a configurable option to sanitize the spec automatically for the documentation endpoint.
Steps to Reproduce
- Create an OpenAPI 3.x spec containing propertyNames or patternProperties.
- Load it in Connexion with Swagger UI enabled.
- Access /docs/.
- Observe runtime error preventing the Swagger UI from loading.
Environment
- Connexion version: connexion[flask,uvicorn,swagger-ui]==3.2.0
- Python version: 3.12.2
Our workaround
def patch_openapi_spec(schema):
"""
Patch OpenAPI spec to replace problematic JSON Schema keywords
with x-prefixed versions for documentation compatibility
"""
if isinstance(schema, dict):
if "patternProperties" in schema:
schema["x-patternProperties"] = schema.pop("patternProperties")
if "propertyNames" in schema:
schema["x-propertyNames"] = schema.pop("propertyNames")
for v in schema.values():
patch_openapi_spec(v)
elif isinstance(schema, list):
for v in schema:
patch_openapi_spec(v)
def generate_config_doc_yaml():
"""
Generate documentation-safe YAML from validation YAML
"""
validate_config_path = os.path.join(os.path.dirname(__file__), 'openapi', 'config-api.yaml')
doc_config_path = os.path.join(os.path.dirname(__file__), 'openapi', 'config-api-doc.yaml')
try:
with open(validate_config_path, 'r', encoding='utf-8') as f:
spec = yaml.safe_load(f)
patch_openapi_spec(spec)
with open(doc_config_path, 'w', encoding='utf-8') as f:
yaml.dump(spec, f, sort_keys=False, allow_unicode=True)
return doc_config_path
except Exception as e:
logging.error(f"Error generating config doc YAML: {e}")
return None
app.add_api('config-api.yaml', name='config', arguments={'title': 'Configuration'}, strict_validation=True, swagger_ui=False)
# Custom documentation routes for config API
@app.route("/api/v1.0/configuration/docs/openapi.yaml")
def config_docs_spec():
"""Serve the documentation-safe OpenAPI spec for config API"""
return send_from_directory(os.path.join(os.path.dirname(__file__), 'openapi'), 'config-api-doc.yaml')
@app.route("/api/v1.0/configuration/docs/")
def config_swagger_ui():
"""Serve custom Swagger UI for config API"""
return f"""
<!DOCTYPE html>
<html>
<head>
<title>C3PO Configuration API Documentation</title>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
<script>
const ui = SwaggerUIBundle({{
url: '/api/v1.0/configuration/docs/openapi.yaml',
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.presets.standalone
]
}});
</script>
</body>
</html>
"""Maybe related
Metadata
Metadata
Assignees
Labels
No labels