desk_api 0.5.8 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +2 -2
- data/README.md +274 -120
- data/lib/desk_api.rb +2 -2
- data/lib/desk_api/client.rb +6 -1
- data/lib/desk_api/configuration.rb +34 -26
- data/lib/desk_api/request/encode_json.rb +11 -0
- data/lib/desk_api/request/oauth.rb +20 -0
- data/lib/desk_api/request/retry.rb +37 -42
- data/lib/desk_api/resource.rb +86 -22
- data/lib/desk_api/response/parse_dates.rb +28 -0
- data/lib/desk_api/response/parse_json.rb +9 -0
- data/lib/desk_api/response/raise_error.rb +11 -16
- data/lib/desk_api/version.rb +1 -1
- data/spec/cassettes/DeskApi_Resource/_all/iterates_over_each_resource_on_each_page.yml +1953 -0
- data/spec/cassettes/DeskApi_Resource/_each_page/iterates_over_each_page.yml +1953 -0
- data/spec/cassettes/DeskApi_Resource/_each_page/raises_NoMethodError_is_called_on_non-page_resources.yml +207 -0
- data/spec/cassettes/DeskApi_Resource/_each_page/uses_a_default_per_page_of_1000.yml +1953 -0
- data/spec/cassettes/DeskApi_Resource/_load/loads_the_resource_if_not_already_loaded.yml +205 -0
- data/spec/cassettes/DeskApi_Resource/_loaded_/returns_true_if_the_resource_is_loaded.yml +205 -0
- data/spec/cassettes/DeskApi_Resource/_next_/changes__definition_to_next_page.yml +407 -0
- data/spec/cassettes/DeskApi_Resource/_next_/returns_nil_on_the_last_page.yml +315 -0
- data/spec/cassettes/DeskApi_Resource/_request/sends_request_through_a_client_and_returns_Faraday_Response.yml +207 -0
- data/spec/cassettes/DeskApi_Resource/_reset_/sets__links__embedded__changed_and__loaded_to_default_values.yml +253 -0
- data/spec/cassettes/DeskApi_Resource/_to_hash/converts_embedded_resources_to_hashes.yml +251 -0
- data/spec/cassettes/DeskApi_Resource/_to_hash/returns_a_hash_for_a_desk_resource.yml +66 -0
- data/spec/cassettes/DeskApi_Resource/_update/can_handle_action_params.yml +427 -0
- data/spec/desk_api/client_spec.rb +23 -17
- data/spec/desk_api/configuration_spec.rb +49 -41
- data/spec/desk_api/default_spec.rb +3 -3
- data/spec/desk_api/error_spec.rb +9 -7
- data/spec/desk_api/rate_limit_spec.rb +1 -1
- data/spec/desk_api/request/encode_json_spec.rb +33 -0
- data/spec/desk_api/request/oauth_spec.rb +31 -0
- data/spec/desk_api/request/retry_spec.rb +4 -4
- data/spec/desk_api/resource_spec.rb +247 -71
- data/spec/desk_api/response/parse_dates_spec.rb +34 -0
- data/spec/desk_api/response/parse_json_spec.rb +34 -0
- data/spec/desk_api/response/raise_error_spec.rb +31 -0
- data/spec/desk_api_spec.rb +6 -6
- data/spec/spec_helper.rb +2 -2
- data/spec/stubs/article.json +51 -0
- data/spec/stubs/to_hash_embed.json +1 -0
- metadata +78 -28
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -23
- data/.rspec +0 -1
- data/.travis.yml +0 -4
- data/.yardopts +0 -3
- data/Gemfile +0 -10
- data/Guardfile +0 -5
- data/Rakefile +0 -32
- data/desk_api.gemspec +0 -32
data/lib/desk_api.rb
CHANGED
data/lib/desk_api/client.rb
CHANGED
@@ -34,6 +34,11 @@ class DeskApi::Client
|
|
34
34
|
request(:patch, path, params)
|
35
35
|
end
|
36
36
|
|
37
|
+
# allow lookup by url
|
38
|
+
def by_url(url)
|
39
|
+
DeskApi::Resource.new(self, DeskApi::Resource.build_self_link(url))
|
40
|
+
end
|
41
|
+
|
37
42
|
private
|
38
43
|
# If the method is missing create a resource
|
39
44
|
def method_missing(method, params = {}, &block)
|
@@ -53,4 +58,4 @@ private
|
|
53
58
|
def connection
|
54
59
|
@connection ||= Faraday.new endpoint, connection_options, &middleware
|
55
60
|
end
|
56
|
-
end
|
61
|
+
end
|
@@ -1,9 +1,11 @@
|
|
1
|
-
require '
|
2
|
-
require 'faraday_middleware/version'
|
3
|
-
require 'faraday_middleware/response/parse_dates'
|
1
|
+
require 'faraday'
|
4
2
|
|
5
3
|
require 'desk_api/default'
|
6
4
|
require 'desk_api/request/retry'
|
5
|
+
require 'desk_api/request/oauth'
|
6
|
+
require 'desk_api/request/encode_json'
|
7
|
+
require 'desk_api/response/parse_dates'
|
8
|
+
require 'desk_api/response/parse_json'
|
7
9
|
require 'desk_api/response/raise_error'
|
8
10
|
require 'desk_api/error/configuration_error'
|
9
11
|
require 'desk_api/error/client_error'
|
@@ -29,6 +31,24 @@ module DeskApi::Configuration
|
|
29
31
|
:connection_options
|
30
32
|
]
|
31
33
|
end
|
34
|
+
|
35
|
+
def included(base)
|
36
|
+
if Gem::Version.new(Faraday::VERSION) >= Gem::Version.new('0.9.0')
|
37
|
+
Faraday::Request.register_middleware desk_encode_json: DeskApi::Request::EncodeJson
|
38
|
+
Faraday::Request.register_middleware desk_oauth: DeskApi::Request::OAuth
|
39
|
+
Faraday::Request.register_middleware desk_retry: DeskApi::Request::Retry
|
40
|
+
Faraday::Response.register_middleware desk_parse_dates: DeskApi::Response::ParseDates
|
41
|
+
Faraday::Response.register_middleware desk_parse_json: DeskApi::Response::ParseJson
|
42
|
+
Faraday::Response.register_middleware desk_raise_error: DeskApi::Response::RaiseError
|
43
|
+
else
|
44
|
+
Faraday.register_middleware :request, desk_encode_json: DeskApi::Request::EncodeJson
|
45
|
+
Faraday.register_middleware :request, desk_oauth: DeskApi::Request::OAuth
|
46
|
+
Faraday.register_middleware :request, desk_retry: DeskApi::Request::Retry
|
47
|
+
Faraday.register_middleware :response, desk_parse_dates: DeskApi::Response::ParseDates
|
48
|
+
Faraday.register_middleware :response, desk_parse_json: DeskApi::Response::ParseJson
|
49
|
+
Faraday.register_middleware :response, desk_raise_error: DeskApi::Response::RaiseError
|
50
|
+
end
|
51
|
+
end
|
32
52
|
end
|
33
53
|
|
34
54
|
# if subdomain is set make sure endpoint is correct
|
@@ -38,28 +58,16 @@ module DeskApi::Configuration
|
|
38
58
|
|
39
59
|
def middleware
|
40
60
|
@middleware ||= Proc.new do |builder|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
else
|
52
|
-
builder.use FaradayMiddleware::EncodeJson
|
53
|
-
builder.use Faraday::Request::BasicAuthentication, @username, @password if basic_auth.values.all?
|
54
|
-
builder.use FaradayMiddleware::OAuth, oauth if oauth.values.all?
|
55
|
-
builder.use DeskApi::Request::Retry
|
56
|
-
|
57
|
-
builder.use FaradayMiddleware::ParseDates
|
58
|
-
builder.use DeskApi::Response::RaiseError, DeskApi::Error::ClientError
|
59
|
-
builder.use DeskApi::Response::RaiseError, DeskApi::Error::ServerError
|
60
|
-
builder.use FaradayMiddleware::ParseJson, content_type: /application\/json/
|
61
|
-
end
|
62
|
-
|
61
|
+
builder.request :desk_encode_json
|
62
|
+
builder.request :basic_auth, @username, @password if basic_auth.values.all?
|
63
|
+
builder.request :desk_oauth, oauth if oauth.values.all?
|
64
|
+
builder.request :desk_retry
|
65
|
+
|
66
|
+
builder.response :desk_parse_dates
|
67
|
+
builder.response :desk_raise_error, DeskApi::Error::ClientError
|
68
|
+
builder.response :desk_raise_error, DeskApi::Error::ServerError
|
69
|
+
builder.response :desk_parse_json
|
70
|
+
|
63
71
|
builder.adapter Faraday.default_adapter
|
64
72
|
end
|
65
73
|
end
|
@@ -136,4 +144,4 @@ private
|
|
136
144
|
raise(DeskApi::Error::ConfigurationError, "Invalid endpoint specified: `#{endpoint}` must be a valid url.")
|
137
145
|
end
|
138
146
|
end
|
139
|
-
end
|
147
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module DeskApi::Request
|
2
|
+
class EncodeJson < Faraday::Middleware
|
3
|
+
dependency 'json'
|
4
|
+
|
5
|
+
def call(env)
|
6
|
+
env[:request_headers]['Content-Type'] = 'application/json'
|
7
|
+
env[:body] = ::JSON.dump(env[:body]) if env[:body] and not env[:body].to_s.empty?
|
8
|
+
@app.call env
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module DeskApi::Request
|
2
|
+
class OAuth < Faraday::Middleware
|
3
|
+
dependency 'simple_oauth'
|
4
|
+
|
5
|
+
def initialize(app, options)
|
6
|
+
super(app)
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
env[:request_headers]['Authorization'] = oauth(env).to_s
|
12
|
+
@app.call env
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def oauth(env)
|
17
|
+
SimpleOAuth::Header.new env[:method], env[:url].to_s, {}, @options
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,52 +1,47 @@
|
|
1
|
-
module DeskApi
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
1
|
+
module DeskApi::Request
|
2
|
+
class Retry < Faraday::Middleware
|
3
|
+
def initialize(app, options = {})
|
4
|
+
@max = options[:max] || 3
|
5
|
+
@interval = options[:interval] || 10
|
6
|
+
super(app)
|
7
|
+
end
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
raise
|
23
|
-
rescue exception_matcher
|
24
|
-
if retries > 0
|
25
|
-
retries -= 1
|
26
|
-
sleep @interval
|
27
|
-
env = env_clone
|
28
|
-
retry
|
29
|
-
end
|
30
|
-
raise
|
9
|
+
def call(env)
|
10
|
+
retries = @max
|
11
|
+
request_body = env[:body]
|
12
|
+
begin
|
13
|
+
env[:body] = request_body
|
14
|
+
@app.call(env)
|
15
|
+
rescue DeskApi::Error::TooManyRequests => e
|
16
|
+
if retries > 0
|
17
|
+
retries = 0
|
18
|
+
sleep e.rate_limit.reset_in
|
19
|
+
retry
|
31
20
|
end
|
21
|
+
raise
|
22
|
+
rescue exception_matcher
|
23
|
+
if retries > 0
|
24
|
+
retries -= 1
|
25
|
+
sleep @interval
|
26
|
+
retry
|
27
|
+
end
|
28
|
+
raise
|
32
29
|
end
|
30
|
+
end
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
32
|
+
def exception_matcher
|
33
|
+
exceptions = [Errno::ETIMEDOUT, 'Timeout::Error', Faraday::Error::TimeoutError]
|
34
|
+
matcher = Module.new
|
35
|
+
(class << matcher; self; end).class_eval do
|
36
|
+
define_method(:===) do |error|
|
37
|
+
exceptions.any? do |ex|
|
38
|
+
if ex.is_a? Module then error.is_a? ex
|
39
|
+
else error.class.to_s == ex.to_s
|
43
40
|
end
|
44
41
|
end
|
45
42
|
end
|
46
|
-
matcher
|
47
43
|
end
|
44
|
+
matcher
|
48
45
|
end
|
49
|
-
|
50
|
-
Faraday.register_middleware :request, retry: lambda { Retry }
|
51
46
|
end
|
52
|
-
end
|
47
|
+
end
|
data/lib/desk_api/resource.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
class DeskApi::Resource
|
2
|
+
# by_url is deprecated on resources
|
3
|
+
extend Forwardable
|
4
|
+
def_delegator :@_client, :by_url, :by_url
|
5
|
+
|
2
6
|
class << self
|
3
7
|
def build_self_link(link, params = {})
|
4
8
|
link = {'href'=>link} if link.kind_of?(String)
|
@@ -7,11 +11,13 @@ class DeskApi::Resource
|
|
7
11
|
end
|
8
12
|
|
9
13
|
def initialize(client, definition = {}, loaded = false)
|
10
|
-
|
14
|
+
reset!
|
15
|
+
@_client, @_definition, @_loaded = client, definition, loaded
|
16
|
+
# better default
|
11
17
|
end
|
12
18
|
|
13
19
|
def create(params = {})
|
14
|
-
|
20
|
+
new_resource(@_client.post(clean_base_url, params).body, true)
|
15
21
|
end
|
16
22
|
|
17
23
|
def update(params = {})
|
@@ -30,26 +36,51 @@ class DeskApi::Resource
|
|
30
36
|
params = { q: params } if params.kind_of?(String)
|
31
37
|
url = Addressable::URI.parse(clean_base_url + '/search')
|
32
38
|
url.query_values = params
|
33
|
-
|
39
|
+
new_resource(self.class.build_self_link(url.to_s))
|
34
40
|
end
|
35
41
|
|
36
42
|
def find(id, options = {})
|
37
|
-
res =
|
43
|
+
res = new_resource(self.class.build_self_link("#{clean_base_url}/#{id}"))
|
38
44
|
res.embed(*(options[:embed].kind_of?(Array) ? options[:embed] : [options[:embed]])) if options[:embed]
|
39
45
|
res.exec!
|
40
46
|
end
|
41
47
|
alias_method :by_id, :find
|
42
48
|
|
49
|
+
def next!
|
50
|
+
self.load
|
51
|
+
next_page = @_definition['_links']['next']
|
52
|
+
|
53
|
+
if next_page
|
54
|
+
@_definition = self.class.build_self_link(next_page)
|
55
|
+
self.reset!
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def all(&block)
|
61
|
+
raise ArgumentError, "Block must be given for #all" unless block_given?
|
62
|
+
each_page do |page, page_num|
|
63
|
+
page.entries.each { |resource| yield resource, page_num }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def each_page
|
68
|
+
raise ArgumentError, "Block must be given for #each_page" unless block_given?
|
69
|
+
page = self.first.per_page(self.query_params['per_page'] || 1000).dup
|
70
|
+
begin
|
71
|
+
yield page, page.page
|
72
|
+
end while page.next!
|
73
|
+
rescue NoMethodError => err
|
74
|
+
raise NoMethodError, "#each_page and #all are only available on resources which offer pagination"
|
75
|
+
end
|
76
|
+
|
77
|
+
|
43
78
|
def embed(*embedds)
|
44
79
|
# make sure we don't try to embed anything that's not defined
|
45
80
|
# add it to the query
|
46
81
|
self.tap{ |res| res.query_params = { embed: embedds.join(',') } }
|
47
82
|
end
|
48
83
|
|
49
|
-
def by_url(url)
|
50
|
-
self.class.new(@_client, self.class.build_self_link(url))
|
51
|
-
end
|
52
|
-
|
53
84
|
def get_self
|
54
85
|
@_definition['_links']['self']
|
55
86
|
end
|
@@ -63,6 +94,16 @@ class DeskApi::Resource
|
|
63
94
|
@_definition['_links']['self']['href'] = value
|
64
95
|
end
|
65
96
|
|
97
|
+
def to_hash
|
98
|
+
self.load
|
99
|
+
|
100
|
+
{}.tap do |hash|
|
101
|
+
@_definition.each do |k, v|
|
102
|
+
hash[k] = v
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
66
107
|
def resource_type
|
67
108
|
get_self['class']
|
68
109
|
end
|
@@ -100,7 +141,7 @@ class DeskApi::Resource
|
|
100
141
|
end
|
101
142
|
|
102
143
|
def respond_to?(method, include_private = false)
|
103
|
-
self.
|
144
|
+
self.load
|
104
145
|
meth = method.to_s
|
105
146
|
|
106
147
|
return true if is_embedded?(meth)
|
@@ -114,6 +155,15 @@ class DeskApi::Resource
|
|
114
155
|
def reload!
|
115
156
|
self.exec! true
|
116
157
|
end
|
158
|
+
alias_method :load!, :reload!
|
159
|
+
|
160
|
+
def load
|
161
|
+
self.exec! unless @_loaded
|
162
|
+
end
|
163
|
+
|
164
|
+
def loaded?
|
165
|
+
@_loaded
|
166
|
+
end
|
117
167
|
|
118
168
|
protected
|
119
169
|
|
@@ -127,11 +177,16 @@ protected
|
|
127
177
|
self
|
128
178
|
end
|
129
179
|
|
180
|
+
def reset!
|
181
|
+
@_links, @_embedded, @_changed, @_loaded = {}, {}, {}, false
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
130
185
|
private
|
131
|
-
attr_accessor :_client, :_loaded, :_changed, :_definition
|
186
|
+
attr_accessor :_client, :_loaded, :_changed, :_embedded, :_links, :_definition
|
132
187
|
|
133
188
|
def filter_update_actions(params = {})
|
134
|
-
params.select{ |key, _| key.to_s.include?('
|
189
|
+
params.select{ |key, _| key.to_s.include?('_action') }
|
135
190
|
end
|
136
191
|
|
137
192
|
def is_field?(method)
|
@@ -151,28 +206,37 @@ private
|
|
151
206
|
end
|
152
207
|
|
153
208
|
def get_embedded_resource(method)
|
154
|
-
|
209
|
+
return @_embedded[method] if @_embedded.key?(method)
|
210
|
+
@_embedded[method] = @_definition['_embedded'][method]
|
155
211
|
|
156
|
-
if
|
157
|
-
|
158
|
-
|
159
|
-
|
212
|
+
if @_embedded[method].kind_of?(Array)
|
213
|
+
@_embedded[method].tap do |ary|
|
214
|
+
ary.map!{ |definition| new_resource(definition, true) } unless ary.first.kind_of?(self.class)
|
215
|
+
end
|
160
216
|
else
|
161
|
-
|
217
|
+
@_embedded[method] = new_resource(@_embedded[method], true)
|
162
218
|
end
|
163
219
|
end
|
164
220
|
|
165
221
|
def get_linked_resource(method)
|
166
|
-
|
222
|
+
return @_links[method] if @_links.key?(method)
|
223
|
+
@_links[method] = @_definition['_links'][method]
|
224
|
+
|
225
|
+
if @_links[method] and not @_links[method].kind_of?(self.class)
|
226
|
+
@_links[method] = new_resource(self.class.build_self_link(@_links[method]))
|
227
|
+
end
|
228
|
+
end
|
167
229
|
|
168
|
-
|
169
|
-
|
230
|
+
def new_resource(definition, loaded=false, client=@_client)
|
231
|
+
self.class.new(client, definition, loaded)
|
232
|
+
end
|
170
233
|
|
171
|
-
|
234
|
+
def request(method, url, params={}, client=@_client)
|
235
|
+
client.send(method, url, params)
|
172
236
|
end
|
173
237
|
|
174
238
|
def method_missing(method, *args, &block)
|
175
|
-
self.
|
239
|
+
self.load
|
176
240
|
|
177
241
|
meth = method.to_s
|
178
242
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module DeskApi::Response
|
2
|
+
class ParseDates < Faraday::Response::Middleware
|
3
|
+
dependency 'time'
|
4
|
+
|
5
|
+
def on_complete(env)
|
6
|
+
env[:body] = parse_dates env[:body]
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def parse_dates(value)
|
12
|
+
case value
|
13
|
+
when Hash
|
14
|
+
value.each_pair do |key, element|
|
15
|
+
value[key] = parse_dates element
|
16
|
+
end
|
17
|
+
when Array
|
18
|
+
value.each_with_index do |element, index|
|
19
|
+
value[index] = parse_dates element
|
20
|
+
end
|
21
|
+
when /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z\Z/m
|
22
|
+
Time.parse value
|
23
|
+
else
|
24
|
+
value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|