Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
1 change: 1 addition & 0 deletions backend/api/Projetcs-service/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DB_USE = "JSONDB" # Change this to "PostgreSQL" or "MongoDB" as needed
45 changes: 45 additions & 0 deletions backend/api/Projetcs-service/projects_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from fastapi import APIRouter, HTTPException
from config import DB_USE
from src import ProjectCreate, ProjectOut
from src import get_repo

ProjectRouter = APIRouter()

db = get_repo(DB_USE)


@ProjectRouter.post("/projects/", response_model=ProjectOut)
def create_project(project: ProjectCreate):
"""Create a new project."""
return db.create_project(project)


@ProjectRouter.get("/projects/", response_model=list[ProjectOut])
def get_projects():
"""Get all projects."""
return db.get_projects()


@ProjectRouter.get("/projects/{project_id}", response_model=ProjectOut)
def get_project(project_id: str):
"""Get a project by ID."""
project = db.get_project(project_id)
if not project:
raise HTTPException(status_code=404, detail="Project not found")
return project


@ProjectRouter.delete("/projects/{project_id}")
def delete_project(project_id: str):
"""Delete a project by ID."""
db.delete_project(project_id)
return {"detail": "Project deleted"}


@ProjectRouter.put("/projects/{project_id}", response_model=ProjectOut)
def update_project(project_id: str, project: ProjectCreate):
"""Update a project by ID."""
updated_project = db.update_project(project_id, project)
if not updated_project:
raise HTTPException(status_code=404, detail="Project not found")
return updated_project
11 changes: 11 additions & 0 deletions backend/api/Projetcs-service/src/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from database.DBSelect import get_repo
from models.projects import Project
from schemas.projects_schema import ProjectCreate, ProjectOut


__all__ = [
"get_repo",
"Project",
"ProjectCreate",
"ProjectOut",
]
28 changes: 28 additions & 0 deletions backend/api/Projetcs-service/src/database/AbstradDB.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from abc import ABC, abstractmethod


class AbstractDB(ABC):
@abstractmethod
def create_project(self, project):
"""Create a new project in the database."""
pass

@abstractmethod
def get_projects(self):
"""Retrieve all projects from the database."""
pass

@abstractmethod
def get_project(self, project_id):
"""Retrieve a specific project by its ID."""
pass

@abstractmethod
def delete_project(self, project_id):
"""Delete a project from the database."""
pass

@abstractmethod
def update_project(self, project_id, project_data):
"""Update an existing project."""
pass
21 changes: 21 additions & 0 deletions backend/api/Projetcs-service/src/database/DBSelect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from database.JSONDB import JSONDB
from database.PostgreSQLDB import PostgreSQLDB
from sqlalchemy import SessionLocal
from pymongo import MongoClient
from database.MongoDB import MongoDB


def get_repo(db_type: str):
"""Get the appropriate database repository based on the type."""
if db_type == "JSONDB":
return JSONDB("projects.json")

elif db_type == "PostgreSQL":
return PostgreSQLDB(SessionLocal())

elif db_type == "MongoDB":
return MongoDB(MongoClient("mongodb://localhost:27017/"),
"projects_db")

else:
raise ValueError("Unknown DB type")
67 changes: 67 additions & 0 deletions backend/api/Projetcs-service/src/database/JSONDB.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from database.AbstradDB import AbstractDB
from models.projects import Project
from schemas.projects_schema import ProjectCreate
import json

file_path = "projectsDB.json"

# JSONDB is a simple file-based database for storing project data in JSON.
# It implements the AbstractDB interface and provides methods for creating,


class JSONDB(AbstractDB):
def __init__(self, file_path: str):
"""Initialize the JSONDB with a file path."""
self.file_path = file_path
self.projects = []
self.load_data()

def load_data(self):
"""Load data from the JSON file."""
try:
with open(self.file_path, "r") as file:
self.projects = json.load(file)
except FileNotFoundError:
self.projects = []

def save_data(self):
"""Save data to the JSON file."""
with open(self.file_path, "w") as file:
json.dump(self.projects, file, indent=4)

def create_project(self, project: ProjectCreate) -> Project:
"""Create a new project in the database."""
new_project = Project(**project.dict())
self.projects.append(new_project.dict())
self.save_data()
return new_project

def get_projects(self) -> list[Project]:
"""Retrieve all projects from the database."""
return [Project(**project) for project in self.projects]

def get_project(self, project_id: int) -> Project:
"""Retrieve a specific project by its ID."""
for project in self.projects:
if project["id"] == project_id:
return Project(**project)
return None

def delete_project(self, project_id: int) -> None:
"""Delete a project from the database."""
self.projects = [
project for project in self.projects if project["id"] != project_id
]
self.save_data()

