import json, os, sys, urllib.request, urllib.parse, urllib.error # Assignment rides the same alertTriggerActions mutation as the status and # verdict updates, with the assignUser payload. The action id is the console's, # and can be overridden for a tenant that names it differently. DEFAULT_ACTION_ID = "S1/alert/assignUser" MUTATION = """mutation AlertTriggerActions($id: String!, $actionId: String!, $user: ID!) { alertTriggerActions( filter: { or: [ { and: [ { fieldId: "id", stringEqual: { value: $id } } ] } ] }, actions: [ { id: $actionId, payload: { assignUser: { value: $user } } } ] ) { __typename ... on ActionsTriggered { actions { actionId alertCount success { id } failure { id errorType errorMessage } skip { id } } } } }""" def request(method, url, headers, body=None): data = json.dumps(body).encode("utf-8") if body is not None else None req = urllib.request.Request(url, data=data, headers=headers, method=method) with urllib.request.urlopen(req, timeout=30) as resp: raw = resp.read() return json.loads(raw) if raw else {} def main(): secrets = json.loads(os.environ.get("INTEGRATION_SECRETS", "{}")) inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}")) base = secrets.get("url", "").rstrip("/") + "/web/api/v2.1" headers = { "Authorization": "ApiToken " + secrets.get("api_token", ""), "Accept": "application/json", "Content-Type": "application/json", } alert_id = str(inputs.get("alert_id") or "").strip() user_id = str(inputs.get("assignee_user_id") or "").strip() if not alert_id or not user_id: raise ValueError("alert_id and assignee_user_id are required") variables = { "id": alert_id, "actionId": str(inputs.get("action_id") or "").strip() or DEFAULT_ACTION_ID, # The console types the user id as an ID, which accepts the number it # returns in assignee.userId as well as its string form. "user": int(user_id) if user_id.isdigit() else user_id, } resp = request("POST", base + "/unifiedalerts/graphql", headers, {"query": MUTATION, "variables": variables}) if resp.get("errors"): print(json.dumps({"error": "GraphQL error", "detail": resp["errors"]})) sys.exit(1) print(json.dumps({"alert_id": alert_id, "assignee_user_id": user_id, "result": resp.get("data") or {}})) try: main() except urllib.error.HTTPError as e: print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")})) sys.exit(1) except Exception as e: print(json.dumps({"error": str(e)})) sys.exit(1)