Where attachments of Steps are stored in VSTS REST API? - python

I am using the Python REST API of VSTS for TFS / Azure Dev Ops (https://github.com/Microsoft/azure-devops-python-api).
I would like to add attachments to some of the steps of my Test Cases like I can do in the Web Interface.
This is how I want my step to look like:
... and when you run it, that would look like this:
However, I have not been able to find where this information is stored.
This is the JSON data for the WorkItem of my Test Case
{
id: 224,
rev: 2,
fields: {
System.AreaPath: "GM_sandbox\GM-Toto",
System.TeamProject: "GM_sandbox",
System.IterationPath: "GM_sandbox",
System.WorkItemType: "Test Case",
System.State: "Design",
System.Reason: "New",
System.AssignedTo: "Jeff",
System.CreatedDate: "2019-01-03T01:43:09.743Z",
System.CreatedBy: "Jeff",
System.ChangedDate: "2019-01-03T02:12:07.15Z",
System.ChangedBy: "Jeff",
System.Title: "Titi",
Microsoft.VSTS.Common.StateChangeDate: "2019-01-03T01:43:09.743Z",
Microsoft.VSTS.Common.ActivatedDate: "2019-01-03T01:43:09.743Z",
Microsoft.VSTS.Common.ActivatedBy: "Jeff",
Microsoft.VSTS.Common.Priority: 2,
Microsoft.VSTS.TCM.AutomationStatus: "Not Automated",
Microsoft.VSTS.TCM.Steps: "<steps id="0" last="2"><step id="2" type="ValidateStep"><parameterizedString isformatted="true"><DIV><P>Click on the rainbow button</P></DIV></parameterizedString><parameterizedString isformatted="true"><P>Screen becomes Blue (see picture)</P></parameterizedString><description/></step></steps>"
},
_links: {
self: {
href: "https://my_server.com:8443/tfs/PRODUCT/23d89bd4-8547-4be3-aa73-13a30866f176/_apis/wit/workItems/224"
},
workItemUpdates: {
href: "https://my_server.com:8443/tfs/PRODUCT/_apis/wit/workItems/224/updates"
},
workItemRevisions: {
href: "https://my_server.com:8443/tfs/PRODUCT/_apis/wit/workItems/224/revisions"
},
workItemHistory: {
href: "https://my_server.com:8443/tfs/PRODUCT/_apis/wit/workItems/224/history"
},
html: {
href: "https://my_server.com:8443/tfs/PRODUCTi.aspx?pcguid=4107d6a2-eaaa-40b9-9a8d-f8fdbb31d4b7&id=224"
},
workItemType: {
href: "https://my_server.com:8443/tfs/PRODUCT/23d89bd4-8547-4be3-aa73-13a30866f176/_apis/wit/workItemTypes/Test%20Case"
},
fields: {
href: "https://my_server.com:8443/tfs/PRODUCT/23d89bd4-8547-4be3-aa73-13a30866f176/_apis/wit/fields"
}
},
url: "https://my_server.com:8443/tfs/PRODUCT/23d89bd4-8547-4be3-aa73-13a30866f176/_apis/wit/workItems/224"
}
Any idea on where this information is stored?
And, if you are familiar with the Python REST API, how to add an attachment from a file and link it to the test step?
Thanks a lot

Here is the flow using just the azure-devops-rest-api
Create the attachment:
Request:
POST https://dev.azure.com/{organization}/_apis/wit/attachments?fileName=info.txt&api-version=4.1
Body:
{"User text content to upload"}
Response:
{
"id": "f5016cf4-4c36-4bd6-9762-b6ad60838cf7",
"url": "https://dev.azure.com/{organization}/_apis/wit/attachments/f5016cf4-4c36-4bd6-9762-b6ad60838cf7?fileName=info.txt"
}
Create the Work Item:
Request:
PATCH https://dev.azure.com/{organization}/{project}/_apis/wit/workitems/$Test Case?api-version=4.1
Body:
[
{
"op": "add",
"path": "/fields/System.Title",
"from": null,
"value": "Sample test case"
},
{
"op": "add",
"path": "/fields/Microsoft.VSTS.TCM.Steps",
"value": "<steps id=\"0\" last=\"4\"><step id=\"2\" type=\"ActionStep\"><parameterizedString isformatted=\"true\"><DIV><P>test&nbsp;</P></DIV></parameterizedString><parameterizedString isformatted=\"true\"><DIV><P>&nbsp;</P></DIV></parameterizedString><description/></step><step id=\"3\" type=\"ActionStep\"><parameterizedString isformatted=\"true\"><DIV><DIV><P>test&nbsp;</P></DIV></DIV></parameterizedString><parameterizedString isformatted=\"true\"><DIV><P>&nbsp;</P></DIV></parameterizedString><description/></step><step id=\"4\" type=\"ActionStep\"><parameterizedString isformatted=\"true\"><DIV><P>test&nbsp;</P></DIV></parameterizedString><parameterizedString isformatted=\"true\"><DIV><P>&nbsp;</P></DIV></parameterizedString><description/></step></steps>"
},
{
"op": "add",
"path": "/relations/-",
"value": {
"rel": "AttachedFile",
"url": "https://dev.azure.com/{organization}/_apis/wit/attachments/f5016cf4-4c36-4bd6-9762-b6ad60838cf7?fileName=info.txt",
"attributes": {
"comment": "[TestStep=3]:",
"name": "info.txt"
}
}
}
]
The test case that is created will look like the below. There is something off with the step numbering for the number in the comment. Looks like it need to be +1 for the actual step you want to reference.
The key is to have in the attributes of the attached file, the comment with "[TestStep=3]:" as well as a name for the attachment.
In Python, that would give something like this:
Creating of attachment with function create_attachment
Updating a Test Case with url, comment, and filename
So something like that...
from vsts.work_item_tracking.v4_1.models.json_patch_operation import JsonPatchOperation
def add_attachment(wit_id: int, project: str, url:str, comment: str, step = 0, name = ""):
"""Add attachment already uploaded to a WorkItem
"""
# For linking the attachment to a step, we need to modify the comment and add a name
if step:
attributes = {
"comment":f"[TestStep={step}]:{comment}",
"name": name if name else re.sub(r".*fileName=", "", url)
}
else:
attributes = {"comment": comment}
patch_document = [
JsonPatchOperation(
op="add",
path="/relations/-",
value={
"rel": "AttachedFile",
"url": url,
"attributes": attributes,
},
)
]
return client.wit.update_work_item(patch_document, wit_id, project)
attachment = client_wit.create_attachment(stream, project, 'smiley.png')
add_attachment(tcid, project, attachment.url, 'Attaching file to work item', step=3)

Related

How to parse nested JSON object?

I am working on a new project in HubSpot that returns nested JSON like the sample below. I am trying to access the associated contacts id, but am struggling to reference it correctly (the id I am looking for is the value '201' in the example below). I've put together this script, but this script only returns the entire associations portion of the JSON and I only want the id. How do I reference the id correctly?
Here is the output from the script:
{'contacts': {'paging': None, 'results': [{'id': '201', 'type': 'ticket_to_contact'}]}}
And here is the script I put together:
import hubspot
from pprint import pprint
client = hubspot.Client.create(api_key="API_KEY")
try:
api_response = client.crm.tickets.basic_api.get_page(limit=2, associations=["contacts"], archived=False)
for x in range(2):
pprint(api_response.results[x].associations)
except ApiException as e:
print("Exception when calling basic_api->get_page: %s\n" % e)
Here is what the full JSON looks like ('contacts' property shortened for readability):
{
"results": [
{
"id": "34018123",
"properties": {
"content": "Hi xxxxx,\r\n\r\nCan you clarify on how the blocking of script happens? Is it because of any CSP (or) the script will decide run time for every URL’s getting triggered from browser?\r\n\r\nRegards,\r\nLogan",
"createdate": "2019-07-03T04:20:12.366Z",
"hs_lastmodifieddate": "2020-12-09T01:16:12.974Z",
"hs_object_id": "34018123",
"hs_pipeline": "0",
"hs_pipeline_stage": "4",
"hs_ticket_category": null,
"hs_ticket_priority": null,
"subject": "RE: call followup"
},
"createdAt": "2019-07-03T04:20:12.366Z",
"updatedAt": "2020-12-09T01:16:12.974Z",
"archived": false
},
{
"id": "34018892",
"properties": {
"content": "Hi Guys,\r\n\r\nI see that we were placed back on the staging and then removed again.",
"createdate": "2019-07-03T07:59:10.606Z",
"hs_lastmodifieddate": "2021-12-17T09:04:46.316Z",
"hs_object_id": "34018892",
"hs_pipeline": "0",
"hs_pipeline_stage": "3",
"hs_ticket_category": null,
"hs_ticket_priority": null,
"subject": "Re: Issue due to server"
},
"createdAt": "2019-07-03T07:59:10.606Z",
"updatedAt": "2021-12-17T09:04:46.316Z",
"archived": false,
"associations": {
"contacts": {
"results": [
{
"id": "201",
"type": "ticket_to_contact"
}
]
}
}
}
],
"paging": {
"next": {
"after": "35406270",
"link": "https://api.hubapi.com/crm/v3/objects/tickets?associations=contacts&archived=false&hs_static_app=developer-docs-ui&limit=2&after=35406270&hs_static_app_version=1.3488"
}
}
}
You can do api_response.results[x].associations["contacts"]["results"][0]["id"].
Sorted this out, posting in case anyone else is struggling with the response from the HubSpot v3 Api. The response schema for this call is:
Response schema type: Object
String results[].id
Object results[].properties
String results[].createdAt
String results[].updatedAt
Boolean results[].archived
String results[].archivedAt
Object results[].associations
Object paging
Object paging.next
String paging.next.after
String paging.next.linkResponse schema type: Object
String results[].id
Object results[].properties
String results[].createdAt
String results[].updatedAt
Boolean results[].archived
String results[].archivedAt
Object results[].associations
Object paging
Object paging.next
String paging.next.after
String paging.next.link
So to access the id of the contact associated with the ticket, you need to reference it using this notation:
api_response.results[1].associations["contacts"].results[0].id
notes:
results[x] - reference the result in the index
associations["contacts"] -
associations is a dictionary object, you can access the contacts item
by it's name
associations["contacts"].results is a list - reference
by the index []
id - is a string
In my case type was ModelProperty or CollectionResponseProperty couldn't reach dict anyhow.
For the record this got me to go through the results.
for result in list(api_response.results):
ID = result.id

How to get th content of a string inside a request response?

I was coding a webapp based on GPT-2 but it was not good so I decided to switch to official OpenAI GPT-3.
So I make that request:
response = openai.Completion.create(
engine="davinci",
prompt="Hello",
temperature=0.7,
max_tokens=64,
top_p=1,
frequency_penalty=0,
presence_penalty=0
)
And when I print the response I get this:
{
"choices": [
{
"finish_reason": "length",
"index": 0,
"logprobs": null,
"text": ", everyone, and welcome to the first installment of the new opening"
}
],
"created": 1624033807,
"id": "cmpl-3CBfb8yZAFEUIVXfZO90m77dgd9V4",
"model": "davinci:2020-05-03",
"object": "text_completion"
}
But I only want to print the text, so how can I do to print the "text" value in the response list.
Thank you in advance and have a good day.
Using the dict indexing by key, and the list indexing by index
x = {"choices": [{"finish_reason": "length",
"text": ", everyone, and welcome to the first installment of the new opening"}], }
text = x['choices'][0]['text']
print(text) # , everyone, and welcome to the first installment of the new opening
You can try print(response["choices"][0]["text"])
Hope this helps.
I think GPT-3 response structure has been changed, for reference, the response object looks this:
const response = await openai.createCompletion({
model: "text-davinci-002",
prompt: "Say this is a test",
temperature: 0,
max_tokens: 6,
});
// the response looks like the following
{
status: 200,
statusText: 'OK',
headers: {
},
config: {
},
request: <ref *1> ClientRequest {
},
data: {
id: 'cmpl-5zzyzqvh4Hmi5yyNL2LMI9ADkLBU0',
object: 'text_completion',
created: 1665457953,
model: 'text-davinci-002',
choices: [ [Object] ],
usage: { prompt_tokens: 5, completion_tokens: 6, total_tokens: 11 }
}
}
// choices can be accessed like this
var { choices } = { ...response.data }

flask-ask slot is always being mapped to None

My slot for a custom intent is always being recognised as None.
I have an intents schema which looks like this:
{
"interactionModel": {
"languageModel": {
"invocationName": "name_of_app",
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "EventsIntent",
"slots": [
{
"name": "eventCity",
"type": "AMAZON.GB_CITY"
}
],
"samples": [
"whats on in {eventCity}",
"whats going on in {eventCity} ",
"tell me what events are in {eventCity}"
]
}
],
"types": []
}
}
}
My code is in python, using the flask-ask framework. My main entrypoint looks something like this:
#ask.launch
def start_skill():
welcome_message = 'Welcome to name_of_app, what do you want?'
return question(welcome_message)
#ask.intent('EventsIntent', mapping={'city': 'eventCity'})
def weather(city):
return statement('you have selected {}'.format(city))
Even this simple example however, does not work when the input is:
"whats on in London?"
I have tried with lowercase and with/without punctuation in the testing panel on the amazon developer console, however the return is always:
"you have selected None"
indicating that None is passed as 'eventCity'. Am I passing this slot incorrectly in either the intent schema or in the code?
Thanks
hey buddy following is the solution of your problem:
#ask.launch
def start_skill():
welcome_message = 'Welcome to name_of_app, what do you want?'
return question(welcome_message)
#ask.intent('EventsIntent', convert={'eventCity': str})
def weather(eventCity):
return statement('you have selected {}'.format(eventCity))
I was also facing similar issue this thing resolved my issue.