def update_project(
self, project_id: int, project_data: ProjectCreate
) -> Project:
"""Update an existing project."""
for project in self.projects:
if project["id"] == project_id:
for key, value in project_data.dict().items():
project[key] = value
self.save_data()
return Project(**project)
return None
35 changes: 35 additions & 0 deletions backend/api/Projetcs-service/src/database/MongoDB.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from pymongo import MongoClient
from database.AbstradDB import AbstractDB
from models.projects import Project


class MondoDB(AbstractDB):
def __init__(self, uri: str, db_name: str):
"""Initialize the MongoDB client and database."""
self.uri = uri
self.client = MongoClient(uri)
self.db = self.client[db_name]
self.collection = self.db["projects"]

def create_project(self, project: Project) -> Project:
"""Create a new project in the database."""
project_dict = project.dict()
result = self.collection.insert_one(project_dict)
project.id = str(result.inserted_id)
return project

def get_projects(self) -> list[Project]:
"""Retrieve all projects from the database."""
projects = self.collection.find()
return [Project(**project) for project in projects]

def get_project(self, project_id: str) -> Project:
"""Retrieve a specific project by its ID."""
project = self.collection.find_one({"_id": project_id})
if project:
return Project(**project)
return None

def delete_project(self, project_id: str) -> None:
"""Delete a project from the database."""
self.collection.delete_one({"_id": project_id})
45 changes: 45 additions & 0 deletions backend/api/Projetcs-service/src/database/PostgreSQLDB.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from sqlalchemy.orm import Session
from models.projects import Project
from schemas.projects_schema import ProjectCreate
from database.AbstradDB import AbstractDB


class PostgreSQLDB(AbstractDB):
def __init__(self, db: Session):
self.db = db

def create_project(self, project: ProjectCreate) -> Project:
"""Create a new project in the database."""
db_project = Project(**project.dict())
self.db.add(db_project)
self.db.commit()
self.db.refresh(db_project)
return db_project

def get_projects(self) -> list[Project]:
"""Retrieve all projects from the database."""
return self.db.query(Project).all()

def get_project(self, project_id: int) -> Project:
"""Retrieve a specific project by its ID."""
return self.db.query(Project).filter(Project.id == project_id).first()

def delete_project(self, project_id: int) -> None:
"""Delete a project from the database."""
project = self.get_project(project_id)
if project:
self.db.delete(project)
self.db.commit()

def update_project(
self, project_id: int, project_data: ProjectCreate
) -> Project:
"""Update an existing project."""
project = self.get_project(project_id)
if project:
for key, value in project_data.dict().items():
setattr(project, key, value)
self.db.commit()
self.db.refresh(project)
return project
return None
10 changes: 10 additions & 0 deletions backend/api/Projetcs-service/src/models/projects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from sqlalchemy import Column, Integer, String, Text
from database import Base


class Project(Base):
__tablename__ = "projects"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), nullable=False)
description = Column(Text)
owner_id = Column(Integer, nullable=False)
20 changes: 20 additions & 0 deletions backend/api/Projetcs-service/src/schemas/projects_schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pydantic import BaseModel


class ProjectBase(BaseModel):
name: str
description: str | None = None
owner_id: int


class ProjectCreate(ProjectBase):
name: str
description: str


class ProjectOut(ProjectBase):
id: int
owner_id: int

class Config:
orm_mode = True
59 changes: 59 additions & 0 deletions backend/api/Projetcs-service/tests/project_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from unittest.mock import MagicMock
import pytest


class TestCodeUnderTest:

# create_project successfully adds a new project to the database
def test_create_project_success(self):
# Arrange
from sqlalchemy.orm import Session
from src import Project
from src import ProjectCreate
from src import create_project
# Mock session
mock_db = MagicMock(spec=Session)
# Create project data
project_data = {
"name": "Test Project",
"description": "Test Description",
"owner_id": 1
}
project_create = ProjectCreate(**project_data)
# Act
result = create_project(mock_db, project_create)
# Assert
mock_db.add.assert_called_once()
mock_db.commit.assert_called_once()
mock_db.refresh.assert_called_once()
assert isinstance(result, Project)
assert result.name == project_data["name"]
assert result.description == project_data["description"]
assert result.owner_id == project_data["owner_id"]

# create_project with missing required fields (name, owner_id)
def test_create_project_missing_required_fields(self):
# Arrange
from sqlalchemy.orm import Session
from src import ProjectCreate
from src import create_project
from sqlalchemy.exc import IntegrityError
# Mock session
mock_db = MagicMock(spec=Session)
# Set up the mock to raise IntegrityError when commit is called
mock_db.commit.side_effect = IntegrityError(
"(sqlite3.IntegrityError) NOT NULL constraint failed", None, None
)
# Create project with missing required fields
project_data = {
"description": "Test Description"
# Missing name and owner_id
}
project_create = ProjectCreate(**project_data)
# Act & Assert
with pytest.raises(IntegrityError):
create_project(mock_db, project_create)
# Verify the session interactions
mock_db.add.assert_called_once()
mock_db.commit.assert_called_once()
mock_db.refresh.assert_not_called()
Loading