Skip to main content
Glama
projects.ts11.1 kB
/** * Dooray Projects API * Handles project and task management operations */ import { getClient } from './client.js'; import { Project, ProjectListParams, Task, TaskListParams, CreateTaskParams, UpdateTaskParams, CreateTaskCommentParams, CreateTaskCommentResponse, UpdateTaskCommentParams, TaskCommentListParams, TaskComment, Milestone, MilestoneListParams, Tag, TagListParams, Workflow, WorkflowListParams, ProjectTemplate, GetProjectTemplateListParams, GetProjectTemplateParams, ProjectMember, GetProjectMemberListParams, ProjectMemberGroup, GetProjectMemberGroupListParams, FileUploadResult, PaginatedResponse, } from '../types/dooray-api.js'; const PROJECTS_BASE = '/project/v1'; /** * Get list of projects accessible by the user */ export async function getProjects(params?: ProjectListParams): Promise<PaginatedResponse<Project>> { const client = getClient(); return client.getPaginated<Project>(`${PROJECTS_BASE}/projects`, { member: 'me', state: 'active', page: params?.page || 0, size: params?.size || 20, }); } /** * Get details of a specific project */ export async function getProjectDetails(projectId: string): Promise<Project> { const client = getClient(); return client.get(`${PROJECTS_BASE}/projects/${projectId}`); } /** * Get list of tasks based on filters */ export async function getTasks(params: TaskListParams): Promise<PaginatedResponse<Task>> { const client = getClient(); const queryParams: Record<string, unknown> = { page: params.page || 0, size: params.size || 20, }; // Add optional filters if (params.fromEmailAddress) queryParams.fromEmailAddress = params.fromEmailAddress; if (params.fromMemberIds) queryParams.fromMemberIds = params.fromMemberIds.join(','); if (params.toMemberIds) queryParams.toMemberIds = params.toMemberIds.join(','); if (params.ccMemberIds) queryParams.ccMemberIds = params.ccMemberIds.join(','); if (params.tagIds) queryParams.tagIds = params.tagIds.join(','); if (params.parentPostId) queryParams.parentPostId = params.parentPostId; if (params.postNumber !== undefined) queryParams.postNumber = params.postNumber; if (params.postWorkflowClasses) queryParams.postWorkflowClasses = params.postWorkflowClasses.join(','); if (params.postWorkflowIds) queryParams.postWorkflowIds = params.postWorkflowIds.join(','); if (params.milestoneIds) queryParams.milestoneIds = params.milestoneIds.join(','); if (params.subjects) queryParams.subjects = params.subjects; if (params.createdAt) queryParams.createdAt = params.createdAt; if (params.updatedAt) queryParams.updatedAt = params.updatedAt; if (params.dueAt) queryParams.dueAt = params.dueAt; if (params.order) queryParams.order = params.order; return client.getPaginated<Task>(`${PROJECTS_BASE}/projects/${params.projectId}/posts`, queryParams); } /** * Get details of a specific task * Can be called with or without projectId */ export async function getTaskDetails(taskId: string, projectId?: string): Promise<Task> { const client = getClient(); // If projectId is provided, use the project-scoped endpoint // Otherwise, use the global endpoint that works with just taskId if (projectId) { return client.get(`${PROJECTS_BASE}/projects/${projectId}/posts/${taskId}`); } else { return client.get(`${PROJECTS_BASE}/posts/${taskId}`); } } /** * Create a new task */ export async function createTask(params: CreateTaskParams): Promise<Task> { const client = getClient(); const requestBody: Record<string, unknown> = { subject: params.subject, }; if (params.parentPostId) requestBody.parentPostId = params.parentPostId; if (params.body) requestBody.body = params.body; if (params.users) requestBody.users = params.users; if (params.dueDate) { requestBody.dueDate = params.dueDate; // Set dueDateFlag to true when dueDate is provided (API recommendation) requestBody.dueDateFlag = params.dueDateFlag !== undefined ? params.dueDateFlag : true; } if (params.milestoneId) requestBody.milestoneId = params.milestoneId; if (params.tagIds) requestBody.tagIds = params.tagIds; if (params.priority) requestBody.priority = params.priority; return client.post(`${PROJECTS_BASE}/projects/${params.projectId}/posts`, requestBody); } /** * Update an existing task */ export async function updateTask( projectId: string, taskNumber: number, params: UpdateTaskParams ): Promise<Task> { const client = getClient(); const requestBody: Record<string, unknown> = {}; if (params.subject !== undefined) requestBody.subject = params.subject; if (params.body !== undefined) requestBody.body = params.body; if (params.users !== undefined) requestBody.users = params.users; if (params.dueDate !== undefined) requestBody.dueDate = params.dueDate; if (params.dueDateFlag !== undefined) requestBody.dueDateFlag = params.dueDateFlag; if (params.milestoneId !== undefined) requestBody.milestoneId = params.milestoneId; if (params.tagIds !== undefined) requestBody.tagIds = params.tagIds; if (params.priority !== undefined) requestBody.priority = params.priority; if (params.workflowId !== undefined) requestBody.workflowId = params.workflowId; return client.put(`${PROJECTS_BASE}/projects/${projectId}/posts/${taskNumber}`, requestBody); } /** * Create a comment on a task (댓글 생성) */ export async function createTaskComment( params: CreateTaskCommentParams ): Promise<CreateTaskCommentResponse> { const client = getClient(); const requestBody: Record<string, unknown> = { body: { content: params.body.content, mimeType: params.body.mimeType, }, }; if (params.attachFileIds && params.attachFileIds.length > 0) { requestBody.attachFileIds = params.attachFileIds; } return client.post<CreateTaskCommentResponse>( `${PROJECTS_BASE}/projects/${params.projectId}/posts/${params.taskId}/logs`, requestBody ); } /** * Get list of comments on a task (댓글 목록 조회) */ export async function getTaskComments( params: TaskCommentListParams ): Promise<PaginatedResponse<TaskComment>> { const client = getClient(); const queryParams: Record<string, unknown> = { page: params.page || 0, size: params.size || 20, }; if (params.order) { queryParams.order = params.order; } return client.getPaginated( `${PROJECTS_BASE}/projects/${params.projectId}/posts/${params.taskId}/logs`, queryParams ); } /** * Update a comment on a task (댓글 수정) */ export async function updateTaskComment( params: UpdateTaskCommentParams ): Promise<void> { const client = getClient(); const requestBody: Record<string, unknown> = {}; if (params.body) { requestBody.body = { content: params.body.content, mimeType: params.body.mimeType, }; } if (params.attachFileIds && params.attachFileIds.length > 0) { requestBody.attachFileIds = params.attachFileIds; } await client.put( `${PROJECTS_BASE}/projects/${params.projectId}/posts/${params.taskId}/logs/${params.commentId}`, requestBody ); } /** * Get milestones for a project */ export async function getMilestones(params: MilestoneListParams): Promise<Milestone[]> { const client = getClient(); const queryParams: Record<string, unknown> = {}; if (params.status) queryParams.status = params.status; return client.get(`${PROJECTS_BASE}/projects/${params.projectId}/milestones`, queryParams); } /** * Get tags for a project */ export async function getTags(params: TagListParams): Promise<PaginatedResponse<Tag>> { const client = getClient(); const queryParams: Record<string, unknown> = { page: params.page || 0, size: params.size || 100, // Default to max size to get all tags }; return client.getPaginated<Tag>(`${PROJECTS_BASE}/projects/${params.projectId}/tags`, queryParams); } /** * Get details of a specific tag */ export async function getTagDetails(projectId: string, tagId: string): Promise<Tag> { const client = getClient(); return client.get(`${PROJECTS_BASE}/projects/${projectId}/tags/${tagId}`); } /** * Get workflows (업무 상태) for a project */ export async function getProjectWorkflows( params: WorkflowListParams ): Promise<Workflow[]> { const client = getClient(); return client.get<Workflow[]>( `${PROJECTS_BASE}/projects/${params.projectId}/workflows` ); } /** * Get project templates */ export async function getProjectTemplates( params: GetProjectTemplateListParams ): Promise<PaginatedResponse<ProjectTemplate>> { const client = getClient(); const queryParams: Record<string, unknown> = { page: params.page ?? 0, size: params.size ?? 20, }; return client.getPaginated<ProjectTemplate>( `${PROJECTS_BASE}/projects/${params.projectId}/templates`, queryParams ); } /** * Get details of a specific project template */ export async function getProjectTemplate( params: GetProjectTemplateParams ): Promise<ProjectTemplate> { const client = getClient(); // No query params needed - interpolation defaults to false return client.get( `${PROJECTS_BASE}/projects/${params.projectId}/templates/${params.templateId}` ); } /** * Get project members */ export async function getProjectMembers( params: GetProjectMemberListParams ): Promise<PaginatedResponse<ProjectMember>> { const client = getClient(); const queryParams: Record<string, unknown> = { page: params.page ?? 0, size: params.size ?? 20, }; if (params.roles && params.roles.length > 0) { queryParams.roles = params.roles.join(','); } return client.getPaginated<ProjectMember>( `${PROJECTS_BASE}/projects/${params.projectId}/members`, queryParams ); } /** * Get project member groups */ export async function getProjectMemberGroups( params: GetProjectMemberGroupListParams ): Promise<PaginatedResponse<ProjectMemberGroup>> { const client = getClient(); const queryParams: Record<string, unknown> = { page: params.page ?? 0, size: params.size ?? 20, }; const result = await client.getPaginated<ProjectMemberGroup>( `${PROJECTS_BASE}/projects/${params.projectId}/member-groups`, queryParams ); // API returns double-nested array: result.data[0] contains the actual array // Flatten it to get the correct structure if (Array.isArray(result.data) && result.data.length > 0 && Array.isArray(result.data[0])) { result.data = result.data[0] as ProjectMemberGroup[]; } return result; } /** * Upload a file to a task */ export async function uploadFileToTask( projectId: string, taskNumber: number, file: { name: string; data: Buffer | Blob; mimeType?: string } ): Promise<FileUploadResult> { const client = getClient(); const formData = new FormData(); const blob = file.data instanceof Buffer ? new Blob([file.data], { type: file.mimeType || 'application/octet-stream' }) : file.data; formData.append('file', blob, file.name); return client.uploadFile( `${PROJECTS_BASE}/projects/${projectId}/posts/${taskNumber}/files`, formData ); }

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/jhl8041/dooray-mcp'

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