Add Activepieces integration for workflow automation
- Add Activepieces fork with SmoothSchedule custom piece - Create integrations app with Activepieces service layer - Add embed token endpoint for iframe integration - Create Automations page with embedded workflow builder - Add sidebar visibility fix for embed mode - Add list inactive customers endpoint to Public API - Include SmoothSchedule triggers: event created/updated/cancelled - Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import { Property, createAction } from '@activepieces/pieces-framework';
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getClients = createAction({
|
||||
name: 'get_clients',
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Clients',
|
||||
description: 'Fetches Clients',
|
||||
props: {
|
||||
is_active: Property.ShortText({
|
||||
description: 'Pass `true` to only return active clients and `false` to return inactive clients.',
|
||||
displayName: 'Is Active',
|
||||
required: false,
|
||||
}),
|
||||
updated_since: Property.ShortText({
|
||||
description: 'Only return clients that have been updated since the given date and time.',
|
||||
displayName: 'Updated since',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'DEPRECATED: The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`clients`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
import { Property, createAction } from "@activepieces/pieces-framework";
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getEstimates = createAction({
|
||||
name: 'get_estimates', // Must be a unique across the piece, this shouldn't be changed.
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Estimates',
|
||||
description: 'Fetches Estimates',
|
||||
props: {
|
||||
from: Property.ShortText({
|
||||
description: 'Only return estimates with an issue_date on or after the given date. (YYYY-MM-DD)',
|
||||
displayName: 'From',
|
||||
required: false,
|
||||
}),
|
||||
to: Property.ShortText({
|
||||
description: 'Only return estimates with an issue_date on or before the given date. (YYYY-MM-DD)',
|
||||
displayName: 'To',
|
||||
required: false,
|
||||
}),
|
||||
state: Property.ShortText({
|
||||
description: 'Only return estimates with a state matching the value provided. Options: draft, open, accepted, or declined.',
|
||||
displayName: 'State',
|
||||
required: false,
|
||||
}),
|
||||
updated_since: Property.ShortText({
|
||||
description: 'Only return estimates that have been updated since the given date and time.',
|
||||
displayName: 'Updated since',
|
||||
required: false,
|
||||
}),
|
||||
client_id: Property.ShortText({
|
||||
description: 'Only return estimates belonging to the client with the given ID.',
|
||||
displayName: 'Client Id',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`estimates`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { Property, createAction } from "@activepieces/pieces-framework";
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getExpenses = createAction({
|
||||
name: 'get_expenses', // Must be a unique across the piece, this shouldn't be changed.
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Expenses',
|
||||
description: 'Fetches expenses',
|
||||
props: {
|
||||
from: Property.ShortText({
|
||||
description: 'Only return expenses with an spent_date on or after the given date. (YYYY-MM-DD)',
|
||||
displayName: 'From',
|
||||
required: false,
|
||||
}),
|
||||
to: Property.ShortText({
|
||||
description: 'Only return expenses with an spent_date on or before the given date. (YYYY-MM-DD)',
|
||||
displayName: 'To',
|
||||
required: false,
|
||||
}),
|
||||
user_id: Property.ShortText({
|
||||
description: 'Only return expenses belonging to the user with the given ID.',
|
||||
displayName: 'User Id',
|
||||
required: false,
|
||||
}),
|
||||
client_id: Property.ShortText({
|
||||
description: 'Only return expenses belonging to the client with the given ID.',
|
||||
displayName: 'Client Id',
|
||||
required: false,
|
||||
}),
|
||||
project_id: Property.ShortText({
|
||||
description: 'Only return expenses belonging to the project with the given ID.',
|
||||
displayName: 'Project Id',
|
||||
required: false,
|
||||
}),
|
||||
is_billed: Property.ShortText({
|
||||
description: 'Pass `true` to only return expenses that have been invoiced and `false` to return expenses that have not been invoiced.',
|
||||
displayName: 'Is Billed',
|
||||
required: false,
|
||||
}),
|
||||
updated_since: Property.ShortText({
|
||||
description: 'Only return expenses that have been updated since the given date and time.',
|
||||
displayName: 'Updated since',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`expenses`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
import { Property, createAction } from "@activepieces/pieces-framework";
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getInvoices = createAction({
|
||||
name: 'get_invoices', // Must be a unique across the piece, this shouldn't be changed.
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Invoices',
|
||||
description: 'Fetches invoices',
|
||||
props: {
|
||||
from: Property.ShortText({
|
||||
description: 'Only return invoices with an issue_date on or after the given date. (YYYY-MM-DD)',
|
||||
displayName: 'From',
|
||||
required: false,
|
||||
}),
|
||||
to: Property.ShortText({
|
||||
description: 'Only return invoices with an issue_date on or before the given date. (YYYY-MM-DD)',
|
||||
displayName: 'To',
|
||||
required: false,
|
||||
}),
|
||||
state: Property.ShortText({
|
||||
description: 'Only return invoices with a state matching the value provided. Options: draft, open, paid, or closed.',
|
||||
displayName: 'State',
|
||||
required: false,
|
||||
}),
|
||||
updated_since: Property.ShortText({
|
||||
description: 'Only return invoices that have been updated since the given date and time.',
|
||||
displayName: 'Updated since',
|
||||
required: false,
|
||||
}),
|
||||
client_id: Property.ShortText({
|
||||
description: 'Only return invoices belonging to the client with the given ID.',
|
||||
displayName: 'Client Id',
|
||||
required: false,
|
||||
}),
|
||||
project_id: Property.ShortText({
|
||||
description: 'Only return invoices belonging to the project with the given ID.',
|
||||
displayName: 'Project Id',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`invoices`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { Property, createAction } from '@activepieces/pieces-framework';
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getProjects = createAction({
|
||||
name: 'get_projects', // Must be a unique across the piece, this shouldn't be changed.
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Projects',
|
||||
description: 'Fetches projects',
|
||||
props: {
|
||||
is_active: Property.ShortText({
|
||||
description: 'Pass `true` to only return active projects and `false` to return inactive projects.',
|
||||
displayName: 'Is Active',
|
||||
required: false,
|
||||
}),
|
||||
updated_since: Property.ShortText({
|
||||
description: 'Only return projects that have been updated since the given date and time.',
|
||||
displayName: 'Updated since',
|
||||
required: false,
|
||||
}),
|
||||
client_id: Property.ShortText({
|
||||
description: 'Only return invoices belonging to the client with the given ID.',
|
||||
displayName: 'Client Id',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'DEPRECATED: The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`projects`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Property, createAction } from '@activepieces/pieces-framework';
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getRoles = createAction({
|
||||
name: 'get_roles', // Must be a unique across the piece, this shouldn't be changed.
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Roles',
|
||||
description: 'Fetches Roles',
|
||||
props: {
|
||||
page: Property.ShortText({
|
||||
description: 'DEPRECATED: The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`roles`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Property, createAction } from '@activepieces/pieces-framework';
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getTasks = createAction({
|
||||
name: 'get_tasks', // Must be a unique across the piece, this shouldn't be changed.
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Tasks',
|
||||
description: 'Fetches Tasks',
|
||||
props: {
|
||||
is_active: Property.ShortText({
|
||||
description: 'Pass `true` to only return active tasks and `false` to return inactive tasks.',
|
||||
displayName: 'Is Active',
|
||||
required: false,
|
||||
}),
|
||||
updated_since: Property.ShortText({
|
||||
description: 'Only return tasks that have been updated since the given date and time.',
|
||||
displayName: 'Updated since',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'DEPRECATED: The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`tasks`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Property, createAction } from "@activepieces/pieces-framework";
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getTime_entries = createAction({
|
||||
name: 'get_time_entries', // Must be a unique across the piece, this shouldn't be changed.
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Time Entries',
|
||||
description: 'Fetches Time Entries',
|
||||
props: {
|
||||
from: Property.ShortText({
|
||||
description: 'Only return time entries with an spent_date on or after the given date. (YYYY-MM-DD)',
|
||||
displayName: 'From',
|
||||
required: false,
|
||||
}),
|
||||
to: Property.ShortText({
|
||||
description: 'Only return time entries with an spent_date on or before the given date. (YYYY-MM-DD)',
|
||||
displayName: 'To',
|
||||
required: false,
|
||||
}),
|
||||
user_id: Property.ShortText({
|
||||
description: 'Only return time entries belonging to the user with the given ID.',
|
||||
displayName: 'User Id',
|
||||
required: false,
|
||||
}),
|
||||
client_id: Property.ShortText({
|
||||
description: 'Only return time entries belonging to the client with the given ID.',
|
||||
displayName: 'Client Id',
|
||||
required: false,
|
||||
}),
|
||||
project_id: Property.ShortText({
|
||||
description: 'Only return time entries belonging to the project with the given ID.',
|
||||
displayName: 'Project Id',
|
||||
required: false,
|
||||
}),
|
||||
task_id: Property.ShortText({
|
||||
description: 'Only return time entries belonging to the task with the given ID.',
|
||||
displayName: 'Task Id',
|
||||
required: false,
|
||||
}),
|
||||
external_reference_id: Property.ShortText({
|
||||
description: 'Only return time entries with the given external reference ID.',
|
||||
displayName: 'External Reference Id',
|
||||
required: false,
|
||||
}),
|
||||
is_billed: Property.ShortText({
|
||||
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 Billed',
|
||||
required: false,
|
||||
}),
|
||||
is_running: Property.ShortText({
|
||||
description: 'Pass `true` to only return running time entries and `false` to return non-running time entries.',
|
||||
displayName: 'Is Running',
|
||||
required: false,
|
||||
}),
|
||||
updated_since: Property.ShortText({
|
||||
description: 'Only return time entries that have been updated since the given date and time.',
|
||||
displayName: 'Updated since',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`time_entries`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { Property, createAction } from '@activepieces/pieces-framework';
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getUsers = createAction({
|
||||
name: 'get_users',
|
||||
auth: harvestAuth,
|
||||
displayName: 'Get Users',
|
||||
description: 'Fetches Users',
|
||||
props: {
|
||||
is_active: Property.ShortText({
|
||||
description: 'Pass `true` to only return active users and `false` to return inactive users.',
|
||||
displayName: 'Is Active',
|
||||
required: false,
|
||||
}),
|
||||
updated_since: Property.ShortText({
|
||||
description: 'Only return users that have been updated since the given date and time.',
|
||||
displayName: 'Updated since',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'DEPRECATED: The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`users`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
import { Property, createAction } from "@activepieces/pieces-framework";
|
||||
import { harvestAuth } from '../..';
|
||||
import {
|
||||
getAccessTokenOrThrow,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { callHarvestApi, filterDynamicFields } from '../common';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const reportsUninvoiced = createAction({
|
||||
name: 'reports-uninvoiced',
|
||||
auth: harvestAuth,
|
||||
displayName: 'Uninvoiced Report',
|
||||
description: 'Uninvoiced hours and expenses for all billable projects',
|
||||
props: {
|
||||
from: Property.ShortText({
|
||||
description: 'Only report on time entries and expenses with a spent_date on or after the given date. (YYYY-MM-DD)',
|
||||
displayName: 'From',
|
||||
required: true,
|
||||
}),
|
||||
to: Property.ShortText({
|
||||
description: 'Only report on time entries and expenses with a spent_date on or before the given date. (YYYY-MM-DD)',
|
||||
displayName: 'To',
|
||||
required: true,
|
||||
}),
|
||||
include_fixed_fee: Property.ShortText({
|
||||
description: 'Whether or not to include fixed-fee projects in the response. (Default: true)',
|
||||
displayName: 'Include Fixed Fee',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.ShortText({
|
||||
description: 'The page number to use in pagination.',
|
||||
displayName: 'Page',
|
||||
required: false,
|
||||
}),
|
||||
per_page: Property.ShortText({
|
||||
description: 'The number of records to return per page. (1-2000)',
|
||||
displayName: 'Records per page',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(context.propsValue, {
|
||||
per_page: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((val) => (val === undefined || val === '' ? undefined : parseInt(val, 10)))
|
||||
.refine(
|
||||
(val) => val === undefined || (Number.isInteger(val) && val >= 1 && val <= 2000),
|
||||
'Per Page must be a number between 1 and 2000.'
|
||||
),
|
||||
});
|
||||
|
||||
const params = filterDynamicFields(context.propsValue);
|
||||
|
||||
const response = await callHarvestApi(
|
||||
HttpMethod.GET,
|
||||
`reports/uninvoiced`,
|
||||
getAccessTokenOrThrow(context.auth),
|
||||
params
|
||||
);
|
||||
|
||||
return response.body; },
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import { DynamicPropsValue } from '@activepieces/pieces-framework';
|
||||
import {
|
||||
HttpMethod,
|
||||
HttpMessageBody,
|
||||
HttpResponse,
|
||||
httpClient,
|
||||
AuthenticationType,
|
||||
} from '@activepieces/pieces-common';
|
||||
|
||||
export async function callHarvestApi<T extends HttpMessageBody = any>(
|
||||
method: HttpMethod,
|
||||
apiUrl: string,
|
||||
accessToken: string,
|
||||
queryParams: any | undefined = undefined,
|
||||
body: any | undefined = undefined,
|
||||
headers: any | undefined = undefined
|
||||
): Promise<HttpResponse<T>> {
|
||||
return await httpClient.sendRequest<T>({
|
||||
method: method,
|
||||
url: `https://api.harvestapp.com/v2/${apiUrl}`,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token: accessToken,
|
||||
},
|
||||
headers,
|
||||
body,
|
||||
queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
//Remove null/undefined values and create an array to be used for queryparams
|
||||
export function filterDynamicFields(dynamicFields: DynamicPropsValue): { [key: string]: string } {
|
||||
const fields: { [key: string]: string } = {};
|
||||
|
||||
const props = Object.entries(dynamicFields);
|
||||
for (const [propertyKey, propertyValue] of props) {
|
||||
if (
|
||||
propertyValue !== null &&
|
||||
propertyValue !== undefined &&
|
||||
propertyValue !== '' &&
|
||||
!(typeof propertyValue === 'string' && propertyValue.trim() === '')
|
||||
) {
|
||||
fields[propertyKey] = propertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
Reference in New Issue
Block a user