From cce1020a8272aa222fd137e34f0c5bedbda57f96 Mon Sep 17 00:00:00 2001 From: Laszlo GOROG Date: Wed, 14 Aug 2024 16:29:38 +0200 Subject: [PATCH] Allow to delete and redact attachments --- README.md | 24 + ....test_delete_attachment-upload-single.json | 252 +++++++++ ....test_redact_attachment-upload-single.json | 532 ++++++++++++++++++ .../test_create_update_delete_zendesk.py | 28 + zenpy/lib/api.py | 22 + zenpy/lib/endpoint.py | 3 + zenpy/lib/request.py | 6 +- 7 files changed, 864 insertions(+), 3 deletions(-) create mode 100644 tests/test_api/betamax/TestAttachmentUpload.test_delete_attachment-upload-single.json create mode 100644 tests/test_api/betamax/TestAttachmentUpload.test_redact_attachment-upload-single.json diff --git a/README.md b/README.md index a0e06fdd..f6f8538d 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,30 @@ ticket.comment = Comment(body='This comment has my file attached', uploads=[uplo zenpy_client.tickets.update(ticket) ``` +##### Creating a comment attachment and then redacting it + +```python +# Upload the attachment +upload_instance = zenpy_client.attachments.upload('/tmp/awesome_file.txt') +comment = Comment(body='Some comment') +# Set the comment attachment affinity to this token. +comment.uploads = upload_instance.token + +# Create the ticket, with that comment with that attachment affinity. Can just as easily be a new comment on existing ticket. +ticket = Ticket(subject='example ticket', comment=comment) +ticket_audit = zenpy_client.tickets.create(ticket) +ticket = ticket_audit.ticket + +# Get the last comment we just uploaded on that ticket. +the_commentresult = zenpy_client.tickets.comments(ticket) + +# Redact the comment now that we just associated it with an attachment. +the_comment = the_commentresult.values[0] +attachment = zenpy_client.attachments.redact(ticket, the_comment, the_comment.attachments[0].id) + +# Barring no errors, the attachment is an Attachment object with the same id as was passed in! +``` + ##### Creating a ticket with a custom field set ```python diff --git a/tests/test_api/betamax/TestAttachmentUpload.test_delete_attachment-upload-single.json b/tests/test_api/betamax/TestAttachmentUpload.test_delete_attachment-upload-single.json new file mode 100644 index 00000000..fe34a4fd --- /dev/null +++ b/tests/test_api/betamax/TestAttachmentUpload.test_delete_attachment-upload-single.json @@ -0,0 +1,252 @@ +{ + "http_interactions": [ + { + "recorded_at": "2024-08-20T05:48:17", + "request": { + "body": { + "encoding": "utf-8", + "string": "" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "14165" + ], + "Content-Type": [ + "application/octet-stream" + ], + "User-Agent": [ + "Zenpy/2.0.49" + ] + }, + "method": "POST", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/uploads.json?filename=README.md" + }, + "response": { + "body": { + "encoding": "utf-8", + "string": "{\"upload\":{\"token\":\"343fz6gCfPmOZn5cJ2v88tKXt\",\"expires_at\":\"2024-08-23T05:48:17Z\",\"attachments\":[{\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/attachments/32535150721043.json\",\"id\":32535150721043,\"file_name\":\"README.md\",\"content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/szo0KO3YjKNc9SxOWR0CJc9eQ/?name=README.md\",\"mapped_content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/szo0KO3YjKNc9SxOWR0CJc9eQ/?name=README.md\",\"content_type\":\"application/x-genesis-rom\",\"size\":14165,\"width\":null,\"height\":null,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\",\"thumbnails\":[]}],\"attachment\":{\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/attachments/32535150721043.json\",\"id\":32535150721043,\"file_name\":\"README.md\",\"content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/szo0KO3YjKNc9SxOWR0CJc9eQ/?name=README.md\",\"mapped_content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/szo0KO3YjKNc9SxOWR0CJc9eQ/?name=README.md\",\"content_type\":\"application/x-genesis-rom\",\"size\":14165,\"width\":null,\"height\":null,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\",\"thumbnails\":[]}}}" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8b6017ebbf955ef5-PDX" + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "1187" + ], + "Content-Type": [ + "text/plain; charset=utf-8" + ], + "Date": [ + "Tue, 20 Aug 2024 05:48:17 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=VVVdXtFCAVu5LkFZDhwK4rin63OBIyf0%2BKRvOBKQmcnbQPdsXzM%2FDhRg4OcGOIxB2bjeWGK4opr29%2BoP0iwg5XDZaa0SeUfcoXOpGJnlq5y6wsCPTTvEEoN9mRzTdDK2IfVgkf4q\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "X-Request-ID": [ + "8b6017ebbf955ef5-PDX" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "access-control-allow-origin": [ + "*" + ], + "access-control-expose-headers": [ + "X-Zendesk-API-Warn,X-Zendesk-User-Id,X-Zendesk-User-Session-Expires-At" + ], + "cache-control": [ + "max-age=0, private, must-revalidate" + ], + "etag": [ + "W/\"59b122719a1709752512f71036bd02d2\"" + ], + "location": [ + "https://d3v-zenpydev.zendesk.com/api/v2/attachments/32535150721043.json" + ], + "ratelimit-limit": [ + "700" + ], + "ratelimit-remaining": [ + "699" + ], + "ratelimit-reset": [ + "43" + ], + "set-cookie": [ + "_zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca; path=/; expires=Wed, 20 Aug 2025 01:56:32 GMT; secure; HttpOnly; SameSite=None" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "699" + ], + "x-runtime": [ + "0.181056" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-application-version": [ + "v21803" + ], + "x-zendesk-origin-server": [ + "classic-app-server-855c884564-mv7h4" + ] + }, + "status": { + "code": 201, + "message": "Created" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/uploads.json?filename=README.md" + } + }, + { + "recorded_at": "2024-08-20T05:48:17", + "request": { + "body": { + "encoding": "utf-8", + "string": "" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "0" + ], + "Cookie": [ + "_zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca" + ], + "User-Agent": [ + "Zenpy/2.0.49" + ] + }, + "method": "DELETE", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/uploads/343fz6gCfPmOZn5cJ2v88tKXt.json" + }, + "response": { + "body": { + "encoding": null, + "string": "" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8b6017effdd05ef5-PDX" + ], + "Connection": [ + "keep-alive" + ], + "Date": [ + "Tue, 20 Aug 2024 05:48:17 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=VpDDoH%2BjkCu%2F9rRdP5K8w3t30tsQ%2BIKjPYnZRwK3SmRSlmBxp%2F0SyB%2BTa9OggkPl02gPZPx8BBXuolnZHrBF26fRaeiLTJDHu4aBTY8UwmUyqpT2GtuaR9iqEcLwe7CeQ1cr%2Fc%2BJ\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "X-Request-ID": [ + "8b6017effdd05ef5-PDX" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "cache-control": [ + "no-cache" + ], + "ratelimit-limit": [ + "700" + ], + "ratelimit-remaining": [ + "698" + ], + "ratelimit-reset": [ + "43" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "698" + ], + "x-runtime": [ + "0.122429" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-application-version": [ + "v21803" + ], + "x-zendesk-origin-server": [ + "classic-app-server-855c884564-dt5rt" + ] + }, + "status": { + "code": 204, + "message": "No Content" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/uploads/343fz6gCfPmOZn5cJ2v88tKXt.json" + } + } + ], + "recorded_with": "betamax/0.8.1" +} \ No newline at end of file diff --git a/tests/test_api/betamax/TestAttachmentUpload.test_redact_attachment-upload-single.json b/tests/test_api/betamax/TestAttachmentUpload.test_redact_attachment-upload-single.json new file mode 100644 index 00000000..e18ca234 --- /dev/null +++ b/tests/test_api/betamax/TestAttachmentUpload.test_redact_attachment-upload-single.json @@ -0,0 +1,532 @@ +{ + "http_interactions": [ + { + "recorded_at": "2024-09-06T18:52:10", + "request": { + "body": { + "encoding": "utf-8", + "string": "" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "15156" + ], + "Content-Type": [ + "application/octet-stream" + ], + "Cookie": [ + "_zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca" + ], + "User-Agent": [ + "Zenpy/2.0.50" + ] + }, + "method": "POST", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/uploads.json?filename=README.md" + }, + "response": { + "body": { + "encoding": "utf-8", + "string": "{\"upload\":{\"token\":\"uIMhPFteaq1vsQy08sTW3lXVQ\",\"expires_at\":\"2024-09-09T18:52:10Z\",\"attachments\":[{\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/attachments/33144647570963.json\",\"id\":33144647570963,\"file_name\":\"README.md\",\"content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/ipl7iHe99p1WhkrNBVSW1L8Ie/?name=README.md\",\"mapped_content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/ipl7iHe99p1WhkrNBVSW1L8Ie/?name=README.md\",\"content_type\":\"application/x-genesis-rom\",\"size\":15156,\"width\":null,\"height\":null,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\",\"thumbnails\":[]}],\"attachment\":{\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/attachments/33144647570963.json\",\"id\":33144647570963,\"file_name\":\"README.md\",\"content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/ipl7iHe99p1WhkrNBVSW1L8Ie/?name=README.md\",\"mapped_content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/ipl7iHe99p1WhkrNBVSW1L8Ie/?name=README.md\",\"content_type\":\"application/x-genesis-rom\",\"size\":15156,\"width\":null,\"height\":null,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\",\"thumbnails\":[]}}}" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8bf0a791bfdaf9cc-SJC" + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "1187" + ], + "Content-Type": [ + "text/plain; charset=utf-8" + ], + "Date": [ + "Fri, 06 Sep 2024 18:52:10 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=KPwhOnjgX4guuclhGWqumltTbY3dl69%2B7Oa7vY1%2FFFjfaJPw4vztw7HYxbZWdQGahBozXta4NVJnKnXhPW5aaYa6oldffyycgBdulQFpmu65TcST2agt8CrODc9kikl3IA2lwGzx\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "Set-Cookie": [ + "__cfruid=bb4dc6030a06758763996b140beeb067d64b4f3a-1725648730; path=/; domain=.d3v-zenpydev.zendesk.com; HttpOnly; Secure; SameSite=None", + "_cfuvid=SOG4oW7ZzVKQIUGifDIZjcRMOGgN5rAiEeFSw7BH_Oo-1725648730195-0.0.1.1-604800000; path=/; domain=.d3v-zenpydev.zendesk.com; HttpOnly; Secure; SameSite=None" + ], + "X-Request-ID": [ + "8bf0a791bfdaf9cc-SJC" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "access-control-allow-origin": [ + "*" + ], + "access-control-expose-headers": [ + "X-Zendesk-API-Warn,X-Zendesk-User-Id,X-Zendesk-User-Session-Expires-At" + ], + "cache-control": [ + "max-age=0, private, must-revalidate" + ], + "etag": [ + "W/\"280253dfeeab660ab8d3a0886c11935c\"" + ], + "location": [ + "https://d3v-zenpydev.zendesk.com/api/v2/attachments/33144647570963.json" + ], + "ratelimit-limit": [ + "700" + ], + "ratelimit-remaining": [ + "699" + ], + "ratelimit-reset": [ + "50" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "699" + ], + "x-runtime": [ + "0.206328" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-application-version": [ + "v21938" + ], + "x-zendesk-origin-server": [ + "classic-app-server-68c76ff84d-ks5hg" + ] + }, + "status": { + "code": 201, + "message": "Created" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/uploads.json?filename=README.md" + } + }, + { + "recorded_at": "2024-09-06T18:52:11", + "request": { + "body": { + "encoding": "utf-8", + "string": "{\"ticket\": {\"subject\": \"example ticket\", \"comment\": {\"body\": \"Some comment\", \"uploads\": \"uIMhPFteaq1vsQy08sTW3lXVQ\"}}}" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "118" + ], + "Content-Type": [ + "application/json" + ], + "Cookie": [ + "__cfruid=bb4dc6030a06758763996b140beeb067d64b4f3a-1725648730; _cfuvid=SOG4oW7ZzVKQIUGifDIZjcRMOGgN5rAiEeFSw7BH_Oo-1725648730195-0.0.1.1-604800000; _zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca" + ], + "User-Agent": [ + "Zenpy/2.0.50" + ] + }, + "method": "POST", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/tickets.json" + }, + "response": { + "body": { + "encoding": "utf-8", + "string": "{\"ticket\":{\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/tickets/33.json\",\"id\":33,\"external_id\":null,\"via\":{\"channel\":\"api\",\"source\":{\"from\":{},\"to\":{},\"rel\":null}},\"created_at\":\"2024-09-06T18:52:11Z\",\"updated_at\":\"2024-09-06T18:52:11Z\",\"generated_timestamp\":0,\"type\":null,\"subject\":\"example ticket\",\"raw_subject\":\"example ticket\",\"description\":\"Some comment\",\"priority\":null,\"status\":\"new\",\"recipient\":null,\"requester_id\":26155588908179,\"submitter_id\":26155588908179,\"assignee_id\":null,\"organization_id\":26155621659539,\"group_id\":null,\"collaborator_ids\":[],\"follower_ids\":[],\"email_cc_ids\":[],\"forum_topic_id\":null,\"problem_id\":null,\"has_incidents\":false,\"is_public\":true,\"due_at\":null,\"tags\":[],\"custom_fields\":[{\"id\":26155630658963,\"value\":null},{\"id\":26155656851603,\"value\":null}],\"satisfaction_rating\":null,\"sharing_agreement_ids\":[],\"custom_status_id\":26155621627539,\"fields\":[{\"id\":26155630658963,\"value\":null},{\"id\":26155656851603,\"value\":null}],\"followup_ids\":[],\"ticket_form_id\":26155621471251,\"brand_id\":26155621587603,\"allow_channelback\":false,\"allow_attachments\":true,\"from_messaging_channel\":false},\"audit\":{\"id\":33144692244371,\"ticket_id\":33,\"created_at\":\"2024-09-06T18:52:11Z\",\"author_id\":26155588908179,\"metadata\":{\"system\":{\"client\":\"Zenpy/2.0.50\",\"ip_address\":\"107.131.76.73\",\"location\":\"San Bruno, CA, United States\",\"latitude\":37.6261,\"longitude\":-122.4304},\"custom\":{}},\"events\":[{\"id\":33144692244499,\"type\":\"Comment\",\"author_id\":26155588908179,\"body\":\"Some comment\",\"html_body\":\"

Some comment

\",\"plain_body\":\"Some comment\",\"public\":true,\"attachments\":[{\"url\":\"https://d3v-zenpydev.zendesk.com/api/v2/attachments/33144647570963.json\",\"id\":33144647570963,\"file_name\":\"README.md\",\"content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/ipl7iHe99p1WhkrNBVSW1L8Ie/?name=README.md\",\"mapped_content_url\":\"https://d3v-zenpydev.zendesk.com/attachments/token/ipl7iHe99p1WhkrNBVSW1L8Ie/?name=README.md\",\"content_type\":\"application/x-genesis-rom\",\"size\":15156,\"width\":null,\"height\":null,\"inline\":false,\"deleted\":false,\"malware_access_override\":false,\"malware_scan_result\":\"not_scanned\",\"thumbnails\":[]}],\"audit_id\":33144692244371},{\"id\":33144692244627,\"type\":\"Create\",\"value\":\"example ticket\",\"field_name\":\"subject\"},{\"id\":33144692244755,\"type\":\"Create\",\"value\":\"26155588908179\",\"field_name\":\"requester_id\"},{\"id\":33144692244883,\"type\":\"Create\",\"value\":null,\"field_name\":\"priority\"},{\"id\":33144692245011,\"type\":\"Create\",\"value\":null,\"field_name\":\"type\"},{\"id\":33144692245139,\"type\":\"Create\",\"value\":\"new\",\"field_name\":\"status\"},{\"id\":33144692245267,\"type\":\"Create\",\"value\":\"26155621659539\",\"field_name\":\"organization_id\"},{\"id\":33144692245395,\"type\":\"Create\",\"value\":\"26155621587603\",\"field_name\":\"brand_id\"},{\"id\":33144692245523,\"type\":\"Create\",\"value\":\"26155621471251\",\"field_name\":\"ticket_form_id\"},{\"id\":33144692245651,\"type\":\"Create\",\"value\":\"26155621627539\",\"field_name\":\"custom_status_id\"},{\"id\":33144692245779,\"type\":\"Notification\",\"via\":{\"channel\":\"rule\",\"source\":{\"from\":{\"deleted\":false,\"title\":\"Notify requester of new proactive ticket\",\"id\":26155621683731,\"revision_id\":3},\"rel\":\"trigger\"}},\"subject\":\"{{ticket.title}}\",\"body\":\"This ticket was created on your behalf.\\n\\nTo add additional comments, reply to this email.\\n\\n{{ticket.latest_public_comment_html}}\",\"recipients\":[26155588908179]},{\"id\":33144692245907,\"type\":\"Notification\",\"via\":{\"channel\":\"rule\",\"source\":{\"from\":{\"deleted\":false,\"title\":\"Notify all agents of received request\",\"id\":26155621705619,\"revision_id\":3},\"rel\":\"trigger\"}},\"subject\":\"[{{ticket.account}}] {{ticket.title}}\",\"body\":\"A ticket (#{{ticket.id}}) by {{ticket.requester.name}} has been received. It is unassigned.\\n\\n{{ticket.latest_comment_html}}\",\"recipients\":[26155588908179]}],\"via\":{\"channel\":\"api\",\"source\":{\"from\":{},\"to\":{\"name\":\"Joshua Teitelbaum\",\"address\":\"jteitelbaum@zendesk.com\"},\"rel\":null}}}}" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8bf0a793d9a6f9cc-SJC" + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "3923" + ], + "Content-Type": [ + "application/json; charset=utf-8" + ], + "Date": [ + "Fri, 06 Sep 2024 18:52:11 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=3uTmNSEudyqHLJDq6A1qpVezA6R4zOLZ0J2O8OW8YlJZEhfzkbV99BgKfFsyWf6ZE5NHl31V1LbaxZZX270yXLsWjr4Ru909yLHxYlVTZeG6le%2FsebgG1pidSojjS%2Ba2jZNUC2fb\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "X-Request-ID": [ + "8bf0a793d9a6f9cc-SJC" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "cache-control": [ + "max-age=0, private, must-revalidate" + ], + "etag": [ + "W/\"20fc9f93d11af9a99e9071865fca93e2\"" + ], + "location": [ + "https://d3v-zenpydev.zendesk.com/api/v2/tickets/33.json" + ], + "ratelimit-limit": [ + "700" + ], + "ratelimit-remaining": [ + "698" + ], + "ratelimit-reset": [ + "49" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "698" + ], + "x-runtime": [ + "0.848970" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-application-version": [ + "v21938" + ], + "x-zendesk-origin-server": [ + "classic-app-server-68c76ff84d-fgdmg" + ], + "zendesk-ratelimit-ticket-request-create-by-user-and-ip": [ + "total=300; remaining=299; resets=50" + ] + }, + "status": { + "code": 201, + "message": "Created" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/tickets.json" + } + }, + { + "recorded_at": "2024-09-06T18:52:12", + "request": { + "body": { + "encoding": "utf-8", + "string": "" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Cookie": [ + "__cfruid=bb4dc6030a06758763996b140beeb067d64b4f3a-1725648730; _cfuvid=SOG4oW7ZzVKQIUGifDIZjcRMOGgN5rAiEeFSw7BH_Oo-1725648730195-0.0.1.1-604800000; _zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca" + ], + "User-Agent": [ + "Zenpy/2.0.50" + ] + }, + "method": "GET", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/tickets/33/comments.json?include_inline_images=false&page%5Bsize%5D=100" + }, + "response": { + "body": { + "base64_string": "H4sIAAAAAAAAA7xU227bOBD9FWOA9InW1bItIU63zgUbo92Fk3rbeFMItDi2GFOkQFJunMD/XtCXxHt56MNuHoecmTPnzOUZClVVKK2B7M9n4AyyOA47nW4aRZ1OJ00J2HWNkMH5zg8I0MaWSufON+qGSZL0+2nQD3spgZlia8jgVlXYKl4CSluJfP91yviqVQhqzOAenlh773UPLcb14N4lV/dwdlr/1T7OeOrXZ6c+46szIFALymX+77h1MxO8gMzqBglQa2lRvnJttIAMSmtrk/n+UyzrhjHpPaFkaJZeoSqf1txfRf5RpL9Tp9NLekHajb0HoySQI90OPwTmXGAuaeXEu7n8cPHp0qsYECiUtCht/jP4R8BWLVH6vBY9/iumaR1+KZf6t+Eft1/Cj/1r9N87qMExUEXrGln+ZngHoP3A0LoWvKCWK+k/thco0XDT1qoCAoY/IWRhEiZdAt85syVkshGCQIl8UdqDxaXgEiGbU2GQAEOBFtmLXVHxnWrMaVGgMblaodac4T/+TUFlrtE0wkIGUtnti0RXtS2baiYpF24qvm2+uflm3OZ/W4W4FxJYcQrZMxSlCxZbjtyxUY0u0P3MHb3seUPAKmfv2z9Spmxo6zNyi2JGGycBZUyjMZDBg315/+WoG7AhoB2Kk2KzIVBopBZZTh2JKIg67SBtB93PYT9LoiwMp67naCmjdlumWRuL1bZgwd1GZDBFWa/9yAu8JHBjW+evZYRBzwvj0Ot1vV4MBITaNc/tFZWtoW6kIq3zD6Q1kdwia91aatE4T2q5bZzucc/rRt3QBcvF/q0dRpHXiYOOo9AYu1No45R21br6SmrySunXxtG5RZ0XjTZKQwa4Hq2uHxS/i0fi7uuNmF6lJTt/rKfn1+Za3vHf+YjeyJvxp4fRdDy5Go4nV+VkmQ5nX0s9uyzleHLJ5+PBAAjMcK40/g+pNwQEl0vj+NQaVz95Wywvlri9K/7hEm+PynsuC9EwzHcrkPOKLtAMtvq8q+kCT5LhjstJcjH4b1icxBcn8cUhu9tRlzsM3KhIfLRvQGnb+LdgtNn8AAAA//8DAKR8xBb7BgAA", + "encoding": "utf-8", + "string": "" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8bf0a79be98ef9cc-SJC" + ], + "Connection": [ + "keep-alive" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=utf-8" + ], + "Date": [ + "Fri, 06 Sep 2024 18:52:12 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=ipcO25m5nKBzTpTfBaQxLuNEtEa54AUZb7rBSnGXwrEitT%2F6qu%2Bf8dQiEP9uJcQPmORxv2WJkSzOo4nzeEHh90HWh4qF0LQOIgxJhNmV89IIN03bvrzPg6Xqg3HGO%2FyfpXa21qXG\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "Transfer-Encoding": [ + "chunked" + ], + "X-Request-ID": [ + "8bf0a79be98ef9cc-SJC" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "cache-control": [ + "max-age=0, private, must-revalidate" + ], + "etag": [ + "W/\"1baf8555c4f958a8dbf600a470f46e10\"" + ], + "last-modified": [ + "Fri, 06 Sep 2024 18:52:11 GMT" + ], + "ratelimit-limit": [ + "700" + ], + "ratelimit-remaining": [ + "697" + ], + "ratelimit-reset": [ + "48" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "697" + ], + "x-runtime": [ + "0.115100" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-application-version": [ + "v21938" + ], + "x-zendesk-origin-server": [ + "classic-app-server-68c76ff84d-jtwss" + ] + }, + "status": { + "code": 200, + "message": "OK" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/tickets/33/comments.json?include_inline_images=false&page%5Bsize%5D=100" + } + }, + { + "recorded_at": "2024-09-06T18:52:12", + "request": { + "body": { + "encoding": "utf-8", + "string": "{}" + }, + "headers": { + "Accept": [ + "*/*" + ], + "Accept-Encoding": [ + "gzip, deflate" + ], + "Authorization": [ + "Basic " + ], + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "2" + ], + "Content-Type": [ + "application/json" + ], + "Cookie": [ + "__cfruid=bb4dc6030a06758763996b140beeb067d64b4f3a-1725648730; _cfuvid=SOG4oW7ZzVKQIUGifDIZjcRMOGgN5rAiEeFSw7BH_Oo-1725648730195-0.0.1.1-604800000; _zendesk_cookie=BAhJIhl7ImRldmljZV90b2tlbnMiOnt9fQY6BkVU--0bf2100788cb010d0183feca16aaf88ccaf719ca" + ], + "User-Agent": [ + "Zenpy/2.0.50" + ] + }, + "method": "PUT", + "uri": "https://d3v-zenpydev.zendesk.com/api/v2/tickets/33/comments/33144692244499/attachments/33144647570963/redact.json" + }, + "response": { + "body": { + "base64_string": "H4sIAAAAAAAAA7zPTUsDQQwG4P+S83bHdfthB0QUCwrqQcEeRJZxJ+3GzmSGnWyrLf3vUqFa8eLJ45uX8CQbMCKmbjyygN5A1zrQ0IjEpJValxw7azlfI1tMi7wOXplIanmsvveSKsui3x/2R4PR0XhY5q8pMGRAFvTPJoMZOazYeAQN95Pzy9tJ7i1kUAcWZKn+4h/AEhbIiqIb0RWOx7GYNov27uLxYVrcnFyjOttRp4eQNzGirf7N20PyHnc/mxgd1UYosHrrzZExUeq1wUMGidYIuhgUg2EGK7LSgObOuQwapHkj+0TsiBH0zLiEGVh0KGi/sjduZVqsTF1jSlVYYtuSxV99qg1XLabOCWjgIJ8Txt3V0nT+hQ25BPrpebv9AAAA//8DAMHVFKAnAgAA", + "encoding": "utf-8", + "string": "" + }, + "headers": { + "CF-Cache-Status": [ + "DYNAMIC" + ], + "CF-RAY": [ + "8bf0a79f6d45f9cc-SJC" + ], + "Connection": [ + "keep-alive" + ], + "Content-Encoding": [ + "gzip" + ], + "Content-Type": [ + "application/json; charset=utf-8" + ], + "Date": [ + "Fri, 06 Sep 2024 18:52:12 GMT" + ], + "NEL": [ + "{\"success_fraction\":0.01,\"report_to\":\"cf-nel\",\"max_age\":604800}" + ], + "Report-To": [ + "{\"endpoints\":[{\"url\":\"https:\\/\\/a.nel.cloudflare.com\\/report\\/v4?s=njAdNlGYpTSuwqvU87aVEN9H7HTd6fbtgjEn7h6TkYDQHP7zNcGdQjCdC3O%2FZVI0eQuZRRMzeHnAAg%2FTtNa5R5g6m6xuPK0DtH%2BCBtr10IeuIAWq6TfAk3ePUZALTZPWABuUgwLA\"}],\"group\":\"cf-nel\",\"max_age\":604800}" + ], + "Server": [ + "cloudflare" + ], + "Transfer-Encoding": [ + "chunked" + ], + "X-Request-ID": [ + "8bf0a79f6d45f9cc-SJC" + ], + "X-Zendesk-API-Gateway": [ + "yes" + ], + "X-Zendesk-Zorg": [ + "yes" + ], + "cache-control": [ + "max-age=0, private, must-revalidate" + ], + "etag": [ + "W/\"87ec1837ce67ad73294ec4d287757e73\"" + ], + "ratelimit-limit": [ + "700" + ], + "ratelimit-remaining": [ + "696" + ], + "ratelimit-reset": [ + "48" + ], + "strict-transport-security": [ + "max-age=31536000; includeSubDomains" + ], + "x-frame-options": [ + "SAMEORIGIN" + ], + "x-rate-limit": [ + "700" + ], + "x-rate-limit-remaining": [ + "696" + ], + "x-runtime": [ + "0.511491" + ], + "x-zendesk-api-version": [ + "v2" + ], + "x-zendesk-application-version": [ + "v21938" + ], + "x-zendesk-origin-server": [ + "classic-app-server-68c76ff84d-b525q" + ], + "zendesk-ratelimit-per-ticket-updates": [ + "total=30; remaining=29; resets=468" + ] + }, + "status": { + "code": 200, + "message": "OK" + }, + "url": "https://d3v-zenpydev.zendesk.com/api/v2/tickets/33/comments/33144692244499/attachments/33144647570963/redact.json" + } + } + ], + "recorded_with": "betamax/0.8.1" +} \ No newline at end of file diff --git a/tests/test_api/test_create_update_delete_zendesk.py b/tests/test_api/test_create_update_delete_zendesk.py index d1e7fd3e..326a3fcf 100644 --- a/tests/test_api/test_create_update_delete_zendesk.py +++ b/tests/test_api/test_create_update_delete_zendesk.py @@ -12,6 +12,7 @@ from zenpy.lib.api_objects import ( Ticket, TicketAudit, + Comment, Group, User, Organization, @@ -22,6 +23,7 @@ Upload, UserField, CustomStatus, + Attachment ) from zenpy.lib.exception import ( @@ -100,6 +102,32 @@ def call_upload_method(self, *args, **kwargs): ): return self.zenpy_client.attachments.upload(*args, **kwargs) + def test_delete_attachment(self): + with open(self.file_path, "rb") as f: + with self.recorder.use_cassette( + "{}-upload-single".format(self.generate_cassette_name()), + serialize_with="prettyjson", + ): + upload = self.zenpy_client.attachments.upload(f,target_name="README.md") + retval = self.zenpy_client.attachments.delete(upload.token) + self.assertTrue((retval is not None) and (retval.status_code >= 200 and retval.status_code < 300)) + + def test_redact_attachment(self): + with open(self.file_path, "rb") as f: + with self.recorder.use_cassette( + "{}-upload-single".format(self.generate_cassette_name()), + serialize_with="prettyjson", + ): + upload = self.zenpy_client.attachments.upload(f,target_name="README.md") + comment = Comment(body='Some comment') + comment.uploads = upload.token + ticket = Ticket(subject='example ticket', comment=comment) + ticket_audit = self.zenpy_client.tickets.create(ticket) + ticket = ticket_audit.ticket + the_comment = self.zenpy_client.tickets.comments(ticket).values[0] + attachment = self.zenpy_client.attachments.redact(ticket, the_comment, the_comment.attachments[0].id) + self.assertTrue((attachment is not None) and (attachment.id == the_comment.attachments[0].id)) + def test_upload_with_file_obj(self): with open(self.file_path, "rb") as f: upload = self.call_upload_method(f, target_name="README.md") diff --git a/zenpy/lib/api.py b/zenpy/lib/api.py index e1d04e19..0f29b3a9 100644 --- a/zenpy/lib/api.py +++ b/zenpy/lib/api.py @@ -1256,6 +1256,28 @@ def download(self, attachment_id, destination=None): self._write_to_stream(attachment.content_url, f) return destination + def delete(self, token_id): + """ + Delete an attachment from Zendesk. + + :param token_id: id of the attachment to delete + :return: the path the file was written to or the BytesIO object + """ + return UploadRequest(self).delete(token_id) + + @extract_id(Ticket, TicketComment) + def redact(self, ticket, comment, attachment_id): + """ + Redacts a comment's attacchment + + :param ticket: the ticket that owns the comments + :param comment: the comment that owns the attachment + :param attachment_id: the attachment ID of the attachment to be deleted/redacted + :return: the attachment in JSON with the attachment id with HTTP 200 OK + """ + url = self._build_url(self.endpoint.redact(ticket, comment, attachment_id)) + return self._put(url, payload={}) + def _write_to_stream(self, source_url, stream): r = self.session.get(source_url, stream=True) for chunk in r.iter_content(chunk_size=None): diff --git a/zenpy/lib/endpoint.py b/zenpy/lib/endpoint.py index 5756f6c4..f3ea0be0 100644 --- a/zenpy/lib/endpoint.py +++ b/zenpy/lib/endpoint.py @@ -581,6 +581,9 @@ class EndpointFactory(object): activities = PrimaryEndpoint('activities') attachments = PrimaryEndpoint('attachments') attachments.upload = AttachmentEndpoint('uploads.json') + attachments.delete = SecondaryEndpoint('uploads/%(id)s.json') + attachments.redact = MultipleIDEndpoint( + 'tickets/{}/comments/{}/attachments/{}/redact.json') automations = PrimaryEndpoint('automations') brands = PrimaryEndpoint('brands') chats = ChatEndpoint('chats') diff --git a/zenpy/lib/request.py b/zenpy/lib/request.py index a7e6791e..9039e23e 100644 --- a/zenpy/lib/request.py +++ b/zenpy/lib/request.py @@ -261,9 +261,9 @@ def post(self, def put(self, api_objects, *args, **kwargs): raise NotImplementedError("POST is not implemented fpr UploadRequest!") - def delete(self, api_objects, *args, **kwargs): - raise NotImplementedError( - "DELETE is not implemented fpr UploadRequest!") + def delete(self, token, *args, **kwargs): + url = self.api._build_url(self.api.endpoint.delete(id=token)) + return self.api._delete(url) class UserMergeRequest(BaseZendeskRequest):