feat(server): export title and summary on doc resolver (#13139)

close AF-2732






#### PR Dependency Tree


* **PR #13139** 👈

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**
* Added support for a document summary field, allowing documents to
include and display an optional summary alongside the title.

* **Bug Fixes**
* Improved access control when retrieving documents, ensuring proper
permission checks are enforced.

* **Tests**
* Expanded test coverage to verify correct handling of document title
and summary fields, including cases where the summary is absent.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
fengmk2
2025-07-10 19:13:19 +08:00
committed by GitHub
parent 46a9d0f7fe
commit f655e6e8bf
8 changed files with 79 additions and 2 deletions

View File

@@ -99,3 +99,56 @@ e2e(
t.is(result2.workspace.doc.public, true);
}
);
e2e('should get doc with title and summary', async t => {
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
const docSnapshot = await app.create(Mockers.DocSnapshot, {
workspaceId: workspace.id,
user: owner,
});
const doc = await app.create(Mockers.DocMeta, {
workspaceId: workspace.id,
docId: docSnapshot.id,
title: 'doc1',
summary: 'summary1',
});
const result = await app.gql({
query: getWorkspacePageByIdQuery,
variables: { workspaceId: workspace.id, pageId: doc.docId },
});
t.is(result.workspace.doc.title, doc.title);
t.is(result.workspace.doc.summary, doc.summary);
});
e2e('should get doc with title and null summary', async t => {
const owner = await app.signup();
const workspace = await app.create(Mockers.Workspace, {
owner: { id: owner.id },
});
const docSnapshot = await app.create(Mockers.DocSnapshot, {
workspaceId: workspace.id,
user: owner,
});
const doc = await app.create(Mockers.DocMeta, {
workspaceId: workspace.id,
docId: docSnapshot.id,
title: 'doc1',
});
const result = await app.gql({
query: getWorkspacePageByIdQuery,
variables: { workspaceId: workspace.id, pageId: doc.docId },
});
t.is(result.workspace.doc.title, doc.title);
t.is(result.workspace.doc.summary, null);
});

View File

@@ -669,7 +669,10 @@ test('should get doc info', async t => {
};
await t.context.doc.upsert(snapshot);
await t.context.doc.upsertMeta(workspace.id, docId);
await t.context.doc.upsertMeta(workspace.id, docId, {
title: 'test title',
summary: 'test summary',
});
const docInfo = await t.context.doc.getDocInfo(workspace.id, docId);
@@ -679,6 +682,8 @@ test('should get doc info', async t => {
updatedAt: new Date(snapshot.timestamp),
creatorId: user.id,
lastUpdaterId: user.id,
title: 'test title',
summary: 'test summary',
});
});

View File

@@ -79,6 +79,9 @@ class DocType {
@Field(() => String, { nullable: true })
title?: string | null;
@Field(() => String, { nullable: true })
summary?: string | null;
}
@InputType()
@@ -250,10 +253,11 @@ export class WorkspaceDocResolver {
deprecationReason: 'use [WorkspaceType.doc] instead',
})
async publicPage(
@CurrentUser() me: CurrentUser,
@Parent() workspace: WorkspaceType,
@Args('pageId') pageId: string
) {
return this.doc(workspace, pageId);
return this.doc(me, workspace, pageId);
}
@ResolveField(() => PaginatedDocType)
@@ -294,11 +298,14 @@ export class WorkspaceDocResolver {
complexity: 2,
})
async doc(
@CurrentUser() me: CurrentUser,
@Parent() workspace: WorkspaceType,
@Args('docId') docId: string
): Promise<DocType> {
const doc = await this.models.doc.getDocInfo(workspace.id, docId);
if (doc) {
// check if doc is readable
await this.ac.user(me.id).doc(workspace.id, docId).assert('Doc.Read');
return doc;
}

View File

@@ -558,6 +558,8 @@ export class DocModel extends BaseModel {
mode: PublicDocMode;
public: boolean;
defaultRole: DocRole;
title: string | null;
summary: string | null;
createdAt: Date;
updatedAt: Date;
creatorId?: string;
@@ -570,6 +572,8 @@ export class DocModel extends BaseModel {
"workspace_pages"."mode" as "mode",
"workspace_pages"."public" as "public",
"workspace_pages"."defaultRole" as "defaultRole",
"workspace_pages"."title" as "title",
"workspace_pages"."summary" as "summary",
"snapshots"."created_at" as "createdAt",
"snapshots"."updated_at" as "updatedAt",
"snapshots"."created_by" as "creatorId",

View File

@@ -595,6 +595,7 @@ type DocType {
mode: PublicDocMode!
permissions: DocPermissions!
public: Boolean!
summary: String
title: String
updatedAt: DateTime
workspaceId: String!