Adds a complete GraphQL API for querying MarkiTect database content including: CORE FEATURES: - Type-safe GraphQL schema with comprehensive field definitions - Full database access: markdown files, schemas, ASTs, and metadata - Advanced search capabilities with relevance scoring - Pagination support for efficient data access - Real-time schema introspection and development tools IMPLEMENTATION: - GraphQL schema definition with 6 core types (MarkdownFile, Schema, AST, etc.) - Complete resolver implementation with database integration - Flask-based GraphQL server with CORS support - GraphQL Playground for interactive development - Health check and schema introspection endpoints CLI INTEGRATION: - graphql-serve: Start GraphQL server with customizable options - graphql-query: Execute queries from command line (local/remote) - graphql-schema: Retrieve schema definition in SDL/JSON format - graphql-examples: Comprehensive usage examples and documentation API FEATURES: - Single item queries (by ID or filename) - List queries with filtering and pagination - Full-text search across files and schemas - Database statistics and analytics - AST querying with JSONPath expressions - Computed fields (word count, line count, etc.) TESTING: - Comprehensive test suite with 38 passing tests - Unit tests for schema, resolvers, server, and client - Integration tests for query execution - Error handling and edge case coverage - Mock and fixture support for isolated testing DOCUMENTATION: - Complete API documentation with examples - Usage guide for all CLI commands - Programming examples in Python and JavaScript - Performance optimization guidelines - Troubleshooting and security considerations The GraphQL interface enables developers to build rich applications on top of MarkiTect data with flexible, efficient querying capabilities. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
196 lines
6.8 KiB
Python
196 lines
6.8 KiB
Python
"""
|
|
GraphQL schema definition for MarkiTect data.
|
|
|
|
Defines the complete GraphQL schema for querying Markdown files,
|
|
ASTs, schemas, and related metadata.
|
|
"""
|
|
|
|
import graphene
|
|
from graphene import ObjectType, String, Int, DateTime, List, Field, JSONString
|
|
from typing import Optional
|
|
|
|
|
|
class FrontMatter(ObjectType):
|
|
"""GraphQL type for front matter data."""
|
|
key = String(required=True, description="Front matter key")
|
|
value = JSONString(description="Front matter value (can be any JSON type)")
|
|
|
|
|
|
class MarkdownFile(ObjectType):
|
|
"""GraphQL type for markdown files stored in MarkiTect."""
|
|
id = Int(required=True, description="Unique identifier")
|
|
filename = String(required=True, description="File path/name")
|
|
content = String(description="Markdown content")
|
|
front_matter = List(FrontMatter, description="Parsed front matter data")
|
|
front_matter_raw = JSONString(description="Raw front matter as JSON")
|
|
created_at = DateTime(description="Creation timestamp")
|
|
|
|
# Computed fields
|
|
word_count = Int(description="Number of words in content")
|
|
line_count = Int(description="Number of lines in content")
|
|
has_front_matter = graphene.Boolean(description="Whether file has front matter")
|
|
|
|
def resolve_front_matter(self, info):
|
|
"""Resolve front matter as key-value pairs."""
|
|
if self.front_matter_raw:
|
|
return [
|
|
FrontMatter(key=k, value=v)
|
|
for k, v in self.front_matter_raw.items()
|
|
]
|
|
return []
|
|
|
|
def resolve_word_count(self, info):
|
|
"""Calculate word count."""
|
|
if self.content:
|
|
return len(self.content.split())
|
|
return 0
|
|
|
|
def resolve_line_count(self, info):
|
|
"""Calculate line count."""
|
|
if self.content:
|
|
return len(self.content.splitlines())
|
|
return 0
|
|
|
|
def resolve_has_front_matter(self, info):
|
|
"""Check if file has front matter."""
|
|
return bool(self.front_matter_raw)
|
|
|
|
|
|
class Schema(ObjectType):
|
|
"""GraphQL type for JSON schemas."""
|
|
id = Int(required=True, description="Unique identifier")
|
|
filename = String(required=True, description="Schema filename")
|
|
title = String(description="Schema title")
|
|
description = String(description="Schema description")
|
|
schema_content = JSONString(required=True, description="JSON schema content")
|
|
created_at = DateTime(description="Creation timestamp")
|
|
updated_at = DateTime(description="Last update timestamp")
|
|
|
|
# Computed fields
|
|
schema_version = String(description="JSON Schema version")
|
|
property_count = Int(description="Number of properties in schema")
|
|
|
|
def resolve_schema_version(self, info):
|
|
"""Extract schema version."""
|
|
if self.schema_content and isinstance(self.schema_content, dict):
|
|
return self.schema_content.get('$schema', 'Unknown')
|
|
return 'Unknown'
|
|
|
|
def resolve_property_count(self, info):
|
|
"""Count properties in schema."""
|
|
if (self.schema_content and
|
|
isinstance(self.schema_content, dict) and
|
|
'properties' in self.schema_content):
|
|
return len(self.schema_content['properties'])
|
|
return 0
|
|
|
|
|
|
class ASTNode(ObjectType):
|
|
"""GraphQL type for AST nodes."""
|
|
type = String(required=True, description="Node type")
|
|
value = String(description="Node value/content")
|
|
level = Int(description="Heading level (for heading nodes)")
|
|
children = List(lambda: ASTNode, description="Child nodes")
|
|
attrs = JSONString(description="Node attributes")
|
|
|
|
|
|
class AST(ObjectType):
|
|
"""GraphQL type for parsed AST."""
|
|
file_id = Int(description="Associated file ID")
|
|
filename = String(required=True, description="Source filename")
|
|
tree = List(ASTNode, description="AST tree structure")
|
|
metadata = JSONString(description="AST metadata")
|
|
|
|
# Statistics
|
|
heading_count = Int(description="Number of headings")
|
|
link_count = Int(description="Number of links")
|
|
image_count = Int(description="Number of images")
|
|
code_block_count = Int(description="Number of code blocks")
|
|
|
|
|
|
class DatabaseStats(ObjectType):
|
|
"""Database statistics."""
|
|
total_files = Int(description="Total number of markdown files")
|
|
total_schemas = Int(description="Total number of schemas")
|
|
total_size_bytes = Int(description="Total database size in bytes")
|
|
last_updated = DateTime(description="Last database update")
|
|
|
|
|
|
class SearchResult(ObjectType):
|
|
"""Search result union type."""
|
|
type = String(required=True, description="Result type (file, schema)")
|
|
score = graphene.Float(description="Search relevance score")
|
|
file = Field(MarkdownFile, description="Matched file (if type=file)")
|
|
schema = Field(Schema, description="Matched schema (if type=schema)")
|
|
highlight = String(description="Highlighted match text")
|
|
|
|
|
|
class Query(ObjectType):
|
|
"""Root GraphQL query type."""
|
|
|
|
# Single item queries
|
|
markdown_file = Field(
|
|
MarkdownFile,
|
|
id=Int(description="File ID"),
|
|
filename=String(description="File path"),
|
|
description="Get a specific markdown file"
|
|
)
|
|
|
|
schema = Field(
|
|
Schema,
|
|
id=Int(description="Schema ID"),
|
|
filename=String(description="Schema filename"),
|
|
description="Get a specific schema"
|
|
)
|
|
|
|
ast = Field(
|
|
AST,
|
|
file_id=Int(description="File ID"),
|
|
filename=String(description="File path"),
|
|
description="Get AST for a specific file"
|
|
)
|
|
|
|
# List queries
|
|
markdown_files = List(
|
|
MarkdownFile,
|
|
limit=Int(default_value=50, description="Maximum number of results"),
|
|
offset=Int(default_value=0, description="Offset for pagination"),
|
|
has_front_matter=graphene.Boolean(description="Filter by front matter presence"),
|
|
created_after=DateTime(description="Filter by creation date"),
|
|
description="List markdown files with optional filtering"
|
|
)
|
|
|
|
schemas = List(
|
|
Schema,
|
|
limit=Int(default_value=50, description="Maximum number of results"),
|
|
offset=Int(default_value=0, description="Offset for pagination"),
|
|
description="List all schemas"
|
|
)
|
|
|
|
# Search
|
|
search = List(
|
|
SearchResult,
|
|
query=String(required=True, description="Search query"),
|
|
type=String(description="Search type filter (file, schema, all)"),
|
|
limit=Int(default_value=20, description="Maximum number of results"),
|
|
description="Search across files and schemas"
|
|
)
|
|
|
|
# Statistics
|
|
database_stats = Field(
|
|
DatabaseStats,
|
|
description="Get database statistics"
|
|
)
|
|
|
|
# JSONPath queries for ASTs
|
|
ast_query = List(
|
|
JSONString,
|
|
file_id=Int(),
|
|
filename=String(),
|
|
jsonpath=String(required=True, description="JSONPath expression"),
|
|
description="Query AST using JSONPath expressions"
|
|
)
|
|
|
|
|
|
# Create the schema
|
|
schema = graphene.Schema(query=Query) |