""" 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)