zendesk_api 1.37.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c5a2ced45e0201f6f9a45e5b8f7cf6951ca92c15ce893283c2289d0b260dc77
4
- data.tar.gz: 31fa7566865e7191f49dadf6a5e01573264f4fc62ddfd4f635f8f7edd4be9ab2
3
+ metadata.gz: 138f1dab58b3b37d450c4b1dd9200b1c878944b19571f53888b2c4d17a307276
4
+ data.tar.gz: 24459eda2e613e21be9924ad6bb571e53861f949d6be291f2bec3516cd1e9ad2
5
5
  SHA512:
6
- metadata.gz: a1b355c67c8126f69f32ff6cd24d03b81b75eec27a0a72044383d0e06a871ba5bd18778119523a013725162048393d7a6bf58f075013e852303255b78bccb73f
7
- data.tar.gz: 78ecdd8fd4d2fd889df06aea1ac98df7c68e6f89378f1358594b30853b23b3179f304ee4d87a781bda0047bc1970b8c28dfd147907d74223e28ffceb1676d1b1
6
+ metadata.gz: 55f26e4b66e457d5e9e634eefa44fc8b50da170ab2d5a5f97d2460daa423da04d7c607bfa626b1ecd7ce56117a1ee410e7213df6717d84a8e4a109392e1611a5
7
+ data.tar.gz: 0bab4d20e8989a0e1a4f8be708f76ccd462fddec1b880fb360591f9f048d74c5bbd17d20b678290fb6891dcb0fa6755139e5165fb7c7ea28055643abe583efbb
@@ -1,5 +1,3 @@
1
- require 'faraday'
2
-
3
1
  require 'zendesk_api/version'
4
2
  require 'zendesk_api/sideloading'
5
3
  require 'zendesk_api/configuration'
@@ -26,7 +24,7 @@ module ZendeskAPI
26
24
  # The top-level class that handles configuration and connection to the Zendesk API.
27
25
  # Can also be used as an accessor to resource collections.
28
26
  class Client
29
- GZIP_EXCEPTIONS = [:em_http, :httpclient]
27
+ GZIP_EXCEPTIONS = [:em_http, :httpclient, :httpx]
30
28
 
31
29
  # @return [Configuration] Config instance
32
30
  attr_reader :config
@@ -39,24 +37,25 @@ module ZendeskAPI
39
37
  def method_missing(method, *args, &block)
40
38
  method = method.to_s
41
39
  options = args.last.is_a?(Hash) ? args.pop : {}
42
-
43
40
  unless config.use_resource_cache
44
- raise "Resource for #{method} does not exist" unless method_as_class(method)
45
- return ZendeskAPI::Collection.new(self, method_as_class(method), options)
41
+ resource_class = resource_class_for(method)
42
+ raise "Resource for #{method} does not exist" unless resource_class
43
+ return ZendeskAPI::Collection.new(self, resource_class, options)
46
44
  end
47
45
 
48
46
  @resource_cache[method] ||= { :class => nil, :cache => ZendeskAPI::LRUCache.new }
49
47
  if !options.delete(:reload) && (cached = @resource_cache[method][:cache].read(options.hash))
50
48
  cached
51
49
  else
52
- @resource_cache[method][:class] ||= method_as_class(method)
50
+ @resource_cache[method][:class] ||= resource_class_for(method)
53
51
  raise "Resource for #{method} does not exist" unless @resource_cache[method][:class]
54
52
  @resource_cache[method][:cache].write(options.hash, ZendeskAPI::Collection.new(self, @resource_cache[method][:class], options))
55
53
  end
56
54
  end
57
55
 
58
56
  def respond_to?(method, *args)
59
- ((cache = @resource_cache[method]) && cache[:class]) || !method_as_class(method).nil? || super
57
+ cache = @resource_cache[method]
58
+ !!(cache.to_h[:class] || resource_class_for(method) || super)
60
59
  end
61
60
 
62
61
  # Returns the current user (aka me)
@@ -100,9 +99,7 @@ module ZendeskAPI
100
99
  config.retry = !!config.retry # nil -> false
101
100
 
102
101
  set_raise_error_when_rated_limited
103
-
104
102
  set_token_auth
105
-
106
103
  set_default_logger
107
104
  add_warning_callback
108
105
  end
@@ -112,7 +109,6 @@ module ZendeskAPI
112
109
  # @return [Faraday::Connection] Faraday connection for the client
113
110
  def connection
114
111
  @connection ||= build_connection
115
- return @connection
116
112
  end
117
113
 
118
114
  # Pushes a callback onto the stack. Callbacks are executed on responses, last in the Faraday middleware stack.
@@ -155,7 +151,6 @@ module ZendeskAPI
155
151
  builder.use ZendeskAPI::Middleware::Response::ParseIsoDates
156
152
  builder.use ZendeskAPI::Middleware::Response::ParseJson
157
153
  builder.use ZendeskAPI::Middleware::Response::SanitizeResponse
158
-
159
154
  adapter = config.adapter || Faraday.default_adapter
160
155
 
161
156
  unless GZIP_EXCEPTIONS.include?(adapter)
@@ -163,14 +158,7 @@ module ZendeskAPI
163
158
  builder.use ZendeskAPI::Middleware::Response::Deflate
164
159
  end
165
160
 
166
- # request
167
- if config.access_token && !config.url_based_access_token
168
- builder.request(:authorization, "Bearer", config.access_token)
169
- elsif config.access_token
170
- builder.use ZendeskAPI::Middleware::Request::UrlBasedAccessToken, config.access_token
171
- else
172
- builder.use Faraday::Request::BasicAuthentication, config.username, config.password
173
- end
161
+ set_authentication(builder, config)
174
162
 
175
163
  if config.cache
176
164
  builder.use ZendeskAPI::Middleware::Request::EtagCache, :cache => config.cache
@@ -181,20 +169,25 @@ module ZendeskAPI
181
169
  builder.use ZendeskAPI::Middleware::Request::EncodeJson
