diff --git a/lib/zendesk_api/client.rb b/lib/zendesk_api/client.rb index ec5fed22..cd84ad52 100644 --- a/lib/zendesk_api/client.rb +++ b/lib/zendesk_api/client.rb @@ -31,6 +31,10 @@ class Client # @return [Array] Custom response callbacks attr_reader :callbacks + def ticket_fields_metadata + @ticket_fields_metadata ||= [] + end + # Handles resources such as 'tickets'. Any options are passed to the underlying collection, except reload which disregards # memoization and creates a new Collection instance. # @return [Collection] Collection instance for resource @@ -102,6 +106,17 @@ def initialize set_token_auth set_default_logger add_warning_callback + load_ticket_fields_metadata if @config.load_ticket_fields_metadata + end + + def load_ticket_fields_metadata + @ticket_fields_metadata = [] + ticket_fields.all do |f| + if f + @ticket_fields_metadata << f + end + end + @ticket_fields_metadata end # Creates a connection if there is none, otherwise returns the existing connection. diff --git a/lib/zendesk_api/configuration.rb b/lib/zendesk_api/configuration.rb index 4f34df73..ba1e22e1 100644 --- a/lib/zendesk_api/configuration.rb +++ b/lib/zendesk_api/configuration.rb @@ -37,8 +37,12 @@ class Configuration # @return [String] OAuth2 access_token attr_accessor :access_token + # @return [String] url_based_access_token attr_accessor :url_based_access_token + # @return [Boolean] load_ticket_fields_metadata + attr_accessor :load_ticket_fields_metadata + # Use this cache instead of default ZendeskAPI::LRUCache.new # - must respond to read/write/fetch e.g. ActiveSupport::Cache::MemoryStore.new) # - pass false to disable caching diff --git a/lib/zendesk_api/resources.rb b/lib/zendesk_api/resources.rb index 7df38c4e..27a232b4 100644 --- a/lib/zendesk_api/resources.rb +++ b/lib/zendesk_api/resources.rb @@ -464,6 +464,60 @@ class Ticket < Resource extend UpdateMany extend DestroyMany + # Proxy to trap array operator usage on custom_field_symbol + class CustomFieldSymbolProxy + def initialize(ticket, _arr) + @ticket = ticket + @field_array = @ticket.custom_fields || [] + end + + def [](key) + raise "Cannot find custom field #{key}, configuration ticket_fields_metadata is OFF" unless + @ticket.instance_variable_get("@client").ticket_fields_metadata + # Trap read access + fld = @ticket.instance_variable_get("@client").ticket_fields_metadata.find { |val| val[:title] == key } + raise "Cannot find custom field #{key}" unless fld + cf = @ticket.custom_fields.find { |h| h[:id] == fld[:id] } + cf ? cf[:value] : nil + end + + def []=(key, value) + raise "Cannot find custom field #{key}, configuration ticket_fields_metadata is OFF" unless + @ticket.instance_variable_get("@client").ticket_fields_metadata + # Trap write access + fld = @ticket.instance_variable_get("@client").ticket_fields_metadata.find { |val| val[:title] == key } + raise "Cannot find custom field #{key}" unless fld + cf = @ticket.custom_fields.find { |h| h[:id] == fld[:id] } if @ticket.custom_fields + if cf + cf[:value] = value + else + @ticket.custom_fields << { id: fld[:id], value: value } + end + end + + def to_a + @field_array + end + + # Delegate other hash methods as needed + def method_missing(method, ...) + @field_array.send(method, ...) + end + + def respond_to_missing?(method, include_private = false) + @field_array.respond_to?(method, include_private) + end + end + + def custom_field_symbol + @custom_field_symbol ||= CustomFieldSymbolProxy.new(self, @custom_field_symbol) + end + + def custom_field_symbol=(val) + @custom_field_symbol = val + @custom_field_symbol_proxy = CustomFieldSymbolProxy.new(self, @custom_field_symbol) + end + def self.cbp_path_regexes [/^tickets$/, %r{organizations/\d+/tickets}, %r{users/\d+/tickets/requested}] end diff --git a/spec/core/resources/custom_field_symbol_proxy_spec.rb b/spec/core/resources/custom_field_symbol_proxy_spec.rb new file mode 100644 index 00000000..69922e94 --- /dev/null +++ b/spec/core/resources/custom_field_symbol_proxy_spec.rb @@ -0,0 +1,99 @@ +require 'core/spec_helper' +require_relative '../../../lib/zendesk_api/resources' + +RSpec.describe ZendeskAPI::Ticket::CustomFieldSymbolProxy do + let(:field_metadata) do + [ + { id: 1, title: "foo" }, + { id: 2, title: "bar" } + ] + end + let(:client) do + double("Client").tap do |c| + allow(c).to receive(:ticket_fields_metadata).and_return(field_metadata) + end + end + let(:ticket) do + t = ZendeskAPI::Ticket.new({}) + t.instance_variable_set(:@client, client) + t.instance_variable_set(:@custom_fields, [{ id: 1, value: "abc" }]) + def t.custom_fields + _foo = 1 + @custom_fields + end + t + end + let(:proxy) { described_class.new(ticket, nil) } + + describe "[] and []=" do + it "reads a custom field by symbol (existing)" do + expect(proxy["foo"]).to eq("abc") + end + + it "returns nil for existing field with no value" do + ticket.instance_variable_set(:@custom_fields, [{ id: 1 }]) + expect(proxy["foo"]).to be_nil + end + + it "raises error for missing field title" do + expect { proxy["baz"] }.to raise_error(/Cannot find custom field/) + end + + it "writes a custom field by symbol (existing)" do + proxy["foo"] = "updated" + expect(ticket.custom_fields.find { |h| h[:id] == 1 }[:value]).to eq("updated") + end + + it "writes a custom field by symbol (new)" do + proxy["bar"] = "def" + expect(ticket.custom_fields.find { |h| h[:id] == 2 }[:value]).to eq("def") + end + end + + describe "delegation and integration" do + it "delegates to_a" do + expect(proxy.to_a).to eq(ticket.custom_fields) + end + + it "delegates method_missing and respond_to_missing?" do + expect(proxy.respond_to?(:each)).to be true + expect(proxy.map { |h| h[:id] }).to include(1) + end + + it "returns proxy from custom_field_symbol accessor" do + t = ZendeskAPI::Ticket.new({}) + t.instance_variable_set(:@client, client) + t.instance_variable_set(:@custom_fields, [{ id: 1, value: "abc" }]) + def t.custom_fields + _foo = 1 + @custom_fields + end + expect(t.custom_field_symbol["foo"]).to eq("abc") + end + + it "updates proxy on custom_field_symbol= assignment" do + t = ZendeskAPI::Ticket.new({}) + t.instance_variable_set(:@client, client) + def t.custom_fields + _foo = 1 + @custom_fields + end + t.custom_field_symbol = [{ id: 1, value: "xyz" }] + expect(t.custom_field_symbol.to_a).to eq([{ id: 1, value: "xyz" }]) + end + end + + describe "[] and []= with missing ticket_fields_metadata" do + before do + allow(client).to receive(:ticket_fields_metadata).and_return(nil) + end + + it "raises error for [] when ticket_fields_metadata is missing" do + expect { proxy["foo"] }.to raise_error(/configuration ticket_fields_metadata is OFF/) + end + + it "raises error for []= when ticket_fields_metadata is missing" do + expect { proxy["foo"] = "bar" }.to raise_error(/configuration ticket_fields_metadata is OFF/) + end + end +end diff --git a/spec/live/ticket_spec.rb b/spec/live/ticket_spec.rb index 72646b32..74c45ae2 100644 --- a/spec/live/ticket_spec.rb +++ b/spec/live/ticket_spec.rb @@ -211,4 +211,61 @@ def valid_attributes end end end + + describe "CustomFieldSymbolProxy" do + let(:field_metadata) do + [ + { id: 1, title: "foo" }, + { id: 2, title: "bar" } + ] + end + let(:client) do + double("Client", :instance_variable_get => field_metadata) + end + let(:ticket) do + t = ZendeskAPI::Ticket.allocate + t.instance_variable_set(:@client, client) + t.instance_variable_set(:@custom_fields, [{ id: 1, value: "abc" }]) + t + end + let(:proxy) { ZendeskAPI::Ticket::CustomFieldSymbolProxy.new(ticket, nil) } + + it "reads a custom field by symbol" do + expect(proxy["foo"]).to eq("abc") + end + + it "raises error for missing field" do + expect { proxy["baz"] }.to raise_error(/Cannot find custom field/) + end + + it "writes a custom field by symbol" do + proxy["bar"] = "def" + expect(ticket.custom_fields.find { |h| h[:id] == 2 }[:value]).to eq("def") + end + + it "delegates to_a" do + expect(proxy.to_a).to eq(ticket.custom_fields) + end + + it "delegates method_missing and respond_to_missing?" do + expect(proxy.respond_to?(:each)).to be true + expect(proxy.map { |h| h[:id] }).to eq([1]) + end + + describe "integration with Ticket methods" do + it "returns proxy from custom_field_symbol accessor" do + t = ZendeskAPI::Ticket.allocate + t.instance_variable_set(:@client, client) + t.instance_variable_set(:@custom_fields, [{ id: 1, value: "abc" }]) + expect(t.custom_field_symbol["foo"]).to eq("abc") + end + + it "updates proxy on custom_field_symbol= assignment" do + t = ZendeskAPI::Ticket.allocate + t.instance_variable_set(:@client, client) + t.custom_field_symbol = [{ id: 1, value: "xyz" }] + expect(t.custom_field_symbol.to_a).to eq([{ id: 1, value: "xyz" }]) + end + end + end end