desk_api 0.1.3 → 0.5.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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/Gemfile +0 -6
  4. data/README.md +38 -31
  5. data/desk_api.gemspec +1 -4
  6. data/lib/desk_api.rb +5 -7
  7. data/lib/desk_api/client.rb +44 -57
  8. data/lib/desk_api/configuration.rb +89 -95
  9. data/lib/desk_api/default.rb +51 -55
  10. data/lib/desk_api/error.rb +42 -44
  11. data/lib/desk_api/rate_limit.rb +20 -22
  12. data/lib/desk_api/resource.rb +142 -65
  13. data/lib/desk_api/version.rb +1 -1
  14. data/spec/cassettes/DeskApi_Client/using_Basic_Authentication/_delete/deletes_a_resource.yml +22 -22
  15. data/spec/cassettes/DeskApi_Client/using_Basic_Authentication/_get/fetches_resources.yml +26 -26
  16. data/spec/cassettes/DeskApi_Client/using_Basic_Authentication/_patch/updates_a_resource.yml +30 -30
  17. data/spec/cassettes/DeskApi_Client/using_Basic_Authentication/_post/creates_a_resource.yml +27 -27
  18. data/spec/cassettes/DeskApi_Client/using_OAuth/_delete/deletes_a_resource.yml +22 -22
  19. data/spec/cassettes/DeskApi_Client/using_OAuth/_get/fetches_resources.yml +31 -286
  20. data/spec/cassettes/DeskApi_Client/using_OAuth/_patch/updates_a_resource.yml +35 -35
  21. data/spec/cassettes/DeskApi_Client/using_OAuth/_post/creates_a_resource.yml +28 -28
  22. data/spec/cassettes/DeskApi_Error/_from_response/can_be_created_from_a_faraday_response.yml +22 -22
  23. data/spec/cassettes/DeskApi_Error/_from_response/uses_the_body_message_if_present.yml +22 -22
  24. data/spec/cassettes/DeskApi_Error/on_validation_error/allows_access_to_error_hash.yml +22 -22
  25. data/spec/cassettes/DeskApi_Resource/_by_url/finds_resources_by_url.yml +37 -286
  26. data/spec/cassettes/DeskApi_Resource/_create/creates_a_new_topic.yml +27 -27
  27. data/spec/cassettes/DeskApi_Resource/_create/throws_an_error_creating_a_user.yml +54 -0
  28. data/spec/cassettes/DeskApi_Resource/_delete/deletes_a_resource.yml +83 -79
  29. data/spec/cassettes/DeskApi_Resource/_delete/throws_an_error_deleting_a_non_deletalbe_resource.yml +78 -28
  30. data/spec/cassettes/DeskApi_Resource/_exec_/can_be_forced_to_reload.yml +34 -285
  31. data/spec/cassettes/DeskApi_Resource/_exec_/loads_the_current_resource.yml +34 -285
  32. data/spec/cassettes/DeskApi_Resource/_find/has_an_alias_by_id.yml +54 -0
  33. data/spec/cassettes/DeskApi_Resource/_find/loads_the_requested_resource.yml +54 -0
  34. data/spec/cassettes/DeskApi_Resource/_get_linked_resource/returns_linked_resources.yml +113 -0
  35. data/spec/cassettes/DeskApi_Resource/_get_linked_resource/returns_nil_if_link_is_nil.yml +62 -0
  36. data/spec/cassettes/DeskApi_Resource/_get_linked_resource/saves_the_linked_resource_instead_of_the_url.yml +113 -0
  37. data/spec/cassettes/DeskApi_Resource/_method_missing/loads_the_resource_to_find_a_suitable_method.yml +34 -285
  38. data/spec/cassettes/DeskApi_Resource/_method_missing/raises_an_error_if_method_does_not_exist.yml +34 -285
  39. data/spec/cassettes/DeskApi_Resource/_page/keeps_the_resource_as_loaded.yml +113 -0
  40. data/spec/cassettes/DeskApi_Resource/_page/returns_the_current_page_and_loads_if_page_not_defined.yml +62 -0
  41. data/spec/cassettes/DeskApi_Resource/_page/sets_the_resource_to_not_loaded.yml +113 -0
  42. data/spec/cassettes/DeskApi_Resource/_search/allows_searching_on_search_enabled_resources.yml +23 -23
  43. data/spec/cassettes/DeskApi_Resource/_search/throws_an_error_if_search_is_not_enabled.yml +52 -0
  44. data/spec/cassettes/DeskApi_Resource/_update/can_update_without_a_hash.yml +178 -58
  45. data/spec/cassettes/DeskApi_Resource/_update/throws_an_error_updating_a_user.yml +86 -28
  46. data/spec/cassettes/DeskApi_Resource/_update/updates_a_topic.yml +61 -57
  47. data/spec/desk_api/client_spec.rb +9 -16
  48. data/spec/desk_api/resource_spec.rb +118 -43
  49. data/spec/spec_helper.rb +1 -1
  50. metadata +24 -102
  51. data/lib/desk_api/action/create.rb +0 -15
  52. data/lib/desk_api/action/delete.rb +0 -9
  53. data/lib/desk_api/action/embeddable.rb +0 -47
  54. data/lib/desk_api/action/field.rb +0 -33
  55. data/lib/desk_api/action/link.rb +0 -29
  56. data/lib/desk_api/action/resource.rb +0 -14
  57. data/lib/desk_api/action/search.rb +0 -15
  58. data/lib/desk_api/action/update.rb +0 -17
  59. data/lib/desk_api/error/method_not_supported.rb +0 -9
  60. data/lib/desk_api/error/not_embeddable.rb +0 -8
  61. data/lib/desk_api/error/not_updateable.rb +0 -10
  62. data/lib/desk_api/error/parse_error.rb +0 -9
  63. data/lib/desk_api/resource/article.rb +0 -10
  64. data/lib/desk_api/resource/article_translation.rb +0 -8
  65. data/lib/desk_api/resource/attachment.rb +0 -8
  66. data/lib/desk_api/resource/case.rb +0 -11
  67. data/lib/desk_api/resource/company.rb +0 -8
  68. data/lib/desk_api/resource/customer.rb +0 -9
  69. data/lib/desk_api/resource/filter.rb +0 -7
  70. data/lib/desk_api/resource/inbound_mailbox.rb +0 -7
  71. data/lib/desk_api/resource/integration_url.rb +0 -9
  72. data/lib/desk_api/resource/job.rb +0 -9
  73. data/lib/desk_api/resource/label.rb +0 -9
  74. data/lib/desk_api/resource/macro.rb +0 -9
  75. data/lib/desk_api/resource/macro_action.rb +0 -7
  76. data/lib/desk_api/resource/note.rb +0 -7
  77. data/lib/desk_api/resource/page.rb +0 -64
  78. data/lib/desk_api/resource/reply.rb +0 -10
  79. data/lib/desk_api/resource/topic.rb +0 -9
  80. data/lib/desk_api/resource/topic_translation.rb +0 -9
  81. data/lib/desk_api/resource/user_preference.rb +0 -7
  82. data/lib/desk_api/resources.json +0 -76
  83. data/spec/cassettes/DeskApi_Client/_delete/deletes_a_resource.yml +0 -48
  84. data/spec/cassettes/DeskApi_Client/_get/fetches_resources.yml +0 -55
  85. data/spec/cassettes/DeskApi_Client/_patch/updates_a_resource.yml +0 -62
  86. data/spec/cassettes/DeskApi_Client/_post/creates_a_resource.yml +0 -58
  87. data/spec/cassettes/DeskApi_Resource_Case/once_closed/can_not_be_updated.yml +0 -124
  88. data/spec/cassettes/DeskApi_Resource_Page/_find/has_an_alias_by_id.yml +0 -54
  89. data/spec/cassettes/DeskApi_Resource_Page/_find/loads_the_requested_resource.yml +0 -54
  90. data/spec/cassettes/DeskApi_Resource_Page/_page/keeps_the_resource_as_loaded.yml +0 -113
  91. data/spec/cassettes/DeskApi_Resource_Page/_page/returns_the_current_page_and_loads_if_page_not_defined.yml +0 -313
  92. data/spec/cassettes/DeskApi_Resource_Page/_page/sets_the_resource_to_not_loaded.yml +0 -113
  93. data/spec/desk_api/resource/case_spec.rb +0 -28
  94. data/spec/desk_api/resource/page_spec.rb +0 -68
