esi 0.4.5 → 0.4.6

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
  SHA1:
3
- metadata.gz: 2917822d51f6a158183715498948e66554cf2ba9
4
- data.tar.gz: 3f0bfedf032f8b29c975e6c7fc0f42eb1914647d
3
+ metadata.gz: 4ef7c659090137fe7ff8581cdb6c6a9e9c240faa
4
+ data.tar.gz: 1e47ffce54eda2555fa7f64e3e841e0507162040
5
5
  SHA512:
6
- metadata.gz: 462fe687f11691c7a8c8fe34deb7ad834152caffa919db850a77a1a0f99958732600d7f8bef5500d913354f912d577eb05cadb2b922de709064127ed77b4ad1f
7
- data.tar.gz: e82ddb4376801d1c28fb420c93a9e383ae46c126cdae35c43a81009920245a5a705a2a383b42201871a8ac7faab8dcfd15fc1c912de3476e013dc639b10e21f5
6
+ metadata.gz: 3354040d05b7f3d674c072081b30815bc63b1ced681d64933e24461b039839f424e29724e2e624320120b652a5e4e43dc7253a5eca4fd3371b0a745bc6db60c1
7
+ data.tar.gz: 318a635d10e008cd0fc1daf06f8cfe028c677f041af49db8261a8f6d08d535862ff1a25ac758835b2397d9d96f0cbc84dce9dbecfffbbe6c5c0f3f85e366d825
data/esi.gemspec CHANGED
@@ -1,26 +1,30 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'esi/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "esi"
8
+ spec.name = 'esi'
8
9
  spec.version = Esi::VERSION
9
- spec.authors = ["Danny Hiemstra"]
10
- spec.email = ["dannyhiemstra@gmail.com"]
10
+ spec.authors = ['Danny Hiemstra', 'Aaron Allen']
11
+ spec.email = ['dannyhiemstra@gmail.com', 'aaronmallen4@gmail.com']
11
12
 
12
- spec.summary = "EVE ESI API wrapper"
13
- spec.description = "EVE ESI API wrapper"
14
- spec.homepage = "https://github.com/dhiemstra/esi"
15
- spec.license = "MIT"
13
+ spec.summary = 'EVE ESI API wrapper'
14
+ spec.description = 'EVE ESI API wrapper'
15
+ spec.homepage = 'https://github.com/dhiemstra/esi'
16
+ spec.license = 'MIT'
16
17
 
17
18
  spec.files = %w(LICENSE.txt README.md esi.gemspec) + Dir['lib/**/*.rb']
18
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
19
20
 
20
- spec.add_dependency "oauth2", "~> 1.4"
21
- spec.add_dependency "addressable", "~> 2.3"
22
- spec.add_dependency "activesupport"
23
- spec.add_development_dependency "bundler", "~> 1.14"
24
- spec.add_development_dependency "rake", "~> 10.0"
25
- spec.add_development_dependency "minitest", "~> 5.0"
21
+ spec.add_dependency 'activesupport'
22
+ spec.add_dependency 'addressable', '~> 2.3'
23
+ spec.add_dependency 'oauth2', '~> 1.4'
24
+ spec.add_development_dependency 'bundler', '~> 1.14'
25
+ spec.add_development_dependency 'minitest', '~> 5.0'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rubocop', '~> 0.52'
28
+ spec.add_development_dependency 'shoulda', '~> 3.5'
29
+ spec.add_development_dependency 'yard', '~> 0.9'
26
30
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Esi
2
4
  class AccessToken < OAuth2::AccessToken
3
5
  EXPIRES_MARGIN = 30.seconds
@@ -13,7 +15,7 @@ module Esi
13
15
  end
14
16
 
15
17
  def verify
16
- Esi::Response.new(get("/oauth/verify"))
18
+ Esi::Response.new(get('/oauth/verify'))
17
19
  end
18
20
 
