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,132 @@
|
||||
import { zuoraAuth } from '../../';
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { getAccessToken } from '../common';
|
||||
import {
|
||||
AuthenticationType,
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
HttpRequest,
|
||||
} from '@activepieces/pieces-common';
|
||||
|
||||
export const createInvoiceAction = createAction({
|
||||
auth: zuoraAuth,
|
||||
name: 'create-invoice',
|
||||
displayName: 'Create Invoice',
|
||||
description: 'Create a standalone invoice.',
|
||||
props: {
|
||||
accountNumber: Property.ShortText({
|
||||
displayName: 'Customer Account Number',
|
||||
description:
|
||||
'The number of the customer account associated with the invoice.',
|
||||
required: true,
|
||||
}),
|
||||
autoPay: Property.Checkbox({
|
||||
displayName: 'Auto Pay?',
|
||||
description:
|
||||
'Whether invoices are automatically picked up for processing in the corresponding payment run.',
|
||||
required: false,
|
||||
}),
|
||||
comments: Property.LongText({
|
||||
displayName: 'Comments',
|
||||
required: false,
|
||||
}),
|
||||
dueDate: Property.DateTime({
|
||||
displayName: 'Due Date',
|
||||
description: 'Provide YYYY-MM-DD format.',
|
||||
required: true,
|
||||
}),
|
||||
invoiceDate: Property.DateTime({
|
||||
displayName: 'Invoice Date',
|
||||
required: true,
|
||||
description: 'Provide YYYY-MM-DD format.',
|
||||
}),
|
||||
invoiceItems: Property.Array({
|
||||
displayName: 'Invoice Items',
|
||||
required: false,
|
||||
properties: {
|
||||
productRatePlanChargeId: Property.ShortText({
|
||||
displayName: 'Product Rate Plan Charge ID',
|
||||
description:
|
||||
'The ID of the product rate plan charge that the invoice item is created from.You can use `Find Product Rate Plan` action to search associate rate plan charge.',
|
||||
required: true,
|
||||
}),
|
||||
amount: Property.Number({
|
||||
displayName: 'Amount',
|
||||
required: true,
|
||||
}),
|
||||
description: Property.LongText({
|
||||
displayName: 'Description',
|
||||
required: false,
|
||||
}),
|
||||
purchaseOrderNumber: Property.ShortText({
|
||||
displayName: 'Purchase Order Number',
|
||||
required: false,
|
||||
}),
|
||||
quantity: Property.ShortText({
|
||||
displayName: 'Quantity',
|
||||
required: false,
|
||||
}),
|
||||
serviceStartDate: Property.ShortText({
|
||||
displayName: 'Service Start Date',
|
||||
description: 'Provide YYYY-MM-DD format.',
|
||||
required: true,
|
||||
}),
|
||||
serviceEndDate: Property.ShortText({
|
||||
displayName: 'Service End Date',
|
||||
description: 'Provide YYYY-MM-DD format.',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
status: Property.StaticDropdown({
|
||||
displayName: 'Status',
|
||||
required: false,
|
||||
defaultValue: 'Draft',
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'Draft', value: 'Draft' },
|
||||
{ label: 'Posted', value: 'Posted' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { accountNumber, autoPay, comments, dueDate, invoiceDate, status } =
|
||||
context.propsValue;
|
||||
|
||||
const invoiceItems = context.propsValue.invoiceItems as InvoiceItemInput[];
|
||||
|
||||
const token = await getAccessToken(context.auth);
|
||||
|
||||
const body = {
|
||||
accountNumber,
|
||||
autoPay,
|
||||
comments,
|
||||
dueDate,
|
||||
invoiceDate,
|
||||
status,
|
||||
invoiceItems,
|
||||
};
|
||||
|
||||
const request: HttpRequest = {
|
||||
method: HttpMethod.POST,
|
||||
url: `${context.auth.props.environment}/v1/invoices`,
|
||||
authentication: { type: AuthenticationType.BEARER_TOKEN, token },
|
||||
body,
|
||||
};
|
||||
|
||||
const response = await httpClient.sendRequest(request);
|
||||
return response.body;
|
||||
},
|
||||
});
|
||||
|
||||
type InvoiceItemInput = {
|
||||
productRatePlanChargeId: string;
|
||||
amount: string;
|
||||
description: string;
|
||||
purchaseOrderNumber: string;
|
||||
quantity: string;
|
||||
serviceStartDate: string;
|
||||
serviceEndDate: string;
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import { zuoraAuth } from '../../';
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { getAccessToken } from '../common';
|
||||
import {
|
||||
AuthenticationType,
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
HttpRequest,
|
||||
} from '@activepieces/pieces-common';
|
||||
|
||||
export const findAccountAction = createAction({
|
||||
auth: zuoraAuth,
|
||||
name: 'find-account',
|
||||
displayName: 'Find Customer Account',
|
||||
description: 'Retrieves account based on name.',
|
||||
props: {
|
||||
name: Property.ShortText({
|
||||
displayName: 'Account Name',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const name = context.propsValue.name;
|
||||
const token = await getAccessToken(context.auth);
|
||||
|
||||
const request: HttpRequest = {
|
||||
method: HttpMethod.GET,
|
||||
url: `${context.auth.props.environment}/object-query/accounts`,
|
||||
authentication: { type: AuthenticationType.BEARER_TOKEN, token },
|
||||
queryParams: {
|
||||
'filter[]': `name.EQ:${name}`,
|
||||
},
|
||||
};
|
||||
|
||||
const response = await httpClient.sendRequest(request);
|
||||
return response.body;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { zuoraAuth } from '../..';
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { getAccessToken } from '../common';
|
||||
import {
|
||||
AuthenticationType,
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
HttpRequest,
|
||||
} from '@activepieces/pieces-common';
|
||||
|
||||
export const findProductRatePlanAction = createAction({
|
||||
auth: zuoraAuth,
|
||||
name: 'find-product-rate-plan',
|
||||
displayName: 'Find Product Rate Plan',
|
||||
description: 'Retrieves product rate plan with charges.',
|
||||
props: {
|
||||
name: Property.ShortText({
|
||||
displayName: 'Product Rate Plan Name',
|
||||
description: 'i.e. StealthCo Premium',
|
||||
required: true,
|
||||
}),
|
||||
productid: Property.ShortText({
|
||||
displayName: 'Product ID',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const name = context.propsValue.name;
|
||||
const productid = context.propsValue.productid;
|
||||
const token = await getAccessToken(context.auth);
|
||||
|
||||
const request: HttpRequest = {
|
||||
method: HttpMethod.GET,
|
||||
url: `${context.auth.props.environment}/object-query/product-rate-plans?filter[]=name.EQ:${name}&filter[]=productid.EQ:${productid}`,
|
||||
authentication: { type: AuthenticationType.BEARER_TOKEN, token },
|
||||
queryParams: {
|
||||
'expand[]': 'productrateplancharges',
|
||||
},
|
||||
};
|
||||
|
||||
const response = await httpClient.sendRequest(request);
|
||||
return response.body;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import { zuoraAuth } from '../../';
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { getAccessToken } from '../common';
|
||||
import {
|
||||
AuthenticationType,
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
HttpRequest,
|
||||
} from '@activepieces/pieces-common';
|
||||
|
||||
export const findProductAction = createAction({
|
||||
auth: zuoraAuth,
|
||||
name: 'find-product',
|
||||
displayName: 'Find Product',
|
||||
description: 'Retrieves product based on sku.',
|
||||
props: {
|
||||
sku: Property.ShortText({
|
||||
displayName: 'Product SKU',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const sku = context.propsValue.sku;
|
||||
const token = await getAccessToken(context.auth);
|
||||
|
||||
const request: HttpRequest = {
|
||||
method: HttpMethod.GET,
|
||||
url: `${context.auth.props.environment}/object-query/products`,
|
||||
authentication: { type: AuthenticationType.BEARER_TOKEN, token },
|
||||
queryParams: {
|
||||
'filter[]': `sku.EQ:${sku}`,
|
||||
},
|
||||
};
|
||||
|
||||
const response = await httpClient.sendRequest(request);
|
||||
return response.body;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
import { zuoraAuth } from '../../';
|
||||
import {
|
||||
AppConnectionValueForAuthProperty,
|
||||
DropdownOption,
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import {
|
||||
AuthenticationType,
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
HttpRequest,
|
||||
QueryParams,
|
||||
} from '@activepieces/pieces-common';
|
||||
|
||||
export async function queryAccounts(
|
||||
auth: AppConnectionValueForAuthProperty<typeof zuoraAuth>
|
||||
) {
|
||||
const token = await getAccessToken(auth);
|
||||
const result: Record<string, any>[] = [];
|
||||
let cursor;
|
||||
do {
|
||||
const qs: QueryParams = {
|
||||
pageSize: '50',
|
||||
'fields[]': 'id,name',
|
||||
'sort[]': 'updated_time.desc',
|
||||
};
|
||||
if (cursor) {
|
||||
qs['cursor'] = cursor;
|
||||
}
|
||||
|
||||
const request: HttpRequest = {
|
||||
method: HttpMethod.GET,
|
||||
url: `${auth.props.environment}/v2/accounts`,
|
||||
queryParams: qs,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token,
|
||||
},
|
||||
};
|
||||
|
||||
const response = await httpClient.sendRequest(request);
|
||||
result.push(...response.body['data']);
|
||||
cursor = response.body['nextPage'];
|
||||
} while (cursor);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getAccessToken(
|
||||
auth: AppConnectionValueForAuthProperty<typeof zuoraAuth>
|
||||
): Promise<string> {
|
||||
const request: HttpRequest = {
|
||||
method: HttpMethod.POST,
|
||||
url: `${auth.props.environment}/oauth/token`,
|
||||
body: new URLSearchParams({
|
||||
client_id: auth.props.clientId,
|
||||
client_secret: auth.props.clientSecret,
|
||||
grant_type: 'client_credentials',
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
};
|
||||
|
||||
const response = await httpClient.sendRequest(request);
|
||||
return response.body['access_token'];
|
||||
}
|
||||
|
||||
export const zuoraCommonProps = {
|
||||
account_id: (displayName: string, description: string, required: boolean) =>
|
||||
Property.Dropdown({
|
||||
auth: zuoraAuth,
|
||||
displayName,
|
||||
description,
|
||||
required,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
placeholder: 'Please connect your account first',
|
||||
};
|
||||
}
|
||||
const authValue = auth as AppConnectionValueForAuthProperty<typeof zuoraAuth>;
|
||||
const accounts = await queryAccounts(authValue);
|
||||
|
||||
const options: DropdownOption<string>[] = [];
|
||||
|
||||
for (const account of accounts) {
|
||||
options.push({
|
||||
label: account['name'] ?? account['id'],
|
||||
value: account['id'],
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}),
|
||||
};
|
||||
Reference in New Issue
Block a user