zendesk_api 1.37.0 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c5a2ced45e0201f6f9a45e5b8f7cf6951ca92c15ce893283c2289d0b260dc77
4
- data.tar.gz: 31fa7566865e7191f49dadf6a5e01573264f4fc62ddfd4f635f8f7edd4be9ab2
3
+ metadata.gz: 8ecc2ccbe7be3ec6da2b36fdcd7b75004fcecd91a26de63d0bb561dcddff86cc
4
+ data.tar.gz: c4b7a7d6baa3246d2835251d03d2a7c0fd69961fd2ca3575a7486f3a7adf06f6
5
5
  SHA512:
6
- metadata.gz: a1b355c67c8126f69f32ff6cd24d03b81b75eec27a0a72044383d0e06a871ba5bd18778119523a013725162048393d7a6bf58f075013e852303255b78bccb73f
7
- data.tar.gz: 78ecdd8fd4d2fd889df06aea1ac98df7c68e6f89378f1358594b30853b23b3179f304ee4d87a781bda0047bc1970b8c28dfd147907d74223e28ffceb1676d1b1
6
+ metadata.gz: 9183c973ac61c198dd69d975c5bc9eb27cc424b47e50027d32c2a462f651b08681925b00fd4686d248b615a80445c34b59273861a164917ea6738b54d04c1a4b
7
+ data.tar.gz: 980637e5be8ba003774a6fe5fe53dc2a4003ba6e58b933d21aaba66cb9c66eb0dd3f706ac6b2e69740f1c22cfe2a3d4c7301a7cfd313c0753ef3cff7cbc2683b
@@ -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
@@ -24,6 +24,10 @@ module ZendeskAPI
24
24
 
25
25
  class Topic < Resource
26
26
  class << self
27
+ def cbp_path_regexes
28
+ [%r{^community/topics$}]
29
+ end
30
+
27
31
  def resource_path
28
32
  "community/topics"
29
33
  end
@@ -83,6 +87,10 @@ module ZendeskAPI
83
87
  def attributes_for_save
84
88
  { self.class.resource_name => [id] }
85
89
  end
90
+
91
+ def self.cbp_path_regexes
92
+ [/^tags$/]
93
+ end
86
94
  end
87
95
 
88
96
  class Attachment < ReadResource
@@ -152,9 +160,17 @@ module ZendeskAPI
152
160
  def self.incremental_export(client, start_time)
153
161
  ZendeskAPI::Collection.new(client, self, :path => "incremental/organizations?start_time=#{start_time.to_i}")
154
162
  end
163
+
164
+ def self.cbp_path_regexes
165
+ [/^organizations$/]
166
+ end
155
167
  end
156
168
 
157
169
  class Brand < Resource
170
+ def self.cbp_path_regexes
171
+ [/^brands$/]
172
+ end
173
+
158
174
  def destroy!
159
175
  self.active = false
160
176
  save!
@@ -180,6 +196,10 @@ module ZendeskAPI
180
196
 
181
197
  has User
182
198
  has Organization
199
+
200
+ def self.cbp_path_regexes
201
+ [%r{^organizations/\d+/subscriptions$}]
202
+ end
183
203
  end
184
204
 
185
205
  class Category < Resource
@@ -228,6 +248,18 @@ module ZendeskAPI
228
248
  has_many Vote
229
249
  class Translation < Resource; end
230
250
  has_many Translation
251
+ class Label < DataResource
252
+ include Read
253
+ include Create
254
+ include Destroy
255
+
256
+ def destroy!
257
+ super do |req|
258
+ req.path = path
259
+ end
260
+ end
261
+ end
262
+ has_many Label
231
263
  end
232
264
 
233
265
  class TopicSubscription < Resource
@@ -246,15 +278,19 @@ module ZendeskAPI
246
278
  end
247
279
 
248
280
  class Topic < Resource
249
- has_many :subscriptions, :class => TopicSubscription, :inline => true
250
- has_many Tag, :extend => Tag::Update, :inline => :create
281
+ has_many :subscriptions, class: TopicSubscription, inline: true
282
+ has_many Tag, extend: Tag::Update, inline: :create
251
283
  has_many Attachment
252
- has_many :uploads, :class => Attachment, :inline => true
284
+ has_many :uploads, class: Attachment, inline: true
253
285
  end
254
286
 
255
287
  class Activity < Resource
256
288
  has User
257
289
  has :actor, :class => User
290
+
291
+ def self.cbp_path_regexes
292
+ [/^activities$/]
293
+ end
258
294
  end
259
295
 
260
296
  class Setting < UpdateResource
@@ -306,7 +342,7 @@ module ZendeskAPI
306
342
  class Comment < DataResource
307
343
  include Save
308
344
 