182
170
 
183
171
  # Should always be first in the stack
184
- builder.use ZendeskAPI::Middleware::Request::Retry, :logger => config.logger, :retry_codes => config.retry_codes, :retry_on_exception => config.retry_on_exception if config.retry
172
+ if config.retry
173
+ builder.use ZendeskAPI::Middleware::Request::Retry,
174
+ :logger => config.logger,
175
+ :retry_codes => config.retry_codes,
176
+ :retry_on_exception => config.retry_on_exception
177
+ end
185
178
  if config.raise_error_when_rate_limited
186
179
  builder.use ZendeskAPI::Middleware::Request::RaiseRateLimited, :logger => config.logger
187
180
  end
188
181
 
189
- builder.adapter(*adapter)
182
+ builder.adapter(*adapter, &config.adapter_proc)
190
183
  end
191
184
  end
192
185
 
193
186
  private
194
187
 
195
- def method_as_class(method)
196
- klass_as_string = ZendeskAPI::Helpers.modulize_string(Inflection.singular(method.to_s.gsub(/\W/, '')))
197
- ZendeskAPI::Association.class_from_namespace(klass_as_string)
188
+ def resource_class_for(method)
189
+ resource_name = ZendeskAPI::Helpers.modulize_string(Inflection.singular(method.to_s.gsub(/\W/, '')))
190
+ ZendeskAPI::Association.class_from_namespace(resource_name)
198
191
  end
199
192
 
200
193
  def check_url
@@ -239,5 +232,16 @@ module ZendeskAPI
239
232
  end
240
233
  end
241
234
  end
235
+
236
+ # See https://lostisland.github.io/faraday/middleware/authentication
237
+ def set_authentication(builder, config)
238
+ if config.access_token && !config.url_based_access_token
239
+ builder.request :authorization, "Bearer", config.access_token
240
+ elsif config.access_token
241
+ builder.use ZendeskAPI::Middleware::Request::UrlBasedAccessToken, config.access_token
242
+ else
243
+ builder.request :authorization, :basic, config.username, config.password
244
+ end
245
+ end
242
246
  end
243
247
  end
@@ -1,12 +1,14 @@
1
1
  require 'zendesk_api/resource'
2
2
  require 'zendesk_api/resources'
3
3
  require 'zendesk_api/search'
4
+ require 'zendesk_api/pagination'
4
5
 
5
6
  module ZendeskAPI
6
7
  # Represents a collection of resources. Lazily loaded, resources aren't
7
8
  # actually fetched until explicitly needed (e.g. #each, {#fetch}).
8
9
  class Collection
9
10
  include ZendeskAPI::Sideloading
11
+ include Pagination
10
12
 
11
13
  # Options passed in that are automatically converted from an array to a comma-separated list.
12
14
  SPECIALLY_JOINED_PARAMS = [:ids, :only]
@@ -114,30 +116,6 @@ module ZendeskAPI
114
116
  @count || -1
115
117
  end
116
118
 
117
- # Changes the per_page option. Returns self, so it can be chained. No execution.
118
- # @return [Collection] self
119
- def per_page(count)
120
- clear_cache if count
121
- @options["per_page"] = count
122
- self
123
- end
124
-
125
- # Changes the page option. Returns self, so it can be chained. No execution.
126
- # @return [Collection] self
127
- def page(number)
128
- clear_cache if number
129
- @options["page"] = number
130
- self
131
- end
132
-
133
- def first_page?
134
- !@prev_page
135
- end
136
-
137
- def last_page?
138
- !@next_page || @next_page == @query
139
- end
140
-
141
119
  # Saves all newly created resources stored in this collection.
142
120
  # @return [Collection] self
143
121
  def save
@@ -186,17 +164,8 @@ module ZendeskAPI
186
164
  elsif association && association.options.parent && association.options.parent.new_record?
187
165
  return (@resources = [])
188
166
  end
189
- path_query_link = (@query || path)
190
-
191
- @response = get_response(path_query_link)
192
-
193
- if path_query_link == "search/export"
194
- handle_cursor_response(@response.body)
195
- else
196
- handle_response(@response.body)
197
- end
198
167
 
199
- @resources
168
+ get_resources(@query || path)
200
169
  end
201
170
 
202
171
  def fetch(*args)
@@ -252,10 +221,12 @@ module ZendeskAPI
252
221
  # * If there is a next_page url cached, it executes a fetch on that url and returns the results.
253
222
  # * Otherwise, returns an empty array.
254
223
  def next
255
- if @options["page"]
224
+ if @options["page"] && !cbp_request?
256
225
  clear_cache
257
- @options["page"] += 1
226
+ @options["page"] = @options["page"].to_i + 1
258
227
  elsif (@query = @next_page)
228
+ # Send _only_ url param "?page[after]=token" to get the next page
229
+ @options.page&.delete("before")
259
230
  fetch(true)
260
231
  else
261
232
  clear_cache
@@ -268,10 +239,12 @@ module ZendeskAPI
268
239
  # * If there is a prev_page url cached, it executes a fetch on that url and returns the results.
269
240
  # * Otherwise, returns an empty array.
270
241
  def prev
271
- if @options["page"] && @options["page"] > 1
242
+ if !cbp_request? && @options["page"].to_i > 1
272
243
  clear_cache
273
244
  @options["page"] -= 1
274
245
  elsif (@query = @prev_page)
246
+ # Send _only_ url param "?page[before]=token" to get the prev page
247
+ @options.page&.delete("after")
275
248
  fetch(true)
276
249
  else
277
250
  clear_cache
@@ -326,22 +299,13 @@ module ZendeskAPI
326
299
  map(&:to_param)
327
300
  end
328
301
 
329
- def more_results?(response)
330
- response["meta"].present? && response["results"].present?
331
- end
332
- alias_method :has_more_results?, :more_results? # For backward compatibility with 1.33.0 and 1.34.0
333
-
334
- def get_response_body(link)
335
- @client.connection.send("get", link).body
336
- end
337
-
338
302
  def get_next_page_data(original_response_body)