19
21
  def expired?
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esi
4
+ # ApiError base class
5
+ # @!attribute [r] response
6
+ # @return [Esi::Response] the ApiError Response
7
+ # @!attribute [r] key
8
+ # @return [String] the response.data[:key]
9
+ # @!attribute [r] message
10
+ # @return [String] the response error message
11
+ # @!attribute [r] type
12
+ # @return [String] the response.data[:exceptionType]
13
+ # @!attribute [r] original_exception
14
+ # @return [ExceptionClass|nil] the orginal raised exception
15
+ class ApiError < OAuth2::Error
16
+ attr_reader :response, :key, :message, :type, :original_exception
17
+
18
+ # Create a new instance of ApiError
19
+ # @param [Esi::Response] response the response that generated the exception
20
+ # @param [ExceptionClass|nil] the orginally raised exception
21
+ # @return [Esi::ApiError] an instance of ApiError
22
+ def initialize(response, original_exception = nil)
23
+ super(response.original_response)
24
+
25
+ @response = response
26
+ @original_exception = original_exception
27
+ @code = response.original_response.status
28
+ @key = response.data[:key]
29
+ @message = response.data[:message].presence || response.data[:error] || original_exception.try(:message)
30
+ @type = response.data[:exceptionType]
31
+ end
32
+ end
33
+
34
+ # ApiRequestError Class
35
+ # @!attribute [r] original_exception
36
+ # @return [ExceptionClass|nil] the orginal raised exception
37
+ class ApiRequestError < StandardError
38
+ attr_reader :original_exception
39
+
40
+ # Create a new instance of ApiRequestError
41
+ # @param [ExceptionClass|nil] the orginally raised exception
42
+ # @return [Esi::ApiRequestError] the instance of ApiRequestError
43
+ def initialize(original_exception)
44
+ @original_exception = original_exception
45
+ msg = "#{original_exception.class}: " \
46
+ "#{original_exception.try(:response).try(:status)}" \
47
+ ' - ' \
48
+ "#{original_exception.try(:message)}"
49
+ super(msg)
50
+ end
51
+ end
52
+
53
+ # ApiUnknowntError Class
54
+ # @!attribute [r] response
55
+ # @return [Esi::Response] the ApiError Response
56
+ # @!attribute [r] key
57
+ # @return [String] the response.data[:key]
58
+ # @!attribute [r] message
59
+ # @return [String] the response error message
60
+ # @!attribute [r] type
61
+ # @return [String] the response.data[:exceptionType]
62
+ # @!attribute [r] original_exception
63
+ # @return [ExceptionClass|nil] the orginal raised exception
64
+ class ApiUnknownError < ApiError; end
65
+
66
+ # ApiBadRequestError Class
67
+ # @!attribute [r] response
68
+ # @return [Esi::Response] the ApiError Response
69
+ # @!attribute [r] key
70
+ # @return [String] the response.data[:key]
71
+ # @!attribute [r] message
72
+ # @return [String] the response error message
73
+ # @!attribute [r] type
74
+ # @return [String] the response.data[:exceptionType]
75
+ # @!attribute [r] original_exception
76
+ # @return [ExceptionClass|nil] the orginal raised exception
77
+ class ApiBadRequestError < ApiError; end
78
+
79
+ # ApiRefreshTokenExpiredError Class
80
+ # @!attribute [r] response
81
+ # @return [Esi::Response] the ApiError Response
82
+ # @!attribute [r] key
83
+ # @return [String] the response.data[:key]
84
+ # @!attribute [r] message
85
+ # @return [String] the response error message
86
+ # @!attribute [r] type
87
+ # @return [String] the response.data[:exceptionType]
88
+ # @!attribute [r] original_exception
89
+ # @return [ExceptionClass|nil] the orginal raised exception
90
+ class ApiRefreshTokenExpiredError < ApiError; end
91
+
92
+ # ApiInvalidAppClientKeysError Class
93
+ # @!attribute [r] response
94
+ # @return [Esi::Response] the ApiError Response
95
+ # @!attribute [r] key
96
+ # @return [String] the response.data[:key]
97
+ # @!attribute [r] message
98
+ # @return [String] the response error message
99
+ # @!attribute [r] type
100
+ # @return [String] the response.data[:exceptionType]
101
+ # @!attribute [r] original_exception
102
+ # @return [ExceptionClass|nil] the orginal raised exception
103
+ class ApiInvalidAppClientKeysError < ApiError; end
104
+
105
+ # ApiNotFoundError Class
106
+ # @!attribute [r] response
107
+ # @return [Esi::Response] the ApiError Response
108
+ # @!attribute [r] key
109
+ # @return [String] the response.data[:key]
110
+ # @!attribute [r] message
111
+ # @return [String] the response error message
112
+ # @!attribute [r] type
113
+ # @return [String] the response.data[:exceptionType]
114
+ # @!attribute [r] original_exception
115
+ # @return [ExceptionClass|nil] the orginal raised exception
116
+ class ApiNotFoundError < ApiError; end
117
+
118
+ # ApiForbiddenError Class
119
+ # @!attribute [r] response
120
+ # @return [Esi::Response] the ApiError Response
121
+ # @!attribute [r] key
122
+ # @return [String] the response.data[:key]
123
+ # @!attribute [r] message
124
+ # @return [String] the response error message
125
+ # @!attribute [r] type
126
+ # @return [String] the response.data[:exceptionType]
127
+ # @!attribute [r] original_exception
128
+ # @return [ExceptionClass|nil] the orginal raised exception
129
+ class ApiForbiddenError < ApiError; end
130
+
131
+ # Error Class
132
+ # @see [StandardError](https://ruby-doc.org/core-2.3.2/StandardError.html)
133
+ class Error < StandardError; end
134
+ end
File without changes
data/lib/esi/calls.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Esi
2
4
  class Calls
3
5
  class << self
@@ -29,13 +31,18 @@ module Esi
29
31
  end
30
32
 
31
33
  class Base
34
+ CACHE_NAMESPACE = 'esi'
35
+
32
36
  class_attribute :scope
33
37
  class_attribute :cache_duration
34
38
 
35
39
  attr_accessor :path, :params
36
40
 
37
41
  def cache_key
38
- @cache_key ||= ActiveSupport::Cache.expand_cache_key([path, params].flatten, :esi)
42
+ @cache_key ||= begin
43
+ cache_args = [CACHE_NAMESPACE, path.gsub(%r{^\/}, ''), params.sort].flatten
44
+ ActiveSupport::Cache.expand_cache_key(cache_args)
45
+ end
39
46
  end
40
47
 
41
48
  def method
@@ -52,13 +59,13 @@ module Esi
52
59
  end
53
60
 
54
61
  def paginated?
55
- !!@paginated
62
+ !@paginated
56
63
  end
57
64
  end
58
65
 
59
66
  class Regions < Base
60
67
  def initialize
61
- @path = "/universe/regions/"
68
+ @path = '/universe/regions/'
62
69
  end
63
70
  end
