mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
feat(api): add OpenAPI schema postprocessing hooks for Mintlify compatibility
Add three postprocessing hooks to fix OpenAPI 3.0.x compatibility issues
generated by drf-spectacular-jsonapi:
1. fix_empty_id_fields: Fixes empty id field definitions ({}) in JSON:API
request schemas (particularly PATCH/update requests) by replacing them
with proper schema: {"type": "string", "format": "uuid", "description": "..."}
2. fix_pattern_properties: Converts patternProperties to additionalProperties
for OpenAPI 3.0 compatibility. patternProperties is only available in
OpenAPI 3.1+, but drf-spectacular generates 3.0.3 specs. This is needed
for the Prowler mutelist configuration which uses dynamic keys.
3. fix_type_formats: Fixes invalid type values like "email" that
drf-spectacular generates from Django's EmailField. Converts them to
proper OpenAPI format: "type": "string" with "format": "email".
Also handles "url" → "uri" and "uuid" formats.
These hooks ensure the generated OpenAPI schema validates correctly with
Mintlify and other OpenAPI 3.0.x tools.
This commit is contained in:
@@ -36,6 +36,122 @@ def _extract_task_example_from_components(components):
|
||||
}
|
||||
|
||||
|
||||
def fix_empty_id_fields(result, generator, request, public): # noqa: F841
|
||||
"""
|
||||
Fix empty id fields in JSON:API request schemas.
|
||||
drf-spectacular-jsonapi sometimes generates empty id field definitions ({})
|
||||
which cause validation errors in Mintlify and other OpenAPI validators.
|
||||
"""
|
||||
if not isinstance(result, dict):
|
||||
return result
|
||||
|
||||
components = result.get("components", {}) or {}
|
||||
schemas = components.get("schemas", {}) or {}
|
||||
|
||||
for schema_name, schema in schemas.items():
|
||||
if not isinstance(schema, dict):
|
||||
continue
|
||||
|
||||
# Check if this is a JSON:API request schema with a data object
|
||||
properties = schema.get("properties", {})
|
||||
if not isinstance(properties, dict):
|
||||
continue
|
||||
|
||||
data_prop = properties.get("data")
|
||||
if not isinstance(data_prop, dict):
|
||||
continue
|
||||
|
||||
data_properties = data_prop.get("properties", {})
|
||||
if not isinstance(data_properties, dict):
|
||||
continue
|
||||
|
||||
# Fix empty id field
|
||||
id_field = data_properties.get("id")
|
||||
if id_field == {} or (isinstance(id_field, dict) and not id_field):
|
||||
data_properties["id"] = {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Unique identifier for this resource object.",
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def convert_pattern_properties_to_additional(obj):
|
||||
"""
|
||||
Recursively convert patternProperties to additionalProperties.
|
||||
OpenAPI 3.0.x doesn't support patternProperties (only available in 3.1+).
|
||||
"""
|
||||
if isinstance(obj, dict):
|
||||
if "patternProperties" in obj:
|
||||
# Get the pattern and its schema
|
||||
pattern_props = obj.pop("patternProperties")
|
||||
# Use the first pattern's schema as additionalProperties
|
||||
if pattern_props:
|
||||
first_pattern_schema = next(iter(pattern_props.values()))
|
||||
obj["additionalProperties"] = first_pattern_schema
|
||||
|
||||
# Recursively process all nested objects
|
||||
for key, value in obj.items():
|
||||
obj[key] = convert_pattern_properties_to_additional(value)
|
||||
elif isinstance(obj, list):
|
||||
return [convert_pattern_properties_to_additional(item) for item in obj]
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def fix_pattern_properties(result, generator, request, public): # noqa: F841
|
||||
"""
|
||||
Convert patternProperties to additionalProperties for OpenAPI 3.0 compatibility.
|
||||
patternProperties is only supported in OpenAPI 3.1+, but drf-spectacular
|
||||
generates OpenAPI 3.0.x specs.
|
||||
"""
|
||||
if not isinstance(result, dict):
|
||||
return result
|
||||
|
||||
return convert_pattern_properties_to_additional(result)
|
||||
|
||||
|
||||
def fix_invalid_types(obj):
|
||||
"""
|
||||
Recursively fix invalid type values in OpenAPI schemas.
|
||||
Converts invalid types like "email" to proper OpenAPI format.
|
||||
"""
|
||||
if isinstance(obj, dict):
|
||||
# Fix invalid "type" values
|
||||
if "type" in obj:
|
||||
type_value = obj["type"]
|
||||
if type_value == "email":
|
||||
obj["type"] = "string"
|
||||
obj["format"] = "email"
|
||||
elif type_value == "url":
|
||||
obj["type"] = "string"
|
||||
obj["format"] = "uri"
|
||||
elif type_value == "uuid":
|
||||
obj["type"] = "string"
|
||||
obj["format"] = "uuid"
|
||||
|
||||
# Recursively process all nested objects
|
||||
for key, value in list(obj.items()):
|
||||
obj[key] = fix_invalid_types(value)
|
||||
elif isinstance(obj, list):
|
||||
return [fix_invalid_types(item) for item in obj]
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def fix_type_formats(result, generator, request, public): # noqa: F841
|
||||
"""
|
||||
Fix invalid type values in OpenAPI schemas.
|
||||
drf-spectacular sometimes generates invalid type values like "email"
|
||||
instead of "type": "string" with "format": "email".
|
||||
"""
|
||||
if not isinstance(result, dict):
|
||||
return result
|
||||
|
||||
return fix_invalid_types(result)
|
||||
|
||||
|
||||
def attach_task_202_examples(result, generator, request, public): # noqa: F841
|
||||
if not isinstance(result, dict):
|
||||
return result
|
||||
|
||||
@@ -125,6 +125,9 @@ SPECTACULAR_SETTINGS = {
|
||||
"drf_spectacular_jsonapi.hooks.fix_nested_path_parameters",
|
||||
],
|
||||
"POSTPROCESSING_HOOKS": [
|
||||
"api.schema_hooks.fix_empty_id_fields",
|
||||
"api.schema_hooks.fix_pattern_properties",
|
||||
"api.schema_hooks.fix_type_formats",
|
||||
"api.schema_hooks.attach_task_202_examples",
|
||||
],
|
||||
"TITLE": "API Reference - Prowler",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -329,6 +329,10 @@
|
||||
{
|
||||
"tab": "Public Roadmap",
|
||||
"href": "https://roadmap.prowler.com/"
|
||||
},
|
||||
{
|
||||
"tab": "API Reference",
|
||||
"openapi": "api-reference/openapi.json"
|
||||
}
|
||||
],
|
||||
"global": {
|
||||
|
||||
Reference in New Issue
Block a user