feat: Adds documents.export endpoint to return cleaned up Markdown (#1343)
This commit is contained in:
@ -366,11 +366,7 @@ router.post("documents.drafts", auth(), pagination(), async ctx => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post("documents.info", auth({ required: false }), async ctx => {
|
async function loadDocument({ id, shareId, user }) {
|
||||||
const { id, shareId } = ctx.body;
|
|
||||||
ctx.assertPresent(id || shareId, "id or shareId is required");
|
|
||||||
|
|
||||||
const user = ctx.state.user;
|
|
||||||
let document;
|
let document;
|
||||||
|
|
||||||
if (shareId) {
|
if (shareId) {
|
||||||
@ -404,6 +400,15 @@ router.post("documents.info", auth({ required: false }), async ctx => {
|
|||||||
authorize(user, "read", document);
|
authorize(user, "read", document);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
router.post("documents.info", auth({ required: false }), async ctx => {
|
||||||
|
const { id, shareId } = ctx.body;
|
||||||
|
ctx.assertPresent(id || shareId, "id or shareId is required");
|
||||||
|
|
||||||
|
const user = ctx.state.user;
|
||||||
|
const document = await loadDocument({ id, shareId, user });
|
||||||
const isPublic = cannot(user, "read", document);
|
const isPublic = cannot(user, "read", document);
|
||||||
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
@ -412,6 +417,18 @@ router.post("documents.info", auth({ required: false }), async ctx => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post("documents.export", auth({ required: false }), async ctx => {
|
||||||
|
const { id, shareId } = ctx.body;
|
||||||
|
ctx.assertPresent(id || shareId, "id or shareId is required");
|
||||||
|
|
||||||
|
const user = ctx.state.user;
|
||||||
|
const document = await loadDocument({ id, shareId, user });
|
||||||
|
|
||||||
|
ctx.body = {
|
||||||
|
data: document.toMarkdown(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
router.post("documents.restore", auth(), async ctx => {
|
router.post("documents.restore", auth(), async ctx => {
|
||||||
const { id, revisionId } = ctx.body;
|
const { id, revisionId } = ctx.body;
|
||||||
ctx.assertPresent(id, "id is required");
|
ctx.assertPresent(id, "id is required");
|
||||||
|
@ -210,6 +210,188 @@ describe("#documents.info", async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#documents.export", async () => {
|
||||||
|
it("should return published document", async () => {
|
||||||
|
const { user, document } = await seed();
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { token: user.getJwtToken(), id: document.id },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data).toEqual(document.toMarkdown());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return archived document", async () => {
|
||||||
|
const { user, document } = await seed();
|
||||||
|
await document.archive(user.id);
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { token: user.getJwtToken(), id: document.id },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data).toEqual(document.toMarkdown());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not return published document in collection not a member of", async () => {
|
||||||
|
const user = await buildUser();
|
||||||
|
const collection = await buildCollection({
|
||||||
|
private: true,
|
||||||
|
teamId: user.teamId,
|
||||||
|
});
|
||||||
|
const document = await buildDocument({ collectionId: collection.id });
|
||||||
|
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { token: user.getJwtToken(), id: document.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.status).toEqual(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return drafts", async () => {
|
||||||
|
const { user, document } = await seed();
|
||||||
|
document.publishedAt = null;
|
||||||
|
await document.save();
|
||||||
|
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { token: user.getJwtToken(), id: document.id },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data).toEqual(document.toMarkdown());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return document from shareId without token", async () => {
|
||||||
|
const { document, user } = await seed();
|
||||||
|
const share = await buildShare({
|
||||||
|
documentId: document.id,
|
||||||
|
teamId: document.teamId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { shareId: share.id },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data).toEqual(document.toMarkdown());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not return document from revoked shareId", async () => {
|
||||||
|
const { document, user } = await seed();
|
||||||
|
const share = await buildShare({
|
||||||
|
documentId: document.id,
|
||||||
|
teamId: document.teamId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
await share.revoke(user.id);
|
||||||
|
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { shareId: share.id },
|
||||||
|
});
|
||||||
|
expect(res.status).toEqual(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not return document from archived shareId", async () => {
|
||||||
|
const { document, user } = await seed();
|
||||||
|
const share = await buildShare({
|
||||||
|
documentId: document.id,
|
||||||
|
teamId: document.teamId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
await document.archive(user.id);
|
||||||
|
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { shareId: share.id },
|
||||||
|
});
|
||||||
|
expect(res.status).toEqual(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return document from shareId with token", async () => {
|
||||||
|
const { user, document } = await seed();
|
||||||
|
const share = await buildShare({
|
||||||
|
documentId: document.id,
|
||||||
|
teamId: document.teamId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { token: user.getJwtToken(), shareId: share.id },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data).toEqual(document.toMarkdown());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return draft document from shareId with token", async () => {
|
||||||
|
const { user, document } = await seed();
|
||||||
|
document.publishedAt = null;
|
||||||
|
await document.save();
|
||||||
|
|
||||||
|
const share = await buildShare({
|
||||||
|
documentId: document.id,
|
||||||
|
teamId: document.teamId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { token: user.getJwtToken(), shareId: share.id },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data).toEqual(document.toMarkdown());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return document from shareId in collection not a member of", async () => {
|
||||||
|
const { user, document, collection } = await seed();
|
||||||
|
const share = await buildShare({
|
||||||
|
documentId: document.id,
|
||||||
|
teamId: document.teamId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
collection.private = true;
|
||||||
|
await collection.save();
|
||||||
|
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { token: user.getJwtToken(), shareId: share.id },
|
||||||
|
});
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
expect(res.status).toEqual(200);
|
||||||
|
expect(body.data).toEqual(document.toMarkdown());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should require authorization without token", async () => {
|
||||||
|
const { document } = await seed();
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { id: document.id },
|
||||||
|
});
|
||||||
|
expect(res.status).toEqual(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should require authorization with incorrect token", async () => {
|
||||||
|
const { document } = await seed();
|
||||||
|
const user = await buildUser();
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { token: user.getJwtToken(), id: document.id },
|
||||||
|
});
|
||||||
|
expect(res.status).toEqual(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should require a valid shareId", async () => {
|
||||||
|
const res = await server.post("/api/documents.export", {
|
||||||
|
body: { shareId: 123 },
|
||||||
|
});
|
||||||
|
expect(res.status).toEqual(400);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#documents.list", async () => {
|
describe("#documents.list", async () => {
|
||||||
it("should return documents", async () => {
|
it("should return documents", async () => {
|
||||||
const { user, document } = await seed();
|
const { user, document } = await seed();
|
||||||
|
Reference in New Issue
Block a user