339
303
  link = original_response_body["links"]["next"]
340
-
304
+ result_key = @resource_class.model_key || "results"
341
305
  while link
342
- response = get_response_body(link)
306
+ response = @client.connection.send("get", link).body
343
307
 
344
- original_response_body["results"] = original_response_body["results"] + response["results"]
308
+ original_response_body[result_key] = original_response_body[result_key] + response[result_key]
345
309
 
346
310
  link = response["meta"]["has_more"] ? response["links"]["next"] : nil
347
311
  end
@@ -351,14 +315,20 @@ module ZendeskAPI
351
315
 
352
316
  private
353
317
 
354
- def set_page_and_count(body)
355
- @count = (body["count"] || @resources.size).to_i
356
- @next_page, @prev_page = body["next_page"], body["previous_page"]
318
+ def get_resources(path_query_link)
319
+ if intentional_obp_request?
320
+ warn "Offset Based Pagination will be deprecated soon"
321
+ elsif supports_cbp? && first_cbp_request?
322
+ # only set cbp options if it's the first request, otherwise the options would be already in place
323
+ set_cbp_options
324
+ end
325
+ @response = get_response(path_query_link)
357
326
 
358
- if @next_page =~ /page=(\d+)/
359
- @options["page"] = $1.to_i - 1
360
- elsif @prev_page =~ /page=(\d+)/
361
- @options["page"] = $1.to_i + 1
327
+ # Keep pre-existing behaviour for search/export
328
+ if path_query_link == "search/export"
329
+ handle_search_export_response(@response.body)
330
+ else
331
+ handle_response(@response.body)
362
332
  end
363
333
  end
364
334
 
@@ -370,13 +340,7 @@ module ZendeskAPI
370
340
 
371
341
  while (bang ? fetch! : fetch)
372
342
  each do |resource|
373
- arguments = [resource, @options["page"] || 1]
374
-
375
- if block.arity >= 0
376
- arguments = arguments.take(block.arity)
377
- end
378
-
379
- block.call(*arguments)
343
+ block.call(resource, @options["page"] || 1)
380
344
  end
381
345
 
382
346
  last_page? ? break : self.next
@@ -422,7 +386,7 @@ module ZendeskAPI
422
386
 
423
387
  def get_response(path)
424
388
  @error = nil
425
- @response = @client.connection.send(@verb || "get", path) do |req|
389
+ @client.connection.send(@verb || "get", path) do |req|
426
390
  opts = @options.delete_if { |_, v| v.nil? }
427
391
 
428
392
  req.params.merge!(:include => @includes.join(",")) if @includes.any?
@@ -435,36 +399,30 @@ module ZendeskAPI
435
399
  end
436
400
  end
437
401
 
438
- def handle_cursor_response(response_body)
439
- unless response_body.is_a?(Hash)
440
- raise ZendeskAPI::Error::NetworkError, @response.env
441
- end
402
+ def handle_search_export_response(response_body)
403
+ assert_valid_response_body(response_body)
442
404
 
405
+ # Note this doesn't happen in #handle_response
443
406
  response_body = get_next_page_data(response_body) if more_results?(response_body)
444
407
 
445
408
  body = response_body.dup
446
409
  results = body.delete(@resource_class.model_key) || body.delete("results")
447
410
 
448
- unless results
449
- raise ZendeskAPI::Error::ClientError, "Expected #{@resource_class.model_key} or 'results' in response keys: #{body.keys.inspect}"
450
- end
411
+ assert_results(results, body)
451
412
 
452
413
  @resources = results.map do |res|
453
414
  wrap_resource(res)
454
415
  end
455
416
  end
456
417
 
418
+ # For both CBP and OBP
457
419
  def handle_response(response_body)
458
- unless response_body.is_a?(Hash)
459
- raise ZendeskAPI::Error::NetworkError, @response.env
460
- end
420
+ assert_valid_response_body(response_body)
461
421
 
462
422
  body = response_body.dup
463
423
  results = body.delete(@resource_class.model_key) || body.delete("results")
464
424
 
465
- unless results
466
- raise ZendeskAPI::Error::ClientError, "Expected #{@resource_class.model_key} or 'results' in response keys: #{body.keys.inspect}"
467
- end
425
+ assert_results(results, body)
468
426
 
469
427
  @resources = results.map do |res|
470
428
  wrap_resource(res)
@@ -472,6 +430,8 @@ module ZendeskAPI
472
430
 
473
431
  set_page_and_count(body)
474
432
  set_includes(@resources, @includes, body)
433
+
434
+ @resources
475
435
  end
476
436
 
477
437
  # Simplified Associations#wrap_resource
@@ -501,9 +461,13 @@ module ZendeskAPI
501
461
  to_a.public_send(name, *args, &block)
502
462
  end
503
463
 
464
+ # If you call client.tickets.foo - and foo is not an attribute nor an association, it ends up here, as a new collection
504
465
  def next_collection(name, *args, &block)
505
466
  opts = args.last.is_a?(Hash) ? args.last : {}
506
- opts.merge!(:collection_path => @collection_path.dup.push(name))
467
+ opts.merge!(collection_path: [*@collection_path, name], page: nil)
468
+ # Why `page: nil`?
469
+ # when you do client.tickets.fetch followed by client.tickets.foos => the request to /tickets/foos will
470
+ # have the options page set to whatever the last options were for the tickets collection
507
471
  self.class.new(@client, @resource_class, @options.merge(opts))
508
472
  end
509
473
 
@@ -514,5 +478,16 @@ module ZendeskAPI
514
478
  def resource_methods
515
479
  @resource_methods ||= @resource_class.singleton_methods(false).map(&:to_sym)
516
480
  end
481
+
482
+ def assert_valid_response_body(response_body)
483
+ unless response_body.is_a?(Hash)
484
+ raise ZendeskAPI::Error::NetworkError, @response.env
485
+ end
486
+ end
487
+
488
+ def assert_results(results, body)
489
+ return if results
490
+ raise ZendeskAPI::Error::ClientError, "Expected #{@resource_class.model_key} or 'results' in response keys: #{body.keys.inspect}"
491
+ end
517
492
  end
