yandex_client 0.2.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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