Fix some issues on Harvest-Node

This commit is contained in:
Jan Oberhauser 2020-01-29 15:51:14 -08:00
parent d752b555bb
commit e05b46c7e6
10 changed files with 308 additions and 301 deletions

View file

@ -98,7 +98,7 @@ export const clientFields = [
displayName: 'Is Active',
name: 'is_active',
type: 'boolean',
default: '',
default: true,
description: 'Pass true to only return active clients and false to return inactive clients.',
},
{

View file

@ -104,7 +104,7 @@ export const estimateFields = [
{
displayName: 'Updated Since',
name: 'updated_since',
type: 'string',
type: 'dateTime',
default: '',
description: 'Only return time entries that have been updated since the given date and time.',
},
@ -132,8 +132,11 @@ export const estimateFields = [
{
displayName: 'Page',
name: 'page',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 1,
},
default: 1,
description: 'The page number to use in pagination. For instance, if you make a list request and receive 100 records, your subsequent call can include page=2 to retrieve the next page of the list. (Default: 1)',
}
]

View file

@ -125,7 +125,7 @@ export const expenseFields = [
{
displayName: 'Updated Since',
name: 'updated_since',
type: 'string',
type: 'dateTime',
default: '',
description: 'Only return time entries that have been updated since the given date and time.',
},
@ -146,8 +146,11 @@ export const expenseFields = [
{
displayName: 'Page',
name: 'page',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 1,
},
default: 1,
description: 'The page number to use in pagination. For instance, if you make a list request and receive 100 records, your subsequent call can include page=2 to retrieve the next page of the list. (Default: 1)',
}
]

View file

@ -60,13 +60,13 @@ export async function harvestApiRequest(
throw new Error('The Harvest credentials are not valid!');
}
if (error.error && error.error.error_summary) {
if (error.error && error.error.message) {
// Try to return the error prettier
throw new Error(`Harvest error response [${error.statusCode}]: ${error.error.error_summary}`);
throw new Error(`Harvest error response [${error.statusCode}]: ${error.error.message}`);
}
// If that data does not exist for some reason return the actual error
throw error;
throw new Error(`Harvest error response: ${error.message}`);
}
}

View file

