Skip to content

Commit

Permalink
Merge pull request #813 from bruce-pain/refactor(products)/update-rou…
Browse files Browse the repository at this point in the history
…te-paths

refactor(products): Refactor Product Endpoints to Include Organizational Context
  • Loading branch information
joboy-dev authored Aug 8, 2024
2 parents 506e2cb + 0d38189 commit f09fc21
Show file tree
Hide file tree
Showing 26 changed files with 1,184 additions and 805 deletions.
3 changes: 2 additions & 1 deletion api/v1/routes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from api.v1.routes.auth import auth
from api.v1.routes.newsletter import newsletter
from api.v1.routes.user import user_router
from api.v1.routes.product import product
from api.v1.routes.product import product, non_organization_product
from api.v1.routes.product_comment import product_comment
from api.v1.routes.notification import notification
from api.v1.routes.testimonial import testimonial
Expand Down Expand Up @@ -53,6 +53,7 @@
api_version_one.include_router(user_router)
api_version_one.include_router(profile)
api_version_one.include_router(organization)
api_version_one.include_router(non_organization_product)
api_version_one.include_router(product)
api_version_one.include_router(payment)
api_version_one.include_router(bill_plan)
Expand Down
19 changes: 11 additions & 8 deletions api/v1/routes/billing_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
from api.v1.services.user import user_service
from api.v1.schemas.plans import CreateSubscriptionPlan

bill_plan = APIRouter(prefix='/organizations', tags=['Billing-Plan'])
bill_plan = APIRouter(prefix="/organisations", tags=["Billing-Plan"])

@bill_plan.get('/{organization_id}/billing-plans', response_model=success_response)

@bill_plan.get("/{organization_id}/billing-plans", response_model=success_response)
async def retrieve_all_billing_plans(
organization_id: str,
db: Session = Depends(get_db)
organization_id: str, db: Session = Depends(get_db)
):
"""
Endpoint to get all billing plans
Expand All @@ -33,6 +33,7 @@ async def retrieve_all_billing_plans(
},
)


@bill_plan.post("/billing-plans", response_model=success_response)
async def create_new_billing_plan(
request: CreateSubscriptionPlan,
Expand All @@ -51,12 +52,13 @@ async def create_new_billing_plan(
data=jsonable_encoder(plan),
)

@bill_plan.patch('/billing-plans/{billing_plan_id}', response_model=success_response)

@bill_plan.patch("/billing-plans/{billing_plan_id}", response_model=success_response)
async def update_a_billing_plan(
billing_plan_id: str,
request: CreateSubscriptionPlan,
current_user: User = Depends(user_service.get_current_super_admin),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
):
"""
Endpoint to update a billing plan by ID
Expand All @@ -70,11 +72,12 @@ async def update_a_billing_plan(
data=jsonable_encoder(plan),
)

@bill_plan.delete('/billing-plans/{billing_plan_id}', response_model=success_response)

