// src/integrations/optisigns/client.js import { GraphQLClient, gql } from 'graphql-request'; import { logger } from '../../utils/logger.js'; // Global caches (populated from bulk fetch in getOptiSignStatus) const playlistById = new Map(); const assetById = new Map(); /** * Update global caches from the playlistMap returned by getOptiSignStatus */ export function updateOptiSignsCaches(playlistMap) { playlistMap.forEach((name, id) => { playlistById.set(id, name); }); logger('optisigns:client', `Updated global cache with ${playlistMap.size} playlists`, 'debug'); } const client = new GraphQLClient('https://graphql-gateway.optisigns.com/graphql', { headers: { Authorization: `Bearer ${process.env.OPTISIGN_API_KEY}` }, }); /** * Fetch OptiSigns devices and playlists for a store */ export async function getOptiSignStatus(storeNumber) { const paddedStore = String(storeNumber).padStart(6, '0'); logger('optisigns:client', `Fetching status for store ${storeNumber} (padded: ${paddedStore})`, 'debug'); let storeDevices = []; const playlistMap = new Map(); try { // 1. Fetch all devices with pagination logger('optisigns:client', 'Fetching all devices...', 'debug'); let allDevices = []; let after = null; const pageSize = 50; do { const query = gql` query GetDevices($first: Int, $after: String) { devices(query: {}, first: $first, after: $after) { page { edges { node { _id deviceName UUID pairingCode currentType currentAssetId currentPlaylistId localAppVersion lastHeartBeat } } pageInfo { hasNextPage endCursor } } } } `; const data = await client.request(query, { first: pageSize, after }); const pageEdges = data.devices.page.edges || []; allDevices = allDevices.concat(pageEdges.map(e => e.node)); after = data.devices.page.pageInfo.hasNextPage ? data.devices.page.pageInfo.endCursor : null; logger('optisigns:client', `Fetched page with ${pageEdges.length} devices (after: ${after || 'null'})`, 'debug'); } while (after); logger('optisigns:client', `Total devices fetched: ${allDevices.length}`, 'debug'); // Filter devices for this store storeDevices = allDevices.filter(d => d.deviceName?.includes(paddedStore)); logger('optisigns:client', `Matching devices for store ${paddedStore}: ${storeDevices.length}`, 'debug'); // 2. Fetch all playlists (for name mapping) logger('optisigns:client', 'Fetching all playlists...', 'debug'); let allPlaylists = []; after = null; do { const playlistsData = await client.request(gql` query GetAllPlaylists($first: Int, $after: String) { playlists(first: $first, after: $after) { page { edges { node { _id name } } pageInfo { hasNextPage endCursor } } } } `, { first: 100, after }); const pageEdges = playlistsData.playlists.page.edges || []; allPlaylists = allPlaylists.concat(pageEdges.map(e => e.node)); after = playlistsData.playlists.page.pageInfo.hasNextPage ? playlistsData.playlists.page.pageInfo.endCursor : null; } while (after); logger('optisigns:client', `Total playlists fetched: ${allPlaylists.length}`, 'debug'); // Build playlist name map allPlaylists.forEach(pl => { if (pl._id && pl.name) playlistMap.set(pl._id, pl.name); }); // Update global cache so getPlaylistName can use it updateOptiSignsCaches(playlistMap); } catch (err) { logger('optisigns:client', `Error fetching OptiSigns data: ${err.message}`, 'error'); if (err.response) { logger('optisigns:client', `GraphQL response status: ${err.response.status}`, 'error'); } } return { devices: storeDevices, playlistMap }; } /** * Fast cached lookup (synchronous) */ export function getPlaylistName(playlistId) { if (!playlistId) return '—'; return playlistById.get(playlistId) || playlistId; } /** * Fast cached lookup (synchronous) */ export function getAssetName(assetId) { if (!assetId) return '—'; return assetById.get(assetId) || assetId; }