mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 13:29:02 +08:00
feat(server): comment service and resolver (#12761)
close CLOUD-227 close CLOUD-230 #### PR Dependency Tree * **PR #12761** 👈 * **PR #12924** * **PR #12925** This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Introduced a comprehensive commenting system, enabling users to create, update, resolve, and delete comments and replies on documents. * Added support for uploading attachments to comments, with clear error messaging if size limits are exceeded. * Implemented role-based permissions for comment actions, including a new "Commenter" role. * Enabled paginated listing and change tracking of comments and replies via GraphQL queries. * Provided full localization and error handling for comment-related actions. * **Bug Fixes** * Improved uniqueness handling when fetching user data for comments and replies. * **Documentation** * Extended GraphQL schema and frontend localization to document and support new comment features. * **Tests** * Added extensive backend test suites covering all comment and reply functionalities, permissions, and attachment uploads. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -99,6 +99,73 @@ type ChatMessage {
|
||||
streamObjects: [StreamObject!]
|
||||
}
|
||||
|
||||
"""Comment change action"""
|
||||
enum CommentChangeAction {
|
||||
delete
|
||||
update
|
||||
}
|
||||
|
||||
type CommentChangeObjectType {
|
||||
"""The action of the comment change"""
|
||||
action: CommentChangeAction!
|
||||
commentId: ID
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
The item of the comment or reply, different types have different fields, see UnionCommentObjectType
|
||||
"""
|
||||
item: JSONObject!
|
||||
}
|
||||
|
||||
type CommentChangeObjectTypeEdge {
|
||||
cursor: String!
|
||||
node: CommentChangeObjectType!
|
||||
}
|
||||
|
||||
input CommentCreateInput {
|
||||
content: JSONObject!
|
||||
docId: ID!
|
||||
workspaceId: ID!
|
||||
}
|
||||
|
||||
type CommentObjectType {
|
||||
"""The content of the comment"""
|
||||
content: JSONObject!
|
||||
|
||||
"""The created at time of the comment"""
|
||||
createdAt: DateTime!
|
||||
id: ID!
|
||||
|
||||
"""The replies of the comment"""
|
||||
replies: [ReplyObjectType!]!
|
||||
|
||||
"""Whether the comment is resolved"""
|
||||
resolved: Boolean!
|
||||
|
||||
"""The updated at time of the comment"""
|
||||
updatedAt: DateTime!
|
||||
|
||||
"""The user who created the comment"""
|
||||
user: PublicUserType!
|
||||
}
|
||||
|
||||
type CommentObjectTypeEdge {
|
||||
cursor: String!
|
||||
node: CommentObjectType!
|
||||
}
|
||||
|
||||
input CommentResolveInput {
|
||||
id: ID!
|
||||
|
||||
"""Whether the comment is resolved"""
|
||||
resolved: Boolean!
|
||||
}
|
||||
|
||||
input CommentUpdateInput {
|
||||
content: JSONObject!
|
||||
id: ID!
|
||||
}
|
||||
|
||||
enum ContextCategories {
|
||||
Collection
|
||||
Tag
|
||||
@@ -456,6 +523,10 @@ type DocNotFoundDataType {
|
||||
}
|
||||
|
||||
type DocPermissions {
|
||||
Doc_Comments_Create: Boolean!
|
||||
Doc_Comments_Delete: Boolean!
|
||||
Doc_Comments_Read: Boolean!
|
||||
Doc_Comments_Resolve: Boolean!
|
||||
Doc_Copy: Boolean!
|
||||
Doc_Delete: Boolean!
|
||||
Doc_Duplicate: Boolean!
|
||||
@@ -473,6 +544,7 @@ type DocPermissions {
|
||||
|
||||
"""User permission in doc"""
|
||||
enum DocRole {
|
||||
Commenter
|
||||
Editor
|
||||
External
|
||||
Manager
|
||||
@@ -541,6 +613,7 @@ enum ErrorNames {
|
||||
CAN_NOT_REVOKE_YOURSELF
|
||||
CAPTCHA_VERIFICATION_FAILED
|
||||
COMMENT_ATTACHMENT_NOT_FOUND
|
||||
COMMENT_ATTACHMENT_QUOTA_EXCEEDED
|
||||
COMMENT_NOT_FOUND
|
||||
COPILOT_ACTION_TAKEN
|
||||
COPILOT_CONTEXT_FILE_NOT_SUPPORTED
|
||||
@@ -1090,6 +1163,7 @@ type Mutation {
|
||||
|
||||
"""Create a subscription checkout link of stripe"""
|
||||
createCheckoutSession(input: CreateCheckoutSessionInput!): String!
|
||||
createComment(input: CommentCreateInput!): CommentObjectType!
|
||||
|
||||
"""Create a context session"""
|
||||
createCopilotContext(sessionId: String!, workspaceId: String!): String!
|
||||
@@ -1106,6 +1180,7 @@ type Mutation {
|
||||
"""Create a stripe customer portal to manage payment methods"""
|
||||
createCustomerPortal: String!
|
||||
createInviteLink(expireTime: WorkspaceInviteLinkExpireTime!, workspaceId: String!): InviteLink!
|
||||
createReply(input: ReplyCreateInput!): ReplyObjectType!
|
||||
createSelfhostWorkspaceCustomerPortal(workspaceId: String!): String!
|
||||
|
||||
"""Create a new user"""
|
||||
@@ -1117,6 +1192,12 @@ type Mutation {
|
||||
deleteAccount: DeleteAccount!
|
||||
deleteBlob(hash: String @deprecated(reason: "use parameter [key]"), key: String, permanently: Boolean! = false, workspaceId: String!): Boolean!
|
||||
|
||||
"""Delete a comment"""
|
||||
deleteComment(id: String!): Boolean!
|
||||
|
||||
"""Delete a reply"""
|
||||
deleteReply(id: String!): Boolean!
|
||||
|
||||
"""Delete a user account"""
|
||||
deleteUser(id: String!): DeleteAccount!
|
||||
deleteWorkspace(id: String!): Boolean!
|
||||
@@ -1165,6 +1246,9 @@ type Mutation {
|
||||
"""Remove workspace embedding files"""
|
||||
removeWorkspaceEmbeddingFiles(fileId: String!, workspaceId: String!): Boolean!
|
||||
removeWorkspaceFeature(feature: FeatureType!, workspaceId: String!): Boolean!
|
||||
|
||||
"""Resolve a comment or not"""
|
||||
resolveComment(input: CommentResolveInput!): Boolean!
|
||||
resumeSubscription(idempotencyKey: String @deprecated(reason: "use header `Idempotency-Key`"), plan: SubscriptionPlan = Pro, workspaceId: String): SubscriptionType!
|
||||
retryAudioTranscription(jobId: String!, workspaceId: String!): TranscriptionResultType
|
||||
revoke(userId: String!, workspaceId: String!): Boolean! @deprecated(reason: "use [revokeMember] instead")
|
||||
@@ -1185,6 +1269,9 @@ type Mutation {
|
||||
"""update app configuration"""
|
||||
updateAppConfig(updates: [UpdateAppConfigInput!]!): JSONObject!
|
||||
|
||||
"""Update a comment content"""
|
||||
updateComment(input: CommentUpdateInput!): Boolean!
|
||||
|
||||
"""Update a copilot prompt"""
|
||||
updateCopilotPrompt(messages: [CopilotPromptMessageInput!]!, name: String!): CopilotPromptType!
|
||||
|
||||
@@ -1194,6 +1281,9 @@ type Mutation {
|
||||
updateDocUserRole(input: UpdateDocUserRoleInput!): Boolean!
|
||||
updateProfile(input: UpdateUserInput!): UserType!
|
||||
|
||||
"""Update a reply content"""
|
||||
updateReply(input: ReplyUpdateInput!): Boolean!
|
||||
|
||||
"""Update user settings"""
|
||||
updateSettings(input: UpdateUserSettingsInput!): Boolean!
|
||||
updateSubscriptionRecurring(idempotencyKey: String @deprecated(reason: "use header `Idempotency-Key`"), plan: SubscriptionPlan = Pro, recurring: SubscriptionRecurring!, workspaceId: String): SubscriptionType!
|
||||
@@ -1213,6 +1303,9 @@ type Mutation {
|
||||
"""Upload user avatar"""
|
||||
uploadAvatar(avatar: Upload!): UserType!
|
||||
|
||||
"""Upload a comment attachment and return the access url"""
|
||||
uploadCommentAttachment(attachment: Upload!, docId: String!, workspaceId: String!): String!
|
||||
|
||||
"""validate app configuration"""
|
||||
validateAppConfig(updates: [UpdateAppConfigInput!]!): [AppConfigValidateResult!]!
|
||||
verifyEmail(token: String!): Boolean!
|
||||
@@ -1301,6 +1394,18 @@ type PageInfo {
|
||||
startCursor: String
|
||||
}
|
||||
|
||||
type PaginatedCommentChangeObjectType {
|
||||
edges: [CommentChangeObjectTypeEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int!
|
||||
}
|
||||
|
||||
type PaginatedCommentObjectType {
|
||||
edges: [CommentObjectTypeEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int!
|
||||
}
|
||||
|
||||
type PaginatedCopilotWorkspaceFileType {
|
||||
edges: [CopilotWorkspaceFileTypeEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
@@ -1474,6 +1579,33 @@ input RemoveContextFileInput {
|
||||
fileId: String!
|
||||
}
|
||||
|
||||
input ReplyCreateInput {
|
||||
commentId: ID!
|
||||
content: JSONObject!
|
||||
}
|
||||
|
||||
type ReplyObjectType {
|
||||
commentId: ID!
|
||||
|
||||
"""The content of the reply"""
|
||||
content: JSONObject!
|
||||
|
||||
"""The created at time of the reply"""
|
||||
createdAt: DateTime!
|
||||
id: ID!
|
||||
|
||||
"""The updated at time of the reply"""
|
||||
updatedAt: DateTime!
|
||||
|
||||
"""The user who created the reply"""
|
||||
user: PublicUserType!
|
||||
}
|
||||
|
||||
input ReplyUpdateInput {
|
||||
content: JSONObject!
|
||||
id: ID!
|
||||
}
|
||||
|
||||
input RevokeDocUserRoleInput {
|
||||
docId: String!
|
||||
userId: String!
|
||||
@@ -2017,6 +2149,12 @@ type WorkspaceType {
|
||||
"""Blobs size of workspace"""
|
||||
blobsSize: Int!
|
||||
|
||||
"""Get comment changes of a doc"""
|
||||
commentChanges(docId: String!, pagination: PaginationInput!): PaginatedCommentChangeObjectType!
|
||||
|
||||
"""Get comments of a doc"""
|
||||
comments(docId: String!, pagination: PaginationInput): PaginatedCommentObjectType!
|
||||
|
||||
"""Workspace created date"""
|
||||
createdAt: DateTime!
|
||||
|
||||
|
||||
Reference in New Issue
Block a user