Jira API Assignee not populating with "name" anymore, instead needs "accoundId" - python

I'm working with Python 3.x
Previously, I had a function to create tickets that looks like this
def jira_incident(jira_subject, jira_description):
user = "username"
apikey = 'apikey'
server = 'https://serverName.atlassian.net'
options = {
'server': server,
'verify': False
}
issue_dict = {
'project': {'key': 'project_name'},
'summary': str(jira_subject),
'description': str(jira_description),
'issuetype': {'name': 'Incident'},
'assignee': {'name': my_username},
'priority': {'name': 'Low'},
'customfield_10125':
{'value': 'Application Ops'}
}
jira = JIRA(options, basic_auth=(user, apikey))
new_issue = jira.create_issue(fields=issue_dict)
return new_issue
my_username is a global variable that's used for other things as well.
Anyway, the assignee is no longer working as of about 2 days ago. I did some googling and found that it now needs the accountId instead of the name, I can get this via the web UI by leaving a comment as #'ing someone in a comment. As a temporary solution I've populated a dictionary to reference (and that works), however I'd like to make this more dynamic for future proofing the script.
'assignee': {'accountId': jira_dict[my_username]},
I can't seem to really find any documentation on looking up the accountId from the name, and I figured I'd go ahead and ask the community to see if anyone else has run into/solved this issue.
I was thinking about just writing a new function that performs this query for me, then returns the accountId.
EDIT
I did find this:
import requests
from requests.auth import HTTPBasicAuth
import json
url = "/rest/api/3/user/bulk/migration"
auth = HTTPBasicAuth("email#example.com", "<api_token>")
headers = {
"Accept": "application/json"
}
response = requests.request(
"GET",
url,
headers=headers,
auth=auth
)
print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": ")))
However it 404's on me, I add the server address to the beginning of the url, and replace user, with the username in question.

Okay, I found a solution, it's not an elegant solution, but it does exactly what I need it to. So here is the new function:
def jira_account_id_from_username(username):
r = requests.get('https://serverName.atlassian.net/rest/api/3/user?username=' + username, auth=("username",api_key), verify=False)
value = re.search('"accountId":"(.*?)",', str(r.text)).group(1)
return value

I strongly encourage you to not rely on the username anymore. The endpoint you are using is deprecated, see also https://developer.atlassian.com/cloud/jira/platform/deprecation-notice-user-privacy-api-migration-guide/.
The "new" or probably better way is to use the /user/search endpoint as described here: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-user-search-get There you can define a query that is matching against certain properties of a user (displayName or emailAddress), or search for the accountId if you already have it. Therefore, if you are linking users from the cloud to some other "user directory" (or just a scripts where you have stored some usernames), replace it by using either email address or accountId so you can properly link your users.

Related

Unable to access notion comments via API/python

