UNPKG

n8n-nodes-toggl

Version:

n8n community node for Toggl Track time tracking integration with comprehensive operations including client management and webhook support

749 lines (748 loc) 31.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Toggl = void 0; const TogglApi_helper_1 = require("./TogglApi.helper"); class Toggl { constructor() { this.description = { displayName: 'Toggl', name: 'toggl', icon: 'file:toggl.png', group: ['output'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Interact with Toggl Track API', defaults: { name: 'Toggl', }, inputs: ["main" /* NodeConnectionType.Main */], outputs: ["main" /* NodeConnectionType.Main */], credentials: [ { name: 'togglApi', required: true, }, ], properties: [ { displayName: 'Resource', name: 'resource', type: 'options', noDataExpression: true, options: [ { name: 'Time Entry', value: 'timeEntry', }, { name: 'Project', value: 'project', }, { name: 'Client', value: 'client', }, { name: 'Raw Request', value: 'rawRequest', }, ], default: 'timeEntry', }, // Time Entry Operations { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['timeEntry'], }, }, options: [ { name: 'Start Time Entry', value: 'startTimeEntry', description: 'Start a new time entry', action: 'Start a time entry', }, { name: 'Stop Time Entry', value: 'stopTimeEntry', description: 'Stop the current running time entry', action: 'Stop a time entry', }, { name: 'Get Current Time Entry', value: 'getCurrentTimeEntry', description: 'Get the currently running time entry', action: 'Get current time entry', }, { name: 'List Time Entries', value: 'listTimeEntries', description: 'Get a list of time entries', action: 'List time entries', }, ], default: 'startTimeEntry', }, // Project Operations { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['project'], }, }, options: [ { name: 'Create Project', value: 'createProject', description: 'Create a new project', action: 'Create a project', }, { name: 'List Projects', value: 'listProjects', description: 'Get a list of projects', action: 'List projects', }, ], default: 'createProject', }, // Client Operations { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['client'], }, }, options: [ { name: 'Create Client', value: 'createClient', description: 'Create a new client', action: 'Create a client', }, { name: 'List Clients', value: 'listClients', description: 'Get a list of clients', action: 'List clients', }, ], default: 'createClient', }, // Raw Request Operation { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, displayOptions: { show: { resource: ['rawRequest'], }, }, options: [ { name: 'Raw Request', value: 'rawRequest', description: 'Make a raw API request', action: 'Make a raw request', }, ], default: 'rawRequest', }, // Start Time Entry Fields { displayName: 'Description', name: 'description', type: 'string', displayOptions: { show: { resource: ['timeEntry'], operation: ['startTimeEntry'], }, }, default: '', description: 'Description of the time entry', }, { displayName: 'Project ID', name: 'projectId', type: 'number', displayOptions: { show: { resource: ['timeEntry'], operation: ['startTimeEntry'], }, }, default: 0, description: 'ID of the project to assign the time entry to', }, { displayName: 'Tags', name: 'tags', type: 'string', displayOptions: { show: { resource: ['timeEntry'], operation: ['startTimeEntry'], }, }, default: '', description: 'Comma-separated list of tags', }, { displayName: 'Workspace ID', name: 'workspaceId', type: 'number', displayOptions: { show: { resource: ['timeEntry'], operation: ['startTimeEntry'], }, }, default: 0, description: 'ID of the workspace to create the time entry in', required: true, }, // Stop Time Entry Fields { displayName: 'Time Entry ID', name: 'timeEntryId', type: 'number', displayOptions: { show: { resource: ['timeEntry'], operation: ['stopTimeEntry'], }, }, default: 0, description: 'ID of the time entry to stop. Leave empty to stop the current running entry.', }, { displayName: 'Workspace ID', name: 'workspaceId', type: 'number', displayOptions: { show: { resource: ['timeEntry'], operation: ['stopTimeEntry'], timeEntryId: [{ _cnd: { gte: 1 } }], }, }, default: 0, description: 'ID of the workspace containing the time entry', required: true, }, // List Time Entries Fields { displayName: 'Start Date', name: 'startDate', type: 'dateTime', displayOptions: { show: { resource: ['timeEntry'], operation: ['listTimeEntries'], }, }, default: '', description: 'Start date for the time entries to retrieve', }, { displayName: 'End Date', name: 'endDate', type: 'dateTime', displayOptions: { show: { resource: ['timeEntry'], operation: ['listTimeEntries'], }, }, default: '', description: 'End date for the time entries to retrieve', }, { displayName: 'Return All', name: 'returnAll', type: 'boolean', displayOptions: { show: { resource: ['timeEntry'], operation: ['listTimeEntries'], }, }, default: false, description: 'Whether to return all results or only up to a given limit', }, { displayName: 'Limit', name: 'limit', type: 'number', displayOptions: { show: { resource: ['timeEntry'], operation: ['listTimeEntries'], returnAll: [false], }, }, typeOptions: { minValue: 1, }, default: 50, description: 'Max number of results to return', }, // Create Project Fields { displayName: 'Project Name', name: 'projectName', type: 'string', displayOptions: { show: { resource: ['project'], operation: ['createProject'], }, }, default: '', description: 'Name of the project to create', required: true, }, { displayName: 'Workspace ID', name: 'workspaceId', type: 'number', displayOptions: { show: { resource: ['project'], operation: ['createProject'], }, }, default: 0, description: 'ID of the workspace to create the project in', required: true, }, { displayName: 'Client ID', name: 'clientId', type: 'number', displayOptions: { show: { resource: ['project'], operation: ['createProject'], }, }, default: 0, description: 'ID of the client to assign the project to', }, { displayName: 'Color', name: 'color', type: 'color', displayOptions: { show: { resource: ['project'], operation: ['createProject'], }, }, default: '#06aaf5', description: 'Hex color code for the project', }, // List Projects Fields { displayName: 'Workspace ID', name: 'workspaceId', type: 'number', displayOptions: { show: { resource: ['project'], operation: ['listProjects'], }, }, default: 0, description: 'ID of the workspace to list projects from', required: true, }, { displayName: 'Include Archived', name: 'includeArchived', type: 'boolean', displayOptions: { show: { resource: ['project'], operation: ['listProjects'], }, }, default: false, description: 'Whether to include archived projects', }, // Create Client Fields { displayName: 'Client Name', name: 'clientName', type: 'string', displayOptions: { show: { resource: ['client'], operation: ['createClient'], }, }, default: '', description: 'Name of the client to create', required: true, }, { displayName: 'Workspace ID', name: 'workspaceId', type: 'number', displayOptions: { show: { resource: ['client'], operation: ['createClient'], }, }, default: 0, description: 'ID of the workspace to create the client in', required: true, }, { displayName: 'External Reference', name: 'externalReference', type: 'string', displayOptions: { show: { resource: ['client'], operation: ['createClient'], }, }, default: '', description: 'External reference for the client', }, { displayName: 'Notes', name: 'notes', type: 'string', displayOptions: { show: { resource: ['client'], operation: ['createClient'], }, }, default: '', description: 'Notes for the client', }, // List Clients Fields { displayName: 'Workspace ID', name: 'workspaceId', type: 'number', displayOptions: { show: { resource: ['client'], operation: ['listClients'], }, }, default: 0, description: 'ID of the workspace to list clients from', required: true, }, { displayName: 'Status', name: 'status', type: 'options', displayOptions: { show: { resource: ['client'], operation: ['listClients'], }, }, options: [ { name: 'Active', value: 'active', }, { name: 'Archived', value: 'archived', }, { name: 'Both', value: 'both', }, ], default: 'active', description: 'Status of clients to retrieve', }, { displayName: 'Client Name Filter', name: 'nameFilter', type: 'string', displayOptions: { show: { resource: ['client'], operation: ['listClients'], }, }, default: '', description: 'Filter clients by name (case insensitive)', }, // Raw Request Fields { displayName: 'HTTP Method', name: 'method', type: 'options', displayOptions: { show: { resource: ['rawRequest'], operation: ['rawRequest'], }, }, options: [ { name: 'GET', value: 'GET', }, { name: 'POST', value: 'POST', }, { name: 'PUT', value: 'PUT', }, { name: 'DELETE', value: 'DELETE', }, ], default: 'GET', description: 'HTTP method to use', }, { displayName: 'Endpoint', name: 'endpoint', type: 'string', displayOptions: { show: { resource: ['rawRequest'], operation: ['rawRequest'], }, }, default: '', description: 'API endpoint (e.g., /me, /workspaces, /time_entries)', required: true, }, { displayName: 'Body', name: 'body', type: 'json', displayOptions: { show: { resource: ['rawRequest'], operation: ['rawRequest'], method: ['POST', 'PUT'], }, }, default: '{}', description: 'Request body as JSON', }, { displayName: 'Query Parameters', name: 'queryParameters', type: 'json', displayOptions: { show: { resource: ['rawRequest'], operation: ['rawRequest'], }, }, default: '{}', description: 'Query parameters as JSON object', }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; const resource = this.getNodeParameter('resource', 0); const operation = this.getNodeParameter('operation', 0); for (let i = 0; i < items.length; i++) { try { let responseData; if (resource === 'timeEntry') { if (operation === 'startTimeEntry') { const description = this.getNodeParameter('description', i); const projectId = this.getNodeParameter('projectId', i); const tags = this.getNodeParameter('tags', i); const workspaceId = this.getNodeParameter('workspaceId', i); const body = { description, created_with: 'n8n', workspace_id: workspaceId, duration: -1, start: new Date().toISOString(), }; if (projectId) { body.project_id = projectId; } if (tags) { body.tags = tags.split(',').map(tag => tag.trim()); } responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'POST', `/workspaces/${workspaceId}/time_entries`, body); } if (operation === 'stopTimeEntry') { const timeEntryId = this.getNodeParameter('timeEntryId', i); if (timeEntryId) { // Get workspace ID for the time entry const workspaceId = this.getNodeParameter('workspaceId', i); responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'PATCH', `/workspaces/${workspaceId}/time_entries/${timeEntryId}/stop`); } else { // Get current time entry first const currentEntry = await TogglApi_helper_1.togglApiRequest.call(this, 'GET', '/me/time_entries/current'); if (currentEntry && currentEntry.id && currentEntry.workspace_id) { responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'PATCH', `/workspaces/${currentEntry.workspace_id}/time_entries/${currentEntry.id}/stop`); } else { throw new Error('No running time entry found'); } } } if (operation === 'getCurrentTimeEntry') { responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'GET', '/me/time_entries/current'); } if (operation === 'listTimeEntries') { const startDate = this.getNodeParameter('startDate', i); const endDate = this.getNodeParameter('endDate', i); const returnAll = this.getNodeParameter('returnAll', i); const limit = this.getNodeParameter('limit', i); const qs = {}; if (startDate) { qs.start_date = new Date(startDate).toISOString(); } if (endDate) { qs.end_date = new Date(endDate).toISOString(); } if (returnAll) { responseData = await TogglApi_helper_1.togglApiRequestAllItems.call(this, 'data', 'GET', '/me/time_entries', {}, qs); } else { qs.per_page = limit; responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'GET', '/me/time_entries', {}, qs); } } } if (resource === 'project') { if (operation === 'createProject') { const projectName = this.getNodeParameter('projectName', i); const workspaceId = this.getNodeParameter('workspaceId', i); const clientId = this.getNodeParameter('clientId', i); const color = this.getNodeParameter('color', i); const body = { name: projectName, color, }; if (clientId) { body.client_id = clientId; } responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'POST', `/workspaces/${workspaceId}/projects`, body); } if (operation === 'listProjects') { const workspaceId = this.getNodeParameter('workspaceId', i); const includeArchived = this.getNodeParameter('includeArchived', i); const qs = {}; if (includeArchived) { qs.include_archived = true; } responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'GET', `/workspaces/${workspaceId}/projects`, {}, qs); } } if (resource === 'client') { if (operation === 'createClient') { const clientName = this.getNodeParameter('clientName', i); const workspaceId = this.getNodeParameter('workspaceId', i); const externalReference = this.getNodeParameter('externalReference', i); const notes = this.getNodeParameter('notes', i); const body = { name: clientName, }; if (externalReference) { body.external_reference = externalReference; } if (notes) { body.notes = notes; } responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'POST', `/workspaces/${workspaceId}/clients`, body); } if (operation === 'listClients') { const workspaceId = this.getNodeParameter('workspaceId', i); const status = this.getNodeParameter('status', i); const nameFilter = this.getNodeParameter('nameFilter', i); const qs = {}; if (status && status !== 'active') { qs.status = status; } if (nameFilter) { qs.name = nameFilter; } responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'GET', `/workspaces/${workspaceId}/clients`, {}, qs); } } if (resource === 'rawRequest') { if (operation === 'rawRequest') { const method = this.getNodeParameter('method', i); const endpoint = this.getNodeParameter('endpoint', i); const body = this.getNodeParameter('body', i, '{}'); const queryParameters = this.getNodeParameter('queryParameters', i, '{}'); let parsedBody = {}; let parsedQuery = {}; try { parsedBody = JSON.parse(body); } catch (error) { throw new Error('Invalid JSON in body parameter'); } try { parsedQuery = JSON.parse(queryParameters); } catch (error) { throw new Error('Invalid JSON in query parameters'); } responseData = await TogglApi_helper_1.togglApiRequest.call(this, method, endpoint, parsedBody, parsedQuery); } } if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData.map(item => ({ json: item }))); } else { returnData.push({ json: responseData }); } } catch (error) { if (this.continueOnFail()) { returnData.push({ json: { error: error.message, }, pairedItem: { item: i }, }); continue; } throw error; } } return [returnData]; } } exports.Toggl = Toggl;