diff --git a/packages/v1-ready/zoho-crm/src/api.ts b/packages/v1-ready/zoho-crm/src/api.ts index 06ddfd5..c1ebde9 100644 --- a/packages/v1-ready/zoho-crm/src/api.ts +++ b/packages/v1-ready/zoho-crm/src/api.ts @@ -43,6 +43,15 @@ const LOCATION_CONFIG: Record = const DEFAULT_LOCATION: ZohoLocation = 'us'; +/** + * Formats datetime for Zoho API (removes milliseconds, converts Z to +00:00) + * Zoho expects format: 2019-05-02T15:00:00+05:30 + */ +function formatDateTimeForZoho(dateStr: string | undefined): string | undefined { + if (!dateStr) return dateStr; + return dateStr.replace(/\.\d{3}Z$/, '+00:00').replace(/Z$/, '+00:00'); +} + export class Api extends OAuth2Requester { public URLs: Record string)>; public location: ZohoLocation; @@ -608,10 +617,64 @@ export class Api extends OAuth2Requester { } }); + const formattedBody: NotificationWatchConfig = { + ...body, + watch: body.watch.map(item => ({ + ...item, + ...(item.channel_expiry && { channel_expiry: formatDateTimeForZoho(item.channel_expiry) }) + })) + }; + try { return await this._post({ url: this.baseUrl + this.URLs.notificationsWatch, - body: body, + body: formattedBody, + }); + } catch (error) { + throw error; + } + } + + /** + * Update/renew notification channel configuration + * Use this to extend the channel_expiry before it expires (max 7 days from now) + * @param body - Notification configuration with watch items to update + * @returns Promise Response with updated channel details + * @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/update.html + */ + async updateNotification(body: NotificationWatchConfig): Promise { + if (!body || !body.watch || !Array.isArray(body.watch)) { + throw new Error('Body must contain watch array'); + } + + // Validate each watch item + body.watch.forEach((item, index) => { + if (!item.channel_id) { + throw new Error(`watch[${index}].channel_id is required`); + } + if (!item.events || !Array.isArray(item.events) || item.events.length === 0) { + throw new Error(`watch[${index}].events must be a non-empty array`); + } + if (!item.notify_url) { + throw new Error(`watch[${index}].notify_url is required`); + } + if (item.token && item.token.length > 50) { + throw new Error(`watch[${index}].token must be 50 characters or less`); + } + }); + + const formattedBody: NotificationWatchConfig = { + ...body, + watch: body.watch.map(item => ({ + ...item, + ...(item.channel_expiry && { channel_expiry: formatDateTimeForZoho(item.channel_expiry) }) + })) + }; + + try { + return await this._patch({ + url: this.baseUrl + this.URLs.notificationsWatch, + body: formattedBody, }); } catch (error) { throw error; diff --git a/packages/v1-ready/zoho-crm/src/frigg-core.d.ts b/packages/v1-ready/zoho-crm/src/frigg-core.d.ts index 8a13af8..7360588 100644 --- a/packages/v1-ready/zoho-crm/src/frigg-core.d.ts +++ b/packages/v1-ready/zoho-crm/src/frigg-core.d.ts @@ -16,6 +16,7 @@ declare module '@friggframework/core' { parsedBody(response: any): Promise; _get(options: any, stringify?: boolean): Promise; _post(options: any, stringify?: boolean): Promise; + _patch(options: any): Promise; _put(options: any, stringify?: boolean): Promise; _delete(options: any): Promise; }