I'm trying to read the comments on a database entry in notion but I can't figure out how I need to make the request.
import requests
_url = 'https://api.notion.com/v1/comments'
_headers = {"Authorization": _auth,
"Notion-Version": "2021-08-16"}
_data = {'block_id': _page_id}
_results = requests.post(_url, headers=_headers, data=json.dumps(_data))
_data = _results.json()
These results I get back are something like this:
{u'code': u'validation_error',
u'message': u'body failed validation. Fix one:\nbody.parent should be defined, instead was `undefined`.\nbody.discussion_id should be defined, instead was `undefined`.',
u'object': u'error',
u'status': 400}
I'm trying to follow the docs here https://developers.notion.com/reference/retrieve-a-comment but I'm not sure how it translates into python.
Has anyone managed to do this?
If you are reading/getting data from the API, you should use GET, not POST.
To translate the API to Python code, you will do something like this. Notice that the block id is a query parameter not in the request body.
_url = 'https://api.notion.com/v1/comments'
_headers = {"Authorization": _auth,
"Notion-Version": "2021-08-16"}
_query = {'block_id': _page_id}
_results = requests.get(_url, headers=_headers, params=_query)
_data = _results.json()
You also need to ensure that you have added the integration to the notion page.
Run the code, you will get the response like this, (in my case, I don't have any comments on the page).
{'object': 'list', 'results': [],
'next_cursor': None, 'has_more': False,
'type': 'comment', 'comment': {}}

CloudKit Server-to-Server auth: Keep getting 401 Authentication failed

I have been recently exploring the CloudKit and related frameworks. I got the communication with my app working, as well as with my website using CloudKitJS. Where I am struggling is the Server-to-Server communication (which I would need for exporting data from public database in csv.
I have tried Python package requests-cloudkit, which others were suggesting. I have created a Server-to-Server token, and have copied only the key between START and END line once creating the eckey.pem file. I then got this code:
from requests_cloudkit import CloudKitAuth
from restmapper import restmapper
import json
KEY_ID = '[my key ID from CK Dashboard]'
SECRET_FILE_KEY = 'eckey.pem'
AUTH = CloudKitAuth(KEY_ID, SECRET_FILE_KEY)
PARAMS = {
'query':{
'recordType': '[my record type]'
},
}
CloudKit = restmapper.RestMapper("https://api.apple-cloudkit.com/database/1/[my container]/development/")
cloudkit = CloudKit(auth=AUTH)
response = cloudkit.POST.public.records.query(json.dumps(PARAMS))
I am then getting the 401 Authentication failed response. I am stuck on this for days, so I would be grateful for any help or advice. 😊
Creating the server-to-server key is an important first step, but in order to make HTTP requests after that, you have to sign each request.
Look for the Authenticate Web Service Requests section near the bottom of this documentation page.
It's a little bit convoluted, but you have to carefully construct signed headers to include with each request you make. I'm not familiar with how to do it in Python, but here's how I do it in NodeJS which may help:
//Get the timestamp in a very specific format
let date = moment().utc().format('YYYY-MM-DD[T]HH:mm:ss[Z]')
//Construct the subpath
let endpoint = '/records/lookup'
let path = '/database/1/iCloud.*****/development/public'
let subpath = path+endpoint
//Get the key file
let privateKeyFile = fs.readFileSync('../../'+SECRET_FILE_KEY, 'utf8')
//Make a string out of your JSON query
let query = {
recordType: '[my record type]'
}
let requestBody = JSON.stringify(query)
//Hash the query
let bodyHash = crypto.createHash('sha256').update(requestBody, 'utf8').digest('base64')
//Assemble the components you just generated in a special format
//[Current date]:[Request body]:[Web service URL subpath]
let message = date+':'+bodyHash+':'+subpath
//Sign it
let signature = crypto.createSign('RSA-SHA256').update(message).sign(privateKeyFile, 'base64')
//Assemble your headers and include them in your HTTP request
let headers = {
'X-Apple-CloudKit-Request-KeyID': KEY_ID,
'X-Apple-CloudKit-Request-ISO8601Date': date,
'X-Apple-CloudKit-Request-SignatureV1': signature
}
This is a bit hairy at first, but I just put all this stuff in a function that I reuse whenever I need to make a request.
Apple's documentation has pretty much been abandoned and it's hard to find good help with CloudKit Web Services these days.

PagerDuty assignments list empty

How can I get "assignments" key of the incidents in PagerDuty? I have a python script which returns info on particular incident, but the list with assignments is empty.
#!/usr/bin/env python
import requests
import json
API_KEY = 'iiiiiiiiiiiiiiiiii'
# incident ID
ID = 'PPPPP'
def get_incident():
url = 'https://api.pagerduty.com/incidents/{id}'.format(id=ID)
headers = {
'Accept': 'application/vnd.pagerduty+json;version=2',
'Authorization': 'Token token={token}'.format(token=API_KEY)
}
params = {
'include': 'assignees',
'time_zone': 'Europe/Sofia'
}
r = requests.get(url, headers=headers,data=json.dumps(params))
print ('Status Code: {code}'.format(code=r.status_code))
print (r.json())
if __name__ == '__main__':
get_incident()
In their documentation here there are entries for that key, see on the picture bellow:
How can I achieve that?
Based on experimentation, it appears that assignments are only populated while an incident is still active. I just queried /incidents/id for an incident in the triggered state and in the resolved state, the triggered one shows an assignment while the resolved one does not.
It's kind of frustrating, because I want to look at the LogEntrys for an incident via their API and I haven't figured that out yet.
The alert_counts field always seems to contain zeros too, which doesn't make any sense.

How to unsubscribe / delete list members using Mailchimp3 in Python?

I am using mailchimp3 in Python. I managed to make batch insertion of users using this function:
client = MailChimp(USERNAME, APIKEY)
def fill_list(list_id, subscribers_data):
data = {'operations': create_subscriptions_data(list_id, subscribers_data)}
client.batches.create(data)
def create_subscriptions_data(list_id, users_data):
return [{'method': 'PUT',
'path': 'lists/{}/members/{}'.format(list_id, str(md5(user['email_address'].encode('utf-8')))),
'body': json.dumps(user)} for user in users_data]
Here is how one user dict looks like:
{"email_address": "user#somemail.com", "status": "subscribed"}
Then I wanted to use similar method to unsubscribe list of users. To achieve that I tried to use the same logic, just to change the user objects. Now, I used this user format:
{"email_address": "user#somemail.com", "status": "unsubscribed"}
It doesn't update the subscribe status. When I deleted all users manually (using the web interface) and tried the same command I successfully created users with "status": "unsubscribed". I am wondering why this approach can't change the status? I tried also using POST instead of PUT, but it didn't work. Any idea what can be the issue?
I used this reference https://devs.mailchimp.com/blog/batch-operations-and-put-in-api-v3-0/ and it mentions that this approach should work fine for updates as well.
Thank you in advance!
The only way to unsubscribe an already subscribed user will be to update with a list id and an MD5 hash of the lowercase version of the list member’s email address.
client.lists.members.update('LIST_ID', 'MD5 HASH', {"status":
"unsubscribed"})
Actually, I was using some wrong functions, so here is the fixed code. I also had some problems with the size of the batches. The maximum batch size is 500, so I did some splits of the data across several batches.
Here is a simple code how the insertion should be done:
client = MailChimp(USERNAME, APIKEY)
def _update_list(list_id: str, members_data: list):
client.lists.update_members(list_id, {'members': members_data, 'update_existing': True})
Each member in members_data has data like this:
mailchimp_user = {
'email_address': user.email,
'status': user.subscription_status,
'merge_fields': {
'FNAME': user.first_name,
'LNAME': user.last_name
},
'interests': {}
}
And here comes the most important function:
def fill_in_multiple_batches(list_id, mailchimp_members):
step_size = 400
for i in range(0, len(mailchimp_members), step_size):
batch_start_idx = i
batch_end_idx = min(i + step_size, len(mailchimp_members))
this_batch_of_members = mailchimp_members[batch_start_idx:batch_end_idx]
client.lists.update_members(list_id, {'members': members_data, 'update_existing': True})
After that, in the main of the script:
if __name__ == '__main__':
fill_in_multiple_batches('your_list_id', your_data_list)

ServiceNow SOAP API close ticket with python

I'm working on connecting a python script to a ServiceNow ticketing environment. Thankfully, ServiceNow has documentation on how to create a ticket from a python script, see documentation here:
http://wiki.servicenow.com/index.php?title=Python_Web_Services_Client_Examples#gsc.tab=0
Here's the script I'm using to create a ticket:
#!/usr/bin/python
from SOAPpy import SOAPProxy
import sys
def createincident(params_dict):
# instance to send to
instance='demo'
# username/password
username='itil'
password='itil'
# proxy - NOTE: ALWAYS use https://INSTANCE.service-now.com, not https://www.service-now.com/INSTANCE for web services URL from now on!
proxy = 'https://%s:%s#%s.service-now.com/incident.do?SOAP' % (username, password, instance)
namespace = 'http://www.service-now.com/'
server = SOAPProxy(proxy, namespace)
# uncomment these for LOTS of debugging output
#server.config.dumpHeadersIn = 1
#server.config.dumpHeadersOut = 1
#server.config.dumpSOAPOut = 1
#server.config.dumpSOAPIn = 1
response = server.insert(impact=int(params_dict['impact']), urgency=int(params_dict['urgency']), priority=int(params_dict['priority']), category=params_dict['category'], location=params_dict['location'], caller_id=params_dict['user'], assignment_group=params_dict['assignment_group'], assigned_to=params_dict['assigned_to'], short_description=params_dict['short_description'], comments=params_dict['comments'])
return response
values = {'impact': '1', 'urgency': '1', 'priority': '1', 'category': 'High',
'location': 'San Diego', 'user': 'fred.luddy#yourcompany.com',
'assignment_group': 'Technical Support', 'assigned_to': 'David Loo',
'short_description': 'An incident created using python, SOAPpy, and web
services.', 'comments': 'This a test making an incident with python.\nIsn\'t
life wonderful?'}
new_incident_sysid=createincident(values)
print "Returned sysid: "+repr(new_incident_sysid)
However, I cannot find any good documentation on the process to resolve the ticket that I just created using the API. When I run the above script, I get the ticket number as well as the sys_id.
Any help would be appreciated.
Thanks.
Apparently thats an old request, but i landed here searching, and apprently 400 other people too, so here is my solution :
Use the server.update(sys_id="...",state="..",...) to modify your record and set the right values to "resolve" it.
For this method only the sys_id parameter is mandatory, it's up to you for the other fields of your form.
P.S. : you can find the API here - https://.service-now.com/incident.do?WSDL

Categories