@@ -1,67 +1,63 @@
1
- require 'desk_api/version'
2
-
3
- module DeskApi
4
- module Default
5
- CONNECTION_OPTIONS = {
6
- headers: {
7
- accept: 'application/json',
8
- user_agent: "desk.com Ruby Gem v#{DeskApi::VERSION}"
9
- },
10
- request: {
11
- open_timeout: 5,
12
- timeout: 10
13
- }
14
- } unless defined? DeskApi::Default::CONNECTION_OPTIONS
15
-
16
- class << self
17
- # @return [Hash]
18
- def options
19
- Hash[DeskApi::Configuration.keys.map{|key| [key, send(key)]}]
20
- end
1
+ module DeskApi::Default
2
+ CONNECTION_OPTIONS = {
3
+ headers: {
4
+ accept: 'application/json',
5
+ user_agent: "desk.com Ruby Gem v#{DeskApi::VERSION}"
6
+ },
7
+ request: {
8
+ open_timeout: 5,
9
+ timeout: 10
10
+ }
11
+ } unless defined? DeskApi::Default::CONNECTION_OPTIONS
12
+
13
+ class << self
14
+ # @return [Hash]
15
+ def options
16
+ Hash[DeskApi::Configuration.keys.map{|key| [key, send(key)]}]
17
+ end
21
18
 
