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,133 @@
|
||||
---
|
||||
title: "Piece Auth"
|
||||
description: "Learn about piece authentication"
|
||||
icon: 'key'
|
||||
---
|
||||
|
||||
Piece authentication is used to gather user credentials and securely store them for future use in different flows.
|
||||
The authentication must be defined as the `auth` parameter in the `createPiece`, `createTrigger`, and `createAction` functions.
|
||||
|
||||
This requirement ensures that the type of authentication can be inferred correctly in triggers and actions.
|
||||
|
||||
<Warning>
|
||||
The auth parameter for `createPiece`, `createTrigger`, and `createAction` functions can take an array, but you cannot have more than one auth property of the same type, i.e two OAUTH2 properties.
|
||||
</Warning>
|
||||
|
||||
### Secret Text
|
||||
|
||||
This authentication collects sensitive information, such as passwords or API keys. It is displayed as a masked input field.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
PieceAuth.SecretText({
|
||||
displayName: 'API Key',
|
||||
description: 'Enter your API key',
|
||||
required: true,
|
||||
// Optional Validation
|
||||
validate: async ({auth}) => {
|
||||
if(auth.startsWith('sk_')){
|
||||
return {
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid Api Key'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Username and Password
|
||||
|
||||
This authentication collects a username and password as separate fields.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
PieceAuth.BasicAuth({
|
||||
displayName: 'Credentials',
|
||||
description: 'Enter your username and password',
|
||||
required: true,
|
||||
username: {
|
||||
displayName: 'Username',
|
||||
description: 'Enter your username',
|
||||
},
|
||||
password: {
|
||||
displayName: 'Password',
|
||||
description: 'Enter your password',
|
||||
},
|
||||
// Optional Validation
|
||||
validate: async ({auth}) => {
|
||||
if(auth){
|
||||
return {
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid Api Key'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Custom
|
||||
|
||||
This authentication allows for custom authentication by collecting specific properties, such as a base URL and access token.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
PieceAuth.CustomAuth({
|
||||
displayName: 'Custom Authentication',
|
||||
description: 'Enter custom authentication details',
|
||||
props: {
|
||||
base_url: Property.ShortText({
|
||||
displayName: 'Base URL',
|
||||
description: 'Enter the base URL',
|
||||
required: true,
|
||||
}),
|
||||
access_token: PieceAuth.SecretText({
|
||||
displayName: 'Access Token',
|
||||
description: 'Enter the access token',
|
||||
required: true
|
||||
})
|
||||
},
|
||||
// Optional Validation
|
||||
validate: async ({auth}) => {
|
||||
if(auth){
|
||||
return {
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid Api Key'
|
||||
}
|
||||
},
|
||||
required: true
|
||||
})
|
||||
```
|
||||
|
||||
### OAuth2
|
||||
|
||||
This authentication collects OAuth2 authentication details, including the authentication URL, token URL, and scope.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
PieceAuth.OAuth2({
|
||||
displayName: 'OAuth2 Authentication',
|
||||
grantType: OAuth2GrantType.AUTHORIZATION_CODE,
|
||||
required: true,
|
||||
authUrl: 'https://example.com/auth',
|
||||
tokenUrl: 'https://example.com/token',
|
||||
scope: ['read', 'write']
|
||||
})
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Please note `OAuth2GrantType.CLIENT_CREDENTIALS` is also supported for service-based authentication.
|
||||
</Tip>
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "Enable Custom API Calls"
|
||||
description: "Learn how to enable custom API calls for your pieces"
|
||||
icon: 'webhook'
|
||||
---
|
||||
|
||||
Custom API Calls allow the user to send a request to a specific endpoint if no action has been implemented for it.
|
||||
|
||||
This will show in the actions list of the piece as `Custom API Call`, to enable this action for a piece, you need to call the `createCustomApiCallAction` in your actions array.
|
||||
|
||||
## Basic Example
|
||||
|
||||
The example below implements the action for the OpenAI piece. The OpenAI piece uses a `Bearer token` authorization header to identify the user sending the request.
|
||||
|
||||
```typescript
|
||||
actions: [
|
||||
...yourActions,
|
||||
createCustomApiCallAction({
|
||||
// The auth object defined in the piece
|
||||
auth: openaiAuth,
|
||||
// The base URL for the API
|
||||
baseUrl: () => {
|
||||
'https://api.openai.com/v1'
|
||||
},
|
||||
// Mapping the auth object to the needed authorization headers
|
||||
authMapping: async (auth) => {
|
||||
return {
|
||||
'Authorization': `Bearer ${auth}`
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
```
|
||||
|
||||
## Dynamic Base URL and Basic Auth Example
|
||||
|
||||
The example below implements the action for the Jira Cloud piece. The Jira Cloud piece uses a dynamic base URL for it's actions, where the base URL changes based on the values the user authenticated with. We will also implement a Basic authentication header.
|
||||
|
||||
```typescript
|
||||
actions: [
|
||||
...yourActions,
|
||||
createCustomApiCallAction({
|
||||
baseUrl: (auth) => {
|
||||
return `${(auth as JiraAuth).instanceUrl}/rest/api/3`
|
||||
},
|
||||
auth: jiraCloudAuth,
|
||||
authMapping: async (auth) => {
|
||||
const typedAuth = auth as JiraAuth
|
||||
return {
|
||||
'Authorization': `Basic ${typedAuth.email}:${typedAuth.apiToken}`
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
```
|
||||
31
activepieces-fork/docs/build-pieces/piece-reference/examples.mdx
Executable file
31
activepieces-fork/docs/build-pieces/piece-reference/examples.mdx
Executable file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
title: "Piece Examples"
|
||||
description: "Explore a collection of example triggers and actions"
|
||||
icon: 'brackets-curly'
|
||||
---
|
||||
|
||||
To get the full benefit, it is recommended to read the tutorial first.
|
||||
|
||||
## Triggers:
|
||||
|
||||
**Webhooks:**
|
||||
- [New Form Submission on Typeform](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/typeform/src/lib/trigger/new-submission.ts)
|
||||
|
||||
**Polling:**
|
||||
- [New Completed Task On Todoist](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/todoist/src/lib/triggers/task-completed-trigger.ts)
|
||||
|
||||
## Actions:
|
||||
- [Send a message On Discord](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/discord/src/lib/actions/send-message-webhook.ts)
|
||||
- [Send an mail On Gmail](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/gmail/src/lib/actions/send-email-action.ts)
|
||||
|
||||
## Authentication
|
||||
|
||||
**OAuth2:**
|
||||
- [Slack](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/slack/src/index.ts)
|
||||
- [Gmail](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/gmail/src/index.ts)
|
||||
|
||||
**API Key:**
|
||||
- [Sendgrid](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/sendgrid/src/index.ts)
|
||||
|
||||
**Basic Authentication:**
|
||||
- [Twilio](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/twilio/src/index.ts)
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: "External Libraries"
|
||||
icon: 'npm'
|
||||
description: "Learn how to install and use external libraries."
|
||||
---
|
||||
|
||||
The Activepieces repository is structured as a monorepo, employing Nx as its build tool.
|
||||
|
||||
To keep our main `package.json` as light as possible, we keep libraries that are only used for a piece in the piece `package.json` . This means when adding a new library you should navigate to the piece folder and install the library with our package manager `bun`
|
||||
|
||||
```bash
|
||||
cd packages/pieces/<piece-path>
|
||||
bun install --save <library-name>
|
||||
```
|
||||
|
||||
- Import the library into your piece.
|
||||
|
||||
Guidelines:
|
||||
- Make sure you are using well-maintained libraries.
|
||||
- Ensure that the library size is not too large to avoid bloating the bundle size; this will make the piece load faster in the sandbox.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: "Files"
|
||||
icon: 'file'
|
||||
description: "Learn how to use files object to create file references."
|
||||
---
|
||||
|
||||
The `ctx.files` object allow you to store files in local storage or in a remote storage depending on the run environment.
|
||||
|
||||
## Write
|
||||
|
||||
You can use the `write` method to write a file to the storage, It returns a string that can be used in other actions or triggers properties to reference the file.
|
||||
|
||||
**Example:**
|
||||
```ts
|
||||
const fileReference = await files.write({
|
||||
fileName: 'file.txt',
|
||||
data: Buffer.from('text')
|
||||
});
|
||||
```
|
||||
|
||||
<Tip>
|
||||
This code will store the file in the database If the run environment is testing mode since it will be required to test other steps, other wise it will store it in the local temporary directory.
|
||||
</Tip>
|
||||
|
||||
For Reading the file If you are using the file property in a trigger or action, It will be automatically parsed and you can use it directly, please refer to `Property.File` in the [properties](./properties#file) section.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: 'Flow Control'
|
||||
icon: 'Joystick'
|
||||
description: 'Learn How to Control Flow from Inside the Piece'
|
||||
---
|
||||
|
||||
Flow Controls provide the ability to control the flow of execution from inside a piece. By using the `ctx` parameter in the `run` method of actions, you can perform various operations to control the flow.
|
||||
|
||||
## Stop Flow
|
||||
|
||||
You can stop the flow and provide a response to the webhook trigger. This can be useful when you want to terminate the execution of the piece and send a specific response back.
|
||||
|
||||
**Example with Response:**
|
||||
|
||||
```typescript
|
||||
context.run.stop({
|
||||
response: {
|
||||
status: context.propsValue.status ?? StatusCodes.OK,
|
||||
body: context.propsValue.body,
|
||||
headers: (context.propsValue.headers as Record<string, string>) ?? {},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Example without Response:**
|
||||
|
||||
```typescript
|
||||
context.run.stop();
|
||||
```
|
||||
|
||||
## Pause Flow and Wait for Webhook
|
||||
|
||||
You can pause flow and return HTTP response, also provide a callback to URL that you can call with certain payload and continue the flow.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
ctx.run.pause({
|
||||
pauseMetadata: {
|
||||
type: PauseType.WEBHOOK,
|
||||
response: {
|
||||
callbackUrl: context.generateResumeUrl({
|
||||
queryParams: {},
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Pause Flow and Delay
|
||||
|
||||
You can pause or delay the flow until a specific timestamp. Currently, the only supported type of pause is a delay based on a future timestamp.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
ctx.run.pause({
|
||||
pauseMetadata: {
|
||||
type: PauseType.DELAY,
|
||||
resumeDateTime: futureTime.toUTCString()
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
These flow hooks give you control over the execution of the piece by allowing you to stop the flow or pause it until a certain condition is met. You can use these hooks to customize the behavior and flow of your actions.
|
||||
36
activepieces-fork/docs/build-pieces/piece-reference/i18n.mdx
Normal file
36
activepieces-fork/docs/build-pieces/piece-reference/i18n.mdx
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: 'Piece i18n'
|
||||
description: 'Learn about translating pieces to multiple locales'
|
||||
icon: 'globe'
|
||||
---
|
||||
|
||||
<Steps>
|
||||
<Step title="Generate">
|
||||
Run the following command to create a translation file with all the strings that need translation in your piece
|
||||
```bash
|
||||
npm run cli pieces generate-translation-file PIECE_FOLDER_NAME
|
||||
```
|
||||
</Step>
|
||||
<Step title="Translate">
|
||||
Make a copy of `packages/pieces/<community_or_custom>/<your_piece>/src/i18n/translation.json`, name it `<locale>.json` i.e fr.json and translate the values.
|
||||
<Tip>
|
||||
For open source pieces, you can use the [Crowdin project](https://crowdin.com/project/activepieces) to translate to different languages. These translations will automatically sync back to your code.
|
||||
</Tip>
|
||||
</Step>
|
||||
|
||||
<Step title="Test Locally">
|
||||
After following the steps to [setup your development environment](/build-pieces/building-pieces/development-setup), click the small cog icon next to the logo in your dashboard and change the locale.
|
||||
|
||||

|
||||
<br></br>
|
||||
|
||||
In the builder your piece will now appear in the translated language:
|
||||

|
||||
</Step>
|
||||
|
||||
<Step title="Publish">
|
||||
Follow the docs here to [publish your piece](/build-pieces/sharing-pieces/overview)
|
||||
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
title: "Persistent Storage"
|
||||
icon: 'database'
|
||||
description: "Learn how to store and retrieve data from a key-value store"
|
||||
---
|
||||
|
||||
The `ctx` parameter inside triggers and actions provides a simple key/value storage mechanism. The storage is persistent, meaning that the stored values are retained even after the execution of the piece.
|
||||
|
||||
By default, the storage operates at the flow level, but it can also be configured to store values at the project level.
|
||||
|
||||
<Tip>
|
||||
The storage scope is completely isolated. If a key is stored in a different scope, it will not be fetched when requested in different scope.
|
||||
</Tip>
|
||||
|
||||
## Put
|
||||
|
||||
You can store a value with a specified key in the storage.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
ctx.store.put('KEY', 'VALUE', StoreScope.PROJECT);
|
||||
```
|
||||
|
||||
## Get
|
||||
|
||||
You can retrieve the value associated with a specific key from the storage.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
const value = ctx.store.get<string>('KEY', StoreScope.PROJECT);
|
||||
```
|
||||
|
||||
## Delete
|
||||
|
||||
You can delete a key-value pair from the storage.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
ctx.store.delete('KEY', StoreScope.PROJECT);
|
||||
```
|
||||
|
||||
These storage operations allow you to store, retrieve, and delete key-value pairs in the persistent storage. You can use this storage mechanism to store and retrieve data as needed within your triggers and actions.
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: 'Piece Versioning'
|
||||
icon: 'code-compare'
|
||||
description: 'Learn how to version your pieces'
|
||||
---
|
||||
|
||||
Pieces are npm packages and follows **semantic versioning**.
|
||||
|
||||
## Semantic Versioning
|
||||
|
||||
The version number consists of three numbers: `MAJOR.MINOR.PATCH`, where:
|
||||
|
||||
- **MAJOR** It should be incremented when there are breaking changes to the piece.
|
||||
- **MINOR** It should be incremented for new features or functionality that is compatible with the previous version, unless the major version is less than 1.0, in which case it can be a breaking change.
|
||||
- **PATCH** It should be incremented for bug fixes and small changes that do not introduce new features or break backward compatibility.
|
||||
|
||||
## Engine
|
||||
The engine will use the most up-to-date compatible version for a given piece version during the **DRAFT** flow versions. Once the flow is published, all pieces will be locked to a specific version.
|
||||
|
||||
**Case 1: Piece Version is Less Than 1.0**:
|
||||
The engine will select the latest **patch** version that shares the same **minor** version number.
|
||||
|
||||
**Case 2: Piece Version Reaches Version 1.0**:
|
||||
The engine will select the latest **minor** version that shares the same **major** version number.
|
||||
|
||||
## Examples
|
||||
<Tip>
|
||||
when you make a change, remember to increment the version accordingly.
|
||||
</Tip>
|
||||
|
||||
### Breaking changes
|
||||
- Remove an existing action.
|
||||
- Add a required `action` prop.
|
||||
- Remove an existing action prop, whether required or optional.
|
||||
- Remove an attribute from an action output.
|
||||
- Change the existing behavior of an action/trigger.
|
||||
|
||||
### Non-breaking changes
|
||||
- Add a new action.
|
||||
- Add an optional `action` prop.
|
||||
- Add an attribute to an action output.
|
||||
|
||||
i.e., any removal is breaking, any required addition is breaking, everything else is not breaking.
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: "Props Validation"
|
||||
description: "Learn about different types of properties validation "
|
||||
icon: 'magnifying-glass'
|
||||
---
|
||||
|
||||
Activepieces uses Zod for runtime validation of piece properties. Zod provides a powerful schema validation system that helps ensure your piece receives valid inputs.
|
||||
|
||||
To use Zod validation in your piece, first import the validation helper and Zod:
|
||||
|
||||
<Warning>
|
||||
Please make sure the `minimumSupportedRelease` is set to at least `0.36.1` for the validation to work.
|
||||
</Warning>
|
||||
|
||||
```typescript
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { propsValidation } from '@activepieces/pieces-common';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const getIcecreamFlavor = createAction({
|
||||
name: 'get_icecream_flavor', // Unique name for the action.
|
||||
displayName: 'Get Ice Cream Flavor',
|
||||
description: 'Fetches a random ice cream flavor based on user preferences.',
|
||||
props: {
|
||||
sweetnessLevel: Property.Number({
|
||||
displayName: 'Sweetness Level',
|
||||
required: true,
|
||||
description: 'Specify the sweetness level (0 to 10).',
|
||||
}),
|
||||
includeToppings: Property.Checkbox({
|
||||
displayName: 'Include Toppings',
|
||||
required: false,
|
||||
description: 'Should the flavor include toppings?',
|
||||
defaultValue: true,
|
||||
}),
|
||||
numberOfFlavors: Property.Number({
|
||||
displayName: 'Number of Flavors',
|
||||
required: true,
|
||||
description: 'How many flavors do you want to fetch? (1-5)',
|
||||
defaultValue: 1,
|
||||
}),
|
||||
},
|
||||
async run({ propsValue }) {
|
||||
// Validate the input properties using Zod
|
||||
await propsValidation.validateZod(propsValue, {
|
||||
sweetnessLevel: z.number().min(0).max(10, 'Sweetness level must be between 0 and 10.'),
|
||||
numberOfFlavors: z.number().min(1).max(5, 'You can fetch between 1 and 5 flavors.'),
|
||||
});
|
||||
|
||||
// Action logic
|
||||
const sweetnessLevel = propsValue.sweetnessLevel;
|
||||
const includeToppings = propsValue.includeToppings ?? true; // Default to true
|
||||
const numberOfFlavors = propsValue.numberOfFlavors;
|
||||
|
||||
// Simulate fetching random ice cream flavors
|
||||
const allFlavors = [
|
||||
'Vanilla',
|
||||
'Chocolate',
|
||||
'Strawberry',
|
||||
'Mint',
|
||||
'Cookie Dough',
|
||||
'Pistachio',
|
||||
'Mango',
|
||||
'Coffee',
|
||||
'Salted Caramel',
|
||||
'Blackberry',
|
||||
];
|
||||
const selectedFlavors = allFlavors.slice(0, numberOfFlavors);
|
||||
|
||||
return {
|
||||
message: `Here are your ${numberOfFlavors} flavors: ${selectedFlavors.join(', ')}`,
|
||||
sweetnessLevel: sweetnessLevel,
|
||||
includeToppings: includeToppings,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
```
|
||||
@@ -0,0 +1,449 @@
|
||||
---
|
||||
title: 'Props'
|
||||
description: 'Learn about different types of properties used in triggers / actions'
|
||||
icon: 'input-pipe'
|
||||
---
|
||||
|
||||
Properties are used in actions and triggers to collect information from the user. They are also displayed to the user for input. Here are some commonly used properties:
|
||||
|
||||
## Basic Properties
|
||||
|
||||
These properties collect basic information from the user.
|
||||
|
||||
### Short Text
|
||||
|
||||
This property collects a short text input from the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.ShortText({
|
||||
displayName: 'Name',
|
||||
description: 'Enter your name',
|
||||
required: true,
|
||||
defaultValue: 'John Doe',
|
||||
});
|
||||
```
|
||||
|
||||
### Long Text
|
||||
|
||||
This property collects a long text input from the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.LongText({
|
||||
displayName: 'Description',
|
||||
description: 'Enter a description',
|
||||
required: false,
|
||||
});
|
||||
```
|
||||
|
||||
### Checkbox
|
||||
|
||||
This property presents a checkbox for the user to select or deselect.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.Checkbox({
|
||||
displayName: 'Agree to Terms',
|
||||
description: 'Check this box to agree to the terms',
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
});
|
||||
```
|
||||
|
||||
### Markdown
|
||||
|
||||
This property displays a markdown snippet to the user, useful for documentation or instructions. It includes a `variant` option to style the markdown, using the `MarkdownVariant` enum:
|
||||
|
||||
- **BORDERLESS**: For a minimalistic, no-border layout.
|
||||
- **INFO**: Displays informational messages.
|
||||
- **WARNING**: Alerts the user to cautionary information.
|
||||
- **TIP**: Highlights helpful tips or suggestions.
|
||||
|
||||
The default value for `variant` is **INFO**.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.MarkDown({
|
||||
value: '## This is a markdown snippet',
|
||||
variant: MarkdownVariant.WARNING,
|
||||
}),
|
||||
```
|
||||
|
||||
<Tip>
|
||||
If you want to show a webhook url to the user, use `{{ webhookUrl }}` in the
|
||||
markdown snippet.
|
||||
</Tip>
|
||||
|
||||
### DateTime
|
||||
|
||||
This property collects a date and time from the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.DateTime({
|
||||
displayName: 'Date and Time',
|
||||
description: 'Select a date and time',
|
||||
required: true,
|
||||
defaultValue: '2023-06-09T12:00:00Z',
|
||||
});
|
||||
```
|
||||
|
||||
### Number
|
||||
|
||||
This property collects a numeric input from the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.Number({
|
||||
displayName: 'Quantity',
|
||||
description: 'Enter a number',
|
||||
required: true,
|
||||
});
|
||||
```
|
||||
|
||||
### Static Dropdown
|
||||
|
||||
This property presents a dropdown menu with predefined options.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.StaticDropdown({
|
||||
displayName: 'Country',
|
||||
description: 'Select your country',
|
||||
required: true,
|
||||
options: {
|
||||
options: [
|
||||
{
|
||||
label: 'Option One',
|
||||
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: 'Option Two',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Static Multiple Dropdown
|
||||
|
||||
This property presents a dropdown menu with multiple selection options.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.StaticMultiSelectDropdown({
|
||||
displayName: 'Colors',
|
||||
description: 'Select one or more colors',
|
||||
required: true,
|
||||
options: {
|
||||
options: [
|
||||
{
|
||||
label: 'Red',
|
||||
value: 'red',
|
||||
},
|
||||
{
|
||||
label: 'Green',
|
||||
value: 'green',
|
||||
},
|
||||
{
|
||||
label: 'Blue',
|
||||
value: 'blue',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### JSON
|
||||
|
||||
This property collects JSON data from the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.Json({
|
||||
displayName: 'Data',
|
||||
description: 'Enter JSON data',
|
||||
required: true,
|
||||
defaultValue: { key: 'value' },
|
||||
});
|
||||
```
|
||||
|
||||
### Dictionary
|
||||
|
||||
This property collects key-value pairs from the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.Object({
|
||||
displayName: 'Options',
|
||||
description: 'Enter key-value pairs',
|
||||
required: true,
|
||||
defaultValue: {
|
||||
key1: 'value1',
|
||||
key2: 'value2',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### File
|
||||
|
||||
This property collects a file from the user, either by providing a URL or uploading a file.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.File({
|
||||
displayName: 'File',
|
||||
description: 'Upload a file',
|
||||
required: true,
|
||||
});
|
||||
```
|
||||
|
||||
### Array of Strings
|
||||
|
||||
This property collects an array of strings from the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.Array({
|
||||
displayName: 'Tags',
|
||||
description: 'Enter tags',
|
||||
required: false,
|
||||
defaultValue: ['tag1', 'tag2'],
|
||||
});
|
||||
```
|
||||
|
||||
### Array of Fields
|
||||
|
||||
This property collects an array of objects from the user.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.Array({
|
||||
displayName: 'Fields',
|
||||
description: 'Enter fields',
|
||||
properties: {
|
||||
fieldName: Property.ShortText({
|
||||
displayName: 'Field Name',
|
||||
required: true,
|
||||
}),
|
||||
fieldType: Property.StaticDropdown({
|
||||
displayName: 'Field Type',
|
||||
required: true,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'TEXT', value: 'TEXT' },
|
||||
{ label: 'NUMBER', value: 'NUMBER' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
});
|
||||
```
|
||||
|
||||
## Dynamic Data Properties
|
||||
|
||||
These properties provide more advanced options for collecting user input.
|
||||
|
||||
### Dropdown
|
||||
|
||||
This property allows for dynamically loaded options based on the user's input.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.Dropdown({
|
||||
displayName: 'Options',
|
||||
description: 'Select an option',
|
||||
required: true,
|
||||
auth: yourPieceAuth,
|
||||
refreshers: ['auth'],
|
||||
refreshOnSearch: false,
|
||||
options: async ({ auth }, { searchValue }) => {
|
||||
// Search value only works when refreshOnSearch is true
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
options: [
|
||||
{
|
||||
label: 'Option One',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: 'Option Two',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<Tip>
|
||||
When accessing the Piece auth, be sure to use exactly `auth` as it is
|
||||
hardcoded. However, for other properties, use their respective names.
|
||||
</Tip>
|
||||
|
||||
### Multi-Select Dropdown
|
||||
|
||||
This property allows for multiple selections from dynamically loaded options.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
Property.MultiSelectDropdown({
|
||||
displayName: 'Options',
|
||||
description: 'Select one or more options',
|
||||
required: true,
|
||||
refreshers: ['auth'],
|
||||
auth: yourPieceAuth,
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
options: [
|
||||
{
|
||||
label: 'Option One',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: 'Option Two',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
<Tip>
|
||||
When accessing the Piece auth, be sure to use exactly `auth` as it is
|
||||
hardcoded. However, for other properties, use their respective names.
|
||||
</Tip>
|
||||
|
||||
### Dynamic Properties
|
||||
|
||||
This property is used to construct forms dynamically based on API responses or user input.
|
||||
|
||||
**Example:**
|
||||
|
||||
```typescript
|
||||
|
||||
import {
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
|
||||
|
||||
Property.DynamicProperties({
|
||||
description: 'Dynamic Form',
|
||||
displayName: 'Dynamic Form',
|
||||
required: true,
|
||||
refreshers: ['auth'],
|
||||
auth: yourPieceAuth,
|
||||
props: async ({auth}) => {
|
||||
const apiEndpoint = 'https://someapi.com';
|
||||
const response = await httpClient.sendRequest<{ values: [string[]][] }>({
|
||||
method: HttpMethod.GET,
|
||||
url: apiEndpoint ,
|
||||
//you can add the auth value to the headers
|
||||
});
|
||||
|
||||
const properties = {
|
||||
prop1: Property.ShortText({
|
||||
displayName: 'Property 1',
|
||||
description: 'Enter property 1',
|
||||
required: true,
|
||||
}),
|
||||
prop2: Property.Number({
|
||||
displayName: 'Property 2',
|
||||
description: 'Enter property 2',
|
||||
required: false,
|
||||
}),
|
||||
};
|
||||
|
||||
return properties;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Custom Property (BETA)
|
||||
|
||||
<Warning>
|
||||
This feature is still in BETA and not fully released yet, please let us know if you use it and face any issues and consider it a possibility could have breaking changes in the future
|
||||
</Warning>
|
||||
This is a property that lets you inject JS code into the frontend and manipulate the DOM of this content however you like, it is extremely useful in case you are [embedding](/embedding/overview) Activepieces and want to have a way to communicate with the SaaS embedding it.
|
||||
It has a `code` property which is a function that takes in an object parameter which will have the following schema:
|
||||
|
||||
|
||||
| Parameter Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| onChange | `(value:unknown)=>void` | A callback you call to set the value of your input (only call this inside event handlers)|
|
||||
| value | `unknown` | Whatever the type of the value you pass to onChange|
|
||||
| containerId | `string` | The ID of an HTML element in which you can modify the DOM however you like |
|
||||
| isEmbedded | `boolean` | The flag that tells you if the code is running inside an [embedded instance](/embedding/overview) of Activepieces |
|
||||
| projectId | `string` | The project ID of the flow the step that contains this property is in |
|
||||
| disabled | `boolean` | The flag that tells you whether or not the property is disabled |
|
||||
| property | `{ displayName:string, description?: string, required: boolean}` | The current property information|
|
||||
|
||||
- You can return a clean up function at the end of the `code` property function to remove any listeners or HTML elements you inserted (this is important for development mode, the component gets [mounted twice](https://react.dev/reference/react/useEffect#my-effect-runs-twice-when-the-component-mounts)).
|
||||
- This function must be pure without any imports from external packages or variables outside the function scope.
|
||||
- **Must** mark your piece `minimumSupportedRelease` property to be at least `0.58.0` after introducing this property to it.
|
||||
|
||||
Here is how to define such a property:
|
||||
```typescript
|
||||
Property.Custom({
|
||||
code:(({value,onChange,containerId})=>{
|
||||
const container = document.getElementById(containerId);
|
||||
const input = document.createElement('input');
|
||||
input.classList.add(...['border','border-solid', 'border-border', 'rounded-md'])
|
||||
input.type = 'text';
|
||||
input.value = `${value}`;
|
||||
input.oninput = (e: Event) => {
|
||||
const value = (e.target as HTMLInputElement).value;
|
||||
onChange(value);
|
||||
}
|
||||
container!.appendChild(input);
|
||||
const windowCallback = (e:MessageEvent<{type:string,value:string,propertyName:string}>) => {
|
||||
if(e.data.type === 'updateInput' && e.data.propertyName === 'YOUR_PROPERTY_NAME'){
|
||||
input.value= e.data.value;
|
||||
onChange(e.data.value);
|
||||
}
|
||||
}
|
||||
window.addEventListener('message', windowCallback);
|
||||
return ()=>{
|
||||
window.removeEventListener('message', windowCallback);
|
||||
container!.removeChild(input);
|
||||
}
|
||||
}),
|
||||
displayName: 'Custom Property',
|
||||
required: true
|
||||
})
|
||||
```
|
||||
|
||||
- If you would like to know more about how to setup communication between Activepieces and the SaaS that's embedding it, check the [window postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).
|
||||
76
activepieces-fork/docs/build-pieces/piece-reference/triggers/overview.mdx
Executable file
76
activepieces-fork/docs/build-pieces/piece-reference/triggers/overview.mdx
Executable file
@@ -0,0 +1,76 @@
|
||||
---
|
||||
title: 'Overview'
|
||||
description: ''
|
||||
---
|
||||
|
||||
This tutorial explains three techniques for creating triggers:
|
||||
|
||||
- `Polling`: Periodically call endpoints to check for changes.
|
||||
- `Webhooks`: Listen to user events through a single URL.
|
||||
- `App Webhooks (Subscriptions)`: Use a developer app (using OAuth2) to receive all authorized user events at a single URL (Not Supported).
|
||||
|
||||
to create new trigger run following command,
|
||||
|
||||
```bash
|
||||
npm run cli triggers create
|
||||
```
|
||||
|
||||
1. `Piece Folder Name`: This is the name associated with the folder where the trigger resides. It helps organize and categorize triggers within the piece.
|
||||
2. `Trigger Display Name`: The name users see in the interface, conveying the trigger's purpose clearly.
|
||||
3. `Trigger Description`: A brief, informative text in the UI, guiding users about the trigger's function and purpose.
|
||||
4. `Trigger Technique`: Specifies the trigger type - either polling or webhook.
|
||||
|
||||
# Trigger Structure
|
||||
|
||||
```typescript
|
||||
export const createNewIssue = createTrigger({
|
||||
auth: PieceAuth | undefined
|
||||
name: string, // Unique name across the piece.
|
||||
displayName: string, // Display name on the interface.
|
||||
description: string, // Description for the action
|
||||
sampleData: null,
|
||||
type: TriggerStrategy.WEBHOOK | TriggerStrategy.POLLING | TriggerStrategy.APP_WEBHOOK,
|
||||
|
||||
props: {}; // Required properties from the user.
|
||||
// Run when the user enable or publish the flow.
|
||||
|
||||
onEnable: (ctx) => {},
|
||||
// Run when the user disable the flow or
|
||||
// the old flow is deleted after new one is published.
|
||||
onDisable: (ctx) => {},
|
||||
|
||||
// Trigger implementation, It takes context as parameter.
|
||||
// should returns an array of payload, each payload considered
|
||||
run: async run(ctx): unknown[] => {}
|
||||
})
|
||||
```
|
||||
|
||||
<Tip>
|
||||
It's important to note that the `run` method returns an array. The reason for
|
||||
this is that a single polling can contain multiple triggers, so each item in
|
||||
the array will trigger the flow to run.
|
||||
</Tip>
|
||||
|
||||
## Context Object
|
||||
|
||||
The Context object contains multiple helpful pieces of information and tools that can be useful while developing.
|
||||
|
||||
```typescript
|
||||
// Store: A simple, lightweight key-value store that is helpful when you are developing triggers that persist between runs, used to store information like the last polling date.
|
||||
await context.store.put('_lastFetchedDate', new Date());
|
||||
const lastFetchedData = await context.store.get('_lastFetchedDate', new Date());
|
||||
|
||||
// Webhook URL: A unique, auto-generated URL that will trigger the flow. Useful when you need to develop a trigger based on webhooks.
|
||||
context.webhookUrl;
|
||||
|
||||
// Payload: Contains information about the HTTP request sent by the third party. It has three properties: status, headers, and body.
|
||||
context.payload;
|
||||
|
||||
// PropsValue: Contains the information filled by the user in defined properties.
|
||||
context.propsValue;
|
||||
```
|
||||
|
||||
**App Webhooks (Not Supported)**
|
||||
|
||||
Certain services, such as `Slack` and `Square`, only support webhooks at the developer app level.
|
||||
This means that all authorized users for the app will be sent to the same endpoint. While this technique will be supported soon, for now, a workaround is to perform polling on the endpoint.
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
title: "Polling Trigger"
|
||||
description: "Periodically call endpoints to check for changes"
|
||||
---
|
||||
|
||||
|
||||
|
||||
The way polling triggers usually work is as follows:
|
||||
|
||||
**On Enable:**
|
||||
Store the last timestamp or most recent item id using the context store property.
|
||||
|
||||
**Run:**
|
||||
This method runs every **5 minutes**, fetches the endpoint between a certain timestamp or traverses until it finds the last item id, and returns the new items as an array.
|
||||
|
||||
**Testing:**
|
||||
You can implement a test function which should return some of the most recent items. It's recommended to limit this to five.
|
||||
|
||||
**Examples:**
|
||||
- [New Record Airtable](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/airtable/src/lib/trigger/new-record.trigger.ts)
|
||||
- [New Updated Item Salesforce](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/salesforce/src/lib/trigger/new-updated-record.ts)
|
||||
|
||||
# Polling library
|
||||
|
||||
There multiple strategy to implement polling triggers, and we have created a library to help you with that.
|
||||
|
||||
## Strategies
|
||||
|
||||
**Timebased:**
|
||||
|
||||
This strategy fetches new items using a timestamp. You need to implement the items method, which should return the most recent items.
|
||||
The library will detect new items based on the timestamp.
|
||||
|
||||
The polling object's generic type consists of the props value and the object type.
|
||||
|
||||
```typescript
|
||||
const polling: Polling<Polling<AppConnectionValueForAuthProperty<typeof auth>> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ propsValue, lastFetchEpochMS }) => {
|
||||
// Todo implement the logic to fetch the items
|
||||
const items = [ {id: 1, created_date: '2021-01-01T00:00:00Z'}, {id: 2, created_date: '2021-01-01T00:00:00Z'}];
|
||||
return items.map((item) => ({
|
||||
epochMilliSeconds: dayjs(item.created_date).valueOf(),
|
||||
data: item,
|
||||
}));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Last ID Strategy:**
|
||||
|
||||
This strategy fetches new items based on the last item ID. To use this strategy, you need to implement the items method, which should return the most recent items.
|
||||
The library will detect new items after the last item ID.
|
||||
|
||||
The polling object's generic type consists of the props value and the object type
|
||||
|
||||
```typescript
|
||||
const polling: Polling<AppConnectionValueForAuthProperty<typeof auth>, Record<string,any>> = {
|
||||
strategy: DedupeStrategy.LAST_ITEM,
|
||||
items: async ({ propsValue }) => {
|
||||
// Implement the logic to fetch the items
|
||||
const items = [{ id: 1 }, { id: 2 }];
|
||||
return items.map((item) => ({
|
||||
id: item.id,
|
||||
data: item,
|
||||
}));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Trigger Implementation
|
||||
|
||||
After implementing the polling object, you can use the polling helper to implement the trigger.
|
||||
|
||||
```typescript
|
||||
export const newTicketInView = createTrigger({
|
||||
name: 'new_ticket_in_view',
|
||||
displayName: 'New ticket in view',
|
||||
description: 'Triggers when a new ticket is created in a view',
|
||||
type: TriggerStrategy.POLLING,
|
||||
props: {
|
||||
authentication: Property.SecretText({
|
||||
displayName: 'Authentication',
|
||||
description: markdownProperty,
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
sampleData: {},
|
||||
onEnable: async (context) => {
|
||||
await pollingHelper.onEnable(polling, {
|
||||
store: context.store,
|
||||
propsValue: context.propsValue,
|
||||
auth: context.auth
|
||||
})
|
||||
},
|
||||
onDisable: async (context) => {
|
||||
await pollingHelper.onDisable(polling, {
|
||||
store: context.store,
|
||||
propsValue: context.propsValue,
|
||||
auth: context.auth
|
||||
|
||||
})
|
||||
},
|
||||
run: async (context) => {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
test: async (context) => {
|
||||
return await pollingHelper.test(polling, context);
|
||||
}
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: 'Webhook Trigger'
|
||||
description: 'Listen to user events through a single URL'
|
||||
---
|
||||
|
||||
The way webhook triggers usually work is as follows:
|
||||
|
||||
**On Enable:**
|
||||
Use `context.webhookUrl` to perform an HTTP request to register the webhook in a third-party app, and store the webhook Id in the `store`.
|
||||
|
||||
**On Handshake:**
|
||||
Some services require a successful handshake request usually consisting of some challenge. It works similar to a normal run except that you return the correct challenge response. This is optional and in order to enable the handshake you need to configure one of the available handshake strategies in the `handshakeConfiguration` option.
|
||||
|
||||
**Run:**
|
||||
You can find the HTTP body inside `context.payload.body`. If needed, alter the body; otherwise, return an array with a single item `context.payload.body`.
|
||||
|
||||
**Disable:**
|
||||
Using the `context.store`, fetch the webhook ID from the enable step and delete the webhook on the third-party app.
|
||||
|
||||
**Testing:**
|
||||
You cannot test it with Test Flow, as it uses static sample data provided in the piece.
|
||||
To test the trigger, publish the flow, perform the event. Then check the flow runs from the main dashboard.
|
||||
|
||||
**Examples:**
|
||||
|
||||
- [New Form Submission on Typeform](https://github.com/activepieces/activepieces/blob/main/packages/pieces/community/typeform/src/lib/trigger/new-submission.ts)
|
||||
|
||||
<Warning>
|
||||
To make your webhook accessible from the internet, you need to configure the backend URL. Follow these steps:
|
||||
|
||||
1. Install ngrok.
|
||||
2. Run the command `ngrok http 4200`.
|
||||
3. Replace the `AP_FRONTEND_URL` environment variable in `packages/server/api/.env` with the ngrok URL.
|
||||
4. Go to /packages/react-ui/vite.config.ts, uncomment allowedHosts and set the value to what ngrok gives you.
|
||||
|
||||
Once you have completed these configurations, you are good to go!
|
||||
|
||||
</Warning>
|
||||
Reference in New Issue
Block a user