64
71
 
@@ -70,7 +77,7 @@ module Esi
70
77
 
71
78
  class Constellations < Base
72
79
  def initialize
73
- @path = "/universe/constellations/"
80
+ @path = '/universe/constellations/'
74
81
  end
75
82
  end
76
83
 
@@ -88,7 +95,7 @@ module Esi
88
95
 
89
96
  class SolarSystems < Base
90
97
  def initialize
91
- @path = "/universe/systems/"
98
+ @path = '/universe/systems/'
92
99
  end
93
100
  end
94
101
 
@@ -124,7 +131,7 @@ module Esi
124
131
 
125
132
  class Structures < Base
126
133
  def initialize
127
- @path = "/universe/structures/"
134
+ @path = '/universe/structures/'
128
135
  end
129
136
  end
130
137
 
@@ -136,7 +143,7 @@ module Esi
136
143
 
137
144
  class Types < Base
138
145
  def initialize
139
- @path = "/universe/types"
146
+ @path = '/universe/types'
140
147
  @paginated = true
141
148
  end
142
149
  end
@@ -149,7 +156,7 @@ module Esi
149
156
 
150
157
  class DogmaAttributes < Base
151
158
  def initialize
152
- @path = "/dogma/attributes/"
159
+ @path = '/dogma/attributes/'
153
160
  end
154
161
  end
155
162
 
@@ -161,7 +168,7 @@ module Esi
161
168
 
162
169
  class DogmaEffects < Base
163
170
  def initialize
164
- @path = "/dogma/effects/"
171
+ @path = '/dogma/effects/'
165
172
  end
166
173
  end
167
174
 
@@ -176,7 +183,7 @@ module Esi
176
183
  self.cache_duration = 3600
177
184
 
178
185
  def initialize
179
- @path = "/industry/facilities"
186
+ @path = '/industry/facilities'
180
187
  end
181
188
  end
182
189
 
@@ -185,7 +192,7 @@ module Esi
185
192
  self.cache_duration = 3600
186
193
 
187
194
  def initialize
188
- @path = "/industry/systems"
195
+ @path = '/industry/systems'
189
196
  end
190
197
  end
191
198
 
@@ -194,7 +201,7 @@ module Esi
194
201
  # https://esi.tech.ccp.is/latest/characters/907452336/search/?categories=structure&datasource=tranquility&search=Kamela&strict=false&token=Fp3ThF7wjvYBIDIIrtWE_Ryjt9BhYwUP75y2EL5Eq9mHPm8tYt9I9NwgZz8o26FFQBKoUToh2DYVc-Q5Ws400g2
195
202
 
196
203
  def initialize(character_id: nil, categories:, search:, strict: false)
197
- @path = (character_id ? "/characters/#{character_id}" : '') + "/search"
204
+ @path = (character_id ? "/characters/#{character_id}" : '') + '/search'
198
205
  @params = { categories: categories, search: search, strict: strict }
199
206
  end
200
207
  end
@@ -210,7 +217,7 @@ module Esi
210
217
  self.cache_duration = 3600
211
218
 
212
219
  def initialize(character_ids)
213
- @path = "/characters/names"
220
+ @path = '/characters/names'
214
221
  @params = { character_ids: character_ids.join(',') }
215
222
  end
216
223
  end
@@ -412,7 +419,7 @@ module Esi
412
419
  self.cache_duration = 3600
413
420
 
414
421
  def initialize
415
- @path = "/alliances"
422
+ @path = '/alliances'
416
423
  end
417
424
  end
418
425
 
@@ -420,7 +427,7 @@ module Esi
420
427
  self.cache_duration = 3600
421
428
 
422
429
  def initialize(alliance_ids)
423
- @path = "/alliances/names"
430
+ @path = '/alliances/names'
424
431
  @params = { alliance_ids: alliance_ids.join(',') }
425
432
  end
426
433
  end
@@ -437,7 +444,7 @@ module Esi
437
444
  self.cache_duration = 3600
438
445
 
439
446
  def initialize(corporation_ids)
440
- @path = "/corporations/names"
447
+ @path = '/corporations/names'
441
448
  @params = { corporation_ids: corporation_ids.join(',') }
442
449
  end
443
450
  end
@@ -526,7 +533,7 @@ module Esi
526
533
 
527
534
  class MarketGroups < Base
528
535
  def initialize
529
- @path = "/markets/groups"
536
+ @path = '/markets/groups'
530
537
  @paginated = true
531
538
  end
532
539
  end
@@ -539,7 +546,7 @@ module Esi
539
546
 
540
547
  class MarketPrices < Base
541
548
  def initialize
542
- @path = "/markets/prices"
549
+ @path = '/markets/prices'
543
550
  end
544
551
  end
545
552
 
@@ -605,7 +612,7 @@ module Esi
605
612
  self.scope = 'esi-ui.open_window.v1'
606
613
 
607
614
  def initialize(type_id)
608
- @path = "/ui/openwindow/marketdetails"
615
+ @path = '/ui/openwindow/marketdetails'
609
616
  @method = :post
610
617
  @params = { type_id: type_id }
611
618
  end