309
- has_many :uploads, :class => Attachment, :inline => true
345
+ has_many :uploads, class: Attachment, inline: true
310
346
  has :author, :class => User
311
347
 
312
348
  def save
@@ -337,10 +373,18 @@ module ZendeskAPI
337
373
  namespace 'portal'
338
374
  end
339
375
 
340
- class TicketField < Resource; end
376
+ class TicketField < Resource
377
+ def self.cbp_path_regexes
378
+ [/^ticket_fields$/]
379
+ end
380
+ end
341
381
 
342
382
  class TicketMetric < DataResource
343
383
  include Read
384
+
385
+ def self.cbp_path_regexes
386
+ [/^ticket_metrics$/]
387
+ end
344
388
  end
345
389
 
346
390
  class TicketRelated < DataResource; end
@@ -348,7 +392,7 @@ module ZendeskAPI
348
392
  class TicketEvent < DataResource
349
393
  class Event < Data; end
350
394
 
351
- has_many :child_events, :class => Event
395
+ has_many :child_events, class: Event
352
396
  has Ticket
353
397
  has :updater, :class => User
354
398
 
@@ -366,6 +410,10 @@ module ZendeskAPI
366
410
  extend UpdateMany
367
411
  extend DestroyMany
368
412
 
413
+ def self.cbp_path_regexes
414
+ [/^tickets$/, %r{organizations/\d+/tickets}]
415
+ end
416
+
369
417
  # Unlike other attributes, "comment" is not a property of the ticket,
370
418
  # but is used as a "comment on save", so it should be kept unchanged,
371
419
  # See https://github.com/zendesk/zendesk_api_client_rb/issues/321
@@ -384,13 +432,17 @@ module ZendeskAPI
384
432
  has :author, :class => User
385
433
 
386
434
  has_many Event
435
+
436
+ def self.cbp_path_regexes
437
+ [%r{^tickets/\d+/audits$}]
438
+ end
387
439
  end
388
440
 
389
441
  class Comment < DataResource
390
442
  include Save
391
443
 
392
- has_many :uploads, :class => Attachment, :inline => true
393
- has :author, :class => User
444
+ has_many :uploads, class: Attachment, inline: true
445
+ has :author, class: User
394
446
 
395
447
  def save
396
448
  if new_record?
@@ -417,35 +469,35 @@ module ZendeskAPI
417
469
  has :submitter, :class => User
418
470
  has :assignee, :class => User
419
471
 
