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,50 @@
|
||||
import { gristAuth } from '../..';
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { commonProps } from '../common/props';
|
||||
import { GristAPIClient, transformTableColumnValues } from '../common/helpers';
|
||||
|
||||
export const gristCreateRecordAction = createAction({
|
||||
auth: gristAuth,
|
||||
name: 'grist-create-record',
|
||||
displayName: 'Create Record',
|
||||
description: 'Creates a new record in specific table.',
|
||||
props: {
|
||||
workspace_id: commonProps.workspace_id,
|
||||
document_id: commonProps.document_id,
|
||||
table_id: commonProps.table_id,
|
||||
table_columns: commonProps.table_columns,
|
||||
},
|
||||
async run(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const tableId = context.propsValue.table_id;
|
||||
const tableColumnValues = context.propsValue.table_columns;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
|
||||
const tableColumnSchema = await client.listTableColumns(
|
||||
documentId as string,
|
||||
tableId as string
|
||||
);
|
||||
|
||||
const transformedColumnValues = transformTableColumnValues({
|
||||
tableColumnSchema,
|
||||
tableColumnValues,
|
||||
});
|
||||
|
||||
const createRecordResponse = await client.addRecordsToTable(
|
||||
documentId,
|
||||
tableId,
|
||||
{
|
||||
records: [{ fields: transformedColumnValues }],
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
id: createRecordResponse.records[0].id,
|
||||
fields: transformedColumnValues,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { gristAuth } from '../..';
|
||||
import {
|
||||
createAction,
|
||||
DropdownOption,
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { commonProps } from '../common/props';
|
||||
import { GristAPIClient } from '../common/helpers';
|
||||
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const gristSearchRecordAction = createAction({
|
||||
auth: gristAuth,
|
||||
name: 'grist-search-record',
|
||||
displayName: 'Search Record',
|
||||
description: 'Search record by matching criteria.',
|
||||
props: {
|
||||
workspace_id: commonProps.workspace_id,
|
||||
document_id: commonProps.document_id,
|
||||
table_id: commonProps.table_id,
|
||||
column: Property.Dropdown({
|
||||
displayName: 'Column',
|
||||
auth: gristAuth,
|
||||
refreshers: ['document_id', 'table_id'],
|
||||
required: true,
|
||||
options: async ({ auth, document_id, table_id }) => {
|
||||
if (!auth || !document_id || !table_id) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Please connect account and select document.',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const authValue = auth.props;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: authValue.domain,
|
||||
apiKey: authValue.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.listTableColumns(
|
||||
document_id as string,
|
||||
table_id as string
|
||||
);
|
||||
|
||||
const options: DropdownOption<string>[] = [];
|
||||
for (const column of response.columns) {
|
||||
options.push({ value: column.id, label: column.fields.label });
|
||||
}
|
||||
return {
|
||||
disabled: false,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}),
|
||||
value: Property.ShortText({
|
||||
displayName: 'Value',
|
||||
required: true,
|
||||
description:
|
||||
'Enter the search text. The search operation is case sensitive, exact text match, and supports column-type Text only.',
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const tableId = context.propsValue.table_id;
|
||||
const columnName = context.propsValue.column;
|
||||
const columnValue = context.propsValue.value;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
|
||||
const encodedQuery = encodeURIComponent(
|
||||
JSON.stringify({
|
||||
[columnName]: [columnValue],
|
||||
})
|
||||
);
|
||||
|
||||
return await client.makeRequest(
|
||||
HttpMethod.GET,
|
||||
`/docs/${documentId}/tables/${tableId}/records?filter=${encodedQuery}`
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import { gristAuth } from '../..';
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { commonProps } from '../common/props';
|
||||
import { GristAPIClient, transformTableColumnValues } from '../common/helpers';
|
||||
|
||||
export const gristUpdateRecordAction = createAction({
|
||||
auth: gristAuth,
|
||||
name: 'grist-update-record',
|
||||
displayName: 'Update Record',
|
||||
description: 'Updates an existing record in specific table.',
|
||||
props: {
|
||||
workspace_id: commonProps.workspace_id,
|
||||
document_id: commonProps.document_id,
|
||||
table_id: commonProps.table_id,
|
||||
record_id: Property.Number({
|
||||
displayName: 'Record ID',
|
||||
required: true,
|
||||
}),
|
||||
table_columns: commonProps.table_columns,
|
||||
},
|
||||
async run(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const tableId = context.propsValue.table_id;
|
||||
const recordId = context.propsValue.record_id;
|
||||
const tableColumnValues = context.propsValue.table_columns;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
|
||||
const tableColumnSchema = await client.listTableColumns(
|
||||
documentId as string,
|
||||
tableId as string
|
||||
);
|
||||
|
||||
const transformedColumnValues = transformTableColumnValues({
|
||||
tableColumnSchema,
|
||||
tableColumnValues,
|
||||
});
|
||||
|
||||
const updateRecordResponse = await client.updateRcordsInTable(
|
||||
documentId,
|
||||
tableId,
|
||||
{
|
||||
records: [{ id: recordId, fields: transformedColumnValues }],
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
id: recordId,
|
||||
fields: transformedColumnValues,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
import { gristAuth } from '../..';
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { commonProps } from '../common/props';
|
||||
import FormData from 'form-data';
|
||||
import {
|
||||
AuthenticationType,
|
||||
httpClient,
|
||||
HttpMethod,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { GristAPIClient } from '../common/helpers';
|
||||
|
||||
export const gristUploadAttachmentsToDocumnetAction = createAction({
|
||||
auth: gristAuth,
|
||||
name: 'grist-upload-attachments-to-document',
|
||||
displayName: 'Upload Attachment to Document',
|
||||
description: 'Uploads attachments to specific document.',
|
||||
props: {
|
||||
workspace_id: commonProps.workspace_id,
|
||||
document_id: commonProps.document_id,
|
||||
attachment: Property.File({
|
||||
displayName: 'Attachment',
|
||||
required: true,
|
||||
}),
|
||||
attachment_name: Property.ShortText({
|
||||
displayName: 'Attachment Name',
|
||||
description: 'In case you want to change the name of the attachment.',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const attachment = context.propsValue.attachment;
|
||||
const attachmentName = context.propsValue.attachment_name;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append(
|
||||
'upload',
|
||||
Buffer.from(attachment.base64, 'base64'),
|
||||
attachmentName || attachment.filename
|
||||
);
|
||||
|
||||
const response = await httpClient.sendRequest<Array<number>>({
|
||||
method: HttpMethod.POST,
|
||||
url: context.auth.props.domain + '/api' + `/docs/${documentId}/attachments`,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token: context.auth.props.apiKey,
|
||||
},
|
||||
headers: { ...formData.getHeaders() },
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const attachmentId = response.body[0];
|
||||
|
||||
const attachmentMetadata = await client.getDocumentAttachmentMetadata(
|
||||
documentId,
|
||||
attachmentId
|
||||
);
|
||||
|
||||
return {
|
||||
id: attachmentId,
|
||||
fields: attachmentMetadata,
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,219 @@
|
||||
import { DynamicPropsValue } from '@activepieces/pieces-framework';
|
||||
import {
|
||||
GristAPIClientOptions,
|
||||
GristCreateRecordsRequest,
|
||||
GristCreateRecordsResponse,
|
||||
GristCreateWebhookRequest,
|
||||
GristCreateWebhookResponse,
|
||||
GristListRecordsResponse,
|
||||
GristOrgResponse,
|
||||
GristTableColumnsResponse,
|
||||
GristTableResponse,
|
||||
GristUpdateRecordsRequest,
|
||||
GristWorkspaceResponse,
|
||||
} from './types';
|
||||
import {
|
||||
AuthenticationType,
|
||||
httpClient,
|
||||
HttpHeaders,
|
||||
HttpMessageBody,
|
||||
HttpMethod,
|
||||
HttpRequest,
|
||||
QueryParams,
|
||||
} from '@activepieces/pieces-common';
|
||||
|
||||
type Query = Record<string, string | number | string[] | undefined>;
|
||||
|
||||
export class GristAPIClient {
|
||||
#domaiUrl: string;
|
||||
#apiKey: string;
|
||||
constructor(options: GristAPIClientOptions) {
|
||||
this.#domaiUrl = options.domainUrl;
|
||||
this.#apiKey = options.apiKey;
|
||||
}
|
||||
|
||||
async makeRequest<T extends HttpMessageBody>(
|
||||
method: HttpMethod,
|
||||
resourceUri: string,
|
||||
headers?: HttpHeaders,
|
||||
query?: Query,
|
||||
body: any | undefined = undefined
|
||||
): Promise<T> {
|
||||
const baseUrl = this.#domaiUrl.replace(/\/$/, '');
|
||||
const params: QueryParams = {};
|
||||
if (query) {
|
||||
for (const [key, value] of Object.entries(query)) {
|
||||
if (value !== null && value !== undefined) {
|
||||
params[key] = String(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
const request: HttpRequest = {
|
||||
method: method,
|
||||
url: baseUrl + '/api' + resourceUri,
|
||||
authentication: {
|
||||
type: AuthenticationType.BEARER_TOKEN,
|
||||
token: this.#apiKey,
|
||||
},
|
||||
headers,
|
||||
queryParams: params,
|
||||
body: body,
|
||||
};
|
||||
const response = await httpClient.sendRequest<T>(request);
|
||||
return response.body;
|
||||
}
|
||||
|
||||
async listOrgs() {
|
||||
return await this.makeRequest<GristOrgResponse[]>(
|
||||
HttpMethod.GET,
|
||||
`/orgs`
|
||||
);
|
||||
}
|
||||
|
||||
async listWorkspaces(orgId: string) {
|
||||
return await this.makeRequest<GristWorkspaceResponse[]>(
|
||||
HttpMethod.GET,
|
||||
`/orgs/${orgId}/workspaces`
|
||||
);
|
||||
}
|
||||
|
||||
async getWorkspace(workspaceId: number) {
|
||||
return await this.makeRequest<GristWorkspaceResponse>(
|
||||
HttpMethod.GET,
|
||||
`/workspaces/${workspaceId}`
|
||||
);
|
||||
}
|
||||
|
||||
async listDocumentTables(docId: string) {
|
||||
return await this.makeRequest<{ tables: GristTableResponse[] }>(
|
||||
HttpMethod.GET,
|
||||
`/docs/${docId}/tables`
|
||||
);
|
||||
}
|
||||
|
||||
async listTableColumns(docId: string, tableId: string) {
|
||||
return await this.makeRequest<{ columns: GristTableColumnsResponse[] }>(
|
||||
HttpMethod.GET,
|
||||
`/docs/${docId}/tables/${tableId}/columns`
|
||||
);
|
||||
}
|
||||
|
||||
async addRecordsToTable(
|
||||
docId: string,
|
||||
tableId: string,
|
||||
request: GristCreateRecordsRequest
|
||||
) {
|
||||
return await this.makeRequest<GristCreateRecordsResponse>(
|
||||
HttpMethod.POST,
|
||||
`/docs/${docId}/tables/${tableId}/records`,
|
||||
undefined,
|
||||
{},
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
async updateRcordsInTable(
|
||||
docId: string,
|
||||
tableId: string,
|
||||
request: GristUpdateRecordsRequest
|
||||
) {
|
||||
return await this.makeRequest<GristCreateRecordsResponse>(
|
||||
HttpMethod.PATCH,
|
||||
`/docs/${docId}/tables/${tableId}/records`,
|
||||
undefined,
|
||||
{},
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
async listRecordsFromTable(docId: string, tableId: string, query: Query) {
|
||||
return await this.makeRequest<GristListRecordsResponse>(
|
||||
HttpMethod.GET,
|
||||
`/docs/${docId}/tables/${tableId}/records`,
|
||||
undefined,
|
||||
query
|
||||
);
|
||||
}
|
||||
|
||||
async listDocumentAttachments(docId: string, query: Query) {
|
||||
return await this.makeRequest<GristListRecordsResponse>(
|
||||
HttpMethod.GET,
|
||||
`/docs/${docId}/attachments`,
|
||||
{},
|
||||
query
|
||||
);
|
||||
}
|
||||
|
||||
async getDocumentAttachmentMetadata(docId: string, attachmentId: number) {
|
||||
return await this.makeRequest<GristListRecordsResponse>(
|
||||
HttpMethod.GET,
|
||||
`/docs/${docId}/attachments/${attachmentId}`
|
||||
);
|
||||
}
|
||||
|
||||
async createDocumentWebhook(
|
||||
docId: string,
|
||||
request: GristCreateWebhookRequest
|
||||
) {
|
||||
return await this.makeRequest<GristCreateWebhookResponse>(
|
||||
HttpMethod.POST,
|
||||
`/docs/${docId}/webhooks`,
|
||||
undefined,
|
||||
{},
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
async deleteDocumentWebhook(docId: string, webhookId: number) {
|
||||
return await this.makeRequest(
|
||||
HttpMethod.DELETE,
|
||||
`/docs/${docId}/webhooks/${webhookId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function transformTableColumnValues({
|
||||
tableColumnSchema,
|
||||
tableColumnValues,
|
||||
}: {
|
||||
tableColumnSchema: { columns: GristTableColumnsResponse[] };
|
||||
tableColumnValues: DynamicPropsValue;
|
||||
}): DynamicPropsValue {
|
||||
const fields: DynamicPropsValue = {};
|
||||
|
||||
for (const column of tableColumnSchema.columns) {
|
||||
const columnId = column.id;
|
||||
const columnType = column.fields.type;
|
||||
const columnValue = tableColumnValues[columnId];
|
||||
|
||||
if (columnValue !== undefined && columnValue !== '') {
|
||||
if (columnType === 'Attachments') {
|
||||
const attachmentsArray = Array.isArray(columnValue)
|
||||
? columnValue
|
||||
: JSON.parse(columnValue);
|
||||
if (Array.isArray(attachmentsArray) && attachmentsArray.length > 0) {
|
||||
fields[columnId] = ['L', ...attachmentsArray.map(Number)];
|
||||
}
|
||||
} else if (columnType === 'ChoiceList') {
|
||||
if (Array.isArray(columnValue) && columnValue.length > 0) {
|
||||
fields[columnId] = ['L', ...columnValue];
|
||||
}
|
||||
} else if (columnType === 'Int' || columnType === 'Numeric') {
|
||||
fields[columnId] = Number(columnValue);
|
||||
} else if (columnType.startsWith('RefList')) {
|
||||
const refListArray = Array.isArray(columnValue)
|
||||
? columnValue
|
||||
: JSON.parse(columnValue);
|
||||
if (Array.isArray(refListArray) && refListArray.length > 0) {
|
||||
fields[columnId] = ['L', ...refListArray.map(Number)];
|
||||
}
|
||||
} else if (columnType.startsWith('Ref')) {
|
||||
fields[columnId] = Number(columnValue);
|
||||
} else {
|
||||
fields[columnId] = columnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
import {
|
||||
DropdownOption,
|
||||
DynamicPropsValue,
|
||||
PiecePropValueSchema,
|
||||
Property,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { gristAuth } from '../..';
|
||||
import { GristAPIClient } from './helpers';
|
||||
|
||||
export const commonProps = {
|
||||
workspace_id: Property.Dropdown({
|
||||
displayName: 'Workspace',
|
||||
refreshers: [],
|
||||
required: true,
|
||||
auth: gristAuth,
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Please connect account first.',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const authValue = auth.props;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: authValue.domain,
|
||||
apiKey: authValue.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.listWorkspaces('current');
|
||||
|
||||
const options: DropdownOption<number>[] = [];
|
||||
for (const workspace of response) {
|
||||
options.push({ label: workspace.name, value: workspace.id });
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}),
|
||||
document_id: Property.Dropdown({
|
||||
displayName: 'Document',
|
||||
refreshers: ['workspace_id'],
|
||||
required: true,
|
||||
auth: gristAuth,
|
||||
options: async ({ auth, workspace_id }) => {
|
||||
if (!auth || !workspace_id) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Please connect account and select workspace.',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const authValue = auth.props;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: authValue.domain,
|
||||
apiKey: authValue.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.getWorkspace(
|
||||
workspace_id as unknown as number
|
||||
);
|
||||
|
||||
const options: DropdownOption<string>[] = [];
|
||||
for (const document of response.docs) {
|
||||
options.push({ label: document.name, value: document.id });
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}),
|
||||
table_id: Property.Dropdown({
|
||||
displayName: 'Table',
|
||||
refreshers: ['document_id'],
|
||||
required: true,
|
||||
auth: gristAuth,
|
||||
options: async ({ auth, document_id }) => {
|
||||
if (!auth || !document_id) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Please connect account and select document.',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const authValue = auth.props;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: authValue.domain,
|
||||
apiKey: authValue.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.listDocumentTables(
|
||||
document_id as unknown as string
|
||||
);
|
||||
|
||||
const options: DropdownOption<string>[] = [];
|
||||
for (const table of response.tables) {
|
||||
options.push({ label: table.id, value: table.id });
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}),
|
||||
table_columns: Property.DynamicProperties({
|
||||
displayName: 'Table Columns',
|
||||
refreshers: ['document_id', 'table_id'],
|
||||
required: true,
|
||||
auth: gristAuth,
|
||||
props: async ({ auth, document_id, table_id }) => {
|
||||
if (!auth) return {};
|
||||
if (!document_id) return {};
|
||||
if (!table_id) return {};
|
||||
|
||||
const fields: DynamicPropsValue = {};
|
||||
|
||||
const authValue = auth.props;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: authValue.domain,
|
||||
apiKey: authValue.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.listTableColumns(
|
||||
document_id as unknown as string,
|
||||
table_id as unknown as string
|
||||
);
|
||||
|
||||
for (const column of response.columns) {
|
||||
if (!column.fields.isFormula) {
|
||||
switch (column.fields.type) {
|
||||
case 'Any':
|
||||
fields[column.id] = Property.ShortText({
|
||||
displayName: column.fields.label || column.id,
|
||||
required: false,
|
||||
});
|
||||
break;
|
||||
case 'Attachments':
|
||||
fields[column.id] = Property.Array({
|
||||
displayName: column.fields.label || column.id,
|
||||
description: `Use the **Upload Attachments to Document** action and provide the attachment ID from the response.`,
|
||||
required: false,
|
||||
});
|
||||
break;
|
||||
case 'Bool':
|
||||
fields[column.id] = Property.Checkbox({
|
||||
displayName: column.fields.label || column.id,
|
||||
required: false,
|
||||
});
|
||||
break;
|
||||
case 'Choice':
|
||||
case 'ChoiceList': {
|
||||
let options = [];
|
||||
try {
|
||||
const optionsObject = JSON.parse(column.fields.widgetOptions);
|
||||
options = optionsObject['choices'] as any[];
|
||||
} catch (error) {
|
||||
options = [];
|
||||
}
|
||||
|
||||
const dropdownConfig = {
|
||||
displayName: column.fields.label || column.id,
|
||||
required: false,
|
||||
options: {
|
||||
disabled: false,
|
||||
options: options.map((choice) => {
|
||||
return {
|
||||
label: choice,
|
||||
value: choice,
|
||||
};
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
fields[column.id] =
|
||||
column.fields.type === 'Choice'
|
||||
? Property.StaticDropdown(dropdownConfig)
|
||||
: Property.StaticMultiSelectDropdown(dropdownConfig);
|
||||
break;
|
||||
}
|
||||
case 'Date':
|
||||
fields[column.id] = Property.DateTime({
|
||||
displayName: column.fields.label || column.id,
|
||||
required: false,
|
||||
});
|
||||
break;
|
||||
case 'Int':
|
||||
case 'Numeric':
|
||||
fields[column.id] = Property.Number({
|
||||
displayName: column.fields.label || column.id,
|
||||
required: false,
|
||||
});
|
||||
break;
|
||||
case 'Text':
|
||||
fields[column.id] = Property.LongText({
|
||||
displayName: column.fields.label || column.id,
|
||||
required: false,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
if (column.fields.type.startsWith('DateTime')) {
|
||||
fields[column.id] = Property.DateTime({
|
||||
displayName: column.fields.label || column.id,
|
||||
required: false,
|
||||
});
|
||||
} else if (column.fields.type.startsWith('RefList')) {
|
||||
const refTable = column.fields.type.split(':')[1];
|
||||
fields[column.id] = Property.Array({
|
||||
displayName: column.fields.label || column.id,
|
||||
description: refTable
|
||||
? `Please provide the row ID from the reference table ${refTable}.`
|
||||
: '',
|
||||
required: false,
|
||||
});
|
||||
} else if (column.fields.type.startsWith('Ref')) {
|
||||
const refTable = column.fields.type.split(':')[1];
|
||||
fields[column.id] = Property.Number({
|
||||
displayName: column.fields.label || column.id,
|
||||
description: refTable
|
||||
? `Please provide the row ID from the reference table ${refTable}.`
|
||||
: '',
|
||||
required: false,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fields;
|
||||
},
|
||||
}),
|
||||
readiness_column: Property.Dropdown({
|
||||
displayName: 'Readiness Column',
|
||||
description: `A toggle (boolean) column which is True when the record is ready. The trigger will only be activated when that record becomes ready.Please follow [guideline](https://support.getgrist.com/integrators/#readiness-column) to create readiness column in table.`,
|
||||
refreshers: ['document_id', 'table_id'],
|
||||
required: false,
|
||||
auth: gristAuth,
|
||||
options: async ({ auth, document_id, table_id }) => {
|
||||
if (!auth || !document_id || !table_id) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Please connect account and select document.',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const authValue = auth.props;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: authValue.domain,
|
||||
apiKey: authValue.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.listTableColumns(
|
||||
document_id as string,
|
||||
table_id as string
|
||||
);
|
||||
|
||||
const options: DropdownOption<string>[] = [];
|
||||
for (const column of response.columns) {
|
||||
if (column.fields.type === 'Bool') {
|
||||
options.push({ value: column.id, label: column.fields.label });
|
||||
}
|
||||
}
|
||||
return {
|
||||
disabled: false,
|
||||
options,
|
||||
};
|
||||
},
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,108 @@
|
||||
export type GristAPIClientOptions = {
|
||||
domainUrl: string;
|
||||
apiKey: string;
|
||||
};
|
||||
|
||||
export type GristOrganizationResponse = {
|
||||
name: string;
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type GristDocumentResponse = {
|
||||
name: string;
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type GristWorkspaceResponse = {
|
||||
name: string;
|
||||
id: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
docs: Array<GristDocumentResponse>;
|
||||
};
|
||||
|
||||
export type GristTableResponse = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type GristTableColumnsResponse = {
|
||||
id: string;
|
||||
fields: {
|
||||
type:
|
||||
| 'Any'
|
||||
| 'Text'
|
||||
| 'Numeric'
|
||||
| 'Int'
|
||||
| 'Bool'
|
||||
| 'Date'
|
||||
| `DateTime:${string}`
|
||||
| `Ref:${string}`
|
||||
| `RefList:${string}`
|
||||
| 'Choice'
|
||||
| 'ChoiceList'
|
||||
| 'Attachments';
|
||||
label: string;
|
||||
widgetOptions: string;
|
||||
isFormula: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type GristTableRecordResponse = {
|
||||
id: number;
|
||||
fields: Record<string, any>;
|
||||
};
|
||||
|
||||
export type GristCreateRecordsRequest = {
|
||||
records: Array<Omit<GristTableRecordResponse, 'id'>>;
|
||||
};
|
||||
|
||||
export type GristUpdateRecordsRequest = {
|
||||
records: Array<GristTableRecordResponse>;
|
||||
};
|
||||
|
||||
export type GristCreateRecordsResponse = {
|
||||
records: Array<{ id: number }>;
|
||||
};
|
||||
|
||||
export type GristListRecordsResponse = {
|
||||
records: Array<GristTableRecordResponse>;
|
||||
};
|
||||
|
||||
export type GristCreateWebhookRequest = {
|
||||
webhooks: Array<{
|
||||
fields: {
|
||||
name?: string;
|
||||
memo?: string;
|
||||
url: string;
|
||||
enabled: boolean;
|
||||
eventTypes: Array<string>;
|
||||
isReadyColumn?: string;
|
||||
tableId: string;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
|
||||
export type GristCreateWebhookResponse = {
|
||||
webhooks: Array<{ id: number }>;
|
||||
};
|
||||
|
||||
export type GristWebhookPayload = Record<string, any>;
|
||||
|
||||
export type GristOrgResponse = {
|
||||
id: number
|
||||
name: string
|
||||
domain: string
|
||||
owner: {
|
||||
id: number
|
||||
name: string
|
||||
picture: any
|
||||
}
|
||||
access: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import { gristAuth } from '../..';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { commonProps } from '../common/props';
|
||||
import { GristAPIClient } from '../common/helpers';
|
||||
import { GristWebhookPayload } from '../common/types';
|
||||
|
||||
export const gristNewRecordTrigger = createTrigger({
|
||||
auth: gristAuth,
|
||||
name: 'grist-new-record',
|
||||
displayName: 'New Record',
|
||||
description: 'Triggers when a new record is added to the table.',
|
||||
props: {
|
||||
workspace_id: commonProps.workspace_id,
|
||||
document_id: commonProps.document_id,
|
||||
table_id: commonProps.table_id,
|
||||
readiness_column: commonProps.readiness_column,
|
||||
},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
sampleData: {},
|
||||
async onEnable(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const tableId = context.propsValue.table_id;
|
||||
const readinessColumn = context.propsValue.readiness_column;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.createDocumentWebhook(documentId, {
|
||||
webhooks: [
|
||||
{
|
||||
fields: {
|
||||
url: context.webhookUrl,
|
||||
enabled: true,
|
||||
eventTypes: ['add'],
|
||||
tableId,
|
||||
isReadyColumn: readinessColumn,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await context.store.put<number>(
|
||||
'grist-new-record',
|
||||
response.webhooks[0].id
|
||||
);
|
||||
},
|
||||
async onDisable(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const webhookId = await context.store.get<number>('grist-new-record');
|
||||
|
||||
if (webhookId != null) {
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
await client.deleteDocumentWebhook(documentId, webhookId);
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
const payload = context.payload.body as GristWebhookPayload[];
|
||||
return payload;
|
||||
},
|
||||
async test(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const tableId = context.propsValue.table_id;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.listRecordsFromTable(documentId, tableId, {
|
||||
limit: '10',
|
||||
});
|
||||
|
||||
return response.records.map((record) => {
|
||||
return {
|
||||
id: record.id,
|
||||
...record.fields,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
import { gristAuth } from '../..';
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { commonProps } from '../common/props';
|
||||
import { GristAPIClient } from '../common/helpers';
|
||||
import { GristWebhookPayload } from '../common/types';
|
||||
|
||||
export const gristUpdatedRecordTrigger = createTrigger({
|
||||
auth: gristAuth,
|
||||
name: 'grist-updated-record',
|
||||
displayName: 'Updated Record',
|
||||
description: 'Triggers when a record is updated in the table.',
|
||||
props: {
|
||||
workspace_id: commonProps.workspace_id,
|
||||
document_id: commonProps.document_id,
|
||||
table_id: commonProps.table_id,
|
||||
readiness_column: commonProps.readiness_column,
|
||||
},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
sampleData: {},
|
||||
async onEnable(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const tableId = context.propsValue.table_id;
|
||||
const readinessColumn = context.propsValue.readiness_column;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.createDocumentWebhook(documentId, {
|
||||
webhooks: [
|
||||
{
|
||||
fields: {
|
||||
url: context.webhookUrl,
|
||||
enabled: true,
|
||||
eventTypes: ['update'],
|
||||
tableId,
|
||||
isReadyColumn: readinessColumn,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await context.store.put<number>(
|
||||
'grist-updated-record',
|
||||
response.webhooks[0].id
|
||||
);
|
||||
},
|
||||
async onDisable(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const webhookId = await context.store.get<number>('grist-updated-record');
|
||||
|
||||
if (webhookId != null) {
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
await client.deleteDocumentWebhook(documentId, webhookId);
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
const payload = context.payload.body as GristWebhookPayload[];
|
||||
return payload;
|
||||
},
|
||||
async test(context) {
|
||||
const documentId = context.propsValue.document_id;
|
||||
const tableId = context.propsValue.table_id;
|
||||
|
||||
const client = new GristAPIClient({
|
||||
domainUrl: context.auth.props.domain,
|
||||
apiKey: context.auth.props.apiKey,
|
||||
});
|
||||
|
||||
const response = await client.listRecordsFromTable(documentId, tableId, {
|
||||
limit: '10',
|
||||
});
|
||||
|
||||
return response.records.map((record) => {
|
||||
return {
|
||||
id: record.id,
|
||||
...record.fields,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user