data/lib/esi/client.rb CHANGED
@@ -1,17 +1,90 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Esi
4
+ # The Esi Client class
5
+ # @!attribute [rw] refresh_callback
6
+ # @return [#callback] the refresh_token callback method
7
+ # @!attribute [rw] access_token
8
+ # @return [String] the esi access_token
9
+ # @!attribute [rw] refresh_token
10
+ # @return [String] the esi refresh_token string
11
+ # @!attribute [rw] expires_at
12
+ # @return [Time] the timestamp of the esi token expire
13
+ # @!attribute [r] logger
14
+ # @return [Logger] the logger class for the gem
15
+ # @!attribute [r] oauth
16
+ # @return [Esi::Oauth] the oauth instance for the client
2
17
  class Client
18
+ # @return [Fixnum] The max amount of request attempst Client will make
3
19
  MAX_ATTEMPTS = 2
4
20
 
5
21
  attr_accessor :refresh_callback, :access_token, :refresh_token, :expires_at
6
22
  attr_reader :logger, :oauth
7
23
 
24
+ # Create a new instance of Client
25
+ # @param [String] token the esi access_token
26
+ # @param [String] refresh_token the esi refresh_token
27
+ # @param [Time] expires_at the time stamp the esi token expires_at
8
28
  def initialize(token: nil, refresh_token: nil, expires_at: nil)
9
29
  @logger = Esi.logger
10
30
  @access_token = token
11
31
  @refresh_token = refresh_token
12
32
  @expires_at = expires_at
33
+ @oauth = init_oauth
34
+ end
35
+
36
+ # Set the current thread's Esi::Client
37
+ # @params [Esi::Client] the client to set
38
+ # @return [Esi::Client] the current thread's Esi::Client
39
+ def self.current=(client)
40
+ Thread.current[:esi_client] = client
41
+ end
42
+
43
+ # Get the current thread's Esi::Client
44
+ # @return [Esi::Client] the current thread's Esi::Client
45
+ def self.current
46
+ Thread.current[:esi_client] ||= new
47
+ end
48
+
49
+ # Switch to default Esi::Client (Esi::Client.new)
50
+ # @return [Esi::Client] the current thread's Esi::Client
51
+ def self.switch_to_default
52
+ self.current = new
53
+ end
54
+
55
+ # Switch current thread's client to instance of Esi::Client
56
+ # @return [self] the instance calling switch to
57
+ def switch_to
58
+ Esi::Client.current = self
59
+ end
60
+
61
+ # Yield block with instance of Esi::Client and revert to
62
+ # previous client or default client
63
+ #
64
+ # @example Call an Esi::Client method using an instance of client
65
+ # new_client = Esi::Client.new(token: 'foo', refresh_token: 'foo', expires_at: 30.minutes.from_now)
66
+ # new_client.with_client do |client|
67
+ # client.character(1234)
68
+ # end
69
+ # #=> Esi::Response<#>
70
+ #
71
+ # @yieldreturn [#block] the passed block.
72
+ def with_client
73
+ initial_client = Esi::Client.current
74
+ switch_to
75
+ yield(self) if block_given?
76
+ ensure
77
+ initial_client.switch_to if initial_client
78
+ Esi::Client.switch_to_default unless initial_client
13
79
  end
14
80
 
81
+ # Intercept Esi::Client method_missing and attempt to call an Esi::Request
82
+ # with an Esi::Calls
83
+ # @param [Symbol|String] name the name of the method called
84
+ # @param [Array] *args the arguments to call the method with
85
+ # @param [#block] &block the block to pass to the underlying method
86
+ # @raise [NameError] If the Esi::Calls does not exist
87
+ # @return [Esi::Response] the response given for the call
15
88
  def method_missing(name, *args, &block)
16
89
  klass = nil
17
90
  ActiveSupport::Notifications.instrument('esi.client.detect_call') do
@@ -25,6 +98,9 @@ module Esi
25
98
  cached_response(klass, *args, &block)
26
99
  end
27
100
 
101
+ # Test if the Esi::Client has a method
102
+ # @param [Symbol] name the name of the method to test
103
+ # @return [Boolean] wether or not the method exists
28
104
  def method?(name)
29
105
  begin
30
106
  klass = Esi::Calls.const_get(method_to_class_name(name))
@@ -34,15 +110,24 @@ module Esi
34
110
  !klass.nil?
35
111
  end
36
112
 
113
+ # Test if the Esi::Client has a pluralized version of a method
114
+ # @param [Symbol] name the name of the method to test
115
+ # @return [Boolean] wether or not the pluralized method exists
37
116
  def plural_method?(name)
38
117
  plural = name.to_s.pluralize.to_sym
39
118
  method? plural
40
119
  end
41
120
 
121
+ # Log a message
122
+ # @param [String] message the message to log
123
+ # @return [void] the Logger.info method with message
42
124
  def log(message)
43
125
  logger.info message
44
126
  end
45
127
 
128
+ # Log a message with debug
129
+ # @param [String] message the message to log
130
+ # @return [void] the Logger.debug method with message
46
131
  def debug(message)
47
132
  logger.debug message
48
133
  end
@@ -64,46 +149,45 @@ module Esi
64
149
  name.dup.to_s.split('_').map(&:capitalize).join
65
150
  end
66
151
 
