Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create feature request api endpoint #1204

Open
wants to merge 15 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion alembic.ini
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne
# are written from script.py.mako
# output_encoding = utf-8

sqlalchemy.url =
sqlalchemy.url =


[post_write_hooks]
Expand Down
2 changes: 1 addition & 1 deletion api/v1/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@
from api.v1.models.wishlist import Wishlist
from api.v1.models.totp_device import TOTPDevice
from api.v1.models.bookmark import Bookmark

from api.v1.models.feature_request import FeatureRequest
26 changes: 26 additions & 0 deletions api/v1/models/feature_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
Feature Request data model
"""

from sqlalchemy import Column, String, text, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from api.v1.models.base_model import BaseTableModel


class FeatureRequest(BaseTableModel):
__tablename__ = "feature_requests"

title = Column(String, nullable=False)
description = Column(String, nullable=False)
priority = Column(String, server_default=text("'Low'")) # Low, Medium, High
status = Column(String, server_default=text("'Pending'")) # Pending, Approved, Rejected
is_deleted = Column(Boolean, server_default=text("false"))

# Foreign Keys
user_id = Column(String, ForeignKey("users.id"), nullable=False)

# Relationships
user = relationship("User", back_populates="feature_requests")

def __str__(self):
return self.title
4 changes: 4 additions & 0 deletions api/v1/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ class User(BaseTableModel):
"Bookmark", back_populates="user", cascade="delete"
)

feature_requests = relationship(
"FeatureRequest", back_populates="user", cascade="all, delete-orphan"
)

def to_dict(self):
obj_dict = super().to_dict()
obj_dict.pop("password")
Expand Down
2 changes: 2 additions & 0 deletions api/v1/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from api.v1.routes.terms_and_conditions import terms_and_conditions
from api.v1.routes.stripe import subscription_
from api.v1.routes.wishlist import wishlist
from api.v1.routes.feature_request import feature_request

api_version_one = APIRouter(prefix="/api/v1")

Expand Down Expand Up @@ -98,3 +99,4 @@
api_version_one.include_router(product_comment)
api_version_one.include_router(subscription_)
api_version_one.include_router(wishlist)
api_version_one.include_router(feature_request)
126 changes: 126 additions & 0 deletions api/v1/routes/feature_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from api.db.database import get_db
from api.v1.schemas.feature_request import (
FeatureRequestCreate,
FeatureRequestResponse,
FeatureRequestUpdate
)
from api.v1.services.feature_request import FeatureRequestService
from api.v1.services.user import user_service
from api.v1.models.user import User

feature_request = APIRouter(prefix="/feature-request", tags=["Feature Requests"])


@feature_request.post("/", response_model=FeatureRequestResponse, status_code=status.HTTP_201_CREATED)
def create_feature_request(
feature_request_data: FeatureRequestCreate,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
):
"""
Create a new feature request
"""
return FeatureRequestService.create_feature_request(db, feature_request_data, current_user.id)


@feature_request.get("/", response_model=List[FeatureRequestResponse])
def get_feature_requests(
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
):
"""
Get all feature requests
"""
# If user is a superadmin, return all feature requests
if current_user.is_superadmin:
return FeatureRequestService.get_feature_requests(db, skip, limit)
# Otherwise, return only the user's feature requests
return FeatureRequestService.get_user_feature_requests(db, current_user.id, skip, limit)


@feature_request.get("/{feature_request_id}", response_model=FeatureRequestResponse)
def get_feature_request(
feature_request_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
):
"""
Get a feature request by ID
"""
feature_request = FeatureRequestService.get_feature_request_by_id(db, feature_request_id)
if not feature_request:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Feature request not found"
)

# Check if the user is allowed to access this feature request
if not current_user.is_superadmin and feature_request.user_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to access this feature request"
)

return feature_request


@feature_request.put("/{feature_request_id}", response_model=FeatureRequestResponse)
def update_feature_request(
feature_request_id: str,
feature_request_update: FeatureRequestUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
):
"""
Update a feature request
"""
existing_feature_request = FeatureRequestService.get_feature_request_by_id(db, feature_request_id)
if not existing_feature_request:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Feature request not found"
)

# Check if the user is allowed to update this feature request
if not current_user.is_superadmin and existing_feature_request.user_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to update this feature request"
)

updated_feature_request = FeatureRequestService.update_feature_request(
db, feature_request_id, feature_request_update
)
return updated_feature_request


@feature_request.delete("/{feature_request_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_feature_request(
feature_request_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
):
"""
Delete a feature request
"""
existing_feature_request = FeatureRequestService.get_feature_request_by_id(db, feature_request_id)
if not existing_feature_request:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Feature request not found"
)

# Check if the user is allowed to delete this feature request
if not current_user.is_superadmin and existing_feature_request.user_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not authorized to delete this feature request"
)

FeatureRequestService.delete_feature_request(db, feature_request_id)
return None
36 changes: 36 additions & 0 deletions api/v1/schemas/feature_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field


class FeatureRequestBase(BaseModel):
title: str = Field(..., description="Title of the feature request")
description: str = Field(..., description="Detailed description of the requested feature")
priority: str = Field(default="Low", description="Priority level (Low, Medium, High)")
status: str = Field(default="Pending", description="Status (Pending, Approved, Rejected)")


class FeatureRequestCreate(FeatureRequestBase):
pass


class FeatureRequestUpdate(BaseModel):
title: Optional[str] = Field(None, description="Title of the feature request")
description: Optional[str] = Field(None, description="Detailed description of the requested feature")
priority: Optional[str] = Field(None, description="Priority level (Low, Medium, High)")
status: Optional[str] = Field(None, description="Status (Pending, Approved, Rejected)")


class FeatureRequestInDB(FeatureRequestBase):
id: str
created_at: datetime
updated_at: datetime
user_id: str
is_deleted: bool = False

class Config:
orm_mode = True


class FeatureRequestResponse(FeatureRequestInDB):
pass
77 changes: 77 additions & 0 deletions api/v1/services/feature_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from api.v1.models.feature_request import FeatureRequest
from api.v1.schemas.feature_request import FeatureRequestCreate, FeatureRequestUpdate


class FeatureRequestService:
@staticmethod
def create_feature_request(db: Session, feature_request_data: FeatureRequestCreate, user_id: str):
db_feature_request = FeatureRequest(**feature_request_data.dict(), user_id=user_id)
db.add(db_feature_request)
db.commit()
db.refresh(db_feature_request)
return db_feature_request

@staticmethod
def get_feature_requests(db: Session, skip: int = 0, limit: int = 100):
return db.query(FeatureRequest).offset(skip).limit(limit).all()

@staticmethod
def get_user_feature_requests(db: Session, user_id: str, skip: int = 0, limit: int = 100):
return db.query(FeatureRequest).filter(FeatureRequest.user_id == user_id).offset(skip).limit(limit).all()

@staticmethod
def get_feature_request_by_id(db: Session, feature_request_id: str):
return db.query(FeatureRequest).filter(FeatureRequest.id == feature_request_id).first()

@staticmethod
def update_feature_request(db: Session, feature_request_id: str, feature_request_update: FeatureRequestUpdate):
db_feature_request = db.query(FeatureRequest).filter(FeatureRequest.id == feature_request_id).first()
if db_feature_request:
for key, value in feature_request_update.dict(exclude_unset=True).items():
setattr(db_feature_request, key, value)
db.commit()
db.refresh(db_feature_request)
return db_feature_request

@staticmethod
def delete_feature_request(db: Session, feature_request_id: str):
db_feature_request = db.query(FeatureRequest).filter(FeatureRequest.id == feature_request_id).first()
if db_feature_request:
db.delete(db_feature_request)
db.commit()
return db_feature_request
class FeatureRequestBase(BaseModel):
title: str = Field(..., description="Title of the feature request")
description: str = Field(..., description="Detailed description of the requested feature")
priority: str = Field(default="Low", description="Priority level (Low, Medium, High)")
status: str = Field(default="Pending", description="Status (Pending, Approved, Rejected)")


class FeatureRequestCreate(FeatureRequestBase):
pass


class FeatureRequestUpdate(BaseModel):
title: Optional[str] = Field(None, description="Title of the feature request")
description: Optional[str] = Field(None, description="Detailed description of the requested feature")
priority: Optional[str] = Field(None, description="Priority level (Low, Medium, High)")
status: Optional[str] = Field(None, description="Status (Pending, Approved, Rejected)")


class FeatureRequestInDB(FeatureRequestBase):
id: str
created_at: datetime
updated_at: datetime
user_id: str
is_deleted: bool = False

class Config:
orm_mode = True


class FeatureRequestResponse(FeatureRequestInDB):
pass
Binary file added test.db
Binary file not shown.
Loading