최근 검색


최근 검색 없음

Tuomas Savonius's Avatar

Tuomas Savonius

가입한 날짜: 2021년 4월 15일

·

마지막 활동: 2024년 12월 31일

팔로잉

0

팔로워

0

총 활동 수

45

투표 수

3

플랜 수

18

활동 개요

님의 최근 활동 Tuomas Savonius

Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Developer - Zendesk SDKs

I tried using secure settings too, will not work because:

 

댓글 보기 · 2024년 12월 31일에 게시됨 · Tuomas Savonius

0

팔로워

0

투표 수

0

댓글


Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Developer - Zendesk Apps Framework (ZAF)

Any updates? Why is this blocked in Zafclient?

댓글 보기 · 2024년 12월 23일에 게시됨 · Tuomas Savonius

0

팔로워

0

투표 수

0

댓글


Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Developer - Zendesk SDKs

The issue is still present as of December 2024. I encountered the same "AttachmentUnprocessable" error (status 422) when trying to upload files using the ZAF client. I implemented the file upload using the regular uploads endpoint like this:


const uploadRequest = {
    url: `/api/v2/uploads?filename=${file.name}.${file.fileExtension}`,
    method: 'POST',
    contentType: 'application/binary',
    data: file.url  // ArrayBuffer from FileReader
};

Even with proper ArrayBuffer handling through FileReader and following the API documentation exactly, the ZAF client still fails to process the attachment. This confirms what @Shawn mentioned earlier about ZAF client not supporting binary file uploads, and the issue appears to remain unfixed despite the recent update.

댓글 보기 · 2024년 12월 23일에 게시됨 · Tuomas Savonius

0

팔로워

0

투표 수

0

댓글


Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Developer - Zendesk APIs

I am not a python dev, but I have hit this 1000 hard limit in my C# apps too.
Here's how we cleverly bypass this restriction:

  1. Time Windowing: Instead of trying to fetch all tickets at once, we use a moving time window.
    • Start with the current date and time as the end of our window.
    • Set a start date some time in the past (e.g., 30 days ago).
    • Fetch tickets within this time window.
  2. Pagination Within Windows: For each time window, we use pagination to get all results.
    • This ensures we get all tickets within a specific time range.
  3. Moving Backwards: If we haven't reached our desired number of tickets:
    • Move the time window back (i.e., set the end date to our previous start date).
    • Repeat the process with the new time window.
  4. API Limit Detection: We check for the specific API limit message in responses.
    • If hit, we adjust our time window and continue.
  5. Deduplication: We keep track of seen ticket IDs to avoid duplicates.
    • This is crucial as time windows may overlap.
  6. Sorting: We sort results by 'updated_at' in descending order.
    • This ensures we always get the most recently updated tickets first.
       

I tried to make a python script with this approach, please keep in mind the code is untested and I am not a python expert!

 

import requests
from datetime import datetime, timedelta
from typing import List, Dict, Any, Tuple
import logging
import sys
import time

class ZendeskTicketFetcher:
   def __init__(self, subdomain: str, email: str, api_token: str):
       self.base_url = f"https://{subdomain}.zendesk.com/api/v2"
       self.auth = (f"{email}/token", api_token)
       self.logger = logging.getLogger(__name__)

   def get_solved_tickets(self, max_tickets: int = 2000) -> List[Dict[str, Any]]:
       end_date = datetime.utcnow()
       start_date = end_date - timedelta(days=30)  # Start with a 30-day window
       results = []
       seen_ticket_ids = set()

       while len(results) < max_tickets and start_date > datetime(2000, 1, 1):  # Arbitrary lower bound
           tickets, next_page, api_limit_hit = self.fetch_tickets(start_date, end_date)
           if api_limit_hit:
               end_date = start_date
               start_date -= timedelta(days=30)
               continue

           new_tickets = [t for t in tickets if t['id'] not in seen_ticket_ids]
           results.extend(new_tickets)
           seen_ticket_ids.update(t['id'] for t in new_tickets)

           while next_page and len(results) < max_tickets:
               tickets, next_page, api_limit_hit = self.fetch_tickets(start_date, end_date, next_page)
               if api_limit_hit:
                   break
               new_tickets = [t for t in tickets if t['id'] not in seen_ticket_ids]
               results.extend(new_tickets)
               seen_ticket_ids.update(t['id'] for t in new_tickets)

           if len(results) < max_tickets:
               end_date = start_date
               start_date -= timedelta(days=30)

       return results[:max_tickets]

   def fetch_tickets(self, start_date: datetime, end_date: datetime, page_url: str = None) -> Tuple[List[Dict[str, Any]], str, bool]:
       try:
           if page_url:
               response = requests.get(page_url, auth=self.auth)
           else:
               url = f"{self.base_url}/search.json"
               query = f"type:ticket updated>{start_date.isoformat()} updated<{end_date.isoformat()} status:solved"
               params = {
                   'query': query,
                   'sort_by': 'updated_at',
                   'sort_order': 'desc'
               }
               response = requests.get(url, auth=self.auth, params=params)

           response.raise_for_status()
           data = response.json()
           
           api_limit_hit = self.check_api_limit(response.status_code, response.text)
           if api_limit_hit:
               return [], None, True

           return data['results'], data.get('next_page'), False

       except requests.RequestException as e:
           self.logger.error(f"Failed to fetch tickets: {str(e)}")
           sys.exit(1)

   def check_api_limit(self, status_code: int, response_content: str) -> bool:
       if status_code == 200:
           return False

       if "Requested response size was greater than Search Response Limits" in response_content:
           self.logger.warning("API limit hit. Adjusting time window.")
           return True

       self.logger.error(f"Failed to get ticket data from Zendesk. Status code: {status_code}")
       self.logger.error(f"Response content: {response_content}")
       time.sleep(2)
       sys.exit(1)