67
- def oauth
152
+ def init_oauth
68
153
  @oauth ||= OAuth.new(
69
154
  access_token: @access_token,
70
155
  refresh_token: @refresh_token,
71
156
  expires_at: @expires_at,
72
- callback: -> (token, expires_at) {
157
+ callback: lambda { |token, expires_at|
73
158
  @access_token = token
74
159
  @expires_at = expires_at
75
- if refresh_callback.respond_to?(:call)
76
- refresh_callback.call(token, expires_at)
77
- end
160
+ refresh_callback.call(token, expires_at) if refresh_callback.respond_to?(:call)
78
161
  }
79
162
  )
80
163
  end
81
164
 
82
165
  def request_paginated(call, &block)
166
+ call.page = 1
83
167
  response = nil
84
- page = 1
85
-
86
168
  ActiveSupport::Notifications.instrument('esi.client.request.paginated') do
87
- loop do
88
- call.page = page
89
- page_response = request(call, &block)
90
-
91
- if page_response.data.blank?
92
- break
93
- elsif response
94
- response.merge(page_response)
95
- else
96
- response = page_response
97
- end
98
-
99
- page += 1
100
- end
169
+ response = paginated_response(response, call, &block)
101
170
  end
102
-
103
171
  response
104
172
  end
105
173
 
174
+ def paginated_response(response, call, &block)
175
+ loop do
176
+ page_response = request(call, &block)
177
+ break response if page_response.data.blank?
178
+ response = response ? response.merge(page_response) : page_response
179
+ call.page += 1
180
+ end
181
+ end
182
+
106
183
  # FIXME: esi should not retry
184
+ # FIXME: make rubocop compliant
185
+ # rubocop:disable Lint/ShadowedException
186
+ # rubocop:disable Metrics/AbcSize
187
+ # rubocop:disable Metrics/BlockLength
188
+ # rubocop:disable Metrics/CyclomaticComplexity
189
+ # rubocop:disable Metrics/MethodLength
190
+ # rubocop:disable Metrics/PerceivedComplexity
107
191
  def request(call, &block)
108
192
  response = nil
109
193
  last_ex = nil
@@ -113,12 +197,12 @@ module Esi
113
197
  debug "Starting request: #{url}"
114
198
 
115
199
  ActiveSupport::Notifications.instrument('esi.client.request') do
116
- 1.upto(MAX_ATTEMPTS) do |try|
200
+ 1.upto(MAX_ATTEMPTS) do |_try|
117
201
  last_ex = nil
118
202
  response = nil
119
203
 
120
204
  begin
121
- response = Timeout::timeout(Esi.config.timeout) do
205
+ response = Timeout.timeout(Esi.config.timeout) do
122
206
  oauth.request(call.method, url, options)
123
207
  end
124
208
  rescue Faraday::SSLError, Faraday::ConnectionFailed, Timeout::Error, Net::ReadTimeout => e
@@ -136,7 +220,7 @@ module Esi
136
220
  sleep 5
137
221
  next
138
222
  when 503 # Rate Limit
139
- logger.error "RateLimit error, sleeping for 5 seconds"
223
+ logger.error 'RateLimit error, sleeping for 5 seconds'
140
224
  sleep 5
141
225
  next
142
226
  when 404 # Not Found
@@ -166,34 +250,44 @@ module Esi
166
250
  break if response
167
251
  end
168
252
  end
253
+ # rubocop:enable Lint/ShadowedException
254
+ # rubocop:enable Metrics/AbcSize
255
+ # rubocop:enable Metrics/BlockLength
256
+ # rubocop:enable Metrics/CyclomaticComplexity
257
+ # rubocop:enable Metrics/MethodLength
258
+ # rubocop:enable Metrics/PerceivedComplexity
169
259
 
170
260
  if last_ex
171
261
  logger.error "Request failed with #{last_ex.class}"
172
262
  debug_error(last_ex.class, url, response)
173
- raise Esi::ApiRequestError.new(last_ex)
263
+ raise Esi::ApiRequestError, last_ex
174
264
  end
175
265
 
176
- debug "Request successful"
266
+ debug 'Request successful'
177
267
 
178
268
  ActiveSupport::Notifications.instrument('esi.client.response.render') do
179
269
  response = Response.new(response, call)
180
270
  response.save
181
271
  end
182
272
  ActiveSupport::Notifications.instrument('esi.client.response.callback') do
183
- response.data.each { |item| block.call(item) } if block
273
+ response.data.each { |item| yield(item) } if block
184
274
  end
185
275
  response
186
276
  end
187
277
 
188
278
  def debug_error(klass, url, response)
189
279
  [
190
- '-'*60,
280
+ '-' * 60,
191
281
  "#{klass}(#{response.error})",
192
282
  "STATUS: #{response.status}",
193
- "MESSAGE: #{response.respond_to?(:data) ? (response.data[:message].presence || response.data[:error]) : response.try(:body)}",
283
+ "MESSAGE: #{debug_message_for_response(response)}",
194
284
  "URL: #{url}",
195
- '-'*60,
285
+ '-' * 60
196
286
  ].each { |msg| logger.error(msg) }
197
287
  end
288
+
289
+ def debug_message_for_response(response)
290
+ response.respond_to?(:data) ? (response.data[:message].presence || response.data[:error]) : response.try(:body)
291
+ end
198
292
  end
199
293
  end
data/lib/esi/o_auth.rb CHANGED
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Esi
2
4
  class OAuth
3
5
  extend Forwardable
4
6
 
5
7
  attr_reader :access_token, :refresh_token, :expires_at
6
- def_delegators :token, :get, :post, :delete, :patch, :put
8
+ def_delegators :token, :request, :get, :post, :delete, :patch, :put
7
9
 
8
10
  class << self
9
11
  def authorize_url(redirect_uri:, scopes: nil)
@@ -20,15 +22,13 @@ module Esi
20
22
  def client
21
23
  @client ||= OAuth2::Client.new(
22
24
  Esi.config.client_id, Esi.config.client_secret,
23
- { site: Esi.config.oauth_host }
25
+ site: Esi.config.oauth_host
24
26
  )
25
27
  end
26
28
  end
27
29
 
28
30
  def initialize(access_token:, refresh_token:, expires_at:, callback: nil)
29
- if callback && !callback.respond_to?(:call)
30
- raise Esi::Error.new("Callback should be a callable Proc")
31
- end
31
+ raise Esi::Error, 'Callback should be a callable Proc' if callback && !callback.respond_to?(:call)
32
32
 
33
33
  @access_token = access_token
34
34
  @refresh_token = refresh_token
@@ -36,28 +36,21 @@ module Esi
36
36
  @callback = callback if callback
37
37
  end
38
38
 
39
- def request(*args)
40
- token.request(*args)
41
- end
42
-
43
39
  private
44
40
 
45
41
  def token
46
- @token = Esi::AccessToken.new(OAuth.client, @access_token, {
47
- refresh_token: @refresh_token, expires_at: @expires_at
48
- })
42
+ @token = Esi::AccessToken.new(OAuth.client, @access_token,
43
+ refresh_token: @refresh_token, expires_at: @expires_at)
49
44
  refresh_access_token if @token.expired?
50
45
  @token
51
46
  end
52
47
 
53
48
  def refresh_access_token
54
- Esi.logger.debug "Refreshing access token"
55
-
56
49
  ActiveSupport::Notifications.instrument('esi.oauth.refresh_token') do
57
50
  @token = @token.refresh!
58
51
  @access_token = @token.token
59
52
  @expires_at = @token.expires_at.integer? ? Time.at(@token.expires_at) : @token.expires_at
60
- @callback.call(@access_token, @expires_at) if @callback
53
+ @callback&.call(@access_token, @expires_at)
61
54
  end
62
55
  end
63
56
  end
data/lib/esi/response.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Esi
2
4
  class Response
3
5
  extend Forwardable
@@ -6,10 +8,10 @@ module Esi
6
8
  def_delegators :original_response, :status, :body, :headers
7
9
  def_delegators :data, :each
8
10
 
9
- def initialize(response, call=nil)
11
+ def initialize(response, call = nil)
10
12
  @original_response = response
13
+ @data = normalize_response_body
11
14
  @call = call
12
- @data = MultiJson.load(body || {}, symbolize_keys: true, object_class: OpenStruct) # rescue OpenStruct.new
13
15
  end
14
16
 
15
17
  def merge(other_response)
@@ -29,18 +31,40 @@ module Esi
29
31
  @cached_until ||= headers[:expires] ? Time.parse(headers[:expires]) : nil
30
32
  end
31
33
 
34
+ def response_json
35
+ @response_json ||= begin
36
+ MultiJson.load(body, symbolize_keys: true)
37
+ rescue StandardError
38
+ {}
39
+ end
40
+ end
41
+
32
42
  def save
33
- return if call.nil?
34
- return if Esi.config.response_log_path.blank? || !Dir.exists?(Esi.config.response_log_path)
43
+ return unless should_log_response?
44
+ File.write(log_directroy.join("#{Time.now.to_i}.json"), to_json)
45
+ end
35
46
 
36
- call_name = call.class.to_s.split('::').last.underscore
37
- folder = Pathname.new(Esi.config.response_log_path).join(call_name)
38
- FileUtils.mkdir_p(folder)
39
- File.write(folder.join("#{Time.now.to_i.to_s}.json"), to_json)
47
+ def log_directory
48
+ call.class.to_s.split('::').last.underscore
49
+ dir = Pathname.new(Esi.config.response_log_path).join(call_name)
50
+ FileUtils.mkdir_p(dir)
51
+ dir
52
+ end
53
+
54
+ def should_log_response?
55
+ return false if call.nil?
56
+ return false if Esi.config.response_log_path.blank? || !Dir.exist?(Esi.config.response_log_path)
57
+ true
40
58
  end
41
59
 
42
60
  def method_missing(method, *args, &block)
43
61
  data.send(method, *args, &block)
44
62
  end
63
+
64
+ private
65
+
66
+ def normalize_response_body
67
+ MultiJson.load(body || {}, symbolize_keys: true, object_class: OpenStruct)
68
+ end
45
69
  end
46
70
  end
data/lib/esi/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Esi
2
- VERSION = "0.4.5"
4
+ VERSION = '0.4.6'
3
5
  end
data/lib/esi.rb CHANGED
@@ -1,12 +1,19 @@
1
- require "oauth2"
2
- require "forwardable"
3
- require "ostruct"
4
- require "addressable/uri"
5
- require "active_support/cache"
6
- require "active_support/notifications"
1
+ # frozen_string_literal: true
2
+
3
+ require 'oauth2'
4
+ require 'forwardable'
5
+ require 'ostruct'
6
+ require 'addressable/uri'
7
+ require 'active_support/cache'
8
+ require 'active_support/notifications'
7
9
  require 'active_support/core_ext/string'
8
- require "active_support/core_ext/class/attribute"
10
+ require 'active_support/core_ext/class/attribute'
9
11
 
12
+ # The main Esi Module
13
+ # @!attribute [w] api_version
14
+ # @return [Symbol] the Esi Api version used by the gem
15
+ # @!attribute [w] logger
16
+ # @return [Logger] the logger class for the gem
10
17
  module Esi
11
18
  autoload :VERSION, 'esi/version'
12
19
  autoload :AccessToken, 'esi/access_token'
@@ -15,6 +22,10 @@ module Esi
15
22
  autoload :Client, 'esi/client'
16
23
  autoload :Response, 'esi/response'
17
24
 
25
+ require_relative 'esi/api_error'
26
+
27
+ # Default ESI access scopes
28
+ # @return [Array<String>] the default scopes
18
29
  SCOPES = %w(
19
30
  esi-assets.read_assets.v1
20
31
  esi-bookmarks.read_character_bookmarks.v1
@@ -61,7 +72,10 @@ module Esi
61
72
  esi-universe.read_structures.v1
62
73
  esi-wallet.read_character_wallet.v1
63
74
  esi-wallet.read_corporation_wallets.v1
64
- )
75
+ ).freeze
76
+
77
+ # The default Esi gem configuration
78
+ # @return [Hash{Symbol => Symbol|String|Fixnum|Object|Array}] the default configuration
65
79
  DEFAULT_CONFIG = {
66
80
  datasource: :tranquility,
67
81
  oauth_host: 'https://login.eveonline.com',
@@ -75,21 +89,27 @@ module Esi
75
89
  client_secret: nil,
76
90
  cache: ActiveSupport::Cache::MemoryStore.new,
77
91
  scopes: SCOPES
78
- }
92
+ }.freeze
79
93
 
