Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib/zendesk_api/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions lib/zendesk_api/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 54 additions & 0 deletions lib/zendesk_api/resources.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
99 changes: 99 additions & 0 deletions spec/core/resources/custom_field_symbol_proxy_spec.rb
Original file line number Diff line number Diff line change
@@ -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
57 changes: 57 additions & 0 deletions spec/live/ticket_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading