From 3df5c84d3a4bdee6804c7c6377c3015ca4f07439 Mon Sep 17 00:00:00 2001 From: yashwin-foxy Date: Sat, 13 Sep 2025 15:15:44 +0530 Subject: [PATCH] zoho projects api created --- src/api/controllers/integrationController.js | 92 ++++++++++++++++++- src/api/routes/integrationRoutes.js | 35 ++++++- src/integrations/zoho/client.js | 29 +++++- src/integrations/zoho/mapper.js | 57 +++++++++++- .../integration/integrationService.js | 33 +++++++ 5 files changed, 240 insertions(+), 6 deletions(-) diff --git a/src/api/controllers/integrationController.js b/src/api/controllers/integrationController.js index 0cfbcd5..a0ce64b 100644 --- a/src/api/controllers/integrationController.js +++ b/src/api/controllers/integrationController.js @@ -113,4 +113,94 @@ async function getAllProjectTasks(req, res) { } } -module.exports = { getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks }; +async function getAllProjectTaskLists(req, res) { + try { + const { provider, page, limit, filters, portal_id } = req.query; + + if (provider !== 'zoho') { + return res.status(400).json(failure('All project task lists are only available for Zoho provider', 'INVALID_PROVIDER')); + } + + if (!portal_id) { + return res.status(400).json(failure('portal_id is required in query parameters', 'MISSING_PORTAL_ID')); + } + + const integrationService = new IntegrationService(req.user.id); + const params = { page, limit }; + if (filters) { + try { + params.filters = JSON.parse(filters); + } catch (e) { + return res.status(400).json(failure('Invalid filters format', 'INVALID_FILTERS')); + } + } + + const taskLists = await integrationService.getAllProjectTaskLists(provider, portal_id, params); + res.json(success('All Zoho project task lists retrieved successfully', taskLists)); + } catch (error) { + res.status(400).json(failure(error.message, 'INTEGRATION_ERROR')); + } +} + +async function getAllProjectIssues(req, res) { + try { + const { provider, page, limit, filters, portal_id } = req.query; + console.log('req query is', req.query) + + if (provider !== 'zoho') { + return res.status(400).json(failure('All project issues are only available for Zoho provider', 'INVALID_PROVIDER')); + } + + if (!portal_id) { + return res.status(400).json(failure('portal_id is required in query parameters', 'MISSING_PORTAL_ID')); + } + + const integrationService = new IntegrationService(req.user.id); + const params = { page, limit }; + if (filters) { + try { + params.filters = JSON.parse(filters); + } catch (e) { + return res.status(400).json(failure('Invalid filters format', 'INVALID_FILTERS')); + } + } + + const issues = await integrationService.getAllProjectIssues(provider, portal_id, params); + + + res.json(success('All Zoho project issues retrieved successfully', issues)); + } catch (error) { + res.status(400).json(failure(error.message, 'INTEGRATION_ERROR')); + } +} + +async function getAllProjectPhases(req, res) { + try { + const { provider, page, limit, filters, portal_id } = req.query; + + if (provider !== 'zoho') { + return res.status(400).json(failure('All project phases are only available for Zoho provider', 'INVALID_PROVIDER')); + } + + if (!portal_id) { + return res.status(400).json(failure('portal_id is required in query parameters', 'MISSING_PORTAL_ID')); + } + + const integrationService = new IntegrationService(req.user.id); + const params = { page, limit }; + if (filters) { + try { + params.filters = JSON.parse(filters); + } catch (e) { + return res.status(400).json(failure('Invalid filters format', 'INVALID_FILTERS')); + } + } + + const phases = await integrationService.getAllProjectPhases(provider, portal_id, params); + res.json(success('All Zoho project phases retrieved successfully', phases)); + } catch (error) { + res.status(400).json(failure(error.message, 'INTEGRATION_ERROR')); + } +} + +module.exports = { getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks, getAllProjectTaskLists, getAllProjectIssues, getAllProjectPhases }; diff --git a/src/api/routes/integrationRoutes.js b/src/api/routes/integrationRoutes.js index 52e3f9c..426df6e 100644 --- a/src/api/routes/integrationRoutes.js +++ b/src/api/routes/integrationRoutes.js @@ -1,6 +1,6 @@ const express = require('express'); const Joi = require('joi'); -const { getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks } = require('../controllers/integrationController'); +const { getData, getServices, getResources, getPortals, getAllProjects, getAllProjectTasks, getAllProjectTaskLists, getAllProjectIssues, getAllProjectPhases } = require('../controllers/integrationController'); const auth = require('../middlewares/auth'); const ZohoHandler = require('../../integrations/zoho/handler'); @@ -83,6 +83,39 @@ const allProjectTasksSchema = Joi.object({ router.get('/all-project-tasks', auth, validate(allProjectTasksSchema), getAllProjectTasks); +// Get all project task lists for a specific portal +const allProjectTaskListsSchema = Joi.object({ + provider: Joi.string().valid('zoho').required(), + portal_id: Joi.string().required(), + page: Joi.number().min(1).default(1), + limit: Joi.number().min(1).max(100).default(20), + filters: Joi.string().optional() +}); + +router.get('/all-project-tasklists', auth, validate(allProjectTaskListsSchema), getAllProjectTaskLists); + +// Get all project issues for a specific portal +const allProjectIssuesSchema = Joi.object({ + provider: Joi.string().valid('zoho').required(), + portal_id: Joi.string().required(), + page: Joi.number().min(1).default(1), + limit: Joi.number().min(1).max(100).default(20), + filters: Joi.string().optional() +}); + +router.get('/all-project-issues', auth, validate(allProjectIssuesSchema), getAllProjectIssues); + +// Get all project phases for a specific portal +const allProjectPhasesSchema = Joi.object({ + provider: Joi.string().valid('zoho').required(), + portal_id: Joi.string().required(), + page: Joi.number().min(1).default(1), + limit: Joi.number().min(1).max(100).default(20), + filters: Joi.string().optional() +}); + +router.get('/all-project-phases', auth, validate(allProjectPhasesSchema), getAllProjectPhases); + // Webhook endpoints (no auth required - uses signature verification) const zohoHandler = new ZohoHandler(); router.post('/webhooks/zoho/crm', zohoHandler.handleCrmWebhook.bind(zohoHandler)); diff --git a/src/integrations/zoho/client.js b/src/integrations/zoho/client.js index 5160d03..5751cd1 100644 --- a/src/integrations/zoho/client.js +++ b/src/integrations/zoho/client.js @@ -27,12 +27,19 @@ class ZohoClient { async makeRequest(endpoint, options = {}, service = 'crm') { const { accessToken } = await this.getTokens(); - console.log('i am in make request with token',accessToken) const baseUrl = this.baseUrls[service] || this.baseUrls.crm; const url = `${baseUrl}${endpoint}`; + // Transform limit to per_page for Zoho APIs + let params = options.params || {}; + if (params.limit) { + params.per_page = params.limit; + delete params.limit; + } + const config = { ...options, + params, headers: { 'Authorization': `Zoho-oauthtoken ${accessToken}`, 'Content-Type': 'application/json', @@ -42,6 +49,7 @@ class ZohoClient { try { const response = await axios(url, config); + return response.data; } catch (error) { if (error.response?.status === 401) { @@ -132,7 +140,6 @@ class ZohoClient { try { const projectsResponse = await this.getProjects(portal.id, params); const projects = projectsResponse.data; - console.log('all projects', projects); // Add portal information to each project const projectsWithPortal = projects.map(project => ({ @@ -167,10 +174,26 @@ class ZohoClient { } async getAllProjectTasks(portalId, params = {}) { - const response = await this.makeRequest(`/api/v3/portal/${portalId}/all-tasklists`, { params }, 'projects'); + const response = await this.makeRequest(`/api/v3/portal/${portalId}/tasks`, { params }, 'projects'); return ZohoMapper.mapApiResponse(response, 'tasks'); } + async getAllProjectTaskLists(portalId, params = {}) { + const response = await this.makeRequest(`/api/v3/portal/${portalId}/all-tasklists`, { params }, 'projects'); + return ZohoMapper.mapApiResponse(response, 'tasklists'); + } + + async getAllProjectIssues(portalId, params = {}) { + const response = await this.makeRequest(`/api/v3/portal/${portalId}/issues`, { params }, 'projects'); + console.log('issues response i got',response) + return ZohoMapper.mapApiResponse(response, 'issues'); + } + + async getAllProjectPhases(portalId, params = {}) { + const response = await this.makeRequest(`/api/v3/portal/${portalId}/phases`, { params }, 'projects'); + return ZohoMapper.mapApiResponse(response, 'phases'); + } + // Service discovery getAvailableServices() { return ['crm', 'people', 'projects']; diff --git a/src/integrations/zoho/mapper.js b/src/integrations/zoho/mapper.js index 95124eb..6371d4d 100644 --- a/src/integrations/zoho/mapper.js +++ b/src/integrations/zoho/mapper.js @@ -152,7 +152,7 @@ class ZohoMapper { case 'tasks': // Tasks response has tasklists array and page_info - records = zohoResponse.tasklists || []; + records = zohoResponse.tasks || []; pageInfo = { count: zohoResponse.page_info?.per_page || records.length, moreRecords: zohoResponse.page_info?.has_next_page || false, @@ -161,6 +161,28 @@ class ZohoMapper { }; break; + case 'phases': + // Tasks response has tasklists array and page_info + records = zohoResponse.milestones || []; + pageInfo = { + count: zohoResponse.page_info?.per_page || records.length, + moreRecords: zohoResponse.page_info?.has_next_page || false, + page: zohoResponse.page_info?.page || 1, + pageCount: zohoResponse.page_info?.page_count || 1 + }; + break; + + case 'tasklists': + // Tasks response has tasklists array and page_info + records = zohoResponse.tasklists || []; + pageInfo = { + count: zohoResponse.page_info?.per_page || records.length, + moreRecords: zohoResponse.page_info?.has_next_page || false, + page: zohoResponse.page_info?.page || 1, + pageCount: zohoResponse.page_info?.page_count || 1 + }; + break; + case 'portals': // Portals response is directly an array records = zohoResponse || []; @@ -171,6 +193,39 @@ class ZohoMapper { }; break; + case 'issues': + // Issues response structure (assuming similar to tasks with page_info) + records = zohoResponse.issues || zohoResponse.data || []; + pageInfo = { + count: zohoResponse.page_info?.per_page || records.length, + moreRecords: zohoResponse.page_info?.has_next_page || false, + page: zohoResponse.page_info?.page || 1, + pageCount: zohoResponse.page_info?.page_count || 1 + }; + break; + + case 'tasklists': + // Task lists response structure (assuming similar to tasks with page_info) + records = zohoResponse.tasklists || zohoResponse.data || []; + pageInfo = { + count: zohoResponse.page_info?.per_page || records.length, + moreRecords: zohoResponse.page_info?.has_next_page || false, + page: zohoResponse.page_info?.page || 1, + pageCount: zohoResponse.page_info?.page_count || 1 + }; + break; + + case 'phases': + // Phases response structure (assuming similar to tasks with page_info) + records = zohoResponse.phases || zohoResponse.data || []; + pageInfo = { + count: zohoResponse.page_info?.per_page || records.length, + moreRecords: zohoResponse.page_info?.has_next_page || false, + page: zohoResponse.page_info?.page || 1, + pageCount: zohoResponse.page_info?.page_count || 1 + }; + break; + default: // Default CRM response structure (leads, contacts, deals, employees) records = zohoResponse.data || []; diff --git a/src/services/integration/integrationService.js b/src/services/integration/integrationService.js index 61a0f2e..9e43aab 100644 --- a/src/services/integration/integrationService.js +++ b/src/services/integration/integrationService.js @@ -101,6 +101,39 @@ class IntegrationService { } return await client.getAllProjectTasks(portalId, params); } + + async getAllProjectTaskLists(provider, portalId, params = {}) { + const client = this.clients[provider]; + if (!client) { + throw new Error(`Provider ${provider} not supported`); + } + if (provider !== 'zoho') { + throw new Error('All project task lists are only available for Zoho provider'); + } + return await client.getAllProjectTaskLists(portalId, params); + } + + async getAllProjectIssues(provider, portalId, params = {}) { + const client = this.clients[provider]; + if (!client) { + throw new Error(`Provider ${provider} not supported`); + } + if (provider !== 'zoho') { + throw new Error('All project issues are only available for Zoho provider'); + } + return await client.getAllProjectIssues(portalId, params); + } + + async getAllProjectPhases(provider, portalId, params = {}) { + const client = this.clients[provider]; + if (!client) { + throw new Error(`Provider ${provider} not supported`); + } + if (provider !== 'zoho') { + throw new Error('All project phases are only available for Zoho provider'); + } + return await client.getAllProjectPhases(portalId, params); + } } module.exports = IntegrationService;