-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathso4t_api_v3.py
More file actions
179 lines (134 loc) · 6.07 KB
/
so4t_api_v3.py
File metadata and controls
179 lines (134 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# Standard Python libraries
import json
import time
# Third-party libraries
import requests
# Local libraries
import so4t_request_validate
class V3Client(object):
def __init__(self, url, token):
print("Initializing API v3 client...")
if not url: # check if URL is provided; if not, exit
print("Missing required argument. Please provide a URL.")
raise SystemExit
if not token: # check if API token is provided; if not, exit
print("Missing required argument. Please provide an API token.")
raise SystemExit
else:
self.token = token
self.headers = {
'Authorization': f'Bearer {self.token}',
'User-Agent': 'so4t_api_user_report/1.0 (http://your-app-url.com; your-contact@email.com)'
}
if "stackoverflowteams.com" in url: # Stack Internal (Business) or Basic
self.team_slug = url.split("https://stackoverflowteams.com/c/")[1]
self.api_url = f"https://api.stackoverflowteams.com/v3/teams/{self.team_slug}"
else: # Stack Internal (Enterprise)
self.api_url = url + "/api/v3"
self.ssl_verify = self.test_connection() # test the API connection
def test_connection(self):
endpoint = "/tags"
endpoint_url = self.api_url + endpoint
ssl_verify = True
print("Testing API v3 connection...")
try:
response = requests.get(endpoint_url, headers=self.headers)
except requests.exceptions.SSLError:
print("SSL error. Trying again without SSL verification...")
response = requests.get(endpoint_url, headers=self.headers, verify=False)
ssl_verify = False
if response.status_code == 200:
print("API connection successful")
return ssl_verify
else:
print("Unable to connect to API. Please check your URL and API token.")
print(f"Status code: {response.status_code}")
print(f"Response from server: {response.text}")
raise SystemExit
def get_all_questions(self):
method = "get"
endpoint = "/questions"
params = {
'page': 1,
'pagesize': 100,
}
questions = self.send_api_call(method, endpoint, params)
return questions
def get_all_tags(self):
method = "get"
endpoint = "/tags"
params = {
'page': 1,
'pagesize': 100,
}
tags = self.send_api_call(method, endpoint, params)
return tags
def get_tag_smes(self, tag_id):
method = "get"
endpoint = f"/tags/{tag_id}/subject-matter-experts"
smes = self.send_api_call(method, endpoint)
return smes
def get_user(self, user_id):
method = "get"
endpoint = f"/users/{user_id}"
user = self.send_api_call(method, endpoint)
return user
def get_all_users(self):
method = "get"
endpoint = "/users"
params = {
'page': 1,
'pagesize': 100,
}
users = self.send_api_call(method, endpoint, params)
return users
def send_api_call(self, method, endpoint, params={}):
get_response = getattr(requests, method, None) # get the method from the requests library
endpoint_url = self.api_url + endpoint
data = []
while True:
try:
if method == 'get':
response = get_response(endpoint_url, headers=self.headers, params=params,
verify=self.ssl_verify,
timeout=so4t_request_validate.timeout)
else:
response = get_response(endpoint_url, headers=self.headers, json=params,
verify=self.ssl_verify,
timeout=so4t_request_validate.timeout)
except Exception as ex:
so4t_request_validate.handle_except(ex)
continue
if response.status_code not in [200, 201, 204]:
print(f"API call to {endpoint_url} failed with status code {response.status_code}")
print(f"Response from server: {response.text}")
raise SystemExit
# Respect v3 throttle headers to avoid being blocked
burst_left = response.headers.get('x-burst-throttle-calls-left')
if burst_left is not None and int(burst_left) < 5:
burst_wait = int(response.headers.get('x-burst-throttle-seconds-until-full', 2))
print(f"Approaching burst throttle limit ({burst_left} calls left). "
f"Waiting {burst_wait} seconds...")
time.sleep(burst_wait)
bucket_left = response.headers.get('x-token-bucket-calls-left')
if bucket_left is not None and int(bucket_left) < 100:
bucket_wait = int(response.headers.get('x-token-bucket-seconds-until-next-refill', 60))
print(f"Token bucket running low ({bucket_left} tokens left). "
f"Waiting {bucket_wait} seconds for refill...")
time.sleep(bucket_wait)
try:
json_data = response.json()
except json.decoder.JSONDecodeError: # some API calls do not return JSON data
print(f"API request successfully sent to {endpoint_url}")
return
if type(params) == dict and params.get('page'): # check request for pagination
print(f"Received page {params['page']} from {endpoint_url}")
data += json_data['items']
if params['page'] == json_data['totalPages']:
break
params['page'] += 1
else:
print(f"API request successfully sent to {endpoint_url}")
data = json_data
break
return data