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,65 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { ApitemplateAuth } from '../common/auth';
|
||||
import { ApitemplateAuthConfig, ApitemplateRegion, makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { templateIdDropdown } from '../common/props';
|
||||
|
||||
export const createImage = createAction({
|
||||
auth: ApitemplateAuth,
|
||||
name: 'createImage',
|
||||
displayName: 'Create Image',
|
||||
description: 'Creates an image from a template with provided data.',
|
||||
props: {
|
||||
templateId: templateIdDropdown,
|
||||
data: Property.Json({
|
||||
displayName: 'Template Data',
|
||||
description:
|
||||
'JSON data with overrides array to populate the template. Format: {"overrides": [{"name": "object_name", "property": "value"}]}.',
|
||||
required: true,
|
||||
}),
|
||||
generationDelay: Property.Number({
|
||||
displayName: 'Generation Delay (ms)',
|
||||
description: 'Delay in milliseconds before PDF generation',
|
||||
required: false,
|
||||
}),
|
||||
meta: Property.ShortText({
|
||||
displayName: 'External Reference ID',
|
||||
description: 'Specify an external reference ID for your own reference',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const authConfig = auth.props;
|
||||
const {
|
||||
templateId,
|
||||
data,
|
||||
generationDelay,
|
||||
meta,
|
||||
} = propsValue;
|
||||
|
||||
// Build query parameters
|
||||
const queryParams = new URLSearchParams();
|
||||
queryParams.append('template_id', templateId);
|
||||
|
||||
if (generationDelay) {
|
||||
queryParams.append('generation_delay', generationDelay.toString());
|
||||
}
|
||||
|
||||
if (meta) {
|
||||
queryParams.append('meta', meta);
|
||||
}
|
||||
|
||||
const endpoint = `/create-image?${queryParams.toString()}`;
|
||||
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.POST,
|
||||
endpoint,
|
||||
data,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,253 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { ApitemplateAuth } from '../common/auth';
|
||||
import { ApitemplateAuthConfig, ApitemplateRegion, makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const createPdfFromHtml = createAction({
|
||||
auth: ApitemplateAuth,
|
||||
name: 'createPdfFromHtml',
|
||||
displayName: 'Create PDF From HTML',
|
||||
description: 'Creates a PDF from HTML.',
|
||||
props: {
|
||||
html: Property.LongText({
|
||||
displayName: 'HTML Content',
|
||||
description: 'The HTML content to convert to PDF. Can include CSS styles and external resources.',
|
||||
required: true,
|
||||
}),
|
||||
css: Property.LongText({
|
||||
displayName: 'CSS Styles',
|
||||
description: 'Optional CSS styles to apply to the HTML content. Can include inline styles or external stylesheets.',
|
||||
required: false,
|
||||
}),
|
||||
data: Property.Json({
|
||||
displayName: 'Data for Templating',
|
||||
description: 'Optional JSON data to use for templating the HTML content. Can include variables and dynamic content.',
|
||||
required: false,
|
||||
}),
|
||||
expiration: Property.Number({
|
||||
displayName: 'Expiration (minutes)',
|
||||
description: 'Expiration of the generated PDF in minutes. Use 0 to store permanently, or 1-10080 minutes (7 days) to specify expiration.',
|
||||
required: false,
|
||||
}),
|
||||
pageSize: Property.StaticDropdown({
|
||||
displayName: 'Page Size',
|
||||
description: 'PDF page size format',
|
||||
required: false,
|
||||
defaultValue: 'A4',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'A4', value: 'A4' },
|
||||
{ label: 'A3', value: 'A3' },
|
||||
{ label: 'A5', value: 'A5' },
|
||||
{ label: 'Letter', value: 'Letter' },
|
||||
{ label: 'Legal', value: 'Legal' },
|
||||
{ label: 'Tabloid', value: 'Tabloid' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
orientation: Property.StaticDropdown({
|
||||
displayName: 'Page Orientation',
|
||||
description: 'PDF page orientation',
|
||||
required: false,
|
||||
defaultValue: 'portrait',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Portrait', value: 'portrait' },
|
||||
{ label: 'Landscape', value: 'landscape' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
marginTop: Property.Number({
|
||||
displayName: 'Margin Top (mm)',
|
||||
description: 'Top margin in millimeters',
|
||||
required: false,
|
||||
}),
|
||||
marginBottom: Property.Number({
|
||||
displayName: 'Margin Bottom (mm)',
|
||||
description: 'Bottom margin in millimeters',
|
||||
required: false,
|
||||
}),
|
||||
marginLeft: Property.Number({
|
||||
displayName: 'Margin Left (mm)',
|
||||
description: 'Left margin in millimeters',
|
||||
required: false,
|
||||
}),
|
||||
marginRight: Property.Number({
|
||||
displayName: 'Margin Right (mm)',
|
||||
description: 'Right margin in millimeters',
|
||||
required: false,
|
||||
}),
|
||||
printBackground: Property.Checkbox({
|
||||
displayName: 'Print Background',
|
||||
description: 'Whether to print background graphics and colors',
|
||||
required: false,
|
||||
defaultValue: true,
|
||||
}),
|
||||
headerFontSize: Property.ShortText({
|
||||
displayName: 'Header Font Size',
|
||||
description: 'Font size for header (e.g., "9px")',
|
||||
required: false,
|
||||
}),
|
||||
displayHeaderFooter: Property.Checkbox({
|
||||
displayName: 'Display Header/Footer',
|
||||
description: 'Whether to display header and footer',
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
}),
|
||||
customHeader: Property.LongText({
|
||||
displayName: 'Custom Header HTML',
|
||||
description: 'Custom HTML content for header',
|
||||
required: false,
|
||||
}),
|
||||
customFooter: Property.LongText({
|
||||
displayName: 'Custom Footer HTML',
|
||||
description: 'Custom HTML content for footer',
|
||||
required: false,
|
||||
}),
|
||||
scale: Property.Number({
|
||||
displayName: 'Scale',
|
||||
description: 'Scale factor for the PDF (0.1 to 2.0)',
|
||||
required: false,
|
||||
}),
|
||||
waitForTimeout: Property.Number({
|
||||
displayName: 'Wait Timeout (ms)',
|
||||
description: 'Time to wait before generating PDF (in milliseconds)',
|
||||
required: false,
|
||||
}),
|
||||
meta: Property.ShortText({
|
||||
displayName: 'External Reference ID',
|
||||
description: 'Specify an external reference ID for your own reference',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const authConfig = auth.props;
|
||||
const {
|
||||
html,
|
||||
css,
|
||||
data,
|
||||
expiration,
|
||||
pageSize,
|
||||
orientation,
|
||||
marginTop,
|
||||
marginBottom,
|
||||
marginLeft,
|
||||
marginRight,
|
||||
printBackground,
|
||||
headerFontSize,
|
||||
displayHeaderFooter,
|
||||
customHeader,
|
||||
customFooter,
|
||||
scale,
|
||||
waitForTimeout,
|
||||
meta,
|
||||
} = propsValue;
|
||||
|
||||
// Build query parameters according to API docs
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (expiration !== undefined && expiration !== 0) {
|
||||
queryParams.append('expiration', expiration.toString());
|
||||
}
|
||||
|
||||
if (meta) {
|
||||
queryParams.append('meta', meta);
|
||||
}
|
||||
|
||||
const endpoint = `/create-pdf-from-html${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
|
||||
|
||||
// Build settings object
|
||||
const settings: any = {};
|
||||
|
||||
if (pageSize && pageSize !== 'A4') {
|
||||
settings.paper_size = pageSize;
|
||||
}
|
||||
|
||||
if (orientation) {
|
||||
settings.orientation = orientation === 'landscape' ? '1' : '0';
|
||||
}
|
||||
|
||||
if (marginTop !== undefined) {
|
||||
settings.margin_top = marginTop.toString();
|
||||
}
|
||||
|
||||
if (marginBottom !== undefined) {
|
||||
settings.margin_bottom = marginBottom.toString();
|
||||
}
|
||||
|
||||
if (marginLeft !== undefined) {
|
||||
settings.margin_left = marginLeft.toString();
|
||||
}
|
||||
|
||||
if (marginRight !== undefined) {
|
||||
settings.margin_right = marginRight.toString();
|
||||
}
|
||||
|
||||
if (printBackground !== undefined) {
|
||||
settings.print_background = printBackground ? '1' : '0';
|
||||
}
|
||||
|
||||
if (headerFontSize) {
|
||||
settings.header_font_size = headerFontSize;
|
||||
}
|
||||
|
||||
if (displayHeaderFooter !== undefined) {
|
||||
settings.displayHeaderFooter = displayHeaderFooter;
|
||||
}
|
||||
|
||||
if (customHeader) {
|
||||
settings.custom_header = customHeader;
|
||||
}
|
||||
|
||||
if (customFooter) {
|
||||
settings.custom_footer = customFooter;
|
||||
}
|
||||
|
||||
if (scale !== undefined) {
|
||||
settings.scale = scale.toString();
|
||||
}
|
||||
|
||||
if (waitForTimeout !== undefined) {
|
||||
settings.wait_for_timeout = waitForTimeout.toString();
|
||||
}
|
||||
|
||||
// Build request body
|
||||
const requestBody: any = {
|
||||
body: html,
|
||||
};
|
||||
|
||||
if (css) {
|
||||
requestBody.css = css;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
requestBody.data = data;
|
||||
}
|
||||
|
||||
if (Object.keys(settings).length > 0) {
|
||||
requestBody.settings = settings;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.POST,
|
||||
endpoint,
|
||||
requestBody,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
|
||||
if (error.message.includes('502') && authConfig.region !== 'default') {
|
||||
throw new Error(
|
||||
`${error.message}\n\nThe ${authConfig.region} region appears to be experiencing issues. ` +
|
||||
`Consider switching to the 'default' region in your authentication settings or try again later.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,277 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { ApitemplateAuth } from '../common/auth';
|
||||
import { ApitemplateRegion, makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const createPdfFromUrl = createAction({
|
||||
auth: ApitemplateAuth,
|
||||
name: 'createPdfFromUrl',
|
||||
displayName: 'Create PDF From URL',
|
||||
description: 'Creates a PDF from a webpage URL.',
|
||||
props: {
|
||||
url: Property.ShortText({
|
||||
displayName: 'URL',
|
||||
description: 'The URL of the webpage to convert to PDF',
|
||||
required: true,
|
||||
}),
|
||||
expiration: Property.Number({
|
||||
displayName: 'Expiration (minutes)',
|
||||
description: 'Expiration of the generated PDF in minutes. Use 0 to store permanently, or 1-10080 minutes (7 days) to specify expiration.',
|
||||
required: false,
|
||||
}),
|
||||
pageSize: Property.StaticDropdown({
|
||||
displayName: 'Page Size',
|
||||
description: 'PDF page size format',
|
||||
required: false,
|
||||
defaultValue: 'A4',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'A4', value: 'A4' },
|
||||
{ label: 'A3', value: 'A3' },
|
||||
{ label: 'A5', value: 'A5' },
|
||||
{ label: 'Letter', value: 'Letter' },
|
||||
{ label: 'Legal', value: 'Legal' },
|
||||
{ label: 'Tabloid', value: 'Tabloid' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
orientation: Property.StaticDropdown({
|
||||
displayName: 'Page Orientation',
|
||||
description: 'PDF page orientation',
|
||||
required: false,
|
||||
defaultValue: 'portrait',
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'Portrait', value: 'portrait' },
|
||||
{ label: 'Landscape', value: 'landscape' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
marginTop: Property.Number({
|
||||
displayName: 'Margin Top (mm)',
|
||||
description: 'Top margin in millimeters',
|
||||
required: false,
|
||||
}),
|
||||
marginBottom: Property.Number({
|
||||
displayName: 'Margin Bottom (mm)',
|
||||
description: 'Bottom margin in millimeters',
|
||||
required: false,
|
||||
}),
|
||||
marginLeft: Property.Number({
|
||||
displayName: 'Margin Left (mm)',
|
||||
description: 'Left margin in millimeters',
|
||||
required: false,
|
||||
}),
|
||||
marginRight: Property.Number({
|
||||
displayName: 'Margin Right (mm)',
|
||||
description: 'Right margin in millimeters',
|
||||
required: false,
|
||||
}),
|
||||
printBackground: Property.Checkbox({
|
||||
displayName: 'Print Background',
|
||||
description: 'Whether to print background graphics and colors',
|
||||
required: false,
|
||||
defaultValue: true,
|
||||
}),
|
||||
headerFontSize: Property.ShortText({
|
||||
displayName: 'Header Font Size',
|
||||
description: 'Font size for header (e.g., "9px")',
|
||||
required: false,
|
||||
}),
|
||||
displayHeaderFooter: Property.Checkbox({
|
||||
displayName: 'Display Header/Footer',
|
||||
description: 'Whether to display header and footer',
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
}),
|
||||
customHeader: Property.LongText({
|
||||
displayName: 'Custom Header HTML',
|
||||
description: 'Custom HTML content for header',
|
||||
required: false,
|
||||
}),
|
||||
customFooter: Property.LongText({
|
||||
displayName: 'Custom Footer HTML',
|
||||
description: 'Custom HTML content for footer',
|
||||
required: false,
|
||||
}),
|
||||
scale: Property.Number({
|
||||
displayName: 'Scale',
|
||||
description: 'Scale factor for the PDF (0.1 to 2.0)',
|
||||
required: false,
|
||||
}),
|
||||
waitForTimeout: Property.Number({
|
||||
displayName: 'Wait Timeout (ms)',
|
||||
description: 'Time to wait for page to load before generating PDF (in milliseconds)',
|
||||
required: false,
|
||||
}),
|
||||
waitForSelector: Property.ShortText({
|
||||
displayName: 'Wait for Selector',
|
||||
description: 'CSS selector to wait for before generating PDF (e.g., ".content-loaded")',
|
||||
required: false,
|
||||
}),
|
||||
viewportWidth: Property.Number({
|
||||
displayName: 'Viewport Width',
|
||||
description: 'Browser viewport width in pixels (default: 1920)',
|
||||
required: false,
|
||||
defaultValue: 1920,
|
||||
}),
|
||||
viewportHeight: Property.Number({
|
||||
displayName: 'Viewport Height',
|
||||
description: 'Browser viewport height in pixels (default: 1080)',
|
||||
required: false,
|
||||
defaultValue: 1080,
|
||||
}),
|
||||
fullPage: Property.Checkbox({
|
||||
displayName: 'Full Page',
|
||||
description: 'Capture the full scrollable page',
|
||||
required: false,
|
||||
defaultValue: true,
|
||||
}),
|
||||
meta: Property.ShortText({
|
||||
displayName: 'External Reference ID',
|
||||
description: 'Specify an external reference ID for your own reference',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const authConfig = auth.props;
|
||||
const {
|
||||
url,
|
||||
expiration,
|
||||
pageSize,
|
||||
orientation,
|
||||
marginTop,
|
||||
marginBottom,
|
||||
marginLeft,
|
||||
marginRight,
|
||||
printBackground,
|
||||
headerFontSize,
|
||||
displayHeaderFooter,
|
||||
customHeader,
|
||||
customFooter,
|
||||
scale,
|
||||
waitForTimeout,
|
||||
waitForSelector,
|
||||
viewportWidth,
|
||||
viewportHeight,
|
||||
fullPage,
|
||||
meta,
|
||||
} = propsValue;
|
||||
|
||||
// Build query parameters for basic options
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
|
||||
if (expiration !== undefined && expiration !== 0) {
|
||||
queryParams.append('expiration', expiration.toString());
|
||||
}
|
||||
|
||||
if (meta) {
|
||||
queryParams.append('meta', meta);
|
||||
}
|
||||
|
||||
const endpoint = `/create-pdf-from-url${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
|
||||
|
||||
// Build settings object for page configuration
|
||||
const settings: any = {};
|
||||
|
||||
if (pageSize && pageSize !== 'A4') {
|
||||
settings.paper_size = pageSize;
|
||||
}
|
||||
|
||||
if (orientation) {
|
||||
settings.orientation = orientation === 'landscape' ? '1' : '0';
|
||||
}
|
||||
|
||||
if (marginTop !== undefined) {
|
||||
settings.margin_top = marginTop.toString();
|
||||
}
|
||||
|
||||
if (marginBottom !== undefined) {
|
||||
settings.margin_bottom = marginBottom.toString();
|
||||
}
|
||||
|
||||
if (marginLeft !== undefined) {
|
||||
settings.margin_left = marginLeft.toString();
|
||||
}
|
||||
|
||||
if (marginRight !== undefined) {
|
||||
settings.margin_right = marginRight.toString();
|
||||
}
|
||||
|
||||
if (printBackground !== undefined) {
|
||||
settings.print_background = printBackground ? '1' : '0';
|
||||
}
|
||||
|
||||
if (headerFontSize) {
|
||||
settings.header_font_size = headerFontSize;
|
||||
}
|
||||
|
||||
if (displayHeaderFooter !== undefined) {
|
||||
settings.displayHeaderFooter = displayHeaderFooter;
|
||||
}
|
||||
|
||||
if (customHeader) {
|
||||
settings.custom_header = customHeader;
|
||||
}
|
||||
|
||||
if (customFooter) {
|
||||
settings.custom_footer = customFooter;
|
||||
}
|
||||
|
||||
if (scale !== undefined) {
|
||||
settings.scale = scale.toString();
|
||||
}
|
||||
|
||||
if (waitForTimeout !== undefined) {
|
||||
settings.wait_for_timeout = waitForTimeout.toString();
|
||||
}
|
||||
|
||||
if (waitForSelector) {
|
||||
settings.wait_for_selector = waitForSelector;
|
||||
}
|
||||
|
||||
if (viewportWidth !== undefined && viewportWidth !== 1920) {
|
||||
settings.viewport_width = viewportWidth.toString();
|
||||
}
|
||||
|
||||
if (viewportHeight !== undefined && viewportHeight !== 1080) {
|
||||
settings.viewport_height = viewportHeight.toString();
|
||||
}
|
||||
|
||||
if (fullPage !== undefined) {
|
||||
settings.full_page = fullPage ? '1' : '0';
|
||||
}
|
||||
|
||||
// Build request body
|
||||
const requestBody: any = {
|
||||
url: url,
|
||||
};
|
||||
|
||||
if (Object.keys(settings).length > 0) {
|
||||
requestBody.settings = settings;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.POST,
|
||||
endpoint,
|
||||
requestBody,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
|
||||
if (error.message.includes('502') && authConfig.region !== 'default') {
|
||||
throw new Error(
|
||||
`${error.message}\n\nThe ${authConfig.region} region appears to be experiencing issues. ` +
|
||||
`Consider switching to the 'default' region in your authentication settings or try again later.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { ApitemplateAuth } from '../common/auth';
|
||||
import { ApitemplateRegion, makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { templateIdDropdown } from '../common/props';
|
||||
|
||||
export const createPdf = createAction({
|
||||
auth: ApitemplateAuth,
|
||||
name: 'createPdf',
|
||||
displayName: 'Create PDF',
|
||||
description: 'Creates a PDF from a template with provided data.',
|
||||
props: {
|
||||
templateId: templateIdDropdown,
|
||||
data: Property.Json({
|
||||
displayName: 'Template Data',
|
||||
description:
|
||||
'JSON data with overrides array to populate the template. Format: {"overrides": [{"name": "object_name", "property": "value"}]}.',
|
||||
required: true,
|
||||
}),
|
||||
expiration: Property.Number({
|
||||
displayName: 'Expiration (minutes)',
|
||||
description:
|
||||
'Expiration of the generated PDF in minutes. Use 0 to store permanently, or 1-10080 minutes (7 days) to specify expiration.',
|
||||
required: false,
|
||||
defaultValue: 0,
|
||||
}),
|
||||
generationDelay: Property.Number({
|
||||
displayName: 'Generation Delay (ms)',
|
||||
description: 'Delay in milliseconds before PDF generation',
|
||||
required: false,
|
||||
}),
|
||||
meta: Property.ShortText({
|
||||
displayName: 'External Reference ID',
|
||||
description: 'Specify an external reference ID for your own reference',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const authConfig = auth.props;
|
||||
const {
|
||||
templateId,
|
||||
data,
|
||||
expiration,
|
||||
generationDelay,
|
||||
meta,
|
||||
} = propsValue;
|
||||
|
||||
// Build query parameters according to API docs
|
||||
const queryParams = new URLSearchParams();
|
||||
queryParams.append('template_id', templateId);
|
||||
|
||||
if (expiration !== undefined && expiration !== 0) {
|
||||
queryParams.append('expiration', expiration.toString());
|
||||
}
|
||||
|
||||
if (generationDelay) {
|
||||
queryParams.append('generation_delay', generationDelay.toString());
|
||||
}
|
||||
|
||||
if (meta) {
|
||||
queryParams.append('meta', meta);
|
||||
}
|
||||
|
||||
const endpoint = `/create-pdf?${queryParams.toString()}`;
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.POST,
|
||||
endpoint,
|
||||
data,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
if (error.message.includes('502') && authConfig.region !== 'default') {
|
||||
throw new Error(
|
||||
`${error.message}\n\nThe ${authConfig.region} region appears to be experiencing issues. ` +
|
||||
`Consider switching to the 'default' region in your authentication settings or try again later.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { ApitemplateAuth } from '../common/auth';
|
||||
import { ApitemplateRegion, makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { transactionRefDropdown } from '../common/props';
|
||||
|
||||
export const deleteObject = createAction({
|
||||
auth: ApitemplateAuth,
|
||||
name: 'deleteObject',
|
||||
displayName: 'Delete Object',
|
||||
description: 'Deletes a generated PDF or image by its transaction reference or object ID.',
|
||||
props: {
|
||||
transactionRef: transactionRefDropdown,
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const authConfig = auth.props;
|
||||
const { transactionRef } = propsValue;
|
||||
|
||||
// Build query parameters according to API docs
|
||||
const queryParams = new URLSearchParams();
|
||||
queryParams.append('transaction_ref', transactionRef);
|
||||
|
||||
const endpoint = `/delete-object?${queryParams.toString()}`;
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.GET,
|
||||
endpoint,
|
||||
undefined,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
|
||||
if (error.message.includes('502') && authConfig.region !== 'default') {
|
||||
throw new Error(
|
||||
`${error.message}\n\nThe ${authConfig.region} region appears to be experiencing issues. ` +
|
||||
`Consider switching to the 'default' region in your authentication settings or try again later.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { ApitemplateAuth } from '../common/auth';
|
||||
import { ApitemplateRegion, makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const getAccountInformation = createAction({
|
||||
auth: ApitemplateAuth,
|
||||
name: 'getAccountInformation',
|
||||
displayName: 'Get Account Information',
|
||||
description: 'Retrieves account information including usage statistics and account details.',
|
||||
props: {},
|
||||
async run({ auth }) {
|
||||
const authConfig = auth.props;
|
||||
|
||||
const endpoint = '/account-information';
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.GET,
|
||||
endpoint,
|
||||
undefined,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
|
||||
if (error.message.includes('502') && authConfig.region !== 'default') {
|
||||
throw new Error(
|
||||
`${error.message}\n\nThe ${authConfig.region} region appears to be experiencing issues. ` +
|
||||
`Consider switching to the 'default' region in your authentication settings or try again later.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { ApitemplateAuth } from '../common/auth';
|
||||
import { ApitemplateRegion, makeRequest } from '../common/client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const listObjects = createAction({
|
||||
auth: ApitemplateAuth,
|
||||
name: 'listObjects',
|
||||
displayName: 'List Objects',
|
||||
description:
|
||||
'Retrieves a list of generated PDFs and images with optional filtering',
|
||||
props: {
|
||||
limit: Property.Number({
|
||||
displayName: 'Limit',
|
||||
description:
|
||||
'Maximum number of objects to return (default: 300, max: 300)',
|
||||
required: false,
|
||||
defaultValue: 300,
|
||||
}),
|
||||
offset: Property.Number({
|
||||
displayName: 'Offset',
|
||||
description: 'Number of objects to skip for pagination (default: 0)',
|
||||
required: false,
|
||||
defaultValue: 0,
|
||||
}),
|
||||
templateId: Property.ShortText({
|
||||
displayName: 'Template ID',
|
||||
description: 'Filter objects by template ID (optional)',
|
||||
required: false,
|
||||
}),
|
||||
transactionRef: Property.ShortText({
|
||||
displayName: 'Transaction Reference',
|
||||
description: 'Filter by specific transaction reference (optional)',
|
||||
required: false,
|
||||
}),
|
||||
dateFrom: Property.ShortText({
|
||||
displayName: 'Date From',
|
||||
description: 'Start date for filtering (YYYY-MM-DD format, optional)',
|
||||
required: false,
|
||||
}),
|
||||
dateTo: Property.ShortText({
|
||||
displayName: 'Date To',
|
||||
description: 'End date for filtering (YYYY-MM-DD format, optional)',
|
||||
required: false,
|
||||
}),
|
||||
meta: Property.ShortText({
|
||||
displayName: 'Meta Filter',
|
||||
description: 'Filter by external reference ID (meta field)',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run({ auth, propsValue }) {
|
||||
const authConfig = auth.props;
|
||||
const {
|
||||
limit,
|
||||
offset,
|
||||
templateId,
|
||||
transactionRef,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
meta,
|
||||
} = propsValue;
|
||||
|
||||
// Build query parameters according to API docs
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (limit !== undefined && limit !== 300) {
|
||||
queryParams.append('limit', Math.min(limit, 300).toString());
|
||||
}
|
||||
|
||||
if (offset !== undefined && offset !== 0) {
|
||||
queryParams.append('offset', offset.toString());
|
||||
}
|
||||
|
||||
if (templateId) {
|
||||
queryParams.append('template_id', templateId);
|
||||
}
|
||||
|
||||
if (transactionRef) {
|
||||
queryParams.append('transaction_ref', transactionRef);
|
||||
}
|
||||
|
||||
if (dateFrom) {
|
||||
queryParams.append('date_from', dateFrom);
|
||||
}
|
||||
|
||||
if (dateTo) {
|
||||
queryParams.append('date_to', dateTo);
|
||||
}
|
||||
|
||||
if (meta) {
|
||||
queryParams.append('meta', meta);
|
||||
}
|
||||
|
||||
const endpoint = `/list-objects${
|
||||
queryParams.toString() ? `?${queryParams.toString()}` : ''
|
||||
}`;
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.GET,
|
||||
endpoint,
|
||||
undefined,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
|
||||
if (error.message.includes('502') && authConfig.region !== 'default') {
|
||||
throw new Error(
|
||||
`${error.message}\n\nThe ${authConfig.region} region appears to be experiencing issues. ` +
|
||||
`Consider switching to the 'default' region in your authentication settings or try again later.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import { PieceAuth, Property } from '@activepieces/pieces-framework';
|
||||
import { makeRequest, ApitemplateAuthConfig } from './client';
|
||||
import { regionDropdown } from './props';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const ApitemplateAuth = PieceAuth.CustomAuth({
|
||||
description: `
|
||||
To obtain your API key:
|
||||
1. Go to https://app.apitemplate.io/.
|
||||
2. Navigate to API Integration section.
|
||||
3. Copy your API key.
|
||||
|
||||
Select the region closest to your location for better performance.
|
||||
`,
|
||||
props: {
|
||||
region: regionDropdown,
|
||||
apiKey: Property.ShortText({
|
||||
displayName: 'API Key',
|
||||
description: 'Your APITemplate.io API key',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
required: true,
|
||||
validate: async ({ auth }) => {
|
||||
if (!auth?.apiKey) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'API Key is required',
|
||||
};
|
||||
}
|
||||
|
||||
if (!auth?.region) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Region selection is required',
|
||||
};
|
||||
}
|
||||
|
||||
// Type-safe auth casting
|
||||
const authConfig = auth as ApitemplateAuthConfig;
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.GET,
|
||||
'/account-information',
|
||||
undefined,
|
||||
undefined,
|
||||
authConfig.region
|
||||
);
|
||||
|
||||
// Check if we got a valid response
|
||||
if (response && response.status === 'success') {
|
||||
return {
|
||||
valid: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid API response. Please check your credentials.',
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Authentication failed: ${
|
||||
error.message || 'Invalid API Key or region configuration'
|
||||
}`,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,165 @@
|
||||
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
|
||||
|
||||
export const APITEMPLATE_REGIONS = {
|
||||
default: 'https://rest.apitemplate.io',
|
||||
europe: 'https://rest-de.apitemplate.io',
|
||||
us: 'https://rest-us.apitemplate.io',
|
||||
australia: 'https://rest-au.apitemplate.io',
|
||||
'alt-default': 'https://rest-alt.apitemplate.io',
|
||||
'alt-europe': 'https://rest-alt-de.apitemplate.io',
|
||||
'alt-us': 'https://rest-alt-us.apitemplate.io',
|
||||
} as const;
|
||||
|
||||
export type ApitemplateRegion = keyof typeof APITEMPLATE_REGIONS;
|
||||
|
||||
// Interface for authentication object
|
||||
export interface ApitemplateAuthConfig {
|
||||
apiKey: string;
|
||||
region: ApitemplateRegion;
|
||||
}
|
||||
|
||||
|
||||
export const BASE_URL = APITEMPLATE_REGIONS.default;
|
||||
|
||||
export function getRegionalBaseUrl(region?: ApitemplateRegion): string {
|
||||
return APITEMPLATE_REGIONS[region || 'default'];
|
||||
}
|
||||
|
||||
// Helper function to get alternative regions for fallback
|
||||
function getAlternativeRegions(
|
||||
currentRegion?: ApitemplateRegion
|
||||
): ApitemplateRegion[] {
|
||||
const alternatives: ApitemplateRegion[] = [];
|
||||
|
||||
switch (currentRegion) {
|
||||
case 'default':
|
||||
alternatives.push('alt-default', 'us', 'australia', 'europe');
|
||||
break;
|
||||
case 'europe':
|
||||
alternatives.push('alt-europe', 'default', 'us', 'australia');
|
||||
break;
|
||||
case 'us':
|
||||
alternatives.push('alt-us', 'default', 'australia', 'europe');
|
||||
break;
|
||||
case 'australia':
|
||||
alternatives.push('default', 'us', 'europe');
|
||||
break;
|
||||
case 'alt-default':
|
||||
alternatives.push('default', 'us', 'australia');
|
||||
break;
|
||||
case 'alt-europe':
|
||||
alternatives.push('europe', 'default', 'us');
|
||||
break;
|
||||
case 'alt-us':
|
||||
alternatives.push('us', 'default', 'australia');
|
||||
break;
|
||||
default:
|
||||
alternatives.push('default', 'us', 'australia', 'europe');
|
||||
}
|
||||
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
export async function makeRequest(
|
||||
apiKey: string,
|
||||
method: HttpMethod,
|
||||
path: string,
|
||||
body?: unknown,
|
||||
headers?: Record<string, string> | string,
|
||||
region?: ApitemplateRegion
|
||||
) {
|
||||
const baseUrl = getRegionalBaseUrl(region);
|
||||
|
||||
try {
|
||||
const response = await httpClient.sendRequest({
|
||||
method,
|
||||
url: `${baseUrl}/v2${path}`,
|
||||
headers: {
|
||||
'X-API-KEY': apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
return response.body;
|
||||
} catch (error: any) {
|
||||
// Handle APITemplate.io specific error format
|
||||
// if (error.response?.body && typeof error.response.body === 'object') {
|
||||
// const errorBody = error.response.body as ApitemplateErrorResponse;
|
||||
// if (errorBody.status === 'error' && errorBody.message) {
|
||||
// throw new Error(`APITemplate.io Error: ${errorBody.message}`);
|
||||
// }
|
||||
// }
|
||||
|
||||
// Handle HTTP status errors
|
||||
if (error.response?.status) {
|
||||
const statusCode = error.response.status;
|
||||
const regionInfo = region ? ` (${region} region)` : '';
|
||||
|
||||
switch (statusCode) {
|
||||
case 401:
|
||||
throw new Error(
|
||||
'APITemplate.io Error: Invalid API key or unauthorized access'
|
||||
);
|
||||
case 403:
|
||||
throw new Error(
|
||||
'APITemplate.io Error: Access forbidden - check your API permissions'
|
||||
);
|
||||
case 404:
|
||||
throw new Error(
|
||||
'APITemplate.io Error: Resource not found - check your template ID'
|
||||
);
|
||||
case 429:
|
||||
throw new Error(
|
||||
'APITemplate.io Error: Rate limit exceeded - please try again later'
|
||||
);
|
||||
case 500:
|
||||
throw new Error(
|
||||
`APITemplate.io Error: Internal server error - please try again later${regionInfo}`
|
||||
);
|
||||
case 502:
|
||||
case 503:
|
||||
case 504:
|
||||
{
|
||||
|
||||
const alternatives = getAlternativeRegions(region);
|
||||
const suggestionText =
|
||||
alternatives.length > 0
|
||||
? `\n\nSuggested alternatives: Try switching to one of these regions: ${alternatives
|
||||
.slice(0, 3)
|
||||
.join(', ')}`
|
||||
: '';
|
||||
|
||||
throw new Error(
|
||||
`APITemplate.io Error: Service unavailable${regionInfo}. ` +
|
||||
`The ${
|
||||
region || 'default'
|
||||
} region server is temporarily down.${suggestionText}`
|
||||
);}
|
||||
default:
|
||||
throw new Error(
|
||||
`APITemplate.io Error: HTTP ${statusCode}${regionInfo} - ${
|
||||
error.message || 'Unknown error'
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle network or other errors
|
||||
if (error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
|
||||
const alternatives = getAlternativeRegions(region);
|
||||
const suggestionText =
|
||||
alternatives.length > 0
|
||||
? `\n\nSuggested alternatives: Try switching to one of these regions: ${alternatives
|
||||
.slice(0, 3)
|
||||
.join(', ')}`
|
||||
: '';
|
||||
|
||||
throw new Error(
|
||||
`APITemplate.io Error: Network connectivity issue with ${
|
||||
region || 'default'
|
||||
} region.${suggestionText}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
import { Property } from '@activepieces/pieces-framework';
|
||||
import {
|
||||
ApitemplateAuthConfig,
|
||||
ApitemplateRegion,
|
||||
makeRequest,
|
||||
} from './client';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { ApitemplateAuth } from './auth';
|
||||
|
||||
export const regionDropdown = Property.StaticDropdown({
|
||||
displayName: 'Region',
|
||||
description: 'Select your preferred API region for better performance',
|
||||
required: true,
|
||||
defaultValue: 'default',
|
||||
options: {
|
||||
options: [
|
||||
{
|
||||
label: 'Default (Singapore)',
|
||||
value: 'default',
|
||||
},
|
||||
{
|
||||
label: 'Europe (Frankfurt)',
|
||||
value: 'europe',
|
||||
},
|
||||
{
|
||||
label: 'US East (N. Virginia)',
|
||||
value: 'us',
|
||||
},
|
||||
{
|
||||
label: 'Australia (Sydney)',
|
||||
value: 'australia',
|
||||
},
|
||||
{
|
||||
label: 'Alternative - Default (Singapore)',
|
||||
value: 'alt-default',
|
||||
},
|
||||
{
|
||||
label: 'Alternative - Europe (Frankfurt)',
|
||||
value: 'alt-europe',
|
||||
},
|
||||
{
|
||||
label: 'Alternative - US East (N. Virginia)',
|
||||
value: 'alt-us',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
export const templateIdDropdown = Property.Dropdown({
|
||||
auth: ApitemplateAuth,
|
||||
displayName: 'Template ID',
|
||||
required: true,
|
||||
refreshers: ['auth'],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please connect your account first',
|
||||
};
|
||||
}
|
||||
|
||||
const authConfig = auth.props;
|
||||
|
||||
if (!authConfig.apiKey || !authConfig.region) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please complete authentication setup.',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.GET,
|
||||
'/list-templates',
|
||||
undefined,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
// Handle the specific APITemplate.io response structure
|
||||
const templates = response?.templates || [];
|
||||
|
||||
if (!Array.isArray(templates) || templates.length === 0) {
|
||||
return {
|
||||
disabled: false,
|
||||
options: [],
|
||||
placeholder: 'No templates found',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: templates.map((template: any) => ({
|
||||
label: `${template.name} (${template.format}) - ${template.status}`,
|
||||
value: template.template_id,
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error loading templates:', error);
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Error loading templates',
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const transactionRefDropdown = Property.Dropdown({
|
||||
auth: ApitemplateAuth,
|
||||
displayName: 'Transaction Reference',
|
||||
description: 'Select a transaction reference to filter objects.',
|
||||
required: false,
|
||||
refreshers: ['auth'],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please connect your account first',
|
||||
};
|
||||
}
|
||||
|
||||
const authConfig = auth.props;
|
||||
|
||||
if (!authConfig.apiKey || !authConfig.region) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please complete authentication setup',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await makeRequest(
|
||||
authConfig.apiKey,
|
||||
HttpMethod.GET,
|
||||
'/list-objects',
|
||||
undefined,
|
||||
undefined,
|
||||
authConfig.region as ApitemplateRegion
|
||||
);
|
||||
|
||||
// Handle the specific APITemplate.io response structure
|
||||
const objects = response?.objects || [];
|
||||
|
||||
if (!Array.isArray(objects) || objects.length === 0) {
|
||||
return {
|
||||
disabled: false,
|
||||
options: [],
|
||||
placeholder: 'No objects found',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: objects.map((obj: any) => ({
|
||||
label: obj.transaction_ref || 'Unknown Transaction Ref',
|
||||
value: obj.transaction_ref || '',
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error loading transaction references:', error);
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Error loading transaction references',
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user