0
# Schema Validation and Processing
1
2
The schema module provides functionality for validating API request parameters and response data against JSON schemas defined in discovery documents. It enables pretty-printing of schema definitions and validation of data structures.
3
4
## Capabilities
5
6
### Schema Management
7
8
Manage and validate data against JSON schemas from API discovery documents.
9
10
```python { .api }
11
class Schemas:
12
"""Manage JSON schemas for API validation and documentation."""
13
14
def __init__(self, discovery_doc):
15
"""
16
Initialize schema manager with discovery document.
17
18
Args:
19
discovery_doc (dict): Complete API discovery document containing schemas
20
"""
21
22
def get(self, name, default=None):
23
"""
24
Get a schema definition by name.
25
26
Args:
27
name (str): Name of the schema to retrieve
28
default (object, optional): Default value if schema not found
29
30
Returns:
31
dict: Schema definition, or default if not found
32
"""
33
34
def prettyPrintByName(self, name):
35
"""
36
Pretty print a schema definition by name.
37
38
Args:
39
name (str): Name of the schema to pretty print
40
41
Returns:
42
str: Formatted string representation of the schema
43
"""
44
45
def prettyPrintSchema(self, val):
46
"""
47
Pretty print a schema definition.
48
49
Args:
50
val (dict): Schema definition to pretty print
51
52
Returns:
53
str: Formatted string representation of the schema
54
"""
55
56
def _prettyPrintByName(self, name, seen):
57
"""
58
Internal method for pretty printing schema by name with circular reference tracking.
59
60
Args:
61
name (str): Schema name to print
62
seen (set): Set of already processed schemas to prevent infinite recursion
63
64
Returns:
65
str: Formatted schema representation
66
"""
67
68
def _prettyPrintSchema(self, val, seen):
69
"""
70
Internal method for pretty printing schema with circular reference tracking.
71
72
Args:
73
val (dict): Schema definition to print
74
seen (set): Set of already processed schemas to prevent infinite recursion
75
76
Returns:
77
str: Formatted schema representation
78
"""
79
```
80
81
## Usage Examples
82
83
### Basic Schema Operations
84
85
```python
86
from googleapiclient import discovery
87
from googleapiclient.schema import Schemas
88
89
# Build service and get discovery document
90
service = discovery.build('gmail', 'v1', credentials=credentials)
91
discovery_doc = service._rootDesc # Access discovery document
92
93
# Create schema manager
94
schemas = Schemas(discovery_doc)
95
96
# Get a specific schema
97
message_schema = schemas.get('Message')
98
if message_schema:
99
print("Message schema found:")
100
print(schemas.prettyPrintSchema(message_schema))
101
else:
102
print("Message schema not found")
103
104
# Pretty print schema by name
105
thread_schema_str = schemas.prettyPrintByName('Thread')
106
print("Thread schema:")
107
print(thread_schema_str)
108
```
109
110
### Schema Exploration
111
112
```python
113
from googleapiclient.schema import Schemas
114
import json
115
116
def explore_api_schemas(service_name, version):
117
"""Explore all schemas in an API."""
118
119
service = discovery.build(service_name, version, credentials=credentials)
120
discovery_doc = service._rootDesc
121
122
schemas = Schemas(discovery_doc)
123
124
# Get all available schemas
125
schema_names = discovery_doc.get('schemas', {}).keys()
126
127
print(f"Available schemas in {service_name} {version}:")
128
for name in sorted(schema_names):
129
schema = schemas.get(name)
130
if schema:
131
print(f"\n{name}:")
132
print(schemas.prettyPrintByName(name))
133
print("-" * 50)
134
135
# Explore Gmail API schemas
136
explore_api_schemas('gmail', 'v1')
137
```
138
139
### Schema-Based Validation
140
141
```python
142
from googleapiclient.schema import Schemas
143
import jsonschema
144
145
class SchemaValidator:
146
"""Validate data against API schemas."""
147
148
def __init__(self, discovery_doc):
149
self.schemas = Schemas(discovery_doc)
150
self.discovery_doc = discovery_doc
151
152
def validate_data(self, schema_name, data):
153
"""
154
Validate data against a named schema.
155
156
Args:
157
schema_name (str): Name of the schema to validate against
158
data (dict): Data to validate
159
160
Returns:
161
tuple: (is_valid, errors) - validation result and error list
162
"""
163
schema_def = self.schemas.get(schema_name)
164
if not schema_def:
165
return False, [f"Schema '{schema_name}' not found"]
166
167
try:
168
# Convert Google API schema to JSON Schema format
169
json_schema = self._convert_to_json_schema(schema_def)
170
jsonschema.validate(data, json_schema)
171
return True, []
172
except jsonschema.ValidationError as e:
173
return False, [str(e)]
174
except Exception as e:
175
return False, [f"Validation error: {e}"]
176
177
def _convert_to_json_schema(self, api_schema):
178
"""Convert Google API schema to JSON Schema format."""
179
# This is a simplified conversion - real implementation would be more complex
180
json_schema = {
181
"type": "object",
182
"properties": {},
183
"required": []
184
}
185
186
if "properties" in api_schema:
187
for prop_name, prop_def in api_schema["properties"].items():
188
json_schema["properties"][prop_name] = self._convert_property(prop_def)
189
if prop_def.get("required", False):
190
json_schema["required"].append(prop_name)
191
192
return json_schema
193
194
def _convert_property(self, prop_def):
195
"""Convert a single property definition."""
196
prop_schema = {}
197
198
# Map Google API types to JSON Schema types
199
type_mapping = {
200
"string": "string",
201
"integer": "integer",
202
"number": "number",
203
"boolean": "boolean",
204
"array": "array",
205
"object": "object"
206
}
207
208
api_type = prop_def.get("type", "string")
209
prop_schema["type"] = type_mapping.get(api_type, "string")
210
211
if "description" in prop_def:
212
prop_schema["description"] = prop_def["description"]
213
214
if api_type == "array" and "items" in prop_def:
215
prop_schema["items"] = self._convert_property(prop_def["items"])
216
217
return prop_schema
218
219
# Usage
220
service = discovery.build('gmail', 'v1', credentials=credentials)
221
validator = SchemaValidator(service._rootDesc)
222
223
# Validate message data
224
message_data = {
225
"id": "message123",
226
"threadId": "thread456",
227
"labelIds": ["INBOX", "UNREAD"],
228
"snippet": "This is a test message..."
229
}
230
231
is_valid, errors = validator.validate_data('Message', message_data)
232
if is_valid:
233
print("Message data is valid")
234
else:
235
print("Validation errors:")
236
for error in errors:
237
print(f" - {error}")
238
```
239
240
### Schema Documentation Generator
241
242
```python
243
from googleapiclient.schema import Schemas
244
245
class SchemaDocumentationGenerator:
246
"""Generate documentation from API schemas."""
247
248
def __init__(self, discovery_doc):
249
self.schemas = Schemas(discovery_doc)
250
self.discovery_doc = discovery_doc
251
252
def generate_markdown_docs(self, output_file):
253
"""Generate Markdown documentation for all schemas."""
254
255
with open(output_file, 'w') as f:
256
f.write("# API Schema Documentation\n\n")
257
258
schema_names = self.discovery_doc.get('schemas', {}).keys()
259
260
for name in sorted(schema_names):
261
f.write(f"## {name}\n\n")
262
263
schema = self.schemas.get(name)
264
if schema and 'description' in schema:
265
f.write(f"{schema['description']}\n\n")
266
267
# Pretty print the schema
268
schema_str = self.schemas.prettyPrintByName(name)
269
f.write("```\n")
270
f.write(schema_str)
271
f.write("\n```\n\n")
272
273
# Add properties details
274
if schema and 'properties' in schema:
275
f.write("### Properties\n\n")
276
for prop_name, prop_def in schema['properties'].items():
277
f.write(f"- **{prop_name}** ({prop_def.get('type', 'unknown')}): ")
278
f.write(f"{prop_def.get('description', 'No description')}\n")
279
f.write("\n")
280
281
def get_schema_summary(self):
282
"""Get a summary of all schemas."""
283
schema_names = self.discovery_doc.get('schemas', {}).keys()
284
summary = {
285
'total_schemas': len(schema_names),
286
'schemas': {}
287
}
288
289
for name in schema_names:
290
schema = self.schemas.get(name)
291
if schema:
292
summary['schemas'][name] = {
293
'description': schema.get('description', ''),
294
'property_count': len(schema.get('properties', {})),
295
'type': schema.get('type', 'object')
296
}
297
298
return summary
299
300
# Usage
301
service = discovery.build('gmail', 'v1', credentials=credentials)
302
doc_generator = SchemaDocumentationGenerator(service._rootDesc)
303
304
# Generate documentation
305
doc_generator.generate_markdown_docs('gmail_schemas.md')
306
307
# Get summary
308
summary = doc_generator.get_schema_summary()
309
print(f"Found {summary['total_schemas']} schemas")
310
311
for name, info in summary['schemas'].items():
312
print(f"{name}: {info['property_count']} properties - {info['description'][:50]}...")
313
```
314
315
### Schema Comparison
316
317
```python
318
from googleapiclient.schema import Schemas
319
320
def compare_api_versions(service_name, version1, version2):
321
"""Compare schemas between two API versions."""
322
323
# Build services for both versions
324
service1 = discovery.build(service_name, version1, credentials=credentials)
325
service2 = discovery.build(service_name, version2, credentials=credentials)
326
327
schemas1 = Schemas(service1._rootDesc)
328
schemas2 = Schemas(service2._rootDesc)
329
330
# Get schema names from both versions
331
names1 = set(service1._rootDesc.get('schemas', {}).keys())
332
names2 = set(service2._rootDesc.get('schemas', {}).keys())
333
334
# Find differences
335
added_schemas = names2 - names1
336
removed_schemas = names1 - names2
337
common_schemas = names1 & names2
338
339
print(f"Schema comparison: {service_name} {version1} vs {version2}")
340
print(f"Added schemas: {len(added_schemas)}")
341
for name in sorted(added_schemas):
342
print(f" + {name}")
343
344
print(f"Removed schemas: {len(removed_schemas)}")
345
for name in sorted(removed_schemas):
346
print(f" - {name}")
347
348
print(f"Modified schemas:")
349
for name in sorted(common_schemas):
350
schema1 = schemas1.get(name)
351
schema2 = schemas2.get(name)
352
353
if schema1 != schema2:
354
print(f" ~ {name}")
355
356
# Compare properties
357
props1 = set(schema1.get('properties', {}).keys()) if schema1 else set()
358
props2 = set(schema2.get('properties', {}).keys()) if schema2 else set()
359
360
added_props = props2 - props1
361
removed_props = props1 - props2
362
363
if added_props:
364
print(f" Added properties: {', '.join(sorted(added_props))}")
365
if removed_props:
366
print(f" Removed properties: {', '.join(sorted(removed_props))}")
367
368
# Compare Gmail API versions
369
compare_api_versions('gmail', 'v1', 'v1') # Same version for demo
370
```
371
372
### Interactive Schema Explorer
373
374
```python
375
from googleapiclient.schema import Schemas
376
377
class InteractiveSchemaExplorer:
378
"""Interactive tool for exploring API schemas."""
379
380
def __init__(self, service_name, version):
381
self.service = discovery.build(service_name, version, credentials=credentials)
382
self.schemas = Schemas(self.service._rootDesc)
383
self.schema_names = list(self.service._rootDesc.get('schemas', {}).keys())
384
385
def explore(self):
386
"""Start interactive exploration."""
387
print(f"Schema Explorer - {len(self.schema_names)} schemas available")
388
print("Commands: list, show <name>, search <term>, quit")
389
390
while True:
391
try:
392
command = input("\nschema> ").strip().lower()
393
394
if command == 'quit':
395
break
396
elif command == 'list':
397
self._list_schemas()
398
elif command.startswith('show '):
399
schema_name = command[5:].strip()
400
self._show_schema(schema_name)
401
elif command.startswith('search '):
402
term = command[7:].strip()
403
self._search_schemas(term)
404
else:
405
print("Unknown command. Use: list, show <name>, search <term>, quit")
406
407
except KeyboardInterrupt:
408
break
409
except Exception as e:
410
print(f"Error: {e}")
411
412
def _list_schemas(self):
413
"""List all available schemas."""
414
print("Available schemas:")
415
for i, name in enumerate(sorted(self.schema_names)):
416
schema = self.schemas.get(name)
417
desc = schema.get('description', 'No description') if schema else 'No description'
418
print(f" {i+1:2d}. {name} - {desc[:60]}...")
419
420
def _show_schema(self, name):
421
"""Show detailed information about a schema."""
422
if name not in self.schema_names:
423
print(f"Schema '{name}' not found")
424
return
425
426
schema = self.schemas.get(name)
427
if not schema:
428
print(f"Could not retrieve schema '{name}'")
429
return
430
431
print(f"\nSchema: {name}")
432
if 'description' in schema:
433
print(f"Description: {schema['description']}")
434
435
print("\nSchema definition:")
436
print(self.schemas.prettyPrintByName(name))
437
438
def _search_schemas(self, term):
439
"""Search for schemas containing the term."""
440
matches = []
441
for name in self.schema_names:
442
schema = self.schemas.get(name)
443
if schema:
444
# Search in name and description
445
if (term.lower() in name.lower() or
446
term.lower() in schema.get('description', '').lower()):
447
matches.append(name)
448
449
if matches:
450
print(f"Found {len(matches)} matches:")
451
for name in sorted(matches):
452
schema = self.schemas.get(name)
453
desc = schema.get('description', 'No description') if schema else 'No description'
454
print(f" {name} - {desc[:60]}...")
455
else:
456
print(f"No schemas found containing '{term}'")
457
458
# Usage
459
explorer = InteractiveSchemaExplorer('gmail', 'v1')
460
# explorer.explore() # Uncomment for interactive use
461
```