yandex_client 0.2.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.
@@ -1,13 +0,0 @@
1
- FROM ruby:2.6.5-alpine
2
-
3
- RUN apk add --update --no-cache tzdata openssh-client less git build-base
4
- RUN cp /usr/share/zoneinfo/Asia/Yekaterinburg /etc/localtime && echo 'Asia/Yekaterinburg' > /etc/timezone
5
-
6
- RUN ssh-keyscan -H github.com >> /etc/ssh/ssh_known_hosts
7
-
8
- ENV BUNDLE_APP_CONFIG /app/.bundle
9
-
10
- RUN echo 'gem: --no-rdoc --no-ri --no-document' > /root/.gemrc
11
- RUN gem install 'bundler:2.1.4' rubocop
12
-
13
- WORKDIR /app
@@ -1,18 +0,0 @@
1
- version: '2'
2
-
3
- services:
4
- app:
5
- volumes:
6
- - ..:/app
7
- - ../:/localgems
8
- - ssh-data:/ssh:ro
9
- - bundler-data:/bundle
10
-
11
- volumes:
12
- bundler-data:
13
- external:
14
- name: bundler_data
15
-
16
- ssh-data:
17
- external:
18
- name: ssh_data
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module YandexClient
4
- class ApiRequestError < StandardError
5
- attr_reader :error, :error_description, :code
6
-
7
- def initialize(error:, error_description:, code:)
8
- @error = error
9
- @error_description = error_description
10
- @code = code
11
-
12
- super [error, error_description, "http code #{@code}"].compact.join(', ')
13
- end
14
- end
15
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'uri'
4
-
5
- module YandexClient
6
- module Auth
7
- # https://tech.yandex.ru/oauth/doc/dg/reference/refresh-client-docpage/
8
- # https://tech.yandex.ru/oauth/doc/dg/reference/auto-code-client-docpage/#auto-code-client__get-token
9
- #
10
- # https://oauth.yandex.ru/authorize?response_type=code&client_id=99bcbd17ad7f411694710592d978a4a2&force_confirm=false
11
- #
12
- # Example:
13
- # token = Token.first
14
- #
15
- # client = YandexClient::Auth::Client.new
16
- # client.create_token(code: '9388894')
17
- # client.refresh_token(refresh_token: token.refresh_token)
18
- class Client < ::YandexClient::Client
19
- AUTH_ACTION_URL = 'https://oauth.yandex.ru/token'
20
-
21
- ACTIONS = {
22
- create_token: 'authorization_code',
23
- refresh_token: 'refresh_token'
24
- }.freeze
25
-
26
- private
27
-
28
- def http(_request_uri)
29
- @http ||= super
30
- end
31
-
32
- def http_method_for_action
33
- METHOD_POST
34
- end
35
-
36
- def request_body(params)
37
- body_hash = {
38
- grant_type: ACTIONS.fetch(@action),
39
- client_id: ::YandexClient.config.api_key,
40
- client_secret: ::YandexClient.config.api_secret
41
- }.merge!(params)
42
-
43
- URI.encode_www_form(body_hash)
44
- end
45
-
46
- def request_headers(_params)
47
- {
48
- 'Content-type' => 'application/x-www-form-urlencoded'
49
- }
50
- end
51
-
52
- def request_uri(_params)
53
- @request_uri ||= URI.parse(AUTH_ACTION_URL)
54
- end
55
- end
56
- end
57
- end
@@ -1,98 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'oj'
4
- require 'net/http'
5
-
6
- module YandexClient
7
- # Base api client
8
- class Client
9
- METHOD_GET = 'Get'
10
- METHOD_POST = 'Post'
11
-
12
- ERRORS_BY_CODES = {
13
- 404 => NotFoundError
14
- }.freeze
15
-
16
- def method_missing(method_name, *args, &_)
17
- return super unless self.class::ACTIONS.include?(method_name)
18
-
19
- @action = method_name
20
- result = nil
21
- response = make_request(args.last.is_a?(Hash) ? args.last : {})
22
-
23
- result = parse_response_body(response) unless response.body.nil? || response.body.empty?
24
- process_errors(response, result) unless response.is_a?(Net::HTTPSuccess)
25
-
26
- result
27
- end
28
-
29
- def respond_to_missing?(method_name, include_private = false)
30
- self.class::ACTIONS.include?(method_name) || super
31
- end
32
-
33
- private
34
-
35
- def http(request_uri)
36
- Net::HTTP.new(request_uri.host, request_uri.port).tap do |http|
37
- http.use_ssl = true
38
- http.read_timeout = YandexClient.config.read_timeout
39
- http.continue_timeout = YandexClient.config.read_timeout
40
- end
41
- end
42
-
43
- def http_method_for_action
44
- METHOD_GET
45
- end
46
-
47
- def log_api_request(request_uri, request, response)
48
- return if (logger = YandexClient.config.logger).nil?
49
-
50
- logger.info "#{request.method} #{request_uri} #{response.code} #{response.message}"
51
- logger.info "request headers: #{Oj.dump(request.to_hash)}"
52
- logger.info "response headers: #{Oj.dump(response.to_hash)}"
53
- end
54
-
55
- def make_request(params)
56
- request_uri = request_uri(params)
57
-
58
- @body = request_body(params)
59
-
60
- request = Object.const_get("Net::HTTP::#{http_method_for_action}").new(
61
- request_uri.request_uri,
62
- request_headers(params)
63
- )
64
-
65
- request.body = @body
66
- response = http(request_uri).start { |http| http.request(request) }
67
-
68
- log_api_request(request_uri, request, response)
69
-
70
- response
71
- end
72
-
73
- def parse_response_body(response)
74
- Oj.load(response.body, symbol_keys: true)
75
- end
76
-
77
- def process_errors(response, result)
78
- klass = ERRORS_BY_CODES.fetch(response.code.to_i, ApiRequestError)
79
-
80
- raise klass.new(
81
- error: result.is_a?(Hash) ? result.fetch(:error) : result,
82
- error_description: result.is_a?(Hash) ? result[:error_description] || result[:description] : nil,
83
- code: response.code.to_i
84
- )
85
- end
86
-
87
- def request_body(_params)
88
- end
89
-
90
- def request_headers(_params)
91
- {}
92
- end
93
-
94
- def request_uri(_params)
95
- raise 'not implemented'
96
- end
97
- end
98
- end
@@ -1,117 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'digest'
4
-
5
- module YandexClient
6
- module Dav
7
- # https://tech.yandex.ru/disk/doc/dg/reference/put-docpage/
8
- #
9
- # cli = YandexClient::Dav::Client.new(access_token: access_token)
10
- #
11
- # cli.put(file: '1.txt', name: '/a/b/c/1.txt')
12
- # cli.delete(name: '/a/b/c/1.txt')
13
- # cli.mkcol(name: '/a/b/c')
14
- #
15
- # cli.propfind(name: '/a/dip.yml', depth: 0)
16
- # cli.propfind(name: '/a', depth: 1)
17
- # cli.propfind(name: '/', quota: true)
18
- class Client < ::YandexClient::Client
19
- ACTION_URL = 'https://webdav.yandex.ru'
20
- PROPFIND_QUERY = '<?xml version="1.0" encoding="utf-8"?><propfind xmlns="DAV:"></propfind>'
21
- PROPFIND_QUOTA_QUERY = <<~XML
22
- <D:propfind xmlns:D="DAV:">
23
- <D:prop>
24
- <D:quota-available-bytes/>
25
- <D:quota-used-bytes/>
26
- </D:prop>
27
- </D:propfind>
28
- XML
29
-
30
- # actions with header processors
31
- ACTIONS = {
32
- mkcol: nil,
33
- delete: nil,
34
-
35
- put: lambda do |params|
36
- filename = params.fetch(:file)
37
-
38
- etag = params[:etag] || Digest::MD5.file(filename)
39
- sha256 = params[:sha256] || Digest::SHA256.file(filename)
40
- size = params[:size] || File.size(filename)
41
-
42
- {
43
- Etag: etag.to_s,
44
- Sha256: sha256.to_s,
45
- Expect: '100-continue',
46
- 'Content-Length' => size.to_s
47
- }
48
- end,
49
-
50
- propfind: lambda do |params|
51
- depth = 0
52
- depth = params.fetch(:depth, 0) unless params.key?(:quota)
53
-
54
- headers = {
55
- Depth: depth.to_s,
56
- 'Content-Type' => 'application/x-www-form-urlencoded'
57
- }
58
-
59
- headers['Content-Length'] = @body.length unless @body.nil? || @body.empty?
60
- headers
61
- end
62
- }.freeze
63
-
64
- BODY_PROCESSORS = {
65
- put: ->(params) { File.read(params.fetch(:file)) },
66
- propfind: lambda do |params|
67
- if params.fetch(:quota, false)
68
- PROPFIND_QUOTA_QUERY
69
- elsif params.fetch(:depth, 0).zero?
70
- PROPFIND_QUERY
71
- end
72
- end
73
- }.freeze
74
-
75
- RESPONSE_PARSERS = {
76
- propfind: PropfindParser
77
- }.freeze
78
-
79
- def initialize(access_token:)
80
- @access_token = access_token
81
- end
82
-
83
- private
84
-
85
- def http_method_for_action
86
- @action.to_s.capitalize
87
- end
88
-
89
- def parse_response_body(response)
90
- if response.is_a?(Net::HTTPSuccess) && !(parser = RESPONSE_PARSERS[@action]).nil?
91
- parser.call(response.body)
92
- else
93
- response.body
94
- end
95
- end
96
-
97
- def request_body(params)
98
- return if (proc = BODY_PROCESSORS[@action]).nil?
99
-
100
- proc.call(params)
101
- end
102
-
103
- def request_headers(params)
104
- proc = ACTIONS.fetch(@action)
105
-
106
- headers = {Authorization: "OAuth #{@access_token}"}
107
- headers.merge!(proc.call(params)) unless proc.nil?
108
-
109
- headers
110
- end
111
-
112
- def request_uri(params)
113
- URI.parse("#{ACTION_URL}#{params.fetch(:name)}")
114
- end
115
- end
116
- end
117
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'ox'
4
- require 'cgi'
5
-
6
- module YandexClient
7
- module Dav
8
- class PropfindParser
9
- SUCCESS_STATUS = 'HTTP/1.1 200 OK'
10
-
11
- PROCESSORS = {
12
- getcontentlength: ->(value) { value.to_i },
13
- resourcetype: ->(value) { value.is_a?(Hash) ? :folder : :file }
14
- }.freeze
15
-
16
- class ParseError < StandardError
17
- end
18
-
19
- def initialize(xml)
20
- # FIXME: dup?
21
- @document = Ox.load(xml.dup.force_encoding('UTF-8'), mode: :hash)
22
- end
23
-
24
- def call
25
- @document[:'d:multistatus'].each_with_object({}) do |node, memo|
26
- next if (response = node[:'d:response']).nil?
27
-
28
- name = CGI.unescape(response.fetch(:'d:href'))
29
- memo[name] = parse_node_props(response.fetch(:'d:propstat'))
30
- end
31
- end
32
-
33
- def self.call(xml)
34
- new(xml).call
35
- end
36
-
37
- private
38
-
39
- def parse_node_props(node)
40
- raise ParseError unless node.fetch(:'d:status') == SUCCESS_STATUS
41
-
42
- node.fetch(:'d:prop').each_with_object({}) do |(key, value), memo|
43
- prop_key = key.to_s.gsub(/^d:/, '').to_sym
44
- processor = PROCESSORS[prop_key]
45
-
46
- memo[prop_key] = processor.nil? ? value : processor.call(value)
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module YandexClient
4
- module Disk
5
- # https://yandex.ru/dev/disk/api/reference/capacity-docpage/
6
- # https://yandex.ru/dev/disk/api/reference/content-docpage/
7
- #
8
- # cli = YandexClient::Dav::Client.new(access_token: access_token)
9
- #
10
- # cli.info
11
- # cli.download_url(path: '/my_dir')
12
- class Client < ::YandexClient::Client
13
- ACTION_URL = 'https://cloud-api.yandex.net'
14
-
15
- ACTIONS = {
16
- info: '/v1/disk/',
17
- download_url: '/v1/disk/resources/download'
18
- }.freeze
19
-
20
- REQUIRED_PARAMS = {
21
- download_url: %i[path]
22
- }.freeze
23
-
24
- def initialize(access_token:)
25
- @access_token = access_token
26
- end
27
-
28
- private
29
-
30
- def request_headers(_params)
31
- {
32
- Authorization: "OAuth #{@access_token}"
33
- }
34
- end
35
-
36
- def request_uri(params)
37
- if (required_params = REQUIRED_PARAMS[@action]) && !(missed_params = required_params - params.keys).empty?
38
- raise "required params not found: \"#{missed_params.join(',')}\""
39
- end
40
-
41
- URI.parse("#{ACTION_URL}#{ACTIONS.fetch(@action)}").tap do |uri|
42
- uri.query = URI.encode_www_form(params) unless params.empty?
43
- end
44
- end
45
- end
46
- end
47
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module YandexClient
4
- class NotFoundError < ApiRequestError
5
- end
6
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module YandexClient
4
- module Passport
5
- # https://tech.yandex.ru/passport/doc/dg/reference/request-docpage/
6
- #
7
- # Example:
8
- # client = YandexClient::Passport::Client.new(access_token: Token.first.access_token)
9
- # client.info
10
- class Client < ::YandexClient::Client
11
- ACTION_URL = 'https://login.yandex.ru/info'
12
- ACTIONS = %i[info].freeze
13
-
14
- # action - info
15
- def initialize(access_token:)
16
- @access_token = access_token
17
- end
18
-
19
- private
20
-
21
- def http(_request_uri)
22
- @http ||= super
23
- end
24
-
25
- def request_uri(_params)
26
- @request_uri ||= URI.parse(ACTION_URL)
27
- end
28
-
29
- def request_headers(_params)
30
- {
31
- Authorization: "Bearer #{@access_token}"
32
- }
33
- end
34
- end
35
- end
36
- end