22
- # @return [String]
23
- def username
24
- ENV['DESK_USERNAME']
25
- end
19
+ # @return [String]
20
+ def username
21
+ ENV['DESK_USERNAME']
22
+ end
26
23
 
27
- # @return [String]
28
- def password
29
- ENV['DESK_PASSWORD']
30
- end
24
+ # @return [String]
25
+ def password
26
+ ENV['DESK_PASSWORD']
27
+ end
31
28
 
32
- # @return [String]
33
- def consumer_key
34
- ENV['DESK_CONSUMER_KEY']
35
- end
29
+ # @return [String]
30
+ def consumer_key
31
+ ENV['DESK_CONSUMER_KEY']
32
+ end
36
33
 
37
- # @return [String]
38
- def consumer_secret
39
- ENV['DESK_CONSUMER_SECRET']
40
- end
34
+ # @return [String]
35
+ def consumer_secret
36
+ ENV['DESK_CONSUMER_SECRET']
37
+ end
41
38
 
42
- # @return [String]
43
- def token
44
- ENV['DESK_TOKEN']
45
- end
39
+ # @return [String]
40
+ def token
41
+ ENV['DESK_TOKEN']
42
+ end
46
43
 
47
- # @return [String]
48
- def token_secret
49
- ENV['DESK_TOKEN_SECRET']
50
- end
44
+ # @return [String]
45
+ def token_secret
46
+ ENV['DESK_TOKEN_SECRET']
47
+ end
51
48
 
52
- # @return [String]
53
- def subdomain
54
- ENV['DESK_SUBDOMAIN']
55
- end
49
+ # @return [String]
50
+ def subdomain
51
+ ENV['DESK_SUBDOMAIN']
52
+ end
56
53
 
57
- # @return [String]
58
- def endpoint
59
- ENV['DESK_ENDPOINT']
60
- end
54
+ # @return [String]
55
+ def endpoint
56
+ ENV['DESK_ENDPOINT']
57
+ end
61
58
 
62
- def connection_options
63
- CONNECTION_OPTIONS
64
- end
59
+ def connection_options
60
+ CONNECTION_OPTIONS
65
61
  end
66
62
  end
67
63
  end
@@ -1,59 +1,57 @@
1
1
  require 'desk_api/rate_limit'
2
2
 
