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
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ox'
|
4
|
+
require 'cgi'
|
5
|
+
|
6
|
+
module YandexClient
|
7
|
+
class Dav
|
8
|
+
class PropFindResponse
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
SUCCESS_STATUS = 'HTTP/1.1 200 OK'
|
12
|
+
|
13
|
+
class Item
|
14
|
+
attr_reader :name, :created_at, :last_modified, :content_type
|
15
|
+
|
16
|
+
def initialize(name, node)
|
17
|
+
@name = name
|
18
|
+
@folder = node[:'d:resourcetype'].is_a?(Hash)
|
19
|
+
|
20
|
+
load_node_data(node)
|
21
|
+
end
|
22
|
+
|
23
|
+
def folder?
|
24
|
+
@folder
|
25
|
+
end
|
26
|
+
|
27
|
+
def file?
|
28
|
+
!@folder
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def load_node_data(node)
|
34
|
+
@created_at = node.fetch(:'d:creationdate')
|
35
|
+
@last_modified = node.fetch(:'d:getlastmodified')
|
36
|
+
|
37
|
+
return if folder?
|
38
|
+
|
39
|
+
@etag = node.fetch(:'d:getetag')
|
40
|
+
@size = node.fetch(:'d:getcontentlength').to_i
|
41
|
+
@content_type = node.fetch(:'d:getcontenttype')
|
42
|
+
|
43
|
+
define_singleton_method(:etag) { @etag }
|
44
|
+
define_singleton_method(:size) { @size }
|
45
|
+
define_singleton_method(:content_type) { @content_type }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(xml)
|
50
|
+
@doc = Ox.load(
|
51
|
+
xml.dup.force_encoding('UTF-8'),
|
52
|
+
mode: :hash
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def each
|
57
|
+
@doc[:'d:multistatus'].each do |node|
|
58
|
+
next if (response = node[:'d:response']).nil?
|
59
|
+
|
60
|
+
name = CGI.unescape(response.fetch(:'d:href'))
|
61
|
+
node_data = response.fetch(:'d:propstat')
|
62
|
+
|
63
|
+
raise ParseError unless node_data.fetch(:'d:status') == SUCCESS_STATUS
|
64
|
+
|
65
|
+
yield Item.new(name, node_data.fetch(:'d:prop'))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
require 'http'
|
5
|
+
require 'yandex_client/dav/prop_find_response'
|
6
|
+
|
7
|
+
module YandexClient
|
8
|
+
# https://tech.yandex.ru/disk/doc/dg/reference/put-docpage/
|
9
|
+
#
|
10
|
+
# Chainable client:
|
11
|
+
#
|
12
|
+
# YandexClient::Dav[access_token]
|
13
|
+
# .put('1.txt', '/a/b/c/1.txt')
|
14
|
+
# .put(File.open('1.txt', 'rb'), '/path/to/1.txt')
|
15
|
+
# .put(Tempfile.new.tap { |t| t.write('say ni'); t.rewind }, '/path/to/tmp.txt')
|
16
|
+
# .delete('/a/b/c/1.txt')
|
17
|
+
# .mkcol('/a/b/c')
|
18
|
+
# .propfind('/a/dip.yml', depth: 0)
|
19
|
+
# .propfind('/a', depth: 1)
|
20
|
+
# .propfind.to_a
|
21
|
+
class Dav
|
22
|
+
include Configurable
|
23
|
+
include ErrorHandler
|
24
|
+
|
25
|
+
ACTION_URL = 'https://webdav.yandex.ru'
|
26
|
+
PROPFIND_QUERY = '<?xml version="1.0" encoding="utf-8"?><propfind xmlns="DAV:"></propfind>'
|
27
|
+
|
28
|
+
attr_reader :token
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def with_token(token)
|
32
|
+
new(token)
|
33
|
+
end
|
34
|
+
|
35
|
+
alias [] with_token
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(token)
|
39
|
+
@token = token
|
40
|
+
end
|
41
|
+
|
42
|
+
def put(file, dest, etag: nil, sha256: nil, size: nil)
|
43
|
+
io = file.is_a?(String) ? File.open(file, 'rb') : file
|
44
|
+
|
45
|
+
headers = auth_headers.merge!(
|
46
|
+
'Etag' => etag || Digest::MD5.file(io.path),
|
47
|
+
'Sha256' => sha256 || Digest::SHA256.file(io.path),
|
48
|
+
'Expect' => '100-continue',
|
49
|
+
'Content-Length' => (size || File.size(io.path)).to_s
|
50
|
+
)
|
51
|
+
|
52
|
+
process_response with_config.put(url_for(dest), body: io, headers: headers)
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete(dest)
|
56
|
+
process_response with_config.delete(
|
57
|
+
url_for(dest),
|
58
|
+
headers: auth_headers
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def mkcol(dest)
|
63
|
+
process_response with_config.request(
|
64
|
+
:mkcol,
|
65
|
+
url_for(dest),
|
66
|
+
headers: auth_headers
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def propfind(dest = '', depth = 1)
|
71
|
+
headers = auth_headers.merge!(
|
72
|
+
Depth: depth.to_s,
|
73
|
+
'Content-Type' => 'application/x-www-form-urlencoded',
|
74
|
+
'Content-Length' => PROPFIND_QUERY.length
|
75
|
+
)
|
76
|
+
|
77
|
+
response = with_config.request(:propfind, url_for(dest), headers: headers, body: PROPFIND_QUERY)
|
78
|
+
process_response(response)
|
79
|
+
|
80
|
+
PropFindResponse.new(response.body.to_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def auth_headers
|
86
|
+
{Authorization: "OAuth #{token}"}
|
87
|
+
end
|
88
|
+
|
89
|
+
def url_for(dest)
|
90
|
+
URI.parse(ACTION_URL).tap do |uri|
|
91
|
+
path = dest
|
92
|
+
path = "/#{path}" unless path.match?(%r{^/})
|
93
|
+
|
94
|
+
uri.path = path
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def process_response(response)
|
99
|
+
return self if response.status.success?
|
100
|
+
|
101
|
+
error_for_response(response)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module YandexClient
|
6
|
+
# https://yandex.ru/dev/disk/api/reference/capacity-docpage/
|
7
|
+
# https://yandex.ru/dev/disk/api/reference/content-docpage/
|
8
|
+
#
|
9
|
+
# YandexClient::Disk[access_token].info
|
10
|
+
# YandexClient::Disk[access_token].download_url('path/to/file')
|
11
|
+
class Disk
|
12
|
+
include Configurable
|
13
|
+
include ErrorHandler
|
14
|
+
|
15
|
+
ACTION_URL = 'https://cloud-api.yandex.net'
|
16
|
+
|
17
|
+
attr_reader :token
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def with_token(token)
|
21
|
+
new(token)
|
22
|
+
end
|
23
|
+
|
24
|
+
alias [] with_token
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(token)
|
28
|
+
@token = token
|
29
|
+
end
|
30
|
+
|
31
|
+
def info
|
32
|
+
process_response(
|
33
|
+
with_config.get("#{ACTION_URL}/v1/disk/", headers: auth_headers)
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def download_url(path)
|
38
|
+
url = URI.parse(ACTION_URL).tap do |uri|
|
39
|
+
uri.path = '/v1/disk/resources/download'
|
40
|
+
uri.query = URI.encode_www_form(path: path)
|
41
|
+
end
|
42
|
+
|
43
|
+
process_response(
|
44
|
+
with_config.get(url, headers: auth_headers)
|
45
|
+
).fetch(:href)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def auth_headers
|
51
|
+
{Authorization: "OAuth #{@token}"}
|
52
|
+
end
|
53
|
+
|
54
|
+
def process_response(response)
|
55
|
+
return JSON.parse(response.body, symbolize_names: true) if response.status.success?
|
56
|
+
|
57
|
+
error_for_response(response)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module YandexClient
|
4
|
+
module ErrorHandler
|
5
|
+
private
|
6
|
+
|
7
|
+
def error_for_response(response)
|
8
|
+
msg = response.body.empty? ? response.status.to_s : response.body.to_s.force_encoding('UTF-8')
|
9
|
+
err =
|
10
|
+
case response.code
|
11
|
+
when 404
|
12
|
+
NotFoundError.new(msg, http_code: 404)
|
13
|
+
else
|
14
|
+
ApiRequestError.new(msg, http_code: response.code)
|
15
|
+
end
|
16
|
+
|
17
|
+
raise err
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module YandexClient
|
4
|
+
# https://tech.yandex.ru/passport/doc/dg/reference/request-docpage/
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
# YandexClient::Passport::Client[Token.first.access_token].info
|
8
|
+
class Passport
|
9
|
+
include Configurable
|
10
|
+
include ErrorHandler
|
11
|
+
|
12
|
+
ACTION_URL = 'https://login.yandex.ru/info'
|
13
|
+
|
14
|
+
attr_reader :token
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def with_token(token)
|
18
|
+
new(token)
|
19
|
+
end
|
20
|
+
|
21
|
+
alias [] with_token
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(token)
|
25
|
+
@token = token
|
26
|
+
end
|
27
|
+
|
28
|
+
def info
|
29
|
+
process_response \
|
30
|
+
with_config.get(ACTION_URL, headers: auth_headers)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def auth_headers
|
36
|
+
{Authorization: "Bearer #{@token}"}
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_response(response)
|
40
|
+
return JSON.parse(response.body, symbolize_names: true) if response.status.success?
|
41
|
+
|
42
|
+
error_for_response(response)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/yandex_client.rb
CHANGED
@@ -1,30 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yandex_client/version'
|
2
4
|
|
5
|
+
require 'yandex_client/error_handler'
|
6
|
+
require 'yandex_client/configurable'
|
7
|
+
|
8
|
+
require 'yandex_client/dav'
|
9
|
+
require 'yandex_client/disk'
|
10
|
+
require 'yandex_client/auth'
|
11
|
+
require 'yandex_client/passport'
|
12
|
+
|
3
13
|
module YandexClient
|
4
|
-
Config = Struct.new
|
14
|
+
Config = Struct.new \
|
15
|
+
:api_key,
|
16
|
+
:api_secret,
|
17
|
+
:logger,
|
18
|
+
:read_timeout,
|
19
|
+
:write_timeout,
|
20
|
+
:connect_timeout
|
5
21
|
|
6
|
-
|
7
|
-
|
8
|
-
end
|
22
|
+
class ApiRequestError < StandardError
|
23
|
+
attr_reader :http_code
|
9
24
|
|
10
|
-
|
11
|
-
|
25
|
+
def initialize(msg = nil, http_code: nil)
|
26
|
+
if http_code
|
27
|
+
super "#{msg}, (http code #{http_code})"
|
28
|
+
else
|
29
|
+
super msg
|
30
|
+
end
|
31
|
+
|
32
|
+
@http_code = http_code
|
33
|
+
end
|
12
34
|
end
|
13
35
|
|
14
|
-
|
15
|
-
autoload :ApiRequestError, 'yandex_client/api_request_error'
|
16
|
-
autoload :NotFoundError, 'yandex_client/not_found_error'
|
36
|
+
NotFoundError = Class.new(ApiRequestError)
|
17
37
|
|
18
|
-
|
19
|
-
|
20
|
-
|
38
|
+
class << self
|
39
|
+
def config
|
40
|
+
@config ||= Config.new(nil, nil, nil, 120, 600, 5)
|
41
|
+
end
|
21
42
|
|
22
|
-
|
23
|
-
|
24
|
-
|
43
|
+
def configure
|
44
|
+
yield config
|
45
|
+
end
|
25
46
|
|
26
|
-
|
27
|
-
|
28
|
-
|
47
|
+
def auth
|
48
|
+
@auth ||= Auth.new
|
49
|
+
end
|
29
50
|
end
|
30
51
|
end
|
data/yandex_client.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
5
|
require 'yandex_client/version'
|
4
6
|
|
@@ -8,12 +10,12 @@ Gem::Specification.new do |spec|
|
|
8
10
|
spec.authors = ['Maxim Tretyakov']
|
9
11
|
spec.email = ['max@tretyakov-ma.ru']
|
10
12
|
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
+
spec.summary = 'Yandex Client'
|
14
|
+
spec.description = 'Allows to use yandex disk as webdav nodes'
|
13
15
|
spec.homepage = 'https://github.com/yamax2/yandex_client'
|
14
16
|
spec.license = 'MIT'
|
15
17
|
|
16
|
-
spec.files = Dir.chdir(File.expand_path(
|
18
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
17
19
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
20
|
end
|
19
21
|
|
@@ -21,14 +23,15 @@ Gem::Specification.new do |spec|
|
|
21
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
24
|
spec.require_paths = ['lib']
|
23
25
|
|
24
|
-
spec.add_runtime_dependency '
|
26
|
+
spec.add_runtime_dependency 'http'
|
25
27
|
spec.add_runtime_dependency 'ox'
|
26
28
|
|
27
29
|
spec.add_development_dependency 'bundler'
|
28
30
|
spec.add_development_dependency 'rake', '>= 10'
|
29
31
|
spec.add_development_dependency 'rspec', '>= 3.0'
|
32
|
+
spec.add_development_dependency 'simplecov', '>= 0.9'
|
30
33
|
spec.add_development_dependency 'vcr'
|
31
34
|
spec.add_development_dependency 'webmock'
|
32
35
|
|
33
|
-
spec.required_ruby_version = '>= 2.
|
36
|
+
spec.required_ruby_version = '>= 2.4'
|
34
37
|
end
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yandex_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maxim Tretyakov
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: http
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.9'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.9'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: vcr
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,9 +129,11 @@ executables: []
|
|
115
129
|
extensions: []
|
116
130
|
extra_rdoc_files: []
|
117
131
|
files:
|
132
|
+
- ".github/workflows/main.yml"
|
118
133
|
- ".gitignore"
|
134
|
+
- ".pryrc"
|
119
135
|
- ".rspec"
|
120
|
-
- ".
|
136
|
+
- ".rubocop.yml"
|
121
137
|
- Gemfile
|
122
138
|
- LICENSE.txt
|
123
139
|
- README.md
|
@@ -125,24 +141,24 @@ files:
|
|
125
141
|
- bin/console
|
126
142
|
- bin/setup
|
127
143
|
- dip.yml
|
128
|
-
- docker/Dockerfile.
|
129
|
-
- docker/docker-compose.development.yml
|
144
|
+
- docker/Dockerfile.dip
|
130
145
|
- docker/docker-compose.yml
|
146
|
+
- docker/prepare_env.sh
|
131
147
|
- lib/yandex_client.rb
|
132
|
-
- lib/yandex_client/
|
133
|
-
- lib/yandex_client/
|
134
|
-
- lib/yandex_client/
|
135
|
-
- lib/yandex_client/dav/
|
136
|
-
- lib/yandex_client/
|
137
|
-
- lib/yandex_client/
|
138
|
-
- lib/yandex_client/passport
|
148
|
+
- lib/yandex_client/auth.rb
|
149
|
+
- lib/yandex_client/configurable.rb
|
150
|
+
- lib/yandex_client/dav.rb
|
151
|
+
- lib/yandex_client/dav/prop_find_response.rb
|
152
|
+
- lib/yandex_client/disk.rb
|
153
|
+
- lib/yandex_client/error_handler.rb
|
154
|
+
- lib/yandex_client/passport.rb
|
139
155
|
- lib/yandex_client/version.rb
|
140
156
|
- yandex_client.gemspec
|
141
157
|
homepage: https://github.com/yamax2/yandex_client
|
142
158
|
licenses:
|
143
159
|
- MIT
|
144
160
|
metadata: {}
|
145
|
-
post_install_message:
|
161
|
+
post_install_message:
|
146
162
|
rdoc_options: []
|
147
163
|
require_paths:
|
148
164
|
- lib
|
@@ -150,15 +166,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
150
166
|
requirements:
|
151
167
|
- - ">="
|
152
168
|
- !ruby/object:Gem::Version
|
153
|
-
version: '2.
|
169
|
+
version: '2.4'
|
154
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
171
|
requirements:
|
156
172
|
- - ">="
|
157
173
|
- !ruby/object:Gem::Version
|
158
174
|
version: '0'
|
159
175
|
requirements: []
|
160
|
-
rubygems_version: 3.
|
161
|
-
signing_key:
|
176
|
+
rubygems_version: 3.2.26
|
177
|
+
signing_key:
|
162
178
|
specification_version: 4
|
163
179
|
summary: Yandex Client
|
164
180
|
test_files: []
|