discourse_zendesk_api 1.0.0

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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +176 -0
  3. data/lib/zendesk_api/actions.rb +334 -0
  4. data/lib/zendesk_api/association.rb +195 -0
  5. data/lib/zendesk_api/associations.rb +212 -0
  6. data/lib/zendesk_api/client.rb +243 -0
  7. data/lib/zendesk_api/collection.rb +474 -0
  8. data/lib/zendesk_api/configuration.rb +79 -0
  9. data/lib/zendesk_api/core_ext/inflection.rb +3 -0
  10. data/lib/zendesk_api/delegator.rb +5 -0
  11. data/lib/zendesk_api/error.rb +49 -0
  12. data/lib/zendesk_api/helpers.rb +24 -0
  13. data/lib/zendesk_api/lru_cache.rb +39 -0
  14. data/lib/zendesk_api/middleware/request/encode_json.rb +26 -0
  15. data/lib/zendesk_api/middleware/request/etag_cache.rb +52 -0
  16. data/lib/zendesk_api/middleware/request/raise_rate_limited.rb +31 -0
  17. data/lib/zendesk_api/middleware/request/retry.rb +59 -0
  18. data/lib/zendesk_api/middleware/request/upload.rb +86 -0
  19. data/lib/zendesk_api/middleware/request/url_based_access_token.rb +26 -0
  20. data/lib/zendesk_api/middleware/response/callback.rb +21 -0
  21. data/lib/zendesk_api/middleware/response/deflate.rb +17 -0
  22. data/lib/zendesk_api/middleware/response/gzip.rb +19 -0
  23. data/lib/zendesk_api/middleware/response/logger.rb +44 -0
  24. data/lib/zendesk_api/middleware/response/parse_iso_dates.rb +30 -0
  25. data/lib/zendesk_api/middleware/response/parse_json.rb +23 -0
  26. data/lib/zendesk_api/middleware/response/raise_error.rb +26 -0
  27. data/lib/zendesk_api/middleware/response/sanitize_response.rb +11 -0
  28. data/lib/zendesk_api/resource.rb +208 -0
  29. data/lib/zendesk_api/resources.rb +971 -0
  30. data/lib/zendesk_api/sideloading.rb +58 -0
  31. data/lib/zendesk_api/silent_mash.rb +8 -0
  32. data/lib/zendesk_api/track_changes.rb +85 -0
  33. data/lib/zendesk_api/trackie.rb +13 -0
  34. data/lib/zendesk_api/verbs.rb +65 -0
  35. data/lib/zendesk_api/version.rb +3 -0
  36. data/lib/zendesk_api.rb +4 -0
  37. data/util/resource_handler.rb +74 -0
  38. data/util/verb_handler.rb +16 -0
  39. metadata +166 -0
