{ "name": "Daily Preseem Site Inventory Sync", "description": "SCHEDULED (daily 3am): Iterates all network sites, extracts MACs from assigned equipment fields, syncs each item to Preseem access points with its site name as tower.", "trigger": { "type": "SCHEDULE", "cron": "0 3 * * *", "nextSteps": [{ "slug": "fetch-sites" }] }, "steps": [ { "name": "Fetch all network sites with assigned equipment", "slug": "fetch-sites", "type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST", "nodeSlug": "gaiia-public-api-gateway-graphql-request", "allowSkipExecution": false, "outputMapper": "({ output }) => {\n const sites = output.response.networkSites.nodes;\n const items = [];\n for (const site of sites) {\n for (const assign of (site.assignedInventoryItems?.nodes || [])) {\n const inv = assign.inventoryItem || {};\n const model = inv.model?.name || '';\n // Extract MAC from any field whose name contains 'mac' (case-insensitive)\n const macs = [];\n for (const f of (inv.fields?.nodes || [])) {\n const fieldName = (f.modelField?.name || '').toLowerCase();\n const data = (f.data || '').trim();\n if (fieldName.includes('mac') && data && data !== '00:00:00:00:00:00') {\n // Normalize MAC: strip '1:' prefix, pad segments, uppercase\n const cleaned = data.startsWith('1:') ? data.slice(2) : data;\n const parts = cleaned.split(':');\n if (parts.length === 6) {\n const normalized = parts\n .map((p) => p.padStart(2, '0').toUpperCase())\n .join(':');\n macs.push(normalized);\n }\n }\n }\n if (macs.length > 0) {\n items.push({\n inventoryId: inv.id,\n model,\n siteName: site.name,\n macs: [...new Set(macs)]\n });\n }\n }\n }\n return items;\n}", "inputs": [ { "key": "query", "type": "LITERAL", "value": "string:query {\n networkSites(first: 250) {\n nodes {\n id\n name\n assignedInventoryItems {\n nodes {\n inventoryItem {\n id\n model { name }\n ipAddressV4\n fields {\n nodes { data modelField { name } }\n }\n }\n }\n }\n }\n }\n}" }, { "key": "variables", "type": "FUNCTION", "value": "({ secrets, state, utils, variables }) => ({})" } ], "nextSteps": [{ "slug": "if-any-items" }] }, { "name": "Any items to sync?", "slug": "if-any-items", "type": "CONDITION", "nodeSlug": "condition", "condition": "({ secrets, state, utils, variables }) => {\n const items = state.steps['fetch-sites'].output;\n return Array.isArray(items) && items.length > 0;\n}", "elseSteps": [{ "slug": "end" }], "thenSteps": [{ "slug": "sync-to-preseem" }] }, { "name": "Sync each item to Preseem", "slug": "sync-to-preseem", "type": "PARALLEL_LOOP", "nodeSlug": "parallel-loop", "maxConcurrentIterations": 10, "inputs": [ { "key": "iterable", "type": "FUNCTION", "value": "({ state }) => state.steps['fetch-sites'].output" } ], "initialSteps": [{ "slug": "preseem-put-ap" }], "nextSteps": [{ "slug": "end" }] }, { "name": "PUT to Preseem access points", "slug": "preseem-put-ap", "type": "PRESEEM_REST_REQUEST", "nodeSlug": "preseem-rest-request", "parentParallelLoopSlug": "sync-to-preseem", "allowSkipExecution": false, "inputs": [ { "key": "method", "type": "LITERAL", "value": "string:PUT" }, { "key": "path", "type": "FUNCTION", "value": "({ state }) => {\n const item = state.currentParallelLoopIterations['sync-to-preseem'];\n return `/access_points/${item.inventoryId}`;\n}" }, { "key": "body", "type": "FUNCTION", "value": "({ state }) => {\n const item = state.currentParallelLoopIterations['sync-to-preseem'];\n return {\n id: item.inventoryId,\n name: `${item.model} (${item.macs[0]})`,\n tower: item.siteName\n };\n}" } ], "nextSteps": [{ "slug": "sync-to-preseem-end" }] } ] }