3
- module DeskApi
4
- # Custom error class for rescuing from all desk.com errors
5
- class Error < StandardError
6
- attr_reader :rate_limit
3
+ # Custom error class for rescuing from all desk.com errors
4
+ class DeskApi::Error < StandardError
5
+ attr_reader :rate_limit
6
+
7
+ # Initializes a new Error object
8
+ #
9
+ # @param exception [Exception, String]
10
+ # @param response_headers [Hash]
11
+ # @param code [Integer]
12
+ # @return [DeskApi::Error]
13
+ def initialize(exception=$!, response_headers={}, code = nil, err_hash = nil)
14
+ @rate_limit = DeskApi::RateLimit.new(response_headers)
15
+ @wrapped_exception, @code, @errors = exception, code, err_hash
16
+ exception.respond_to?(:backtrace) ? super(exception.message) : super(exception.to_s)
17
+ end
18
+
19
+ def backtrace
20
+ @wrapped_exception.respond_to?(:backtrace) ? @wrapped_exception.backtrace : super
21
+ end
7
22
 
8
- # Initializes a new Error object
23
+ class << self
24
+ # Create a new error from an HTTP response
9
25
  #
10
- # @param exception [Exception, String]
11
- # @param response_headers [Hash]
12
- # @param code [Integer]
26
+ # @param response [Hash]
13
27
  # @return [DeskApi::Error]
14
- def initialize(exception=$!, response_headers={}, code = nil, err_hash = nil)
15
- @rate_limit = DeskApi::RateLimit.new(response_headers)
16
- @wrapped_exception, @code, @errors = exception, code, err_hash
17
- exception.respond_to?(:backtrace) ? super(exception.message) : super(exception.to_s)
18
- end
19
-
20
- def backtrace
21
- @wrapped_exception.respond_to?(:backtrace) ? @wrapped_exception.backtrace : super
28
+ def from_response(response = {})
29
+ err_hash, error, code = parse_body(response[:body]).push(response[:status])
30
+ new(error, response[:response_headers], code, err_hash)
22
31
  end
23
32
 
24
- class << self
25
- # Create a new error from an HTTP response
26
- #
27
- # @param response [Hash]
28
- # @return [DeskApi::Error]
29
- def from_response(response = {})
30
- err_hash, error, code = parse_body(response[:body]).push(response[:status])
31
- new(error, response[:response_headers], code, err_hash)
32
- end
33
-
34
- # @return [Hash]
35
- def errors
36
- @errors ||= descendants.each_with_object({}) do |klass, hash|
37
- hash[klass::HTTP_STATUS_CODE] = klass
38
- end
39
- end
40
-
41
- # @return [Array]
42
- def descendants
43
- @descendants ||= []
33
+ # @return [Hash]
34
+ def errors
35
+ @errors ||= descendants.each_with_object({}) do |klass, hash|
36
+ hash[klass::HTTP_STATUS_CODE] = klass
44
37
  end
38
+ end
45
39
 
46
- # @return [Array]
47
- def inherited(descendant)
48
- descendants << descendant
49
- end
40
+ # @return [Array]
41
+ def descendants
42
+ @descendants ||= []
43
+ end
50
44
 
51
- private
45
+ # @return [Array]
46
+ def inherited(descendant)
47
+ descendants << descendant
48
+ end
52
49
 
53
- def parse_body(body = {})
54
- [body['errors'] || nil, body['message'] || nil]
55
- end
50
+ private
56
51
 
52
+ def parse_body(body = {})
53
+ [body['errors'] || nil, body['message'] || nil]
57
54
  end
55
+
58
56
  end
59
57
  end
@@ -1,26 +1,24 @@
1
- module DeskApi
2
- class RateLimit
3
- def initialize(attrs = {})
4
- @attrs = attrs
5
- end
6
-
7
- # @return [Integer]
8
- def limit
9
- limit = @attrs['x-rate-limit-limit']
10
- limit.to_i if limit
11
- end
1
+ class DeskApi::RateLimit
2
+ def initialize(attrs = {})
3
+ @attrs = attrs
4
+ end
5
+
6
+ # @return [Integer]
7
+ def limit
8
+ limit = @attrs['x-rate-limit-limit']
9
+ limit.to_i if limit
10
+ end
12
11
 
13
- # @return [Integer]
14
- def remaining
15
- remaining = @attrs['x-rate-limit-remaining']
16
- remaining.to_i if remaining
17
- end
12
+ # @return [Integer]
13
+ def remaining
14
+ remaining = @attrs['x-rate-limit-remaining']
15
+ remaining.to_i if remaining
16
+ end
18
17
 