420
- has_many :collaborators, :class => User, :inline => true, :extend => (Module.new do
472
+ has_many :collaborators, class: User, inline: true, extend: (Module.new do
421
473
  def to_param
422
474
  map(&:id)
423
475
  end
424
476
  end)
425
477
 
426
478
  has_many Audit
427
- has :metrics, :class => TicketMetric
479
+ has :metrics, class: TicketMetric
428
480
  has Group
429
481
  has Organization
430
482
  has Brand
431
- has :related, :class => TicketRelated
483
+ has :related, class: TicketRelated
432
484
 
433
- has Comment, :inline => true
485
+ has Comment, inline: true
434
486
  has_many Comment
435
487
 
436
- has :last_comment, :class => Comment, :inline => true
437
- has_many :last_comments, :class => Comment, :inline => true
488
+ has :last_comment, class: Comment, inline: true
489
+ has_many :last_comments, class: Comment, inline: true
438
490
 
439
- has_many Tag, :extend => Tag::Update, :inline => :create
491
+ has_many Tag, extend: Tag::Update, inline: :create
440
492
 
441
- has_many :incidents, :class => Ticket
493
+ has_many :incidents, class: Ticket
442
494
 
443
495
  # Gets a incremental export of tickets from the start_time until now.
444
496
  # @param [Client] client The {Client} object to be used
445
497
  # @param [Integer] start_time The start_time parameter
446
498
  # @return [Collection] Collection of {Ticket}
447
499
  def self.incremental_export(client, start_time)
448
- ZendeskAPI::Collection.new(client, self, :path => "incremental/tickets?start_time=#{start_time.to_i}")
500
+ ZendeskAPI::Collection.new(client, self, path: "incremental/tickets?start_time=#{start_time.to_i}")
449
501
  end
450
502
 
451
503
  # Imports a ticket through the imports/tickets endpoint using save!
@@ -454,7 +506,7 @@ module ZendeskAPI
454
506
  # @return [Ticket] Created object or nil
455
507
  def self.import!(client, attributes)
456
508
  new(client, attributes).tap do |ticket|
457
- ticket.save!(:path => "imports/tickets")
509
+ ticket.save!(path: "imports/tickets")
458
510
  end
459
511
  end
460
512
 
@@ -464,7 +516,7 @@ module ZendeskAPI
464
516
  # @return [Ticket] Created object or nil
465
517
  def self.import(client, attributes)
466
518
  ticket = new(client, attributes)
467
- return unless ticket.save(:path => "imports/tickets")
519
+ return unless ticket.save(path: "imports/tickets")
468
520
  ticket
469
521
  end
470
522
  end
@@ -474,6 +526,10 @@ module ZendeskAPI
474
526
 
475
527
  # Recovers this suspended ticket to an actual ticket
476
528
  put :recover
529
+
530
+ def self.cbp_path_regexes
531
+ [/^suspended_tickets$/]
532
+ end
477
533
  end
478
534
 
479
535
  class DeletedTicket < ReadResource
@@ -483,6 +539,10 @@ module ZendeskAPI
483
539
  # Restores this previously deleted ticket to an actual ticket
484
540
  put :restore
485
541
  put :restore_many
542
+
543
+ def self.cbp_path_regexes
544
+ [/^deleted_tickets$/]
545
+ end
486
546
  end
487
547
 
488
548
  class UserViewRow < DataResource
@@ -498,9 +558,9 @@ module ZendeskAPI
498
558
  # @internal Optional columns
499
559
 
500
560
  has Group
501
- has :assignee, :class => User
502
- has :requester, :class => User
503
- has :submitter, :class => User
561
+ has :assignee, class: User
562
+ has :requester, class: User
563
+ has :submitter, class: User
504
564
  has Organization
505
565
 
506
566
  def self.model_key
@@ -580,6 +640,10 @@ module ZendeskAPI
580
640
  def self.preview(client, options = {})
581
641
  Collection.new(client, ViewRow, options.merge(:path => "views/preview", :verb => :post))
582
642
  end
643
+
644
+ def self.cbp_path_regexes
645
+ [/^views$/]
646
+ end
583
647
  end
584
648
 
585
649
  class Trigger < Rule
@@ -587,6 +651,10 @@ module ZendeskAPI
587
651
  include Actions
588
652
 
589
653
  has :execution, :class => RuleExecution
654
+
655
+ def self.cbp_path_regexes
656
+ [/^triggers$/, %r{^triggers/active$}]
657
+ end
590
658
  end
591
659
 
592
660
  class Automation < Rule
@@ -594,6 +662,10 @@ module ZendeskAPI
594
662
  include Actions
595
663
 
596
664
  has :execution, :class => RuleExecution
665
+
666
+ def self.cbp_path_regexes
667
+ [/^automations$/]
668
+ end
597
669
  end
598
670
 
599
671
  class Macro < Rule
@@ -601,6 +673,10 @@ module ZendeskAPI
601
673
 
602
674
  has :execution, :class => RuleExecution
603
675
 
676
+ def self.cbp_path_regexes
677
+ [/^macros$/]
678
+ end
679
+
604
680
  # Returns the update to a ticket that happens when a macro will be applied.
605
681
  # @param [Ticket] ticket Optional {Ticket} to apply this macro to.
606
682
  # @raise [Faraday::ClientError] Raised for any non-200 response.
@@ -636,6 +712,18 @@ module ZendeskAPI
636
712
 
637
713
  has User
638
714
  has Group
715
+
716
+ def self.cbp_path_regexes
717
+ [%r{^groups/\d+/memberships$}]
718
+ end
719
+ end
720
+
721
+ class Group < Resource
722
+ has_many :memberships, class: GroupMembership, path: "memberships"
723
+
724
+ def self.cbp_path_regexes
725
+ [/^groups$/, %r{^groups/assignable$}]
726
+ end
639
727
  end
640
728
 
641
729
  class User < Resource
@@ -660,6 +748,10 @@ module ZendeskAPI
660
748
  put :request_verification
661
749
  end
662
750
 
751
+ def self.cbp_path_regexes
752
+ [/^users$/, %r{^organizations/\d+/users$}]
753
+ end
754
+
663
755
  any :password
664
756
 
665
757
  # Set a user's password
@@ -777,6 +869,10 @@ module ZendeskAPI
777
869
  def self.singular_resource_name
778
870
  "client"
779
871
  end
872
+
873
+ def self.cbp_path_regexes
874
+ [%r{^oauth/clients$}]
875
+ end
780
876
  end
781
877
 
782
878
  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.0.5"
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.0.5
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: 2023-09-06 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.0.5/CHANGELOG.md
157
+ documentation_uri: https://www.rubydoc.info/gems/zendesk_api/3.0.5
158
+ source_code_uri: https://github.com/zendesk/zendesk_api_client_rb/tree/v3.0.5
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.0.3.1
176
+ signing_key:
168
177
  specification_version: 4
169
178
  summary: Zendesk REST API Client
170
179
  test_files: []