joeirimpan / zammad_py Goto Github PK
View Code? Open in Web Editor NEWPython API client for accessing zammad REST API
License: MIT License
Python API client for accessing zammad REST API
License: MIT License
In the Readme there is a line that looks like this:
print client.user.search({'firstname': 'Joe'})
Aside from python3 choking on the lack of parenthesis, that doesn’t seem to document correctly how the search API works.
From some trial and error and code combing, I think that the correct way to search for users whose first name is Joe
looks like this:
print( client.user.search({'query': 'firstname:Joe'}) )
Now I'd like to search for users whose first name is Joe
but only those that are active. I can't figure out how to create that query. I tried this:
print( client.user.search({'query': 'firstname:Joe&active:True'}) )
And several other variations, but so far can't get anything to work.
Does anyone know how to formulate a search query using this python client that has multiple parameters?
Thanks for this package! I am trying to filter tickets depending on their label, and so far I have not found any way to do so, neither using zammad_py
nor through CLI. I am wondering if it is even possible to do so, and I hope this could be a place to find an answer.
curl -H "Authorization: Bearer MY_TOKEN" https://{ZAMMAD_URL}/api/v1/tickets/{TICKET_ID}
from zammad_py import ZammadAPI
client = ZammadAPI(
url=f"https://{ZAMMAD_URL}/api/v1/",
oauth2_token=MY_TOKEN
)
client.ticket.find(TICKET_ID)
Both cases return the same info (expected), but not all the characteristics that I can see on Zammad UI (for instance: content of the discussion, state of the ticket (open, closed...), labels most importantly). Are these fields accessible either through CLI and/or zammad_py
? I also tried client.ticket.search()
, but I am not sure which parameters are expected. Ideally, my endgoal is to filter tickets on their creation date (which I could do with the info I have on the /tickets
endpoint), and on their label.
Thanks in advance!
Adding the possibility to instanciate Zammad Objects, instead of having to handle dicts. (See Issue #202)
Hi Everyone,
The zammad_py package uses the vcr test suite to record and play back HTTP interactions with an instance of Zammad. While this approach has worked well for testing existing functionality, it presents a challenge when it comes to testing new endpoints and features. Since not everyone has access to a "clean-slate" instance of Zammad, it can be difficult to extend the test suite and ensure that the package is working correctly.
I wanted to start a discussion to see if we could come up with some solutions to this problem. One possible approach I thought about would be to use a mock server like WireMock to simulate the behavior of Zammad. With WireMock, it would be possible to create a mock Zammad API that can be used for testing new endpoints and features, without requiring access to a live instance of Zammad.
Another approach would be to create a test Zammad instance that contributors could request access to for testing purposes. This instance would be reset every week to ensure that it remains a clean slate for testing. With this approach, contributors could request access to the test instance and use it to run the vcr test suite and test new features and endpoints. While this approach would require some setup and maintenance, it would provide a live instance of Zammad for testing purposes, without requiring users to have their own installation or rely on a mock server.
A third, (IMHO less desirable) option would be to use classic mock objects with unittest.mock or pytest.mock. While this would be a more labor-intensive option and may not provide as comprehensive testing as the other two options, it could be a good choice for contributors who are familiar with these libraries.
What do you think? Have you used WireMock or other mock technologies in other projects? Which approach do you think would be a better solution for the zammad_py package?
I'm interested in hearing your thoughts and feedback on these ideas.
Best Regards,
Chris
Hi there!
I had a couple of questions about the interface.
Pagination
object can be iterated over to find the individual objects. Is it possible to iterate over the pages themselves? how can you check when next_page
is exhausted?not "spam" in tags
)Hello Joe Paul,
When using a token i still have to set a username / password value to = 'nothing' due to it requiring those fields, is it ok to make those optional as you can also do token auth?
I am missing the Pagination when using the function search
on Tickets.
When I’m using the search
function to filter the Tickets, the limit of objects in one call is exceeded and Zammad used Pagination. Therefore, the API should use it as well.
Also, I find using the common parameter param
for all options a bit inconvenient. I think it would be better to outsource the search string in a separate parameter.
the ticket.update function does not pass all parameters to the zammad API .
id = 1728,
data = {
'id' : id,
'title': 'some new title',
'state': 'new',
'priority': '2 normal',
'owner': '-',
'customer': '[email protected]',
'group': 'Users',
'article': {
'sender': 'Customer',
'type': 'note',
'subject': 'some subject',
'content_type': 'text/plain',
'body': "some body\nnext line",
}
}
new_ticket = zammad.ticket.update(id, data)
Paste the command(s) you ran and the output.
If there was a crash, please include the traceback here.
Zammad Logs
I, [2019-04-13T15:59:40.789162 #907-70214465288180] INFO -- : Started PUT "/api/v1/tickets/1" for 213.219.152.139 at 2019-04-13 15:59:40 +0200
I, [2019-04-13T15:59:40.792263 #907-70214465288180] INFO -- : Processing by TicketsController#update as */*
I, [2019-04-13T15:59:40.792366 #907-70214465288180] INFO -- : Parameters: {"id"=>"1"}
I, [2019-04-13T15:59:40.932145 #909-47037373600760] INFO -- : 2019-04-13T15:59:40+0200: [Work
It seems its not passing my other parameter as if i do the request directly using python requests i get:
I, [2019-04-13T16:01:27.726051 #907-70214503958720] INFO -- : Started PUT "/api/v1/tickets/1" for 213.219.152.139 at 2019-04-13 16:01:27 +0200
I, [2019-04-13T16:01:27.731411 #907-70214503958720] INFO -- : Processing by TicketsController#update as */*
I, [2019-04-13T16:01:27.731612 #907-70214503958720] INFO -- : Parameters: {"id"=>"1", "customer"=>"[email protected]", "article"=>{"sender"=>"Customer", "type"=>"note", "subject"=>"ipo", "content_type"=>"text/plain", "body"=>"oip"}}
If you can help me figure out where this issue and the create issue is from i can try to fix it but im not yet skilled enough to fix it myself
The goal is to implement the Mentions feature (See here)
It should be fairly easy to implement like the other endpoints, as it follows the same structure.
I'm having a strange bug. Code:
from zammad_py import ZammadAPI
client = ZammadAPI(url='https://tickets.test.com', username='<USERNAME>', password='<PASSWORD>')
print(client.user.me())
Output
kmille@linbox:zammad-notify poetry run python zammad_notify/zmd.py
my debug output: tickets.test.comusers 443
Traceback (most recent call last):
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/connection.py", line 174, in _new_conn
conn = connection.create_connection(
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/util/connection.py", line 73, in create_connection
for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
File "/usr/lib/python3.10/socket.py", line 955, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/connectionpool.py", line 703, in urlopen
httplib_response = self._make_request(
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/connectionpool.py", line 386, in _make_request
self._validate_conn(conn)
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/connectionpool.py", line 1042, in _validate_conn
conn.connect()
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/connection.py", line 358, in connect
self.sock = conn = self._new_conn()
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/connection.py", line 186, in _new_conn
raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x606ede73ea70>: Failed to establish a new connection: [Errno -2] Name or service not known
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/requests/adapters.py", line 489, in send
resp = conn.urlopen(
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/connectionpool.py", line 787, in urlopen
retries = retries.increment(
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/util/retry.py", line 592, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='tickets.test.comusers', port=443): Max retries exceeded with url: /me (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x606ede73ea70>: Failed to establish a new connection: [Errno -2] Name or service not known'))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/kmille/projects/zammad-notify/zammad_notify/zmd.py", line 5, in <module>
print(client.user.me())
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/zammad_py/api.py", line 396, in me
response = self._connection.session.get(self.url + "/me")
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/requests/sessions.py", line 600, in get
return self.request("GET", url, **kwargs)
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/requests/sessions.py", line 587, in request
resp = self.send(prep, **send_kwargs)
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/requests/sessions.py", line 701, in send
r = adapter.send(request, **kwargs)
File "/home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/requests/adapters.py", line 565, in send
raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='tickets.test.comusers', port=443): Max retries exceeded with url: /me (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x606ede73ea70>: Failed to establish a new connection: [Errno -2] Name or service not known'))
kmille@linbox:zammad-notify
I added a debug output (in /home/kmille/.cache/pypoetry/virtualenvs/zammad-notify-TBuoGnCs-py3.10/lib/python3.10/site-packages/urllib3/util/connection.py:72)
my debug output: tickets.test.comusers 443
So it tries to resolve tickets.test.comusers
and that fails.
Hello Again,
Hope im not being silly but when i try:
new_ticket = zammad.ticket.create({
'title': 'some new title',
'state': 'new',
'priority': '2 normal',
'owner': '-',
'customer': '[email protected]',
'group': 'Users',
'article': {
'sender': 'Customer',
'type': 'note',
'subject': 'some subject',
'content_type': 'text/plain',
'body': "some body\nnext line",
}
})
i get a list back of alot of tickets giving me the feeling that its not actually hitting the create endpoint.
Please let me know if i missed something.
The usage examples in the readme and docs show importing the library with from zammad_py import ZammadAPI
but that does not seem to actually work. Instead from zammad_py.api import ZammadAPI
is what works.
pip install zammad_py
# In python CLI
>>> from zammad_py import ZammadAPI
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name 'ZammadAPI'
>>> from zammad_py.api import ZammadAPI
>>> ZammadAPI
<class 'zammad_py.api.ZammadAPI'>
>>>
Hello Joe Paul,
Was wondering if it would be possible to add the X-On-Behalf-Of param to the API client to allow for running requests to the API on behalf of the user when using HTTP Token auth and Single Sign on.
Otherwise is it ok if i make a pull request with:
self, username, password, host, http_token=None, oauth2_token=None, on_behalf_of=None,
if self._on_behalf_of:
self.session.headers['X-On-Behalf-Of'] = \
'%s' % self._on_behalf_of
Thanks!
I am trying open ticket with attachment
the zammad server is only testing purpose, there isn’t sensitive data.
from zammad_py.api import *
import base64
t= ZammadAPI(username = "[email protected]",password = "1234",host="206.189.16.230")
dosya = open("/Users/macbook/zammad_py-develop/zammad_py/d.txt","rb").read()
artic = {
"ticket_id":54,
"to":"",
"cc":"",
"subject":"some subject",
"body":"huhuhuu<br>huhuhuu<br>huhuhuu<br><br>",
"content_type":"text/html",
"type":"web",
"internal":False,
"time_unit":"12",
"attachments":[
{
"filename": "d.txt",
"data": base64.b64encode(dosya) ,
"mime-type": "text/plain"
}
]
}
print(t.ticket_article.create(artic))
I am getting error
TypeError: Object of type bytes is not JSON serializable
Also I am tried to modify api source code to:
t.ticket_article.create_with_image_upload(artic,dosya)
def create_with_image_upload(self, params, files):
"""Create the requested resource
:param params: Resource data for creating
"""
response = self._connection.session.post(
self.url + '?expand=true',
json=params,
files={'file': files} #<=========added for post in requests(url, files, json)
)
return self._raise_or_return_json(response)
artic = {
"ticket_id":54,
"to":"",
"cc":"",
"subject":"some subject",
"body":"huhuhuu<br>huhuhuu<br>huhuhuu<br><br>",
"content_type":"text/html",
"type":"web",
"internal":False,
"time_unit":"12",
"attachments":[
{
"filename": "d.txt",
"data": "text/plain;" ,
"mime-type": "text/plain"
}
]
}
t.ticket_article.create_with_image_upload(artic,dosya)
RESPONSE 404: b’{“error”:“Couldn’t find Ticket with ‘id’=”}’
also no luck,
also tried
dosya = open("/Users/macbook/zammad_py-develop/zammad_py/d.txt","rb").read()
artic = {
"ticket_id":54,
"to":"",
"cc":"",
"subject":"some subject",
"body":"huhuhuu<br>huhuhuu<br>huhuhuu<br><br>",
"content_type":"text/html",
"type":"web",
"internal":False,
"time_unit":"12",
"attachments":[
{
"filename": "d.txt",
"data": json.dumps(dosya.decode("utf-8")),
"mime-type": "text/plain"
}
]
}
print(t.ticket_article.create_with_image_upload(artic,dosya))
RESPONSE : b’{“error”:“Couldn’t find Ticket with ‘id’=”}’
without attachment I can create articles for ticket with:
artic = {
"ticket_id":54,
"to":"",
"cc":"",
"subject":"some subject",
"body":"huhuhuu<br>huhuhuu<br>huhuhuu<br><br>",
"content_type":"text/html",
"type":"web",
"internal":False,
"time_unit":"12",
}
t.ticket_article.create(artic)
**Success**
{
"id":161,
"ticket_id":54,
"type_id":11,
"sender_id":2,
"from":"test test <[email protected]>",
"to":"",
"cc":"",
"subject":"some subject",
"reply_to":"None",
"message_id":"None",
"message_id_md5":"None",
"in_reply_to":"None",
"content_type":"text\/html",
"references":"None",
"body":"huhuhuu<br>huhuhuu<br>huhuhuu<br><br>",
"internal":false,
"preferences":{
},
"updated_by_id":30,
"created_by_id":30,
"origin_by_id":30,
"created_at":"2018-11-21T08:40:58.108Z",
"updated_at":"2018-11-21T08:40:58.108Z",
"attachments":[
],
"type":"web",
"sender":"Customer",
"created_by":"auto-1542404189-832322",
"updated_by":"auto-1542404189-832322",
"origin_by":"auto-1542404189-832322"
}
any assistance will be appreciated
We (as Regiohelden) are using zammad_py in our projects.
There are a few things like package upgrade and an error handling which we would like to add to it.
Would you be interested in a collaboration on the project? or handling over the maintenance to our organization?
Kind regards
The Docs don't get updated because the build is failing. (See: https://readthedocs.org/projects/zammad-py/builds/19471112/)
I think this is because of python 2.7, as when I build the docs with sphinx on python 3.8 with Sphinx v6.1.3, the docs get build successfully.
Quick question because of integrating ticket into a view in my app i needed a way to present the attachment to the user for inline viewing of the data now your ticket attachment function works great but i need the headers such as below (because the header i forward to the client with the metadata of the file)
My View:
def attachment(request,a,b,c):
response = requests.get('%s/api/v1/ticket_attachment/%d/%d/%d' %(zammad_url,a,b,c),stream=True)
headers = response.raw.headers
return HttpResponse(response.raw.data,headers)%
See that i also return the headers (only the content headers really) based on the request i send to zammad, any easy way i could get that header data?
By default, requests
verifies SSL certificates but gives the possibility to ignore this verification when calling a method (eg, requests.get(..., verify=False)
.
It would be nice if this feature was supported by zammad_py
, for example by defining an extra verify
kwarg (default True
) when initializing ZammadAPI
and passing this argument to the subsequent get
/post
/put
requests.
Zammad returns empty response when deleting a ticket.
Subject says it all: https://zammad-py.readthedocs.io/en/latest is listing 7e40dff as 'latest' which is (as of today) more than 2 years old.
The attribute is_secure
of ZammadAPI()
can modify the host
If the host
contains the string "https" it will be replaced by "http":
Lines 22 to 24 in d2566b1
i would propose to set an protocol
attribute which is set to "http" or "https" depending on the value of is_secure
and then include it into the url-string:
if is_secure:
protocol = "https"
else:
protocol = "http"
self.url = '%s://%s/api/v1/' % (protocol, host)
Hi Joe,
I'd like to participate in the maintenance of this package.
Specifically, I'd like to focus on three areas:
url = 'https://ticket.example.org/api/v1/'
headers = {'username'=secrets_username, 'password'=secrets_password}
query = {'query': 'Search Content'}
result = requests.post(url=url+'tickets', headers=headers, json=query)
I can do
query = {'query': 'Search Content'}
result = client.search(params=query)
I think if we would provide more Resource specific options, the module would be way easier to use.
I think it would be great if the library could also provide functions to handle webhooks. It could then be used within projects that use flask or django.
Example features:
Thanks for your consideration. Please let me know what you think.
I'm available here: [email protected] if you want to connect via email. I also got Discord, Telegram, Signal, Twitter, etc.
Good afternoon. I apologize in advance for the most likely stupid question, but I beg you to help me. I can't figure out the design at all to send even the simplest API request. I need to have an application ID, get its description, please help me make a request. Thank you in advance
I tried the example on a self hosted test instance and since it is using a self signed certificate the module gave the error that certificate validation failed.
To solve this it would be great to be able to give a parameter like skip_ssl_verification=True
Example usage: client = ZammadAPI(url=domain_url, http_token=token, skip_ssl_verification=True)
from zammad_py import ZammadAPI
import os
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Read domain URL, SSL verification, and token from environment variables
domain_url = os.getenv('API_DOMAIN_URL')
token = os.getenv('API_TOKEN')
client = ZammadAPI(url=domain_url, http_token=token)
# Example: Create a group
params = {
"name": "ABCD",
"signature_id": None,
"email_address_id": None,
"assignment_timeout": None,
"follow_up_possible": "yes",
"follow_up_assignment": True,
"active": True,
"note": "zammad_py",
"lasis_code": None
}
new_group = client.group.create(params=params)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/antonio/dev/zammad/tools/API_imports/import_tool.py", line 29, in <module>
new_group = client.group.create(params=params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/antonio/dev/zammad/tools/API_imports/venv/lib/python3.11/site-packages/zammad_py/api.py", line 280, in create
response = self._connection.session.post(self.url, json=params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/antonio/dev/zammad/tools/API_imports/venv/lib/python3.11/site-packages/requests/sessions.py", line 637, in post
return self.request("POST", url, data=data, json=json, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/antonio/dev/zammad/tools/API_imports/venv/lib/python3.11/site-packages/requests/sessions.py", line 589, in request
resp = self.send(prep, **send_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/antonio/dev/zammad/tools/API_imports/venv/lib/python3.11/site-packages/requests/sessions.py", line 703, in send
r = adapter.send(request, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/antonio/dev/zammad/tools/API_imports/venv/lib/python3.11/site-packages/requests/adapters.py", line 517, in send
raise SSLError(e, request=request)
requests.exceptions.SSLError: HTTPSConnectionPool(host='10.0.10.65', port=443): Max retries exceeded with url: /api/v1/groups (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:992)')))
Thank you!
Connected to Zammad 5.4.0 with token and token has admin.user permission.
Trying to update user but do nothing
from zammad_py import ZammadAPI
zm = ZammadAPI(url='https://xxxx.com/api/v1/', http_token='xxxx')
timo = zm.user.find(4)
params = {'firstname':'Timoo'}
timo.update(params=params)
timo = zm.user.find(4)
print(timo)
Testing with curl works .
curl -H "Authorization: Token token=xxxx" https://xxxx.com/api/v1/users/4 -X PUT -H 'Content-Type: application/json' --data-binary '{"firstname": "Timoo"}'
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.