{ "name": "Sync All RADIUS Groups", "description": "MANUAL: Syncs RADIUS usergroups for ALL accounts (2 pages x 250 = 500 accounts). Passes actual account status through — ACTIVE, SUSPENDED, INACTIVE, etc.", "trigger": { "type": "MANUAL", "nextSteps": [{ "slug": "fetch-page-1" }] }, "steps": [ { "name": "Fetch accounts page 1", "slug": "fetch-page-1", "type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST", "nodeSlug": "gaiia-public-api-gateway-graphql-request", "allowSkipExecution": false, "outputMapper": "({ output }) => {\n const data = output.response.accounts;\n return {\n accounts: data.nodes.map((a) => ({\n accountId: a.id,\n status: (a.status?.name || '').toUpperCase(),\n products: (a.billingSubscriptions?.nodes || [])\n .map((s) => s.productVersion?.product?.id)\n .filter(Boolean)\n .filter((id, i, arr) => arr.indexOf(id) === i)\n })),\n hasNextPage: data.pageInfo.hasNextPage,\n endCursor: data.pageInfo.endCursor\n };\n}", "inputs": [ { "key": "query", "type": "LITERAL", "value": "string:query {\n accounts(first: 250) {\n nodes {\n id\n status { name }\n billingSubscriptions {\n nodes { productVersion { product { id } } }\n }\n }\n pageInfo { hasNextPage endCursor }\n }\n}" }, { "key": "variables", "type": "FUNCTION", "value": "({ secrets, state, utils, variables }) => ({})" } ], "nextSteps": [{ "slug": "sync-page-1" }] }, { "name": "Sync page 1", "slug": "sync-page-1", "type": "PARALLEL_LOOP", "nodeSlug": "parallel-loop", "maxConcurrentIterations": 10, "inputs": [ { "key": "iterable", "type": "FUNCTION", "value": "({ state }) => state.steps['fetch-page-1'].output.accounts" } ], "initialSteps": [{ "slug": "call-update-page1" }], "nextSteps": [{ "slug": "need-page-2" }] }, { "name": "Call Update RADIUS Account Groups", "slug": "call-update-page1", "type": "GAIIA_START_WORKFLOW_EXECUTION", "nodeSlug": "start-workflow-execution", "parentParallelLoopSlug": "sync-page-1", "allowSkipExecution": false, "inputs": [ { "key": "workflowId", "type": "LITERAL", "value": "string:workflow_fLXTNCvridPqwEEXxf3CK1" }, { "key": "input", "type": "FUNCTION", "value": "({ state }) => {\n const acct = state.currentParallelLoopIterations['sync-page-1'];\n return { accountId: acct.accountId, status: acct.status, productIds: acct.products };\n}" }, { "key": "fireAndForget", "type": "LITERAL", "value": "boolean:true" } ], "nextSteps": [{ "slug": "sync-page-1-end" }] }, { "name": "Need page 2?", "slug": "need-page-2", "type": "CONDITION", "nodeSlug": "condition", "condition": "({ secrets, state, utils, variables }) => {\n return state.steps['fetch-page-1'].output.hasNextPage;\n}", "elseSteps": [{ "slug": "end" }], "thenSteps": [{ "slug": "fetch-page-2" }] }, { "name": "Fetch accounts page 2", "slug": "fetch-page-2", "type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST", "nodeSlug": "gaiia-public-api-gateway-graphql-request", "allowSkipExecution": false, "outputMapper": "({ output }) => {\n const nodes = output.response.accounts.nodes;\n if (!nodes || nodes.length === 0) return [];\n return nodes.map((a) => ({\n accountId: a.id,\n status: (a.status?.name || '').toUpperCase(),\n products: (a.billingSubscriptions?.nodes || [])\n .map((s) => s.productVersion?.product?.id)\n .filter(Boolean)\n .filter((id, i, arr) => arr.indexOf(id) === i)\n }));\n}", "inputs": [ { "key": "query", "type": "LITERAL", "value": "string:query($after: String) {\n accounts(first: 250, after: $after) {\n nodes {\n id\n status { name }\n billingSubscriptions {\n nodes { productVersion { product { id } } }\n }\n }\n pageInfo { hasNextPage endCursor }\n }\n}" }, { "key": "variables", "type": "FUNCTION", "value": "({ state }) => {\n const p1 = state.steps['fetch-page-1'].output;\n return p1.hasNextPage ? { after: p1.endCursor } : {};\n}" } ], "nextSteps": [{ "slug": "if-page-2-has-data" }] }, { "name": "Page 2 has data?", "slug": "if-page-2-has-data", "type": "CONDITION", "nodeSlug": "condition", "condition": "({ secrets, state, utils, variables }) => {\n const data = state.steps['fetch-page-2'].output;\n return Array.isArray(data) && data.length > 0;\n}", "elseSteps": [{ "slug": "end" }], "thenSteps": [{ "slug": "sync-page-2" }] }, { "name": "Sync page 2", "slug": "sync-page-2", "type": "PARALLEL_LOOP", "nodeSlug": "parallel-loop", "maxConcurrentIterations": 10, "inputs": [ { "key": "iterable", "type": "FUNCTION", "value": "({ state }) => state.steps['fetch-page-2'].output" } ], "initialSteps": [{ "slug": "call-update-page2" }], "nextSteps": [{ "slug": "end" }] }, { "name": "Call Update RADIUS Account Groups (page 2)", "slug": "call-update-page2", "type": "GAIIA_START_WORKFLOW_EXECUTION", "nodeSlug": "start-workflow-execution", "parentParallelLoopSlug": "sync-page-2", "allowSkipExecution": false, "inputs": [ { "key": "workflowId", "type": "LITERAL", "value": "string:workflow_fLXTNCvridPqwEEXxf3CK1" }, { "key": "input", "type": "FUNCTION", "value": "({ state }) => {\n const acct = state.currentParallelLoopIterations['sync-page-2'];\n return { accountId: acct.accountId, status: acct.status, productIds: acct.products };\n}" }, { "key": "fireAndForget", "type": "LITERAL", "value": "boolean:true" } ], "nextSteps": [{ "slug": "sync-page-2-end" }] } ] }