yandex_client 0.1.0 → 1.0.1
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 +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
|