graphxr-database-proxy 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. graphxr_database_proxy/__init__.py +16 -0
  2. graphxr_database_proxy/api/__init__.py +1 -0
  3. graphxr_database_proxy/api/database.py +266 -0
  4. graphxr_database_proxy/api/google.py +437 -0
  5. graphxr_database_proxy/api/projects.py +99 -0
  6. graphxr_database_proxy/common/util.py +38 -0
  7. graphxr_database_proxy/drivers/__init__.py +1 -0
  8. graphxr_database_proxy/drivers/base.py +56 -0
  9. graphxr_database_proxy/drivers/factory.py +36 -0
  10. graphxr_database_proxy/drivers/spanner.py +814 -0
  11. graphxr_database_proxy/main.py +110 -0
  12. graphxr_database_proxy/models/__init__.py +1 -0
  13. graphxr_database_proxy/models/google.py +50 -0
  14. graphxr_database_proxy/models/project.py +170 -0
  15. graphxr_database_proxy/proxy.py +495 -0
  16. graphxr_database_proxy/proxyForDev.py +290 -0
  17. graphxr_database_proxy/services/__init__.py +1 -0
  18. graphxr_database_proxy/services/project_service.py +150 -0
  19. graphxr_database_proxy/static/favicon.ico +0 -0
  20. graphxr_database_proxy/static/index.html +1 -0
  21. graphxr_database_proxy/static/main.7391ee9773c403483393.css +175 -0
  22. graphxr_database_proxy/static/main.7391ee9773c403483393.css.map +1 -0
  23. graphxr_database_proxy/static/main.ce3fbb85a7bc9452edb9.js +2 -0
  24. graphxr_database_proxy/static/main.ce3fbb85a7bc9452edb9.js.map +1 -0
  25. graphxr_database_proxy/static/vendors.70542a99f336a8021013.js +3 -0
  26. graphxr_database_proxy/static/vendors.70542a99f336a8021013.js.LICENSE.txt +95 -0
  27. graphxr_database_proxy/static/vendors.70542a99f336a8021013.js.map +1 -0
  28. graphxr_database_proxy-1.0.0.dist-info/METADATA +180 -0
  29. graphxr_database_proxy-1.0.0.dist-info/RECORD +33 -0
  30. graphxr_database_proxy-1.0.0.dist-info/WHEEL +5 -0
  31. graphxr_database_proxy-1.0.0.dist-info/entry_points.txt +2 -0
  32. graphxr_database_proxy-1.0.0.dist-info/licenses/LICENSE +21 -0
  33. graphxr_database_proxy-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,110 @@
