Skip to content
Open
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
65 changes: 64 additions & 1 deletion packages/v1-ready/zoho-crm/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ const LOCATION_CONFIG: Record<ZohoLocation, { accounts: string; api: string }> =

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, string | ((id: string) => string)>;
public location: ZohoLocation;
Expand Down Expand Up @@ -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<NotificationResponse> Response with updated channel details
* @see https://www.zoho.com/crm/developer/docs/api/v8/notifications/update.html
*/
async updateNotification(body: NotificationWatchConfig): Promise<NotificationResponse> {
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;
Expand Down
1 change: 1 addition & 0 deletions packages/v1-ready/zoho-crm/src/frigg-core.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ declare module '@friggframework/core' {
parsedBody(response: any): Promise<any>;
_get(options: any, stringify?: boolean): Promise<any>;
_post(options: any, stringify?: boolean): Promise<any>;
_patch(options: any): Promise<any>;
_put(options: any, stringify?: boolean): Promise<any>;
_delete(options: any): Promise<any>;
}
Expand Down
Loading