Microsoft Team Foundation Server Python Library is a Microsoft TFS API Python client that can work with Microsoft TFS workflow and workitems.
Read full documentation: https://devopshq.github.io/tfs/
pip install dohq-tfs
Microsoft TFS API Python client
Home Page: https://devopshq.github.io/tfs/
License: MIT License
Microsoft Team Foundation Server Python Library is a Microsoft TFS API Python client that can work with Microsoft TFS workflow and workitems.
Read full documentation: https://devopshq.github.io/tfs/
pip install dohq-tfs
Hi, this is a great api that i've been using for years now. Wonder if we have _apiv 5 available that is compatible with AzureDevops? thanks!
Unable print the columns grouped under Microsoft - eg - Microsoft.VSTS.Common.Activity
The Python REPL process has exited
Running C:\QA\Automation\QA_Effort\Project\TFS_update.py
('System.Id', 'System.WorkItemType', 'System.Title', 'Microsoft.VSTS.Common.Activity', 'System.AssignedTo', 'Microsoft.VSTS.Scheduling.OriginalEstimate', 'Microsoft.VSTS.Scheduling.RemainingWork', 'System.CreatedDate', 'System.State', 'Microsoft.VSTS.Common.ClosedDate')
('ID', 'Work Item Type', 'Title', 'Activity', 'Assigned To', 'Original Estimate', 'Remaining Work', 'Created Date', 'State', 'Closed Date')
33953,Task,QA : Test Case Creation,Vidya Kulkarni <NYMINT\vkulkarni>,2018-07-05T12:04:08.843Z,Closed
Traceback (most recent call last):
File "C:\QA\Automation\QA_Effort\Project\TFS_update.py", line 27, in
print(workitem[0][Microsoft.VSTS.Common.Activity])
NameError: name 'Microsoft' is not defined
Hey guys!
This is the first time I've been using devopshq, but I can't establish a connection.
The error displayed is: ConnectionError: HTTPSConnectionPool
TFS version 16.131.27701.1
can you help me?
Default on-prem installations of TFS 2017 dont enable basic auth. For many requesting enabling of basic auth is a cross-departmental barrier.
It would seem for the access token you would have something like:
curl -u {username}[:{personalaccesstoken}] https://{server}:8080/DefaultCollection/_apis/projects?api-version=2.0
or adding the header:
Authorization: Basic <BASE64 personalaccesstoken>
According to the API docs
validateOnly=true
is used to test work item creation without actually creating one.
Therefore, response contains fields of not-created-workitem.
Logically, it does not contain System.ID
.
That breaks WorkItem.__init__()
method that tries to self.id = self.data["id"]
while data has no "id"
.
When the workitem or changest is accessed using Django app using HTTP_NEGOTIATE_SSPI.HttpNegotiateAuth as authentication it is giving error of 401 as invalid
Add ability to retrieve revisions as a property to Workitem
.
@property
def revisions(self):
return self.tfs.get_tfs_object('wit/workitems/{}/revisions'.format(self.id))
I use this to build control charts.
Hi,
Can someone explain the following line below mentioned in Advanced usage example? I am getting WorkItem undefined error.
new_task = WorkItem(client, new_task_raw)
Default on-prem installations of TFS 2015/2017 dont enable basic auth. For many (myself included) requesting enabling of basic auth is a cross-departmental barrier. Suggest considering support for NTLM auth and/or the personal access token.
I dont have time to work on this (yet) but it would seem for the access token you would have something like:
curl -u {username}[:{personalaccesstoken}] https://{server}:8080/DefaultCollection/_apis/projects?api-version=2.0
or adding the header:
Authorization: Basic <BASE64 personalaccesstoken>
According to the API docs
My particular site of interest does not have basic auth enabled however TFS-CLI does work using personal access tokens thus proving this is possible to accomplish without basic-auth.
There doesn't seem to be a clear example in the documentation to assert that a new work item is to be the child of another existing one. How does one do this with the API?
Now, object class pass in method like this:
self.get_tfs_object('tfvc/changesets', payload=payload, object_class=Changeset)
but better way is use map like jira: https://github.com/pycontribs/jira/blob/develop/jira/resources.py#L922
Libs allows read and update WI. But sometimes people need create WI (for example, from integration test if test is failed we can create new WI with type=Bug)
Like this:
client = TFSAPI("https://tfs.tfs.ru/tfs/", user=user, password=password)
fields = {
'Title': 'My auto bug from pytest',
'assignedTo' : 'allburov',
'WorkItemType': 'Bug',
}
wi = client.create_workitem(**fields)
wi2 = client.create_workitem(Title='My auto bugfrom pytest', assignedTo='allburov', WorkItemType='Bug')
I'm trying to use the send_post()
method to upload an attachment to attach to wit, but the method is treating 201 as an error.
# connect OK
real_path = os.path.join(os.path.dirname(__file__), 'final_log.lst')
with open(real_path, 'rb') as file:
response = client.rest_client.send_post(
'wit/attachments?fileName=textAsFileAttachment.lst&api-version=1.0',
data=f"[{file.read()}]",
headers={'Content-Type': 'application/octet-stream'},
)
print(response)
Traceback (most recent call last):
File "/home/aderbal/Workspace/assert/test-tfs-conn.py", line 86, in <module>
response = client.rest_client.send_post(
File "/home/aderbal/.local/lib/python3.10/site-packages/tfs/connection.py", line 456, in send_post
return self.__send_request('POST', uri, data, headers, payload=payload, underProject=project)
File "/home/aderbal/.local/lib/python3.10/site-packages/tfs/connection.py", line 518, in __send_request
raise TFSClientError('TFS API returned HTTP %s (%s)' % (
tfs.connection.TFSClientError: TFS API returned HTTP 201 (Created)
The API returns 201 - Created
and is being treated as an error but 201 is a success status. I've been looking in the source code and found it.
526 - connection.py
if response.status_code != 200:
raise TFSClientError('TFS API returned HTTP %s (%s)' % (
response.status_code, result['error'] if 'error' in result else response.reason))
return result
when using in local its working fine @ondrno
I get the following error when I try to create w work item:
[05/02/2019 04:49:48 PM] - INFO - Creating a new User story
[05/02/2019 04:49:48 PM] - DEBUG - https://<TFS_URL> "GET /tfs//_apis/wit/workitems?ids=1480421&$expand=all&api-version=1.0&api-version=1.0 HTTP/1.1" 200 1456
[05/02/2019 04:49:48 PM] - DEBUG - https://<TFS_URL> "POST /tfs//TestPortfolio/_apis/wit/workitems/$User%20Story?api-version=1.0 HTTP/1.1" 200 1302
Traceback (most recent call last):
File "<LOCAL_PATH>/TFS_Testing/TFS_automation.py", line 216, in
Main().main()
File "<LOCAL_PATH>/TFS_Testing/TFS_automation.py", line 56, in main
self.create_qa_story_successor()
File "<LOCAL_PATH>/TFS_Testing/TFS_automation.py", line 60, in create_qa_story_successor
self.create_copy_of_work_item(self.qa_story_id_to_create_successor_for)
File "<LOCAL_PATH>/TFS_Testing/TFS_automation.py", line 101, in create_copy_of_work_item
tested_by=any_tested_by_items_in_wi)
File "<LOCAL_PATH>\TFS_Testing\TFSAPI_WRAPPER.py", line 164, in createWorkItem
return self.client.create_workitem(workItemType, fields=fields, relations_raw=relation)
File "C:\Users<USER_NAME>\AppData\Local\Programs\Python\Python37-32\lib\site-packages\tfs\connection.py", line 306, in create_workitem
return Workitem(raw, self)
File "C:\Users<USER_NAME>\AppData\Local\Programs\Python\Python37-32\lib\site-packages\tfs\resources.py", line 213, in _ _ init _ _
super().init(tfs, raw, 'wit/workItems/{0}', underProject=False, listVersion=listVersion)
File "C:\Users<USER_NAME>\AppData\Local\Programs\Python\Python37-32\lib\site-packages\tfs\resources.py", line 115, in _ _ init _ _
self._parse_raw(raw)
File "C:\Users<USER_NAME>\AppData\Local\Programs\Python\Python37-32\lib\site-packages\tfs\resources.py", line 218, in _parse_raw
super()._parse_raw(raw)
File "C:\Users<USER_NAME>\AppData\Local\Programs\Python\Python37-32\lib\site-packages\tfs\resources.py", line 202, in _parse_raw
raw2resource(raw, self, self.tfs)
File "C:\Users<USER_NAME>\AppData\Local\Programs\Python\Python37-32\lib\site-packages\tfs\resources.py", line 496, in raw2resource
for i, j in iteritems(raw):
File "C:\Users<USER_NAME>\AppData\Local\Programs\Python\Python37-32\lib\site-packages\six.py", line 587, in iteritems
return iter(d.items(**kw))
AttributeError: 'TFSAPI' object has no attribute 'items'
Python Version: 3.7.3
Here are my packages installed:
asn1crypto==0.24.0
certifi==2019.3.9
cffi==1.12.3
chardet==3.0.4
cryptography==2.6.1
dohq-tfs==1.0.163
idna==2.8
ntlm-auth==1.3.0
packaging==19.0
pycparser==2.19
pyparsing==2.4.0
requests-ntlm==1.1.0
requests==2.21.0
six==1.12.0
urllib3==1.24.3
virtualenv==16.5.0
dohq-tfs Library not working with Python version 3.6.12 and above. Was working fine with version 3.6.11. Any plans to make it compatible with higher python versions?
#helpwanted #needupgrade
There's an encapsulated functional that doesn't seem to be in the Public API.
More specifically:
workitem._data
which is a dict()workitem._data["relations"]
)Concrete example:
...
logging.info(workitem._data["relations"][-1])
...
STDOUT:
{'rel': 'AttachedFile', 'url': '<uniquely identified link to the file>', 'attributes': {'authorizedDate': '2017-01-09T14:49:32.633Z', 'id': ..., 'resourceCreatedDate': '2017-01-09T14:49:10.473Z', 'resourceModifiedDate': '2017-01-09T14:49:10.473Z', 'revisedDate': '9999-01-01T00:00:00Z', 'resourceSize': 100937, 'name': '<filename>.png'}}
I'm still not sure, but it seems that the ability to work with attached files is already encapsulated.
Another interesting key is workitem._data["_links"]
, which contains:
{'self': {'href': '<link to the workitem in JSON>'}, 'workItemUpdates': {'href': '...'}, 'workItemRevisions': {'href': '...'}, 'workItemHistory': {'href': '<link to workitem's comments in JSON>'}, 'html': {'...'}, 'workItemType': {'...'}, 'fields': {'href': '...'}}
DoD:
Currently the only way to get information for a specific changeset is to use the same value in the from_
and to_
fields, for example changesets = client.get_changesets(from_=601859, to_=601859)
.
I propose updating the get_changesets
method so that if it is called with an ID then it automatically parses that as querying for one changeset. What is mean is that calling get_changesets(601859)
would be equivalent to get_changesets(from_=601859, to_=601859)
.
https://devopshq.github.io/tfs/examples.html#create-or-copy-workitem
create_workitem return error
Trying to automate some daily TFS operations with dohq-tfs, the code is like:
user = input('user:')
pwd = getpass.getpass('password:')
client = TFSAPI('http://tfs/tfs/', project='DefaultCollection/xxx', user=user, password=pwd)
workitem = client.get_workitem(64656)
print(workitem.field_names)
Which gives:
File "d:\Utilities\tfs_utility\venv\lib\site-packages\tfs\connection.py", line 122, in get_workitems
work_items_batch_info = self.__get_workitems(work_items_batch, fields=fields, expand=expand)
File "d:\Utilities\tfs_utility\venv\lib\site-packages\tfs\connection.py", line 103, in __get_workitems
workitems = self.get_tfs_resource(
File "d:\Utilities\tfs_utility\venv\lib\site-packages\tfs\connection.py", line 50, in get_tfs_resource
raw = self.get_json(uri=uri, underProject=underProject, payload=payload)
File "d:\Utilities\tfs_utility\venv\lib\site-packages\tfs\connection.py", line 79, in get_json
return self.rest_client.send_get(uri, payload=payload, project=underProject)
File "d:\Utilities\tfs_utility\venv\lib\site-packages\tfs\connection.py", line 453, in send_get
return self.__send_request('GET', uri, None, payload=payload, underProject=project, json=json)
File "d:\Utilities\tfs_utility\venv\lib\site-packages\tfs\connection.py", line 506, in __send_request
response.raise_for_status()
File "d:\Utilities\tfs_utility\venv\lib\site-packages\requests\models.py", line 943, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: http://tfs/tfs/DefaultCollection/_apis/wit/workitems?ids=64656&$expand=all&api-version=1.0
Don't know where the url comes from. If I put the url into the browser, it would prompt me to input user/pwd and then the page would become blank (without any info/error). Is it possible related with TFS 2010 we are using?
Any suggestion would be greatly appreciated.
Update: just found the compatibility statement on the homepage, looks like it only support TFS 2015+, sad...
Line 160 in 465b83b
The above line fails if the query type is tree.
self._workitems = self.tfs.get_workitems((i['target']['id'] for i in self.result['workItemRelations']))
fixes this, but of course, this should happen dynamically based on the query type.
Relevant API doc: https://www.visualstudio.com/en-us/docs/integrate/api/wit/wiql
I am trying to retrieve and parse the WI changelog but the following does not work:
workitem.history returns [<tfs.resources.UnknownTfsObject object at 0x000001F48D7F83C8>],
while workitem["System.History"] returns None. The changelog of this WI is quite long.
workitem.revisions return a list of <tfs.resources.UnknownTfsObject object at ...
I tried also with workitem.history.data and workitem.revisions.data as I found it under examples but the response is: 'list' object has no attribute 'data'
Using Azure DevOps Server 2019 and dohq-tfs 1.0.175
Is there any other, proper way to get the changelog?
Hi
Could you tll me how to get diff(like ***.py +106 -2) of one changset by tthis tool?
Thanks
BRGs
Li zhuo
DoD:
Can results for a given test case be retrieved using this library?
For now, lib have many usefull API inside code, but have not any docs about it. All docs are manual created and separated from code
To improvement docs, we need:
Hi!
I met this case. I have a link on query which looks like https://server/tfs/DefaultCollection/project/_workitems?_a=query&tempQueryId=7d123e4af-f52e-4c0d-a220-b5cceffa8f5e
As I can see, only 'path' of query is available for getting workitems via api.
But in this link there no path.
Could you add an oppotunity to get workitems and other query result by QueryId?
dohq-tfs
now is silent. Logging is very helpfull for debug and trubleshooting. Idea is use standart logging
When trying to use the Work Item attribute "Tags" (or "System.Tags"), I get the following error:
Traceback (most recent call last):
File ".\get-tfs-data.py", line 35, in <module>
main()
File ".\get-tfs-data.py", line 32, in main
parseTFSQueryData(client, readyToGroomDesign)
File ".\get-tfs-data.py", line 22, in parseTFSQueryData
print("%s, %s, %s, %s, %s, %s" % (workitem['Id'], workitem['WorkItemType'], workitem['System.Title'], workitem['AssignedTo'], workitem['State'], workitem['tags']))
File "C:\Python37\lib\site-packages\tfs\resources.py", line 107, in __getitem__
return self.fields[key]
File "C:\Python37\lib\site-packages\requests\structures.py", line 52, in __getitem__
return self._store[key.lower()][1]
KeyError: 'system.tags'
I've verified other attributes work as expected (i.e. Title (System.Title))
Can you add timePrecision boolean parameter when running WIQL query?
Example:
POST https://dev.azure.com/{organization}/{project}/{team}/_apis/wit/wiql?timePrecision={timePrecision}&api-version=1.0
Hi. I have two TFS, TFS1 and TFS2. I am able to create a bug by calling create_workitem on TFS1, but getting error 403 forbidden when I tried to do the same on TFS2. Both personal access tokens have full access. Any idea of why this is happening? Thanks.
I am specifying a Collection as well as a Project in the call to TFSAPI, however when I try to retrieve a work item there is a HTTPError: 401 Client Error: Unauthorized for url...However, I noticed that the project has been removed from the url and this could be causing the authentication error.
call to TFSAPI:
client = TFSAPI("https://tfsserver:8080/tfs/", project="CollectionName/ProjectName", user=user, password=password, auth_type=HttpNtlmAuth)
workitem = client.get_workitem(10)
Error:
...requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: https://tfsserver:8080/tfs/CollectionName/_apis/wit/workitems?ids=10&$expand=all&api-version=1.0
When trying to get all changesets starting from a specific one affecting a specific folder or branch, get_changesets
returns all changes (basically ignores the from_
directive).
For example let's say that we have the following:
Running get_changesets(from_=4, item_path='$/sample/test')
returns all changesets (1, 2, 3, 4, 5, 6) instead of just 5 and 6.
This is equivalent to just running get_changesets(item_path='$/sample/test')
I am able to get details of a wit field using print(workitem['state'] ) but i am getting error when updating it..
I am getting below error when updating wit field:
Traceback (most recent call last):
File "C:\Python35-32\lib\site-packages\dohq_tfs-1.0.dev0-py3.5.egg\tfs\connection.py", line 224, in _send_request
File "C:\Python35-32\lib\site-packages\requests-2.18.4-py3.5.egg\requests\models.py", line 892, in json
return complexjson.loads(self.text, **kwargs)
File "C:\Python35-32\lib\json_init.py", line 319, in loads
return _default_decoder.decode(s)
File "C:\Python35-32\lib\json\decoder.py", line 339, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "C:\Python35-32\lib\json\decoder.py", line 357, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:/Release/PythonProjects/TFSAPI.py", line 33, in
workitem['state'] = 'Pending Review'
File "C:\Python35-32\lib\site-packages\dohq_tfs-1.0.dev0-py3.5.egg\tfs\resources.py", line 89, in setitem
File "C:\Python35-32\lib\site-packages\dohq_tfs-1.0.dev0-py3.5.egg\tfs\connection.py", line 107, in update_workitem
File "C:\Python35-32\lib\site-packages\dohq_tfs-1.0.dev0-py3.5.egg\tfs\connection.py", line 189, in send_patch
File "C:\Python35-32\lib\site-packages\dohq_tfs-1.0.dev0-py3.5.egg\tfs\connection.py", line 231, in __send_request
tfs.connection.TFSClientError: Response is not json: Invalid Http Request Header
May be, someone needs upload attachments to WI.
wi = client.get_workitem(ids=111)
attachment= wi.attach(filename="myfilename.txt", filepath="C:\Users\allburov\myfile.txt")
I have the following code (fragment):
client = TFSAPI(server_url=bh_tfs_base_url, project=collection, user=user, password=password, auth_type=HttpNtlmAuth)
px = client.run_query("Shared Queries/PX Queries/PX - Priority List")
for work_item in px.result.workitems:
change_sets_relation = work_item.find_in_relation("ArtifactLink")
for changeset_relation in change_sets_relation:
How to I create a Changetset object from the relation pointing to the changeset?
`fields = {
'System.Title': 'Auto Feature Intake 3',
'System.Description': 'The is the third feature created with the TFS API.',
'System.WorkItemType': 'Feature',
}
relations = [{"rel": 'System.LinkTypes.Hierarchy-Reverse',
"url": URL_PATH_TO_PARENT}]
workitem = client.create_workitem("Task", fields=fields, relations_raw=relations)`
When I run the above code without the "relations_raw" argument, I create a Feature just fine. However, when trying to give the Feature a parent with the "relations_raw" argument, I receive a 400 Client Error.
Any help or example for creating items with relations is greatly appreciated.
I am using Azure DevOps Server 17.143.28912.1.
I am able to invoke get_workitem
calls but create_workitem
calls fail. Can someone point me what I may be doing wrong?
staging_client = TFSAPI("https://staged-tfs.lan/tfs/", pat = 'pat')
fields = {'Title': 'test item: placeholder for new bug', 'Description': 'My description', 'AreaPath': 'Dev\\Data\\Server One', 'IterationPath': 'Dev\\Data Server', 'AssignedTo': 'TFSUnassigned <TSI\\_TFSUnassigned>'}
workitem = staging_client.create_workitem('DEFECT', fields=fields, api_version=5.0)
ERROR:
..
File "E:\automateDashboard\proj1\venv\lib\site-packages\tfs\connection.py", line 309, in create_workitem
api_version)
File "E:\automateDashboard\proj1\venv\lib\site-packages\tfs\connection.py", line 282, in __create_workitem
raw = self.rest_client.send_post(uri=uri, data=data, headers=headers, project=True, payload=params)
File "E:\automateDashboard\proj1\venv\lib\site-packages\tfs\connection.py", line 456, in send_post
return self.__send_request('POST', uri, data, headers, payload=payload, underProject=project)
File "E:\automateDashboard\proj1\venv\lib\site-packages\tfs\connection.py", line 506, in __send_request
response.raise_for_status()
File "E:\automateDashboard\proj1\venv\lib\site-packages\requests\models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://staged-tfs.lan/tfs/DefaultCollection/_apis/wit/workitems/$DEFECT?api-version=5.0
I was able to do some kind of task to select about 2.5 thousands workitems (by work item query), and there was one big problem I was tryin to solve - to parse them ASAP.
And that was very ridicilous experience:
my idea was to parse them asynchronously and that taken about 2-3 hours for me to get the problem: heavy workitems are hardly to parse asynchronously on multiprocessing pool.
And the second idea was to take only fields I've needed then parse them on multiproc pool and that worked perfectly for about 20 seconds on the main processor without any pools.
This is where I've got an idea - to make workitems more 'lighter'.
For now I dont know how to make it real, only thought to make it some kind of Lazy from C#, but could anyone give a suggestion if its real or not and if it's ok to deal with such tasks as I did?
Hi,
i keep getting an exception ":"You must pass a valid patch document in the body of the request."
i'm trying to update fields inside workitems this way:
my_workitem = client.get_workitem(162887)
my_workitem.data['fields']['Microsoft.VSTS.Build.IntegrationBuild'] = "master_20180725.1"
client.update_workitem(162887, my_workitem.data)
i guess i'm doing it wrong but i can't figure it out.
please help
thanks
Asher
We have python 2.7 where we have our code base. But this tfs project supports 3.4.
Is there anyway that supports 2.7?
Hi,
I installed python 3.8 through chocolatey on win10. Then in cmder I ran simple script but it fails at the 1st line:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'tfs' is not defined
First line is:
from tfs import TFSAPI
Do anyone has any idea what to do, please?
Thanks!
Can I use the lib in this project to support TFS 2018?
I tried to connect to TFS 2018 directly ,but it failed
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.