Coverage for src/lexigram/admin/openapi/resource_converter.py: 0%
34 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:39 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:39 +0800
1from __future__ import annotations
3from typing import TYPE_CHECKING, Any
5from lexigram.admin.openapi.field_converter import field_to_openapi_property
7if TYPE_CHECKING:
8 from lexigram.admin.schema.base import SchemaField
11def resource_to_schema(
12 name: str,
13 fields: list[SchemaField],
14) -> dict[str, Any]:
15 """Convert a resource's fields to an OpenAPI Schema Object.
17 Args:
18 name: Resource name (used as the schema title).
19 fields: List of SchemaField instances.
21 Returns:
22 An OpenAPI Schema Object dict with ``type: object``, ``properties``,
23 and ``required`` fields.
24 """
25 properties: dict[str, Any] = {}
26 required: list[str] = []
27 for field in fields:
28 prop = field_to_openapi_property(field)
29 properties[field.name] = prop
30 if field.required:
31 required.append(field.name)
33 schema: dict[str, Any] = {
34 "type": "object",
35 "properties": properties,
36 "title": name,
37 }
38 if required:
39 schema["required"] = required
40 return schema
43def resources_to_openapi_spec(
44 resources: dict[str, Any],
45 *,
46 title: str = "Admin API",
47 version: str = "1.0.0",
48) -> dict[str, Any]:
49 """Convert admin resources to a full OpenAPI 3.0.3 specification.
51 Args:
52 resources: A ``{name: resource_instance}`` dict, where each resource
53 has a ``fields`` attribute containing ``SchemaField`` instances.
54 title: OpenAPI info title.
55 version: OpenAPI spec version.
57 Returns:
58 A complete OpenAPI 3.0.3 spec dict with paths, schemas, and tags.
59 """
60 schemas: dict[str, Any] = {}
61 paths: dict[str, Any] = {}
62 tags: list[dict[str, str]] = []
64 for resource_name, resource in resources.items():
65 fields: list[SchemaField] = getattr(resource, "fields", []) or []
66 label: str = getattr(resource, "label", resource_name)
68 schema = resource_to_schema(resource_name, fields)
69 schema_name = f"{resource_name}.Resource"
70 schemas[schema_name] = schema
72 list_schema_name = f"{resource_name}.ListResponse"
73 schemas[list_schema_name] = {
74 "type": "object",
75 "properties": {
76 "data": {
77 "type": "array",
78 "items": {"$ref": f"#/components/schemas/{schema_name}"},
79 },
80 "total": {"type": "integer"},
81 },
82 }
84 tags.append({"name": resource_name, "description": label})
86 prefix = f"/api/{resource_name}"
88 paths[prefix] = {
89 "get": {
90 "tags": [resource_name],
91 "summary": f"List {label}",
92 "operationId": f"list{resource_name.title()}",
93 "parameters": [
94 {
95 "name": "page",
96 "in": "query",
97 "schema": {"type": "integer", "default": 1},
98 },
99 {
100 "name": "per_page",
101 "in": "query",
102 "schema": {"type": "integer", "default": 15},
103 },
104 ],
105 "responses": {
106 "200": {
107 "description": "Successful response",
108 "content": {
109 "application/json": {
110 "schema": {
111 "$ref": f"#/components/schemas/{list_schema_name}"
112 },
113 },
114 },
115 },
116 },
117 },
118 "post": {
119 "tags": [resource_name],
120 "summary": f"Create {label}",
121 "operationId": f"create{resource_name.title()}",
122 "requestBody": {
123 "required": True,
124 "content": {
125 "application/json": {
126 "schema": {"$ref": f"#/components/schemas/{schema_name}"},
127 },
128 },
129 },
130 "responses": {
131 "201": {
132 "description": "Created",
133 "content": {
134 "application/json": {
135 "schema": {
136 "$ref": f"#/components/schemas/{schema_name}"
137 },
138 },
139 },
140 },
141 },
142 },
143 }
145 paths[f"{prefix}/{{id}}"] = {
146 "get": {
147 "tags": [resource_name],
148 "summary": f"Get {label} by ID",
149 "operationId": f"get{resource_name.title()}",
150 "parameters": [
151 {
152 "name": "id",
153 "in": "path",
154 "required": True,
155 "schema": {"type": "string"},
156 },
157 ],
158 "responses": {
159 "200": {
160 "description": "Successful response",
161 "content": {
162 "application/json": {
163 "schema": {
164 "$ref": f"#/components/schemas/{schema_name}"
165 },
166 },
167 },
168 },
169 },
170 },
171 "put": {
172 "tags": [resource_name],
173 "summary": f"Update {label}",
174 "operationId": f"update{resource_name.title()}",
175 "parameters": [
176 {
177 "name": "id",
178 "in": "path",
179 "required": True,
180 "schema": {"type": "string"},
181 },
182 ],
183 "requestBody": {
184 "required": True,
185 "content": {
186 "application/json": {
187 "schema": {"$ref": f"#/components/schemas/{schema_name}"},
188 },
189 },
190 },
191 "responses": {
192 "200": {
193 "description": "Updated",
194 "content": {
195 "application/json": {
196 "schema": {
197 "$ref": f"#/components/schemas/{schema_name}"
198 },
199 },
200 },
201 },
202 },
203 },
204 "delete": {
205 "tags": [resource_name],
206 "summary": f"Delete {label}",
207 "operationId": f"delete{resource_name.title()}",
208 "parameters": [
209 {
210 "name": "id",
211 "in": "path",
212 "required": True,
213 "schema": {"type": "string"},
214 },
215 ],
216 "responses": {
217 "204": {"description": "Deleted"},
218 },
219 },
220 }
222 spec: dict[str, Any] = {
223 "openapi": "3.0.3",
224 "info": {
225 "title": title,
226 "version": version,
227 },
228 "tags": tags,
229 "paths": paths,
230 "components": {
231 "schemas": schemas,
232 },
233 }
234 return spec
237__all__ = ["resource_to_schema", "resources_to_openapi_spec"]