80
94
  class << self
81
95
  attr_writer :api_version, :config, :logger, :cache
82
96
 
97
+ # The Esi Configuration
98
+ # @return [OpenStruct] the configuration object
83
99
  def config
84
100
  @config ||= OpenStruct.new(DEFAULT_CONFIG)
85
101
  end
86
102
 
103
+ # The Esi logger class instance
104
+ # @return [MyLoggerInstance|Logger] an instance of the logger class
87
105
  def logger
88
106
  @logger ||= Esi.config.logger || Logger.new(Esi.config.log_target).tap do |l|
89
107
  l.level = Logger.const_get(Esi.config.log_level.upcase)
90
108
  end
91
109
  end
92
110
 
111
+ # The Esi cache class instance
112
+ # @return [ActiveSupport::Cache::Store] an instance of cache
93
113
  def cache
94
114
  if Esi.config.cache.nil?
95
115
  @cache ||= ActiveSupport::Cache::NullStore.new
@@ -98,54 +118,35 @@ module Esi
98
118
  end
99
119
  end
100
120
 
121
+ # The Esi Api version to interface with
122
+ # @return [Symbol] the esi api version
101
123
  def api_version
102
124
  @api_version || :latest
103
125
  end
104
126
 
105
- def generate_url(path, params={})
106
- path = path[1..-1] if path.start_with?('/')
107
- path += "/" unless path.end_with?('/')
108
-
109
- url = [config.api_host, config.api_version, path].join('/')
127
+ # Generate an Esi url for a given path
128
+ # @param [String] path the path to generate an esi url for
129
+ # @param [Hash{Symbol => String|Fixnum}] params the params for the url query
130
+ # @return [String] the generated url
131
+ def generate_url(path, params = {})
132
+ url = url_for_path(path)
110
133
  uri = Addressable::URI.parse(url)