# Usage
logging.basicConfig(level=logging.INFO)
fetcher = ZendeskTicketFetcher('your_subdomain', 'your_email', 'your_api_token')
solved_tickets = fetcher.get_solved_tickets(2000)
print(f"Retrieved {len(solved_tickets)} solved tickets")

댓글 보기 · 2024년 7월 17일에 게시됨 · Tuomas Savonius

0

팔로워

0

투표 수

0

댓글


Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Developer - Zendesk Apps Framework (ZAF)

No updates? I think this is a massive flaw with the ZAF client / secure string implementation.

댓글 보기 · 2024년 7월 17일에 게시됨 · Tuomas Savonius

0

팔로워

0

투표 수

0

댓글


Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Developer - Zendesk SDKs

This is a absolute disaster, because using another HTTP client makes the whole process insecure.
https://support.zendesk.com/hc/en-us/community/posts/5200263029914-Uploading-attachments-securely-to-a-ticket

댓글 보기 · 2024년 7월 17일에 게시됨 · Tuomas Savonius

0

팔로워

1

투표

0

댓글


Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Feedback - Ticketing system (Support)

This affect us as well, had to make a new view for agents, shame this functionality was so good in the previous dashboard.

댓글 보기 · 2024년 1월 05일에 게시됨 · Tuomas Savonius

0

팔로워

0

투표 수

0

댓글


Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Developer - Zendesk Apps Framework (ZAF)

Do you have any updates on this? This is a high concern item for us currently

댓글 보기 · 2023년 7월 06일에 게시됨 · Tuomas Savonius

0

팔로워

1

투표

0

댓글


Tuomas Savonius님이 에 댓글을 입력함

커뮤니티 댓글 Developer - Zendesk Apps Framework (ZAF)

Duh what a stupid mistake! That fixed the issue. Funny that the adding worked even if the data was given as a string tho!

댓글 보기 · 2023년 6월 01일에 게시됨 · Tuomas Savonius

0

팔로워

0

투표 수

0

댓글


Tuomas Savonius님이 에 게시물을 만듦

게시물 Developer - Zendesk Apps Framework (ZAF)

for some reason, the ticket.collaborators.remove does not work, the adding works fine. No errors in console, verified to happen with latest mozilla, chrome and edge browsers. The id's are 100% correct.

Any idea why this is the case?

         installationNeedChanged() {

          if (!this.installOrderOptions || this.installOrderOptions.noDevices) {
              // THIS DOES NOT WORK (Console logs the removing collaborators, and no erros but the collaborators are not removed)
              console.log('removing collaborators');
              this._client.invoke("ticket.collaborators.remove", { "id": "367232408557" });
                this._client.invoke("ticket.collaborators.remove", { "id": "367232408417" });

          } else {
              // THIS WORKS correctly
              console.log('adding collaborators');
              this._client.invoke("ticket.collaborators.add", { "id": "367232408557" });
              this._client.invoke("ticket.collaborators.add", { "id": "367232408417" });
          }
        },

2023년 5월 31일에 게시됨 · Tuomas Savonius

0

팔로워

2

투표 수

2

댓글