@bill_plan.delete("/billing-plans/{billing_plan_id}", response_model=success_response)
async def delete_a_billing_plan(
billing_plan_id: str,
current_user: User = Depends(user_service.get_current_super_admin),
db: Session = Depends(get_db)
db: Session = Depends(get_db),
):
"""
Endpoint to delete a billing plan by ID
Expand Down
98 changes: 8 additions & 90 deletions api/v1/routes/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@
PaginatedOrgUsers,
OrganizationBase,
)
from api.v1.services.product import product_service
from api.v1.schemas.product import ProductCreate
from api.db.database import get_db
from api.v1.services.user import user_service
from api.v1.services.organization import organization_service
from api.v1.services.product import product_service
from api.v1.schemas.product import ProductDetail

from typing import Annotated

organization = APIRouter(prefix="/organizations", tags=["Organizations"])
organization = APIRouter(prefix="/organisations", tags=["Organizations"])


@organization.post(
Expand Down Expand Up @@ -66,19 +63,21 @@ async def get_organization_users(
return organization_service.paginate_users_in_organization(db, org_id, skip, limit)


@organization.get('/{org_id}/users/export', status_code=200)
@organization.get("/{org_id}/users/export", status_code=200)
async def export_organization_member_data_to_csv(
org_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_super_admin),
):
'''Endpoint to export organization users data to csv'''
"""Endpoint to export organization users data to csv"""

csv_file = organization_service.export_organization_members(db=db, org_id=org_id)

# Stream the response as a CSV file download
response = StreamingResponse(csv_file, media_type="text/csv")
response.headers["Content-Disposition"] = f"attachment; filename=organization_{org_id}_members.csv"
response.headers["Content-Disposition"] = (
f"attachment; filename=organization_{org_id}_members.csv"
)
response.status_code = 200

return response
Expand Down Expand Up @@ -114,47 +113,6 @@ def get_all_organizations(
data=jsonable_encoder(orgs),
)

@organization.post("/{org_id}/products", status_code=status.HTTP_201_CREATED)
def product_create(
org_id: str,
product: ProductCreate,
current_user: Annotated[User, Depends(user_service.get_current_user)],
db: Session = Depends(get_db),
):
created_product = product_service.create(
db=db, schema=product, org_id=org_id, current_user=current_user
)

return success_response(
status_code=status.HTTP_201_CREATED,
message="Product created successfully",
data=jsonable_encoder(created_product),
)

@organization.delete(
"/{org_id}/products/{product_id}", status_code=status.HTTP_204_NO_CONTENT
)
def delete_product(
org_id: str,
product_id: str,
current_user: User = Depends(user_service.get_current_user),
db: Session = Depends(get_db),
):
"""Enpoint to delete a product
Args:
product_id (str): The unique identifier of the product to be deleted
current_user (User): The currently authenticated user, obtained from the `get_current_user` dependency.
db (Session): The database session, provided by the `get_db` dependency.
Raises:
HTTPException: 401 FORBIDDEN (Current user is not a authenticated)
HTTPException: 404 NOT FOUND (Product to be deleted cannot be found)
"""

product_service.delete(
db=db, org_id=org_id, product_id=product_id, current_user=current_user
)

@organization.delete("/{org_id}")
async def delete_organization(
Expand All @@ -167,45 +125,5 @@ async def delete_organization(
organization_service.delete(db, id=org_id)
return success_response(
status_code=status.HTTP_200_OK,
message="Organization with ID {org_id} deleted successfully"
message="Organization with ID {org_id} deleted successfully",
)


@organization.get(
"/{org_id}/products/{product_id}",
response_model=dict[str, int | str | bool | ProductDetail],
summary="Get product detail",
description="Endpoint to get detail about the product with the given `id`",
)
async def get_product_detail(
org_id: str,
product_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user),
):
"""
Retrieve product detail
This endpoint retrieve details about a product
Args:
org_id (UUID): The unique identifier of the organization
product_id (UUID): The unique identifier of the product to be retrieved.
db (Session): The database session, provided by the `get_db` dependency.
current_user (User): The currently authenticated user, obtained from the `get_current_user` dependency.
Returns:
ProductDetail: The detail of the product matching the given id
Raises:
HTTPException: If the product with the specified `id` does not exist, a 404 error is raised.
"""

product = product_service.fetch_single_by_organization(db, org_id, product_id, current_user)

return {
"status_code": status.HTTP_200_OK,
"success": True,
"message": "Product fetched successfully",
"data": product,
}
71 changes: 43 additions & 28 deletions api/v1/routes/permissions/roles.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from fastapi import APIRouter, Depends, Path, Query, HTTPException, status
from api.v1.schemas.permissions.roles import (
RoleCreate, RoleResponse, RoleAssignRequest, RemoveUserFromRoleResponse
RoleCreate,
RoleResponse,
RoleAssignRequest,
RemoveUserFromRoleResponse,
)
from typing import List
from sqlalchemy.orm import Session
Expand All @@ -19,55 +22,66 @@

role_perm = APIRouter(tags=["permissions management"])


@role_perm.post("/custom/roles", tags=["Create Custom Role"])
def create_custom_role_endpoint(
role: RoleCreate,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)):
role: RoleCreate,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user),
):
# Ensure it's a custom role
role.is_builtin = False
return role_service.create_role(db, role)


@role_perm.post("/built-in/roles", tags=["Create Built-in Role"])
def create_built_in_role_endpoint(
role: RoleCreate,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_super_admin)): # Only super admin can create
role: RoleCreate,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_super_admin),
): # Only super admin can create
if not current_user.is_super_admin:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Only super admins can create built-in roles.")
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only super admins can create built-in roles.",
)
# ):

# Ensure it's a built-in role
role.is_builtin = True
return role_service.create_role(db, role)


@role_perm.post("/organizations/{org_id}/users/{user_id}/roles", tags=["assign role to a user"])
@role_perm.post(
"/organisations/{org_id}/users/{user_id}/roles", tags=["assign role to a user"]
)
def assign_role(
request: RoleAssignRequest,
org_id: str = Path(..., description="The ID of the organization"),
user_id: str = Path(..., description="The ID of the user"),
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
current_user: User = Depends(user_service.get_current_user),
):
return role_service.assign_role_to_user(db, org_id, user_id, request.role_id)


@role_perm.put("/organizations/{org_id}/users/{user_id}/roles/{role_id}",
response_model=RemoveUserFromRoleResponse)
@role_perm.put(
"/organisations/{org_id}/users/{user_id}/roles/{role_id}",
response_model=RemoveUserFromRoleResponse,
)
def remove_user_from_role(
org_id: str = Path(..., description="The ID of the organization"),
user_id: str = Path(..., description="The ID of the user"),
role_id: str = Path(..., description="The ID of the role"),
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
current_user: User = Depends(user_service.get_current_user),
):
"""
Endpoint to remove a user from a particular role by `admin`
"""
# GET org
org = org_service.fetch(db=db, id=org_id)

# CONFIRM current_user is admin
org_service.check_user_role_in_org(db, current_user, org, "admin")

Expand All @@ -81,26 +95,27 @@ def remove_user_from_role(
role_service.remove_user_from_role(db, org.id, user.id, role)

return success_response(
status_code=status.HTTP_200_OK,
message="User successfully removed from role"
status_code=status.HTTP_200_OK, message="User successfully removed from role"
)


@role_perm.delete("/roles/{role_id}", tags=["delete role"], response_model=success_response)
@role_perm.delete(
"/roles/{role_id}", tags=["delete role"], response_model=success_response
)
def delete_role(
role_id: str,
role_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user)
current_user: User = Depends(user_service.get_current_user),
):
""" An endpoint that fetches all product comment"""
role_service.delete_role(db, role_id)
return success_response(status_code=200, message="Role successfully deleted.", data={"id": role_id})

return success_response(
status_code=200, message="Role successfully deleted.", data={"id": role_id}
)




@role_perm.get(
"/organizations/{org_id}/roles",
"/organisations/{org_id}/roles",
response_model=List[RoleResponse],
tags=["Fetch Roles"],
)
Expand All @@ -118,20 +133,20 @@ def get_roles_for_organization(
return success_response(
status_code=status.HTTP_200_OK, message="Roles fetched successfully", data=roles
)


@role_perm.put("/roles/{role_id}/permissions", tags=["update role permissions"])
def update_role_permissions(
role_id: str,
permissions: List[str],
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_super_admin)
current_user: User = Depends(user_service.get_current_super_admin),
):
updated_role = role_service.update_role_permissions(db, role_id, permissions)
return success_response(
status_code=status.HTTP_200_OK,
message="Role permissions updated successfully",
data=updated_role
data=updated_role,
)


Expand Down
Loading

0 comments on commit f09fc21

Please sign in to comment.