retailcrm-api 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ecac159b5dfa1826977b6d2e158b7068fb41239d37cda2e9e7965a44f81bc886
4
+ data.tar.gz: 1fde1dcc5a9c929c42147f1da8e3f4308473485e85f77a8e98efddb4fa9ff63d
5
+ SHA512:
6
+ metadata.gz: eedb55035f7278c8b11e7babc274270e3ee1bb4e4a2750b2a15bf6e40c678a604128e81dad3ec27a2a26609a4da23887ed0f4bf60cbd55acc6be6b2212d5f7f0
7
+ data.tar.gz: 87c6371d1e6ec724f866278f8d23487fcb46ae75b619aed65c6df6abb50ee7746e1c331ca3db1f14605c68741bf55fd03cb6c28750a788d92eff76f33cd7cfb4
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Pavel Osetrov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,72 @@
1
+ # RetailCRM API
2
+
3
+ API wrapper для RetailCRM
4
+
5
+ # Установка Ruby
6
+
7
+ ## Ruby
8
+ $ gem install retailcrm-api
9
+ ## Rails
10
+ добавьте в Gemfile:
11
+ gem 'retailcrm-api'
12
+
13
+ и запустите `bundle install`.
14
+
15
+ Затем:
16
+ rails g retailcrm_api:install
17
+ ## Требования
18
+
19
+ Необходимо получить api key https://yourdomains.retailcrm.ru/admin/api-keys
20
+
21
+ ## Использование Rails
22
+
23
+ В файл `config/retailcrm_api.yml` вставьте ваши данные
24
+
25
+ ## Использование Ruby
26
+
27
+ Сначала создайте экземпляр объекта `RetailcrmApi::Request`:
28
+
29
+ ```ruby
30
+ retailcrm = RetailcrmApi::Request.new(api_key: "your_api_key")
31
+ ```
32
+
33
+ Вы можете изменять `api_key`, `timeout`, `open_timeout`, `faraday_adapter`, `proxy`, `symbolize_keys`, `logger`, и `debug`:
34
+
35
+ ```ruby
36
+ RetailcrmApi::Request.api_key = "your_api_key"
37
+ RetailcrmApi::Request.timeout = 15
38
+ RetailcrmApi::Request.open_timeout = 15
39
+ RetailcrmApi::Request.symbolize_keys = true
40
+ RetailcrmApi::Request.debug = false
41
+ ```
42
+
43
+ Либо в файле `config/initializers/retailcrm_api.rb` для Rails.
44
+
45
+ ## Debug Logging
46
+
47
+ Измените `debug: true` чтобы включить логирование в STDOUT.
48
+
49
+ ```ruby
50
+ retailcrm = RetailcrmApi::Request.new(api_key: "your_api_key", debug: true)
51
+ ```
52
+
53
+ ### Custom logger
54
+
55
+ `Logger.new` используется по умолчанию, но вы можете изменить на свой:
56
+
57
+ ```ruby
58
+ retailcrm = RetailcrmApi::Request.new(api_key: "your_api_key", debug: true, logger: MyLogger.new)
59
+ ```
60
+
61
+ Или:
62
+
63
+ ```ruby
64
+ RetailcrmApi::Request.logger = MyLogger.new
65
+ ```
66
+
67
+ # Примеры
68
+
69
+ ```ruby
70
+ RetailcrmApi::Request.customers.retrieve.body
71
+ #=> {:success=>true, :pagination=>{:limit=>20, :totalCount=>0, :currentPage=>1, :totalPageCount=>0}, :customers=>[]}
72
+ ```
@@ -0,0 +1,2 @@
1
+ Description:
2
+ retailcrm_api:install
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ module RetailcrmApi
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ def generate_install
8
+ copy_file 'retailcrm_api.yml', 'config/retailcrm_api.yml'
9
+ copy_file 'retailcrm_api.rb', 'config/initializers/retailcrm_api.rb'
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,17 @@
1
+ require 'retailcrm-api'
2
+
3
+ RetailcrmApi.setup do |config|
4
+ if File.exist?('config/retailcrm_api.yml')
5
+ processed = YAML.load_file('config/retailcrm_api.yml')[Rails.env]
6
+
7
+ processed.each do |k, v|
8
+ config::register k.underscore.to_sym, v
9
+ end
10
+
11
+ config::Request.api_key ||= ENV['RETAILCRM_API_KEY']
12
+ config::Request.timeout = 60
13
+ config::Request.open_timeout = 60
14
+ config::Request.symbolize_keys = true
15
+ config::Request.debug = false
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ defaults: &defaults
2
+ API_KEY: '***'
3
+ API_ENDPOINT: 'https://yourdomain.retailcrm.ru'
4
+ production:
5
+ <<: *defaults
6
+ development:
7
+ <<: *defaults
8
+ test:
9
+ <<: *defaults
@@ -0,0 +1,54 @@
1
+ require 'retailcrm-api/version'
2
+ require 'retailcrm-api/retailcrm_error'
3
+ require 'retailcrm-api/retailcrm_api_error'
4
+ require 'retailcrm-api/request'
5
+ require 'retailcrm-api/api_request'
6
+ require 'retailcrm-api/response'
7
+
8
+ module RetailcrmApi
9
+ class << self
10
+ def setup
11
+ yield self
12
+ end
13
+
14
+ def register(name, value, type = nil)
15
+ cattr_accessor "#{name}_setting".to_sym
16
+
17
+ add_reader(name)
18
+ add_writer(name, type)
19
+ send "#{name}=", value
20
+ end
21
+
22
+ def add_reader(name)
23
+ define_singleton_method(name) do |*args|
24
+ send("#{name}_setting").value(*args)
25
+ end
26
+ end
27
+
28
+ def add_writer(name, type)
29
+ define_singleton_method("#{name}=") do |value|
30
+ send("#{name}_setting=", DynamicSetting.build(value, type))
31
+ end
32
+ end
33
+ end
34
+
35
+ class DynamicSetting
36
+ def self.build(setting, type)
37
+ (type ? klass(type) : self).new(setting)
38
+ end
39
+
40
+ def self.klass(type)
41
+ klass = "#{type.to_s.camelcase}Setting"
42
+ raise ArgumentError, "Unknown type: #{type}" unless RetailcrmApi.const_defined?(klass)
43
+ RetailcrmApi.const_get(klass)
44
+ end
45
+
46
+ def initialize(setting)
47
+ @setting = setting
48
+ end
49
+
50
+ def value(*_args)
51
+ @setting
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,154 @@
1
+ module RetailcrmApi
2
+ class APIRequest
3
+
4
+ def initialize(builder: nil)
5
+ @request_builder = builder
6
+ end
7
+
8
+ def post(params: nil, headers: nil, suffix: nil, body: {}, first_time: true)
9
+ validate_api_key
10
+
11
+ begin
12
+ response = self.rest_client(suffix).post do |request|
13
+ configure_request(request: request, params: params, headers: headers, body: MultiJson.dump(body))
14
+ end
15
+ parse_response(response)
16
+ rescue => e
17
+ handle_error(e)
18
+ end
19
+ end
20
+
21
+ def get(params: nil, headers: nil, first_time: true)
22
+ validate_api_key
23
+
24
+ begin
25
+ response = self.rest_client.get do |request|
26
+ configure_request(request: request, params: params, headers: headers)
27
+ end
28
+ parse_response(response)
29
+ rescue => e
30
+ handle_error(e)
31
+ end
32
+ end
33
+
34
+ protected
35
+
36
+ # Convenience accessors
37
+
38
+ def api_key
39
+ @request_builder.api_key
40
+ end
41
+
42
+ def api_endpoint
43
+ @request_builder.api_endpoint
44
+ end
45
+
46
+ def timeout
47
+ @request_builder.timeout
48
+ end
49
+
50
+ def open_timeout
51
+ @request_builder.open_timeout
52
+ end
53
+
54
+ def proxy
55
+ @request_builder.proxy
56
+ end
57
+
58
+ def adapter
59
+ @request_builder.faraday_adapter
60
+ end
61
+
62
+ def symbolize_keys
63
+ @request_builder.symbolize_keys
64
+ end
65
+
66
+ # Helpers
67
+
68
+ def handle_error(error)
69
+ error_params = {}
70
+
71
+ begin
72
+ if error.is_a?(Faraday::ClientError) && error.response
73
+ error_params[:status_code] = error.response[:status]
74
+ error_params[:raw_body] = error.response[:body]
75
+
76
+ parsed_response = MultiJson.load(error.response[:body], symbolize_keys: symbolize_keys)
77
+
78
+ if parsed_response
79
+ error_params[:body] = parsed_response
80
+
81
+ title_key = symbolize_keys ? :title : "title"
82
+ detail_key = symbolize_keys ? :detail : "detail"
83
+
84
+ error_params[:title] = parsed_response[title_key] if parsed_response[title_key]
85
+ error_params[:detail] = parsed_response[detail_key] if parsed_response[detail_key]
86
+ end
87
+
88
+ end
89
+ rescue MultiJson::ParseError
90
+ end
91
+
92
+ error_to_raise = RetailcrmError.new(error.message, error_params)
93
+
94
+ raise error_to_raise
95
+ end
96
+
97
+ def configure_request(request: nil, params: nil, headers: nil, body: nil)
98
+ if request
99
+ request.params.merge!(params) if params
100
+ request.headers['Content-Type'] = 'application/json'
101
+ request.headers['X-API-KEY'] = "#{self.api_key}"
102
+ request.headers['User-Agent'] = "RetailCrmApi/#{RetailcrmApi::VERSION} Ruby gem"
103
+ request.headers.merge!(headers) if headers
104
+ request.body = body if body
105
+ request.options.timeout = self.timeout
106
+ request.options.open_timeout = self.open_timeout
107
+ end
108
+ end
109
+
110
+ def rest_client(suffix=nil)
111
+ client = Faraday.new("#{self.api_url}#{suffix.present? ? "/#{suffix}": ""}", proxy: self.proxy,
112
+ ssl: { version: "TLSv1_2" }) do |faraday|
113
+ faraday.response :raise_error
114
+ faraday.adapter adapter
115
+ if @request_builder.debug
116
+ faraday.response :logger, @request_builder.logger, bodies: true
117
+ end
118
+ end
119
+ client
120
+ end
121
+
122
+ def parse_response(response)
123
+ parsed_response = nil
124
+
125
+ if response.body && !response.body.empty?
126
+ begin
127
+ headers = response.headers
128
+ body = MultiJson.load(response.body, symbolize_keys: symbolize_keys)
129
+ parsed_response = Response.new(headers: headers, body: body)
130
+ rescue MultiJson::ParseError
131
+ error_params = { title: "UNPARSEABLE_RESPONSE", status_code: 500 }
132
+ error = RetailcrmError.new("Unparseable response: #{response.body}", error_params)
133
+ raise error
134
+ end
135
+ end
136
+
137
+ parsed_response
138
+ end
139
+
140
+ def validate_api_key
141
+ unless self.api_key && self.api_endpoint
142
+ raise RetailcrmApi::RetailcrmApiError, "You must set an api_key prior to making a call #{self.api_key} #{self.api_endpoint}"
143
+ end
144
+ end
145
+
146
+ def api_url
147
+ base_api_url + @request_builder.path
148
+ end
149
+
150
+ def base_api_url
151
+ "#{RetailcrmApi.api_endpoint}/api/v5/"
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,92 @@
1
+ module RetailcrmApi
2
+ class Request
3
+ attr_accessor :api_key, :api_endpoint, :timeout, :open_timeout, :proxy, :faraday_adapter, :symbolize_keys, :debug, :logger, :test
4
+
5
+ DEFAULT_TIMEOUT = 60
6
+ DEFAULT_OPEN_TIMEOUT = 60
7
+
8
+ def initialize(api_key: nil, api_endpoint: nil, timeout: nil, open_timeout: nil, proxy: nil, faraday_adapter: nil, symbolize_keys: false, debug: false, logger: nil, test: false)
9
+ p "Request initialize #{api_key}"
10
+
11
+ @path_parts = []
12
+ @api_key = api_key || self.class.api_key || ENV['RETAILCRM_API_KEY']
13
+ @api_key = @api_key.strip if @api_key
14
+ @api_key = RetailcrmApi::api_key if @api_key.nil?
15
+ @api_endpoint = api_endpoint || self.class.api_endpoint
16
+ @api_endpoint = RetailcrmApi::api_endpoint if @api_endpoint.nil?
17
+ @timeout = timeout || self.class.timeout || DEFAULT_TIMEOUT
18
+ @open_timeout = open_timeout || self.class.open_timeout || DEFAULT_OPEN_TIMEOUT
19
+ @proxy = proxy || self.class.proxy || ENV['RETAILCRM_API_PROXY']
20
+ @faraday_adapter = faraday_adapter || self.class.faraday_adapter || Faraday.default_adapter
21
+ @symbolize_keys = symbolize_keys || self.class.symbolize_keys || false
22
+ @debug = debug || self.class.debug || false
23
+ @test = test || self.class.test || false
24
+ @logger = logger || self.class.logger || ::Logger.new(STDOUT)
25
+ end
26
+
27
+ def method_missing(method, *args)
28
+ @path_parts << method.to_s.downcase
29
+ @path_parts << args if args.length > 0
30
+ @path_parts.flatten!
31
+ self
32
+ end
33
+
34
+ def respond_to_missing?(method_name, include_private = false)
35
+ true
36
+ end
37
+
38
+ def send(*args)
39
+ if args.length == 0
40
+ method_missing(:send, args)
41
+ else
42
+ __send__(*args)
43
+ end
44
+ end
45
+
46
+ def path
47
+ @path_parts.join('/')
48
+ end
49
+
50
+ def create(params: nil, headers: nil, body: {})
51
+ APIRequest.new(builder: self).post(params: params, headers: headers, suffix: 'update', body: body, suffix: 'create')
52
+ ensure
53
+ reset
54
+ end
55
+
56
+ def update(params: nil, headers: nil, body: {})
57
+ APIRequest.new(builder: self).post(params: params, headers: headers, suffix: 'update', body: body)
58
+ ensure
59
+ reset
60
+ end
61
+
62
+ def retrieve(params: nil, headers: nil)
63
+ APIRequest.new(builder: self).get(params: params, headers: headers)
64
+ ensure
65
+ reset
66
+ end
67
+
68
+ def delete(params: nil, headers: nil, suffix: 'delete')
69
+ APIRequest.new(builder: self).post(params: params, headers: headers)
70
+ ensure
71
+ reset
72
+ end
73
+
74
+ protected
75
+
76
+ def reset
77
+ @path_parts = []
78
+ end
79
+
80
+ class << self
81
+ attr_accessor :api_key, :timeout, :open_timeout, :api_endpoint, :proxy, :faraday_adapter, :symbolize_keys, :debug, :logger, :test
82
+
83
+ def method_missing(sym, *args, &block)
84
+ new(api_key: self.api_key, api_endpoint: self.api_endpoint, timeout: self.timeout, open_timeout: self.open_timeout, faraday_adapter: self.faraday_adapter, symbolize_keys: self.symbolize_keys, debug: self.debug, proxy: self.proxy, logger: self.logger, test: self.test).send(sym, *args, &block)
85
+ end
86
+
87
+ def respond_to_missing?(method_name, include_private = false)
88
+ true
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,10 @@
1
+ module RetailcrmApi
2
+ class Response
3
+ attr_accessor :body, :headers
4
+
5
+ def initialize(body: {}, headers: {})
6
+ @body = body
7
+ @headers = headers
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,29 @@
1
+ module RetailcrmApi
2
+ class RetailcrmApiError < StandardError
3
+ attr_reader :title, :detail, :body, :raw_body, :status_code
4
+
5
+ def initialize(message = "", params = {})
6
+ @title = params[:title]
7
+ @detail = params[:detail]
8
+ @body = params[:body]
9
+ @raw_body = params[:raw_body]
10
+ @status_code = params[:status_code]
11
+
12
+ super(message)
13
+ end
14
+
15
+ def to_s
16
+ super + " " + instance_variables_to_s
17
+ end
18
+
19
+ private
20
+
21
+ def instance_variables_to_s
22
+ [:title, :detail, :body, :raw_body, :status_code].map do |attr|
23
+ attr_value = send(attr)
24
+
25
+ "@#{attr}=#{attr_value.inspect}"
26
+ end.join(", ")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module RetailcrmApi
2
+ class RetailcrmError < StandardError
3
+ attr_reader :title, :detail, :body, :raw_body, :status_code
4
+
5
+ def initialize(message = "", params = {})
6
+ @title = params[:title]
7
+ @detail = params[:detail]
8
+ @body = params[:body]
9
+ @raw_body = params[:raw_body]
10
+ @status_code = params[:status_code]
11
+
12
+ super(message)
13
+ end
14
+
15
+ def to_s
16
+ super + " " + instance_variables_to_s
17
+ end
18
+
19
+ private
20
+
21
+ def instance_variables_to_s
22
+ [:title, :detail, :body, :raw_body, :status_code].map do |attr|
23
+ attr_value = send(attr)
24
+
25
+ "@#{attr}=#{attr_value.inspect}"
26
+ end.join(", ")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module RetailcrmApi
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: retailcrm-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Pavel Osetrov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-08-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.16.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.16.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: multi_json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.11.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.11.0
41
+ description: ''
42
+ email: pavel.osetrov@me.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - LICENSE
48
+ - README.markdown
49
+ - lib/generators/retailcrm_api/install/USAGE
50
+ - lib/generators/retailcrm_api/install/install_generator.rb
51
+ - lib/generators/retailcrm_api/install/templates/retailcrm_api.rb
52
+ - lib/generators/retailcrm_api/install/templates/retailcrm_api.yml
53
+ - lib/retailcrm-api.rb
54
+ - lib/retailcrm-api/api_request.rb
55
+ - lib/retailcrm-api/request.rb
56
+ - lib/retailcrm-api/response.rb
57
+ - lib/retailcrm-api/retailcrm_api_error.rb
58
+ - lib/retailcrm-api/retailcrm_error.rb
59
+ - lib/retailcrm-api/version.rb
60
+ homepage: https://github.com/osetrov/retailcrm-api
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.3.8
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.2.22
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: RetailCRM API
83
+ test_files: []