Skip to main content
Glama
document-service.ts10.3 kB
/** * Document Service * * Implements FeiShu Document API operations. */ import { DocumentClient } from '@/client/documents/document-client.js'; import type { CreateBlockRequest } from '@/client/documents/types/index.js'; import type { ApiClientConfig } from '@/client/types.js'; import { FeiShuApiError } from '../error.js'; import type { CreatedBlocksBO, CreatedDocumentBO, DocumentContentBO, DocumentInfoBO, } from './types/index.js'; /** * Document service for FeiShu */ export class DocumentService { private client: DocumentClient; /** * Create document service * * @param config - API client configuration */ constructor(config: ApiClientConfig) { this.client = new DocumentClient(config); } /** * Get document raw content * * @param documentId - Document ID * @returns Document content as string * @throws FeiShuApiError if API request fails */ async getDocumentContent(documentId: string): Promise<string> { try { const response = await this.client.getDocumentContent(documentId); if (response.code !== 0) { throw new FeiShuApiError( `Failed to get document content: ${response.msg}`, response.code, ); } if (!response.data?.content) { throw new FeiShuApiError('Document returned empty content'); } return response.data.content; } catch (error) { if (error instanceof FeiShuApiError) { throw error; } throw new FeiShuApiError( `Error accessing document: ${error instanceof Error ? error.message : String(error)}`, ); } } /** * Get document metadata * * @param documentId - Document ID * @returns Document metadata * @throws FeiShuApiError if API request fails */ async getDocumentInfo(documentId: string): Promise<DocumentInfoBO> { try { const response = await this.client.getDocumentInfo(documentId); if (response.code !== 0) { throw new FeiShuApiError( `Failed to get document info: ${response.msg}`, response.code, ); } if (!response.data?.document) { throw new FeiShuApiError('Document info not found'); } const doc = response.data.document; return { id: doc.document_id || '', revisionId: doc.revision_id || 0, title: doc.title || '', }; } catch (error) { if (error instanceof FeiShuApiError) { throw error; } throw new FeiShuApiError( `Error accessing document info: ${error instanceof Error ? error.message : String(error)}`, ); } } /** * Create a new document * * @param title - Document title * @param folderToken - Folder token to create document in (optional) * @returns Created document info * @throws FeiShuApiError if API request fails */ async createDocument( title?: string, folderToken?: string, ): Promise<CreatedDocumentBO> { try { const response = await this.client.createDocument({ title, folderToken }); if (response.code !== 0) { throw new FeiShuApiError( `Failed to create document: ${response.msg}`, response.code, ); } if (!response.data?.document) { throw new FeiShuApiError('Failed to create document: no document returned'); } const doc = response.data.document; return { documentId: doc.document_id, revisionId: doc.revision_id, title: doc.title, }; } catch (error) { if (error instanceof FeiShuApiError) { throw error; } throw new FeiShuApiError( `Error creating document: ${error instanceof Error ? error.message : String(error)}`, ); } } /** * Create blocks in a document * * @param documentId - Document ID * @param blockId - Parent block ID (use document_id for root) * @param children - Array of blocks to create * @param index - Index to insert blocks at (-1 for end) * @returns Created blocks info * @throws FeiShuApiError if API request fails */ async createBlocks( documentId: string, blockId: string, children: CreateBlockRequest[], index = -1, ): Promise<CreatedBlocksBO> { try { const response = await this.client.createBlocks( documentId, blockId, children, index, ); if (response.code !== 0) { throw new FeiShuApiError( `Failed to create blocks: ${response.msg}`, response.code, ); } const blocks = response.data?.children || []; return { blocks: blocks.map((block) => ({ blockId: block.block_id || '', blockType: block.block_type, })), }; } catch (error) { if (error instanceof FeiShuApiError) { throw error; } throw new FeiShuApiError( `Error creating blocks: ${error instanceof Error ? error.message : String(error)}`, ); } } /** * Helper: Create text block request */ static createTextBlock(content: string): CreateBlockRequest { return { block_type: 2, text: { elements: [{ text_run: { content } }], }, }; } /** * Helper: Create heading block request */ static createHeadingBlock( content: string, level: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 = 1, ): CreateBlockRequest { const blockTypeMap: Record<number, number> = { 1: 3, 2: 4, 3: 5, 4: 6, 5: 7, 6: 8, 7: 9, 8: 10, 9: 11, }; const blockType = blockTypeMap[level] as CreateBlockRequest['block_type']; const headingKey = `heading${level}` as keyof CreateBlockRequest; return { block_type: blockType, [headingKey]: { elements: [{ text_run: { content } }], }, } as CreateBlockRequest; } /** * Helper: Create bullet list block request */ static createBulletBlock(content: string): CreateBlockRequest { return { block_type: 12, bullet: { elements: [{ text_run: { content } }], }, }; } /** * Helper: Create ordered list block request */ static createOrderedBlock(content: string): CreateBlockRequest { return { block_type: 13, ordered: { elements: [{ text_run: { content } }], }, }; } /** * Helper: Create divider block request */ static createDividerBlock(): CreateBlockRequest { return { block_type: 22, divider: {}, }; } /** * Helper: Create code block request */ static createCodeBlock(content: string, language = 1): CreateBlockRequest { return { block_type: 14, code: { elements: [{ text_run: { content } }], style: { language }, }, }; } /** * Helper: Create quote block request */ static createQuoteBlock(content: string): CreateBlockRequest { return { block_type: 15, quote: { elements: [{ text_run: { content } }], }, }; } /** * Helper: Create todo block request */ static createTodoBlock(content: string, done = false): CreateBlockRequest { return { block_type: 17, todo: { elements: [{ text_run: { content } }], style: { done }, }, }; } /** * Set document public sharing settings * * @param documentToken - Token of the document * @param linkShareEntity - Link sharing permission level * @returns Response */ async setPublicSharing( documentToken: string, linkShareEntity: 'tenant_readable' | 'tenant_editable' | 'anyone_readable' | 'anyone_editable' | 'closed' = 'anyone_editable', ): Promise<{ success: boolean }> { try { const response = await this.client.setPublicSharing(documentToken, { link_share_entity: linkShareEntity, external_access_entity: 'open', security_entity: 'anyone_can_edit', share_entity: 'anyone', invite_external: true, }); if (response.code !== 0) { throw new FeiShuApiError( `Failed to set sharing: ${response.msg}`, response.code, ); } return { success: true }; } catch (error) { if (error instanceof FeiShuApiError) { throw error; } throw new FeiShuApiError( `Error setting sharing: ${error instanceof Error ? error.message : String(error)}`, ); } } /** * Create password for document sharing * * @param documentToken - Token of the document * @returns Response with password */ async createSharePassword(documentToken: string): Promise<{ password: string }> { try { const response = await this.client.createSharePassword(documentToken); if (response.code !== 0) { throw new FeiShuApiError( `Failed to create password: ${response.msg}`, response.code, ); } return { password: response.data?.password || '' }; } catch (error) { if (error instanceof FeiShuApiError) { throw error; } throw new FeiShuApiError( `Error creating password: ${error instanceof Error ? error.message : String(error)}`, ); } } /** * Add collaborator to document * * @param documentToken - Token of the document * @param memberId - User ID or open_id of the collaborator * @param memberType - Type of member * @param perm - Permission level * @returns Response */ async addCollaborator( documentToken: string, memberId: string, memberType: 'user' | 'chat' | 'department' | 'group' = 'user', perm: 'view' | 'edit' | 'full_access' = 'edit', ): Promise<{ success: boolean }> { try { const response = await this.client.addCollaborator( documentToken, memberId, memberType, perm, ); if (response.code !== 0) { throw new FeiShuApiError( `Failed to add collaborator: ${response.msg}`, response.code, ); } return { success: true }; } catch (error) { if (error instanceof FeiShuApiError) { throw error; } throw new FeiShuApiError( `Error adding collaborator: ${error instanceof Error ? error.message : String(error)}`, ); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/opsworld30/feishu-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server