Skip to content

Commit

Permalink
Project routes and Github Route (#13)
Browse files Browse the repository at this point in the history
* added project routes, added docker container in docker compose,updated envtemplate and added fix for ssl error for mongo in development

* added project routes

* removed redundant code

* Delete firebase_service_account.json:Zone.Identifier

* added github routes

* separated code from router and added controller methods

* updated env template

* updated Project and Github Controller

* added back connection pooling
  • Loading branch information
GodReaper authored Aug 28, 2024
1 parent 3132a65 commit 04f1ab3
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 19 deletions.
19 changes: 17 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
ENV= development
OPENAI_API_KEY=
OPENAI_MODEL_REASONING=
OPENAI_MODEL_REASONING=
# POSTGRES_SERVER=postgresql://postgres:mysecretpassword@host.docker.internal:5432/momentum Use this when using WSL
POSTGRES_SERVER=postgresql://postgres:mysecretpassword@localhost:5432/momentum
defaultUsername= defaultuser
MONGO_URI=mongodb://127.0.0.1:27017
MONGODB_DB_NAME=momentum
NEO4J_URI=bolt://127.0.0.1:7687
NEO4J_USERNAME=neo4j
NEO4J_PASSWORD=mysecretpassword
REDISHOST=127.0.0.1
REDISPORT=6379
BROKER_URL=redis://127.0.0.1:6379/0
CELERY_QUEUE_NAME=dev
PORTKEY_API_KEY=
GITHUB_PRIVATE_KEY=
GITHUB_APP_ID=
GCP_PROJECT=
defaultUsername= defaultuser
FIREBASE_SERVICE_ACCOUNT=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*__pycache__
.venv
firebase_service_account.json
firebase_service_account.json:Zone.Identifier
cli/dist
cli/.venv
venv/
Expand Down
52 changes: 41 additions & 11 deletions app/core/mongo_manager.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
import os
from typing import Optional

from mongoengine import connect, connection
import certifi
from pymongo import MongoClient
from pymongo.errors import ConnectionFailure, OperationFailure
Expand All @@ -23,19 +23,49 @@ def __init__(self):
raise RuntimeError("Use get_instance() to get the MongoManager instance")
self._connect()

def _connect(self):
if self._client is None:
try:
mongodb_uri = os.environ.get("MONGO_URI")
if not mongodb_uri:
raise ValueError("MONGO_URI environment variable is not set")

self._client = MongoClient(
mongodb_uri,
def get_mongo_connection(self):
try:
db_name = os.getenv("MONGODB_DB_NAME", "test")
mongo_uri = os.getenv("MONGO_URI", "mongodb://localhost:27017")
env = os.getenv("ENV", "development") # Assume development if ENV is not set

if not mongo_uri:
raise ValueError("MONGO_URI environment variable is not set")

if not db_name:
raise ValueError("MONGODB_DB_NAME environment variable is not set")

# Establish the connection based on the environment
if env in ["production", "staging"]:
self.client = MongoClient(
mongo_uri,
maxPoolSize=50,
waitQueueTimeoutMS=2500,
tlsCAFile=certifi.where(), # Use the certifi package to locate the CA bundle
tlsCAFile=certifi.where(),
)
else:
self.client = MongoClient(
mongo_uri,
maxPoolSize=50,
waitQueueTimeoutMS=2500,
)


# Return the established connection
db_connection = self.client[db_name]
db_connection.command("ping") # Verify the connection
return self.client


except (ConnectionFailure, ValueError) as e:
logging.error(f"Failed to connect to MongoDB: {str(e)}")
raise

def _connect(self):
if self._client is None:
try:
# Establish connection using the utility function
self._client = self.get_mongo_connection()

db_name = os.environ.get("MONGODB_DB_NAME")
if not db_name:
Expand Down
5 changes: 5 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
)
from app.modules.search.search_router import router as search_router
from app.modules.users.user_router import router as user_router
from app.modules.projects.projects_router import router as projects_router
from app.modules.github.github_router import router as github_router
from app.modules.utils.dummy_setup import DummyDataSetup
from app.modules.utils.firebase_setup import FirebaseSetup

Expand Down Expand Up @@ -89,7 +91,10 @@ def include_routers(self):
self.app.include_router(
secret_manager_router, prefix="/api/v1", tags=["Secret Manager"]
)

self.app.include_router(projects_router, prefix="/api/v1", tags=["Projects"])
self.app.include_router(search_router, prefix="/api/v1/search", tags=["search"])
self.app.include_router(github_router, prefix="/api/v1", tags=["Github"])

def add_health_check(self):
@self.app.get("/health", tags=["Health"])
Expand Down
12 changes: 12 additions & 0 deletions app/modules/github/github_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from fastapi import Depends, HTTPException
from app.modules.auth.auth_service import AuthService
from app.modules.github.github_service import GithubService

class GithubController:
@staticmethod
def get_user_repos(user=Depends(AuthService.check_auth)):
return GithubService.get_repos_for_user(user)

@staticmethod
def get_branch_list(repo_name: str, user=Depends(AuthService.check_auth)):
return GithubService.get_branch_list(repo_name)
17 changes: 17 additions & 0 deletions app/modules/github/github_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from fastapi import Depends, Query
from app.modules.utils.APIRouter import APIRouter
from app.modules.auth.auth_service import AuthService
from .github_controller import GithubController

router = APIRouter()

@router.get("/github/user-repos")
def get_user_repos(user=Depends(AuthService.check_auth)):
return GithubController.get_user_repos(user=user)

@router.get("/github/get-branch-list")
def get_branch_list(
repo_name: str = Query(..., description="Repository name"),
user=Depends(AuthService.check_auth)
):
return GithubController.get_branch_list(repo_name=repo_name, user=user)
79 changes: 77 additions & 2 deletions app/modules/github/github_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self, db: Session):
self.project_manager = ProjectService(db)

