n8n-nodes-toggl
Version:
n8n community node for Toggl Track time tracking integration with comprehensive operations including client management and webhook support
542 lines (541 loc) • 22.4 kB
JavaScript
"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: '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',
},
// 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',
},
// 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.',
},
// 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',
},
// 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 body = {
description,
created_with: 'n8n',
};
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', '/time_entries', body);
}
if (operation === 'stopTimeEntry') {
const timeEntryId = this.getNodeParameter('timeEntryId', i);
if (timeEntryId) {
responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'PATCH', `/time_entries/${timeEntryId}/stop`);
}
else {
// Get current time entry first
const currentEntry = await TogglApi_helper_1.togglApiRequest.call(this, 'GET', '/time_entries/current');
if (currentEntry && currentEntry.id) {
responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'PATCH', `/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', '/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 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', '/time_entries', {}, qs);
}
else {
const limit = this.getNodeParameter('limit', i);
qs.per_page = limit;
responseData = await TogglApi_helper_1.togglApiRequest.call(this, 'GET', '/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 === '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;