Coverage for class_generator/parsers/explain_parser.py: 94%
66 statements
« prev ^ index » next coverage.py v7.10.1, created at 2025-07-29 12:31 +0300
« prev ^ index » next coverage.py v7.10.1, created at 2025-07-29 12:31 +0300
1"""Parser for OpenAPI explain data to extract resource information."""
3from typing import Any
5from simple_logger.logger import get_logger
7from class_generator.constants import MISSING_DESCRIPTION_STR
8from class_generator.core.schema import extract_group_kind_version, read_resources_mapping_file
9from class_generator.parsers.type_parser import get_property_schema, prepare_property_dict
10from class_generator.utils import get_latest_version
11from ocp_resources.resource import Resource
13LOGGER = get_logger(name=__name__)
16def parse_explain(kind: str) -> list[dict[str, Any]]:
17 """
18 Parse OpenAPI explain data for a given resource kind.
20 Args:
21 kind: The Kubernetes resource kind
23 Returns:
24 List of resource dictionaries with parsed information
25 """
26 _schema_definition = read_resources_mapping_file()
27 _resources: list[dict[str, Any]] = []
29 _kinds_schema = _schema_definition[kind.lower()]
31 # Group schemas by API group
32 schemas_by_group: dict[str, list[dict[str, Any]]] = {}
33 for schema in _kinds_schema:
34 gvk_list = schema.get("x-kubernetes-group-version-kind", [])
35 if gvk_list:
36 group = gvk_list[0].get("group", "")
37 if group not in schemas_by_group:
38 schemas_by_group[group] = []
39 schemas_by_group[group].append(schema)
41 # For each API group, select the latest version
42 filtered_schemas = []
43 for group, group_schemas in schemas_by_group.items():
44 if len(group_schemas) > 1:
45 # Multiple versions in same group - pick latest
46 versions = []
47 for schema in group_schemas:
48 gvk_list = schema.get("x-kubernetes-group-version-kind", [])
49 if gvk_list:
50 version = gvk_list[0].get("version", "")
51 versions.append(version)
53 latest_version = get_latest_version(versions=versions)
55 # Add only the schema with the latest version
56 for schema in group_schemas:
57 gvk_list = schema.get("x-kubernetes-group-version-kind", [])
58 if gvk_list and gvk_list[0].get("version") == latest_version:
59 filtered_schemas.append(schema)
60 break
61 else:
62 # Single version in this group
63 filtered_schemas.extend(group_schemas)
65 # Use filtered schemas instead of all schemas
66 for _kind_schema in filtered_schemas:
67 namespaced = _kind_schema["namespaced"]
68 resource_dict: dict[str, Any] = {
69 "base_class": "NamespacedResource" if namespaced else "Resource",
70 "description": _kind_schema.get("description", MISSING_DESCRIPTION_STR),
71 "fields": [],
72 "spec": [],
73 }
75 schema_properties: dict[str, Any] = _kind_schema.get("properties", {})
76 fields_required = _kind_schema.get("required", [])
78 resource_dict.update(extract_group_kind_version(_kind_schema=_kind_schema))
80 if spec_schema := schema_properties.get("spec", {}):
81 spec_schema = get_property_schema(property_=spec_schema)
82 spec_required = spec_schema.get("required", [])
83 resource_dict = prepare_property_dict(
84 schema=spec_schema.get("properties", {}),
85 required=spec_required,
86 resource_dict=resource_dict,
87 dict_key="spec",
88 )
90 resource_dict = prepare_property_dict(
91 schema=schema_properties,
92 required=fields_required,
93 resource_dict=resource_dict,
94 dict_key="fields",
95 )
97 api_group_real_name = resource_dict.get("group")
98 # Store the original group name before converting
99 resource_dict["original_group"] = api_group_real_name
101 # If API Group is not present in resource, try to get it from VERSION
102 if not api_group_real_name:
103 version_splited = resource_dict["version"].split("/")
104 if len(version_splited) == 2:
105 api_group_real_name = version_splited[0]
106 resource_dict["original_group"] = api_group_real_name
108 if api_group_real_name:
109 api_group_for_resource_api_group = api_group_real_name.upper().replace(".", "_").replace("-", "_")
110 resource_dict["group"] = api_group_for_resource_api_group
111 missing_api_group_in_resource: bool = not hasattr(Resource.ApiGroup, api_group_for_resource_api_group)
113 if missing_api_group_in_resource:
114 LOGGER.warning(
115 f"Missing API Group in Resource\n"
116 f"Please add `Resource.ApiGroup.{api_group_for_resource_api_group} = {api_group_real_name}` "
117 "manually into ocp_resources/resource.py under Resource class > ApiGroup class."
118 )
120 else:
121 api_version_for_resource_api_version = resource_dict["version"].upper()
122 missing_api_version_in_resource: bool = not hasattr(
123 Resource.ApiVersion, api_version_for_resource_api_version
124 )
126 if missing_api_version_in_resource:
127 LOGGER.warning(
128 f"Missing API Version in Resource\n"
129 f"Please add `Resource.ApiVersion.{api_version_for_resource_api_version} = {resource_dict['version']}` "
130 "manually into ocp_resources/resource.py under Resource class > ApiGroup class."
131 )
133 _resources.append(resource_dict)
135 return _resources