From b7b3f5b4e7ed761d55884e7d971fa4d697c97163 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 28 Apr 2026 12:14:06 +0200 Subject: [PATCH 1/6] Fixes #29 - JSON::ParserError on gateway timeout when proxy responds with HTML --- lib/zammad_api/list_base.rb | 2 +- lib/zammad_api/resources/base.rb | 22 ++++++++++++++----- lib/zammad_api/resources/ticket.rb | 2 +- .../resources/ticket_article_attachment.rb | 2 +- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/zammad_api/list_base.rb b/lib/zammad_api/list_base.rb index 7ac483a..0a40aaa 100644 --- a/lib/zammad_api/list_base.rb +++ b/lib/zammad_api/list_base.rb @@ -59,7 +59,7 @@ def request(request, url, parameter) }.join('&') response = @transport.get(url: url) - data = JSON.parse(response.body) + data = safe_json_parse(response.body) if response.status != 200 raise "Can't get .#{request} of object (#{@resource.class.name}): #{data['error']}" end diff --git a/lib/zammad_api/resources/base.rb b/lib/zammad_api/resources/base.rb index f329bfc..9456780 100644 --- a/lib/zammad_api/resources/base.rb +++ b/lib/zammad_api/resources/base.rb @@ -41,7 +41,7 @@ def changed? def destroy response = @transport.delete(url: "#{@url}/#{@attributes[:id]}") if response.body.to_s != '' && response.body.to_s != ' ' - data = JSON.parse(response.body) + data = safe_json_parse(response.body) end return true if response.status == 200 @@ -66,6 +66,12 @@ def self.url(value) @url = value end + def self.safe_json_parse(string) + JSON.parse(string) + rescue JSON::ParserError + {} + end + def self.all(transport, _) ZammadAPI::ListAll.new(self, transport, per_page: 100) end @@ -76,7 +82,7 @@ def self.search(transport, parameter) def self.find(transport, id) response = transport.get(url: "#{@url}/#{id}?expand=true") - data = JSON.parse(response.body) + data = safe_json_parse(response.body) if response.status != 200 raise "Can't find object (#{self.class.name}): #{data['error']}" end @@ -98,6 +104,12 @@ def self.destroy(transport, id) true end + protected + + def safe_json_parse(string) + self.class.safe_json_parse(string) + end + private def saved_attributes @@ -107,8 +119,8 @@ def saved_attributes end def save_new - response = @transport.post(url: "#{@url}?expand=true", params: @attributes) - attributes = JSON.parse(response.body) + response = @transport.post(url: "https://example.com?expand=true", params: @attributes) + attributes = safe_json_parse(response.body) return attributes if response.status == 201 save_error(attributes) @@ -120,7 +132,7 @@ def save_existing attributes_to_post[name] = values[1] end response = @transport.put(url: "#{@url}/#{@attributes[:id]}?expand=true", params: attributes_to_post) - attributes = JSON.parse(response.body) + attributes = safe_json_parse(response.body) return attributes if response.status == 200 diff --git a/lib/zammad_api/resources/ticket.rb b/lib/zammad_api/resources/ticket.rb index 8894ced..dfb9c12 100644 --- a/lib/zammad_api/resources/ticket.rb +++ b/lib/zammad_api/resources/ticket.rb @@ -3,7 +3,7 @@ class ZammadAPI::Resources::Ticket < ZammadAPI::Resources::Base def articles response = @transport.get(url: "/api/v1/ticket_articles/by_ticket/#{id}?expand=true") - data = JSON.parse(response.body) + data = safe_json_parse(response.body) if response.status != 200 raise "Can't get articles (#{self.class.name}): #{data['error']}" end diff --git a/lib/zammad_api/resources/ticket_article_attachment.rb b/lib/zammad_api/resources/ticket_article_attachment.rb index f0abb2e..a445f60 100644 --- a/lib/zammad_api/resources/ticket_article_attachment.rb +++ b/lib/zammad_api/resources/ticket_article_attachment.rb @@ -13,7 +13,7 @@ def download response = @transport.get(url: "/api/v1/ticket_attachment/#{ticket_id}/#{article_id}/#{id}") return response.body if response.status == 200 - data = JSON.parse(response.body) + data = safe_json_parse(response.body) raise "Can't get articles (#{self.class.name}): #{data['error']}" end end From c0e5459fc5f6dcaf3e7c2970ae6ba528df3106d2 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 28 Apr 2026 12:15:39 +0200 Subject: [PATCH 2/6] Revert debug code --- lib/zammad_api/resources/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/zammad_api/resources/base.rb b/lib/zammad_api/resources/base.rb index 9456780..f8be8e8 100644 --- a/lib/zammad_api/resources/base.rb +++ b/lib/zammad_api/resources/base.rb @@ -119,7 +119,7 @@ def saved_attributes end def save_new - response = @transport.post(url: "https://example.com?expand=true", params: @attributes) + response = @transport.post(url: "#{@url}?expand=true", params: @attributes) attributes = safe_json_parse(response.body) return attributes if response.status == 201 From c7f194fa294d6316dbc57ffc94232963c5554806 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 28 Apr 2026 12:32:26 +0200 Subject: [PATCH 3/6] Fix ListBase --- lib/zammad_api/list_base.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/zammad_api/list_base.rb b/lib/zammad_api/list_base.rb index 0a40aaa..3145397 100644 --- a/lib/zammad_api/list_base.rb +++ b/lib/zammad_api/list_base.rb @@ -76,5 +76,13 @@ def request(request, url, parameter) def perform_request(_parameter) raise "no perform_request implementation for #{self.class.name} found" end + + protected + + def safe_json_parse(string) + JSON.parse(string) + rescue JSON::ParserError + {} + end end end From 4de4efba630eac171e7e091edae805b7eb0861da Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 28 Apr 2026 13:56:07 +0200 Subject: [PATCH 4/6] Refactor safe_json_parse --- lib/zammad_api/json_helper.rb | 11 +++++++++++ lib/zammad_api/list_base.rb | 10 +++------- lib/zammad_api/resources/base.rb | 17 ++++------------- 3 files changed, 18 insertions(+), 20 deletions(-) create mode 100644 lib/zammad_api/json_helper.rb diff --git a/lib/zammad_api/json_helper.rb b/lib/zammad_api/json_helper.rb new file mode 100644 index 0000000..fd8ca4c --- /dev/null +++ b/lib/zammad_api/json_helper.rb @@ -0,0 +1,11 @@ +require 'json' + +module ZammadAPI + module JsonHelper + def safe_json_parse(string) + JSON.parse(string) + rescue JSON::ParserError + {} + end + end +end diff --git a/lib/zammad_api/list_base.rb b/lib/zammad_api/list_base.rb index 3145397..7523c0e 100644 --- a/lib/zammad_api/list_base.rb +++ b/lib/zammad_api/list_base.rb @@ -1,6 +1,9 @@ +require 'zammad_api/json_helper' + module ZammadAPI class ListBase include Enumerable + include ZammadAPI::JsonHelper def initialize(resource, transport, parameter = {}) @resource = resource @@ -77,12 +80,5 @@ def perform_request(_parameter) raise "no perform_request implementation for #{self.class.name} found" end - protected - - def safe_json_parse(string) - JSON.parse(string) - rescue JSON::ParserError - {} - end end end diff --git a/lib/zammad_api/resources/base.rb b/lib/zammad_api/resources/base.rb index f8be8e8..c894401 100644 --- a/lib/zammad_api/resources/base.rb +++ b/lib/zammad_api/resources/base.rb @@ -1,10 +1,13 @@ require 'cgi' -require 'json' +require 'zammad_api/json_helper' require 'zammad_api/transport' module ZammadAPI module Resources class Base + include ZammadAPI::JsonHelper + extend ZammadAPI::JsonHelper + attr_accessor :new_instance, :url, :attributes attr_reader :changes @@ -66,12 +69,6 @@ def self.url(value) @url = value end - def self.safe_json_parse(string) - JSON.parse(string) - rescue JSON::ParserError - {} - end - def self.all(transport, _) ZammadAPI::ListAll.new(self, transport, per_page: 100) end @@ -104,12 +101,6 @@ def self.destroy(transport, id) true end - protected - - def safe_json_parse(string) - self.class.safe_json_parse(string) - end - private def saved_attributes From f3665f76069e6eca0283c06b9d333fb72f3eca07 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 28 Apr 2026 13:56:23 +0200 Subject: [PATCH 5/6] Fix lint --- lib/zammad_api/list_base.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/zammad_api/list_base.rb b/lib/zammad_api/list_base.rb index 7523c0e..6b59114 100644 --- a/lib/zammad_api/list_base.rb +++ b/lib/zammad_api/list_base.rb @@ -79,6 +79,5 @@ def request(request, url, parameter) def perform_request(_parameter) raise "no perform_request implementation for #{self.class.name} found" end - end end From 9f77927e3559669508b216ef93d409ab822473a5 Mon Sep 17 00:00:00 2001 From: Martin Gruner Date: Tue, 28 Apr 2026 14:14:01 +0200 Subject: [PATCH 6/6] Add spec --- spec/zammad_api/json_helper_spec.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 spec/zammad_api/json_helper_spec.rb diff --git a/spec/zammad_api/json_helper_spec.rb b/spec/zammad_api/json_helper_spec.rb new file mode 100644 index 0000000..29eb956 --- /dev/null +++ b/spec/zammad_api/json_helper_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe ZammadAPI::JsonHelper do + subject(:helper) do + Class.new { include ZammadAPI::JsonHelper }.new + end + + describe '#safe_json_parse' do + it 'parses valid JSON' do + expect(helper.safe_json_parse('{"key":"value"}')).to eq('key' => 'value') + end + + it 'returns empty hash for invalid JSON' do + expect(helper.safe_json_parse('not json')).to eq({}) + end + + it 'returns empty hash for HTML responses' do + expect(helper.safe_json_parse('Bad Gateway')).to eq({}) + end + end +end