176 lines
6.3 KiB
JavaScript
176 lines
6.3 KiB
JavaScript
const axios = require('axios');
|
|
|
|
function toMillis(completedTime) {
|
|
if (!completedTime) return null;
|
|
const raw = typeof completedTime === 'object' ? completedTime.value : completedTime;
|
|
const ms = Number(raw);
|
|
return Number.isFinite(ms) ? ms : null;
|
|
}
|
|
|
|
function getRelevantTicketTime(ticket) {
|
|
return toMillis(ticket.completed_time)
|
|
|| toMillis(ticket.resolved_time)
|
|
|| toMillis(ticket.last_updated_time)
|
|
|| null;
|
|
}
|
|
|
|
async function fetchClosedTickets({ accessToken, state, tickets, technicians, statusSet, windowMinutes }) {
|
|
if (!accessToken) return;
|
|
|
|
try {
|
|
const windowStart = Date.now() - windowMinutes * 60 * 1000;
|
|
|
|
const listInfo = {
|
|
list_info: {
|
|
row_count: 200,
|
|
sort_field: 'last_updated_time',
|
|
sort_order: 'desc',
|
|
fields_required: [
|
|
'display_id',
|
|
'subject',
|
|
'status',
|
|
'completed_time',
|
|
'resolved_time',
|
|
'last_updated_time',
|
|
'technician'
|
|
],
|
|
search_criteria: [
|
|
{
|
|
field: 'last_updated_time',
|
|
condition: 'greater_than',
|
|
value: windowStart.toString()
|
|
}
|
|
]
|
|
}
|
|
};
|
|
|
|
const response = await axios.get('https://ithelp.mainecc.edu/api/v3/requests', {
|
|
params: { input_data: JSON.stringify(listInfo) },
|
|
headers: {
|
|
Authorization: `Zoho-oauthtoken ${accessToken}`,
|
|
Accept: 'application/vnd.manageengine.sdp.v3+json'
|
|
}
|
|
});
|
|
|
|
const found = response.data?.requests || [];
|
|
|
|
for (const ticket of found) {
|
|
const statusName = ticket.status?.name || '';
|
|
const statusInternalName = ticket.status?.internal_name || '';
|
|
const statusMatches = !statusSet.size
|
|
|| statusSet.has(statusName.toLowerCase())
|
|
|| statusSet.has(statusInternalName.toLowerCase());
|
|
if (!statusMatches) continue;
|
|
|
|
const closedAt = getRelevantTicketTime(ticket);
|
|
if (!closedAt || closedAt <= windowStart) continue;
|
|
|
|
const technician = ticket.technician ? ticket.technician.name : null;
|
|
if (!technician) continue;
|
|
if (technicians.length && !technicians.includes(technician)) continue;
|
|
|
|
tickets.set(String(ticket.display_id), {
|
|
id: ticket.display_id,
|
|
subject: ticket.subject,
|
|
technician,
|
|
status: statusName || null,
|
|
closedAt,
|
|
closedDisplay: ticket.completed_time?.display_value
|
|
|| ticket.resolved_time?.display_value
|
|
|| ticket.last_updated_time?.display_value
|
|
|| null
|
|
});
|
|
}
|
|
|
|
const cutoff = Date.now() - 12 * 60 * 60 * 1000;
|
|
for (const [key, ticket] of tickets) {
|
|
if (ticket.closedAt < cutoff) tickets.delete(key);
|
|
}
|
|
|
|
state.lastCheck = Date.now();
|
|
state.lastError = null;
|
|
} catch (err) {
|
|
const detail = err.response?.data;
|
|
state.lastError = detail?.response_status?.messages?.[0]?.message
|
|
|| (typeof detail === 'string' ? detail : null)
|
|
|| err.message;
|
|
state.lastCheck = Date.now();
|
|
console.error('Error: ', detail || err.message);
|
|
}
|
|
}
|
|
|
|
async function fetchUnassignedCount({ accessToken, state, statusSet, siteSet, groupSet }) {
|
|
if (!accessToken) return;
|
|
|
|
try {
|
|
let count = 0;
|
|
let startIndex = 1;
|
|
const rowCount = 100;
|
|
const maxPages = 50; // safety cap: 5000 unassigned tickets
|
|
|
|
// The SDP instance ignores search_criteria arrays with more than one
|
|
// condition, so only "technician is unassigned" is filtered server-side;
|
|
// status/site/group are matched client-side per ticket below.
|
|
for (let page = 0; page < maxPages; page++) {
|
|
const listInfo = {
|
|
list_info: {
|
|
row_count: rowCount,
|
|
start_index: startIndex,
|
|
search_criteria: {
|
|
field: 'technician',
|
|
condition: 'is',
|
|
value: null
|
|
},
|
|
fields_required: ['status', 'technician', 'group', 'requester']
|
|
}
|
|
};
|
|
|
|
const response = await axios.get('https://ithelp.mainecc.edu/api/v3/requests', {
|
|
params: { input_data: JSON.stringify(listInfo) },
|
|
headers: {
|
|
Authorization: `Zoho-oauthtoken ${accessToken}`,
|
|
Accept: 'application/vnd.manageengine.sdp.v3+json'
|
|
}
|
|
});
|
|
|
|
const found = response.data?.requests || [];
|
|
|
|
for (const ticket of found) {
|
|
if (ticket.technician) continue;
|
|
|
|
const statusName = (ticket.status?.name || '').toLowerCase();
|
|
const statusInternalName = (ticket.status?.internal_name || '').toLowerCase();
|
|
const statusMatches = !statusSet.size
|
|
|| statusSet.has(statusName)
|
|
|| statusSet.has(statusInternalName);
|
|
if (!statusMatches) continue;
|
|
|
|
const groupName = (ticket.group?.name || '').toLowerCase();
|
|
if (groupSet.size && !groupSet.has(groupName)) continue;
|
|
|
|
const siteName = (ticket.requester?.site?.name || '').toLowerCase();
|
|
if (siteSet.size && !siteSet.has(siteName)) continue;
|
|
|
|
count++;
|
|
}
|
|
|
|
if (!response.data?.list_info?.has_more_rows || found.length === 0) break;
|
|
startIndex += rowCount;
|
|
}
|
|
|
|
state.unassignedCount = count;
|
|
state.unassignedError = null;
|
|
} catch (err) {
|
|
const detail = err.response?.data;
|
|
state.unassignedError = detail?.response_status?.messages?.[0]?.message
|
|
|| (typeof detail === 'string' ? detail : null)
|
|
|| err.message;
|
|
state.unassignedCount = null;
|
|
console.error('Error fetching unassigned count: ', detail || err.message);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
fetchClosedTickets,
|
|
fetchUnassignedCount
|
|
}; |