19
- # @return [Integer]
20
- def reset_in
21
- reset_in = @attrs['x-rate-limit-reset']
22
- reset_in.to_i if reset_in
23
- end
24
- alias retry_after reset_in
18
+ # @return [Integer]
19
+ def reset_in
20
+ reset_in = @attrs['x-rate-limit-reset']
21
+ reset_in.to_i if reset_in
25
22
  end
23
+ alias retry_after reset_in
26
24
  end
@@ -1,86 +1,163 @@
1
- require 'desk_api/action/create'
2
- require 'desk_api/action/delete'
3
- require 'desk_api/action/embeddable'
4
- require 'desk_api/action/field'
5
- require 'desk_api/action/link'
6
- require 'desk_api/action/resource'
7
- require 'desk_api/action/search'
8
- require 'desk_api/action/update'
9
-
10
- require 'desk_api/error/method_not_supported'
11
-
12
- module DeskApi
13
- class Resource
14
- include DeskApi::Action::Resource
15
- include DeskApi::Action::Link
16
- include DeskApi::Action::Field
17
- include DeskApi::Action::Embeddable
18
-
19
- def initialize(client, definition = {}, loaded = false)
20
- @client, @loaded, @_changed = client, loaded, {}
21
- setup(definition)
1
+ class DeskApi::Resource
2
+ class << self
3
+ def build_self_link(link)
4
+ link = {'href'=>link} if link.kind_of?(String)
5
+ {'_links'=>{'self'=>link}}
22
6
  end
7
+ end
23
8
 
24
- def by_url(url)
25
- definition = client.get(url).body
26
- resource(definition._links.self['class']).new(client, definition, true)
27
- end
9
+ def initialize(client, definition = {}, loaded = false)
10
+ @_client, @_definition, @_loaded, @_changed = client, definition, loaded, {}
11
+ end
28
12
 
29
- def get_self
30
- @_links.self
31
- end
13
+ def create(params = {})
14
+ self.class.new(@_client, @_client.post(clean_base_url, params).body, true)
15
+ end
32
16
 
33
- def get_href
34
- get_self['href']
35
- end
17
+ def update(params = {})
18
+ params.each_pair{ |key, value| send("#{key}=", value) }
19
+ changes = @_changed.clone
20
+ @_changed = {}
21
+ @_definition = @_client.patch(href, changes).body
22
+ end
36
23
 
37
- protected
24
+ def delete
25
+ @_client.delete(href).status === 204
26
+ end
38
27
 
39
- def exec!(reload = false)
40
- return self if loaded and !reload
41
- definition, @loaded = client.get(get_href).body, true
42
- setup(definition)
43
- end
28
+ def search(params = {})
29
+ params = { q: params } if params.kind_of?(String)
30
+ url = Addressable::URI.parse(clean_base_url + '/search')
31
+ url.query_values = params
32
+ self.class.new(@_client, self.class.build_self_link(url.to_s))
33
+ end
44
34
 
45
- def query_params
46
- Addressable::URI.parse(@_links.self.href).query_values || {}
47
- end
35
+ def find(id, options = {})
36
+ res = self.class.new(@_client, self.class.build_self_link("#{clean_base_url}/#{id}"))
37
+ res.embed(*(options[:embed].kind_of?(Array) ? options[:embed] : [options[:embed]])) if options[:embed]
38
+ res.exec!
39
+ end
40
+ alias_method :by_id, :find
48
41
 
49
- def query_params_include?(param)
50
- query_params.include?(param) ? query_params[param] : nil
51
- end
42
+ def embed(*embedds)
43
+ # make sure we don't try to embed anything that's not defined
44
+ # add it to the query
45
+ self.tap{ |res| res.query_params = { embed: embedds.join(',') } }
46
+ end
52
47
 
53
- def query_params=(params = {})
54
- return @_links.self.href if params.empty?
48
+ def by_url(url)
49
+ self.class.new(@_client, self.class.build_self_link(url))
50
+ end
51
+
52
+ def get_self
53
+ @_definition['_links']['self']
54
+ end
55
55
 
56
- uri = Addressable::URI.parse(@_links.self.href)
57
- params = (uri.query_values || {}).merge(params)
56
+ def href
57
+ get_self['href']
58
+ end
59
+ alias_method :get_href, :href
58
60
 