@staticmethod
def get_github_repo_details(repo_name):
def get_github_repo_details(repo_name: str):
private_key = (
"-----BEGIN RSA PRIVATE KEY-----\n"
+ config_provider.get_github_key()
Expand All @@ -38,8 +38,14 @@ def get_github_repo_details(repo_name):
"Authorization": f"Bearer {jwt}",
"X-GitHub-Api-Version": "2022-11-28",
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise HTTPException(status_code=400, detail="Failed to get installation ID")
app_auth = auth.get_installation_auth(response.json()["id"])

github = Github(auth=app_auth)

return requests.get(url, headers=headers), auth, owner
return github, response, auth, owner

@staticmethod
def check_is_commit_added(repo_details, project_details, branch_name):
Expand Down Expand Up @@ -92,3 +98,72 @@ def fetch_method_from_repo(node, db):
logger.error(f"An error occurred: {e}", exc_info=True)

return method_content

@staticmethod
def get_repos_for_user(user):
try:
private_key = (
"-----BEGIN RSA PRIVATE KEY-----\n"
+ config_provider.get_github_key()
+ "\n-----END RSA PRIVATE KEY-----\n"
)
app_id = os.environ["GITHUB_APP_ID"]
auth = AppAuth(app_id=app_id, private_key=private_key)
jwt = auth.create_jwt()

url = f"https://api.github.com/app/installations"
headers = {
"Accept": "application/vnd.github+json",
"Authorization": f"Bearer {jwt}",
"X-GitHub-Api-Version": "2022-11-28",
}
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise HTTPException(status_code=400, detail="Failed to get installations")

installations = response.json()
repos = []

for installation in installations:
app_auth = auth.get_installation_auth(installation["id"])
github = Github(auth=app_auth)
repos_url = installation["repositories_url"]
repos_response = requests.get(repos_url, headers={"Authorization": f"Bearer {app_auth.token}"})
if repos_response.status_code == 200:
repos.extend(repos_response.json().get('repositories', []))
else:
logger.error(f"Failed to fetch repositories for installation ID {installation['id']}")
repo_list = [
{
"id": repo["id"],
"name": repo["name"],
"full_name": repo["full_name"],
"private": repo["private"],
"url": repo["html_url"],
"owner": repo["owner"]["login"],
}
for repo in repos
]
return {"repositories": repo_list}
except Exception as e:
logger.error(f"Failed to fetch repositories: {str(e)}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Failed to fetch repositories: {str(e)}"
)


@staticmethod
def get_branch_list(repo_name: str):
try:
github_client, _, _, _ = GithubService.get_github_repo_details(repo_name)
repo = github_client.get_repo(repo_name)
branches = repo.get_branches()
branch_list = [branch.name for branch in branches]
return {"branches": branch_list}
except Exception as e:
logger.error(f"Error fetching branches for repo {repo_name}: {str(e)}", exc_info=True)
raise HTTPException(
status_code=404,
detail=f"Repository not found or error fetching branches: {str(e)}"
)
32 changes: 32 additions & 0 deletions app/modules/projects/projects_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from fastapi import Depends, HTTPException
from starlette.responses import JSONResponse
from app.core.database import get_db
from app.modules.auth.auth_service import AuthService
from app.modules.projects.projects_service import ProjectService

class ProjectController:

@staticmethod
async def get_project_list(user=Depends(AuthService.check_auth), db = Depends(get_db)):
user_id = user["user_id"]
try:
project_service = ProjectService(db)
project_list = await project_service.list_projects(user_id)
return project_list
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

@staticmethod
def delete_project(project_id: int, user=Depends(AuthService.check_auth), db=Depends(get_db)):
project_service = ProjectService(db)
try:
project_service.delete_project(project_id)
return JSONResponse(
status_code=200,
content={
"message": "Project deleted successfully.",
"id": project_id
}
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"{str(e)}")
15 changes: 15 additions & 0 deletions app/modules/projects/projects_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from fastapi import Depends
from app.modules.utils.APIRouter import APIRouter
from app.modules.auth.auth_service import AuthService
from app.core.database import get_db
from .projects_controller import ProjectController

router = APIRouter()

@router.get("/projects/list")
async def get_project_list(user=Depends(AuthService.check_auth), db=Depends(get_db)):
return await ProjectController.get_project_list(user=user, db=db)

@router.delete("/projects")
def delete_project(project_id: int, user=Depends(AuthService.check_auth), db=Depends(get_db)):
return ProjectController.delete_project(project_id=project_id, user=user, db=db)
7 changes: 6 additions & 1 deletion app/modules/projects/projects_schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from enum import Enum

from typing import Optional
from pydantic import BaseModel, Field

class ProjectStatusEnum(str, Enum):
SUBMITTED = "submitted"
Expand All @@ -8,3 +9,7 @@ class ProjectStatusEnum(str, Enum):
PROCESSING = "processing"
READY = "ready"
ERROR = "error"


class RepoDetails(BaseModel):
repo_name: str
23 changes: 20 additions & 3 deletions app/modules/projects/projects_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from sqlalchemy import and_
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from sqlalchemy import cast, String

from app.modules.projects.projects_model import Project
from app.modules.projects.projects_schema import ProjectStatusEnum
Expand Down Expand Up @@ -312,9 +313,13 @@ def update_project(db: Session, project_id: int, **kwargs):

return None

def delete_project(db: Session, project_id: int):
db.query(Project).filter(Project.id == project_id).delete()
db.commit()
def delete_project(self, project_id: int):
project = self.db.query(Project).filter(cast(Project.id, String) == str(project_id)).first()
if not project:
raise HTTPException(status_code=404, detail="Project not found.")
self.db.delete(project)
self.db.commit()


def get_projects_by_repo_name(
db: Session, repo_name: str, user_id: str, is_deleted: bool = False
Expand All @@ -339,3 +344,15 @@ def get_projects_by_repo_name(
logging.error(f"Error fetching projects: {str(e)}")
# You might want to raise a custom exception here instead of returning None
return None

async def list_projects(self, user_id: str):
projects = ProjectService.get_projects_by_user_id(self.db, user_id)
project_list = []
for project in projects:
project_dict = {
"id": project.id,
"directory": project.directory,
"active": project.is_default,
}
project_list.append(project_dict)
return project_list
11 changes: 11 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ services:
networks:
- app-network

mongodb:
image: mongo:latest
container_name: mongodb
restart: always
environment:
- MONGO_INITDB_DATABASE=momentum
ports:
- "27017:27017"
networks:
- app-network

redis:
image: redis:latest
container_name: redis_broker
Expand Down

0 comments on commit 04f1ab3

Please sign in to comment.