Facebook Messenger bot Generic Template Not Working

I have created a Facebook Messenger bot which works fine. I have used the Button Template and Image template, and both work perfectly. But when I try the Generic Template, I get no response. I have simply copy pasted the code from here, by performing the appropriate modifications.
I don't know how to debug. Facebook Messenger gives no output on the messaging box. I am currently running the app via Heroku.
Here is my code:
def send_message(token, recipient):
r = requests.post("https://graph.facebook.com/v2.6/me/messages",
params={"access_token": token},
data=json.dumps({
"recipient":{
"id":recipient
},
"message":{
"attachment":{
"type":"template",
"payload":{
"template_type":"generic",
"elements":[
{
"title":"Welcome to Peter\'s Hats",
"image_url":"http://www.godominion.com/content/images/feature-img-small-appliance-electronics.png",
"subtitle":"We\'ve got the right hat for everyone.",
"default_action": {
"type": "web_url",
"url": "https://peterssendreceiveapp.ngrok.io/view?item=103",
"messenger_extensions": true,
"webview_height_ratio": "tall",
"fallback_url": "https://peterssendreceiveapp.ngrok.io/"
},
"buttons":[
{
"type":"web_url",
"url":"https://petersfancybrownhats.com",
"title":"View Website"
}
]
}
]
}
}
}
}),
headers={'Content-type': 'application/json'})
if r.status_code != requests.codes.ok:
print r.text
I would appreciate any help.
Thank you.
EDIT 1: SOLUTION
I got rid of the issue by commenting out:
"messenger_extensions": true,
and
"fallback_url": "https://peterssendreceiveapp.ngrok.io/"},
I'm sure this is not the correct method. But as I am creating a bot, without actual links, this works.
On the second button, "url":"https://petersfancybrownhats.com" is broken.
Try like this
firstly make a function
def function():
extra_data = {
"attachment": {
"type": "template",
"payload": {
"template_type": "generic",
"elements": [
{
"title": "Any Title",
"image_url": "https://mbtskoudsalg.com/images/road-clipart-journey-3.png",
"subtitle": "Subtitle.",
"buttons": [
{
"type": "web_url",
"title": "View",
"url": "**MAKE SURE TO WHITELIST THIS URL**", # URL
"messenger_extensions": "true",
"webview_height_ratio": "full"
}
]
}
]
}
}
}
# w_message = "Hi there! How may I help you?"
fb_message_template(extra_data["attachment"], "****RECIEVER ID****")
Make another function
import requests
# // Importing User Defined Modules // #
from get_environ_var import get_environ_var
# // Global vars // #
ACCESS_TOKEN = "FB_ACCESS_TOKEN"
def fb_message_template(extra_data, sender_id):
"""This function sends template message to facebook"""
data = {
'recipient': {'id': sender_id},
'message': {
"attachment": extra_data
}
}
qs = 'access_token=' + ACCESS_TOKEN
resp = requests.post('https://graph.facebook.com/v2.6/me/messages?' + qs, json=data)
print(resp.content)