59
- @loaded = false unless params == uri.query_values
61
+ def href=(value)
62
+ @_definition['_links']['self']['href'] = value
63
+ end
64
+
65
+ def type
66
+ get_self['class']
67
+ end
60
68
 
61
- uri.query_values = params
62
- @_links.self.href = uri.to_s
69
+ [:page, :per_page].each do |method|
70
+ define_method(method) do |value = nil|
71
+ unless value
72
+ self.exec! if self.query_params_include?(method.to_s) == nil
73
+ return self.query_params_include?(method.to_s).to_i
74
+ end
75
+ self.tap{ |res| res.query_params = Hash[method.to_s, value.to_s] }
63
76
  end
77
+ end
78
+
79
+ protected
80
+
81
+ def clean_base_url
82
+ Addressable::URI.parse(href).path.gsub(/\/(search|\d+)$/, '')
83
+ end
84
+
85
+ def exec!(reload = false)
86
+ return self if @_loaded and !reload
87
+ @_definition, @_loaded = @_client.get(href).body, true
88
+ self
89
+ end
64
90
 
65
- def base_class
66
- self.class
91
+ def query_params
92
+ Addressable::URI.parse(href).query_values || {}
93
+ end
94
+
95
+ def query_params_include?(param)
96
+ query_params.include?(param) ? query_params[param] : nil
97
+ end
98
+
99
+ def query_params=(params = {})
100
+ return href if params.empty?
101
+
102
+ uri = Addressable::URI.parse(href)
103
+ params = (uri.query_values || {}).merge(params)
104
+
105
+ @_loaded = false unless params == uri.query_values
106
+
107
+ uri.query_values = params
108
+ self.href = uri.to_s
109
+ end
110
+
111
+ private
112
+ attr_accessor :_client, :_loaded, :_changed, :_definition
113
+
114
+ def is_field?(method)
115
+ @_definition.key?(method)
116
+ end
117
+
118
+ def is_link?(method)
119
+ @_definition.key?('_links') and @_definition['_links'].key?(method)
120
+ end
121
+
122
+ def is_embedded?(method)
123
+ @_definition.key?('_embedded') and @_definition['_embedded'].key?(method)
124
+ end
125
+
126
+ def get_field_value(method)
127
+ @_changed.key?(method) ? @_changed[method] : @_definition[method]
128
+ end
129
+
130
+ def get_embedded_resource(method)
131
+ embedds = @_definition['_embedded']
132
+
133
+ if embedds[method].kind_of?(Array) and not embedds[method].first.kind_of?(self.class)
134
+ embedds[method].map!{ |definition| self.class.new(@_client, definition, true) }
135
+ elsif not embedds[method].kind_of?(self.class)
136
+ embedds[method] = self.class.new(@_client, embedds[method], true)
137
+ else
138
+ embedds[method]
67
139
  end
140
+ end
68
141
 
69
- private
142
+ def get_linked_resource(method)
143
+ links = @_definition['_links']
144
+
145
+ return nil if links[method].nil?
146
+ return links[method] if links[method].kind_of?(self.class)
70
147
 
71
- attr_accessor :client, :loaded, :_changed
148
+ links[method] = self.class.new(@_client, self.class.build_self_link(links[method]))
149
+ end
72
150
 
73
- def setup(definition)
74
- setup_links(definition._links) if definition._links?
75
- setup_embedded(definition._embedded) if definition._embedded?
76
- setup_fields(definition)
77
- self
78
- end
151
+ def method_missing(method, *args, &block)
152
+ self.exec! unless @_loaded
79
153
 
80
- def method_missing(method, *args, &block)
81
- self.exec! if !loaded
82
- raise DeskApi::Error::MethodNotSupported unless self.respond_to?(method.to_sym)
83
- self.send(method, *args, &block)
84
- end
154
+ meth = method.to_s
155
+
156
+ return @_changed[meth[0...-1]] = args.first if meth.end_with?('=') and is_field?(meth[0...-1])
157
+ return get_field_value(meth) if is_field?(meth)
158
+ return get_embedded_resource(meth) if is_embedded?(meth)
159
+ return get_linked_resource(meth) if is_link?(meth)
160
+
161
+ super(method, *args, &block)
85
162
  end
86
163
  end