Compare commits

...

2 Commits

Author SHA1 Message Date
Tom Moor 52bc7e02af Support images in comment text 2025-02-24 20:43:56 -05:00
Tom Moor 32b97bfb68 feat: Allow text param for comments.create 2025-02-23 23:47:43 -05:00
4 changed files with 107 additions and 11 deletions
@@ -9,6 +9,59 @@ exports[`#comments.add_reaction should require authentication 1`] = `
}
`;
exports[`#comments.create should create a comment from markdown text 1`] = `
{
"content": [
{
"attrs": {
"level": 2,
},
"content": [
{
"text": "heading",
"type": "text",
},
],
"type": "heading",
},
{
"content": [
{
"content": [
{
"content": [
{
"text": "list item 1",
"type": "text",
},
],
"type": "paragraph",
},
],
"type": "list_item",
},
{
"content": [
{
"content": [
{
"text": "list item 2",
"type": "text",
},
],
"type": "paragraph",
},
],
"type": "list_item",
},
],
"type": "bullet_list",
},
],
"type": "doc",
}
`;
exports[`#comments.create should require authentication 1`] = `
{
"error": "authentication_required",
@@ -481,6 +481,30 @@ describe("#comments.create", () => {
expect(body.policies[0].abilities.delete).toBeTruthy();
});
it("should create a comment from markdown text", async () => {
const team = await buildTeam();
const user = await buildUser({ teamId: team.id });
const document = await buildDocument({
userId: user.id,
teamId: user.teamId,
});
const text = "## heading\n\n- list item 1\n- list item 2";
const res = await server.post("/api/comments.create", {
body: {
token: user.getJwtToken(),
documentId: document.id,
text,
},
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.data.data).toMatchSnapshot();
});
it("should not allow empty comment data", async () => {
const team = await buildTeam();
const user = await buildUser({ teamId: team.id });
+12 -1
View File
@@ -6,6 +6,7 @@ import {
TeamPreference,
MentionType,
} from "@shared/types";
import { parser } from "@server/editor";
import auth from "@server/middlewares/authentication";
import { feature } from "@server/middlewares/feature";
import { rateLimiter } from "@server/middlewares/rateLimiter";
@@ -13,6 +14,7 @@ import { transaction } from "@server/middlewares/transaction";
import validate from "@server/middlewares/validate";
import { Document, Comment, Collection, Reaction } from "@server/models";
import { ProsemirrorHelper } from "@server/models/helpers/ProsemirrorHelper";
import { TextHelper } from "@server/models/helpers/TextHelper";
import { authorize } from "@server/policies";
import { presentComment, presentPolicies } from "@server/presenters";
import { APIContext } from "@server/types";
@@ -30,7 +32,7 @@ router.post(
validate(T.CommentsCreateSchema),
transaction(),
async (ctx: APIContext<T.CommentsCreateReq>) => {
const { id, documentId, parentCommentId, data } = ctx.input.body;
const { id, documentId, parentCommentId } = ctx.input.body;
const { user } = ctx.state.auth;
const { transaction } = ctx.state;
@@ -40,6 +42,15 @@ router.post(
});
authorize(user, "comment", document);
const text = ctx.input.body.text
? await TextHelper.replaceImagesWithAttachments(
ctx,
ctx.input.body.text,
user
)
: undefined;
const data = text ? parser.parse(text).toJSON() : ctx.input.body.data;
const comment = await Comment.createWithCtx(ctx, {
id,
data,
+18 -10
View File
@@ -1,4 +1,5 @@
import emojiRegex from "emoji-regex";
import isEmpty from "lodash/isEmpty";
import { z } from "zod";
import { CommentStatusFilter } from "@shared/types";
import { BaseSchema, ProsemirrorSchema } from "@server/routes/api/schema";
@@ -23,19 +24,26 @@ const CommentsSortParamsSchema = z.object({
});
export const CommentsCreateSchema = BaseSchema.extend({
body: z.object({
/** Allow creation with a specific ID */
id: z.string().uuid().optional(),
body: z
.object({
/** Allow creation with a specific ID */
id: z.string().uuid().optional(),
/** Create comment for this document */
documentId: z.string().uuid(),
/** Create comment for this document */
documentId: z.string().uuid(),
/** Create comment under this parent */
parentCommentId: z.string().uuid().optional(),
/** Create comment under this parent */
parentCommentId: z.string().uuid().optional(),
/** Create comment with this data */
data: ProsemirrorSchema(),
}),
/** Create comment with this data */
data: ProsemirrorSchema().optional(),
/** Create comment with this text */
text: z.string().optional(),
})
.refine((obj) => !(isEmpty(obj.data) && isEmpty(obj.text)), {
message: "One of data or text is required",
}),
});
export type CommentsCreateReq = z.infer<typeof CommentsCreateSchema>;