mirror of
https://github.com/langgenius/dify.git
synced 2026-06-07 16:32:01 +08:00
refactor(api): migrate console.app.workflow_comment to BaseModel (#36180)
This commit is contained in:
parent
a35b28dbef
commit
5798610f27
@ -1,22 +1,16 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from flask_restx import Resource, marshal_with
|
||||
from pydantic import BaseModel, Field, TypeAdapter
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel, Field, TypeAdapter, computed_field, field_validator
|
||||
|
||||
from controllers.common.schema import register_schema_models
|
||||
from controllers.common.schema import register_response_schema_models, register_schema_models
|
||||
from controllers.console import console_ns
|
||||
from controllers.console.app.wraps import get_app_model
|
||||
from controllers.console.wraps import account_initialization_required, edit_permission_required, setup_required
|
||||
from fields.base import ResponseModel
|
||||
from fields.member_fields import AccountWithRole
|
||||
from fields.workflow_comment_fields import (
|
||||
workflow_comment_basic_fields,
|
||||
workflow_comment_create_fields,
|
||||
workflow_comment_detail_fields,
|
||||
workflow_comment_reply_create_fields,
|
||||
workflow_comment_reply_update_fields,
|
||||
workflow_comment_resolve_fields,
|
||||
workflow_comment_update_fields,
|
||||
)
|
||||
from libs.helper import build_avatar_url, dump_response, to_timestamp
|
||||
from libs.login import current_user, login_required
|
||||
from models import App
|
||||
from services.account_service import TenantService
|
||||
@ -51,6 +45,138 @@ class WorkflowCommentMentionUsersPayload(BaseModel):
|
||||
users: list[AccountWithRole]
|
||||
|
||||
|
||||
class WorkflowCommentAccount(ResponseModel):
|
||||
id: str
|
||||
name: str
|
||||
email: str
|
||||
avatar: str | None = Field(default=None, exclude=True)
|
||||
|
||||
@computed_field(return_type=str | None) # type: ignore[prop-decorator]
|
||||
@property
|
||||
def avatar_url(self) -> str | None:
|
||||
return build_avatar_url(self.avatar)
|
||||
|
||||
|
||||
class WorkflowCommentReply(ResponseModel):
|
||||
id: str
|
||||
content: str
|
||||
created_by: str
|
||||
created_by_account: WorkflowCommentAccount | None = None
|
||||
created_at: int | None = None
|
||||
|
||||
@field_validator("created_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class WorkflowCommentMention(ResponseModel):
|
||||
mentioned_user_id: str
|
||||
mentioned_user_account: WorkflowCommentAccount | None = None
|
||||
reply_id: str | None = None
|
||||
|
||||
|
||||
class WorkflowCommentBasic(ResponseModel):
|
||||
id: str
|
||||
position_x: float
|
||||
position_y: float
|
||||
content: str
|
||||
created_by: str
|
||||
created_by_account: WorkflowCommentAccount | None = None
|
||||
created_at: int | None = None
|
||||
updated_at: int | None = None
|
||||
resolved: bool
|
||||
resolved_at: int | None = None
|
||||
resolved_by: str | None = None
|
||||
resolved_by_account: WorkflowCommentAccount | None = None
|
||||
reply_count: int
|
||||
mention_count: int
|
||||
participants: list[WorkflowCommentAccount]
|
||||
|
||||
@field_validator("created_at", "updated_at", "resolved_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class WorkflowCommentBasicList(ResponseModel):
|
||||
data: list[WorkflowCommentBasic]
|
||||
|
||||
|
||||
class WorkflowCommentDetail(ResponseModel):
|
||||
id: str
|
||||
position_x: float
|
||||
position_y: float
|
||||
content: str
|
||||
created_by: str
|
||||
created_by_account: WorkflowCommentAccount | None = None
|
||||
created_at: int | None = None
|
||||
updated_at: int | None = None
|
||||
resolved: bool
|
||||
resolved_at: int | None = None
|
||||
resolved_by: str | None = None
|
||||
resolved_by_account: WorkflowCommentAccount | None = None
|
||||
replies: list[WorkflowCommentReply]
|
||||
mentions: list[WorkflowCommentMention]
|
||||
|
||||
@field_validator("created_at", "updated_at", "resolved_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class WorkflowCommentCreate(ResponseModel):
|
||||
id: str
|
||||
created_at: int | None = None
|
||||
|
||||
@field_validator("created_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class WorkflowCommentUpdate(ResponseModel):
|
||||
id: str
|
||||
updated_at: int | None = None
|
||||
|
||||
@field_validator("updated_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class WorkflowCommentResolve(ResponseModel):
|
||||
id: str
|
||||
resolved: bool
|
||||
resolved_at: int | None = None
|
||||
resolved_by: str | None = None
|
||||
|
||||
@field_validator("resolved_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class WorkflowCommentReplyCreate(ResponseModel):
|
||||
id: str
|
||||
created_at: int | None = None
|
||||
|
||||
@field_validator("created_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
class WorkflowCommentReplyUpdate(ResponseModel):
|
||||
id: str
|
||||
updated_at: int | None = None
|
||||
|
||||
@field_validator("updated_at", mode="before")
|
||||
@classmethod
|
||||
def _normalize_timestamp(cls, value: datetime | int | None) -> int | None:
|
||||
return to_timestamp(value)
|
||||
|
||||
|
||||
register_schema_models(
|
||||
console_ns,
|
||||
AccountWithRole,
|
||||
@ -59,17 +185,19 @@ register_schema_models(
|
||||
WorkflowCommentUpdatePayload,
|
||||
WorkflowCommentReplyPayload,
|
||||
)
|
||||
|
||||
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
|
||||
register_response_schema_models(
|
||||
console_ns,
|
||||
WorkflowCommentAccount,
|
||||
WorkflowCommentReply,
|
||||
WorkflowCommentMention,
|
||||
WorkflowCommentBasic,
|
||||
WorkflowCommentBasicList,
|
||||
WorkflowCommentDetail,
|
||||
WorkflowCommentCreate,
|
||||
WorkflowCommentUpdate,
|
||||
WorkflowCommentResolve,
|
||||
WorkflowCommentReplyCreate,
|
||||
WorkflowCommentReplyUpdate,
|
||||
)
|
||||
|
||||
|
||||
@ -80,28 +208,26 @@ class WorkflowCommentListApi(Resource):
|
||||
@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)
|
||||
@console_ns.response(200, "Comments retrieved successfully", console_ns.models[WorkflowCommentBasicList.__name__])
|
||||
@login_required
|
||||
@setup_required
|
||||
@account_initialization_required
|
||||
@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
|
||||
return WorkflowCommentBasicList.model_validate({"data": comments}).model_dump(mode="json")
|
||||
|
||||
@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)
|
||||
@console_ns.response(201, "Comment created successfully", console_ns.models[WorkflowCommentCreate.__name__])
|
||||
@login_required
|
||||
@setup_required
|
||||
@account_initialization_required
|
||||
@get_app_model()
|
||||
@marshal_with(workflow_comment_create_model)
|
||||
@edit_permission_required
|
||||
def post(self, app_model: App):
|
||||
"""Create a new workflow comment."""
|
||||
@ -117,7 +243,7 @@ class WorkflowCommentListApi(Resource):
|
||||
mentioned_user_ids=payload.mentioned_user_ids,
|
||||
)
|
||||
|
||||
return result, 201
|
||||
return dump_response(WorkflowCommentCreate, result), 201
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/<string:comment_id>")
|
||||
@ -127,30 +253,28 @@ class WorkflowCommentDetailApi(Resource):
|
||||
@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)
|
||||
@console_ns.response(200, "Comment retrieved successfully", console_ns.models[WorkflowCommentDetail.__name__])
|
||||
@login_required
|
||||
@setup_required
|
||||
@account_initialization_required
|
||||
@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(
|
||||
tenant_id=current_user.current_tenant_id, app_id=app_model.id, comment_id=comment_id
|
||||
)
|
||||
|
||||
return comment
|
||||
return dump_response(WorkflowCommentDetail, 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)
|
||||
@console_ns.response(200, "Comment updated successfully", console_ns.models[WorkflowCommentUpdate.__name__])
|
||||
@login_required
|
||||
@setup_required
|
||||
@account_initialization_required
|
||||
@get_app_model()
|
||||
@marshal_with(workflow_comment_update_model)
|
||||
@edit_permission_required
|
||||
def put(self, app_model: App, comment_id: str):
|
||||
"""Update a workflow comment."""
|
||||
@ -167,7 +291,7 @@ class WorkflowCommentDetailApi(Resource):
|
||||
mentioned_user_ids=payload.mentioned_user_ids,
|
||||
)
|
||||
|
||||
return result
|
||||
return dump_response(WorkflowCommentUpdate, result)
|
||||
|
||||
@console_ns.doc("delete_workflow_comment")
|
||||
@console_ns.doc(description="Delete a workflow comment")
|
||||
@ -197,12 +321,11 @@ class WorkflowCommentResolveApi(Resource):
|
||||
@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)
|
||||
@console_ns.response(200, "Comment resolved successfully", console_ns.models[WorkflowCommentResolve.__name__])
|
||||
@login_required
|
||||
@setup_required
|
||||
@account_initialization_required
|
||||
@get_app_model()
|
||||
@marshal_with(workflow_comment_resolve_model)
|
||||
@edit_permission_required
|
||||
def post(self, app_model: App, comment_id: str):
|
||||
"""Resolve a workflow comment."""
|
||||
@ -213,7 +336,7 @@ class WorkflowCommentResolveApi(Resource):
|
||||
user_id=current_user.id,
|
||||
)
|
||||
|
||||
return comment
|
||||
return dump_response(WorkflowCommentResolve, comment)
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/replies")
|
||||
@ -224,12 +347,11 @@ class WorkflowCommentReplyApi(Resource):
|
||||
@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[WorkflowCommentReplyPayload.__name__])
|
||||
@console_ns.response(201, "Reply created successfully", workflow_comment_reply_create_model)
|
||||
@console_ns.response(201, "Reply created successfully", console_ns.models[WorkflowCommentReplyCreate.__name__])
|
||||
@login_required
|
||||
@setup_required
|
||||
@account_initialization_required
|
||||
@get_app_model()
|
||||
@marshal_with(workflow_comment_reply_create_model)
|
||||
@edit_permission_required
|
||||
def post(self, app_model: App, comment_id: str):
|
||||
"""Add a reply to a workflow comment."""
|
||||
@ -247,7 +369,7 @@ class WorkflowCommentReplyApi(Resource):
|
||||
mentioned_user_ids=payload.mentioned_user_ids,
|
||||
)
|
||||
|
||||
return result, 201
|
||||
return dump_response(WorkflowCommentReplyCreate, result), 201
|
||||
|
||||
|
||||
@console_ns.route("/apps/<uuid:app_id>/workflow/comments/<string:comment_id>/replies/<string:reply_id>")
|
||||
@ -258,12 +380,11 @@ class WorkflowCommentReplyDetailApi(Resource):
|
||||
@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[WorkflowCommentReplyPayload.__name__])
|
||||
@console_ns.response(200, "Reply updated successfully", workflow_comment_reply_update_model)
|
||||
@console_ns.response(200, "Reply updated successfully", console_ns.models[WorkflowCommentReplyUpdate.__name__])
|
||||
@login_required
|
||||
@setup_required
|
||||
@account_initialization_required
|
||||
@get_app_model()
|
||||
@marshal_with(workflow_comment_reply_update_model)
|
||||
@edit_permission_required
|
||||
def put(self, app_model: App, comment_id: str, reply_id: str):
|
||||
"""Update a comment reply."""
|
||||
@ -284,7 +405,7 @@ class WorkflowCommentReplyDetailApi(Resource):
|
||||
mentioned_user_ids=payload.mentioned_user_ids,
|
||||
)
|
||||
|
||||
return reply
|
||||
return dump_response(WorkflowCommentReplyUpdate, reply)
|
||||
|
||||
@console_ns.doc("delete_workflow_comment_reply")
|
||||
@console_ns.doc(description="Delete a comment reply")
|
||||
|
||||
@ -6,8 +6,7 @@ from flask_restx import fields
|
||||
from pydantic import computed_field, field_validator
|
||||
|
||||
from fields.base import ResponseModel
|
||||
from graphon.file import helpers as file_helpers
|
||||
from libs.helper import to_timestamp
|
||||
from libs.helper import build_avatar_url, to_timestamp
|
||||
|
||||
simple_account_fields = {
|
||||
"id": fields.String,
|
||||
@ -16,14 +15,6 @@ simple_account_fields = {
|
||||
}
|
||||
|
||||
|
||||
def _build_avatar_url(avatar: str | None) -> str | None:
|
||||
if avatar is None:
|
||||
return None
|
||||
if avatar.startswith(("http://", "https://")):
|
||||
return avatar
|
||||
return file_helpers.get_signed_file_url(avatar)
|
||||
|
||||
|
||||
class SimpleAccount(ResponseModel):
|
||||
id: str
|
||||
name: str
|
||||
@ -36,7 +27,7 @@ class _AccountAvatar(ResponseModel):
|
||||
@computed_field(return_type=str | None) # type: ignore[prop-decorator]
|
||||
@property
|
||||
def avatar_url(self) -> str | None:
|
||||
return _build_avatar_url(self.avatar)
|
||||
return build_avatar_url(self.avatar)
|
||||
|
||||
|
||||
class Account(_AccountAvatar):
|
||||
|
||||
@ -1,96 +0,0 @@
|
||||
from flask_restx import fields
|
||||
|
||||
from libs.helper import AvatarUrlField, TimestampField
|
||||
|
||||
# basic account fields for comments
|
||||
account_fields = {
|
||||
"id": fields.String,
|
||||
"name": fields.String,
|
||||
"email": fields.String,
|
||||
"avatar_url": AvatarUrlField,
|
||||
}
|
||||
|
||||
# Comment mention fields
|
||||
workflow_comment_mention_fields = {
|
||||
"mentioned_user_id": fields.String,
|
||||
"mentioned_user_account": fields.Nested(account_fields, allow_null=True),
|
||||
"reply_id": fields.String,
|
||||
}
|
||||
|
||||
# Comment reply fields
|
||||
workflow_comment_reply_fields = {
|
||||
"id": fields.String,
|
||||
"content": fields.String,
|
||||
"created_by": fields.String,
|
||||
"created_by_account": fields.Nested(account_fields, allow_null=True),
|
||||
"created_at": TimestampField,
|
||||
}
|
||||
|
||||
# Basic comment fields (for list views)
|
||||
workflow_comment_basic_fields = {
|
||||
"id": fields.String,
|
||||
"position_x": fields.Float,
|
||||
"position_y": fields.Float,
|
||||
"content": fields.String,
|
||||
"created_by": fields.String,
|
||||
"created_by_account": fields.Nested(account_fields, allow_null=True),
|
||||
"created_at": TimestampField,
|
||||
"updated_at": TimestampField,
|
||||
"resolved": fields.Boolean,
|
||||
"resolved_at": TimestampField,
|
||||
"resolved_by": fields.String,
|
||||
"resolved_by_account": fields.Nested(account_fields, allow_null=True),
|
||||
"reply_count": fields.Integer,
|
||||
"mention_count": fields.Integer,
|
||||
"participants": fields.List(fields.Nested(account_fields)),
|
||||
}
|
||||
|
||||
# Detailed comment fields (for single comment view)
|
||||
workflow_comment_detail_fields = {
|
||||
"id": fields.String,
|
||||
"position_x": fields.Float,
|
||||
"position_y": fields.Float,
|
||||
"content": fields.String,
|
||||
"created_by": fields.String,
|
||||
"created_by_account": fields.Nested(account_fields, allow_null=True),
|
||||
"created_at": TimestampField,
|
||||
"updated_at": TimestampField,
|
||||
"resolved": fields.Boolean,
|
||||
"resolved_at": TimestampField,
|
||||
"resolved_by": fields.String,
|
||||
"resolved_by_account": fields.Nested(account_fields, allow_null=True),
|
||||
"replies": fields.List(fields.Nested(workflow_comment_reply_fields)),
|
||||
"mentions": fields.List(fields.Nested(workflow_comment_mention_fields)),
|
||||
}
|
||||
|
||||
# Comment creation response fields (simplified)
|
||||
workflow_comment_create_fields = {
|
||||
"id": fields.String,
|
||||
"created_at": TimestampField,
|
||||
}
|
||||
|
||||
# Comment update response fields (simplified)
|
||||
workflow_comment_update_fields = {
|
||||
"id": fields.String,
|
||||
"updated_at": TimestampField,
|
||||
}
|
||||
|
||||
# Comment resolve response fields
|
||||
workflow_comment_resolve_fields = {
|
||||
"id": fields.String,
|
||||
"resolved": fields.Boolean,
|
||||
"resolved_at": TimestampField,
|
||||
"resolved_by": fields.String,
|
||||
}
|
||||
|
||||
# Reply creation response fields (simplified)
|
||||
workflow_comment_reply_create_fields = {
|
||||
"id": fields.String,
|
||||
"created_at": TimestampField,
|
||||
}
|
||||
|
||||
# Reply update response fields
|
||||
workflow_comment_reply_update_fields = {
|
||||
"id": fields.String,
|
||||
"updated_at": TimestampField,
|
||||
}
|
||||
@ -136,6 +136,14 @@ def build_icon_url(icon_type: Any, icon: str | None) -> str | None:
|
||||
return file_helpers.get_signed_file_url(icon)
|
||||
|
||||
|
||||
def build_avatar_url(avatar: str | None) -> str | None:
|
||||
if avatar is None:
|
||||
return None
|
||||
if avatar.startswith(("http://", "https://")):
|
||||
return avatar
|
||||
return file_helpers.get_signed_file_url(avatar)
|
||||
|
||||
|
||||
class AvatarUrlField(fields.Raw):
|
||||
def output(self, key, obj, **kwargs):
|
||||
if obj is None:
|
||||
@ -144,9 +152,7 @@ class AvatarUrlField(fields.Raw):
|
||||
from models import Account
|
||||
|
||||
if isinstance(obj, Account) and obj.avatar is not None:
|
||||
if obj.avatar.startswith(("http://", "https://")):
|
||||
return obj.avatar
|
||||
return file_helpers.get_signed_file_url(obj.avatar)
|
||||
return build_avatar_url(obj.avatar)
|
||||
return None
|
||||
|
||||
|
||||
@ -181,6 +187,11 @@ def to_timestamp(value: datetime | int | None) -> int | None:
|
||||
return value
|
||||
|
||||
|
||||
def dump_response(model: type[BaseModel], data: Any) -> dict[str, Any]:
|
||||
"""Serialize a Pydantic response model to JSON-compatible dict output."""
|
||||
return model.model_validate(data, from_attributes=True).model_dump(mode="json")
|
||||
|
||||
|
||||
def current_timestamp() -> int:
|
||||
"""Return the current Unix timestamp in seconds."""
|
||||
return int(time.time())
|
||||
|
||||
@ -2404,7 +2404,7 @@ Get all comments for a workflow
|
||||
|
||||
| Code | Description | Schema |
|
||||
| ---- | ----------- | ------ |
|
||||
| 200 | Comments retrieved successfully | [WorkflowCommentBasic](#workflowcommentbasic) |
|
||||
| 200 | Comments retrieved successfully | [WorkflowCommentBasicList](#workflowcommentbasiclist) |
|
||||
|
||||
#### POST
|
||||
##### Summary
|
||||
@ -13927,32 +13927,47 @@ User action configuration.
|
||||
| trigger_metadata | | | No |
|
||||
| workflow_run | | | No |
|
||||
|
||||
#### WorkflowCommentAccount
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| avatar_url | | | Yes |
|
||||
| email | string | | Yes |
|
||||
| id | string | | Yes |
|
||||
| name | string | | Yes |
|
||||
|
||||
#### WorkflowCommentBasic
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| content | string | | No |
|
||||
| created_at | object | | No |
|
||||
| created_by | string | | No |
|
||||
| created_by_account | [_AnonymousInlineModel_6fec07cd0d85](#_anonymousinlinemodel_6fec07cd0d85) | | No |
|
||||
| id | string | | No |
|
||||
| mention_count | integer | | No |
|
||||
| participants | [ [_AnonymousInlineModel_6fec07cd0d85](#_anonymousinlinemodel_6fec07cd0d85) ] | | No |
|
||||
| position_x | number | | No |
|
||||
| position_y | number | | No |
|
||||
| reply_count | integer | | No |
|
||||
| resolved | boolean | | No |
|
||||
| resolved_at | object | | No |
|
||||
| resolved_by | string | | No |
|
||||
| resolved_by_account | [_AnonymousInlineModel_6fec07cd0d85](#_anonymousinlinemodel_6fec07cd0d85) | | No |
|
||||
| updated_at | object | | No |
|
||||
| content | string | | Yes |
|
||||
| created_at | | | No |
|
||||
| created_by | string | | Yes |
|
||||
| created_by_account | | | No |
|
||||
| id | string | | Yes |
|
||||
| mention_count | integer | | Yes |
|
||||
| participants | [ [WorkflowCommentAccount](#workflowcommentaccount) ] | | Yes |
|
||||
| position_x | number | | Yes |
|
||||
| position_y | number | | Yes |
|
||||
| reply_count | integer | | Yes |
|
||||
| resolved | boolean | | Yes |
|
||||
| resolved_at | | | No |
|
||||
| resolved_by | | | No |
|
||||
| resolved_by_account | | | No |
|
||||
| updated_at | | | No |
|
||||
|
||||
#### WorkflowCommentBasicList
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| data | [ [WorkflowCommentBasic](#workflowcommentbasic) ] | | Yes |
|
||||
|
||||
#### WorkflowCommentCreate
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| created_at | object | | No |
|
||||
| id | string | | No |
|
||||
| created_at | | | No |
|
||||
| id | string | | Yes |
|
||||
|
||||
#### WorkflowCommentCreatePayload
|
||||
|
||||
@ -13967,20 +13982,28 @@ User action configuration.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| content | string | | No |
|
||||
| created_at | object | | No |
|
||||
| created_by | string | | No |
|
||||
| created_by_account | [_AnonymousInlineModel_6fec07cd0d85](#_anonymousinlinemodel_6fec07cd0d85) | | No |
|
||||
| id | string | | No |
|
||||
| mentions | [ [_AnonymousInlineModel_f7ff64cce858](#_anonymousinlinemodel_f7ff64cce858) ] | | No |
|
||||
| position_x | number | | No |
|
||||
| position_y | number | | No |
|
||||
| replies | [ [_AnonymousInlineModel_55c39c6a4b9e](#_anonymousinlinemodel_55c39c6a4b9e) ] | | No |
|
||||
| resolved | boolean | | No |
|
||||
| resolved_at | object | | No |
|
||||
| resolved_by | string | | No |
|
||||
| resolved_by_account | [_AnonymousInlineModel_6fec07cd0d85](#_anonymousinlinemodel_6fec07cd0d85) | | No |
|
||||
| updated_at | object | | No |
|
||||
| content | string | | Yes |
|
||||
| created_at | | | No |
|
||||
| created_by | string | | Yes |
|
||||
| created_by_account | | | No |
|
||||
| id | string | | Yes |
|
||||
| mentions | [ [WorkflowCommentMention](#workflowcommentmention) ] | | Yes |
|
||||
| position_x | number | | Yes |
|
||||
| position_y | number | | Yes |
|
||||
| replies | [ [WorkflowCommentReply](#workflowcommentreply) ] | | Yes |
|
||||
| resolved | boolean | | Yes |
|
||||
| resolved_at | | | No |
|
||||
| resolved_by | | | No |
|
||||
| resolved_by_account | | | No |
|
||||
| updated_at | | | No |
|
||||
|
||||
#### WorkflowCommentMention
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| mentioned_user_account | | | No |
|
||||
| mentioned_user_id | string | | Yes |
|
||||
| reply_id | | | No |
|
||||
|
||||
#### WorkflowCommentMentionUsersPayload
|
||||
|
||||
@ -13988,12 +14011,22 @@ User action configuration.
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| users | [ [AccountWithRole](#accountwithrole) ] | | Yes |
|
||||
|
||||
#### WorkflowCommentReply
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| content | string | | Yes |
|
||||
| created_at | | | No |
|
||||
| created_by | string | | Yes |
|
||||
| created_by_account | | | No |
|
||||
| id | string | | Yes |
|
||||
|
||||
#### WorkflowCommentReplyCreate
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| created_at | object | | No |
|
||||
| id | string | | No |
|
||||
| created_at | | | No |
|
||||
| id | string | | Yes |
|
||||
|
||||
#### WorkflowCommentReplyPayload
|
||||
|
||||
@ -14006,24 +14039,24 @@ User action configuration.
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| id | string | | No |
|
||||
| updated_at | object | | No |
|
||||
| id | string | | Yes |
|
||||
| updated_at | | | No |
|
||||
|
||||
#### WorkflowCommentResolve
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| id | string | | No |
|
||||
| resolved | boolean | | No |
|
||||
| resolved_at | object | | No |
|
||||
| resolved_by | string | | No |
|
||||
| id | string | | Yes |
|
||||
| resolved | boolean | | Yes |
|
||||
| resolved_at | | | No |
|
||||
| resolved_by | | | No |
|
||||
|
||||
#### WorkflowCommentUpdate
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| id | string | | No |
|
||||
| updated_at | object | | No |
|
||||
| id | string | | Yes |
|
||||
| updated_at | | | No |
|
||||
|
||||
#### WorkflowCommentUpdatePayload
|
||||
|
||||
@ -14427,25 +14460,6 @@ Workflow tool configuration
|
||||
| limit | integer | | No |
|
||||
| page | integer | | No |
|
||||
|
||||
#### _AnonymousInlineModel_55c39c6a4b9e
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| content | string | | No |
|
||||
| created_at | object | | No |
|
||||
| created_by | string | | No |
|
||||
| created_by_account | [_AnonymousInlineModel_6fec07cd0d85](#_anonymousinlinemodel_6fec07cd0d85) | | No |
|
||||
| id | string | | No |
|
||||
|
||||
#### _AnonymousInlineModel_6fec07cd0d85
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| avatar_url | object | | No |
|
||||
| email | string | | No |
|
||||
| id | string | | No |
|
||||
| name | string | | No |
|
||||
|
||||
#### _AnonymousInlineModel_b1954337d565
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
@ -14455,14 +14469,6 @@ Workflow tool configuration
|
||||
| model_provider_name | string | | No |
|
||||
| summary_prompt | string | | No |
|
||||
|
||||
#### _AnonymousInlineModel_f7ff64cce858
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| mentioned_user_account | [_AnonymousInlineModel_6fec07cd0d85](#_anonymousinlinemodel_6fec07cd0d85) | | No |
|
||||
| mentioned_user_id | string | | No |
|
||||
| reply_id | string | | No |
|
||||
|
||||
## FastOpenAPI Preview (OpenAPI 3.0)
|
||||
|
||||
### Dify API (FastOpenAPI PoC)
|
||||
|
||||
@ -17,6 +17,15 @@ from controllers.console.app import wraps as app_wraps
|
||||
from libs import login as login_lib
|
||||
from models.account import Account, AccountStatus, TenantAccountRole
|
||||
|
||||
JAN_1_2024_NOON = datetime(2024, 1, 1, 12, 0, 0)
|
||||
JAN_1_2024_NOON_TS = int(JAN_1_2024_NOON.timestamp())
|
||||
JAN_1_2024_1201 = datetime(2024, 1, 1, 12, 1, 0)
|
||||
JAN_1_2024_1201_TS = int(JAN_1_2024_1201.timestamp())
|
||||
JAN_1_2024_1202 = datetime(2024, 1, 1, 12, 2, 0)
|
||||
JAN_1_2024_1202_TS = int(JAN_1_2024_1202.timestamp())
|
||||
JAN_1_2024_1203 = datetime(2024, 1, 1, 12, 3, 0)
|
||||
JAN_1_2024_1203_TS = int(JAN_1_2024_1203.timestamp())
|
||||
|
||||
|
||||
def _make_account(role: TenantAccountRole) -> Account:
|
||||
account = Account(name="tester", email="tester@example.com")
|
||||
@ -78,6 +87,30 @@ class WriteCase:
|
||||
payload: dict[str, object] | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MutationResponseCase:
|
||||
resource_cls: type
|
||||
method_name: str
|
||||
path: str
|
||||
kwargs: dict[str, str]
|
||||
service_method_name: str
|
||||
service_return: object
|
||||
expected_response: dict[str, object]
|
||||
payload: dict[str, object] | None = None
|
||||
expected_status: int | None = None
|
||||
|
||||
|
||||
def _unwrap_response(result: object) -> tuple[dict[str, object], int | None]:
|
||||
if isinstance(result, tuple):
|
||||
response, status = result
|
||||
assert isinstance(response, dict)
|
||||
assert isinstance(status, int)
|
||||
return response, status
|
||||
|
||||
assert isinstance(result, dict)
|
||||
return result, None
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"case",
|
||||
[
|
||||
@ -151,17 +184,20 @@ def test_create_comment_allows_editor(app: Flask, monkeypatch: pytest.MonkeyPatc
|
||||
|
||||
create_comment_mock = MagicMock(return_value={"id": "comment-1"})
|
||||
monkeypatch.setattr(workflow_comment_module.WorkflowCommentService, "create_comment", create_comment_mock)
|
||||
payload = {"content": "hello", "position_x": 1.0, "position_y": 2.0, "mentioned_user_ids": []}
|
||||
payload: dict[str, object] = {
|
||||
"content": "hello",
|
||||
"position_x": 1.0,
|
||||
"position_y": 2.0,
|
||||
"mentioned_user_ids": [],
|
||||
}
|
||||
|
||||
with app.test_request_context("/console/api/apps/app-123/workflow/comments", method="POST", json=payload):
|
||||
with _patch_payload(payload):
|
||||
result = workflow_comment_module.WorkflowCommentListApi().post(app_id="app-123")
|
||||
|
||||
if isinstance(result, tuple):
|
||||
response = result[0]
|
||||
else:
|
||||
response = result
|
||||
response, status = _unwrap_response(result)
|
||||
assert response["id"] == "comment-1"
|
||||
assert status == 201
|
||||
create_comment_mock.assert_called_once_with(
|
||||
tenant_id="tenant-123",
|
||||
app_id="app-123",
|
||||
@ -181,14 +217,17 @@ def test_update_comment_omits_mentions_when_payload_does_not_include_them(
|
||||
app_model = _make_app()
|
||||
_patch_console_guards(monkeypatch, account, app_model)
|
||||
|
||||
update_comment_mock = MagicMock(return_value={"id": "comment-1", "updated_at": datetime(2024, 1, 1, 12, 0, 0)})
|
||||
update_comment_mock = MagicMock(return_value={"id": "comment-1", "updated_at": JAN_1_2024_NOON})
|
||||
monkeypatch.setattr(workflow_comment_module.WorkflowCommentService, "update_comment", update_comment_mock)
|
||||
payload = {"content": "hello", "position_x": 10.0, "position_y": 20.0}
|
||||
payload: dict[str, object] = {"content": "hello", "position_x": 10.0, "position_y": 20.0}
|
||||
|
||||
with app.test_request_context("/console/api/apps/app-123/workflow/comments/comment-1", method="PUT", json=payload):
|
||||
with _patch_payload(payload):
|
||||
workflow_comment_module.WorkflowCommentDetailApi().put(app_id="app-123", comment_id="comment-1")
|
||||
result = workflow_comment_module.WorkflowCommentDetailApi().put(app_id="app-123", comment_id="comment-1")
|
||||
|
||||
response, status = _unwrap_response(result)
|
||||
assert response == {"id": "comment-1", "updated_at": JAN_1_2024_NOON_TS}
|
||||
assert status is None
|
||||
update_comment_mock.assert_called_once_with(
|
||||
tenant_id="tenant-123",
|
||||
app_id="app-123",
|
||||
@ -199,3 +238,254 @@ def test_update_comment_omits_mentions_when_payload_does_not_include_them(
|
||||
position_y=20.0,
|
||||
mentioned_user_ids=None,
|
||||
)
|
||||
|
||||
|
||||
def test_list_comments_serializes_response_model(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
app.config.setdefault("RESTX_MASK_HEADER", "X-Fields")
|
||||
account = _make_account(TenantAccountRole.NORMAL)
|
||||
app_model = _make_app()
|
||||
_patch_console_guards(monkeypatch, account, app_model)
|
||||
|
||||
comment_author = SimpleNamespace(
|
||||
id="account-123",
|
||||
name="tester",
|
||||
email="tester@example.com",
|
||||
avatar="https://example.com/avatar.png",
|
||||
)
|
||||
comment = SimpleNamespace(
|
||||
id="comment-1",
|
||||
position_x=1.5,
|
||||
position_y=2.5,
|
||||
content="hello",
|
||||
created_by="account-123",
|
||||
created_by_account=comment_author,
|
||||
created_at=1_700_000_000,
|
||||
updated_at=1_700_000_001,
|
||||
resolved=False,
|
||||
resolved_at=None,
|
||||
resolved_by=None,
|
||||
resolved_by_account=None,
|
||||
reply_count=0,
|
||||
mention_count=0,
|
||||
participants=[comment_author],
|
||||
)
|
||||
get_comments_mock = MagicMock(return_value=[comment])
|
||||
monkeypatch.setattr(workflow_comment_module.WorkflowCommentService, "get_comments", get_comments_mock)
|
||||
|
||||
with app.test_request_context("/console/api/apps/app-123/workflow/comments", method="GET"):
|
||||
response = workflow_comment_module.WorkflowCommentListApi().get(app_id="app-123")
|
||||
|
||||
assert response == {
|
||||
"data": [
|
||||
{
|
||||
"id": "comment-1",
|
||||
"position_x": 1.5,
|
||||
"position_y": 2.5,
|
||||
"content": "hello",
|
||||
"created_by": "account-123",
|
||||
"created_by_account": {
|
||||
"id": "account-123",
|
||||
"name": "tester",
|
||||
"email": "tester@example.com",
|
||||
"avatar_url": "https://example.com/avatar.png",
|
||||
},
|
||||
"created_at": 1_700_000_000,
|
||||
"updated_at": 1_700_000_001,
|
||||
"resolved": False,
|
||||
"resolved_at": None,
|
||||
"resolved_by": None,
|
||||
"resolved_by_account": None,
|
||||
"reply_count": 0,
|
||||
"mention_count": 0,
|
||||
"participants": [
|
||||
{
|
||||
"id": "account-123",
|
||||
"name": "tester",
|
||||
"email": "tester@example.com",
|
||||
"avatar_url": "https://example.com/avatar.png",
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
get_comments_mock.assert_called_once_with(tenant_id="tenant-123", app_id="app-123")
|
||||
|
||||
|
||||
def test_get_comment_serializes_detail_response_model(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
app.config.setdefault("RESTX_MASK_HEADER", "X-Fields")
|
||||
account = _make_account(TenantAccountRole.NORMAL)
|
||||
app_model = _make_app()
|
||||
_patch_console_guards(monkeypatch, account, app_model)
|
||||
|
||||
comment_author = SimpleNamespace(
|
||||
id="account-123",
|
||||
name="tester",
|
||||
email="tester@example.com",
|
||||
avatar="https://example.com/avatar.png",
|
||||
)
|
||||
mentioned_user = SimpleNamespace(
|
||||
id="account-456",
|
||||
name="mentioned",
|
||||
email="mentioned@example.com",
|
||||
avatar=None,
|
||||
)
|
||||
comment = SimpleNamespace(
|
||||
id="comment-1",
|
||||
position_x=1.5,
|
||||
position_y=2.5,
|
||||
content="hello",
|
||||
created_by="account-123",
|
||||
created_by_account=comment_author,
|
||||
created_at=JAN_1_2024_NOON,
|
||||
updated_at=JAN_1_2024_1201,
|
||||
resolved=True,
|
||||
resolved_at=JAN_1_2024_1202,
|
||||
resolved_by="account-123",
|
||||
resolved_by_account=comment_author,
|
||||
replies=[
|
||||
SimpleNamespace(
|
||||
id="reply-1",
|
||||
content="reply",
|
||||
created_by="account-456",
|
||||
created_by_account=mentioned_user,
|
||||
created_at=JAN_1_2024_1203,
|
||||
)
|
||||
],
|
||||
mentions=[
|
||||
SimpleNamespace(
|
||||
mentioned_user_id="account-456",
|
||||
mentioned_user_account=mentioned_user,
|
||||
reply_id="reply-1",
|
||||
)
|
||||
],
|
||||
)
|
||||
get_comment_mock = MagicMock(return_value=comment)
|
||||
monkeypatch.setattr(workflow_comment_module.WorkflowCommentService, "get_comment", get_comment_mock)
|
||||
|
||||
with app.test_request_context("/console/api/apps/app-123/workflow/comments/comment-1", method="GET"):
|
||||
response = workflow_comment_module.WorkflowCommentDetailApi().get(app_id="app-123", comment_id="comment-1")
|
||||
|
||||
assert response == {
|
||||
"id": "comment-1",
|
||||
"position_x": 1.5,
|
||||
"position_y": 2.5,
|
||||
"content": "hello",
|
||||
"created_by": "account-123",
|
||||
"created_by_account": {
|
||||
"id": "account-123",
|
||||
"name": "tester",
|
||||
"email": "tester@example.com",
|
||||
"avatar_url": "https://example.com/avatar.png",
|
||||
},
|
||||
"created_at": JAN_1_2024_NOON_TS,
|
||||
"updated_at": JAN_1_2024_1201_TS,
|
||||
"resolved": True,
|
||||
"resolved_at": JAN_1_2024_1202_TS,
|
||||
"resolved_by": "account-123",
|
||||
"resolved_by_account": {
|
||||
"id": "account-123",
|
||||
"name": "tester",
|
||||
"email": "tester@example.com",
|
||||
"avatar_url": "https://example.com/avatar.png",
|
||||
},
|
||||
"replies": [
|
||||
{
|
||||
"id": "reply-1",
|
||||
"content": "reply",
|
||||
"created_by": "account-456",
|
||||
"created_by_account": {
|
||||
"id": "account-456",
|
||||
"name": "mentioned",
|
||||
"email": "mentioned@example.com",
|
||||
"avatar_url": None,
|
||||
},
|
||||
"created_at": JAN_1_2024_1203_TS,
|
||||
}
|
||||
],
|
||||
"mentions": [
|
||||
{
|
||||
"mentioned_user_id": "account-456",
|
||||
"mentioned_user_account": {
|
||||
"id": "account-456",
|
||||
"name": "mentioned",
|
||||
"email": "mentioned@example.com",
|
||||
"avatar_url": None,
|
||||
},
|
||||
"reply_id": "reply-1",
|
||||
}
|
||||
],
|
||||
}
|
||||
get_comment_mock.assert_called_once_with(tenant_id="tenant-123", app_id="app-123", comment_id="comment-1")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"case",
|
||||
[
|
||||
MutationResponseCase(
|
||||
resource_cls=workflow_comment_module.WorkflowCommentResolveApi,
|
||||
method_name="post",
|
||||
path="/console/api/apps/app-123/workflow/comments/comment-1/resolve",
|
||||
kwargs={"app_id": "app-123", "comment_id": "comment-1"},
|
||||
service_method_name="resolve_comment",
|
||||
service_return={
|
||||
"id": "comment-1",
|
||||
"resolved": True,
|
||||
"resolved_at": JAN_1_2024_NOON,
|
||||
"resolved_by": "account-123",
|
||||
},
|
||||
expected_response={
|
||||
"id": "comment-1",
|
||||
"resolved": True,
|
||||
"resolved_at": JAN_1_2024_NOON_TS,
|
||||
"resolved_by": "account-123",
|
||||
},
|
||||
),
|
||||
MutationResponseCase(
|
||||
resource_cls=workflow_comment_module.WorkflowCommentReplyApi,
|
||||
method_name="post",
|
||||
path="/console/api/apps/app-123/workflow/comments/comment-1/replies",
|
||||
kwargs={"app_id": "app-123", "comment_id": "comment-1"},
|
||||
payload={"content": "reply", "mentioned_user_ids": []},
|
||||
service_method_name="create_reply",
|
||||
service_return={"id": "reply-1", "created_at": JAN_1_2024_NOON},
|
||||
expected_response={"id": "reply-1", "created_at": JAN_1_2024_NOON_TS},
|
||||
expected_status=201,
|
||||
),
|
||||
MutationResponseCase(
|
||||
resource_cls=workflow_comment_module.WorkflowCommentReplyDetailApi,
|
||||
method_name="put",
|
||||
path="/console/api/apps/app-123/workflow/comments/comment-1/replies/reply-1",
|
||||
kwargs={"app_id": "app-123", "comment_id": "comment-1", "reply_id": "reply-1"},
|
||||
payload={"content": "reply", "mentioned_user_ids": []},
|
||||
service_method_name="update_reply",
|
||||
service_return={"id": "reply-1", "updated_at": JAN_1_2024_NOON},
|
||||
expected_response={"id": "reply-1", "updated_at": JAN_1_2024_NOON_TS},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_mutation_endpoints_serialize_response_models(
|
||||
app: Flask, monkeypatch: pytest.MonkeyPatch, case: MutationResponseCase
|
||||
) -> None:
|
||||
app.config.setdefault("RESTX_MASK_HEADER", "X-Fields")
|
||||
account = _make_account(TenantAccountRole.EDITOR)
|
||||
app_model = _make_app()
|
||||
_patch_console_guards(monkeypatch, account, app_model)
|
||||
_patch_write_services(monkeypatch)
|
||||
monkeypatch.setattr(
|
||||
workflow_comment_module.WorkflowCommentService,
|
||||
case.service_method_name,
|
||||
MagicMock(return_value=case.service_return),
|
||||
)
|
||||
|
||||
with app.test_request_context(case.path, method=case.method_name.upper(), json=case.payload):
|
||||
with _patch_payload(case.payload):
|
||||
result = getattr(case.resource_cls(), case.method_name)(**case.kwargs)
|
||||
|
||||
response, status = _unwrap_response(result)
|
||||
assert response == case.expected_response
|
||||
assert status == case.expected_status
|
||||
|
||||
|
||||
def test_workflow_comment_response_schemas_are_registered() -> None:
|
||||
assert workflow_comment_module.WorkflowCommentBasicList.__name__ in workflow_comment_module.console_ns.models
|
||||
assert workflow_comment_module.WorkflowCommentDetail.__name__ in workflow_comment_module.console_ns.models
|
||||
|
||||
Loading…
Reference in New Issue
Block a user