@ -25,8 +25,8 @@ import { estimateOperations, estimateFields } from './EstimateDescription';
*/
async function getAllResource(this: IExecuteFunctions, resource: string, i: number) {
const endpoint = resource;
let qs: IDataObject = {};
const requestMethod: string = "GET";
const qs: IDataObject = {};
const requestMethod = 'GET';
qs.per_page = 100;
@ -34,19 +34,15 @@ async function getAllResource(this: IExecuteFunctions, resource: string, i: numb
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
Object.assign(qs, additionalFields);
try {
let responseData: IDataObject = {};
if(returnAll) {
responseData[resource] = await harvestApiRequestAllItems.call(this, requestMethod, qs, endpoint, resource);
} else {
const limit = this.getNodeParameter('limit', i) as string;
qs.per_page = limit;
responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
}
return responseData[resource] as IDataObject[];
} catch (error) {
throw error;
let responseData: IDataObject = {};
if(returnAll) {
responseData[resource] = await harvestApiRequestAllItems.call(this, requestMethod, qs, endpoint, resource);
} else {
const limit = this.getNodeParameter('limit', i) as string;
qs.per_page = limit;
responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
}
return responseData[resource] as IDataObject[];
}
export class Harvest implements INodeType {
@ -76,48 +72,49 @@ export class Harvest implements INodeType {
name: 'resource',
type: 'options',
options: [
{
name: 'Client',
value: 'client',
},
{
name: 'Company',
value: 'company',
},
{
name: 'Contact',
value: 'contact',
},
{
name: 'Estimates',
value: 'estimate',
},
{
name: 'Expense',
value: 'expense',
},
{
name: 'Invoice',
value: 'invoice',
},
{
name: 'Project',
value: 'project',
},
{
name: 'Task',
value: 'task',
},
{
name: 'Time Entries',
value: 'timeEntry',
},
{
name: "Client",
value: "client"
name: 'User',
value: 'user',
},
{
name: "Project",
value: "project"
},
{
name: "Contact",
value: "contact"
},
{
name: "Company",
value: "company"
},
{
name: "Invoice",
value: "invoice"
},
{
name: "Task",
value: "task"
},
{
name: "User",
value: "user"
},
{
name: "Expense",
value: "expense"
},
{
name: "Estimates",
value: "estimate"
}
],
default: 'user',
default: 'task',
description: 'The resource to operate on.',
},
@ -175,12 +172,8 @@ export class Harvest implements INodeType {
endpoint = `time_entries/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------
@ -197,16 +190,15 @@ export class Harvest implements INodeType {
requestMethod = 'POST';
endpoint = 'time_entries';
const createFields = this.getNodeParameter('createFields', i) as IDataObject;
body.project_id = this.getNodeParameter('projectId', i) as string;
body.task_id = this.getNodeParameter('taskId', i) as string;
body.spent_date = this.getNodeParameter('spentDate', i) as string;
Object.assign(qs, createFields);
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
Object.assign(body, additionalFields);
try {
let responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint, body);
returnData.push(responseData);
} else if (operation === 'createByDuration') {
// ----------------------------------
@ -216,16 +208,15 @@ export class Harvest implements INodeType {
requestMethod = 'POST';
endpoint = 'time_entries';
const createFields = this.getNodeParameter('createFields', i) as IDataObject;
body.project_id = this.getNodeParameter('projectId', i) as string;
body.task_id = this.getNodeParameter('taskId', i) as string;
body.spent_date = this.getNodeParameter('spentDate', i) as string;
Object.assign(qs, createFields);
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
Object.assign(body, additionalFields);
try {
let responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint, body);
returnData.push(responseData);
} else if (operation === 'delete') {
// ----------------------------------
@ -236,12 +227,8 @@ export class Harvest implements INodeType {
const id = this.getNodeParameter('id', i) as string;
endpoint = `time_entries/${id}`;
try {
let responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'deleteExternal') {
// ----------------------------------
// deleteExternal
@ -251,12 +238,8 @@ export class Harvest implements INodeType {
const id = this.getNodeParameter('id', i) as string;
endpoint = `time_entries/${id}/external_reference`;
try {
let responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'restartTime') {
// ----------------------------------
@ -267,12 +250,8 @@ export class Harvest implements INodeType {
const id = this.getNodeParameter('id', i) as string;
endpoint = `time_entries/${id}/restart`;
try {
let responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'stopTime') {
// ----------------------------------
@ -283,12 +262,8 @@ export class Harvest implements INodeType {
const id = this.getNodeParameter('id', i) as string;
endpoint = `time_entries/${id}/stop`;
try {
let responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'update') {
// ----------------------------------
@ -301,13 +276,9 @@ export class Harvest implements INodeType {
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
Object.assign(qs, updateFields);
try {
let responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
Object.assign(body, updateFields);
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint, body);
returnData.push(responseData);
} else {
throw new Error(`The operation "${operation}" is not known!`);
@ -324,12 +295,8 @@ export class Harvest implements INodeType {
endpoint = `clients/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------
@ -353,12 +320,8 @@ export class Harvest implements INodeType {
endpoint = `projects/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------
@ -382,12 +345,8 @@ export class Harvest implements INodeType {
endpoint = `users/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------
@ -406,12 +365,8 @@ export class Harvest implements INodeType {
endpoint = 'users/me';
try {
let responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else {
throw new Error(`The resource "${resource}" is not known!`);
@ -427,12 +382,8 @@ export class Harvest implements INodeType {
endpoint = `contacts/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------
@ -454,12 +405,8 @@ export class Harvest implements INodeType {
requestMethod = 'GET';
endpoint = `company`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else {
throw new Error(`The resource "${resource}" is not known!`);
@ -475,12 +422,8 @@ export class Harvest implements INodeType {
endpoint = `tasks/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------
@ -504,12 +447,8 @@ export class Harvest implements INodeType {
endpoint = `invoices/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------
@ -533,12 +472,8 @@ export class Harvest implements INodeType {
endpoint = `expenses/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------
@ -562,12 +497,8 @@ export class Harvest implements INodeType {
endpoint = `estimates/${id}`;
try {
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} catch (error) {
throw error;
}
const responseData = await harvestApiRequest.call(this, requestMethod, qs, endpoint);
returnData.push(responseData);
} else if (operation === 'getAll') {
// ----------------------------------

View file

@ -111,7 +111,7 @@ export const invoiceFields = [
{
displayName: 'Updated Since',
name: 'updated_since',
type: 'string',
type: 'dateTime',
default: '',
description: 'Only return time entries that have been updated since the given date and time.',
},
@ -157,8 +157,11 @@ export const invoiceFields = [
{
displayName: 'Page',
name: 'page',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 1,
},
default: 1,
description: 'The page number to use in pagination. For instance, if you make a list request and receive 100 records, your subsequent call can include page=2 to retrieve the next page of the list. (Default: 1)',
}
]

View file

@ -97,8 +97,8 @@ export const projectFields = [
{
displayName: 'Is Active',
name: 'is_active',
type: 'string',
default: '',
type: 'boolean',
default: true,
description: 'Pass true to only return active projects and false to return inactive projects.',
},
{
@ -111,15 +111,18 @@ export const projectFields = [
{
displayName: 'Updated Since',
name: 'updated_since',
type: 'string',
type: 'dateTime',
default: '',
description: 'Only return projects by updated_since.',
},
{
displayName: 'Page',
name: 'page',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 1,
},
default: 1,
description: 'The page number to use in pagination.',
},

View file

@ -97,22 +97,25 @@ export const taskFields = [
{
displayName: 'Is Active',
name: 'is_active',
type: 'string',
default: '',
type: 'boolean',
default: true,
description: 'Pass true to only return active tasks and false to return inactive tasks.',
},
{
displayName: 'Updated Since',
name: 'updated_since',
type: 'string',
type: 'dateTime',
default: '',
description: 'Only return tasks belonging to the task with the given ID.',
},
{
displayName: 'Page',
name: 'page',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 1,
},
default: 1,
description: 'The page number to use in pagination.',
}
]

View file

@ -145,20 +145,20 @@ export const timeEntryFields = [
displayName: 'Is Billed',
name: 'is_billed',
type: 'boolean',
default: '',
default: true,
description: 'Pass true to only return time entries that have been invoiced and false to return time entries that have not been invoiced.',
},
{
displayName: 'Is Running',
name: 'is_running',
type: 'string',
default: '',
type: 'boolean',
default: true,
description: 'Pass true to only return running time entries and false to return non-running time entries.',
},
{
displayName: 'Updated Since',
name: 'updated_since',
type: 'string',
type: 'dateTime',
default: '',
description: 'Only return time entries that have been updated since the given date and time.',
},
@ -179,8 +179,11 @@ export const timeEntryFields = [
{
displayName: 'Page',
name: 'page',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 1,
},
default: 1,
description: 'The page number to use in pagination. For instance, if you make a list request and receive 100 records, your subsequent call can include page=2 to retrieve the next page of the list. (Default: 1)',
}
]
@ -334,46 +337,22 @@ export const timeEntryFields = [
},
default: {},
options: [
{
displayName: 'Project Id',
name: 'projectId',
type: 'string',
default: '',
description: 'The ID of the project to associate with the time entry.',
},
{
displayName: 'Task Id',
name: 'taskId',
type: 'string',
default: '',
description: 'The ID of the task to associate with the time entry.',
},
{
displayName: 'Spent Date',
name: 'spentDate',
type: 'dateTime',
default: '',
description: 'The ISO 8601 formatted date the time entry was spent.',
},
{
displayName: 'Started Time',
name: 'startedTime',
type: 'string',
default: '',
description: 'The time the entry started. Defaults to the current time. Example: “8:00am”.',
},
{
displayName: 'Ended Time',
name: 'endedTime',
name: 'ended_time',
type: 'string',
default: '',
placeholder: '3:00pm',
description: 'The time the entry ended.',
},
{
displayName: 'Hours',
name: 'hours',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 0,
},
default: 0,
description: 'The current amount of time tracked.',
},
{
@ -382,7 +361,15 @@ export const timeEntryFields = [
type: 'string',
default: '',
description: 'These are notes about the time entry..',
}
},
{
displayName: 'Started Time',
name: 'started_time',
type: 'string',
default: '',
placeholder: '3:00pm',
description: 'The time the entry started. Defaults to the current time. Example: “8:00am”.',
},
],
},
@ -390,8 +377,62 @@ export const timeEntryFields = [
/* timeEntry:createByDuration */
/* -------------------------------------------------------------------------- */
{
displayName: 'Create Fields',
name: 'createFields',
displayName: 'Project Id',
name: 'projectId',
type: 'string',
displayOptions: {
show: {
operation: [
'createByDuration',
],
resource: [
'timeEntry',
],
},
},
default: '',
required: true,
description: 'The ID of the project to associate with the time entry.',
},
{
displayName: 'Task Id',
name: 'taskId',
type: 'string',
displayOptions: {
show: {
operation: [
'createByDuration',
],
resource: [
'timeEntry',
],
},
},
default: '',
required: true,
description: 'The ID of the task to associate with the time entry.',
},
{
displayName: 'Spent Date',
name: 'spentDate',
type: 'dateTime',
displayOptions: {
show: {
operation: [
'createByDuration',
],
resource: [
'timeEntry',
],
},
},
default: '',
required: true,
description: 'The ISO 8601 formatted date the time entry was spent.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
@ -406,39 +447,14 @@ export const timeEntryFields = [
},
default: {},
options: [
{
displayName: 'Project Id',
name: 'projectId',
type: 'string',
default: '',
description: 'The ID of the project to associate with the time entry.',
},
{
displayName: 'Task Id',
name: 'taskId',
type: 'string',
default: '',
description: 'The ID of the task to associate with the time entry.',
},
{
displayName: 'Spent Date',
name: 'spentDate',
type: 'dateTime',
default: '',
description: 'The ISO 8601 formatted date the time entry was spent.',
},
{
displayName: 'User ID',
name: 'userId',
type: 'string',
default: '',
description: 'The ID of the user to associate with the time entry. Defaults to the currently authenticated users ID.',
},
{
displayName: 'Hours',
name: 'hours',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 0,
},
default: 0,
description: 'The current amount of time tracked.',
},
{
@ -447,7 +463,14 @@ export const timeEntryFields = [
type: 'string',
default: '',
description: 'These are notes about the time entry..',
}
},
{
displayName: 'User ID',
name: 'user_id',
type: 'string',
default: '',
description: 'The ID of the user to associate with the time entry. Defaults to the currently authenticated users ID.',
},
],
},
@ -455,8 +478,62 @@ export const timeEntryFields = [
/* timeEntry:createByStartEnd */
/* -------------------------------------------------------------------------- */
{
displayName: 'Create Fields',
name: 'createFields',
displayName: 'Project Id',
name: 'projectId',
type: 'string',
displayOptions: {
show: {
operation: [
'createByStartEnd',
],
resource: [
'timeEntry',
],
},
},
default: '',
required: true,
description: 'The ID of the project to associate with the time entry.',
},
{
displayName: 'Task Id',
name: 'taskId',
type: 'string',
displayOptions: {
show: {
operation: [
'createByStartEnd',
],
resource: [
'timeEntry',
],
},
},
default: '',
required: true,
description: 'The ID of the task to associate with the time entry.',
},
{
displayName: 'Spent Date',
name: 'spentDate',
type: 'dateTime',
displayOptions: {
show: {
operation: [
'createByStartEnd',
],
resource: [
'timeEntry',
],
},
},
default: '',
required: true,
description: 'The ISO 8601 formatted date the time entry was spent.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
@ -471,46 +548,12 @@ export const timeEntryFields = [
},
default: {},
options: [
{
displayName: 'Project Id',
name: 'projectId',
type: 'string',
default: '',
description: 'The ID of the project to associate with the time entry.',
},
{
displayName: 'Task Id',
name: 'taskId',
type: 'string',
default: '',
description: 'The ID of the task to associate with the time entry.',
},
{
displayName: 'Spent Date',
name: 'spentDate',
type: 'dateTime',
default: '',
description: 'The ISO 8601 formatted date the time entry was spent.',
},
{
displayName: 'User ID',
name: 'userId',
type: 'string',
default: '',
description: 'The ID of the user to associate with the time entry. Defaults to the currently authenticated users ID.',
},
{
displayName: 'Started Time',
name: 'startedTime',
type: 'string',
default: '',
description: 'The time the entry started. Defaults to the current time. Example: “8:00am”.',
},
{
displayName: 'Ended Time',
name: 'endedTime',
name: 'ended_time',
type: 'string',
default: '',
placeholder: '3:00pm',
description: 'The time the entry ended.',
},
{
@ -519,7 +562,22 @@ export const timeEntryFields = [
type: 'string',
default: '',
description: 'These are notes about the time entry..',
}
},
{
displayName: 'Started Time',
name: 'started_time',
type: 'string',
default: '',
placeholder: '8:00am',
description: 'The time the entry started. Defaults to the current time. Example: “8:00am”.',
},
{
displayName: 'User ID',
name: 'user_id',
type: 'string',
default: '',
description: 'The ID of the user to associate with the time entry. Defaults to the currently authenticated users ID.',
},
],
},

View file

@ -102,22 +102,25 @@ export const userFields = [
{
displayName: 'Is Active',
name: 'is_active',
type: 'string',
default: '',
description: 'Only return users belonging to the user with the given ID.',
type: 'boolean',
default: true,
description: 'Pass true to only return active users and false to return inactive users.',
},
{
displayName: 'Updated Since',
name: 'updated_since',
type: 'string',
type: 'dateTime',
default: '',
description: 'Only return users belonging to the user with the given ID.',
},
{
displayName: 'Page',
name: 'page',
type: 'string',
default: '',
type: 'number',
typeOptions: {
minValue: 1,
},
default: 1,
description: 'The page number to use in pagination..',
}
]