Skip to main content
Glama

mcp-google-sheets

index.ts8.21 kB
import { AuthenticationType, httpClient, HttpError, HttpMessageBody, HttpMethod, HttpRequest, } from '@activepieces/pieces-common'; import { GetField, PaginatedV2Response, PaginatedV1Response, RequestParams } from './types'; import { isEmpty, isNil } from '@activepieces/shared'; import dayjs from 'dayjs'; type FlexibleQueryParams = Record< string, string | number | boolean | string[] | number[] | null | undefined >; export const pipedriveCommon = { subscribeWebhook: async ( object: string, action: string, webhookUrl: string, apiDomain: string, accessToken: string, ) => { const request: HttpRequest = { method: HttpMethod.POST, url: `${apiDomain}/api/v1/webhooks`, body: { event_object: object, event_action: action, subscription_url: webhookUrl, version: '2.0', }, authentication: { type: AuthenticationType.BEARER_TOKEN, token: accessToken, }, queryParams: {}, }; const { body: webhook } = await httpClient.sendRequest<{ data: { id: string }; }>(request); return webhook; }, unsubscribeWebhook: async (webhookId: string, apiDomain: string, accessToken: string) => { const request: HttpRequest = { method: HttpMethod.DELETE, url: `${apiDomain}/api/v1/webhooks/${webhookId}`, authentication: { type: AuthenticationType.BEARER_TOKEN, token: accessToken, }, }; return await httpClient.sendRequest(request); }, }; export type PipedriveApiCallParams = { accessToken: string; apiDomain: string; method: HttpMethod; resourceUri: string; query?: RequestParams; body?: any; }; export async function pipedriveApiCall<T extends HttpMessageBody>({ accessToken, apiDomain, method, resourceUri, query, body, }: PipedriveApiCallParams): Promise<T> { const url = `${apiDomain}/api${resourceUri}`; const qs: Record<string, string> = {}; if (query) { for (const [key, value] of Object.entries(query)) { if (value !== null && value !== undefined) { qs[key] = Array.isArray(value) ? value.map(String).join(',') : String(value); } } } let requestBody: any; if (body) { requestBody = Object.entries(body).reduce((acc, [key, value]) => { if (!isNil(value)) { acc[key] = value; } return acc; }, {} as Record<string, any>); } const request: HttpRequest = { method, url, authentication: { type: AuthenticationType.BEARER_TOKEN, token: accessToken, }, queryParams: qs, body: requestBody, }; try { const response = await httpClient.sendRequest<T>(request); return response.body; } catch (error) { if (error instanceof HttpError) { if (error.response.status === 403) { throw new Error('Please reconnect your Pipedrive account.'); } } throw error; } } export async function pipedrivePaginatedV1ApiCall<T extends HttpMessageBody>({ accessToken, apiDomain, method, resourceUri, query, body, }: PipedriveApiCallParams): Promise<T[]> { const qs = query ? query : {}; qs.start = 0; qs.limit = 500; const resultData: T[] = []; let hasMoreItems = true; do { const response = await pipedriveApiCall<PaginatedV1Response<T>>({ accessToken, apiDomain, method, resourceUri, query: qs, body, }); if (isNil(response.data)) { break; } resultData.push(...response.data); qs.start = response.additional_data.pagination.next_start; hasMoreItems = response.additional_data.pagination.more_items_in_collection; } while (hasMoreItems); return resultData; } export async function pipedrivePaginatedV2ApiCall<T extends HttpMessageBody>({ accessToken, apiDomain, method, resourceUri, query, body, }: PipedriveApiCallParams): Promise<T[]> { const qs: FlexibleQueryParams = query ? { ...query } : {}; let cursor: string | undefined = undefined; const resultData: T[] = []; let hasMoreItems = true; do { const currentQuery: FlexibleQueryParams = { ...qs }; if (cursor) { currentQuery.cursor = cursor; } currentQuery.limit = 500; const response = await pipedriveApiCall<PaginatedV2Response<T>>({ accessToken, apiDomain, method, resourceUri, query: currentQuery as RequestParams, body, }); if (isNil(response.data)) { break; } if (Array.isArray(response.data)) { resultData.push(...response.data); } else { resultData.push(response.data); } hasMoreItems = response.additional_data?.next_cursor != null; cursor = response.additional_data?.next_cursor; } while (hasMoreItems && cursor); return resultData; } function formatDateIfValid(val: any) { return dayjs(val).isValid() ? dayjs(val).format('YYYY-MM-DD') : val; } export function pipedriveParseCustomFields( customFieldsDefinitions: GetField[], inputData: Record<string, any>, ): Record<string, any> { const fieldTypeMap: Record<string, GetField> = customFieldsDefinitions.reduce((acc, field) => { acc[field.key] = field; return acc; }, {} as Record<string, GetField>); const parsedFields: Record<string, any> = {}; for (const [key, value] of Object.entries(inputData)) { if (isEmpty(value)) continue; const matchedField = fieldTypeMap[key]; if (!matchedField) continue; // https://pipedrive.readme.io/docs/pipedrive-api-v2-migration-guide#custom-fields if (matchedField.is_subfield) { const parentField = key.split('_')[0]; parsedFields[parentField] = { ...parsedFields[parentField], [matchedField.id_suffix]: matchedField.field_type === "date" ? formatDateIfValid(value) : value, }; continue; } switch (matchedField.field_type) { case 'monetary': parsedFields[key] = { ...parsedFields[key], value: Number(value) }; break; case 'address': case 'daterange': case 'timerange': case 'time': parsedFields[key] = { ...parsedFields[key], value: formatDateIfValid(value) }; break; case 'date': parsedFields[key] = formatDateIfValid(value); break; default: parsedFields[key] = value; break; } } return parsedFields; } export function pipedriveTransformCustomFields( customFieldsDefinitions: GetField[], responseData: Record<string, any>, ): Record<string, any> { const updatedResponseData = { ...responseData }; const rawCustomFields = responseData.custom_fields || {}; for (const field of customFieldsDefinitions) { if (!field.edit_flag) { continue; } const oldKey = field.key; const newKey = field.name; const fieldType = field.field_type; if (oldKey in rawCustomFields) { const value = rawCustomFields[oldKey]; if (isNil(value)) { updatedResponseData[newKey] = null; } else if (fieldType === 'enum') { updatedResponseData[newKey] = field.options?.find((option) => option.id === value)?.label || null; } else if (fieldType === 'set') { if (Array.isArray(value)) { updatedResponseData[newKey] = value.map( (item) => field.options?.find((option) => option.id === item)?.label || null, ); } else { updatedResponseData[newKey] = value; } } else { updatedResponseData[newKey] = value; } } } delete updatedResponseData.custom_fields; return updatedResponseData; } export function pipedriveTransformV1CustomFields( CustomFields: GetField[], responseData: Record<string, any>, ): Record<string, any> { const updatedResponseData = { ...responseData }; for (const field of CustomFields) { if (!field.edit_flag) { continue; } const oldKey = field.key; const newKey = field.name; const fieldType = field.field_type; if (oldKey in responseData) { if (responseData[oldKey] === null || responseData[oldKey] === undefined) { updatedResponseData[newKey] = null; } else if (fieldType === 'enum') { updatedResponseData[newKey] = field.options?.find((option) => option.id.toString() === responseData[oldKey])?.label || null; } else if (fieldType === 'set') { const values: string[] = responseData[oldKey].split(','); updatedResponseData[newKey] = values.map( (item) => field.options?.find((option) => option.id.toString() === item)?.label || null, ); } else { updatedResponseData[newKey] = responseData[oldKey]; } delete updatedResponseData[oldKey]; } } return updatedResponseData; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server