Skip to main content
Glama
tasks.service.ts15 kB
import type { ApiError, CreateTaskParams, ListTaskParams, PaginatedResult, Task, TaskDeleted, UpdateTaskParams, } from "@lokalise/node-api"; import type { ApiRequestOptions } from "../../shared/types/common.types.js"; import { createApiError, createUnexpectedError, } from "../../shared/utils/error.util.js"; import { Logger } from "../../shared/utils/logger.util.js"; import { getLokaliseApi } from "../../shared/utils/lokalise-api.util.js"; // Create a contextualized logger for this file const serviceLogger = Logger.forContext( "services/vendor.lokalise.com.tasks.service.ts", ); // Log service initialization serviceLogger.debug("Lokalise Tasks API service initialized"); /** * @namespace VendorLokaliseTasksService * @description Service layer for interacting with Lokalise Tasks API endpoints. * Uses the official Lokalise SDK for reliable API communication. */ /** * Parameters for listing tasks in a project */ export interface TaskListParams extends ApiRequestOptions { project_id: string; filter_title?: string; filter_statuses?: string; } /** * Parameters for creating a task in a project */ export interface CreateTaskServiceParams { project_id: string; title: string; description?: string; keys?: number[]; languages?: Array<{ language_iso: string; users?: number[]; groups?: number[]; }>; assignees?: number[]; // Top-level assignees to be applied to all languages due_date?: string; source_language_iso?: string; auto_close_languages?: boolean; auto_close_task?: boolean; auto_close_items?: boolean; task_type?: CreateTaskParams["task_type"]; parent_task_id?: number; closing_tags?: string[]; do_lock_translations?: boolean; custom_translation_status_ids?: number[]; } /** * Parameters for getting a single task */ export interface GetTaskParams { project_id: string; task_id: number; } /** * Parameters for updating a task */ export interface UpdateTaskServiceParams { project_id: string; task_id: number; title?: string; description?: string; due_date?: string; languages?: Array<{ language_iso: string; users?: number[]; groups?: number[]; close_language?: boolean; }>; auto_close_languages?: boolean; auto_close_task?: boolean; auto_close_items?: boolean; closing_tags?: string[]; do_lock_translations?: boolean; close_task?: boolean; } /** * Parameters for deleting a task */ export interface DeleteTaskParams { project_id: string; task_id: number; } /** * @function getTasks * @description Fetches a list of tasks from a Lokalise project with optional filtering and pagination. * @memberof VendorLokaliseTasksService * @param {TaskListParams} options - Parameters including project ID, filters, and pagination options * @returns {Promise<PaginatedResult<Task>>} A promise that resolves to the API response containing the tasks list * @throws {McpError} Throws an McpError with details if the API call fails */ export async function getTasks( options: TaskListParams, ): Promise<PaginatedResult<Task>> { const methodLogger = serviceLogger.forMethod("getTasks"); try { methodLogger.debug("Calling Lokalise Tasks API - list", { projectId: options.project_id, limit: options.limit, page: options.page, filterTitle: options.filter_title, filterStatuses: options.filter_statuses, }); const api = getLokaliseApi(); // Prepare API parameters with proper typing const apiParams: ListTaskParams = { project_id: options.project_id, limit: options.limit || 100, page: options.page || 1, }; if (options.filter_title) { apiParams.filter_title = options.filter_title; } if (options.filter_statuses) { apiParams.filter_statuses = options.filter_statuses as ListTaskParams["filter_statuses"]; } const result = await api.tasks().list(apiParams); methodLogger.debug("Lokalise Tasks API call successful", { projectId: options.project_id, tasksCount: result.items?.length || 0, hasNextPage: result.hasNextPage(), }); return result; } catch (error: unknown) { methodLogger.error("Lokalise Tasks API call failed - list", { error: (error as Error).message, projectId: options.project_id, }); if ((error as ApiError).code === 404) { throw createApiError(`Project not found: ${options.project_id}`, 404); } if ((error as ApiError).code === 403) { throw createApiError("Access denied to this project", 403); } if ((error as ApiError).code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError( `Failed to fetch tasks from project ${options.project_id}: ${(error as Error).message}`, ); } } /** * @function createTask * @description Creates a task in a Lokalise project * @memberof VendorLokaliseTasksService * @param {CreateTaskServiceParams} options - Parameters including project ID and task data * @returns {Promise<Task>} A promise that resolves to the API response containing created task * @throws {McpError} Throws an McpError with details if the API call fails */ export async function createTask( options: CreateTaskServiceParams, ): Promise<Task> { const methodLogger = serviceLogger.forMethod("createTask"); try { methodLogger.debug("Calling Lokalise Tasks API - create", { projectId: options.project_id, title: options.title, languagesCount: options.languages?.length || 0, }); const api = getLokaliseApi(); // Prepare API parameters matching SDK interface const apiParams: CreateTaskParams = { title: options.title, }; // Only include optional parameters if they have values if (options.description !== undefined) { apiParams.description = options.description; } if (options.keys !== undefined && options.keys.length > 0) { apiParams.keys = options.keys; } if (options.languages !== undefined) { apiParams.languages = options.languages; } if (options.due_date !== undefined) { apiParams.due_date = options.due_date; } if (options.source_language_iso !== undefined) { apiParams.source_language_iso = options.source_language_iso; } if (options.auto_close_languages !== undefined) { apiParams.auto_close_languages = options.auto_close_languages; } if (options.auto_close_task !== undefined) { apiParams.auto_close_task = options.auto_close_task; } if (options.auto_close_items !== undefined) { apiParams.auto_close_items = options.auto_close_items; } if (options.task_type !== undefined) { apiParams.task_type = options.task_type; } if (options.parent_task_id !== undefined) { apiParams.parent_task_id = options.parent_task_id; } if (options.closing_tags !== undefined && options.closing_tags.length > 0) { apiParams.closing_tags = options.closing_tags; } if (options.do_lock_translations !== undefined) { apiParams.do_lock_translations = options.do_lock_translations; } if ( options.custom_translation_status_ids !== undefined && options.custom_translation_status_ids.length > 0 ) { apiParams.custom_translation_status_ids = options.custom_translation_status_ids; } // Ensure languages have users or groups if they're provided empty if (apiParams.languages && apiParams.languages.length > 0) { apiParams.languages = apiParams.languages.map((lang) => { // If neither users nor groups are specified, we need to provide at least one if (!lang.users?.length && !lang.groups?.length) { // If assignees were provided at the top level, use them for each language if (options.assignees && options.assignees.length > 0) { return { ...lang, users: options.assignees }; } // Otherwise, throw a helpful error before calling the API throw createApiError( "Tasks with languages require either 'users' or 'groups' to be specified for each language, or use the 'assignees' parameter to assign users to all languages", 400, ); } return lang; }); } const result = await api .tasks() .create(apiParams, { project_id: options.project_id }); methodLogger.debug("Lokalise Tasks API call successful - create", { projectId: options.project_id, taskId: result.task_id, title: result.title, }); return result; } catch (error: unknown) { methodLogger.error("Lokalise Tasks API call failed - create", { error: (error as Error).message, projectId: options.project_id, }); if ((error as ApiError).code === 404) { throw createApiError(`Project not found: ${options.project_id}`, 404); } if ((error as ApiError).code === 403) { throw createApiError("Access denied to this project", 403); } if ((error as ApiError).code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError( `Failed to create task in project ${options.project_id}: ${(error as Error).message}`, ); } } /** * @function getTask * @description Fetches a single task from a Lokalise project * @memberof VendorLokaliseTasksService * @param {GetTaskParams} options - Parameters including project ID and task ID * @returns {Promise<Task>} A promise that resolves to the task data * @throws {McpError} Throws an McpError with details if the API call fails */ export async function getTask(options: GetTaskParams): Promise<Task> { const methodLogger = serviceLogger.forMethod("getTask"); try { methodLogger.debug("Calling Lokalise Tasks API - get", { projectId: options.project_id, taskId: options.task_id, }); const api = getLokaliseApi(); const result = await api .tasks() .get(options.task_id, { project_id: options.project_id }); methodLogger.debug("Lokalise Tasks API call successful - get", { projectId: options.project_id, taskId: options.task_id, title: result.title, }); return result; } catch (error: unknown) { methodLogger.error("Lokalise Tasks API call failed - get", { error: (error as Error).message, projectId: options.project_id, taskId: options.task_id, }); if ((error as ApiError).code === 404) { throw createApiError( `Task not found: ${options.task_id} in project ${options.project_id}`, 404, ); } if ((error as ApiError).code === 403) { throw createApiError("Access denied to this project", 403); } if ((error as ApiError).code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError( `Failed to fetch task ${options.task_id} from project ${options.project_id}: ${(error as Error).message}`, ); } } /** * @function updateTask * @description Updates a single task in a Lokalise project * @memberof VendorLokaliseTasksService * @param {UpdateTaskServiceParams} options - Parameters including project ID, task ID and update data * @returns {Promise<Task>} A promise that resolves to the updated task data * @throws {McpError} Throws an McpError with details if the API call fails */ export async function updateTask( options: UpdateTaskServiceParams, ): Promise<Task> { const methodLogger = serviceLogger.forMethod("updateTask"); try { methodLogger.debug("Calling Lokalise Tasks API - update", { projectId: options.project_id, taskId: options.task_id, }); const api = getLokaliseApi(); // Prepare update data matching SDK interface const updateData: UpdateTaskParams = {}; if (options.title !== undefined) { updateData.title = options.title; } if (options.description !== undefined) { updateData.description = options.description; } if (options.due_date !== undefined) { updateData.due_date = options.due_date; } if (options.languages !== undefined) { updateData.languages = options.languages; } if (options.auto_close_languages !== undefined) { updateData.auto_close_languages = options.auto_close_languages; } if (options.auto_close_task !== undefined) { updateData.auto_close_task = options.auto_close_task; } if (options.auto_close_items !== undefined) { updateData.auto_close_items = options.auto_close_items; } if (options.closing_tags !== undefined && options.closing_tags.length > 0) { updateData.closing_tags = options.closing_tags; } if (options.do_lock_translations !== undefined) { updateData.do_lock_translations = options.do_lock_translations; } if (options.close_task !== undefined) { updateData.close_task = options.close_task; } const result = await api .tasks() .update(options.task_id, updateData, { project_id: options.project_id }); methodLogger.debug("Lokalise Tasks API call successful - update", { projectId: options.project_id, taskId: options.task_id, title: result.title, }); return result; } catch (error: unknown) { methodLogger.error("Lokalise Tasks API call failed - update", { error: (error as Error).message, projectId: options.project_id, taskId: options.task_id, }); if ((error as ApiError).code === 404) { throw createApiError( `Task not found: ${options.task_id} in project ${options.project_id}`, 404, ); } if ((error as ApiError).code === 403) { throw createApiError("Access denied to this project", 403); } if ((error as ApiError).code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError( `Failed to update task ${options.task_id} in project ${options.project_id}: ${(error as Error).message}`, ); } } /** * @function deleteTask * @description Deletes a single task from a Lokalise project * @memberof VendorLokaliseTasksService * @param {DeleteTaskParams} options - Parameters including project ID and task ID * @returns {Promise<TaskDeleted>} A promise that resolves to the deletion confirmation * @throws {McpError} Throws an McpError with details if the API call fails */ export async function deleteTask( options: DeleteTaskParams, ): Promise<TaskDeleted> { const methodLogger = serviceLogger.forMethod("deleteTask"); try { methodLogger.debug("Calling Lokalise Tasks API - delete", { projectId: options.project_id, taskId: options.task_id, }); const api = getLokaliseApi(); const result = await api .tasks() .delete(options.task_id, { project_id: options.project_id }); methodLogger.debug("Lokalise Tasks API call successful - delete", { projectId: options.project_id, taskId: options.task_id, }); return result; } catch (error: unknown) { methodLogger.error("Lokalise Tasks API call failed - delete", { error: (error as Error).message, projectId: options.project_id, taskId: options.task_id, }); if ((error as ApiError).code === 404) { throw createApiError( `Task not found: ${options.task_id} in project ${options.project_id}`, 404, ); } if ((error as ApiError).code === 403) { throw createApiError("Access denied to this project", 403); } if ((error as ApiError).code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError( `Failed to delete task ${options.task_id} from project ${options.project_id}: ${(error as Error).message}`, ); } }

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/AbdallahAHO/lokalise-mcp'

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