srilakshu012456 commited on
Commit
35e37df
·
verified ·
1 Parent(s): 175f240

Create generate_ticket.py

Browse files
Files changed (1) hide show
  1. generate_ticket.py +112 -0
generate_ticket.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from dotenv import load_dotenv
3
+ import requests
4
+ import time
5
+ import os
6
+ from typing import Optional, Dict, Any
7
+
8
+ load_dotenv()
9
+
10
+ # --- ServiceNow OAuth / instance config ---
11
+ INSTANCE_URL = os.getenv("SERVICENOW_INSTANCE_URL") # e.g., https://your-instance.service-now.com
12
+ CLIENT_ID = os.getenv("SERVICENOW_CLIENT_ID")
13
+ CLIENT_SECRET = os.getenv("SERVICENOW_CLIENT_SECRET")
14
+
15
+ # Optional but recommended: provide defaults for create payload (helps BR/ACL allow quick resolution)
16
+ DEFAULT_ASSIGNMENT_GROUP_SYSID = os.getenv("SERVICENOW_ASSIGNMENT_GROUP_SYSID") # e.g., Service Desk group sys_id
17
+ DEFAULT_CATEGORY = os.getenv("SERVICENOW_DEFAULT_CATEGORY", "inquiry")
18
+ DEFAULT_SUBCATEGORY = os.getenv("SERVICENOW_DEFAULT_SUBCATEGORY", "general")
19
+
20
+ # OAuth token storage (simple in-memory cache)
21
+ _token_info: Dict[str, Any] = {"access_token": None, "expires_at": 0}
22
+
23
+ # Read SSL verify flag from env (default True)
24
+ VERIFY_SSL = os.getenv("SERVICENOW_SSL_VERIFY", "true").lower() in ("1", "true", "yes")
25
+
26
+
27
+ def get_oauth_token() -> str:
28
+ """
29
+ Fetch a new OAuth token using client credentials flow.
30
+ """
31
+ if not INSTANCE_URL or not CLIENT_ID or not CLIENT_SECRET:
32
+ raise RuntimeError(
33
+ "ServiceNow OAuth env vars are missing (SERVICENOW_INSTANCE_URL / CLIENT_ID / CLIENT_SECRET)."
34
+ )
35
+ url = f"{INSTANCE_URL}/oauth_token.do"
36
+ data = {
37
+ "grant_type": "client_credentials",
38
+ "client_id": CLIENT_ID,
39
+ "client_secret": CLIENT_SECRET,
40
+ "scope": "global",
41
+ }
42
+ # NOTE: verify=False is OK for local dev; set verify=True in production
43
+ resp = requests.post(url, data=data, verify=VERIFY_SSL, timeout=25) # <-- use VERIFY_SSL
44
+ if resp.status_code == 200:
45
+ tok = resp.json()
46
+ _token_info["access_token"] = tok.get("access_token")
47
+ _token_info["expires_at"] = time.time() + tok.get("expires_in", 0)
48
+ return _token_info["access_token"]
49
+ raise Exception(f"Failed to get token: {resp.status_code} - {resp.text}")
50
+
51
+
52
+ def get_valid_token() -> str:
53
+ """
54
+ Return a valid token, refreshing if expired.
55
+ """
56
+ if _token_info["access_token"] and time.time() < _token_info["expires_at"]:
57
+ return _token_info["access_token"]
58
+ return get_oauth_token()
59
+
60
+
61
+ def create_incident(short_description: str, description: str) -> Dict[str, Any]:
62
+ """
63
+ Create an incident in ServiceNow using OAuth token.
64
+
65
+ Returns:
66
+ - {"number": "<INC...>", "sys_id": "<sys_id>"} on success
67
+ - {"error": "<message>", "status_code": <int>} on failure
68
+
69
+ NOTE:
70
+ - Includes optional defaults (category/subcategory/assignment_group) to satisfy common BR/ACL requirements
71
+ so your subsequent PATCH to Resolved succeeds.
72
+ """
73
+ token = get_valid_token()
74
+ url = f"{INSTANCE_URL}/api/now/table/incident"
75
+
76
+ headers = {
77
+ "Authorization": f"Bearer {token}",
78
+ "Content-Type": "application/json",
79
+ "Accept": "application/json",
80
+ }
81
+
82
+ payload: Dict[str, Any] = {
83
+ "short_description": short_description,
84
+ "description": description,
85
+ "urgency": "3", # Medium
86
+ "priority": "3",
87
+ # Optional defaults—adjust for your instance if needed
88
+ "category": DEFAULT_CATEGORY,
89
+ "subcategory": DEFAULT_SUBCATEGORY,
90
+ "caller_id": os.getenv("SERVICENOW_CALLER_SYSID"),
91
+ }
92
+
93
+ # Only include assignment_group if the env var is set
94
+ if DEFAULT_ASSIGNMENT_GROUP_SYSID:
95
+ payload["assignment_group"] = DEFAULT_ASSIGNMENT_GROUP_SYSID
96
+
97
+ # NOTE: verify=False is OK for local dev; set verify=True in production
98
+ resp = requests.post(url, headers=headers, json=payload, verify=VERIFY_SSL, timeout=30) # <-- use VERIFY_SSL
99
+
100
+ if resp.status_code in (200, 201):
101
+ try:
102
+ result = resp.json().get("result", {})
103
+ except Exception:
104
+ # Some instances respond with bare object
105
+ result = resp.json()
106
+ number = result.get("number")
107
+ sys_id = result.get("sys_id")
108
+ if number and sys_id:
109
+ return {"number": number, "sys_id": sys_id}
110
+ # If SN responded but fields missing, surface an error
111
+ return {"error": f"Missing Incident fields in response: {resp.text}", "status_code": resp.status_code}
112
+ # Failure: return structured error for caller (main.py /incident & /chat will handle this gracefully)