518
493
  end
@@ -28,6 +28,9 @@ module ZendeskAPI
28
28
  # @return [Symbol] Faraday adapter
29
29
  attr_accessor :adapter
30
30
 
31
+ # @return [Proc] Faraday adapter proc
32
+ attr_accessor :adapter_proc
33
+
31
34
  # @return [Boolean] Whether to allow non-HTTPS connections for development purposes.
32
35
  attr_accessor :allow_http
33
36
 
@@ -33,12 +33,7 @@ module ZendeskAPI
33
33
  private
34
34
 
35
35
  def generate_error_msg(response_body)
36
- return unless response_body["description"] || response_body["message"]
37
-
38
- [
39
- response_body["description"],
40
- response_body["message"]
41
- ].compact.join(" - ")
36
+ response_body.values_at("description", "message", "error", "errors").compact.join(" - ")
42
37
  end
43
38
  end
44
39
 
@@ -1,6 +1,10 @@
1
1
  module ZendeskAPI
2
2
  # @private
3
3
  module Helpers
4
+ def self.present?(value)
5
+ ![nil, false, "", " ", [], {}].include?(value)
6
+ end
7
+
4
8
  # From https://github.com/rubyworks/facets/blob/master/lib/core/facets/string/modulize.rb
5
9
  # Converts a string to module name representation.
6
10
  #
@@ -6,7 +6,6 @@ module ZendeskAPI
6
6
  class EncodeJson < Faraday::Middleware
7
7
  CONTENT_TYPE = 'Content-Type'.freeze
8
8
  MIME_TYPE = 'application/json'.freeze
9
- dependency 'json'
10
9
 
11
10
  def call(env)
12
11
  type = env[:request_headers][CONTENT_TYPE].to_s
@@ -4,7 +4,7 @@ module ZendeskAPI
4
4
  module Middleware
5
5
  module Response
6
6
  # @private
7
- class Callback < Faraday::Response::Middleware
7
+ class Callback < Faraday::Middleware
8
8
  def initialize(app, client)
9
9
  super(app)
10
10
  @client = client
@@ -5,11 +5,12 @@ module ZendeskAPI
5
5
  module Response
6
6
  # Faraday middleware to handle content-encoding = inflate
7
7
  # @private
8
- class Deflate < Faraday::Response::Middleware
8
+ class Deflate < Faraday::Middleware
9
9
  def on_complete(env)
10
- if !env.body.strip.empty? && env[:response_headers]['content-encoding'] == "deflate"
11
- env.body = Zlib::Inflate.inflate(env.body)
12
- end
10
+ return if env[:response_headers]['content-encoding'] != "deflate"
11
+ return if env.body.strip.empty?
12
+
13
+ env.body = Zlib::Inflate.inflate(env.body)
13
14
  end
14
15
  end
15
16
  end
@@ -7,11 +7,12 @@ module ZendeskAPI
7
7
  # @private
8
8
  module Response
9
9
  # Faraday middleware to handle content-encoding = gzip
10
- class Gzip < Faraday::Response::Middleware
10
+ class Gzip < Faraday::Middleware
11
11
  def on_complete(env)
12
- if !env[:body].strip.empty? && env[:response_headers]['content-encoding'] == "gzip"
13
- env[:body] = Zlib::GzipReader.new(StringIO.new(env[:body])).read
14
- end
12
+ return if env[:response_headers]['content-encoding'] != "gzip"
13
+ return if env[:body].force_encoding(Encoding::BINARY).strip.empty?
14
+
15
+ env[:body] = Zlib::GzipReader.new(StringIO.new(env[:body])).read
15
16
  end
16
17
  end
17
18
  end
@@ -6,7 +6,7 @@ module ZendeskAPI
6
6
  module Response
7
7
  # Parse ISO dates from response body
8
8
  # @private
9
- class ParseIsoDates < Faraday::Response::Middleware
9
+ class ParseIsoDates < Faraday::Middleware
10
10
  def call(env)
11
11
  @app.call(env).on_complete do |env|
12
12
  parse_dates!(env[:body])
@@ -3,9 +3,8 @@ module ZendeskAPI
3
3
  module Middleware
4
4
  # @private
5
5
  module Response
6
- class ParseJson < Faraday::Response::Middleware
6
+ class ParseJson < Faraday::Middleware
7
7
  CONTENT_TYPE = 'Content-Type'.freeze
8
- dependency 'json'
9
8
 
10
9
  def on_complete(env)
11
10
  type = env[:response_headers][CONTENT_TYPE].to_s
@@ -1,7 +1,7 @@
1
1
  module ZendeskAPI
2
2
  module Middleware
3
3
  module Response
4
- class SanitizeResponse < Faraday::Response::Middleware
4
+ class SanitizeResponse < Faraday::Middleware
5
5
  def on_complete(env)
6
6
  env[:body].scrub!('')
7
7
  end