1
+ """
2
+ Main FastAPI application
3
+ """
4
+
5
+ import os
6
+ import uvicorn
7
+ from pathlib import Path
8
+ from fastapi import FastAPI
9
+ from fastapi.staticfiles import StaticFiles
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+ from fastapi.responses import FileResponse
12
+
13
+ from .api.projects import router as projects_router
14
+ from .api.database import router as database_router
15
+ from .api.google import router as google_router
16
+
17
+ # Create FastAPI app
18
+ app = FastAPI(
19
+ title="GraphXR Database Proxy",
20
+ description="Secure middleware for connecting GraphXR to databases",
21
+ version="1.0.0",
22
+ docs_url="/docs",
23
+ redoc_url="/redoc"
24
+ )
25
+
26
+ # Add CORS middleware
27
+ app.add_middleware(
28
+ CORSMiddleware,
29
+ allow_origins=["*"], # Configure this for production
30
+ allow_credentials=True,
31
+ allow_methods=["*"],
32
+ allow_headers=["*"],
33
+ )
34
+
35
+ # Include API routers
36
+ app.include_router(projects_router)
37
+ app.include_router(database_router)
38
+ app.include_router(google_router)
39
+
40
+
41
+ @app.get("/health")
42
+ async def health_check():
43
+ """Health check endpoint"""
44
+ return {
45
+ "status": "healthy",
46
+ "message": "GraphXR Database Proxy is running",
47
+ "version": "1.0.0",
48
+ "environment": "development",
49
+ "auto_reload": "enabled"
50
+ }
51
+
52
+ # Mount static files from package
53
+ static_dir = Path(__file__).parent / "static"
54
+ frontend_dist = Path(__file__).parent.parent.parent / "frontend" / "dist"
55
+
56
+ if static_dir.exists() or frontend_dist.exists():
57
+ static_dir = frontend_dist if frontend_dist.exists() else static_dir
58
+ print(f"🔧 Serving static files from: {static_dir}")
59
+ # Development: Use frontend dist directory
60
+ app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
61
+
62
+ @app.get("/{full_path:path}")
63
+ async def serve_frontend(full_path: str):
64
+ """Serve the frontend application for all unmatched paths"""
65
+ index_file = static_dir / "index.html"
66
+ if index_file.exists():
67
+ return FileResponse(str(index_file))
68
+ return {"message": "Frontend not available"}
69
+
70
+ else:
71
+ @app.get("/")
72
+ async def serve_fallback():
73
+ """Fallback when frontend is not built"""
74
+ return {
75
+ "message": "Frontend not available",
76
+ "hint": "Run 'python scripts/build_frontend.py' to build and package frontend"
77
+ }
78
+
79
+
80
+ def main():
81
+ """Main entry point"""
82
+ import argparse
83
+
84
+ parser = argparse.ArgumentParser(description="GraphXR Database Proxy")
85
+ parser.add_argument("--host", default="0.0.0.0", help="Host to bind to")
86
+ parser.add_argument("--port", type=int, default=9080, help="Port to bind to")
87
+ parser.add_argument("--ui", action="store_true", help="Enable UI mode")
88
+ parser.add_argument("--dev", action="store_true", help="Development mode with hot reload")
89
+
90
+ args = parser.parse_args()
91
+
92
+ # Configure logging
93
+ import logging
94
+ logging.basicConfig(
95
+ level=logging.INFO,
96
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
97
+ )
98
+
99
+ # Run the server
100
+ uvicorn.run(
101
+ "src.graphxr_database_proxy.main:app",
102
+ host=args.host,
103
+ port=args.port,
104
+ reload=args.dev,
105
+ log_level="info" if not args.dev else "debug"
106
+ )
107
+
108
+
109
+ if __name__ == "__main__":
110
+ main()
@@ -0,0 +1 @@
1
+ # Models package
@@ -0,0 +1,50 @@
1
+ """
2
+ Google Cloud API models
3
+ """
4
+
5
+ from typing import Dict, Any, List, Optional
6
+ from pydantic import BaseModel
7
+
8
+
9
+ class GoogleAuthInfo(BaseModel):
10
+ """Google authentication information"""
11
+ client_id: Optional[str] = None
12
+ client_secret: Optional[str] = None
13
+ token: Optional[str] = None
14
+ refresh_token: Optional[str] = None
15
+ project_id: Optional[str] = None
16
+ instance_id: Optional[str] = None
17
+ database_id: Optional[str] = None
18
+ graph_name: Optional[str] = None
19
+
20
+
21
+ class GoogleProject(BaseModel):
22
+ """Google Cloud project information"""
23
+ name: str
24
+ id: str
25
+ instances: List[Any] = []
26
+
27
+ class GraphDatabase(BaseModel):
28
+ """Graph database information"""
29
+ id: str
30
+ name: str
31
+
32
+
33
+ class SpannerDatabase(BaseModel):
34
+ """Spanner database information"""
35
+ id: str
36
+ name: str
37
+ graphDBs: List[GraphDatabase] = []
38
+
39
+
40
+ class SpannerInstance(BaseModel):
41
+ """Spanner instance information"""
42
+ id: str
43
+ name: str
44
+ databases: List[SpannerDatabase] = []
45
+
46
+
47
+ class ProjectDetails(BaseModel):
48
+ """Project details with instances and databases"""
49
+ # Key is instance_id, value is instance details
50
+ instances: List[SpannerInstance] = []
@@ -0,0 +1,170 @@
1
+ """
2
+ Data models for the GraphXR Database Proxy
3
+ """
4
+
5
+ from datetime import datetime
6
+ from typing import Any, Dict, List, Optional, Literal
7
+ from pydantic import BaseModel, Field
8
+ from enum import Enum
9
+
10
+
11
+ class DatabaseType(str, Enum):
12
+ """Supported database types"""
13
+ SPANNER = "spanner"
14
+ POSTGRESQL = "postgresql"
15
+ MYSQL = "mysql"
16
+ MONGODB = "mongodb"
17
+
18
+
19
+ class AuthType(str, Enum):
20
+ """Authentication types"""
21
+ OAUTH2 = "oauth2"
22
+ SERVICE_ACCOUNT = "service_account"
23
+ USERNAME_PASSWORD = "username_password"
24
+
25
+
26
+ class OAuthConfig(BaseModel):
27
+ """OAuth2 configuration"""
28
+ client_id: Optional[str] = None
29
+ client_secret: Optional[str] = None
30
+ redirect_uri: str = "http://localhost:9080/google/spanner/callback"
31
+ scopes: List[str] = Field(default_factory=list)
32
+
33
+ # For OAuth2 token-based auth
34
+ token: Optional[str] = None
35
+ refresh_token: Optional[str] = None
36
+ expires_in: Optional[int] = None
37
+ last_refreshed: Optional[float] = None # Unix timestamp of last token refresh
38
+ token_uri: Optional[str] = None
39
+
40
+ # For Service Account JSON key
41
+ type: Optional[str] = None
42
+ project_id: Optional[str] = None
43
+ private_key_id: Optional[str] = None
44
+ private_key: Optional[str] = None
45
+ client_email: Optional[str] = None
46
+ client_x509_cert_url: Optional[str] = None
47
+ auth_uri: Optional[str] = None
48
+ auth_provider_x509_cert_url: Optional[str] = None
49
+
50
+ # Allow additional fields for flexibility
51
+ class Config:
52
+ extra = "allow"
53
+
54
+
55
+ class DatabaseConfig(BaseModel):
56
+ """Database connection configuration"""
57
+ # Common fields
58
+ type: DatabaseType
59
+ host: Optional[str] = None
60
+ port: Optional[int] = None
61
+
62
+ # Spanner specific
63
+ project_id: Optional[str] = None
64
+ instance_id: Optional[str] = None
65
+ database_id: Optional[str] = None
66
+ graph_name: Optional[str] = None
67
+
68
+ # Authentication
69
+ auth_type: AuthType = AuthType.USERNAME_PASSWORD
70
+ username: Optional[str] = None
71
+ password: Optional[str] = None
72
+ oauth_config: Optional[OAuthConfig] = None
73
+ service_account_path: Optional[str] = None # For backward compatibility
74
+ # Additional options
75
+ options: Dict[str, Any] = Field(default_factory=dict)
76
+
77
+
78
+ class Project(BaseModel):
79
+ """Project model"""
80
+ id: str = Field(..., description="Unique project identifier")
81
+ name: str = Field(..., description="Project name (should be unique)")
82
+ database_type: DatabaseType = Field(..., description="Database type")
83
+ database_config: DatabaseConfig = Field(..., description="Database configuration")
84
+ create_time: datetime = Field(default_factory=datetime.utcnow)
85
+ update_time: datetime = Field(default_factory=datetime.utcnow)
86
+
87
+ class Config:
88
+ json_encoders = {
89
+ datetime: lambda v: v.isoformat()
90
+ }
91
+
92
+
93
+ class ProjectCreate(BaseModel):
94
+ """Project creation request"""
95
+ name: str = Field(..., description="Project name")
96
+ database_type: DatabaseType = Field(..., description="Database type")
97
+ database_config: DatabaseConfig = Field(..., description="Database configuration")
98
+
99
+
100
+ class ProjectUpdate(BaseModel):
101
+ """Project update request"""
102
+ name: Optional[str] = None
103
+ database_config: Optional[DatabaseConfig] = None
104
+
105
+
106
+ class APIInfo(BaseModel):
107
+ """Database API information"""
108
+ type: DatabaseType
109
+ api_urls: Dict[str, str]
110
+ version: Optional[str] = None
111
+
112
+
113
+ class QueryRequest(BaseModel):
114
+ """Database query request"""
115
+ query: str = Field(..., description="Query string")
116
+ parameters: Dict[str, Any] = Field(default_factory=dict, description="Query parameters")
117
+
118
+
119
+ class QueryResponse(BaseModel):
120
+ """Database query response"""
121
+ success: bool
122
+ data: Optional[Any] = None
123
+ error: Optional[str] = None
124
+ execution_time: Optional[float] = None
125
+
126
+
127
+ class SchemaResponse(BaseModel):
128
+ """Database schema response"""
129
+ success: bool
130
+ data: Optional[Dict[str, Dict[str, str]]] = None
131
+ error: Optional[str] = None
132
+
133
+ class Category(BaseModel):
134
+ name: str
135
+ props: Optional[List[str]] = None
136
+ keys: Optional[List[str]] = None
137
+ propsTypes: Optional[Dict[str, str]] = None
138
+
139
+ class Relationship(BaseModel):
140
+ name: str
141
+ props: Optional[List[str]] = None
142
+ keys: Optional[List[str]] = None
143
+ propsTypes: Optional[Dict[str, str]] = None
144
+ startCategory: str
145
+ endCategory: str
146
+
147
+ class GraphSchema(BaseModel):
148
+ categories: List[Category]
149
+ relationships: List[Relationship]
150
+
151
+ class GraphSchemaResponse(BaseModel):
152
+ """Graph database schema response"""
153
+ success: bool
154
+ data: GraphSchema = Field(default_factory=lambda: GraphSchema(categories=[], relationships=[]))
155
+ error: Optional[str] = None
156
+
157
+ class SampleDataResponse(BaseModel):
158
+ """Sample data response"""
159
+ success: bool
160
+ data: Optional[Dict[str, Any]] = None
161
+ error: Optional[str] = None
162
+
163
+ class GraphData(BaseModel):
164
+ nodes: List[Dict[str, Any]] = Field(default_factory=list)
165
+ relationships: List[Dict[str, Any]] = Field(default_factory=list)
166
+
167
+ class QueryData(BaseModel):
168
+ type: Literal["TABLE", "GRAPH"]
169
+ data: List[Dict[str, Any]] | GraphData | None = None
170
+ summary: Dict[str, str] = Field(default_factory=lambda: {"version": "4.0.1"})