yandex_client 0.1.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +30 -0
- data/.gitignore +2 -0
- data/.pryrc +6 -0
- data/.rubocop.yml +36 -0
- data/Gemfile +6 -3
- data/README.md +32 -83
- data/Rakefile +5 -3
- data/dip.yml +9 -8
- data/docker/Dockerfile.dip +8 -0
- data/docker/docker-compose.yml +16 -8
- data/docker/prepare_env.sh +11 -0
- data/lib/yandex_client/auth.rb +62 -0
- data/lib/yandex_client/configurable.rb +26 -0
- data/lib/yandex_client/dav/prop_find_response.rb +70 -0
- data/lib/yandex_client/dav.rb +104 -0
- data/lib/yandex_client/disk.rb +60 -0
- data/lib/yandex_client/error_handler.rb +20 -0
- data/lib/yandex_client/passport.rb +45 -0
- data/lib/yandex_client/version.rb +3 -1
- data/lib/yandex_client.rb +39 -18
- data/yandex_client.gemspec +9 -6
- metadata +34 -18
- data/.travis.yml +0 -12
- data/docker/Dockerfile.2.6 +0 -9
- data/docker/docker-compose.development.yml +0 -18
- data/lib/yandex_client/api_request_error.rb +0 -13
- data/lib/yandex_client/auth/client.rb +0 -55
- data/lib/yandex_client/client.rb +0 -97
- data/lib/yandex_client/dav/client.rb +0 -115
- data/lib/yandex_client/dav/propfind_parser.rb +0 -48
- data/lib/yandex_client/not_found_error.rb +0 -4
- data/lib/yandex_client/passport/client.rb +0 -34
data/.travis.yml
DELETED
data/docker/Dockerfile.2.6
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
module YandexClient
|
2
|
-
class ApiRequestError < StandardError
|
3
|
-
attr_reader :error, :error_description, :code
|
4
|
-
|
5
|
-
def initialize(error:, error_description:, code:)
|
6
|
-
@error = error
|
7
|
-
@error_description = error_description
|
8
|
-
@code = code
|
9
|
-
|
10
|
-
super [error, error_description, "http code #{@code}"].compact.join(', ')
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
|
3
|
-
module YandexClient
|
4
|
-
module Auth
|
5
|
-
# https://tech.yandex.ru/oauth/doc/dg/reference/refresh-client-docpage/
|
6
|
-
# https://tech.yandex.ru/oauth/doc/dg/reference/auto-code-client-docpage/#auto-code-client__get-token
|
7
|
-
#
|
8
|
-
# https://oauth.yandex.ru/authorize?response_type=code&client_id=99bcbd17ad7f411694710592d978a4a2&force_confirm=false
|
9
|
-
#
|
10
|
-
# Example:
|
11
|
-
# token = Token.first
|
12
|
-
#
|
13
|
-
# client = YandexClient::Auth::Client.new
|
14
|
-
# client.create_token(code: '9388894')
|
15
|
-
# client.refresh_token(refresh_token: token.refresh_token)
|
16
|
-
class Client < ::YandexClient::Client
|
17
|
-
AUTH_ACTION_URL = 'https://oauth.yandex.ru/token'.freeze
|
18
|
-
|
19
|
-
ACTIONS = {
|
20
|
-
create_token: 'authorization_code',
|
21
|
-
refresh_token: 'refresh_token'
|
22
|
-
}.freeze
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def http(_request_uri)
|
27
|
-
@http ||= super
|
28
|
-
end
|
29
|
-
|
30
|
-
def http_method_for_action
|
31
|
-
METHOD_POST
|
32
|
-
end
|
33
|
-
|
34
|
-
def request_body(params)
|
35
|
-
body_hash = {
|
36
|
-
grant_type: ACTIONS.fetch(@action),
|
37
|
-
client_id: ::YandexClient.config.api_key,
|
38
|
-
client_secret: ::YandexClient.config.api_secret
|
39
|
-
}.merge!(params)
|
40
|
-
|
41
|
-
URI.encode_www_form(body_hash)
|
42
|
-
end
|
43
|
-
|
44
|
-
def request_headers(_params)
|
45
|
-
{
|
46
|
-
'Content-type' => 'application/x-www-form-urlencoded'
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
def request_uri(_params)
|
51
|
-
@request_uri ||= URI.parse(AUTH_ACTION_URL)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/lib/yandex_client/client.rb
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
require 'oj'
|
2
|
-
require 'net/http'
|
3
|
-
|
4
|
-
module YandexClient
|
5
|
-
# Base api client
|
6
|
-
class Client
|
7
|
-
TIMEOUT = 10
|
8
|
-
METHOD_GET = 'Get'.freeze
|
9
|
-
METHOD_POST = 'Post'.freeze
|
10
|
-
|
11
|
-
ERRORS_BY_CODES = {
|
12
|
-
404 => NotFoundError
|
13
|
-
}.freeze
|
14
|
-
|
15
|
-
def method_missing(method_name, *args, &_)
|
16
|
-
return super unless self.class::ACTIONS.include?(method_name)
|
17
|
-
|
18
|
-
@action = method_name
|
19
|
-
result = nil
|
20
|
-
response = make_request(args.last.is_a?(Hash) ? args.last : {})
|
21
|
-
|
22
|
-
result = parse_response_body(response) unless response.body.nil? || response.body.empty?
|
23
|
-
process_errors(response, result) unless response.is_a?(Net::HTTPSuccess)
|
24
|
-
|
25
|
-
result
|
26
|
-
end
|
27
|
-
|
28
|
-
def respond_to_missing?(method_name, include_private = false)
|
29
|
-
self.class::ACTIONS.include?(method_name) || super
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def http(request_uri)
|
35
|
-
Net::HTTP.new(request_uri.host, request_uri.port).tap do |http|
|
36
|
-
http.use_ssl = true
|
37
|
-
http.read_timeout = TIMEOUT
|
38
|
-
http.continue_timeout = TIMEOUT
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def http_method_for_action
|
43
|
-
METHOD_GET
|
44
|
-
end
|
45
|
-
|
46
|
-
def log_api_request(request_uri, request, response)
|
47
|
-
return if (logger = YandexClient.config.logger).nil?
|
48
|
-
|
49
|
-
logger.info "#{request.method} #{request_uri} #{response.code} #{response.message}"
|
50
|
-
logger.info "request headers: #{Oj.dump(request.to_hash)}"
|
51
|
-
logger.info "response headers: #{Oj.dump(response.to_hash)}"
|
52
|
-
end
|
53
|
-
|
54
|
-
def make_request(params)
|
55
|
-
request_uri = request_uri(params)
|
56
|
-
@body = request_body(params)
|
57
|
-
|
58
|
-
request = Object.const_get("Net::HTTP::#{http_method_for_action}").new(
|
59
|
-
request_uri.request_uri,
|
60
|
-
request_headers(params)
|
61
|
-
)
|
62
|
-
|
63
|
-
request.body = @body
|
64
|
-
response = http(request_uri).start { |http| http.request(request) }
|
65
|
-
|
66
|
-
log_api_request(request_uri, request, response)
|
67
|
-
|
68
|
-
response
|
69
|
-
end
|
70
|
-
|
71
|
-
def parse_response_body(response)
|
72
|
-
Oj.load(response.body, symbol_keys: true)
|
73
|
-
end
|
74
|
-
|
75
|
-
def process_errors(response, result)
|
76
|
-
klass = ERRORS_BY_CODES.fetch(response.code.to_i, ApiRequestError)
|
77
|
-
|
78
|
-
raise klass.new(
|
79
|
-
error: result.is_a?(Hash) ? result&.fetch(:error) : result,
|
80
|
-
error_description: result.is_a?(Hash) ? result&.fetch(:error_description) : nil,
|
81
|
-
code: response.code.to_i
|
82
|
-
)
|
83
|
-
end
|
84
|
-
|
85
|
-
def request_body(_params)
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
def request_headers(_params)
|
90
|
-
{}
|
91
|
-
end
|
92
|
-
|
93
|
-
def request_uri(_params)
|
94
|
-
raise 'not implemented'
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
@@ -1,115 +0,0 @@
|
|
1
|
-
require 'digest'
|
2
|
-
|
3
|
-
module YandexClient
|
4
|
-
module Dav
|
5
|
-
# https://tech.yandex.ru/disk/doc/dg/reference/put-docpage/
|
6
|
-
#
|
7
|
-
# cli = YandexClient::Dav::Client.new(access_token: access_token)
|
8
|
-
#
|
9
|
-
# cli.put(file: '1.txt', name: '/a/b/c/1.txt')
|
10
|
-
# cli.delete(name: '/a/b/c/1.txt')
|
11
|
-
# cli.mkcol(name: '/a/b/c')
|
12
|
-
#
|
13
|
-
# cli.propfind(name: '/a/dip.yml', depth: 0)
|
14
|
-
# cli.propfind(name: '/a', depth: 1)
|
15
|
-
# cli.propfind(name: '/', quota: true)
|
16
|
-
class Client < ::YandexClient::Client
|
17
|
-
ACTION_URL = 'https://webdav.yandex.ru'.freeze
|
18
|
-
PROPFIND_QUERY = '<?xml version="1.0" encoding="utf-8"?><propfind xmlns="DAV:"></propfind>'.freeze
|
19
|
-
PROPFIND_QUOTA_QUERY = <<~XML.freeze
|
20
|
-
<D:propfind xmlns:D="DAV:">
|
21
|
-
<D:prop>
|
22
|
-
<D:quota-available-bytes/>
|
23
|
-
<D:quota-used-bytes/>
|
24
|
-
</D:prop>
|
25
|
-
</D:propfind>
|
26
|
-
XML
|
27
|
-
|
28
|
-
# actions with header processors
|
29
|
-
ACTIONS = {
|
30
|
-
mkcol: nil,
|
31
|
-
delete: nil,
|
32
|
-
|
33
|
-
put: lambda do |params|
|
34
|
-
filename = params.fetch(:file)
|
35
|
-
|
36
|
-
etag = params[:etag] || Digest::MD5.file(filename)
|
37
|
-
sha256 = params[:sha256] || Digest::SHA256.file(filename)
|
38
|
-
size = params[:size] || File.size(filename)
|
39
|
-
|
40
|
-
{
|
41
|
-
Etag: etag.to_s,
|
42
|
-
Sha256: sha256.to_s,
|
43
|
-
Expect: '100-continue',
|
44
|
-
'Content-Length' => size.to_s
|
45
|
-
}
|
46
|
-
end,
|
47
|
-
|
48
|
-
propfind: lambda do |params|
|
49
|
-
depth = 0
|
50
|
-
depth = params.fetch(:depth, 0) unless params.key?(:quota)
|
51
|
-
|
52
|
-
headers = {
|
53
|
-
Depth: depth.to_s,
|
54
|
-
'Content-Type' => 'application/x-www-form-urlencoded'
|
55
|
-
}
|
56
|
-
|
57
|
-
headers['Content-Length'] = @body.length unless @body.nil? || @body.empty?
|
58
|
-
headers
|
59
|
-
end
|
60
|
-
}.freeze
|
61
|
-
|
62
|
-
BODY_PROCESSORS = {
|
63
|
-
put: ->(params) { File.read(params.fetch(:file)) },
|
64
|
-
propfind: lambda do |params|
|
65
|
-
if params.fetch(:quota, false)
|
66
|
-
PROPFIND_QUOTA_QUERY
|
67
|
-
elsif params.fetch(:depth, 0).zero?
|
68
|
-
PROPFIND_QUERY
|
69
|
-
end
|
70
|
-
end
|
71
|
-
}.freeze
|
72
|
-
|
73
|
-
RESPONSE_PARSERS = {
|
74
|
-
propfind: PropfindParser
|
75
|
-
}.freeze
|
76
|
-
|
77
|
-
def initialize(access_token:)
|
78
|
-
@access_token = access_token
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def http_method_for_action
|
84
|
-
@action.to_s.capitalize
|
85
|
-
end
|
86
|
-
|
87
|
-
def parse_response_body(response)
|
88
|
-
if response.is_a?(Net::HTTPSuccess) && !(parser = RESPONSE_PARSERS[@action]).nil?
|
89
|
-
parser.call(response.body)
|
90
|
-
else
|
91
|
-
response.body
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def request_body(params)
|
96
|
-
return if (proc = BODY_PROCESSORS[@action]).nil?
|
97
|
-
|
98
|
-
proc.call(params)
|
99
|
-
end
|
100
|
-
|
101
|
-
def request_headers(params)
|
102
|
-
proc = ACTIONS.fetch(@action)
|
103
|
-
|
104
|
-
headers = {Authorization: "OAuth #{@access_token}"}
|
105
|
-
headers.merge!(proc.call(params)) unless proc.nil?
|
106
|
-
|
107
|
-
headers
|
108
|
-
end
|
109
|
-
|
110
|
-
def request_uri(params)
|
111
|
-
URI.parse("#{ACTION_URL}#{params.fetch(:name)}")
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'ox'
|
2
|
-
require 'cgi'
|
3
|
-
|
4
|
-
module YandexClient
|
5
|
-
module Dav
|
6
|
-
class PropfindParser
|
7
|
-
SUCCESS_STATUS = 'HTTP/1.1 200 OK'.freeze
|
8
|
-
|
9
|
-
PROCESSORS = {
|
10
|
-
getcontentlength: ->(value) { value.to_i },
|
11
|
-
resourcetype: ->(value) { value.is_a?(Hash) ? :folder : :file }
|
12
|
-
}.freeze
|
13
|
-
|
14
|
-
class ParseError < StandardError
|
15
|
-
end
|
16
|
-
|
17
|
-
def initialize(xml)
|
18
|
-
@document = Ox.load(xml.force_encoding('UTF-8'), mode: :hash)
|
19
|
-
end
|
20
|
-
|
21
|
-
def call
|
22
|
-
@document[:'d:multistatus'].each_with_object({}) do |node, memo|
|
23
|
-
next if (response = node[:'d:response']).nil?
|
24
|
-
|
25
|
-
name = CGI.unescape(response.fetch(:'d:href'))
|
26
|
-
memo[name] = parse_node_props(response.fetch(:'d:propstat'))
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.call(xml)
|
31
|
-
new(xml).call
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def parse_node_props(node)
|
37
|
-
raise ParseError unless node.fetch(:'d:status') == SUCCESS_STATUS
|
38
|
-
|
39
|
-
node.fetch(:'d:prop').each_with_object({}) do |(key, value), memo|
|
40
|
-
prop_key = key.to_s.gsub(/^d:/, '').to_sym
|
41
|
-
processor = PROCESSORS[prop_key]
|
42
|
-
|
43
|
-
memo[prop_key] = processor.nil? ? value : processor.call(value)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
module YandexClient
|
2
|
-
module Passport
|
3
|
-
# https://tech.yandex.ru/passport/doc/dg/reference/request-docpage/
|
4
|
-
#
|
5
|
-
# Example:
|
6
|
-
# client = YandexClient::Passport::Client.new(access_token: Token.first.access_token)
|
7
|
-
# client.info
|
8
|
-
class Client < ::YandexClient::Client
|
9
|
-
ACTION_URL = 'https://login.yandex.ru/info'.freeze
|
10
|
-
ACTIONS = %i[info].freeze
|
11
|
-
|
12
|
-
# action - info
|
13
|
-
def initialize(access_token:)
|
14
|
-
@access_token = access_token
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def http(_request_uri)
|
20
|
-
@http ||= super
|
21
|
-
end
|
22
|
-
|
23
|
-
def request_uri(_params)
|
24
|
-
@request_uri ||= URI.parse(ACTION_URL)
|
25
|
-
end
|
26
|
-
|
27
|
-
def request_headers(_params)
|
28
|
-
{
|
29
|
-
Authorization: "Bearer #{@access_token}"
|
30
|
-
}
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|