|
|
|
|
@ -1,8 +1,9 @@
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
from flask_restx import Resource, fields, marshal_with, reqparse
|
|
|
|
|
from flask_restx import Resource, fields, marshal_with
|
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
|
|
|
|
from controllers.console import api
|
|
|
|
|
from controllers.console import console_ns
|
|
|
|
|
from controllers.console.app.wraps import get_app_model
|
|
|
|
|
from controllers.console.wraps import account_initialization_required, setup_required
|
|
|
|
|
from fields.member_fields import account_with_role_fields
|
|
|
|
|
@ -21,57 +22,117 @@ from services.account_service import TenantService
|
|
|
|
|
from services.workflow_comment_service import WorkflowCommentService
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
DEFAULT_REF_TEMPLATE_SWAGGER_2_0 = "#/definitions/{model}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WorkflowCommentCreatePayload(BaseModel):
|
|
|
|
|
position_x: float = Field(..., description="Comment X position")
|
|
|
|
|
position_y: float = Field(..., description="Comment Y position")
|
|
|
|
|
content: str = Field(..., description="Comment content")
|
|
|
|
|
mentioned_user_ids: list[str] = Field(default_factory=list, description="Mentioned user IDs")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WorkflowCommentUpdatePayload(BaseModel):
|
|
|
|
|
content: str = Field(..., description="Comment content")
|
|
|
|
|
position_x: float | None = Field(default=None, description="Comment X position")
|
|
|
|
|
position_y: float | None = Field(default=None, description="Comment Y position")
|
|
|
|
|
mentioned_user_ids: list[str] = Field(default_factory=list, description="Mentioned user IDs")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WorkflowCommentReplyCreatePayload(BaseModel):
|
|
|
|
|
content: str = Field(..., description="Reply content")
|
|
|
|
|
mentioned_user_ids: list[str] = Field(default_factory=list, description="Mentioned user IDs")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WorkflowCommentReplyUpdatePayload(BaseModel):
|
|
|
|
|
content: str = Field(..., description="Reply content")
|
|
|
|
|
mentioned_user_ids: list[str] = Field(default_factory=list, description="Mentioned user IDs")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for model in (
|
|
|
|
|
WorkflowCommentCreatePayload,
|
|
|
|
|
WorkflowCommentUpdatePayload,
|
|
|
|
|
WorkflowCommentReplyCreatePayload,
|
|
|
|
|
WorkflowCommentReplyUpdatePayload,
|
|
|
|
|
):
|
|
|
|
|
console_ns.schema_model(model.__name__, model.model_json_schema(ref_template=DEFAULT_REF_TEMPLATE_SWAGGER_2_0))
|
|
|
|
|
|
|
|
|
|
workflow_comment_basic_model = console_ns.model("WorkflowCommentBasic", workflow_comment_basic_fields)
|
|
|
|
|
workflow_comment_detail_model = console_ns.model("WorkflowCommentDetail", workflow_comment_detail_fields)
|
|
|
|
|
workflow_comment_create_model = console_ns.model("WorkflowCommentCreate", workflow_comment_create_fields)
|
|
|
|
|
workflow_comment_update_model = console_ns.model("WorkflowCommentUpdate", workflow_comment_update_fields)
|
|
|
|
|
workflow_comment_resolve_model = console_ns.model("WorkflowCommentResolve", workflow_comment_resolve_fields)
|
|
|
|
|
workflow_comment_reply_create_model = console_ns.model(
|
|
|
|
|
"WorkflowCommentReplyCreate", workflow_comment_reply_create_fields
|
|
|
|
|
)
|
|
|
|
|
workflow_comment_reply_update_model = console_ns.model(
|
|
|
|
|
"WorkflowCommentReplyUpdate", workflow_comment_reply_update_fields
|
|
|
|
|
)
|
|
|
|
|
workflow_comment_mention_users_model = console_ns.model(
|
|
|
|
|
"WorkflowCommentMentionUsers",
|
|
|
|
|
{"users": fields.List(fields.Nested(account_with_role_fields))},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@console_ns.route("/apps/<uuid:app_id>/workflow/comments")
|
|
|
|
|
class WorkflowCommentListApi(Resource):
|
|
|
|
|
"""API for listing and creating workflow comments."""
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("list_workflow_comments")
|
|
|
|
|
@console_ns.doc(description="Get all comments for a workflow")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID"})
|
|
|
|
|
@console_ns.response(200, "Comments retrieved successfully", workflow_comment_basic_model)
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@marshal_with(workflow_comment_basic_fields, envelope="data")
|
|
|
|
|
@get_app_model()
|
|
|
|
|
@marshal_with(workflow_comment_basic_model, envelope="data")
|
|
|
|
|
def get(self, app_model: App):
|
|
|
|
|
"""Get all comments for a workflow."""
|
|
|
|
|
comments = WorkflowCommentService.get_comments(tenant_id=current_user.current_tenant_id, app_id=app_model.id)
|
|
|
|
|
|
|
|
|
|
return comments
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("create_workflow_comment")
|
|
|
|
|
@console_ns.doc(description="Create a new workflow comment")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID"})
|
|
|
|
|
@console_ns.expect(console_ns.models[WorkflowCommentCreatePayload.__name__])
|
|
|
|
|
@console_ns.response(201, "Comment created successfully", workflow_comment_create_model)
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@marshal_with(workflow_comment_create_fields)
|
|
|
|
|
@get_app_model()
|
|
|
|
|
@marshal_with(workflow_comment_create_model)
|
|
|
|
|
def post(self, app_model: App):
|
|
|
|
|
"""Create a new workflow comment."""
|
|
|
|
|
parser = reqparse.RequestParser()
|
|
|
|
|
parser.add_argument("position_x", type=float, required=True, location="json")
|
|
|
|
|
parser.add_argument("position_y", type=float, required=True, location="json")
|
|
|
|
|
parser.add_argument("content", type=str, required=True, location="json")
|
|
|
|
|
parser.add_argument("mentioned_user_ids", type=list, location="json", default=[])
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
payload = WorkflowCommentCreatePayload.model_validate(console_ns.payload or {})
|
|
|
|
|
|
|
|
|
|
result = WorkflowCommentService.create_comment(
|
|
|
|
|
tenant_id=current_user.current_tenant_id,
|
|
|
|
|
app_id=app_model.id,
|
|
|
|
|
created_by=current_user.id,
|
|
|
|
|
content=args.content,
|
|
|
|
|
position_x=args.position_x,
|
|
|
|
|
position_y=args.position_y,
|
|
|
|
|
mentioned_user_ids=args.mentioned_user_ids,
|
|
|
|
|
content=payload.content,
|
|
|
|
|
position_x=payload.position_x,
|
|
|
|
|
position_y=payload.position_y,
|
|
|
|
|
mentioned_user_ids=payload.mentioned_user_ids,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return result, 201
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/<string:comment_id>")
|
|
|
|
|
class WorkflowCommentDetailApi(Resource):
|
|
|
|
|
"""API for managing individual workflow comments."""
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("get_workflow_comment")
|
|
|
|
|
@console_ns.doc(description="Get a specific workflow comment")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID", "comment_id": "Comment ID"})
|
|
|
|
|
@console_ns.response(200, "Comment retrieved successfully", workflow_comment_detail_model)
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@marshal_with(workflow_comment_detail_fields)
|
|
|
|
|
@get_app_model()
|
|
|
|
|
@marshal_with(workflow_comment_detail_model)
|
|
|
|
|
def get(self, app_model: App, comment_id: str):
|
|
|
|
|
"""Get a specific workflow comment."""
|
|
|
|
|
comment = WorkflowCommentService.get_comment(
|
|
|
|
|
@ -80,37 +141,41 @@ class WorkflowCommentDetailApi(Resource):
|
|
|
|
|
|
|
|
|
|
return comment
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("update_workflow_comment")
|
|
|
|
|
@console_ns.doc(description="Update a workflow comment")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID", "comment_id": "Comment ID"})
|
|
|
|
|
@console_ns.expect(console_ns.models[WorkflowCommentUpdatePayload.__name__])
|
|
|
|
|
@console_ns.response(200, "Comment updated successfully", workflow_comment_update_model)
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@marshal_with(workflow_comment_update_fields)
|
|
|
|
|
@get_app_model()
|
|
|
|
|
@marshal_with(workflow_comment_update_model)
|
|
|
|
|
def put(self, app_model: App, comment_id: str):
|
|
|
|
|
"""Update a workflow comment."""
|
|
|
|
|
parser = reqparse.RequestParser()
|
|
|
|
|
parser.add_argument("content", type=str, required=True, location="json")
|
|
|
|
|
parser.add_argument("position_x", type=float, required=False, location="json")
|
|
|
|
|
parser.add_argument("position_y", type=float, required=False, location="json")
|
|
|
|
|
parser.add_argument("mentioned_user_ids", type=list, location="json", default=[])
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
payload = WorkflowCommentUpdatePayload.model_validate(console_ns.payload or {})
|
|
|
|
|
|
|
|
|
|
result = WorkflowCommentService.update_comment(
|
|
|
|
|
tenant_id=current_user.current_tenant_id,
|
|
|
|
|
app_id=app_model.id,
|
|
|
|
|
comment_id=comment_id,
|
|
|
|
|
user_id=current_user.id,
|
|
|
|
|
content=args.content,
|
|
|
|
|
position_x=args.position_x,
|
|
|
|
|
position_y=args.position_y,
|
|
|
|
|
mentioned_user_ids=args.mentioned_user_ids,
|
|
|
|
|
content=payload.content,
|
|
|
|
|
position_x=payload.position_x,
|
|
|
|
|
position_y=payload.position_y,
|
|
|
|
|
mentioned_user_ids=payload.mentioned_user_ids,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("delete_workflow_comment")
|
|
|
|
|
@console_ns.doc(description="Delete a workflow comment")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID", "comment_id": "Comment ID"})
|
|
|
|
|
@console_ns.response(204, "Comment deleted successfully")
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@get_app_model()
|
|
|
|
|
def delete(self, app_model: App, comment_id: str):
|
|
|
|
|
"""Delete a workflow comment."""
|
|
|
|
|
WorkflowCommentService.delete_comment(
|
|
|
|
|
@ -123,14 +188,19 @@ class WorkflowCommentDetailApi(Resource):
|
|
|
|
|
return {"result": "success"}, 204
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/resolve")
|
|
|
|
|
class WorkflowCommentResolveApi(Resource):
|
|
|
|
|
"""API for resolving and reopening workflow comments."""
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("resolve_workflow_comment")
|
|
|
|
|
@console_ns.doc(description="Resolve a workflow comment")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID", "comment_id": "Comment ID"})
|
|
|
|
|
@console_ns.response(200, "Comment resolved successfully", workflow_comment_resolve_model)
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@marshal_with(workflow_comment_resolve_fields)
|
|
|
|
|
@get_app_model()
|
|
|
|
|
@marshal_with(workflow_comment_resolve_model)
|
|
|
|
|
def post(self, app_model: App, comment_id: str):
|
|
|
|
|
"""Resolve a workflow comment."""
|
|
|
|
|
comment = WorkflowCommentService.resolve_comment(
|
|
|
|
|
@ -143,14 +213,20 @@ class WorkflowCommentResolveApi(Resource):
|
|
|
|
|
return comment
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/replies")
|
|
|
|
|
class WorkflowCommentReplyApi(Resource):
|
|
|
|
|
"""API for managing comment replies."""
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("create_workflow_comment_reply")
|
|
|
|
|
@console_ns.doc(description="Add a reply to a workflow comment")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID", "comment_id": "Comment ID"})
|
|
|
|
|
@console_ns.expect(console_ns.models[WorkflowCommentReplyCreatePayload.__name__])
|
|
|
|
|
@console_ns.response(201, "Reply created successfully", workflow_comment_reply_create_model)
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@marshal_with(workflow_comment_reply_create_fields)
|
|
|
|
|
@get_app_model()
|
|
|
|
|
@marshal_with(workflow_comment_reply_create_model)
|
|
|
|
|
def post(self, app_model: App, comment_id: str):
|
|
|
|
|
"""Add a reply to a workflow comment."""
|
|
|
|
|
# Validate comment access first
|
|
|
|
|
@ -158,29 +234,32 @@ class WorkflowCommentReplyApi(Resource):
|
|
|
|
|
comment_id=comment_id, tenant_id=current_user.current_tenant_id, app_id=app_model.id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
parser = reqparse.RequestParser()
|
|
|
|
|
parser.add_argument("content", type=str, required=True, location="json")
|
|
|
|
|
parser.add_argument("mentioned_user_ids", type=list, location="json", default=[])
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
payload = WorkflowCommentReplyCreatePayload.model_validate(console_ns.payload or {})
|
|
|
|
|
|
|
|
|
|
result = WorkflowCommentService.create_reply(
|
|
|
|
|
comment_id=comment_id,
|
|
|
|
|
content=args.content,
|
|
|
|
|
content=payload.content,
|
|
|
|
|
created_by=current_user.id,
|
|
|
|
|
mentioned_user_ids=args.mentioned_user_ids,
|
|
|
|
|
mentioned_user_ids=payload.mentioned_user_ids,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return result, 201
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/replies/<string:reply_id>")
|
|
|
|
|
class WorkflowCommentReplyDetailApi(Resource):
|
|
|
|
|
"""API for managing individual comment replies."""
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("update_workflow_comment_reply")
|
|
|
|
|
@console_ns.doc(description="Update a comment reply")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID", "comment_id": "Comment ID", "reply_id": "Reply ID"})
|
|
|
|
|
@console_ns.expect(console_ns.models[WorkflowCommentReplyUpdatePayload.__name__])
|
|
|
|
|
@console_ns.response(200, "Reply updated successfully", workflow_comment_reply_update_model)
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@marshal_with(workflow_comment_reply_update_fields)
|
|
|
|
|
@get_app_model()
|
|
|
|
|
@marshal_with(workflow_comment_reply_update_model)
|
|
|
|
|
def put(self, app_model: App, comment_id: str, reply_id: str):
|
|
|
|
|
"""Update a comment reply."""
|
|
|
|
|
# Validate comment access first
|
|
|
|
|
@ -188,21 +267,25 @@ class WorkflowCommentReplyDetailApi(Resource):
|
|
|
|
|
comment_id=comment_id, tenant_id=current_user.current_tenant_id, app_id=app_model.id
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
parser = reqparse.RequestParser()
|
|
|
|
|
parser.add_argument("content", type=str, required=True, location="json")
|
|
|
|
|
parser.add_argument("mentioned_user_ids", type=list, location="json", default=[])
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
payload = WorkflowCommentReplyUpdatePayload.model_validate(console_ns.payload or {})
|
|
|
|
|
|
|
|
|
|
reply = WorkflowCommentService.update_reply(
|
|
|
|
|
reply_id=reply_id, user_id=current_user.id, content=args.content, mentioned_user_ids=args.mentioned_user_ids
|
|
|
|
|
reply_id=reply_id,
|
|
|
|
|
user_id=current_user.id,
|
|
|
|
|
content=payload.content,
|
|
|
|
|
mentioned_user_ids=payload.mentioned_user_ids,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return reply
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("delete_workflow_comment_reply")
|
|
|
|
|
@console_ns.doc(description="Delete a comment reply")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID", "comment_id": "Comment ID", "reply_id": "Reply ID"})
|
|
|
|
|
@console_ns.response(204, "Reply deleted successfully")
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@get_app_model()
|
|
|
|
|
def delete(self, app_model: App, comment_id: str, reply_id: str):
|
|
|
|
|
"""Delete a comment reply."""
|
|
|
|
|
# Validate comment access first
|
|
|
|
|
@ -215,26 +298,20 @@ class WorkflowCommentReplyDetailApi(Resource):
|
|
|
|
|
return {"result": "success"}, 204
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/mention-users")
|
|
|
|
|
class WorkflowCommentMentionUsersApi(Resource):
|
|
|
|
|
"""API for getting mentionable users for workflow comments."""
|
|
|
|
|
|
|
|
|
|
@console_ns.doc("workflow_comment_mention_users")
|
|
|
|
|
@console_ns.doc(description="Get all users in current tenant for mentions")
|
|
|
|
|
@console_ns.doc(params={"app_id": "Application ID"})
|
|
|
|
|
@console_ns.response(200, "Mentionable users retrieved successfully", workflow_comment_mention_users_model)
|
|
|
|
|
@login_required
|
|
|
|
|
@setup_required
|
|
|
|
|
@account_initialization_required
|
|
|
|
|
@get_app_model
|
|
|
|
|
@marshal_with({"users": fields.List(fields.Nested(account_with_role_fields))})
|
|
|
|
|
@get_app_model()
|
|
|
|
|
@marshal_with(workflow_comment_mention_users_model)
|
|
|
|
|
def get(self, app_model: App):
|
|
|
|
|
"""Get all users in current tenant for mentions."""
|
|
|
|
|
members = TenantService.get_tenant_members(current_user.current_tenant)
|
|
|
|
|
return {"users": members}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Register API routes
|
|
|
|
|
api.add_resource(WorkflowCommentListApi, "/apps/<uuid:app_id>/workflow/comments")
|
|
|
|
|
api.add_resource(WorkflowCommentDetailApi, "/apps/<uuid:app_id>/workflow/comments/<string:comment_id>")
|
|
|
|
|
api.add_resource(WorkflowCommentResolveApi, "/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/resolve")
|
|
|
|
|
api.add_resource(WorkflowCommentReplyApi, "/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/replies")
|
|
|
|
|
api.add_resource(
|
|
|
|
|
WorkflowCommentReplyDetailApi, "/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/replies/<string:reply_id>"
|
|
|
|
|
)
|
|
|
|
|
api.add_resource(WorkflowCommentMentionUsersApi, "/apps/<uuid:app_id>/workflow/comments/mention-users")
|
|
|
|
|
|