@@ -0,0 +1,99 @@
1
+ module ZendeskAPI
2
+ class Collection
3
+ # Contains all methods related to pagination in an attempt to slim down collection.rb
4
+ module Pagination
5
+ DEFAULT_PAGE_SIZE = 100
6
+ def more_results?(response)
7
+ Helpers.present?(response["meta"]) && response["meta"]["has_more"]
8
+ end
9
+ alias has_more_results? more_results? # For backward compatibility with 1.33.0 and 1.34.0
10
+
11
+ # Changes the per_page option. Returns self, so it can be chained. No execution.
12
+ # @return [Collection] self
13
+ def per_page(count)
14
+ clear_cache if count
15
+ @options["per_page"] = count
16
+ self
17
+ end
18
+
19
+ # Changes the page option. Returns self, so it can be chained. No execution.
20
+ # @return [Collection] self
21
+ def page(number)
22
+ clear_cache if number
23
+ @options["page"] = number
24
+ self
25
+ end
26
+
27
+ def first_page?
28
+ !@prev_page
29
+ end
30
+
31
+ def last_page?
32
+ !@next_page || @next_page == @query
33
+ end
34
+
35
+ private
36
+
37
+ def page_links(body)
38
+ if body["meta"] && body["links"]
39
+ [body["links"]["next"], body["links"]["prev"]]
40
+ else
41
+ [body["next_page"], body["previous_page"]]
42
+ end
43
+ end
44
+
45
+ def cbp_response?(body)
46
+ !!(body["meta"] && body["links"])
47
+ end
48
+
49
+ def set_cbp_options
50
+ @options_per_page_was = @options.delete("per_page")
51
+ # Default to CBP by using the page param as a map
52
+ @options.page = { size: (@options_per_page_was || DEFAULT_PAGE_SIZE) }
53
+ end
54
+
55
+ # CBP requests look like: `/resources?page[size]=100`
56
+ # OBP requests look like: `/resources?page=2`
57
+ def cbp_request?
58
+ @options["page"].is_a?(Hash)
59
+ end
60
+
61
+ def intentional_obp_request?
62
+ Helpers.present?(@options["page"]) && !cbp_request?
63
+ end
64
+
65
+ def supports_cbp?
66
+ @resource_class.cbp_path_regexes.any? { |supported_path_regex| path.match?(supported_path_regex) }
67
+ end
68
+
69
+ def first_cbp_request?
70
+ # @next_page will be nil when making the first cbp request
71
+ @next_page.nil?
72
+ end
73
+
74
+ def set_page_and_count(body)
75
+ @count = (body["count"] || @resources.size).to_i
76
+ @next_page, @prev_page = page_links(body)
77
+
78
+ if cbp_response?(body)
79
+ set_cbp_response_options(body)
80
+ elsif @next_page =~ /page=(\d+)/
81
+ @options["page"] = Regexp.last_match(1).to_i - 1
82
+ elsif @prev_page =~ /page=(\d+)/
83
+ @options["page"] = Regexp.last_match(1).to_i + 1
84
+ end
85
+ end
86
+
87
+ def set_cbp_response_options(body)
88
+ @options.page = {} unless cbp_request?
89
+ # the line above means an intentional CBP request where page[size] is passed on the query
90
+ # this is to cater for CBP responses where we don't specify page[size] but the endpoint
91
+ # responds CBP by default. i.e `client.trigger_categories.fetch`
92
+ @options.page.merge!(
93
+ before: body["meta"]["before_cursor"],
94
+ after: body["meta"]["after_cursor"]
95
+ )
96
+ end
97
+ end
98
+ end
99
+ end
@@ -39,6 +39,14 @@ module ZendeskAPI
39
39
  def namespace(namespace)
40
40
  @namespace = namespace
41
41
  end
42
+
43
+ def new_from_response(client, response, includes = nil)
44
+ new(client).tap do |resource|
45
+ resource.handle_response(response)
46
+ resource.set_includes(resource, includes, response.body) if includes
47
+ resource.attributes.clear_changes
48
+ end
49
+ end
42
50
  end
43
51
 
44
52
  # @return [Hash] The resource's attributes
@@ -123,19 +131,16 @@ module ZendeskAPI
123
131
 
124
132
  # Compares resources by class and id. If id is nil, then by object_id
125
133
  def ==(other)
134
+ return false unless other
135
+
126
136
  return true if other.object_id == object_id
127
137
 
128
- if other && !(other.is_a?(Data) || other.is_a?(Integer))
129
- warn "Trying to compare #{other.class} to a Resource from #{caller.first}"
130
- end
138
+ return other.id && (other.id == id) if other.is_a?(Data)
131
139
 
132
- if other.is_a?(Data)
133
- other.id && other.id == id
134
- elsif other.is_a?(Integer)
135
- id == other
136
- else
137
- false
138
- end
140
+ return id == other if other.is_a?(Integer)
141
+
142
+ warn "Trying to compare #{other.class} to a Resource
143
+ from #{caller.first}"
139
144
  end
140
145
  alias :eql :==
141
146
 
@@ -163,6 +168,10 @@ module ZendeskAPI
163
168
  class DataResource < Data
164
169
  attr_accessor :error, :error_message
165
170
  extend Verbs
171
+
172
+ def self.cbp_path_regexes
173
+ []
174
+ end
166
175
  end
167
176
 
168
177
  # Represents a resource that can only GET
@@ -16,6 +16,58 @@ module ZendeskAPI
16
16
 
17
17
  class CustomRole < DataResource; end
18
18
 
19
+ class WorkItem < Resource; end
20
+
21
+ class Channel < Resource
22
+ def work_items
23
+ @work_items ||= attributes.fetch('relationships', {}).fetch('work_items', {}).fetch('data', []).map do |work_item_attributes|
24
+ WorkItem.new(@client, work_item_attributes)
25
+ end
26
+ end
27
+ end
28
+
29
+ # client.agent_availabilities.fetch
30
+ # client.agent_availabilities.find 20401208368
31
+ # both return consistently - ZendeskAPI::AgentAvailability
32
+ class AgentAvailability < DataResource
33
+ def self.model_key
34
+ "data"
35
+ end
36
+
37
+ def initialize(client, attributes = {})
38
+ nested_attributes = attributes.delete('attributes')
39
+ super(client, attributes.merge(nested_attributes))
40
+ end
41
+
42
+ def self.find(client, id)
43
+ attributes = client.connection.get("#{resource_path}/#{id}").body.fetch(model_key, {})
44
+ new(client, attributes)
45
+ end
46
+
47
+ # Examples:
48
+ # ZendeskAPI::AgentAvailability.search(client, { channel_status: 'support:online' })
49
+ # ZendeskAPI::AgentAvailability.search(client, { agent_status_id: 1 })
50
+ # Just pass a hash that includes the key and value you want to search for, it gets turned into a query string
51
+ # on the format of filter[key]=value
52
+ # Returns a collection of AgentAvailability objects
53
+ def self.search(client, args_hash)
54
+ query_string = args_hash.map { |k, v| "filter[#{k}]=#{v}" }.join("&")
55
+ client.connection.get("#{resource_path}?#{query_string}").body.fetch(model_key, []).map do |attributes|
56
+ new(client, attributes)
57
+ end
58
+ end
59
+
60
+ def channels
61
+ @channels ||= begin
62
+ channel_attributes_array = @client.connection.get(attributes['links']['self']).body.fetch('included')
63
+ channel_attributes_array.map do |channel_attributes|
64
+ nested_attributes = channel_attributes.delete('attributes')
65
+ Channel.new(@client, channel_attributes.merge(nested_attributes))
66
+ end
67
+ end
68
+ end
69
+ end
70
+
19
71
  class Role < DataResource
