Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/pieces/community/google-contacts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@activepieces/piece-google-contacts",
"version": "0.4.8",
"version": "0.4.9",
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '@activepieces/pieces-common';
import { googleContactsCommon } from '../common';
import { googleContactsAuth } from '../auth';
import { addContactOutputSchema } from '../output-schemas';

export const googleContactsAddContactAction = createAction({
auth: googleContactsAuth,
Expand All @@ -15,6 +16,7 @@ export const googleContactsAddContactAction = createAction({
audience: 'both',
aiMetadata: { description: 'Creates a new person in the authenticated Google Contacts account from name, email, phone, company, and job title fields. Use when an agent needs to add someone to address book contacts; first and last name are required. Not idempotent — each call creates a separate contact even with identical input, so guard against duplicates.', idempotent: false },
displayName: 'Add Contact',
outputSchema: addContactOutputSchema,
props: {
firstName: Property.ShortText({
displayName: 'First Name',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@activepieces/pieces-common';
import { googleContactsCommon } from '../common';
import { googleContactsAuth } from '../auth';
import { searchContactsOutputSchema } from '../output-schemas';

export const googleContactsSearchContactsAction = createAction({
auth: googleContactsAuth,
Expand All @@ -16,6 +17,7 @@ export const googleContactsSearchContactsAction = createAction({
audience: 'both',
aiMetadata: { description: 'Searches the authenticated Google Contacts account for people matching a plain-text query, which prefix-matches against contact fields. Use when an agent needs to find a contact or its resourceName before updating it; the query is required and a read mask selects which fields come back (max 30 results). Read-only and idempotent.', idempotent: true },
displayName: 'Search Contacts',
outputSchema: searchContactsOutputSchema,
props: {
query: Property.ShortText({
displayName: 'Query',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '@activepieces/pieces-common';
import { googleContactsCommon } from '../common';
import { googleContactsAuth } from '../auth';
import { updateContactOutputSchema } from '../output-schemas';

export const googleContactsUpdateContactAction = createAction({
auth: googleContactsAuth,
Expand All @@ -19,6 +20,7 @@ export const googleContactsUpdateContactAction = createAction({
audience: 'both',
aiMetadata: { description: 'Updates fields (name, email, phone, company, job title) on an existing Google Contacts person identified by its resourceName (people/{id}). Use when an agent needs to modify a known contact; requires both the resourceName and the current etag, and a field mask listing which fields to update — the call is rejected if the contact changed since the etag was fetched. Repeating the same update with the same etag/values is safe and yields the same result.', idempotent: true },
displayName: 'Update Contact',
outputSchema: updateContactOutputSchema,
props: {
resourceName: Property.ShortText({
displayName: 'Resource Name',
Expand Down
134 changes: 134 additions & 0 deletions packages/pieces/community/google-contacts/src/lib/output-schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { OutputSchema } from '@activepieces/pieces-framework';

// Google People returns every contact detail as a repeated field: a contact can hold several
// names, emails, phone numbers and organizations, each carrying its own `metadata`. Only the
// `metadata.primary` flag is surfaced from that block -- the rest (source ids, per-field etags)
// is server bookkeeping that says nothing about the contact.
const nameFields: OutputSchema['fields'] = [
{ key: 'displayName', label: 'Display Name' },
{ key: 'givenName', label: 'First Name' },
{ key: 'middleName', label: 'Middle Name' },
{ key: 'familyName', label: 'Last Name' },
{ key: 'displayNameLastFirst', label: 'Display Name (Last, First)' },
{ key: 'unstructuredName', label: 'Unstructured Name' },
{ key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' },
];

const emailFields: OutputSchema['fields'] = [
{ key: 'value', label: 'Email Address', format: 'email' },
{ key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' },
];

const phoneFields: OutputSchema['fields'] = [
{ key: 'value', label: 'Phone Number' },
// Google normalises to E.164 here; the raw `value` keeps whatever the user typed.
{ key: 'canonicalForm', label: 'Canonical Form' },
{ key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' },
];

const organizationFields: OutputSchema['fields'] = [
{ key: 'name', label: 'Company' },
{ key: 'title', label: 'Job Title' },
{ key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' },
];

const photoFields: OutputSchema['fields'] = [
{ key: 'url', label: 'Photo URL', format: 'image' },
// True when this is Google's generated placeholder rather than a photo the contact has.
{ key: 'default', label: 'Is Placeholder', format: 'boolean' },
{ key: 'primary', label: 'Primary', value: 'metadata.primary', format: 'boolean' },
];

const membershipFields: OutputSchema['fields'] = [
{
key: 'contactGroupId',
label: 'Contact Group ID',
value: 'contactGroupMembership.contactGroupId',
},
{
key: 'contactGroupResourceName',
label: 'Contact Group Resource Name',
value: 'contactGroupMembership.contactGroupResourceName',
},
];

const metadataFields: OutputSchema['fields'] = [
{ key: 'objectType', label: 'Object Type' },
{
key: 'sources',
label: 'Sources',
labelKey: 'updateTime',
listItems: [
{ key: 'type', label: 'Source Type' },
{ key: 'id', label: 'Source ID' },
// The only timestamp on a contact, and what the polling trigger orders and dedupes on.
{ key: 'updateTime', label: 'Last Updated', format: 'datetime' },
],
},
];

// One person record. Shared by every action and the trigger, because People returns the same
// shape whether the contact was just created, just updated, matched by a search, or polled.
const personFields: OutputSchema['fields'] = [
// `people/{person_id}` -- the id Update Contact requires.
{ key: 'resourceName', label: 'Resource Name' },
// Required alongside resourceName on update; the call is rejected if the contact moved on.
{ key: 'etag', label: 'ETag' },
{ key: 'names', label: 'Names', labelKey: 'displayName', listItems: nameFields },
{
key: 'emailAddresses',
label: 'Email Addresses',
labelKey: 'value',
listItems: emailFields,
},
{
key: 'phoneNumbers',
label: 'Phone Numbers',
labelKey: 'value',
listItems: phoneFields,
},
{
key: 'organizations',
label: 'Organizations',
labelKey: 'name',
listItems: organizationFields,
},
{ key: 'photos', label: 'Photos', labelKey: 'url', listItems: photoFields },
{
key: 'memberships',
label: 'Group Memberships',
// labelKey resolves by dot path against the raw item, so it has to name the nested
// property rather than the flattened key used in membershipFields.
labelKey: 'contactGroupMembership.contactGroupId',
listItems: membershipFields,
},
{ key: 'metadata', label: 'Metadata', children: metadataFields },
];

export const addContactOutputSchema: OutputSchema = {
fields: personFields,
};

export const updateContactOutputSchema: OutputSchema = {
fields: personFields,
};

// searchContacts wraps each hit as `{ person: {...} }` under a `results` array, so the person
// fields sit one level deeper than they do on the other three steps. Which of them are actually
// populated follows the caller's Read Mask -- the default is names and email addresses only.
export const searchContactsOutputSchema: OutputSchema = {
fields: [
{
key: 'results',
label: 'Matching Contacts',
listItems: [
{ key: 'person', label: 'Contact', children: personFields },
],
},
],
};

// The polling trigger emits one person per run, so its payload is a person record directly.
export const newOrUpdatedContactOutputSchema: OutputSchema = {
fields: personFields,
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
pollingHelper,
} from '@activepieces/pieces-common';
import { googleContactsAuth } from '../auth';
import { newOrUpdatedContactOutputSchema } from '../output-schemas';
import { people as googlePeople } from '@googleapis/people';
import { OAuth2Client } from 'google-auth-library';
import dayjs from 'dayjs';
Expand Down Expand Up @@ -91,6 +92,7 @@ export const googleContactNewOrUpdatedContact = createTrigger({
description: 'Fires when a contact in the authenticated Google Contacts account is newly created or has been modified since the last poll. Each event represents one person record with its current fields; deleted contacts are excluded.',
},
props: {},
outputSchema: newOrUpdatedContactOutputSchema,
sampleData: {
resourceName: 'people/c4278485694217203807',
etag: '%EiMBAgMFBgcICQoLDA0ODxATFBUWGSEiIyQlJicuNDU3PT4/QBoEAQIFByIMZFVwNlJPNEVKUzg9',
Expand Down
2 changes: 1 addition & 1 deletion packages/pieces/community/imap/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@activepieces/piece-imap",
"version": "0.4.7",
"version": "0.4.8",
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"dependencies": {
Expand Down
2 changes: 2 additions & 0 deletions packages/pieces/community/imap/src/lib/actions/copy-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
mailboxDropdown,
copyEmail as copyImapEmail,
} from '../common';
import { copyEmailActionOutputSchema } from '../output-schemas';

const props = {
sourceMailbox: mailboxDropdown({
Expand All @@ -28,6 +29,7 @@ export const copyEmail = createAction({
name: 'copy_email',
displayName: 'Copy Email',
description: 'Copy an email to another mailbox',
outputSchema: copyEmailActionOutputSchema,
audience: 'both',
aiMetadata: { description: 'Copies an email (by message UID) from a source IMAP folder into a target folder, leaving the original in place. Use to duplicate a message into another mailbox without removing it from the source. Requires the source folder, target folder, and the UID; not idempotent since each call appends another copy with a new UID.', idempotent: false },
props,
Expand Down
2 changes: 2 additions & 0 deletions packages/pieces/community/imap/src/lib/actions/move-email.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { imapAuth, mailboxDropdown, moveEmail as moveImapEmail } from '../common';
import { moveEmailActionOutputSchema } from '../output-schemas';

const props = {
sourceMailbox: mailboxDropdown({
Expand All @@ -24,6 +25,7 @@ export const moveEmail = createAction({
name: 'move_email',
displayName: 'Move Email',
description: 'Move an email to another mailbox',
outputSchema: moveEmailActionOutputSchema,
audience: 'both',
aiMetadata: { description: 'Moves an email (by message UID) from a source IMAP folder to a target folder, removing it from the source. Use to file, archive, or send a message to a Trash folder. Requires the source folder, target folder, and the UID; not idempotent since after the move the message no longer exists at the source UID and a repeat call cannot find it.', idempotent: false },
props,
Expand Down
69 changes: 69 additions & 0 deletions packages/pieces/community/imap/src/lib/output-schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { OutputSchema } from '@activepieces/pieces-framework';

const addressFields: OutputSchema['fields'] = [
{ key: 'text', label: 'Address Text' },
{
key: 'value',
label: 'Addresses',
labelKey: 'address',
listItems: [
{ key: 'address', label: 'Email', format: 'email' },
{ key: 'name', label: 'Name' },
],
},
];

export const newEmailTriggerOutputSchema: OutputSchema = {
fields: [
{ key: 'subject', label: 'Subject' },
{ key: 'date', label: 'Date', format: 'datetime' },
{ key: 'from', label: 'From', children: addressFields },
{ key: 'to', label: 'To', children: addressFields },
{ key: 'replyTo', label: 'Reply To', children: addressFields },
{ key: 'text', label: 'Text Body' },
{ key: 'html', label: 'HTML Body', format: 'html' },
{ key: 'textAsHtml', label: 'Text Body as HTML', format: 'html' },
{ key: 'messageId', label: 'Message ID' },
{ key: 'uid', label: 'Message UID', format: 'number' },
{
key: 'attachments',
label: 'Attachments',
description:
'Stored attachment references, one per attachment — each a signed download URL produced by writing the attachment to file storage. Pass an entry straight to an action that accepts a file or a URL; the original filename and content type are not carried here.',
},
{
key: 'headerLines',
label: 'Header Lines',
labelKey: 'key',
listItems: [
{ key: 'key', label: 'Header Name' },
{ key: 'line', label: 'Raw Header Line' },
],
},
],
};

export const copyEmailActionOutputSchema: OutputSchema = {
fields: [
{ key: 'success', label: 'Success', format: 'boolean' },
{
key: 'newUid',
label: 'New Message UID',
format: 'number',
description: 'UID of the copy in the target folder.',
},
],
};

export const moveEmailActionOutputSchema: OutputSchema = {
fields: [
{ key: 'success', label: 'Success', format: 'boolean' },
{
key: 'newUid',
label: 'New Message UID',
format: 'number',
description:
'UID of the message in the target folder. Absent when the server reports no new UID, in which case Success is false.',
},
],
};
2 changes: 2 additions & 0 deletions packages/pieces/community/imap/src/lib/triggers/new-email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
mailboxDropdown,
fetchEmails,
} from '../common';
import { newEmailTriggerOutputSchema } from '../output-schemas';

const filterInstructions = `
**Emails Filtering:**
Expand Down Expand Up @@ -73,6 +74,7 @@ export const newEmail = createTrigger({
name: 'new_email',
displayName: 'New Email',
description: 'Trigger when a new email is received',
outputSchema: newEmailTriggerOutputSchema,
aiMetadata: {
description: 'Fires when a new email arrives in the selected IMAP mailbox folder. Polls the folder on an interval and emits one event per newly received message, including its parsed content and any attachments. Represents an inbound email landing in that mailbox.',
},
Expand Down
2 changes: 1 addition & 1 deletion packages/pieces/community/resend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@activepieces/piece-resend",
"version": "0.4.4",
"version": "0.4.5",
"main": "./dist/src/index.js",
"types": "./dist/src/index.d.ts",
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common';
import { resendAuth } from '../..';
import { cancelScheduledEmailOutputSchema } from '../output-schemas';

export const cancelScheduledEmail = createAction({
name: 'cancel_scheduled_email',
auth: resendAuth,
displayName: 'Cancel Scheduled Email',
outputSchema: cancelScheduledEmailOutputSchema,
description: 'Cancel a scheduled email before it is sent',
audience: 'both',
aiMetadata: { description: 'Cancels a previously scheduled email so it will not be delivered, identified by its Resend email ID. Use this to stop a future send before its scheduled time. Effectively idempotent — once an email is cancelled, repeating the call has no further effect; only works while the email is still pending.', idempotent: true },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common';
import { resendAuth } from '../..';
import { createAudienceOutputSchema } from '../output-schemas';

export const createAudience = createAction({
name: 'create_audience',
auth: resendAuth,
displayName: 'Create Audience',
outputSchema: createAudienceOutputSchema,
description: 'Create a new contact audience in Resend',
audience: 'both',
aiMetadata: { description: 'Creates a new contact audience (mailing list) in Resend with the given name and returns its ID. Use this before adding contacts or sending broadcasts when no suitable audience exists yet. Not idempotent — each call creates a new audience even if the name matches an existing one.', idempotent: false },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { createBroadcastOutputSchema } from '../output-schemas';
import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common';
import { resendAuth } from '../..';
import { resendProps } from '../common/props';
Expand All @@ -7,6 +8,7 @@ export const createBroadcast = createAction({
name: 'create_broadcast',
auth: resendAuth,
displayName: 'Create Broadcast',
outputSchema: createBroadcastOutputSchema,
description: 'Create a new broadcast email to send to an audience',
audience: 'both',
aiMetadata: { description: 'Creates a draft broadcast email (subject and body) targeting a Resend audience and returns its ID. Use this to prepare a campaign; it only creates the draft and does not deliver anything — pair it with Send Broadcast to actually send. Requires the audience ID and a sender on a verified domain. Not idempotent — each call creates a new broadcast.', idempotent: false },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { createAction, Property } from '@activepieces/pieces-framework';
import { AuthenticationType, HttpMethod, httpClient } from '@activepieces/pieces-common';
import { resendAuth } from '../..';
import { resendProps } from '../common/props';
import { createContactOutputSchema } from '../output-schemas';

export const createContact = createAction({
name: 'create_contact',
auth: resendAuth,
displayName: 'Create Contact',
outputSchema: createContactOutputSchema,
description: 'Add a contact to a Resend audience',
audience: 'both',
aiMetadata: { description: 'Adds a new contact (email plus optional name and subscription status) to a specific Resend audience. Use this to grow a mailing list; requires the target audience ID (use List Audiences to find it). Not idempotent — each call creates a new contact, so guard against re-adding the same email.', idempotent: false },
Expand Down
Loading
Loading