mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 52bc7e02af | |||
| 32b97bfb68 |
@@ -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 });
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user