@@ -0,0 +1,195 @@
1
+ require 'zendesk_api/helpers'
2
+
3
+ module ZendeskAPI
4
+ # Represents an association between two resources
5
+ # @private
6
+ class Association
7
+ class << self
8
+ def namespaces
9
+ [ZendeskAPI] + ZendeskAPI::DataNamespace.descendants
10
+ end
11
+
12
+ def class_from_namespace(klass_as_string)
13
+ namespaces.each do |ns|
14
+ if module_defines_class?(ns, klass_as_string)
15
+ return ns.const_get(klass_as_string)
16
+ end
17
+ end
18
+
19
+ nil
20
+ end
21
+
22
+ # 1.9+ changed default to search ancestors, added flag to disable behavior.
23
+ def module_defines_class?(mod, klass_as_string)
24
+ if RUBY_VERSION < '1.9'
25
+ mod.const_defined?(klass_as_string)
26
+ else
27
+ mod.const_defined?(klass_as_string, false)
28
+ end
29
+ end
30
+ end
31
+
32
+ # @return [Hash] Options passed into the association
33
+ attr_reader :options
34
+
35
+ # Options to pass in
36
+ # * class - Required
37
+ # * parent - Parent instance
38
+ # * path - Optional path instead of resource name
39
+ def initialize(options = {})
40
+ @options = SilentMash.new(options)
41
+ end
42
+
43
+ # Generate a path to the resource.
44
+ # id and <parent>_id attributes will be deleted from passed in options hash if they are used in the built path.
45
+ # Arguments that can be passed in:
46
+ # An instance, any resource instance
47
+ # Hash Options:
48
+ # * with_parent - Include the parent path (false by default)
49
+ # * with_id - Include the instance id, if possible (true)
50
+ def generate_path(*args)
51
+ options = SilentMash.new(:with_id => true)
52
+ if args.last.is_a?(Hash)
53
+ original_options = args.pop
54
+ options.merge!(original_options)
55
+ end
56
+
57
+ instance = args.first
58
+
59
+ namespace = @options[:class].to_s.split("::")
60
+ namespace[-1] = @options[:class].resource_path
61
+
62
+ # Remove components without path information
63
+ ignorable_namespace_strings.each { |ns| namespace.delete(ns) }
64
+ has_parent = namespace.size > 1 || (options[:with_parent] && @options.parent)
65
+
66
+ if has_parent
67
+ parent_class = @options.parent ? @options.parent.class : Association.class_from_namespace(ZendeskAPI::Helpers.modulize_string(namespace[0]))
68
+ parent_namespace = build_parent_namespace(parent_class, instance, options, original_options)
69
+ namespace[1..1] = parent_namespace if parent_namespace
70
+ namespace[0] = parent_class.resource_path
71
+ else
72
+ namespace[0] = @options.path || @options[:class].resource_path
73
+ end
74
+
75
+ if id = extract_id(instance, options, original_options)
76
+ namespace << id
77
+ end
78
+
79
+ namespace.join("/")
80
+ end
81
+
82
+ # Tries to place side loads onto given resources.
83
+ def side_load(resources, side_loads)
84
+ key = "#{options.name}_id"
85
+ plural_key = "#{Inflection.singular options.name.to_s}_ids"
86
+
87
+ resources.each do |resource|
88
+ if resource.key?(plural_key) # Grab associations from child_ids field on resource
89
+ side_load_from_child_ids(resource, side_loads, plural_key)
90
+ elsif resource.key?(key) || options.singular
91
+ side_load_from_child_or_parent_id(resource, side_loads, key)
92
+ else # Grab associations from parent_id field from multiple child resources
93
+ side_load_from_parent_id(resource, side_loads, key)
94
+ end
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ # @return [Array<String>] ['ZendeskAPI', 'Voice', etc.. ]
101
+ def ignorable_namespace_strings
102
+ ZendeskAPI::DataNamespace.descendants.map { |klass| klass.to_s.split('::') }.flatten.uniq
103
+ end
104
+
105
+ def _side_load(resource, side_loads)
106
+ side_loads.map! do |side_load|
107
+ resource.send(:wrap_resource, side_load, options)
108
+ end
109
+
110
+ ZendeskAPI::Collection.new(resource.client, options[:class]).tap do |collection|
111
+ collection.replace(side_loads)
112
+ end
113
+ end
114
+
115
+ def side_load_from_parent_id(resource, side_loads, key)
116
+ key = "#{resource.class.singular_resource_name}_id"
117
+
118
+ resource.send("#{options.name}=", _side_load(resource, side_loads.select {|side_load|
119
+ side_load[key] == resource.id
120
+ }))
121
+ end
122
+
123
+ def side_load_from_child_ids(resource, side_loads, plural_key)
124
+ ids = resource.send(plural_key)
125
+
126
+ resource.send("#{options.name}=", _side_load(resource, side_loads.select {|side_load|
127
+ ids.include?(side_load[options.include_key])
128
+ }))
129
+ end
130
+
131
+ def side_load_from_child_or_parent_id(resource, side_loads, key)
132
+ # Either grab association from child_id field on resource or parent_id on child resource
133
+ if resource.key?(key)
134
+ id = resource.send(key)
135
+ include_key = options.include_key
136
+ else
137
+ id = resource.id
138
+ include_key = "#{resource.class.singular_resource_name}_id"
139
+ end
140
+
141
+ return unless id
142
+
143
+ side_load = side_loads.detect do |side_load|
144
+ id == side_load[include_key]
145
+ end
146
+
147
+ resource.send("#{options.name}=", side_load) if side_load
148
+ end
149
+
150
+ def build_parent_namespace(parent_class, instance, options, original_options)
151
+ path = @options.path
152
+
153
+ association_on_parent = parent_class.associations.detect { |a| a[:name] == @options[:name] }
154
+ association_on_parent ||= parent_class.associations.detect do |a|
155
+ !a[:inline] && a[:class] == @options[:class]
156
+ end
157
+
158
+ if association_on_parent
159
+ path ||= association_on_parent[:path]
160
+ path ||= association_on_parent[:name].to_s
161
+ end
162
+
163
+ path ||= @options[:class].resource_path
164
+
165
+ [
166
+ extract_parent_id(parent_class, instance, options, original_options),
167
+ path
168
+ ]
169
+ end
170
+
171
+ def extract_parent_id(parent_class, instance, options, original_options)
172
+ parent_id_column = "#{parent_class.singular_resource_name}_id"
173
+
174
+ if @options.parent
175
+ @options.parent.id
176
+ elsif instance
177
+ instance.send(parent_id_column)
178
+ elsif options[parent_id_column]
179
+ original_options.delete(parent_id_column) || original_options.delete(parent_id_column.to_sym)
180
+ else
181
+ raise ArgumentError.new("#{@options[:class].resource_name} requires #{parent_id_column} or parent")
182
+ end
183
+ end
184
+
185
+ def extract_id(instance, options, original_options)
186
+ if options[:with_id] && !@options[:class].ancestors.include?(SingularResource)
187
+ if instance && instance.id
188
+ instance.id
189
+ elsif options[:id]
190
+ original_options.delete(:id) || original_options.delete("id")
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,212 @@
1
+ require 'zendesk_api/helpers'
2
+
3
+ module ZendeskAPI
4
+ # This module holds association method for resources.
5
+ # Associations can be loaded in three ways:
6
+ # * Commonly used resources are automatically side-loaded server side and sent along with their parent object.
7
+ # * Associated resource ids are sent and are then loaded one-by-one into the parent collection.
8
+ # * The association is represented with Rails' nested association urls (such as tickets/:id/groups) and are loaded that way.
9
+ #
10
+ # @private
11
+ module Associations
12
+ def self.included(base)
13
+ base.extend ClassMethods
14
+ end
15
+
16
+ def wrap_resource(resource, class_level_association, options = {})
17
+ instance_association = Association.new(class_level_association.merge(:parent => self))
18
+ klass = class_level_association[:class]
19
+
20
+ case resource
21
+ when Hash
22
+ klass.new(@client, resource.merge(:association => instance_association))
23
+ when String, Integer
24
+ klass.new(@client, (options[:include_key] || :id) => resource, :association => instance_association)
25
+ else
26
+ resource.association = instance_association
27
+ resource
28
+ end
29
+ end
30
+
31
+ # @private
32
+ module ClassMethods
33
+ def self.extended(klass)
34
+ klass.extend Has
35
+ klass.extend HasMany
36
+ end
37
+
38
+ def associations
39
+ @associations ||= []
40
+ end
41
+
42
+ def associated_with(name)
43
+ associations.inject([]) do |associated_with, association|
44
+ if association[:include] == name.to_s
45
+ associated_with.push(Association.new(association))
46
+ end
47
+
48
+ associated_with
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def build_association(klass, resource_name, options)
55
+ {
56
+ :class => klass,
57
+ :name => resource_name,
58
+ :inline => options.delete(:inline),
59
+ :path => options.delete(:path),
60
+ :include => (options.delete(:include) || klass.resource_name).to_s,
61
+ :include_key => (options.delete(:include_key) || :id).to_s,
62
+ :singular => options.delete(:singular),
63
+ :extensions => Array(options.delete(:extend))
64
+ }
65
+ end
66
+
67
+ def define_used(association)
68
+ define_method "#{association[:name]}_used?" do
69
+ !!instance_variable_get("@#{association[:name]}")
70
+ end
71
+ end
72
+
73
+ module Has
74
+ # Represents a parent-to-child association between resources. Options to pass in are: class, path.
75
+ # @param [Symbol] resource_name_or_class The underlying resource name or a class to get it from
76
+ # @param [Hash] class_level_options The options to pass to the method definition.
77
+ def has(resource_name_or_class, class_level_options = {})
78
+ if klass = class_level_options.delete(:class)
79
+ resource_name = resource_name_or_class
80
+ else
81
+ klass = resource_name_or_class
82
+ resource_name = klass.singular_resource_name
83
+ end
84
+
85
+ class_level_association = build_association(klass, resource_name, class_level_options)
86
+ class_level_association.merge!(:singular => true, :id_column => "#{resource_name}_id")
87
+
88
+ associations << class_level_association
89
+
90
+ define_used(class_level_association)
91
+ define_has_getter(class_level_association)
92
+ define_has_setter(class_level_association)
93
+ end
94
+
95
+ private
96
+
97
+ def define_has_getter(association)
98
+ klass = association[:class] # shorthand
99
+
100
+ define_method association[:name] do |*args|
101
+ instance_options = args.last.is_a?(Hash) ? args.pop : {}
102
+
103
+ # return if cached
104
+ cached = instance_variable_get("@#{association[:name]}")
105
+ return cached if cached && !instance_options[:reload]
106
+
107
+ # find and cache association
108
+ instance_association = Association.new(association.merge(:parent => self))
109
+ resource = if klass.respond_to?(:find) && resource_id = method_missing(association[:id_column])
110
+ klass.find(@client, :id => resource_id, :association => instance_association)
111
+ elsif found = method_missing(association[:name].to_sym)
112
+ wrap_resource(found, association, :include_key => association[:include_key])
113
+ elsif klass.superclass == DataResource && !association[:inline]
114
+ response = @client.connection.get(instance_association.generate_path(:with_parent => true))
115
+ klass.new(@client, response.body[klass.singular_resource_name].merge(:association => instance_association))
116
+ end
117
+
118
+ send("#{association[:id_column]}=", resource.id) if resource && has_key?(association[:id_column])
119
+ instance_variable_set("@#{association[:name]}", resource)
120
+ end
121
+ end
122
+
123
+ def define_has_setter(association)
124
+ define_method "#{association[:name]}=" do |resource|
125
+ resource = wrap_resource(resource, association)
126
+ send("#{association[:id_column]}=", resource.id) if has_key?(association[:id_column])
127
+ instance_variable_set("@#{association[:name]}", resource)
128
+ end
129
+ end
130
+ end
131
+
132
+ module HasMany
133
+ # Represents a parent-to-children association between resources. Options to pass in are: class, path.
134
+ # @param [Symbol] resource_name_or_class The underlying resource name or class to get it from
135
+ # @param [Hash] class_level_options The options to pass to the method definition.
136
+ def has_many(resource_name_or_class, class_level_options = {})
137
+ if klass = class_level_options.delete(:class)
138
+ resource_name = resource_name_or_class
139
+ else
140
+ klass = resource_name_or_class
141
+ resource_name = klass.resource_name
142
+ end
143
+
144
+ class_level_association = build_association(klass, resource_name, class_level_options)
145
+ class_level_association.merge!(:singular => false, :id_column => "#{resource_name}_ids")
146
+
147
+ associations << class_level_association
148
+
149
+ define_used(class_level_association)
150
+ define_has_many_getter(class_level_association)
151
+ define_has_many_setter(class_level_association)
152
+ end
153
+
154
+ private
155
+
156
+ def define_has_many_getter(association)
157
+ klass = association[:class]
158
+
159
+ define_method association[:name] do |*args|
160
+ instance_opts = args.last.is_a?(Hash) ? args.pop : {}
161
+
162
+ # return if cached
163
+ cached = instance_variable_get("@#{association[:name]}")
164
+ return cached if cached && !instance_opts[:reload]
165
+
166
+ # find and cache association
167
+ instance_association = Association.new(association.merge(:parent => self))
168
+ singular_resource_name = Inflection.singular(association[:name].to_s)
169
+
170
+ resources = if (ids = method_missing("#{singular_resource_name}_ids")) && ids.any?
171
+ ids.map do |id|
172
+ klass.find(@client, :id => id, :association => instance_association)
173
+ end.compact
174
+ elsif (resources = method_missing(association[:name].to_sym)) && resources.any?
175
+ resources.map { |res| wrap_resource(res, association) }
176
+ else
177
+ []
178
+ end
179
+
180
+ collection = ZendeskAPI::Collection.new(@client, klass, instance_opts.merge(:association => instance_association))
181
+
182
+ if association[:extensions].any?
183
+ collection.extend(*association[:extensions])
184
+ end
185
+
186
+ if resources.any?
187
+ collection.replace(resources)
188
+ end
189
+
190
+ send("#{association[:id_column]}=", resources.map(&:id)) if has_key?(association[:id_column])
191
+ instance_variable_set("@#{association[:name]}", collection)
192
+ end
193
+ end
194
+
195
+ def define_has_many_setter(association)
196
+ define_method "#{association[:name]}=" do |resources|
197
+ if resources.is_a?(Array)
198
+ wrapped = resources.map { |attr| wrap_resource(attr, association) }
199
+ send(association[:name]).replace(wrapped)
200
+ else
201
+ resources.association = Association.new(association.merge(:parent => self))
202
+ instance_variable_set("@#{association[:name]}", resources)
203
+ end
204
+
205
+ send("#{association[:id_column]}=", resources.map(&:id)) if resources && has_key?(association[:id_column])
206
+ resource
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,243 @@
1
+ require 'faraday'
2
+
3
+ require 'zendesk_api/version'
4
+ require 'zendesk_api/sideloading'
5
+ require 'zendesk_api/configuration'
6
+ require 'zendesk_api/collection'
7
+ require 'zendesk_api/lru_cache'
8
+ require 'zendesk_api/silent_mash'
9
+ require 'zendesk_api/middleware/request/etag_cache'
10
+ require 'zendesk_api/middleware/request/retry'
11
+ require 'zendesk_api/middleware/request/raise_rate_limited'
12
+ require 'zendesk_api/middleware/request/upload'
13
+ require 'zendesk_api/middleware/request/encode_json'
14
+ require 'zendesk_api/middleware/request/url_based_access_token'
15
+ require 'zendesk_api/middleware/response/callback'
16
+ require 'zendesk_api/middleware/response/deflate'
17
+ require 'zendesk_api/middleware/response/gzip'
18
+ require 'zendesk_api/middleware/response/sanitize_response'
19
+ require 'zendesk_api/middleware/response/parse_iso_dates'
20
+ require 'zendesk_api/middleware/response/parse_json'
21
+ require 'zendesk_api/middleware/response/raise_error'
22
+ require 'zendesk_api/middleware/response/logger'
23
+ require 'zendesk_api/delegator'
24
+
25
+ module ZendeskAPI
26
+ # The top-level class that handles configuration and connection to the Zendesk API.
27
+ # Can also be used as an accessor to resource collections.
28
+ class Client
29
+ GZIP_EXCEPTIONS = [:em_http, :httpclient]
30
+
31
+ # @return [Configuration] Config instance
32
+ attr_reader :config
33
+ # @return [Array] Custom response callbacks
34
+ attr_reader :callbacks
35
+
36
+ # Handles resources such as 'tickets'. Any options are passed to the underlying collection, except reload which disregards
37
+ # memoization and creates a new Collection instance.
38
+ # @return [Collection] Collection instance for resource
39
+ def method_missing(method, *args, &block)
40
+ method = method.to_s
41
+ options = args.last.is_a?(Hash) ? args.pop : {}
42
+
43
+ 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)
46
+ end
47
+
48
+ @resource_cache[method] ||= { :class => nil, :cache => ZendeskAPI::LRUCache.new }
49
+ if !options.delete(:reload) && (cached = @resource_cache[method][:cache].read(options.hash))
50
+ cached
51
+ else
52
+ @resource_cache[method][:class] ||= method_as_class(method)
53
+ raise "Resource for #{method} does not exist" unless @resource_cache[method][:class]
54
+ @resource_cache[method][:cache].write(options.hash, ZendeskAPI::Collection.new(self, @resource_cache[method][:class], options))
55
+ end
56
+ end
57
+
58
+ def respond_to?(method, *args)
59
+ ((cache = @resource_cache[method]) && cache[:class]) || !method_as_class(method).nil? || super
60
+ end
61
+
62
+ # Returns the current user (aka me)
63
+ # @return [ZendeskAPI::User] Current user or nil
64
+ def current_user(reload = false)
65
+ return @current_user if @current_user && !reload
66
+ @current_user = users.find(:id => 'me')
67
+ end
68
+
69
+ # Returns the current account
70
+ # @return [Hash] The attributes of the current account or nil
71
+ def current_account(reload = false)
72
+ return @current_account if @current_account && !reload
73
+ @current_account = SilentMash.new(connection.get('account/resolve').body)
74
+ end
75
+
76
+ # Returns the current locale
77
+ # @return [ZendeskAPI::Locale] Current locale or nil
78
+ def current_locale(reload = false)
79
+ return @locale if @locale && !reload
80
+ @locale = locales.find(:id => 'current')
81
+ end
82
+
83
+ # Creates a new {Client} instance and yields {#config}.
84
+ #
85
+ # Requires a block to be given.
86
+ #
87
+ # Does basic configuration constraints:
88
+ # * {Configuration#url} must be https unless {Configuration#allow_http} is set.
89
+ def initialize
90
+ raise ArgumentError, "block not given" unless block_given?
91
+
92
+ @config = ZendeskAPI::Configuration.new
93
+ yield config
94
+
95
+ @callbacks = []
96
+ @resource_cache = {}
97
+
98
+ check_url
99
+
100
+ config.retry = !!config.retry # nil -> false
101
+
102
+ set_raise_error_when_rated_limited
103
+
104
+ set_token_auth
105
+
106
+ set_default_logger
107
+ add_warning_callback
108
+ end
109
+
110
+ # Creates a connection if there is none, otherwise returns the existing connection.
111
+ #
112
+ # @return [Faraday::Connection] Faraday connection for the client
113
+ def connection
114
+ @connection ||= build_connection
115
+ return @connection
116
+ end
117
+
118
+ # Pushes a callback onto the stack. Callbacks are executed on responses, last in the Faraday middleware stack.
119
+ # @param [Proc] block The block to execute. Takes one parameter, env.
120
+ def insert_callback(&block)
121
+ @callbacks << block
122
+ end
123
+
124
+ # show a nice warning for people using the old style api
125
+ def self.check_deprecated_namespace_usage(attributes, name)
126
+ if attributes[name].is_a?(Hash)
127
+ raise "un-nest '#{name}' from the attributes"
128
+ end
129
+ end
130
+
131
+ ZendeskAPI::DataNamespace.descendants.each do |namespace|
132
+ delegator = ZendeskAPI::Helpers.snakecase_string(namespace.to_s.split("::").last)
133
+ define_method delegator do |*| # takes arguments, but doesn't do anything with them
134
+ Delegator.new(self)
135
+ end
136
+ end
137
+
138
+ protected
139
+
140
+ # Called by {#connection} to build a connection. Can be overwritten in a
141
+ # subclass to add additional middleware and make other configuration
142
+ # changes.
143
+ #
144
+ # Uses middleware according to configuration options.
145
+ #
146
+ # Request logger if logger is not nil
147
+ #
148
+ # Retry middleware if retry is true
149
+ def build_connection
150
+ Faraday.new(config.options) do |builder|
151
+ # response
152
+ builder.use ZendeskAPI::Middleware::Response::RaiseError
153
+ builder.use ZendeskAPI::Middleware::Response::Callback, self
154
+ builder.use ZendeskAPI::Middleware::Response::Logger, config.logger if config.logger
155
+ builder.use ZendeskAPI::Middleware::Response::ParseIsoDates
156
+ builder.use ZendeskAPI::Middleware::Response::ParseJson
157
+ builder.use ZendeskAPI::Middleware::Response::SanitizeResponse
158
+
159
+ adapter = config.adapter || Faraday.default_adapter
160
+
161
+ unless GZIP_EXCEPTIONS.include?(adapter)
162
+ builder.use ZendeskAPI::Middleware::Response::Gzip
163
+ builder.use ZendeskAPI::Middleware::Response::Deflate
164
+ end
165
+
166
+ # request
167
+ if config.access_token && !config.url_based_access_token
168
+ builder.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
174
+
175
+ if config.cache
176
+ builder.use ZendeskAPI::Middleware::Request::EtagCache, :cache => config.cache
177
+ end
178
+
179
+ builder.use ZendeskAPI::Middleware::Request::Upload
180
+ builder.request :multipart
181
+ builder.use ZendeskAPI::Middleware::Request::EncodeJson
182
+
183
+ # 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
185
+ if config.raise_error_when_rate_limited
186
+ builder.use ZendeskAPI::Middleware::Request::RaiseRateLimited, :logger => config.logger
187
+ end
188
+
189
+ builder.adapter(*adapter)
190
+ end
191
+ end
192
+
193
+ private
194
+
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)
198
+ end
199
+
200
+ def check_url
201
+ if !config.allow_http && config.url !~ /^https/
202
+ raise ArgumentError, "zendesk_api is ssl only; url must begin with https://"
203
+ end
204
+ end
205
+
206
+ def set_raise_error_when_rated_limited
207
+ config.raise_error_when_rate_limited = if config.retry
208
+ false
209
+ else
210
+ !!config.raise_error_when_rate_limited
211
+ end
212
+ end
213
+
214
+ def set_token_auth
215
+ return if config.password || config.token.nil?
216
+
217
+ if config.username.nil?
218
+ raise ArgumentError, "you need to provide a username when using API token auth"
219
+ else
220
+ config.password = config.token
221
+ config.username += "/token" unless config.username.end_with?("/token")
222
+ end
223
+ end
224
+
225
+ def set_default_logger
226
+ if config.logger.nil? || config.logger == true
227
+ require 'logger'
228
+ config.logger = Logger.new($stderr)
229
+ config.logger.level = Logger::WARN
230
+ end
231
+ end
232
+
233
+ def add_warning_callback
234
+ return unless logger = config.logger
235
+
236
+ insert_callback do |env|
237
+ if warning = env[:response_headers]["X-Zendesk-API-Warn"]
238
+ logger.warn "WARNING: #{warning}"
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end