111
- uri.query_values = {datasource: config.datasource}.merge(params.to_h)
134
+ uri.query_values = { datasource: config.datasource }.merge(params.to_h)
112
135
  uri.to_s
113
136
  end
114
137
 
138
+ # The current Esi client
139
+ # @return [Esi::Client] the current client
115
140
  def client
116
- @client ||= Client.new
117
- end
118
- end
119
-
120
- class ApiError < OAuth2::Error
121
- attr_reader :response, :key, :message, :type, :original_exception
122
-
123
- def initialize(response, original_exception=nil)
124
- super(response.original_response)
125
-
126
- @response = response
127
- @original_exception = original_exception
128
- @code = response.original_response.status
129
- @key = response.data[:key]
130
- @message = response.data[:message].presence || response.data[:error] || original_exception.try(:message)
131
- @type = response.data[:exceptionType]
141
+ @client ||= Client.current
132
142
  end
133
- end
134
143
 
135
- class ApiRequestError < StandardError
136
- attr_reader :original_exception
144
+ private
137
145
 
138
- def initialize(original_exception)
139
- @original_exception = original_exception
140
- super("#{original_exception.class}: #{original_exception.try(:response).try(:status)} - #{original_exception.try(:message)}")
146
+ def url_for_path(path)
147
+ path = path[1..-1] if path.start_with?('/')
148
+ path += '/' unless path.end_with?('/')
149
+ [config.api_host, config.api_version, path].join('/')
141
150
  end
142
151
  end
143
-
144
- class ApiUnknownError < ApiError; end
145
- class ApiBadRequestError < ApiError; end
146
- class ApiRefreshTokenExpiredError < ApiError; end
147
- class ApiInvalidAppClientKeysError < ApiError; end
148
- class ApiNotFoundError < ApiError; end
149
- class ApiForbiddenError < ApiError; end
150
- class Error < StandardError; end
151
152
  end
data/lib/omniauth/esi.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'omniauth/strategies/esi'
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'omniauth/strategies/oauth2'
2
4
 
3
5
  module OmniAuth
4
6
  module Strategies
5
7
  class Esi < OmniAuth::Strategies::OAuth2
6
8
  option :name, 'esi'
