Coverage for src/lexigram/graphql/schema/documentation.py: 36%
73 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
1"""Automatic GraphQL documentation generation.
3This module provides utilities for generating documentation
4from GraphQL schemas.
5"""
7from __future__ import annotations
9from dataclasses import dataclass, field
10from typing import Any
13@dataclass
14class FieldDoc:
15 """Documentation for a GraphQL field.
17 Attributes:
18 name: Field name.
19 description: Field description.
20 type: Field type.
21 args: Field arguments.
22 """
24 name: str
25 description: str | None = None
26 type: str = ""
27 args: list[dict[str, Any]] = field(default_factory=list)
30@dataclass
31class TypeDoc:
32 """Documentation for a GraphQL type.
34 Attributes:
35 name: Type name.
36 description: Type description.
37 fields: Type fields.
38 """
40 name: str
41 description: str | None = None
42 fields: list[FieldDoc] = field(default_factory=list)
45@dataclass
46class SchemaDoc:
47 """Complete schema documentation.
49 Attributes:
50 query_type: Query type name.
51 mutation_type: Mutation type name.
52 subscription_type: Subscription type name.
53 types: All type documentations.
54 """
56 query_type: str | None = None
57 mutation_type: str | None = None
58 subscription_type: str | None = None
59 types: list[TypeDoc] = field(default_factory=list)
61 def to_markdown(self) -> str:
62 """Generate Markdown documentation.
64 Returns:
65 Markdown-formatted documentation.
66 """
67 lines = ["# GraphQL Schema\n"]
69 # Root types
70 if self.query_type:
71 lines.append(f"**Query Type**: `{self.query_type}`")
72 if self.mutation_type:
73 lines.append(f"**Mutation Type**: `{self.mutation_type}`")
74 if self.subscription_type:
75 lines.append(f"**Subscription Type**: `{self.subscription_type}`")
77 lines.append("\n## Types\n")
79 # Types
80 for type_doc in self.types:
81 lines.append(f"### {type_doc.name}\n")
83 if type_doc.description:
84 lines.append(f"{type_doc.description}\n")
86 if type_doc.fields:
87 lines.append("| Field | Type | Description |")
88 lines.append("|-------|------|-------------|")
90 for field_doc in type_doc.fields:
91 desc = field_doc.description or ""
92 lines.append(f"| {field_doc.name} | `{field_doc.type}` | {desc} |")
94 lines.append("")
96 return "\n".join(lines)
99class SchemaDocumentationGenerator:
100 """Generate documentation from GraphQL schemas.
102 Example:
103 from lexigram.logging import get_logger
105 logger = get_logger(__name__)
106 generator = SchemaDocumentationGenerator()
107 doc = generator.generate(schema)
109 # Output as Markdown
110 markdown = doc.to_markdown()
111 logger.info("schema_documentation", doc_length=len(markdown))
112 """
114 def generate(self, schema: Any) -> SchemaDoc:
115 """Generate documentation from a schema.
117 Args:
118 schema: GraphQL schema.
120 Returns:
121 SchemaDoc with all documentation.
122 """
123 doc = SchemaDoc()
125 # Get root types
126 if hasattr(schema, "query"):
127 doc.query_type = "Query"
129 if hasattr(schema, "mutation"):
130 doc.mutation_type = "Mutation"
132 if hasattr(schema, "subscription"):
133 doc.subscription_type = "Subscription"
135 # Get types
136 if hasattr(schema, "types"):
137 for type_obj in schema.types:
138 type_doc = self._document_type(type_obj)
139 if type_doc:
140 doc.types.append(type_doc)
142 return doc
144 def _document_type(self, type_obj: Any) -> TypeDoc | None:
145 """Document a single type.
147 Args:
148 type_obj: Type object.
150 Returns:
151 TypeDoc or None.
152 """
153 if not hasattr(type_obj, "name"):
154 return None
156 name = type_obj.name
158 # Skip internal types
159 if name.startswith("__"):
160 return None
162 type_doc = TypeDoc(
163 name=name,
164 description=getattr(type_obj, "description", None),
165 )
167 # Get fields
168 if hasattr(type_obj, "fields"):
169 for field_obj in type_obj.fields:
170 field_doc = FieldDoc(
171 name=getattr(field_obj, "name", ""),
172 description=getattr(field_obj, "description", None),
173 type=str(getattr(field_obj, "type", "")),
174 )
175 type_doc.fields.append(field_doc)
177 return type_doc
180def generate_schema_docs(schema: Any) -> str:
181 """Convenience function to generate schema documentation.
183 Args:
184 schema: GraphQL schema.
186 Returns:
187 Markdown-formatted documentation.
188 """
189 generator = SchemaDocumentationGenerator()
190 doc = generator.generate(schema)
191 return doc.to_markdown()
194__all__ = [
195 "FieldDoc",
196 "SchemaDoc",
197 "SchemaDocumentationGenerator",
198 "TypeDoc",
199 "generate_schema_docs",
200]