20
72
  def to_param
21
73
  name
@@ -24,6 +76,10 @@ module ZendeskAPI
24
76
 
25
77
  class Topic < Resource
26
78
  class << self
79
+ def cbp_path_regexes
80
+ [%r{^community/topics$}]
81
+ end
82
+
27
83
  def resource_path
28
84
  "community/topics"
29
85
  end
@@ -83,6 +139,10 @@ module ZendeskAPI
83
139
  def attributes_for_save
84
140
  { self.class.resource_name => [id] }
85
141
  end
142
+
143
+ def self.cbp_path_regexes
144
+ [/^tags$/]
145
+ end
86
146
  end
87
147
 
88
148
  class Attachment < ReadResource
@@ -152,9 +212,17 @@ module ZendeskAPI
152
212
  def self.incremental_export(client, start_time)
153
213
  ZendeskAPI::Collection.new(client, self, :path => "incremental/organizations?start_time=#{start_time.to_i}")
154
214
  end
215
+
216
+ def self.cbp_path_regexes
217
+ [/^organizations$/]
218
+ end
155
219
  end
156
220
 
157
221
  class Brand < Resource
222
+ def self.cbp_path_regexes
223
+ [/^brands$/]
224
+ end
225
+
158
226
  def destroy!
159
227
  self.active = false
160
228
  save!
@@ -180,6 +248,10 @@ module ZendeskAPI
180
248
 
181
249
  has User
182
250
  has Organization
251
+
252
+ def self.cbp_path_regexes
253
+ [%r{^organizations/\d+/subscriptions$}]
254
+ end
183
255
  end
184
256
 
185
257
  class Category < Resource
@@ -228,6 +300,18 @@ module ZendeskAPI
228
300
  has_many Vote
229
301
  class Translation < Resource; end
230
302
  has_many Translation
303
+ class Label < DataResource
304
+ include Read
305
+ include Create
306
+ include Destroy
307
+
308
+ def destroy!
309
+ super do |req|
310
+ req.path = path
311
+ end
312
+ end
313
+ end
314
+ has_many Label
231
315
  end
232
316
 
233
317
  class TopicSubscription < Resource
@@ -246,15 +330,19 @@ module ZendeskAPI
246
330
  end
247
331
 
248
332
  class Topic < Resource
249
- has_many :subscriptions, :class => TopicSubscription, :inline => true
250
- has_many Tag, :extend => Tag::Update, :inline => :create
333
+ has_many :subscriptions, class: TopicSubscription, inline: true
334
+ has_many Tag, extend: Tag::Update, inline: :create
251
335
  has_many Attachment
252
- has_many :uploads, :class => Attachment, :inline => true
336
+ has_many :uploads, class: Attachment, inline: true
253
337
  end
254
338
 
255
339
  class Activity < Resource
256
340
  has User
257
341
  has :actor, :class => User
342
+
343
+ def self.cbp_path_regexes
344
+ [/^activities$/]
345
+ end
258
346
  end
259
347
 
260
348
  class Setting < UpdateResource
@@ -306,7 +394,7 @@ module ZendeskAPI
306
394
  class Comment < DataResource
307
395
  include Save
308
396
 
309
- has_many :uploads, :class => Attachment, :inline => true
397
+ has_many :uploads, class: Attachment, inline: true
310
398
  has :author, :class => User
311
399
 
312
400
  def save
@@ -337,10 +425,18 @@ module ZendeskAPI
337
425
  namespace 'portal'
338
426
  end
339
427
 
340
- class TicketField < Resource; end
428
+ class TicketField < Resource
429
+ def self.cbp_path_regexes
430
+ [/^ticket_fields$/]
431
+ end
432
+ end
341
433
 
342
434
  class TicketMetric < DataResource
343
435
  include Read
436
+
437
+ def self.cbp_path_regexes
438
+ [/^ticket_metrics$/]
439
+ end
344
440
  end
345
441
 
346
442
  class TicketRelated < DataResource; end
@@ -348,7 +444,7 @@ module ZendeskAPI
348
444
  class TicketEvent < DataResource
349
445
  class Event < Data; end
350
446
 
351
- has_many :child_events, :class => Event
447
+ has_many :child_events, class: Event
352
448
  has Ticket
353
449
  has :updater, :class => User
354
450
 
@@ -366,6 +462,10 @@ module ZendeskAPI
366
462
  extend UpdateMany
367
463
  extend DestroyMany
368
464
 
465
+ def self.cbp_path_regexes
466
+ [/^tickets$/, %r{organizations/\d+/tickets}, %r{users/\d+/tickets/requested}]
467
+ end
468
+
369
469
  # Unlike other attributes, "comment" is not a property of the ticket,
370
470
  # but is used as a "comment on save", so it should be kept unchanged,
371
471
  # See https://github.com/zendesk/zendesk_api_client_rb/issues/321
@@ -384,13 +484,17 @@ module ZendeskAPI
384
484
  has :author, :class => User
385
485
 
386
486
  has_many Event
487
+
488
+ def self.cbp_path_regexes
489
+ [%r{^tickets/\d+/audits$}]
490
+ end
387
491
  end
