mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-22 19:53:48 +08:00
feat(server): get recently updated docs (#12861)
close AI-218 #### PR Dependency Tree * **PR #12861** 👈 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 a "Recently Updated Documents" feature, allowing users to view a paginated list of the most recently updated documents within a workspace. - Document metadata now includes a "title" field for easier identification. - **Tests** - Introduced new end-to-end tests to verify the recently updated documents query and its pagination behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
import { getRecentlyUpdatedDocsQuery } from '@affine/graphql';
|
||||||
|
|
||||||
|
import { Mockers } from '../../mocks';
|
||||||
|
import { app, e2e } from '../test';
|
||||||
|
|
||||||
|
e2e('should get recently updated docs', async t => {
|
||||||
|
const owner = await app.signup();
|
||||||
|
|
||||||
|
const workspace = await app.create(Mockers.Workspace, {
|
||||||
|
owner: { id: owner.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
const docSnapshot1 = await app.create(Mockers.DocSnapshot, {
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
user: owner,
|
||||||
|
});
|
||||||
|
const doc1 = await app.create(Mockers.DocMeta, {
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
docId: docSnapshot1.id,
|
||||||
|
title: 'doc1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const docSnapshot2 = await app.create(Mockers.DocSnapshot, {
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
user: owner,
|
||||||
|
});
|
||||||
|
const doc2 = await app.create(Mockers.DocMeta, {
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
docId: docSnapshot2.id,
|
||||||
|
title: 'doc2',
|
||||||
|
});
|
||||||
|
|
||||||
|
const docSnapshot3 = await app.create(Mockers.DocSnapshot, {
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
user: owner,
|
||||||
|
});
|
||||||
|
const doc3 = await app.create(Mockers.DocMeta, {
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
docId: docSnapshot3.id,
|
||||||
|
title: 'doc3',
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
workspace: { recentlyUpdatedDocs },
|
||||||
|
} = await app.gql({
|
||||||
|
query: getRecentlyUpdatedDocsQuery,
|
||||||
|
variables: {
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
pagination: {
|
||||||
|
first: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
t.is(recentlyUpdatedDocs.totalCount, 3);
|
||||||
|
t.is(recentlyUpdatedDocs.edges[0].node.id, doc3.docId);
|
||||||
|
t.is(recentlyUpdatedDocs.edges[0].node.title, doc3.title);
|
||||||
|
t.is(recentlyUpdatedDocs.edges[1].node.id, doc2.docId);
|
||||||
|
t.is(recentlyUpdatedDocs.edges[1].node.title, doc2.title);
|
||||||
|
t.is(recentlyUpdatedDocs.edges[2].node.id, doc1.docId);
|
||||||
|
t.is(recentlyUpdatedDocs.edges[2].node.title, doc1.title);
|
||||||
|
});
|
||||||
@@ -76,6 +76,9 @@ class DocType {
|
|||||||
|
|
||||||
@Field(() => String, { nullable: true })
|
@Field(() => String, { nullable: true })
|
||||||
lastUpdaterId?: string;
|
lastUpdaterId?: string;
|
||||||
|
|
||||||
|
@Field(() => String, { nullable: true })
|
||||||
|
title?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@InputType()
|
@InputType()
|
||||||
@@ -266,6 +269,26 @@ export class WorkspaceDocResolver {
|
|||||||
return paginate(rows, 'createdAt', pagination, count);
|
return paginate(rows, 'createdAt', pagination, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ResolveField(() => PaginatedDocType, {
|
||||||
|
description: 'Get recently updated docs of a workspace',
|
||||||
|
})
|
||||||
|
async recentlyUpdatedDocs(
|
||||||
|
@CurrentUser() me: CurrentUser,
|
||||||
|
@Parent() workspace: WorkspaceType,
|
||||||
|
@Args('pagination', PaginationInput.decode) pagination: PaginationInput
|
||||||
|
): Promise<PaginatedDocType> {
|
||||||
|
const [count, rows] = await this.models.doc.paginateDocInfoByUpdatedAt(
|
||||||
|
workspace.id,
|
||||||
|
pagination
|
||||||
|
);
|
||||||
|
const needs = await this.ac
|
||||||
|
.user(me.id)
|
||||||
|
.workspace(workspace.id)
|
||||||
|
.docs(rows, 'Doc.Read');
|
||||||
|
|
||||||
|
return paginate(needs, 'updatedAt', pagination, count);
|
||||||
|
}
|
||||||
|
|
||||||
@ResolveField(() => DocType, {
|
@ResolveField(() => DocType, {
|
||||||
description: 'Get get with given id',
|
description: 'Get get with given id',
|
||||||
complexity: 2,
|
complexity: 2,
|
||||||
|
|||||||
@@ -636,5 +636,61 @@ export class DocModel extends BaseModel {
|
|||||||
|
|
||||||
return [count, rows] as const;
|
return [count, rows] as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async paginateDocInfoByUpdatedAt(
|
||||||
|
workspaceId: string,
|
||||||
|
pagination: PaginationInput
|
||||||
|
) {
|
||||||
|
const count = await this.db.workspaceDoc.count({
|
||||||
|
where: {
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const after = pagination.after
|
||||||
|
? Prisma.sql`AND "snapshots"."updated_at" < ${new Date(pagination.after)}`
|
||||||
|
: Prisma.sql``;
|
||||||
|
|
||||||
|
const rows = await this.db.$queryRaw<
|
||||||
|
{
|
||||||
|
workspaceId: string;
|
||||||
|
docId: string;
|
||||||
|
mode: PublicDocMode;
|
||||||
|
public: boolean;
|
||||||
|
defaultRole: DocRole;
|
||||||
|
title: string | null;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
creatorId?: string;
|
||||||
|
lastUpdaterId?: string;
|
||||||
|
}[]
|
||||||
|
>`
|
||||||
|
SELECT
|
||||||
|
"workspace_pages"."workspace_id" as "workspaceId",
|
||||||
|
"workspace_pages"."page_id" as "docId",
|
||||||
|
"workspace_pages"."mode" as "mode",
|
||||||
|
"workspace_pages"."public" as "public",
|
||||||
|
"workspace_pages"."defaultRole" as "defaultRole",
|
||||||
|
"workspace_pages"."title" as "title",
|
||||||
|
"snapshots"."created_at" as "createdAt",
|
||||||
|
"snapshots"."updated_at" as "updatedAt",
|
||||||
|
"snapshots"."created_by" as "creatorId",
|
||||||
|
"snapshots"."updated_by" as "lastUpdaterId"
|
||||||
|
FROM "workspace_pages"
|
||||||
|
INNER JOIN "snapshots"
|
||||||
|
ON "workspace_pages"."workspace_id" = "snapshots"."workspace_id"
|
||||||
|
AND "workspace_pages"."page_id" = "snapshots"."guid"
|
||||||
|
WHERE
|
||||||
|
"workspace_pages"."workspace_id" = ${workspaceId}
|
||||||
|
${after}
|
||||||
|
ORDER BY
|
||||||
|
"snapshots"."updated_at" DESC
|
||||||
|
LIMIT ${pagination.first}
|
||||||
|
OFFSET ${pagination.offset}
|
||||||
|
`;
|
||||||
|
|
||||||
|
return [count, rows] as const;
|
||||||
|
}
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -517,6 +517,7 @@ type DocType {
|
|||||||
mode: PublicDocMode!
|
mode: PublicDocMode!
|
||||||
permissions: DocPermissions!
|
permissions: DocPermissions!
|
||||||
public: Boolean!
|
public: Boolean!
|
||||||
|
title: String
|
||||||
updatedAt: DateTime
|
updatedAt: DateTime
|
||||||
workspaceId: String!
|
workspaceId: String!
|
||||||
}
|
}
|
||||||
@@ -2062,6 +2063,9 @@ type WorkspaceType {
|
|||||||
"""quota of workspace"""
|
"""quota of workspace"""
|
||||||
quota: WorkspaceQuotaType!
|
quota: WorkspaceQuotaType!
|
||||||
|
|
||||||
|
"""Get recently updated docs of a workspace"""
|
||||||
|
recentlyUpdatedDocs(pagination: PaginationInput!): PaginatedDocType!
|
||||||
|
|
||||||
"""Role of current signed in user in workspace"""
|
"""Role of current signed in user in workspace"""
|
||||||
role: Permission!
|
role: Permission!
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
query getRecentlyUpdatedDocs($workspaceId: String!, $pagination: PaginationInput!) {
|
||||||
|
workspace(id: $workspaceId) {
|
||||||
|
recentlyUpdatedDocs(pagination: $pagination) {
|
||||||
|
totalCount
|
||||||
|
pageInfo {
|
||||||
|
endCursor
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
creatorId
|
||||||
|
lastUpdaterId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1180,6 +1180,32 @@ export const getPublicUserByIdQuery = {
|
|||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getRecentlyUpdatedDocsQuery = {
|
||||||
|
id: 'getRecentlyUpdatedDocsQuery' as const,
|
||||||
|
op: 'getRecentlyUpdatedDocs',
|
||||||
|
query: `query getRecentlyUpdatedDocs($workspaceId: String!, $pagination: PaginationInput!) {
|
||||||
|
workspace(id: $workspaceId) {
|
||||||
|
recentlyUpdatedDocs(pagination: $pagination) {
|
||||||
|
totalCount
|
||||||
|
pageInfo {
|
||||||
|
endCursor
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
creatorId
|
||||||
|
lastUpdaterId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
export const getUserFeaturesQuery = {
|
export const getUserFeaturesQuery = {
|
||||||
id: 'getUserFeaturesQuery' as const,
|
id: 'getUserFeaturesQuery' as const,
|
||||||
op: 'getUserFeatures',
|
op: 'getUserFeatures',
|
||||||
|
|||||||
@@ -630,6 +630,7 @@ export interface DocType {
|
|||||||
mode: PublicDocMode;
|
mode: PublicDocMode;
|
||||||
permissions: DocPermissions;
|
permissions: DocPermissions;
|
||||||
public: Scalars['Boolean']['output'];
|
public: Scalars['Boolean']['output'];
|
||||||
|
title: Maybe<Scalars['String']['output']>;
|
||||||
updatedAt: Maybe<Scalars['DateTime']['output']>;
|
updatedAt: Maybe<Scalars['DateTime']['output']>;
|
||||||
workspaceId: Scalars['String']['output'];
|
workspaceId: Scalars['String']['output'];
|
||||||
}
|
}
|
||||||
@@ -2647,6 +2648,8 @@ export interface WorkspaceType {
|
|||||||
publicPages: Array<DocType>;
|
publicPages: Array<DocType>;
|
||||||
/** quota of workspace */
|
/** quota of workspace */
|
||||||
quota: WorkspaceQuotaType;
|
quota: WorkspaceQuotaType;
|
||||||
|
/** Get recently updated docs of a workspace */
|
||||||
|
recentlyUpdatedDocs: PaginatedDocType;
|
||||||
/** Role of current signed in user in workspace */
|
/** Role of current signed in user in workspace */
|
||||||
role: Permission;
|
role: Permission;
|
||||||
/** Search a specific table */
|
/** Search a specific table */
|
||||||
@@ -2694,6 +2697,10 @@ export interface WorkspaceTypePublicPageArgs {
|
|||||||
pageId: Scalars['String']['input'];
|
pageId: Scalars['String']['input'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WorkspaceTypeRecentlyUpdatedDocsArgs {
|
||||||
|
pagination: PaginationInput;
|
||||||
|
}
|
||||||
|
|
||||||
export interface WorkspaceTypeSearchArgs {
|
export interface WorkspaceTypeSearchArgs {
|
||||||
input: SearchInput;
|
input: SearchInput;
|
||||||
}
|
}
|
||||||
@@ -4064,6 +4071,39 @@ export type GetPublicUserByIdQuery = {
|
|||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GetRecentlyUpdatedDocsQueryVariables = Exact<{
|
||||||
|
workspaceId: Scalars['String']['input'];
|
||||||
|
pagination: PaginationInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type GetRecentlyUpdatedDocsQuery = {
|
||||||
|
__typename?: 'Query';
|
||||||
|
workspace: {
|
||||||
|
__typename?: 'WorkspaceType';
|
||||||
|
recentlyUpdatedDocs: {
|
||||||
|
__typename?: 'PaginatedDocType';
|
||||||
|
totalCount: number;
|
||||||
|
pageInfo: {
|
||||||
|
__typename?: 'PageInfo';
|
||||||
|
endCursor: string | null;
|
||||||
|
hasNextPage: boolean;
|
||||||
|
};
|
||||||
|
edges: Array<{
|
||||||
|
__typename?: 'DocTypeEdge';
|
||||||
|
node: {
|
||||||
|
__typename?: 'DocType';
|
||||||
|
id: string;
|
||||||
|
title: string | null;
|
||||||
|
createdAt: string | null;
|
||||||
|
updatedAt: string | null;
|
||||||
|
creatorId: string | null;
|
||||||
|
lastUpdaterId: string | null;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type GetUserFeaturesQueryVariables = Exact<{ [key: string]: never }>;
|
export type GetUserFeaturesQueryVariables = Exact<{ [key: string]: never }>;
|
||||||
|
|
||||||
export type GetUserFeaturesQuery = {
|
export type GetUserFeaturesQuery = {
|
||||||
@@ -5197,6 +5237,11 @@ export type Queries =
|
|||||||
variables: GetPublicUserByIdQueryVariables;
|
variables: GetPublicUserByIdQueryVariables;
|
||||||
response: GetPublicUserByIdQuery;
|
response: GetPublicUserByIdQuery;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
name: 'getRecentlyUpdatedDocsQuery';
|
||||||
|
variables: GetRecentlyUpdatedDocsQueryVariables;
|
||||||
|
response: GetRecentlyUpdatedDocsQuery;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
name: 'getUserFeaturesQuery';
|
name: 'getUserFeaturesQuery';
|
||||||
variables: GetUserFeaturesQueryVariables;
|
variables: GetUserFeaturesQueryVariables;
|
||||||
|
|||||||
Reference in New Issue
Block a user