esi 0.1.2
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +59 -0
- data/esi.gemspec +26 -0
- data/lib/esi.rb +120 -0
- data/lib/esi/access_token.rb +17 -0
- data/lib/esi/calls.rb +339 -0
- data/lib/esi/client.rb +121 -0
- data/lib/esi/o_auth.rb +54 -0
- data/lib/esi/response.rb +53 -0
- data/lib/esi/version.rb +3 -0
- data/lib/omniauth/esi.rb +1 -0
- data/lib/omniauth/strategies/esi.rb +56 -0
- metadata +140 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b5d0ba8745382e4dc76d80883f103938d1db197f
|
4
|
+
data.tar.gz: 86b2d0155c58f106b2115ae636cd9dcf152b667d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 495ca804fd67f4e55a327a9e6990613ab9cb36682d921c8913413c2e2ff8b4b161bac61e4536441817a75cb94f6740dba3b2059f7cde0fac8b970f83dd554d48
|
7
|
+
data.tar.gz: 3117e5bf411a9808f9fcbc621df104e79d2c9527e567975723c7500b0ec3d96a2b3717ceaa33e8b2dc7c0b854d51b92f02d5034723009918ebcf2d931aaa2e54
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Danny Hiemstra
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Esi
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/esi`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'esi'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install esi
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
esi = Esi::Client.new(token: 'YOUR_TOKEN', refresh_token: 'REFRESH_TOKEN', token_expires_at: EXPIRE_TIMESTAMP)
|
26
|
+
esi.character()
|
27
|
+
|
28
|
+
### You can optionally specify a callback that will be executed after a new token has been received using the refresh token
|
29
|
+
|
30
|
+
esi.refresh_callback = ~> (token, expires_at) {
|
31
|
+
# save new token & expires at
|
32
|
+
}
|
33
|
+
|
34
|
+
## Authentication
|
35
|
+
|
36
|
+
Register your application on [https://developers.eveonline.com/applications].
|
37
|
+
Add your client id and secret in an initializer or so.
|
38
|
+
|
39
|
+
Esi.config.client_id = 'APP_CLIENT_ID'
|
40
|
+
Esi.config.client_secret = 'APP_CLIENT_SECRET'
|
41
|
+
|
42
|
+
## Configuration
|
43
|
+
|
44
|
+
Create a file `config/initializers/esi.rb` with the following options:
|
45
|
+
|
46
|
+
# Specify a custom log level
|
47
|
+
Esi.config.log_level = :debug
|
48
|
+
|
49
|
+
# Specify a custom log path
|
50
|
+
Esi.config.log_target = Rails.root.join('log', 'esi.log')
|
51
|
+
|
52
|
+
# Specify a custom logger
|
53
|
+
Esi.config.logger = Rails.logger
|
54
|
+
|
55
|
+
# Set esi api version to dev
|
56
|
+
Esi.config.api_version = :dev
|
57
|
+
|
58
|
+
# Save all responses in this folder
|
59
|
+
Esi.config.response_log_path = Rails.root.join('tmp', 'esi')
|
data/esi.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'esi/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "esi"
|
8
|
+
spec.version = Esi::VERSION
|
9
|
+
spec.authors = ["Danny Hiemstra"]
|
10
|
+
spec.email = ["dannyhiemstra@gmail.com"]
|
11
|
+
|
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"
|
16
|
+
|
17
|
+
spec.files = %w(LICENSE.txt README.md esi.gemspec) + Dir['lib/**/*.rb']
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "oauth2", "~> 1.2"
|
21
|
+
spec.add_dependency "addressable", "~> 2.3"
|
22
|
+
spec.add_dependency "recursive-open-struct", "~> 1"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
26
|
+
end
|
data/lib/esi.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require "oauth2"
|
2
|
+
require "forwardable"
|
3
|
+
require "ostruct"
|
4
|
+
require "addressable/uri"
|
5
|
+
|
6
|
+
module Esi
|
7
|
+
autoload :Version, 'esi/version'
|
8
|
+
autoload :AccessToken, 'esi/access_token'
|
9
|
+
autoload :OAuth, 'esi/o_auth'
|
10
|
+
autoload :Calls, 'esi/calls'
|
11
|
+
autoload :Client, 'esi/client'
|
12
|
+
autoload :Response, 'esi/response'
|
13
|
+
|
14
|
+
SCOPES = %w(
|
15
|
+
esi-calendar.respond_calendar_events.v1
|
16
|
+
esi-calendar.read_calendar_events.v1
|
17
|
+
esi-location.read_location.v1
|
18
|
+
esi-location.read_ship_type.v1
|
19
|
+
esi-mail.organize_mail.v1
|
20
|
+
esi-mail.read_mail.v1
|
21
|
+
esi-mail.send_mail.v1
|
22
|
+
esi-skills.read_skills.v1
|
23
|
+
esi-skills.read_skillqueue.v1
|
24
|
+
esi-wallet.read_character_wallet.v1
|
25
|
+
esi-search.search_structures.v1
|
26
|
+
esi-clones.read_clones.v1
|
27
|
+
esi-characters.read_contacts.v1
|
28
|
+
esi-universe.read_structures.v1
|
29
|
+
esi-bookmarks.read_character_bookmarks.v1
|
30
|
+
esi-killmails.read_killmails.v1
|
31
|
+
esi-corporations.read_corporation_membership.v1
|
32
|
+
esi-assets.read_assets.v1
|
33
|
+
esi-planets.manage_planets.v1
|
34
|
+
esi-fleets.read_fleet.v1
|
35
|
+
esi-fleets.write_fleet.v1
|
36
|
+
esi-ui.open_window.v1
|
37
|
+
esi-ui.write_waypoint.v1
|
38
|
+
esi-characters.write_contacts.v1
|
39
|
+
esi-fittings.read_fittings.v1
|
40
|
+
esi-fittings.write_fittings.v1
|
41
|
+
esi-markets.structure_markets.v1
|
42
|
+
esi-corporations.read_structures.v1
|
43
|
+
esi-corporations.write_structures.v1
|
44
|
+
esi-characters.read_loyalty.v1
|
45
|
+
esi-characters.read_opportunities.v1
|
46
|
+
esi-characters.read_chat_channels.v1
|
47
|
+
esi-characters.read_medals.v1
|
48
|
+
esi-characters.read_standings.v1
|
49
|
+
esi-characters.read_agents_research.v1
|
50
|
+
esi-industry.read_character_jobs.v1
|
51
|
+
esi-markets.read_character_orders.v1
|
52
|
+
esi-characters.read_blueprints.v1
|
53
|
+
esi-characters.read_corporation_roles.v1
|
54
|
+
esi-location.read_online.v1
|
55
|
+
esi-contracts.read_character_contracts.v1
|
56
|
+
)
|
57
|
+
DEFAULT_CONFIG = {
|
58
|
+
datasource: :tranquility,
|
59
|
+
oauth_host: 'https://login.eveonline.com',
|
60
|
+
api_host: 'https://esi.tech.ccp.is',
|
61
|
+
api_version: :latest,
|
62
|
+
log_level: :info,
|
63
|
+
log_target: STDOUT,
|
64
|
+
response_log_path: nil,
|
65
|
+
client_id: nil,
|
66
|
+
client_secret: nil,
|
67
|
+
scopes: SCOPES
|
68
|
+
}
|
69
|
+
|
70
|
+
class << self
|
71
|
+
attr_writer :api_version, :logger
|
72
|
+
|
73
|
+
def config
|
74
|
+
@config ||= OpenStruct.new(DEFAULT_CONFIG)
|
75
|
+
end
|
76
|
+
|
77
|
+
def logger
|
78
|
+
@logger ||= Esi.config.logger || Logger.new(Esi.config.log_target).tap do |l|
|
79
|
+
l.level = Logger.const_get(Esi.config.log_level.upcase)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def api_version
|
84
|
+
@api_version || :latest
|
85
|
+
end
|
86
|
+
|
87
|
+
def generate_url(path, params={})
|
88
|
+
path = path[1..-1] if path.start_with?('/')
|
89
|
+
path += "/" unless path.end_with?('/')
|
90
|
+
|
91
|
+
url = [config.api_host, config.api_version, path].join('/')
|
92
|
+
uri = Addressable::URI.parse(url)
|
93
|
+
uri.query_values = {datasource: config.datasource}.merge(params.to_h)
|
94
|
+
uri.to_s
|
95
|
+
end
|
96
|
+
|
97
|
+
def client
|
98
|
+
@client ||= Client.new
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class ApiError < OAuth2::Error
|
103
|
+
attr_reader :key, :message, :type
|
104
|
+
|
105
|
+
def initialize(response)
|
106
|
+
super(response.original_response)
|
107
|
+
|
108
|
+
@code = response.original_response.status
|
109
|
+
@key = response.data[:key]
|
110
|
+
@message = response.data[:message].presence || response.data[:error]
|
111
|
+
@type = response.data[:exceptionType]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class ApiUnknownError < ApiError; end
|
116
|
+
class ApiInvalidAppClientKeysError < ApiError; end
|
117
|
+
class ApiNotFoundError < ApiError; end
|
118
|
+
class ApiForbiddenError < ApiError; end
|
119
|
+
class Error < StandardError; end
|
120
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Esi
|
2
|
+
class AccessToken < OAuth2::AccessToken
|
3
|
+
def initialize(*args)
|
4
|
+
if args[0].is_a?(OAuth2::AccessToken)
|
5
|
+
token = args[0]
|
6
|
+
options = { refresh_token: token.refresh_token, expires_at: token.expires_at }
|
7
|
+
super(token.client, token.token, options)
|
8
|
+
else
|
9
|
+
super(*args)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def verify
|
14
|
+
Esi::Response.new(get("/oauth/verify"))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/esi/calls.rb
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
module Esi
|
2
|
+
class Calls
|
3
|
+
class Base
|
4
|
+
attr_accessor :path, :params
|
5
|
+
|
6
|
+
def method
|
7
|
+
@method ||= :get
|
8
|
+
end
|
9
|
+
|
10
|
+
def url
|
11
|
+
Esi.generate_url(path, params)
|
12
|
+
end
|
13
|
+
|
14
|
+
def page=(page)
|
15
|
+
self.params ||= {}
|
16
|
+
self.params[:page] = page
|
17
|
+
end
|
18
|
+
|
19
|
+
def paginated?
|
20
|
+
!!@paginated
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class OpenMarketDetails < Base
|
25
|
+
def initialize(type_id)
|
26
|
+
@path = "/ui/openwindow/marketdetails"
|
27
|
+
@method = :post
|
28
|
+
@params = { type_id: type_id }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SolarSystem < Base
|
33
|
+
def initialize(system_id)
|
34
|
+
@path = "/universe/systems/#{system_id}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Types < Base
|
39
|
+
def initialize
|
40
|
+
@path = "/universe/types"
|
41
|
+
@paginated = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Type < Base
|
46
|
+
def initialize(type_id)
|
47
|
+
@path = "/universe/types/#{type_id}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Search < Base
|
52
|
+
def initialize(character_id: nil, categories:, search:, strict: false)
|
53
|
+
@path = (character_id ? "/characters/#{character_id}" : '') + "/search"
|
54
|
+
@params = { categories: categories, search: search, strict: strict }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Characters < Base
|
59
|
+
def initialize(character_ids)
|
60
|
+
@path = "/characters/names"
|
61
|
+
@params = { character_ids: character_ids.join(',') }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class CharacterNames < Base
|
66
|
+
def initialize(character_ids)
|
67
|
+
@path = "/characters/names"
|
68
|
+
@params = { character_ids: character_ids.join(',') }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Link: https://esi.tech.ccp.is/latest/#!/Character/get_characters_character_id
|
73
|
+
# Cache: 1 hour
|
74
|
+
class Character < Base
|
75
|
+
def initialize(character_id)
|
76
|
+
@path = "/characters/#{character_id}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Wallet/get_characters_character_id_wallet
|
81
|
+
# Scope: esi-wallet.read_character_wallet.v1
|
82
|
+
# Cache: 2 minutes
|
83
|
+
class CharacterWallet < Base
|
84
|
+
def initialize(character_id)
|
85
|
+
@path = "/characters/#{character_id}/wallet"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Wallet/get_characters_character_id_wallet_journal
|
90
|
+
# Scope: esi-wallet.read_character_wallet.v1
|
91
|
+
# Cache: 1 hour
|
92
|
+
class CharacterWalletJournal < Base
|
93
|
+
def initialize(character_id)
|
94
|
+
@path = "/characters/#{character_id}/wallet/journal"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Wallet/get_characters_character_id_wallet_transactions
|
99
|
+
# Scope: esi-wallet.read_character_wallet.v1
|
100
|
+
# Cache: 1 hour
|
101
|
+
class CharacterWalletTransactions < Base
|
102
|
+
def initialize(character_id)
|
103
|
+
@path = "/characters/#{character_id}/wallet/transactions"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Link: https://esi.tech.ccp.is/latest/#!/Market/get_characters_character_id_orders
|
108
|
+
# Scope: esi-markets.read_character_orders.v1
|
109
|
+
# Cache: 1 hour
|
110
|
+
class CharacterOrders < Base
|
111
|
+
def initialize(character_id)
|
112
|
+
@path = "/characters/#{character_id}/orders"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Link: https://esi.tech.ccp.is/latest/#!/Industry/get_characters_character_id_industry_jobs
|
117
|
+
# Scope: esi-industry.read_character_jobs.v1
|
118
|
+
# Cache: 5 minutes
|
119
|
+
class CharacterIndustryJobs < Base
|
120
|
+
def initialize(character_id, with_completed: false)
|
121
|
+
@path = "/characters/#{character_id}/industry/jobs"
|
122
|
+
@params = { with_completed: with_completed }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Link: https://esi.tech.ccp.is/dev/?datasource=tranquility#!/Contacts/get_characters_character_id_contacts
|
127
|
+
# Scope: esi-contracts.read_character_contracts.v1
|
128
|
+
# Cache: 1 hour
|
129
|
+
class CharacterContracts < Base
|
130
|
+
def initialize(character_id)
|
131
|
+
@path = "/characters/#{character_id}/contracts"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Location/get_characters_character_id_location
|
136
|
+
# Scope: esi-location.read_location.v1
|
137
|
+
# Cache: 1 hour
|
138
|
+
class CharacterLocation < Base
|
139
|
+
def initialize(character_id)
|
140
|
+
@path = "/characters/#{character_id}/location"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Contracts/get_characters_character_id_contracts_contract_id_items
|
145
|
+
# Scope: esi-contracts.read_character_contracts.v1
|
146
|
+
# Cache: 1 hour
|
147
|
+
class ContractItems < Base
|
148
|
+
def initialize(character_id, contract_id)
|
149
|
+
@path = "/characters/#{character_id}/contracts/#{contract_id}/items"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Location/get_characters_character_id_location
|
154
|
+
# Scope: esi-location.read_location.v1
|
155
|
+
# Cache: 5 seconds
|
156
|
+
class CharacterLocation < Base
|
157
|
+
def initialize(character_id)
|
158
|
+
@path = "/characters/#{character_id}/location"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Assets/get_characters_character_id_assets
|
163
|
+
# Scope: esi-assets.read_assets.v1
|
164
|
+
# Cache: 1 hour
|
165
|
+
class Assets < Base
|
166
|
+
def initialize(character_id)
|
167
|
+
@path = "/characters/#{character_id}/assets"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class Alliances < Base
|
172
|
+
def initialize
|
173
|
+
@path = "/alliances"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class AllianceNames < Base
|
178
|
+
def initialize(alliance_ids)
|
179
|
+
@path = "/alliances/names"
|
180
|
+
@params = { alliance_ids: alliance_ids.join(',') }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class Alliance < Base
|
185
|
+
def initialize(alliance_id)
|
186
|
+
@path = "/alliances/#{alliance_id}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class CorporationNames < Base
|
191
|
+
def initialize(corporation_ids)
|
192
|
+
@path = "/corporations/names"
|
193
|
+
@params = { corporation_ids: corporation_ids.join(',') }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class Corporation < Base
|
198
|
+
def initialize(corporation_id)
|
199
|
+
@path = "/corporations/#{corporation_id}"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class CorporationStructures < Base
|
204
|
+
def initialize(corporation_id)
|
205
|
+
@path = "/corporations/#{corporation_id}/structures"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class CorporationStructure < Base
|
210
|
+
def initialize(corporation_id, structure_id)
|
211
|
+
@path = "/corporations/#{corporation_id}/structures/#{structure_id}"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Corporation/get_corporations_corporation_id_members
|
216
|
+
# Scope: esi-corporations.read_corporation_membership.v1
|
217
|
+
# Cache: 1 hour
|
218
|
+
class CorporationMembers < Base
|
219
|
+
def initialize(corporation_id)
|
220
|
+
@path = "/corporations/#{corporation_id}/members"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Link: https://esi.tech.ccp.is/dev/#!/Corporation/get_corporations_corporation_id_roles
|
225
|
+
# Scope: esi-corporations.read_corporation_membership.v1
|
226
|
+
# Cache: 1 hour
|
227
|
+
class CorporationRoles < Base
|
228
|
+
def initialize(corporation_id)
|
229
|
+
@path = "/corporations/#{corporation_id}/roles"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
class Structures < Base
|
234
|
+
def initialize
|
235
|
+
@path = "/universe/structures"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class Structure < Base
|
240
|
+
def initialize(structure_id)
|
241
|
+
@path = "/universe/structures/#{structure_id}"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
class Route < Base
|
246
|
+
def initialize(origin_id, destination_id)
|
247
|
+
@path = "/route/#{origin_id}/#{destination_id}"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
class MarketGroups < Base
|
252
|
+
def initialize
|
253
|
+
@path = "/markets/groups"
|
254
|
+
@paginated = true
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
class MarketGroup < Base
|
259
|
+
def initialize(id)
|
260
|
+
@path = "/markets/groups/#{id}"
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class MarketPrices < Base
|
265
|
+
def initialize
|
266
|
+
@path = "/markets/prices"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
class MarketOrders < Base
|
271
|
+
def initialize(type_id: nil, structure_id: nil, region_id: Region::FORGE)
|
272
|
+
@path = "/markets/structures/#{structure_id}"
|
273
|
+
@path = "/markets/#{region_id}/orders"
|
274
|
+
@params = { order_type: :all }
|
275
|
+
@params[:type_id] = type_id if type_id.present?
|
276
|
+
@paginated = type_id.blank?
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
class StructureOrders < Base
|
281
|
+
def initialize(structure_id:)
|
282
|
+
@path = "/markets/structures/#{structure_id}"
|
283
|
+
@paginated = true
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
class MarketHistory < Base
|
288
|
+
def initialize(type_id:, region_id: Region::FORGE)
|
289
|
+
@path = "/markets/#{region_id}/history"
|
290
|
+
@params = { type_id: type_id }
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
class Killmails < Base
|
295
|
+
def initialize(character_id:, max_count: 50, max_kill_id: nil)
|
296
|
+
@path = "/characters/#{character_id}/killmails/recent"
|
297
|
+
@params = { max_count: max_count }
|
298
|
+
@params[:max_kill_id] = max_kill_id if max_kill_id
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
class Killmail < Base
|
303
|
+
def initialize(id:, hash:)
|
304
|
+
@path = "killmails/#{id}/#{hash}"
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
class Fleet < Base
|
309
|
+
def initialize(fleet_id)
|
310
|
+
@path = "fleets/#{fleet_id}"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
class FleetMembers < Base
|
315
|
+
def initialize(fleet_id)
|
316
|
+
@path = "fleets/#{fleet_id}/members"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
class FleetWings < Base
|
321
|
+
def initialize(fleet_id)
|
322
|
+
@path = "fleets/#{fleet_id}/wings"
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
class Fittings < Base
|
327
|
+
def initialize(character_id)
|
328
|
+
@path = "characters/#{character_id}/fittings"
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
class DeleteFitting < Base
|
333
|
+
def initialize(character_id, fitting_id)
|
334
|
+
@path = "characters/#{character_id}/fittings/#{fitting_id}"
|
335
|
+
@method = :delete
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
data/lib/esi/client.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
module Esi
|
2
|
+
class Client
|
3
|
+
MAX_ATTEMPTS = 5
|
4
|
+
|
5
|
+
attr_accessor :refresh_callback, :access_token, :refresh_token, :expires_at
|
6
|
+
attr_reader :logger, :oauth
|
7
|
+
|
8
|
+
def initialize(token: nil, refresh_token: nil, expires_at: nil)
|
9
|
+
@logger = Esi.logger
|
10
|
+
@token = token
|
11
|
+
@refresh_token = refresh_token
|
12
|
+
@expires_at = expires_at
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *args, &block)
|
16
|
+
class_name = name.to_s.split('_').map(&:capitalize).join
|
17
|
+
begin
|
18
|
+
klass = Esi::Calls.const_get(class_name)
|
19
|
+
rescue NameError
|
20
|
+
super(name, *args, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
call = klass.new(*args)
|
24
|
+
call.paginated? ? request_paginated(call, &block) : request(call, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def log(message)
|
28
|
+
logger.info message
|
29
|
+
end
|
30
|
+
|
31
|
+
def debug(message)
|
32
|
+
logger.debug message
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def oauth
|
38
|
+
@oauth ||= OAuth.new(
|
39
|
+
access_token: @token,
|
40
|
+
refresh_token: @refresh_token,
|
41
|
+
expires_at: @expires_at,
|
42
|
+
callback: -> (token, expires_at) {
|
43
|
+
@token = token
|
44
|
+
@expires_at = expires_at
|
45
|
+
if refresh_callback.respond_to?(:call)
|
46
|
+
refresh_callback.call(token, expires_at)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def request_paginated(call, &block)
|
53
|
+
items = []
|
54
|
+
page = 1
|
55
|
+
|
56
|
+
loop do
|
57
|
+
call.page = page
|
58
|
+
response = request(call, &block)
|
59
|
+
break if response.data.blank?
|
60
|
+
items += response.data
|
61
|
+
page += 1
|
62
|
+
end
|
63
|
+
|
64
|
+
items
|
65
|
+
end
|
66
|
+
|
67
|
+
def request(call, url=nil, &block)
|
68
|
+
response = nil
|
69
|
+
url ||= call.url
|
70
|
+
|
71
|
+
debug "Starting request: #{url}"
|
72
|
+
|
73
|
+
1.upto(MAX_ATTEMPTS) do |try|
|
74
|
+
begin
|
75
|
+
response = oauth.request(call.method, url)
|
76
|
+
rescue OAuth2::Error => e
|
77
|
+
case e.response.status
|
78
|
+
when 502 # Temporary server error
|
79
|
+
logger.error "TemporaryServerError, sleeping for 15 seconds"
|
80
|
+
sleep 15
|
81
|
+
next
|
82
|
+
when 503 # Rate Limit
|
83
|
+
logger.error "RateLimit error, sleeping for 15 seconds"
|
84
|
+
sleep 15
|
85
|
+
next
|
86
|
+
when 403 # Forbidden
|
87
|
+
logger.error "ApiForbiddenError: #{e.response.status}: #{e.response.body}"
|
88
|
+
logger.error url
|
89
|
+
raise Esi::ApiForbiddenError.new(Response.new(e.response))
|
90
|
+
when 404 # Not Found
|
91
|
+
raise Esi::ApiNotFoundError.new(Response.new(e.response))
|
92
|
+
else
|
93
|
+
response = Response.new(e.response)
|
94
|
+
logger.error "ApiUnknownError (#{response.status}): #{url}"
|
95
|
+
|
96
|
+
case response.error
|
97
|
+
when 'invalid_client' then
|
98
|
+
raise ApiInvalidAppClientKeysError.new(response)
|
99
|
+
else
|
100
|
+
raise Esi::ApiUnknownError.new(response)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
break if response
|
105
|
+
end
|
106
|
+
|
107
|
+
debug "Request finished"
|
108
|
+
response = Response.new(response)
|
109
|
+
|
110
|
+
if Esi.config.response_log_path && Dir.exists?(Esi.config.response_log_path)
|
111
|
+
call_name = call.class.to_s.downcase.split('::').last
|
112
|
+
folder = Pathname.new(Esi.config.response_log_path).join(call_name)
|
113
|
+
FileUtils.mkdir_p(folder)
|
114
|
+
File.write(folder.join("#{Time.now.to_i.to_s}.json"), response.to_s)
|
115
|
+
end
|
116
|
+
|
117
|
+
response.data.each { |item| block.call(item) } if block
|
118
|
+
response
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/lib/esi/o_auth.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Esi
|
2
|
+
class OAuth
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_reader :access_token, :refresh_token, :expires_at
|
6
|
+
def_delegators :token, :request, :get, :post, :delete, :patch, :put
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def authorize_url(redirect_uri:, scopes: nil)
|
10
|
+
scopes ||= Esi.config.scopes
|
11
|
+
client.auth_code.authorize_url(scope: scopes.join(' '), redirect_uri: redirect_uri)
|
12
|
+
end
|
13
|
+
|
14
|
+
def obtain_token(code)
|
15
|
+
Esi::AccessToken.new(client.auth_code.get_token(code))
|
16
|
+
end
|
17
|
+
|
18
|
+
def client
|
19
|
+
@client ||= OAuth2::Client.new(
|
20
|
+
Esi.config.client_id, Esi.config.client_secret,
|
21
|
+
{ site: Esi.config.oauth_host }
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(access_token:, refresh_token:, expires_at:, callback: nil)
|
27
|
+
if callback && !callback.respond_to?(:call)
|
28
|
+
raise Esi::Error.new("Callback should be a callable Proc")
|
29
|
+
end
|
30
|
+
|
31
|
+
@access_token = access_token
|
32
|
+
@refresh_token = refresh_token
|
33
|
+
@expires_at = expires_at
|
34
|
+
@callback = callback if callback
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def token
|
40
|
+
@token = Esi::AccessToken.new(OAuth.client, @access_token, {
|
41
|
+
refresh_token: @refresh_token, expires_at: @expires_at
|
42
|
+
})
|
43
|
+
refresh_access_token if @token.expired?
|
44
|
+
@token
|
45
|
+
end
|
46
|
+
|
47
|
+
def refresh_access_token
|
48
|
+
@token = @token.refresh!
|
49
|
+
@access_token = @token.token
|
50
|
+
@expires_at = @token.expires_at.integer? ? Time.at(@token.expires_at) : @token.expires_at
|
51
|
+
@callback.call(@access_token, @expires_at) if @callback
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/esi/response.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'recursive-open-struct'
|
2
|
+
|
3
|
+
module Esi
|
4
|
+
class Response
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :original_response, :json
|
8
|
+
def_delegators :original_response, :status, :body, :headers
|
9
|
+
|
10
|
+
def initialize(response)
|
11
|
+
@original_response = response
|
12
|
+
@json = MultiJson.load(body, symbolize_keys: true) rescue {}
|
13
|
+
@json = normalize_keys(@json) if @json.is_a?(Hash)
|
14
|
+
end
|
15
|
+
|
16
|
+
def data
|
17
|
+
@data ||= begin
|
18
|
+
if @json.is_a?(Array)
|
19
|
+
@json[0].is_a?(Hash) ? @json.map { |e| RecursiveOpenStruct.new(e, recurse_over_arrays: true) } : @json
|
20
|
+
else
|
21
|
+
RecursiveOpenStruct.new(@json, recurse_over_arrays: true)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
MultiJson.dump(json, pretty: true)
|
28
|
+
end
|
29
|
+
|
30
|
+
def cached_until
|
31
|
+
original_response.headers[:expires] ? Time.parse(original_response.headers[:expires]) : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(method, *args, &block)
|
35
|
+
begin
|
36
|
+
data.send(method, *args, &block)
|
37
|
+
rescue NameError
|
38
|
+
super(name, *args, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def normalize_keys(data)
|
45
|
+
data.dup.each_key { |k| data[underscore(k).to_sym] = data.delete(k) }
|
46
|
+
data
|
47
|
+
end
|
48
|
+
|
49
|
+
def underscore(str)
|
50
|
+
str.to_s.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/esi/version.rb
ADDED
data/lib/omniauth/esi.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'omniauth/strategies/esi'
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'omniauth/strategies/oauth2'
|
2
|
+
|
3
|
+
module OmniAuth
|
4
|
+
module Strategies
|
5
|
+
class Esi < OmniAuth::Strategies::OAuth2
|
6
|
+
option :name, 'esi'
|
7
|
+
option :client_options, { site: ::Esi.config.oauth_host, verify_url: '/oauth/verify' }
|
8
|
+
option :authorize_options, [:scope]
|
9
|
+
|
10
|
+
uid { extra_info[:character_id] }
|
11
|
+
|
12
|
+
info do
|
13
|
+
{
|
14
|
+
character_id: extra_info[:character_id],
|
15
|
+
character_name: extra_info[:character_name],
|
16
|
+
character_owner_hash: extra_info[:character_owner_hash],
|
17
|
+
token_type: raw_info[:token_type]
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
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]
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
|
30
|
+
def raw_info
|
31
|
+
@raw_info ||= deep_symbolize(access_token.params)
|
32
|
+
end
|
33
|
+
|
34
|
+
def extra_info
|
35
|
+
@extra_info ||= deep_symbolize(access_token.get(options.client_options.verify_url).parsed.transform_keys!(&:underscore))
|
36
|
+
end
|
37
|
+
|
38
|
+
def authorize_params
|
39
|
+
params = super
|
40
|
+
params = params.merge(request.params) unless OmniAuth.config.test_mode
|
41
|
+
params[:scope] = params[:scope].join(' ') if params[:scope].is_a?(Array)
|
42
|
+
params[:redirect_uri] = options[:callback_url] unless options[:callback_url].nil?
|
43
|
+
params
|
44
|
+
end
|
45
|
+
|
46
|
+
def request_phase
|
47
|
+
redirect client.auth_code.authorize_url(authorize_params)
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_access_token
|
51
|
+
verifier = request.params['code']
|
52
|
+
client.auth_code.get_token(verifier, token_params)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: esi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Danny Hiemstra
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: oauth2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: addressable
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: recursive-open-struct
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.14'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.14'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '10.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '10.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '5.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '5.0'
|
97
|
+
description: EVE ESI API wrapper
|
98
|
+
email:
|
99
|
+
- dannyhiemstra@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- LICENSE.txt
|
105
|
+
- README.md
|
106
|
+
- esi.gemspec
|
107
|
+
- lib/esi.rb
|
108
|
+
- lib/esi/access_token.rb
|
109
|
+
- lib/esi/calls.rb
|
110
|
+
- lib/esi/client.rb
|
111
|
+
- lib/esi/o_auth.rb
|
112
|
+
- lib/esi/response.rb
|
113
|
+
- lib/esi/version.rb
|
114
|
+
- lib/omniauth/esi.rb
|
115
|
+
- lib/omniauth/strategies/esi.rb
|
116
|
+
homepage: https://github.com/dhiemstra/esi
|
117
|
+
licenses:
|
118
|
+
- MIT
|
119
|
+
metadata: {}
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 2.6.11
|
137
|
+
signing_key:
|
138
|
+
specification_version: 4
|
139
|
+
summary: EVE ESI API wrapper
|
140
|
+
test_files: []
|