Grab element from json dump

I'm using the following python code to connect to a jsonrpc server and nick some song information. However, I can't work out how to get the current title in to a variable to print elsewhere. Here is the code:
TracksInfo = []
for song in playingSongs:
data = { "id":1,
"method":"slim.request",
"params":[ "",
["songinfo",0,100, "track_id:%s" % song, "tags:GPASIediqtymkovrfijnCYXRTIuwxN"]
]
}
params = json.dumps(data, sort_keys=True, indent=4)
conn.request("POST", "/jsonrpc.js", params)
httpResponse = conn.getresponse()
data = httpResponse.read()
responce = json.loads(data)
print json.dumps(responce, sort_keys=True, indent=4)
TrackInfo = responce['result']["songinfo_loop"][0]
TracksInfo.append(TrackInfo)
This brings me back the data in json format and the print json.dump brings back:
pi#raspberrypi ~/pithon $ sudo python tom3.py
{
"id": 1,
"method": "slim.request",
"params": [
"",
[
"songinfo",
"0",
100,
"track_id:-140501481178464",
"tags:GPASIediqtymkovrfijnCYXRTIuwxN"
]
],
"result": {
"songinfo_loop": [
{
"id": "-140501481178464"
},
{
"title": "Witchcraft"
},
{
"artist": "Pendulum"
},
{
"duration": "253"
},
{
"tracknum": "1"
},
{
"type": "Ogg Vorbis (Spotify)"
},
{
"bitrate": "320k VBR"
},
{
"coverart": "0"
},
{
"url": "spotify:track:2A7ZZ1tjaluKYMlT3ItSfN"
},
{
"remote": 1
}
]
}
}
What i'm trying to get is result.songinfoloop.title (but I tried that!)
The songinfo_loop structure is.. peculiar. It is a list of dictionaries each with just one key.
Loop through it until you have one with a title:
TrackInfo = next(d['title'] for d in responce['result']["songinfo_loop"] if 'title' in d)
TracksInfo.append(TrackInfo)
A better option would be to 'collapse' all those dictionaries into one:
songinfo = reduce(lambda d, p: d.update(p) or d,
responce['result']["songinfo_loop"], {})
TracksInfo.append(songinfo['title'])
songinfo_loop is a list not a dict. That means you need to call it by position, or loop through it and find the dict with a key value of "title"
positional:
responce["result"]["songinfo_loop"][1]["title"]
loop:
for info in responce["result"]["songinfo_loop"]:
if "title" in info.keys():
print info["title"]
break
else:
print "no song title found"
Really, it seems like you would want to have the songinfo_loop be a dict, not a list. But if you need to leave it as a list, this is how you would pull the title.
The result is really a standard python dict, so you can use
responce["result"]["songinfoloop"]["title"]
which should work

Categories