- Add Role model with 17 default permissions - Add init_wizard to create admin/guest roles on first startup - Protect admin role from modification/deletion via API - Fix MilestoneCreate schema (project_id optional) - Fix delete role to clean up role_permissions first - Add check_project_role RBAC function
248 lines
5.5 KiB
Python
248 lines
5.5 KiB
Python
from pydantic import BaseModel
|
|
from typing import Optional, List
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
|
|
class IssueTypeEnum(str, Enum):
|
|
MEETING = "meeting"
|
|
SUPPORT = "support"
|
|
ISSUE = "issue"
|
|
MAINTENANCE = "maintenance"
|
|
RESEARCH = "research"
|
|
REVIEW = "review"
|
|
STORY = "story"
|
|
TEST = "test"
|
|
RESOLUTION = "resolution"
|
|
TASK = "task" # legacy
|
|
|
|
|
|
class IssueStatusEnum(str, Enum):
|
|
OPEN = "open"
|
|
IN_PROGRESS = "in_progress"
|
|
RESOLVED = "resolved"
|
|
CLOSED = "closed"
|
|
BLOCKED = "blocked"
|
|
|
|
|
|
class IssuePriorityEnum(str, Enum):
|
|
LOW = "low"
|
|
MEDIUM = "medium"
|
|
HIGH = "high"
|
|
CRITICAL = "critical"
|
|
|
|
|
|
# Issue schemas
|
|
class IssueBase(BaseModel):
|
|
title: str
|
|
description: Optional[str] = None
|
|
issue_type: IssueTypeEnum = IssueTypeEnum.ISSUE
|
|
issue_subtype: Optional[str] = None
|
|
priority: IssuePriorityEnum = IssuePriorityEnum.MEDIUM
|
|
tags: Optional[str] = None
|
|
depends_on_id: Optional[int] = None
|
|
due_date: Optional[datetime] = None
|
|
milestone_id: Optional[int] = None
|
|
|
|
|
|
class IssueCreate(IssueBase):
|
|
project_id: int
|
|
reporter_id: int
|
|
assignee_id: Optional[int] = None
|
|
# Resolution specific
|
|
resolution_summary: Optional[str] = None
|
|
positions: Optional[str] = None
|
|
pending_matters: Optional[str] = None
|
|
|
|
|
|
class IssueUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
issue_type: Optional[IssueTypeEnum] = None
|
|
issue_subtype: Optional[str] = None
|
|
status: Optional[IssueStatusEnum] = None
|
|
priority: Optional[IssuePriorityEnum] = None
|
|
assignee_id: Optional[int] = None
|
|
tags: Optional[str] = None
|
|
depends_on_id: Optional[int] = None
|
|
due_date: Optional[datetime] = None
|
|
milestone_id: Optional[int] = None
|
|
# Resolution specific
|
|
resolution_summary: Optional[str] = None
|
|
positions: Optional[str] = None
|
|
pending_matters: Optional[str] = None
|
|
|
|
|
|
class IssueResponse(IssueBase):
|
|
id: int
|
|
status: IssueStatusEnum
|
|
project_id: int
|
|
reporter_id: int
|
|
assignee_id: Optional[int]
|
|
resolution_summary: Optional[str]
|
|
positions: Optional[str]
|
|
pending_matters: Optional[str]
|
|
due_date: Optional[datetime] = None
|
|
milestone_id: Optional[int] = None
|
|
created_at: datetime
|
|
updated_at: Optional[datetime]
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Comment schemas
|
|
class CommentBase(BaseModel):
|
|
content: str
|
|
|
|
|
|
class CommentCreate(CommentBase):
|
|
issue_id: int
|
|
author_id: int
|
|
|
|
|
|
class CommentUpdate(BaseModel):
|
|
content: Optional[str] = None
|
|
|
|
|
|
class CommentResponse(CommentBase):
|
|
id: int
|
|
issue_id: int
|
|
author_id: int
|
|
created_at: datetime
|
|
updated_at: Optional[datetime]
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Project schemas
|
|
class ProjectBase(BaseModel):
|
|
name: str
|
|
owner_name: Optional[str] = None
|
|
description: Optional[str] = None
|
|
sub_projects: Optional[list[str]] = None
|
|
related_projects: Optional[list[str]] = None
|
|
|
|
|
|
class ProjectCreate(ProjectBase):
|
|
owner_id: int
|
|
|
|
|
|
class ProjectUpdate(BaseModel):
|
|
description: Optional[str] = None
|
|
owner_name: Optional[str] = None
|
|
sub_projects: Optional[list[str]] = None
|
|
related_projects: Optional[list[str]] = None
|
|
|
|
|
|
class ProjectResponse(BaseModel):
|
|
id: int
|
|
name: str
|
|
owner_name: Optional[str] = None
|
|
project_code: Optional[str] = None
|
|
description: Optional[str] = None
|
|
sub_projects: Optional[list[str]] = None
|
|
related_projects: Optional[list[str]] = None
|
|
owner_id: int
|
|
created_at: datetime
|
|
|
|
class _ProjectResponse_Inactive(ProjectBase):
|
|
id: int
|
|
owner_id: int
|
|
project_code: str | None = None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# User schemas
|
|
class UserBase(BaseModel):
|
|
username: str
|
|
email: str
|
|
full_name: Optional[str] = None
|
|
|
|
|
|
class UserCreate(UserBase):
|
|
password: Optional[str] = None
|
|
is_admin: bool = False
|
|
|
|
|
|
class UserResponse(UserBase):
|
|
id: int
|
|
is_active: bool
|
|
is_admin: bool
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Project Member schemas
|
|
class ProjectMemberBase(BaseModel):
|
|
user_id: int
|
|
role: str = "dev"
|
|
|
|
|
|
class ProjectMemberCreate(ProjectMemberBase):
|
|
pass
|
|
|
|
|
|
class ProjectMemberResponse(BaseModel):
|
|
id: int
|
|
user_id: int
|
|
project_id: int
|
|
role: str = "dev"
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Milestone schemas
|
|
class MilestoneBase(BaseModel):
|
|
title: str
|
|
description: Optional[str] = None
|
|
status: Optional[str] = "open"
|
|
due_date: Optional[datetime] = None
|
|
planned_release_date: Optional[datetime] = None
|
|
depend_on_milestones: Optional[List[str]] = None
|
|
depend_on_tasks: Optional[List[int]] = None
|
|
|
|
|
|
class MilestoneCreate(MilestoneBase):
|
|
project_id: Optional[int] = None
|
|
pass
|
|
|
|
|
|
class MilestoneUpdate(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
status: Optional[str] = None
|
|
due_date: Optional[datetime] = None
|
|
planned_release_date: Optional[datetime] = None
|
|
depend_on_milestones: Optional[List[str]] = None
|
|
depend_on_tasks: Optional[List[int]] = None
|
|
|
|
|
|
class MilestoneResponse(MilestoneBase):
|
|
id: int
|
|
project_id: int
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# Paginated response
|
|
from typing import Generic, TypeVar
|
|
T = TypeVar("T")
|
|
|
|
class PaginatedResponse(BaseModel, Generic[T]):
|
|
items: List[T]
|
|
total: int
|
|
page: int
|
|
page_size: int
|
|
total_pages: int
|