/** * OData helper functions for Microsoft Graph API */ /** * Escapes a string for use in OData queries * @param {string} str - The string to escape * @returns {string} - The escaped string */ function escapeODataString(str) { if (!str) return str; // Replace single quotes with double single quotes (OData escaping) // And remove any special characters that could cause OData syntax errors str = str.replace(/'/g, "''"); // Escape other potentially problematic characters str = str.replace(/[\(\)\{\}\[\]\:\;\,\/\?\&\=\+\*\%\$\#\@\!\^]/g, ''); console.error(`Escaped OData string: '${str}'`); return str; } /** * Builds an OData filter from filter conditions * @param {Array} conditions - Array of filter conditions * @returns {string} - Combined OData filter expression */ function buildODataFilter(conditions) { if (!conditions || conditions.length === 0) { return ''; } return conditions.join(' and '); } /** * Gets start of day for a given date * @param {Date} date - The date * @returns {Date} - Start of day */ function startOfDay(date) { const start = new Date(date); start.setHours(0, 0, 0, 0); return start; } /** * Gets end of day for a given date * @param {Date} date - The date * @returns {Date} - End of day */ function endOfDay(date) { const end = new Date(date); end.setHours(23, 59, 59, 999); return end; } /** * Parses date input (ISO string or relative date) * @param {string} dateInput - Date string * @returns {Date} - Parsed date */ function parseDate(dateInput) { if (!dateInput) return null; const now = new Date(); const today = new Date(now); // Handle relative dates const relativeMap = { 'today': today, 'yesterday': new Date(now.getTime() - 24*60*60*1000), 'tomorrow': new Date(now.getTime() + 24*60*60*1000), 'last7days': new Date(now.getTime() - 7*24*60*60*1000), 'last30days': new Date(now.getTime() - 30*24*60*60*1000), 'last90days': new Date(now.getTime() - 90*24*60*60*1000) }; if (relativeMap[dateInput.toLowerCase()]) { return relativeMap[dateInput.toLowerCase()]; } // Handle ISO dates const parsed = new Date(dateInput); if (isNaN(parsed.getTime())) { throw new Error(`Invalid date format: ${dateInput}`); } return parsed; } /** * Processes predefined date ranges * @param {string} dateRange - Predefined range * @returns {Object} - Object with from and to dates */ function processDateRange(dateRange) { if (!dateRange) return null; const now = new Date(); const today = new Date(now); switch (dateRange.toLowerCase()) { case 'today': return { from: startOfDay(today), to: endOfDay(today) }; case 'yesterday': { const yesterday = new Date(now.getTime() - 24*60*60*1000); return { from: startOfDay(yesterday), to: endOfDay(yesterday) }; } case 'last7days': return { from: new Date(now.getTime() - 7*24*60*60*1000), to: now }; case 'last30days': return { from: new Date(now.getTime() - 30*24*60*60*1000), to: now }; case 'thisweek': { const startOfWeek = new Date(today); startOfWeek.setDate(today.getDate() - today.getDay()); return { from: startOfDay(startOfWeek), to: endOfDay(today) }; } case 'lastweek': { const startOfLastWeek = new Date(today); startOfLastWeek.setDate(today.getDate() - today.getDay() - 7); const endOfLastWeek = new Date(startOfLastWeek); endOfLastWeek.setDate(startOfLastWeek.getDate() + 6); return { from: startOfDay(startOfLastWeek), to: endOfDay(endOfLastWeek) }; } case 'thismonth': { const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); return { from: startOfDay(startOfMonth), to: endOfDay(today) }; } case 'lastmonth': { const startOfLastMonth = new Date(today.getFullYear(), today.getMonth() - 1, 1); const endOfLastMonth = new Date(today.getFullYear(), today.getMonth(), 0); return { from: startOfDay(startOfLastMonth), to: endOfDay(endOfLastMonth) }; } default: throw new Error(`Unknown date range: ${dateRange}`); } } /** * Builds date filter conditions for OData queries * @param {string} dateFrom - Start date * @param {string} dateTo - End date * @param {string} dateRange - Predefined range * @returns {Array} - Array of filter conditions */ function buildDateFilter(dateFrom, dateTo, dateRange) { const conditions = []; try { if (dateRange) { const range = processDateRange(dateRange); if (range) { conditions.push(`receivedDateTime ge ${range.from.toISOString()}`); conditions.push(`receivedDateTime le ${range.to.toISOString()}`); } } else { if (dateFrom) { const fromDate = parseDate(dateFrom); conditions.push(`receivedDateTime ge ${fromDate.toISOString()}`); } if (dateTo) { const toDate = parseDate(dateTo); // If only date provided (no time), set to end of day if (dateTo.length === 10) { // YYYY-MM-DD format conditions.push(`receivedDateTime le ${endOfDay(toDate).toISOString()}`); } else { conditions.push(`receivedDateTime le ${toDate.toISOString()}`); } } } } catch (error) { console.error(`Date filter error: ${error.message}`); // Return empty conditions on error to avoid breaking the query } return conditions; } module.exports = { escapeODataString, buildODataFilter, parseDate, processDateRange, buildDateFilter, startOfDay, endOfDay };