|
# https://target.my.com/doc/api/oauth2 |
|
class MyTarget::Api::ApiBase |
|
extend DslAttribute |
|
|
|
TIMEOUT = 120 |
|
OPEN_TIMEOUT = 30 |
|
|
|
dsl_attribute :api_version, :v1 |
|
dsl_attribute :mutex_with_ids, false |
|
|
|
REQUEST_MUTEX_EXPIRE = (TIMEOUT + OPEN_TIMEOUT).seconds |
|
MUTEX_OPTIONS = { |
|
block: 30.seconds, |
|
expire: REQUEST_MUTEX_EXPIRE |
|
} |
|
|
|
def api_request method, path, params = {} |
|
status, data = RedisMutex.with_lock(redis_key(path), MUTEX_OPTIONS) do |
|
MyTarget::Api::Logger.instance.info(self, method, path, params) do |
|
perform_request method, path, params |
|
end |
|
end |
|
|
|
return data if status == 200 |
|
raise ApiError.new("#{status} #{data.to_json}", data) |
|
end |
|
|
|
def access_token |
|
return existing_token if existing_token |
|
token_data = MyTarget::Api::Tokens.call(token_username) |
|
cache_token token_data |
|
token_data[:access_token] |
|
end |
|
|
|
private |
|
|
|
def perform_request method, path, params, attempt = 0 |
|
response = send "#{method}_request", path_to_url(path), params |
|
|
|
# ApiError: 401 "Unknown access token" |
|
if response.status == 401 && attempt.zero? |
|
expire_token |
|
perform_request method, path, params, attempt + 1 |
|
else |
|
[ |
|
response.status, |
|
JSON.parse(response.body, symbolize_names: true, quirks_mode: true) |
|
] |
|
end |
|
end |
|
|
|
def get_request url, params |
|
Faraday.get(url, params) do |request| |
|
request.headers['Authorization'] = "Bearer #{access_token}" |
|
request.options.timeout = TIMEOUT |
|
request.options.open_timeout = OPEN_TIMEOUT |
|
end |
|
end |
|
|
|
def post_request url, params |
|
Faraday.post(url) do |request| |
|
request.headers['Authorization'] = "Bearer #{access_token}" |
|
request.body = params.to_json |
|
request.options.timeout = TIMEOUT |
|
request.options.open_timeout = OPEN_TIMEOUT |
|
end |
|
end |
|
|
|
def file_request url, params |
|
conn = Faraday.new do |f| |
|
f.request :multipart |
|
f.request :url_encoded |
|
f.adapter Faraday.default_adapter |
|
end |
|
|
|
file_key = params.keys.find { |v| v.to_s =~ /(\b|_)file(\b|_)/ } |
|
file_field = params[file_key] |
|
# NOTE раскоментировать этот вариант при выполнении таска rake entity:my_target |
|
# params[file_key] = Faraday::UploadIO.new params[file_key], 'image/jpg' |
|
|
|
S3AttachmentProxy.new(file_field).proxify do |file| |
|
params[file_key] = Faraday::UploadIO.new( |
|
file.path, params[file_key].content_type |
|
) |
|
|
|
conn.post(url, params) do |request| |
|
request.headers['Authorization'] = "Bearer #{access_token}" |
|
request.options.timeout = TIMEOUT |
|
request.options.open_timeout = OPEN_TIMEOUT |
|
end |
|
end |
|
|
|
ensure |
|
# нужно восстанавливать оригинальное значение, запрос может пойти повторно |
|
params[file_key] = file_field |
|
end |
|
|
|
def credentials |
|
Rails.application.secrets.oauth[:my_target] |
|
end |
|
|
|
def path_to_url path |
|
credentials[:"api_#{api_version}_url"] + path + '.json' |
|
end |
|
|
|
def token_username |
|
MyTarget::Api::Tokens::MAIN_USERNAME |
|
end |
|
|
|
def token_cache_key |
|
[:api_token, :my_target, :access_token] |
|
end |
|
|
|
def existing_token |
|
Rails.cache.read token_cache_key |
|
end |
|
|
|
def expire_token |
|
Rails.cache.delete token_cache_key |
|
end |
|
|
|
def cache_token token_data |
|
Rails.cache.write( |
|
token_cache_key, |
|
token_data[:access_token], |
|
expires_in: token_data[:expires_in].seconds - 1.minute |
|
) |
|
token_data[:access_token] |
|
end |
|
|
|
def redis_key path |
|
path_key = mutex_with_ids ? path : path.gsub(/\d+/, 'ID') |
|
[:api, :my_target, @agency_client_name, path_key].select(&:present?).to_json |
|
end |
|
end |