7
- option :client_options, { site: ::Esi.config.oauth_host, verify_url: '/oauth/verify' }
8
- option :authorize_options, [:scope, :callback_url]
9
+ option :client_options, site: ::Esi.config.oauth_host, verify_url: '/oauth/verify'
10
+ option :authorize_options, %i(scope callback_url)
9
11
 
10
12
  uid { extra_info[:character_id] }
11
13
 
@@ -19,11 +21,11 @@ module OmniAuth
19
21
  end
20
22
 
21
23
  credentials do
22
- hash = {token: access_token.token}
23
- hash.merge!(refresh_token: access_token.refresh_token) if access_token.refresh_token
24
- hash.merge!(expires_at: access_token.expires_at) if access_token.expires?
25
- hash.merge!(expires: access_token.expires?)
26
- hash.merge!(scopes: extra_info[:scopes].split(' ')) if extra_info[:scopes]
24
+ hash = { token: access_token.token }
25
+ hash[:refresh_token] = access_token.refresh_token if access_token.refresh_token
26
+ hash[:expires_at] = access_token.expires_at if access_token.expires?
27
+ hash[:expires] = access_token.expires?
28
+ hash[:scopes] = extra_info[:scopes].split(' ') if extra_info[:scopes]
27
29
  hash
28
30
  end
29
31
 
@@ -32,7 +34,9 @@ module OmniAuth
32
34
  end
33
35
 
34
36
  def extra_info
35
- @extra_info ||= deep_symbolize(access_token.get(options.client_options.verify_url).parsed.transform_keys!(&:underscore))
37
+ @extra_info ||= deep_symbolize(
38
+ access_token.get(options.client_options.verify_url).parsed.transform_keys!(&:underscore)
39
+ )
36
40
  end
37
41
 
38
42
  def authorize_params
metadata CHANGED
@@ -1,29 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: esi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Hiemstra
8
+ - Aaron Allen
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2018-02-14 00:00:00.000000000 Z
12
+ date: 2018-02-22 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: oauth2
15
+ name: activesupport
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - "~>"
18
+ - - ">="
18
19
  - !ruby/object:Gem::Version
19
- version: '1.4'
20
+ version: '0'
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
- - - "~>"
25
+ - - ">="
25
26
  - !ruby/object:Gem::Version
26
- version: '1.4'
27
+ version: '0'
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: addressable
29
30
  requirement: !ruby/object:Gem::Requirement
@@ -39,19 +40,19 @@ dependencies:
39
40
  - !ruby/object:Gem::Version
40
41
  version: '2.3'
41
42
  - !ruby/object:Gem::Dependency
42
- name: activesupport
43
+ name: oauth2
43
44
  requirement: !ruby/object:Gem::Requirement
44
45
  requirements:
45
- - - ">="
46
+ - - "~>"
46
47
  - !ruby/object:Gem::Version
47
- version: '0'
48
+ version: '1.4'
48
49
  type: :runtime
49
50
  prerelease: false
50
51
  version_requirements: !ruby/object:Gem::Requirement
51
52
  requirements:
52
- - - ">="
53
+ - - "~>"
53
54
  - !ruby/object:Gem::Version
54
- version: '0'
55
+ version: '1.4'
55
56
  - !ruby/object:Gem::Dependency
56
57
  name: bundler
57
58
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +67,20 @@ dependencies:
66
67
  - - "~>"
67
68
  - !ruby/object:Gem::Version
68
69
  version: '1.14'
70
+ - !ruby/object:Gem::Dependency
71
+ name: minitest
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '5.0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '5.0'
69
84
  - !ruby/object:Gem::Dependency
70
85
  name: rake
71
86
  requirement: !ruby/object:Gem::Requirement
@@ -81,22 +96,51 @@ dependencies:
81
96
  - !ruby/object:Gem::Version
82
97
  version: '10.0'
83
98
  - !ruby/object:Gem::Dependency
84
- name: minitest
99
+ name: rubocop
85
100
  requirement: !ruby/object:Gem::Requirement
86
101
  requirements:
87
102
  - - "~>"
88
103
  - !ruby/object:Gem::Version
89
- version: '5.0'
104
+ version: '0.52'
90
105
  type: :development
91
106
  prerelease: false
92
107
  version_requirements: !ruby/object:Gem::Requirement
93
108
  requirements:
94
109
  - - "~>"
95
110
  - !ruby/object:Gem::Version
96
- version: '5.0'
111
+ version: '0.52'
112
+ - !ruby/object:Gem::Dependency
113
+ name: shoulda
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '3.5'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '3.5'
126
+ - !ruby/object:Gem::Dependency
127
+ name: yard
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - "~>"
131
+ - !ruby/object:Gem::Version
132
+ version: '0.9'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '0.9'
97
140
  description: EVE ESI API wrapper
98
141
  email:
99
142
  - dannyhiemstra@gmail.com
143
+ - aaronmallen4@gmail.com
100
144
  executables: []
101
145
  extensions: []
102
146
  extra_rdoc_files: []
@@ -106,7 +150,9 @@ files:
106
150
  - esi.gemspec
107
151
  - lib/esi.rb
108
152
  - lib/esi/access_token.rb
153
+ - lib/esi/api_error.rb
109
154
  - lib/esi/calls.rb
155
+ - lib/esi/calls/info.rb
110
156
  - lib/esi/client.rb
111
157
  - lib/esi/o_auth.rb
112
158
  - lib/esi/response.rb