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:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -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;
},
});

View File

@@ -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;
}
},
});

View File

@@ -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;
}
},
});

View File

@@ -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;
}
},
});

View File

@@ -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;
}
},
});

View File

@@ -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;
}
},
});

View File

@@ -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;
}
},
});

View File

@@ -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'
}`,
};
}
},
});

View File

@@ -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}`
);
}
}
}

View File

@@ -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',
};
}
},
});