388
492
 
389
493
  class Comment < DataResource
390
494
  include Save
391
495
 
392
- has_many :uploads, :class => Attachment, :inline => true
393
- has :author, :class => User
496
+ has_many :uploads, class: Attachment, inline: true
497
+ has :author, class: User
394
498
 
395
499
  def save
396
500
  if new_record?
@@ -417,35 +521,35 @@ module ZendeskAPI
417
521
  has :submitter, :class => User
418
522
  has :assignee, :class => User
419
523
 
420
- has_many :collaborators, :class => User, :inline => true, :extend => (Module.new do
524
+ has_many :collaborators, class: User, inline: true, extend: (Module.new do
421
525
  def to_param
422
526
  map(&:id)
423
527
  end
424
528
  end)
425
529
 
426
530
  has_many Audit
427
- has :metrics, :class => TicketMetric
531
+ has :metrics, class: TicketMetric
428
532
  has Group
429
533
  has Organization
430
534
  has Brand
431
- has :related, :class => TicketRelated
535
+ has :related, class: TicketRelated
432
536
 
433
- has Comment, :inline => true
537
+ has Comment, inline: true
434
538
  has_many Comment
435
539
 
436
- has :last_comment, :class => Comment, :inline => true
437
- has_many :last_comments, :class => Comment, :inline => true
540
+ has :last_comment, class: Comment, inline: true
541
+ has_many :last_comments, class: Comment, inline: true
438
542
 
439
- has_many Tag, :extend => Tag::Update, :inline => :create
543
+ has_many Tag, extend: Tag::Update, inline: :create
440
544
 
441
- has_many :incidents, :class => Ticket
545
+ has_many :incidents, class: Ticket
442
546
 
443
547
  # Gets a incremental export of tickets from the start_time until now.
444
548
  # @param [Client] client The {Client} object to be used
445
549
  # @param [Integer] start_time The start_time parameter
446
550
  # @return [Collection] Collection of {Ticket}
447
551
  def self.incremental_export(client, start_time)
448
- ZendeskAPI::Collection.new(client, self, :path => "incremental/tickets?start_time=#{start_time.to_i}")
552
+ ZendeskAPI::Collection.new(client, self, path: "incremental/tickets?start_time=#{start_time.to_i}")
449
553
  end
450
554
 
451
555
  # Imports a ticket through the imports/tickets endpoint using save!
@@ -454,7 +558,7 @@ module ZendeskAPI
454
558
  # @return [Ticket] Created object or nil
455
559
  def self.import!(client, attributes)
456
560
  new(client, attributes).tap do |ticket|
457
- ticket.save!(:path => "imports/tickets")
561
+ ticket.save!(path: "imports/tickets")
458
562
  end
459
563
  end
460
564
 
@@ -464,7 +568,7 @@ module ZendeskAPI
464
568
  # @return [Ticket] Created object or nil
465
569
  def self.import(client, attributes)
466
570
  ticket = new(client, attributes)
467
- return unless ticket.save(:path => "imports/tickets")
571
+ return unless ticket.save(path: "imports/tickets")
468
572
  ticket
469
573
  end
470
574
  end
@@ -474,6 +578,10 @@ module ZendeskAPI
474
578
 
475
579
  # Recovers this suspended ticket to an actual ticket
476
580
  put :recover
581
+
582
+ def self.cbp_path_regexes
583
+ [/^suspended_tickets$/]
584
+ end
477
585
  end
478
586
 
479
587
  class DeletedTicket < ReadResource
@@ -483,6 +591,10 @@ module ZendeskAPI
483
591
  # Restores this previously deleted ticket to an actual ticket
484
592
  put :restore
485
593
  put :restore_many
594
+
595
+ def self.cbp_path_regexes
596
+ [/^deleted_tickets$/]
597
+ end
486
598
  end
487
599
 
488
600
  class UserViewRow < DataResource
@@ -498,9 +610,9 @@ module ZendeskAPI
498
610
  # @internal Optional columns
499
611
 
500
612
  has Group
501
- has :assignee, :class => User
502
- has :requester, :class => User
503
- has :submitter, :class => User
613
+ has :assignee, class: User
614
+ has :requester, class: User
615
+ has :submitter, class: User
504
616
  has Organization
505
617
 
506
618
  def self.model_key
@@ -580,6 +692,10 @@ module ZendeskAPI
580
692
  def self.preview(client, options = {})
581
693
  Collection.new(client, ViewRow, options.merge(:path => "views/preview", :verb => :post))
582
694
  end
695
+
696
+ def self.cbp_path_regexes
697
+ [/^views$/]
698
+ end
583
699
  end
584
700
 
585
701
  class Trigger < Rule
@@ -587,6 +703,10 @@ module ZendeskAPI
587
703
  include Actions
588
704
 
589
705
  has :execution, :class => RuleExecution
706
+
707
+ def self.cbp_path_regexes
708
+ [/^triggers$/, %r{^triggers/active$}]
709
+ end
590
710
  end
591
711
 
592
712
  class Automation < Rule
@@ -594,6 +714,10 @@ module ZendeskAPI
594
714
  include Actions
595
715
 
596
716
  has :execution, :class => RuleExecution
717
+
718
+ def self.cbp_path_regexes
719
+ [/^automations$/]
720
+ end
597
721
  end
598
722
 
599
723
  class Macro < Rule
@@ -601,6 +725,10 @@ module ZendeskAPI
601
725
 
602
726
  has :execution, :class => RuleExecution
603
727
 
728
+ def self.cbp_path_regexes
729
+ [/^macros$/]
730
+ end
731
+
604
732
  # Returns the update to a ticket that happens when a macro will be applied.
605
733
  # @param [Ticket] ticket Optional {Ticket} to apply this macro to.
606
734
  # @raise [Faraday::ClientError] Raised for any non-200 response.
@@ -636,6 +764,18 @@ module ZendeskAPI
636
764
 
637
765
  has User
638
766
  has Group
767
+
768
+ def self.cbp_path_regexes
769
+ [%r{^groups/\d+/memberships$}]
770
+ end
771
+ end
772
+
773
+ class Group < Resource
774
+ has_many :memberships, class: GroupMembership, path: "memberships"
775
+
776
+ def self.cbp_path_regexes
777
+ [/^groups$/, %r{^groups/assignable$}]
778
+ end
639
779
  end
640
780
 
641
781
  class User < Resource
@@ -660,6 +800,10 @@ module ZendeskAPI
660
800
  put :request_verification
661
801
  end
662
802
 
803
+ def self.cbp_path_regexes
804
+ [/^users$/, %r{^organizations/\d+/users$}]
805
+ end
806
+
663
807
  any :password
664
808
 
665
809
  # Set a user's password
@@ -777,6 +921,10 @@ module ZendeskAPI
777
921
  def self.singular_resource_name
778
922
  "client"
779
923
  end
924
+
925
+ def self.cbp_path_regexes
926
+ [%r{^oauth/clients$}]
927
+ end
780
928
  end
781
929
 
782
930
  class OauthToken < ReadResource
@@ -25,6 +25,10 @@ module ZendeskAPI
25
25
  (klass || Result).new(client, attributes)
26
26
  end
27
27
 
28
+ def self.cbp_path_regexes
29
+ []
30
+ end
31
+
28
32
  class Result < Data; end
29
33
 
30
34
  class << self
@@ -44,14 +44,12 @@ module ZendeskAPI
44
44
  end
45
45
 
46
46
  define_method method do |*method_args|
47
- begin
48
- send("#{method}!", *method_args)
49
- rescue ZendeskAPI::Error::RecordInvalid => e
50
- @errors = e.errors
51
- false
52
- rescue ZendeskAPI::Error::ClientError
53
- false
54
- end
47
+ send("#{method}!", *method_args)
48
+ rescue ZendeskAPI::Error::RecordInvalid => e
49
+ @errors = e.errors
50
+ false
51
+ rescue ZendeskAPI::Error::ClientError
52
+ false
55
53
  end
56
54
  end
57
55
  end
@@ -1,3 +1,3 @@
1
1
  module ZendeskAPI
2
- VERSION = "1.37.0"
2
+ VERSION = "3.1.1"
3
3
  end
data/lib/zendesk_api.rb CHANGED
@@ -1,4 +1,8 @@
1
1
  module ZendeskAPI; end
2
2
 
3
+ require 'faraday'
4
+ require 'faraday/multipart'
5
+
6
+ require 'zendesk_api/helpers'
3
7
  require 'zendesk_api/core_ext/inflection'
4
8
  require 'zendesk_api/client'
metadata CHANGED
@@ -1,36 +1,44 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zendesk_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.37.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Davidovitz
8
8
  - Michael Grosser
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-09-07 00:00:00.000000000 Z
12
+ date: 2024-09-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: faraday
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: 0.9.0
21
- - - "<"
18
+ - - ">"
22
19
  - !ruby/object:Gem::Version
23
20
  version: 2.0.0
24
21
  type: :runtime
25
22
  prerelease: false
26
23
  version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">"
26
+ - !ruby/object:Gem::Version
27
+ version: 2.0.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: faraday-multipart
30
+ requirement: !ruby/object:Gem::Requirement
27
31
  requirements:
28
32
  - - ">="
29
33
  - !ruby/object:Gem::Version
30
- version: 0.9.0
31
- - - "<"
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
32
40
  - !ruby/object:Gem::Version
33
- version: 2.0.0
41
+ version: '0'
34
42
  - !ruby/object:Gem::Dependency
35
43
  name: hashie
36
44
  requirement: !ruby/object:Gem::Requirement
@@ -128,6 +136,7 @@ files:
128
136
  - lib/zendesk_api/middleware/response/parse_json.rb
129
137
  - lib/zendesk_api/middleware/response/raise_error.rb
130
138
  - lib/zendesk_api/middleware/response/sanitize_response.rb
139
+ - lib/zendesk_api/pagination.rb
131
140
  - lib/zendesk_api/resource.rb
132
141
  - lib/zendesk_api/resources.rb
133
142
  - lib/zendesk_api/search.rb
@@ -144,11 +153,11 @@ licenses:
144
153
  - Apache-2.0
145
154
  metadata:
146
155
  bug_tracker_uri: https://github.com/zendesk/zendesk_api_client_rb/issues
147
- changelog_uri: https://github.com/zendesk/zendesk_api_client_rb/blob/v1.37.0/CHANGELOG.md
148
- documentation_uri: https://www.rubydoc.info/gems/zendesk_api/1.37.0
149
- source_code_uri: https://github.com/zendesk/zendesk_api_client_rb/tree/v1.37.0
156
+ changelog_uri: https://github.com/zendesk/zendesk_api_client_rb/blob/v3.1.1/CHANGELOG.md
157
+ documentation_uri: https://www.rubydoc.info/gems/zendesk_api/3.1.1
158
+ source_code_uri: https://github.com/zendesk/zendesk_api_client_rb/tree/v3.1.1
150
159
  wiki_uri: https://github.com/zendesk/zendesk_api_client_rb/wiki
151
- post_install_message:
160
+ post_install_message:
152
161
  rdoc_options: []
153
162
  require_paths:
154
163
  - lib
@@ -156,15 +165,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
156
165
  requirements:
157
166
  - - ">="
158
167
  - !ruby/object:Gem::Version
159
- version: '2.3'
168
+ version: '2.7'
160
169
  required_rubygems_version: !ruby/object:Gem::Requirement
161
170
  requirements:
162
171
  - - ">="
163
172
  - !ruby/object:Gem::Version
164
173
  version: 1.3.6
165
174
  requirements: []
166
- rubygems_version: 3.0.3
167
- signing_key:
175
+ rubygems_version: 3.5.16
176
+ signing_key:
168
177
  specification_version: 4
169
178
  summary: Zendesk REST API Client
170
179
  test_files: []