diff --git a/apps/desktop/src/main/lib/utils.ts b/apps/desktop/src/main/lib/utils.ts index 5c0464dc..cf9a7f3d 100644 --- a/apps/desktop/src/main/lib/utils.ts +++ b/apps/desktop/src/main/lib/utils.ts @@ -112,13 +112,13 @@ export const getFileMetadata = (filePath: string): FileMetadata | null => { }; }; -export const fetchNodeAncestors = async ( +export const fetchNodeTree = async ( database: | Kysely | Transaction, nodeId: string ): Promise => { - return database + const nodes = await database .withRecursive('ancestor_nodes', (cte) => cte .selectFrom('nodes') @@ -138,6 +138,8 @@ export const fetchNodeAncestors = async ( .selectFrom('ancestor_nodes') .selectAll() .execute(); + + return nodes.reverse(); }; export const fetchNode = ( diff --git a/apps/desktop/src/main/queries/nodes/node-tree-get.ts b/apps/desktop/src/main/queries/nodes/node-tree-get.ts index c5188e78..dd72a7c7 100644 --- a/apps/desktop/src/main/queries/nodes/node-tree-get.ts +++ b/apps/desktop/src/main/queries/nodes/node-tree-get.ts @@ -2,7 +2,7 @@ import { ChangeCheckResult, QueryHandler } from '@/main/lib/types'; import { NodeTreeGetQueryInput } from '@/shared/queries/nodes/node-tree-get'; import { Event } from '@/shared/types/events'; import { WorkspaceQueryHandlerBase } from '@/main/queries/workspace-query-handler-base'; -import { fetchNodeAncestors } from '@/main/lib/utils'; +import { fetchNodeTree } from '@/main/lib/utils'; import { mapNode } from '@/main/lib/mappers'; import { LocalNode } from '@/shared/types/nodes'; @@ -12,7 +12,7 @@ export class NodeTreeGetQueryHandler { public async handleQuery(input: NodeTreeGetQueryInput): Promise { const workspace = this.getWorkspace(input.accountId, input.workspaceId); - const rows = await fetchNodeAncestors(workspace.database, input.nodeId); + const rows = await fetchNodeTree(workspace.database, input.nodeId); return rows.map(mapNode); } diff --git a/apps/desktop/src/main/services/workspaces/node-service.ts b/apps/desktop/src/main/services/workspaces/node-service.ts index eefdb239..a77fc908 100644 --- a/apps/desktop/src/main/services/workspaces/node-service.ts +++ b/apps/desktop/src/main/services/workspaces/node-service.ts @@ -15,7 +15,7 @@ import { } from '@colanode/core'; import { decodeState, encodeState, YDoc } from '@colanode/crdt'; -import { fetchNodeAncestors } from '@/main/lib/utils'; +import { fetchNodeTree } from '@/main/lib/utils'; import { mapFile, mapNode } from '@/main/lib/mappers'; import { eventBus } from '@/shared/lib/event-bus'; import { WorkspaceService } from '@/main/services/workspaces/workspace-service'; @@ -47,8 +47,8 @@ export class NodeService { public async createNode(input: CreateNodeInput): Promise { this.debug(`Creating ${Array.isArray(input) ? 'nodes' : 'node'}`); - const ancestors = input.parentId - ? await fetchNodeAncestors(this.workspace.database, input.parentId) + const tree = input.parentId + ? await fetchNodeTree(this.workspace.database, input.parentId) : []; const model = getNodeModel(input.attributes.type); @@ -59,7 +59,7 @@ export class NodeService { workspaceId: this.workspace.id, accountId: this.workspace.accountId, }, - ancestors: ancestors.map(mapNode), + tree: tree.map(mapNode), attributes: input.attributes, }; @@ -75,7 +75,7 @@ export class NodeService { } const createdAt = new Date().toISOString(); - const rootId = ancestors[0]?.id ?? input.id; + const rootId = tree[0]?.id ?? input.id; const { createdNode, createdMutation } = await this.workspace.database .transaction() @@ -181,8 +181,8 @@ export class NodeService { ): Promise { this.debug(`Updating node ${nodeId}`); - const ancestors = await fetchNodeAncestors(this.workspace.database, nodeId); - const nodeRow = ancestors[ancestors.length - 1]; + const tree = await fetchNodeTree(this.workspace.database, nodeId); + const nodeRow = tree[tree.length - 1]; if (!nodeRow || nodeRow.id !== nodeId) { return 'not_found'; } @@ -201,7 +201,7 @@ export class NodeService { workspaceId: this.workspace.id, accountId: this.workspace.accountId, }, - ancestors: ancestors.map(mapNode), + tree: tree.map(mapNode), node: node, attributes: updatedAttributes, }; @@ -332,8 +332,8 @@ export class NodeService { } public async deleteNode(nodeId: string) { - const ancestors = await fetchNodeAncestors(this.workspace.database, nodeId); - const nodeRow = ancestors[ancestors.length - 1]; + const tree = await fetchNodeTree(this.workspace.database, nodeId); + const nodeRow = tree[tree.length - 1]; if (!nodeRow || nodeRow.id !== nodeId) { return 'not_found'; } @@ -349,7 +349,7 @@ export class NodeService { workspaceId: this.workspace.id, accountId: this.workspace.accountId, }, - ancestors: ancestors.map(mapNode), + tree: tree.map(mapNode), node: node, }; diff --git a/apps/server/src/lib/nodes.ts b/apps/server/src/lib/nodes.ts index a3c7e543..4f2ea9f9 100644 --- a/apps/server/src/lib/nodes.ts +++ b/apps/server/src/lib/nodes.ts @@ -64,9 +64,7 @@ export const fetchNode = async (nodeId: string): Promise => { return result ?? null; }; -export const fetchNodeAncestors = async ( - nodeId: string -): Promise => { +export const fetchNodeTree = async (nodeId: string): Promise => { const result = await database .selectFrom('nodes') .selectAll() @@ -317,7 +315,7 @@ export const createNodeFromMutation = async ( parentId = attributes.parentId; } - const ancestors = parentId ? await fetchNodeAncestors(parentId) : []; + const tree = parentId ? await fetchNodeTree(parentId) : []; const canCreateNodeContext: CanCreateNodeContext = { user: { id: user.id, @@ -325,7 +323,7 @@ export const createNodeFromMutation = async ( workspaceId: user.workspace_id, accountId: user.account_id, }, - ancestors: ancestors.map(mapNode), + tree: tree.map(mapNode), attributes, }; @@ -333,7 +331,7 @@ export const createNodeFromMutation = async ( return null; } - const rootId = ancestors[0]?.id ?? mutation.id; + const rootId = tree[0]?.id ?? mutation.id; const createNode: CreateNode = { id: mutation.id, root_id: rootId, @@ -430,12 +428,12 @@ const tryUpdateNodeFromMutation = async ( user: SelectUser, mutation: UpdateNodeMutationData ): Promise> => { - const ancestors = await fetchNodeAncestors(mutation.id); - if (ancestors.length === 0) { + const tree = await fetchNodeTree(mutation.id); + if (tree.length === 0) { return { type: 'error', output: null }; } - const node = ancestors[ancestors.length - 1]; + const node = tree[tree.length - 1]; if (!node || node.id !== mutation.id) { return { type: 'error', output: null }; } @@ -454,7 +452,7 @@ const tryUpdateNodeFromMutation = async ( workspaceId: user.workspace_id, accountId: user.account_id, }, - ancestors: ancestors.map(mapNode), + tree: tree.map(mapNode), node: mapNode(node), attributes, }; @@ -544,12 +542,12 @@ export const deleteNode = async ( user: SelectUser, input: DeleteNodeInput ): Promise => { - const ancestors = await fetchNodeAncestors(input.id); - if (ancestors.length === 0) { + const tree = await fetchNodeTree(input.id); + if (tree.length === 0) { return null; } - const node = ancestors[ancestors.length - 1]; + const node = tree[tree.length - 1]; if (!node || node.id !== input.id) { return null; } @@ -562,7 +560,7 @@ export const deleteNode = async ( workspaceId: user.workspace_id, accountId: user.account_id, }, - ancestors: ancestors.map(mapNode), + tree: tree.map(mapNode), node: mapNode(node), }; diff --git a/packages/core/src/registry/nodes/channel.ts b/packages/core/src/registry/nodes/channel.ts index 5ebf153f..6ce1a406 100644 --- a/packages/core/src/registry/nodes/channel.ts +++ b/packages/core/src/registry/nodes/channel.ts @@ -20,11 +20,11 @@ export const channelModel: NodeModel = { type: 'channel', attributesSchema: channelAttributesSchema, canCreate: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -32,11 +32,11 @@ export const channelModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -47,11 +47,11 @@ export const channelModel: NodeModel = { return false; }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } diff --git a/packages/core/src/registry/nodes/core.ts b/packages/core/src/registry/nodes/core.ts index efc030ba..90d8dfb1 100644 --- a/packages/core/src/registry/nodes/core.ts +++ b/packages/core/src/registry/nodes/core.ts @@ -22,26 +22,26 @@ export interface NodeMutationUser { export type CanCreateNodeContext = { user: NodeMutationUser; - ancestors: Node[]; + tree: Node[]; attributes: NodeAttributes; }; export type CanUpdateAttributesContext = { user: NodeMutationUser; - ancestors: Node[]; + tree: Node[]; node: Node; attributes: NodeAttributes; }; export type CanUpdateDocumentContext = { user: NodeMutationUser; - ancestors: Node[]; + tree: Node[]; node: Node; }; export type CanDeleteNodeContext = { user: NodeMutationUser; - ancestors: Node[]; + tree: Node[]; node: Node; }; diff --git a/packages/core/src/registry/nodes/database-view.ts b/packages/core/src/registry/nodes/database-view.ts index 5ce3ae89..612283a1 100644 --- a/packages/core/src/registry/nodes/database-view.ts +++ b/packages/core/src/registry/nodes/database-view.ts @@ -93,11 +93,11 @@ export const databaseViewModel: NodeModel = { type: 'database_view', attributesSchema: databaseViewAttributesSchema, canCreate: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -105,11 +105,11 @@ export const databaseViewModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -120,11 +120,11 @@ export const databaseViewModel: NodeModel = { return false; }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } diff --git a/packages/core/src/registry/nodes/database.ts b/packages/core/src/registry/nodes/database.ts index 12367f51..3a2be2dd 100644 --- a/packages/core/src/registry/nodes/database.ts +++ b/packages/core/src/registry/nodes/database.ts @@ -20,11 +20,11 @@ export const databaseModel: NodeModel = { type: 'database', attributesSchema: databaseAttributesSchema, canCreate: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -32,11 +32,11 @@ export const databaseModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -47,11 +47,11 @@ export const databaseModel: NodeModel = { return false; }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } diff --git a/packages/core/src/registry/nodes/file.ts b/packages/core/src/registry/nodes/file.ts index d5a9f6a3..5044e858 100644 --- a/packages/core/src/registry/nodes/file.ts +++ b/packages/core/src/registry/nodes/file.ts @@ -18,16 +18,16 @@ export const fileModel: NodeModel = { type: 'file', attributesSchema: fileAttributesSchema, canCreate: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } - const parent = context.ancestors[context.ancestors.length - 1]!; + const parent = context.tree[context.tree.length - 1]!; if (parent.type === 'message') { return hasNodeRole(role, 'collaborator'); } @@ -35,16 +35,16 @@ export const fileModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } - const parent = context.ancestors[context.ancestors.length - 1]!; + const parent = context.tree[context.tree.length - 1]!; if (parent.type === 'message') { return parent.createdBy === context.user.id || hasNodeRole(role, 'admin'); } @@ -55,16 +55,16 @@ export const fileModel: NodeModel = { return false; }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } - const parent = context.ancestors[context.ancestors.length - 1]!; + const parent = context.tree[context.tree.length - 1]!; if (parent.type === 'message') { return parent.createdBy === context.user.id || hasNodeRole(role, 'admin'); } diff --git a/packages/core/src/registry/nodes/folder.ts b/packages/core/src/registry/nodes/folder.ts index b30794c4..629b0420 100644 --- a/packages/core/src/registry/nodes/folder.ts +++ b/packages/core/src/registry/nodes/folder.ts @@ -18,11 +18,11 @@ export const folderModel: NodeModel = { type: 'folder', attributesSchema: folderAttributesSchema, canCreate: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -30,11 +30,11 @@ export const folderModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -45,11 +45,11 @@ export const folderModel: NodeModel = { return false; }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } diff --git a/packages/core/src/registry/nodes/message.ts b/packages/core/src/registry/nodes/message.ts index f3d51d23..484a91f8 100644 --- a/packages/core/src/registry/nodes/message.ts +++ b/packages/core/src/registry/nodes/message.ts @@ -22,11 +22,11 @@ export const messageModel: NodeModel = { type: 'message', attributesSchema: messageAttributesSchema, canCreate: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -34,11 +34,11 @@ export const messageModel: NodeModel = { return hasNodeRole(role, 'collaborator'); }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -49,11 +49,11 @@ export const messageModel: NodeModel = { return false; }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } diff --git a/packages/core/src/registry/nodes/page.ts b/packages/core/src/registry/nodes/page.ts index 4d3a2389..8284242c 100644 --- a/packages/core/src/registry/nodes/page.ts +++ b/packages/core/src/registry/nodes/page.ts @@ -20,11 +20,11 @@ export const pageModel: NodeModel = { attributesSchema: pageAttributesSchema, documentSchema: richTextContentSchema, canCreate: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -32,11 +32,11 @@ export const pageModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -44,11 +44,11 @@ export const pageModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canUpdateDocument: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -56,11 +56,11 @@ export const pageModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } diff --git a/packages/core/src/registry/nodes/record.ts b/packages/core/src/registry/nodes/record.ts index adabee60..f791c6ad 100644 --- a/packages/core/src/registry/nodes/record.ts +++ b/packages/core/src/registry/nodes/record.ts @@ -23,11 +23,11 @@ export const recordModel: NodeModel = { attributesSchema: recordAttributesSchema, documentSchema: richTextContentSchema, canCreate: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -35,11 +35,11 @@ export const recordModel: NodeModel = { return hasNodeRole(role, 'collaborator'); }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -51,11 +51,11 @@ export const recordModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canUpdateDocument: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -67,11 +67,11 @@ export const recordModel: NodeModel = { return hasNodeRole(role, 'editor'); }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } diff --git a/packages/core/src/registry/nodes/space.ts b/packages/core/src/registry/nodes/space.ts index 973a2684..75cc26e6 100644 --- a/packages/core/src/registry/nodes/space.ts +++ b/packages/core/src/registry/nodes/space.ts @@ -20,7 +20,7 @@ export const spaceModel: NodeModel = { type: 'space', attributesSchema: spaceAttributesSchema, canCreate: (context) => { - if (context.ancestors.length > 0) { + if (context.tree.length > 0) { return false; } @@ -44,11 +44,11 @@ export const spaceModel: NodeModel = { return true; }, canUpdateAttributes: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; } @@ -59,11 +59,11 @@ export const spaceModel: NodeModel = { return false; }, canDelete: (context) => { - if (context.ancestors.length === 0) { + if (context.tree.length === 0) { return false; } - const role = extractNodeRole(context.ancestors, context.user.id); + const role = extractNodeRole(context.tree, context.user.id); if (!role) { return false; }